leash
Composable local-LLM and robotics harness. Rust-first, MCP-first, simulation-safe.
Leash is a Rust harness runtime that lets LLM agents control robots through typed modules, safety gates, and a shared capability registry. Run in simulation with zero hardware, or connect physical robots behind explicit actuation gates.
Why Leash
- Simulation-safe by default. CI, demos, and smoke tests require zero hardware. Physical actuation is an explicit opt-in gate.
- MCP-native. Agents get 8 typed tools (health, capabilities, modules, observe, invoke_capability, stop, estop, capture) over stdio or localhost MCP HTTP.
- Safety gates at every layer. Deadman switch, estop, soft odometry limits, physical actuation gate. Policy-gated capability invocation.
- Feature-gated hardware. Waveshare UGV today, MAVLink drone + manipulator planned. No hardware compiles without explicit
--features. - Stack catalog. Runnable sim, MCP, HTTP, and compatibility demos.
leash list+leash run <stack>. - Module graph with typed streams. Modules declare inputs, outputs, lifecycle, health, and selected stream transport. Coordinator manages startup/shutdown order.
- Local stream transport. Module streams can use
local-pubsubfor async fan-out ormemoryfor deterministic tests. - Deterministic replay. JSONL record/replay fixtures can drive HTTP and MCP observe paths in non-physical replay mode.
Repository Map
flowchart TB
root["repo root\nCargo crate, package metadata, top-level README"] --> src["src/\nReusable harness library"]
root --> cli["src/bin/\nleash CLI entrypoint"]
root --> scripts["scripts/\nSmoke tests and bot installer"]
root --> docs["docs/\nOperator, release, and source-map docs"]
root --> examples["examples/\nSafe runnable examples and fixtures"]
root --> specs["specs/leash/\nDotDog project graph source and compiled DAG"]
root --> workflows[".github/workflows/\nCI and release automation"]
src --> runtime["runtime.rs\nHarness state, drivers, telemetry"]
src --> capability["capability.rs\nSafety classes and invocation policy"]
src --> http["http.rs\nHTTP, SSE, WebSocket routes"]
src --> mcp["mcp.rs\nMCP tools and transport"]
src --> config["config.rs\nProfiles, env, CLI precedence"]
Runtime Map
flowchart LR
human["Human operator\nCLI or curl"] --> surfaces["Control surfaces\nCLI, HTTP, MCP"]
agent["LLM agent\nMCP stdio or MCP HTTP"] --> surfaces
surfaces --> registry["CapabilityRegistry\nschemas, safety class, policy"]
registry --> harness["Harness runtime\ncommand state, modules, telemetry"]
harness --> drivers{"Profile driver"}
drivers --> sim["sim\nno hardware"]
drivers --> replay["replay\nJSONL fixtures"]
drivers --> ugv["waveshare-ugv\n/dev/ttyTHS1 serial"]
harness --> streams["Telemetry streams\nHTTP, SSE, WebSocket, MCP observe"]
registry --> safety["Safety gates\ntoken, approval, dry-run, deny, estop, deadman"]
Quick Start
# Install
# List built-in stacks
# Run MCP in simulation (zero hardware)
# Run with HTTP + WebSocket
# Run as a daemon and inspect JSONL logs
# Run MCP HTTP for agent or CLI tooling
# Check health
# Replay a deterministic fixture
MCP Tools
| Tool | Description |
|---|---|
health |
Harness health and safety state |
capabilities |
Endpoints, MCP tools, speed modes |
modules |
Module graph and stream metadata |
observe |
Latest telemetry frame (odometry, battery, sensors) |
invoke_capability |
authorize, drive, stop, estop, estop_reset, speed_mode |
stop |
Non-latching zero-speed motor stop |
estop |
Latch emergency stop until reset |
capture |
Deterministic frame capture |
MCP HTTP And CLI
/mcp/tools, /mcp/status, /mcp/modules, and /mcp/call return JSON for
local agents and automation. Module/tool mapping exposes tool names by module
without returning pilot session tokens.
HTTP Endpoints
GET / Local command center dashboard
GET /dashboard Local command center dashboard
POST /dashboard/authorize Authorize pilot token from dashboard form
POST /dashboard/stop Stop through shared capability registry
POST /dashboard/estop Latch estop through shared capability registry
POST /dashboard/estop-reset Clear estop through shared capability registry
POST /dashboard/capture Capture frame through shared capability registry
GET /health Harness health
GET /capabilities Endpoints + tools + stream transport
GET /telemetry Latest TelemetryFrame
GET /events/telemetry Server-sent telemetry stream
GET /sse/telemetry Alias for /events/telemetry
GET /agent Local web input form
GET /agent/messages Recent agent input messages
POST /agent/messages { source, text }
POST /drive { token, left, right, speed_mode, approval }
POST /estop Latch emergency stop
POST /estop/reset { token, approval } Clear estop
WS /ws/telemetry Streaming telemetry envelope frames
Viewer Frames
WS /ws/telemetry, GET /events/telemetry, and GET /sse/telemetry emit
TelemetryStreamFrame JSON. Each frame includes a versioned
visualization.version = "leash-visualization-v1" payload with:
pose: map-frame 2D posepath: viewer-ready pose listoccupancy_grid: compact grid metadata + cellspoint_cloud: metadata only, no viewer dependencydetections: generic detection boxescommand: motor command overlay for operators
External viewers can subscribe to the stream endpoints and render those fields without linking a viewer SDK into the core crate.
Agent Input
Run a local HTTP stack, then send natural-language text into the runtime without requiring a model provider:
|
The command center at /dashboard is server-rendered HTML with regular form
posts for authorize, stop, estop, reset, and capture actions. Those forms call
the same shared capability registry used by JSON HTTP, CLI, and MCP paths.
POST /agent/messages records a bounded recent inbox and publishes each message
on the agent stream for local subscribers. The web form at /agent is only
available when the http feature is built, and Leash binds HTTP to
127.0.0.1:8000 by default.
Agent Model Providers
Leash defaults to deterministic-test, a no-network provider for CI and demos.
It returns a stable synthetic response with each agent input acknowledgement.
Hosted and local HTTP providers can be configured and validated without printing
secrets:
LEASH_AGENT_API_KEY=... \
Provider settings are available as --agent-provider, --agent-model,
--agent-base-url, --agent-api-key, and --agent-timeout-ms, or via matching
LEASH_AGENT_* environment variables. agent_api_key is redacted from
show-config fields and omitted from top-level serialized config output.
Safety Policy
Capability invocations carry safety classes: observe-only, sim-control,
physical-stop, physical-motion, and physical-high-risk. drive is
physical-motion; stop and estop are physical-stop; estop_reset is
physical-high-risk.
--policy-mode and LEASH_POLICY_MODE support:
require-token(default): physical motion and high-risk calls need a token.require-approval: physical motion and high-risk calls needapproval=true.dry-run: physical motion and high-risk calls return a dry-run result without changing command state.deny: physical motion and high-risk calls are blocked.
stop and estop remain available under restrictive policy modes. Approved and
denied physical actions are emitted as structured log events. Agent-origin
physical motion always requires approval=true; physical profiles still require
--allow-physical-actuation or LEASH_ALLOW_PHYSICAL_ACTUATION=1 before the
harness starts.
Features
| Feature | Description | Default |
|---|---|---|
sim |
Simulation driver (no hardware) | ✓ |
http |
HTTP server + WebSocket | ✓ |
mcp |
MCP stdio server | ✓ |
waveshare-ugv |
Waveshare UGV physical adapter | opt-in |
bridge-compat |
Legacy robot bridge compatibility | opt-in |
Smoke Tests
scripts/smoke-all.sh runs the no-hardware release proof and prints a JSON
summary covering HTTP routes and policy denial, stdio MCP, physical-gate
refusal, daemon lifecycle, graph export, MCP HTTP + CLI calls, replay HTTP/MCP
observe paths, and config preflight checks.
Stream Transport
stream_transport selects the module-stream backend used by WebSocket and SSE
telemetry streams and shown in graph and capability output:
local-pubsubis the default runtime backend. It uses bounded async broadcast channels, supports fan-out across local tasks, drops oldest messages under receiver lag, and reports lag to subscribers.memoryis an unbounded in-process backend for deterministic tests and local harness checks. Dropped subscribers are pruned on publish.
Use --stream-transport memory or LEASH_STREAM_TRANSPORT=memory when a run
needs deterministic in-process delivery.
/ws/telemetry, /events/telemetry, and /sse/telemetry emit the same
transport-backed envelope: latest telemetry, health/module state, command state,
and safety state.
Run Logs and Resource Samples
Daemon runs write structured JSONL logs under the Leash state directory. Each
entry includes timestamp, run_id, module, event, level, and any
structured fields emitted with the tracing event:
Runtime telemetry keeps process resource sampling off by default. Enable it for
development or field debugging with --resource-sampling or
LEASH_RESOURCE_SAMPLING=1; use --no-resource-sampling to force it off when
an environment or config file enables it.
leash_harness::stream_processing provides generic helpers for high-rate
streams: latest-value backpressure, per-key rate limiting, quality filtering,
and timestamp pairing within a tolerance window.
Replay
leash record writes compact leash-replay-v1 JSONL events for telemetry,
sensors, camera status, and command state. leash replay emits those events
with original timing, scaled by --speed:
Use a replay source to serve deterministic observations through the normal HTTP and MCP surfaces without hardware:
Replay mode resolves to profile: replay, mode: replay, replay: true, and
physical: false / physical_actuation_enabled: false in config, health, and
capability output.
Run narrower checks when you need to isolate one surface:
Roadmap
See issues for the full plan. Highlights:
- Module graph with typed streams, lifecycle, and health aggregation
- Stack catalog:
leash list+leash run - Replay engine: deterministic sensor record + playback
- Transport abstraction: memory + local async pubsub
- Stream processing helpers: latest-value backpressure, quality filters, timestamp pairing
- Agent input channels: one-shot CLI, interactive CLI, and localhost web input
- Cross-process and network transports
- MAVLink drone + manipulator adapters
- Localhost command center dashboard
- Viewer-ready visualization frames
- Spatial memory and perception primitives
- Patrol and exploration in simulation
- Full no-hardware smoke suite
License
MIT