or build from source (any platform)
April 11, 2026 · View on GitHub
______ _ _____ _____
|___ / | | | __ \_ _|
/ / ___ _ __ | |_ ___| |__) || |
/ / / _ \ '_ \| __/ _ \ _ / | |
/ /_| __/ |_) | || (_) | | \ \ | |
/_____\___| .__/ \__\___/|_| \_\___|
| |
|_| durable agent runtime
Process supervisor and durable runtime for AI agents. Think systemd meets Temporal, purpose-built for LLM workloads. Supervise any agent binary with automatic restart, crash recovery, budget enforcement, and a tamper-evident audit trail.
┌──────────────────────────────────────────────────────┐ │ SUPERVISION restart · backoff · recovery sweep │ │ DURABILITY journal · snapshots · effect replay │ │ BUDGET token limits · cost caps · per-agent │ │ AUDIT SHA-256 hash chain · tamper-evident │ │ POLICY allow · deny · require-approval │ │ OBSERVABILITY live TUI · metrics · web dashboard │ └──────────────────────────────────────────────────────┘
No Rust code required. Use the CLI to supervise any process — Python scripts, Node agents, Go binaries, anything.
Quick Start
# Install pre-built binary (pick your platform)
curl -fsSL https://github.com/qhkm/zeptort/releases/latest/download/zeptort-aarch64-apple-darwin -o zeptort # macOS (Apple Silicon)
curl -fsSL https://github.com/qhkm/zeptort/releases/latest/download/zeptort-x86_64-unknown-linux-gnu -o zeptort # Linux (x86_64)
curl -fsSL https://github.com/qhkm/zeptort/releases/latest/download/zeptort-aarch64-unknown-linux-gnu -o zeptort # Linux (ARM64 / Pi)
chmod +x zeptort && sudo mv zeptort /usr/local/bin/
# or build from source (any platform)
cargo install --git https://github.com/qhkm/zeptort --features cli
# Start the daemon
zeptort daemon &
# Launch a Python agent under supervision
$ zeptort run --name researcher --restart always --max-restarts 10 -- python agent.py
[zeptort] started researcher
[zeptort] pid: 4821
[zeptort] log: ~/.zeptort/agents/researcher.log
# Check what's running
$ zeptort status
Name: researcher
State: running
PID: 4821
Command: python agent.py
Restart policy: always (count 0/10)
Uptime: 45s
# Kill it — the supervisor brings it back
$ kill -9 4821
$ zeptort status researcher
State: running
PID: 4955 ← new pid
Restart policy: always (count 1/10)
Uptime: 0s
# Verify the tamper-evident audit trail
$ zeptort audit
Hash chain: intact
Entries verified: 42
# Live dashboard
$ zeptort top --live
zeptort agent runtime daemon alive
─────────────────────────────────────────────────────
AGENT PID STATE RESTARTS UPTIME
researcher 4955 running 1/10 2m34s
worker 5012 running 0/5 1m12s
─────────────────────────────────────────────────────
AUDIT: 42 entries / hash chain intact
# Done
$ zeptort halt researcher
[zeptort] halt requested for researcher
The Problem
AI agents fail. They hallucinate commands, loop infinitely, exhaust budgets, panic on unexpected inputs. When one agent in a swarm fails, it often takes down the entire workflow. Worse: if a crash happens after an expensive LLM call, the call re-executes on restart — double cost, possibly different result. Nobody knows what the agent actually did.
The Solution
ZeptoRT treats failure as normal. When an agent crashes:
- The supervisor detects it within 100ms
- The agent restarts with exponential backoff
- Recorded LLM results replay from the journal — no re-execution, no double cost
- The budget gate prevents runaway spending
- Every event is logged in a tamper-evident SHA-256 hash chain
No 3 AM pages. No $500 surprise bills. No "what did the AI do?"
| Problem | ZeptoRT |
|---|---|
| Agent crashes, loses all state | Durable journal — replay recorded results, no re-execution |
| Runaway LLM calls cost $500 overnight | Budget gating — per-process token/cost limits, checked before every effect |
| No idea what agents did or why | Hash-chained audit trail — tamper-evident log of every lifecycle event |
| One crash takes down the whole system | OTP supervision trees — OneForOne, OneForAll, RestForOne with backoff |
CLI Reference
9 commands. All client commands accept --addr and --token (or ZEPTORT_ADDR / ZEPTORT_TOKEN env vars).
Daemon
zeptort daemon [--config FILE] # Start the runtime daemon (default: ./zeptort.toml)
zeptort check [--config FILE] # Validate config without starting
Process Supervision
# Supervise any command — Python, Node, Go, shell scripts, anything
zeptort run --name NAME [OPTIONS] -- COMMAND [ARGS...]
Options:
--restart <POLICY> never | on-failure | always (default: on-failure)
--max-restarts <N> give up after N restarts (default: 5)
--cwd <DIR> working directory for the child
# Examples
zeptort run --name researcher --restart always --max-restarts 20 -- python agent.py
zeptort run --name worker --restart on-failure -- node worker.js --port 3000
zeptort run --name batch --restart never -- ./run-pipeline.sh
Inspect & Monitor
zeptort status # List all supervised agents
zeptort status NAME # Detail view: state, PID, uptime, restart count, log path
zeptort top # One-shot dashboard (table format)
zeptort top --live # Live-updating TUI dashboard (refreshes every 500ms)
zeptort top --json # Machine-readable JSON output
zeptort ping # Health check — "alive (version=0.1.0, uptime_ms=...)"
Control
zeptort halt NAME # Graceful stop: SIGTERM → wait grace period → SIGKILL
# Terminal — even with --restart always, the agent stays down
zeptort kill --all # SIGKILL every live agent immediately
Audit
zeptort audit # Verify SHA-256 hash chain: "Hash chain: intact, Entries verified: 73"
Environment Variables
| Variable | Description | Default |
|---|---|---|
ZEPTORT_ADDR | Daemon address (host:port) | 127.0.0.1:9090 |
ZEPTORT_TOKEN | Bearer token for auth | none |
# Set once, skip --addr/--token on every command
export ZEPTORT_ADDR=10.0.0.5:9090
export ZEPTORT_TOKEN=my-secret-token
zeptort status # just works
What happens when things go wrong
Agent crashes (SIGKILL, panic, OOM)
→ Supervisor detects within 100ms
→ Exponential backoff (100ms → 200ms → 400ms → ... capped at 30s)
→ Restart with preserved restart counter
→ Journal records every event
Daemon itself crashes
→ Recovery sweep finds orphaned agents in SQLite registry
→ Resurrects them with their restart policy intact
→ Hash-chained audit trail survives the crash
Config-Driven Daemon
No code needed — define agents in TOML:
# zeptort.toml
[daemon]
bind = "127.0.0.1:9090"
db_path = "~/.zeptort/zeptort.db"
[daemon.auth]
bearer_token = "your-secret-token"
[[agents]]
name = "researcher"
type = "llm"
provider = "anthropic"
model = "claude-sonnet-4"
system_prompt = "You are a research assistant."
tools = ["web_fetch"]
auto_start = true
[agents.budget]
token_limit = 100000
cost_limit_usd = 5.00
[external_agents]
default_restart_policy = "on-failure"
default_max_restarts = 5
default_backoff = "exponential"
log_dir = "~/.zeptort/agents"
zeptort daemon # start all agents
curl localhost:9090/ping # health check
curl localhost:9090/agents/external # list supervised processes
curl localhost:9090/metrics # runtime metrics (JSON)
curl localhost:9090/journal/verify # audit chain status
Built-in agent types: llm, http_webhook, cron, pipeline.
REST API + MCP-compatible JSON-RPC at POST /mcp with SSE discovery at GET /mcp/sse.
HTTP API
| Method | Path | Description |
|---|---|---|
| GET | /ping | Health check |
| GET | /metrics | Runtime metrics |
| GET | /agents/external | List all supervised processes |
| GET | /agents/external/{name} | Process detail + recent log lines |
| POST | /agents/external | Spawn a new supervised process |
| POST | /agents/external/{name}/halt | Graceful halt |
| GET | /journal/verify | Audit chain verification |
| GET | /processes | List kernel processes |
| POST | /mcp | MCP JSON-RPC endpoint |
| GET | /mcp/sse | MCP SSE discovery |
Optional bearer token auth — all endpoints except /ping and /health.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ ZEPTORT DAEMON │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ EXTERNAL PROCESS SUPERVISOR │ │
│ │ │ │
│ │ zeptort run ──→ Supervisor ──→ Agent Process │ │
│ │ │ │ │ │
│ │ │ crash / exit │ │
│ │ │ │ │ │
│ │ ↓ ↓ │ │
│ │ Backoff ────→ Auto-restart │ │
│ │ (100ms..30s) (count: 1/10) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ KERNEL RUNTIME (Rust library) │ │
│ │ │ │
│ │ StepBehavior ──→ Scheduler ──→ Reactor (async I/O) │ │
│ │ │ │ │ │
│ │ │ declare effect │ dispatch + record │ │
│ │ ↓ ↓ │ │
│ │ Budget Gate ──→ Policy ──→ Journal ──→ Snapshot │ │
│ │ (tokens/$) (allow/ (23 entry (periodic │ │
│ │ deny) types) checkpoints) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ SQLite: journal + snapshots + registry │ │
│ │ SHA-256 hash chain ──→ tamper-evident audit trail │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ HTTP/REST API + MCP JSON-RPC + SSE │ │
│ │ /agents/external /metrics /journal/verify /mcp │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Two modes — pick what fits:
- CLI supervisor (no Rust needed) —
zeptort runwraps any binary with restart policies, backoff, and audit logging - Rust library (full control) — durable execution with declared effects, journaling, and crash recovery
Features
Supervision
- Restart policies:
always,on-failure,never - Exponential backoff (100ms base, 30s cap)
- OTP-style supervision trees: OneForOne, OneForAll, RestForOne
- Halt is terminal regardless of restart policy
- Recovery sweep on daemon restart (orphaned agents resurrected from SQLite)
Durability
- SQLite journal with 23 entry types
- SHA-256 hash-chained audit trail (tamper-evident)
- Periodic snapshots with pruning
- Crash recovery replays recorded results (no re-execution)
- Idempotency keys for effect deduplication
Budget & Policy
- Per-process token and cost limits
- Per-effect-kind policy (allow/deny/require-approval)
- Human approval gateway with HTTP endpoints
- Budget checked before every effect dispatch
Observability
zeptort top --live— real-time TUI dashboardzeptort audit— journal integrity verification/metricsendpoint with atomic counters- Structured logging via
tracingcrate - Web dashboard with live agent cards (deploy/dashboard/)
Rust Library (Optional)
For full control, embed ZeptoRT as a Rust library:
cargo add zeptort
use zeptort::core::behavior::{StepBehavior, BehaviorMeta};
use zeptort::core::message::{Envelope, EnvelopePayload, Payload};
use zeptort::core::step_result::StepResult;
use zeptort::core::effect::{EffectRequest, EffectKind};
use zeptort::core::turn_context::TurnContext;
use zeptort::error::Reason;
struct MyAgent { state: String }
impl StepBehavior for MyAgent {
fn init(&mut self, _checkpoint: Option<Vec<u8>>) -> StepResult {
StepResult::Continue
}
fn handle(&mut self, msg: Envelope, ctx: &mut TurnContext) -> StepResult {
match &msg.payload {
EnvelopePayload::User(Payload::Text(text)) => {
StepResult::Suspend(EffectRequest::new(
EffectKind::LlmCall,
serde_json::json!({ "prompt": text, "model": "claude-sonnet-4" }),
))
}
EnvelopePayload::Effect(result) => {
if let Some(output) = &result.output {
self.state = output.to_string();
}
StepResult::Continue
}
_ => StepResult::Continue,
}
}
fn terminate(&mut self, _reason: &Reason) {}
fn snapshot(&self) -> Option<Vec<u8>> {
Some(self.state.as_bytes().to_vec())
}
fn restore(&mut self, state: &[u8]) -> Result<(), String> {
self.state = String::from_utf8_lossy(state).into();
Ok(())
}
fn meta(&self) -> Option<BehaviorMeta> {
Some(BehaviorMeta { module: "my_agent".into(), version: "1.0.0".into() })
}
}
16 effect kinds: LlmCall, Http, DbQuery, HumanApproval, ObjectFetch, VectorSearch, CliExec, SandboxExec, Custom, and more. See docs/GUIDE.md for the full walkthrough.
Status
662 tests passing. 17 of 18 spec sections implemented. Single-node, production-ready.
| Feature | Status |
|---|---|
| External process supervision (restart, backoff, recovery) | Done |
| SQLite journal with 23 entry types | Done |
| SHA-256 hash-chained audit trail | Done |
| OTP supervision trees (OneForOne, OneForAll, RestForOne) | Done |
| Budget gating (token + cost limits per process) | Done |
| Effect pipeline (16 kinds: LlmCall, Http, DbQuery, ...) | Done |
| Crash recovery (snapshot + journal replay) | Done |
| CLI tools (daemon, run, halt, status, top, audit, ping) | Done |
| HTTP/REST API with bearer auth | Done |
| MCP JSON-RPC + SSE | Done |
Content security scanning (optional shield feature) | Done |
Live TUI dashboard (zeptort top --live) | Done |
| Web dashboard with chaos-monkey demo | Done |
| Multi-node clustering | Not yet |
Background
ZeptoRT didn't start here. It took three iterations to find the right architecture.
Attempt 1: Replicate Erlang/BEAM in Rust (ZeptoBeam) — We built a full BEAM replica: reduction-counting scheduler, supervision trees, ETS/DETS tables, hot code upgrades, durable mailboxes, MCP integration. 46,000 lines of Rust, 343 tests, built in 5 days. It hit a wall: the sync scheduler can't await, so every LLM call required two thread hops and correlation IDs. Reduction counting models CPU — but AI agents don't consume CPU, they consume tokens and dollars.
Attempt 2: Just make it async — Rewrote with tokio tasks, await directly in handlers. 4x smaller codebase, natural to write. But the runtime went blind — it couldn't see what effects cost, whether policy allowed them, or how to replay them on crash. If an agent crashed after an LLM call, the call re-executed. No journaling, no replay, no budget enforcement.
Attempt 3: Declare, don't execute (ZeptoRT) — Agents don't execute effects. They describe them as intents. The runtime journals, budget-checks, policy-checks, dispatches, records, and delivers. On crash, recorded results replay — no re-execution. This is the Temporal pattern adapted for AI: deterministic shell + nondeterministic effects, with budget gating and OTP supervision that Temporal doesn't have.
The right abstraction for AI agents isn't "processes that run code" — it's "processes that declare intent and let the runtime handle the rest."
Read the full story: docs/BACKGROUND.md
Influences
Erlang/OTP — "Let it crash." The BEAM VM runs WhatsApp (2B+ users) and Discord with nine nines of uptime. ZeptoRT brings OTP's supervision trees, process isolation, and fault-tolerance to AI agents.
Temporal — Durable execution pioneer. ZeptoRT uses Temporal's core pattern but adds budget gating, content policy, and OTP-style supervision.
Ray — Budget-aware scheduling. ZeptoRT adapts Ray's resource model from GPU compute to LLM tokens and API cost.
Principles
- Supervise > Defend — let supervisors handle restarts, not application code
- Journal > Repeat — replay recorded results, don't re-execute expensive effects
- Declare > Execute — describe effects as intents; the runtime decides if/when/how
- Crash > Corrupt — restart clean rather than recover from undefined state
Docs
- Runtime Specification — 30-section spec
- Usage Guide — full walkthrough
- Background — the full ZeptoBeam → ZeptoRT journey
- Handler Conventions — replay safety guidelines
- Gap Analysis — spec compliance (17/18 done)
- Changelog — version history
- Contributing — how to contribute
License
Apache 2.0 — see LICENSE
For commercial licensing, enterprise support, or managed hosting inquiries: qaiyyum@aisar.ai