Hurricane Sentinel v19
This is the stored snapshot for the approved document version. The diff below shows what changed from the previous version.
Document snapshot
Hurricane Sentinel
CLI reference — Every command, flag and choice, generated from the CLI's own parser — plus sentinel-runtime, the macOS VM manager.
Summary
Hurricane Sentinel is the secure agent harness for local AI. It runs AI agents on your own machine under real, enforced guardrails — routing every model call through Muzzle (the inspecting proxy) and running each agent as a Leash-leashed user (so the OS enforces what it can touch, reach, and run). On top of that it adds durable, resumable runs and a human approval queue: when an agent tries something flagged, the run pauses and waits for a person to approve before continuing.
Sentinel is the control center that ties the three Hurricane products together: Muzzle guards the model I/O, Leash guards the OS actions, and Sentinel orchestrates the agent and owns the human-in-the-loop.
Operating Sentinel?
OPERATING.mdcollects the behaviour that surprises people in production — how run scope decides what an agent can see, why a confined tool's "nothing found" may be confinement rather than truth, the memory-bank layout, the macOS/Lima traps, and which lever actually gates each kind of tool. Most of it came from operators hitting it the hard way.
Status
In the Lab — broad and deployed. The full harness is built and validated end-to-end
on Linux and runs on real deployments: agents run as the leashed user with model calls
through Muzzle, flagged actions pause for operator approval and resume on a yes (under
live Leash + Landlock enforcement), a head agent delegates to a roster of persistent
specialists and to whole teams that fan out to their members, and everything is
driven from either the CLI or a web console. Added since the core engine: memory +
context compaction, saved workflows, the web console, teams (leader-led rosters with
a shared workspace), external model providers (OpenAI today, via Muzzle),
uploaded Python tools (drop in a .py, pick its functions, served through a private
MCP tool host), agent/team portability (export a team as a package, import it
elsewhere), and legible guardrails (every Muzzle block and Leash/sandbox denial is
explained to both the agent and the operator, so the agent adapts instead of failing).
Since then: named user accounts with roles replace the single shared console token,
and Sentinel ships a macOS desktop app — one command installs it, and the hardened
runtime (Sentinel + Leash + Muzzle, full kernel enforcement) runs in a managed Linux VM
the app sets up for you. The server install remains Linux. See the Roadmap.
What works today
-
Named accounts, roles, and per-user sessions. Operators sign in with a username and password; each session is its own revocable token. Five roles — admin, approver, operator, member, auditor — gate what a person can do: auditors are read-only, a member only chats, an operator drives runs and the resources they need but holds no credentials (models, secrets and API endpoints are an admin's), approvals require approver or admin, and user management is admin-only. A role is only a preset: each account's Access section lets an admin tick individual capabilities on or off for that one account, give configure models/providers and set up API endpoints either their own records or everyone's, and limit the account to named agents. Accounts are managed in Admin ▸ Users in the console. The old shared console token still works as a break-glass credential (and bootstraps the first admin), so nothing that automated against it breaks.
-
Conversations belong to people, and can be shared. A chat belongs to whoever started it; nobody else sees it until it is shared read or reply, and a run you cannot see answers 404 rather than confirming it exists.
@-ing a colleague gives them access and tells them; a message whose only mentions are people is a note that no model is asked to answer. Every turn carries the account that wrote it — the console labels it and the agent is told who it is talking to and who else can read the answer. If the person you@may not use that chat's agent, you are asked on the spot whether to let them take part in that conversation only. The chat header's + Person does the same thing without needing a message to carry it, and lists everyone in the conversation with a way to take them back out. -
Another agent, mid-conversation. Bring one in for a single task ("peek in and work, then leave") or to stay in the chat so you can keep
@-ing it, and let it leave when you are done. A guest always answers in its own sandbox, so inviting one never widens what it can reach, and the transcript records when it arrived and left. -
Lazy context. An agent's skills and tools can be carried as a one-line catalogue it loads from instead of riding in every turn's prompt (
sentinel agent lazy-context <name> on). Measured on the bundled Blender designer: ~8,619 tokens of system prompt per turn becomes ~465, and the agent pulls in a skill — which then stays for the rest of the conversation — when it decides it needs one. It is a context decision, not a permission one: the grant and the approval gates are unchanged. -
An append-only audit trail. Every mutation records who asked, what for, and what it was about, written through the same symlink-safe privileged append as the rest of the product;
sentinel audit verifychecks it, and an action that cannot be recorded is refused by default rather than allowed unrecorded. -
Runs on a Mac. A desktop app installs with one command and runs the console as a native window. Enforcement is unchanged — Sentinel, Leash and Muzzle run in a managed Linux VM the app creates on first launch, so Landlock/seccomp/nftables are all live. Nothing to install first: the VM runtime is bundled.
-
Supervised runs, one Linux account per agent. Each run executes as its agent's own leashed account (the shared leashed user when an agent has none); the OS enforces its file/network/command access per-UID, with model traffic inspected by Muzzle.
-
Durable, resumable runs + async human approvals. Run state is checkpointed to disk; a
needs_approvalaction pauses the run and enqueues an approval that an operator approves/denies via the CLI, after which the run resumes where it stopped. -
Persistent agents. A roster of on-disk specialists (
~/.sentinel/agents/), each with its own soul (persona), bound model (from your configured list), extensible skills, and tools.sentinel agent create/list/show/add-skill/set-modelandsentinel run --agent <name>. -
Orchestration. A head agent can
delegatea task to a persistent specialist, orspawn_subagentto compose a temporary one when none fits — each as its own durable, enforced child run, with least-privilege grants and a configurable disposition (discard/save/ask). -
Several agents in one conversation.
@namehands that turn straight to another agent — no leader turn, no delegation tool — and its reply joins the same thread under its own name. It reads the conversation so far (attributed, minus other agents' raw tool output) and knows who else is present. Who it can reach follows the run scope, and it still executes in its OWN sandbox as a real, auditable child run. -
Teams. A leader-led roster with a shared workspace, authored guidelines (prepended to every team run), and an activity log. The head delegates to a whole team; the team's leader fans the work out to its members — each member a scoped, sandboxed, approval-gated child run — then synthesizes their results. Fan-out is bounded (concurrency + a per-round cap); cross-team delegation only goes through the head, and each member's sandbox excludes other teams' and agents' private workspaces.
-
A web console. A self-contained, token-authed web UI (
sentinel web): chat with live delegation activity, approve/deny inline, and manage agents, teams, tools, MCP servers, models, endpoints, memory, and the Muzzle/Leash guardrails — all from the browser. Loopback-only by default; expose it over built-in TLS (sentinel web --host 0.0.0.0 --tls-cert … --tls-key …, or--tls-selfsignedfor a quick encrypted console) or a TLS reverse proxy — a non-loopback plaintext bind is refused. Auth is a single bearer token today (multi-user/RBAC is future); keep the host trusted. -
Legible guardrails. When Muzzle blocks a model call or Leash/the Landlock sandbox blocks an action, the agent gets a clear, categorized reason and adapts — it rewrites a reply that tripped an output filter (bounded retries), stops and explains an input it can't process, or moves to an allowed path/host — instead of hard-failing or looping on a raw errno. Each block also surfaces to the operator as a color-coded card in the run feed (Leash denials enriched with the exact reason from its decision log, correlated root-side), and a compact guardrails skill is auto-injected into the agent's context so it knows what each category means and how to respond. Block detection is read-only observability — it never opens a path around enforcement.
-
One-command setup with a model your way.
sentinel setupinstalls the stack and gets you to a working agent. Two model paths: local (--model <name>, defaults toqwen2.5:7b— a small, reliable tool-caller pulled via Ollama), or bring-your-own-key for a no-GPU laptop —sentinel setup --provider openai|anthropic|gemini --model <id>wires the hosted model end-to-end in one step (key resolved fromSENTINEL_MODEL_API_KEYor a prompt, stored only in Muzzle). Setup ends by printing your console URL + login token (nosudo grep), then runssentinel doctor. -
sentinel doctor— an actionable preflight. One command that answers "will my first agent work, and if not, how do I fix it?": it checks the model backend reaches a live model (a real end-to-end ping), Muzzle is up and allows tool calls (an unset policy silently drops every agent tool call — the #1 first-run killer), providers are wired as upstreams, Leash egress, the broker socket, and the console token — printing the exact fix for anything wrong. It runs automatically at the end of install. -
External model providers. Point an agent at OpenAI / Anthropic / Gemini in addition to local Ollama. Keys live only in Muzzle — Sentinel is the control plane and pushes a keyless per-provider listener into Muzzle; the model store never holds a secret, and the API never hands one back.
-
CLI-runtime agents (Codex / Claude Code). Set an agent's model to
codexorclaude-codeand it's driven by that coding CLI's own agentic loop with its native tools — on your Codex / Claude subscription, not a metered API key. Any Sentinel tools you grant are injected into the CLI as MCP, so it has its built-in tools plus exactly what you granted. Enforcement is OS-level (leashed user, Leash egress, per-run cgroup, workspace cwd); the CLI's step trace — reasoning, tool calls, tool output — is surfaced as run events (an "Agent steps" disclosure), so you see the work, not just the final reply. Offered only where the CLI is installed and logged in as the leashed user. -
A workspace file browser. A Finder-like Files tab in the console: pick a workspace (the shared one, or any agent's / team's), navigate folders with a breadcrumb, preview text and images, and upload / new-folder / rename / delete / download. Every path is server-side real-path-jailed to the selected workspace — no
.., absolute, or symlink escape. -
Mounted workspaces. Give an agent, team, or a single chat files from outside its own sandbox — a local directory or an SSH target — as a named workspace, configured in a Workspaces panel or with
sentinel workspace add|list|test|sync|push. Because runs are heavily sandboxed (no kernel mounts), Sentinel materialises the source into a synced directory the run reads (rsync/rsync-over-ssh); read-only by default (opt in to writable), the SSH key kept write-only and the host key pinned, with the agent's changes pushed back to the source only on demand. The SSH sync runs under its own dedicated low-privilege identity (sentinel-sync) via the root broker, so the agent's own leashed account stays fully contained — it can'tsshout or reach the workspace host itself. -
Uploaded Python tools. Upload a
.pyfile; Sentinel AST-scans it (never importing it in the root process), you pick which functions become tools, and a private built-in MCP tool host serves the marked functions to agents — approval-gated like any tool, with their third-party requirements recorded and installed on one click (leashed pip). -
Portability. Export an agent or a whole team as a secret-free
.sentinelpkg— its skills, tools, MCP configs (minus keys), model selection, souls, guidelines, and uploaded tool files + requirements. Import it into another deployment (rename on collision, cross-refs rewritten); the receiver adds their own keys and is good to go. -
Memory + context compaction. Each agent has its own memory bank (private, or shared by scope) — a flat index plus an Obsidian-style note vault; it can
rememberfacts andrecallthem across runs using bounded, weighted lexical retrieval. Relevant memories are surfaced as explicitly untrusted, potentially stale context. Long runs auto-compact: older turns are summarized while the system prompt and recent turns are kept, so an agent never blows its context window. -
Small-model friendly. A tool-use prompt scaffold, forgiving tool-call parsing, arg normalization + validation with self-correction, automatic delegation context, duplicate-call dedup, and a bundled
file-disciplineskill keep things reliable on small local models (e.g. llama3.2, gpt-oss:20b). -
An interactive chat shell.
sentinel chatopens a sleek REPL: a framed banner, colorized live progress, inline yes/no approvals, ↑/↓ history, and completion menus for/commands,@targeted specialists, and$skills. (Run it as the leashed user:sudo -u <user> sentinel chat.) -
Saved workflows. Define a named, multi-step pipeline in YAML where each step runs an agent on a task and outputs thread into later steps via
{{input}}/{{<step>.output}}templating. Run it withsentinel workflow run <name>or let an agent trigger one with therun_workflowtool; every step is its own supervised run. -
A
sentinelCLI + web console. CLI:init,setup,doctor,chat,run,runs,show,approvals,approve/deny,resume,agent …,team …,workflow …,mcp …,model …,cron …,package export/import,import openclaw/import hermes(migrate an OpenClaw or Hermes assistant's agents/skills/memory/cron/tools/timers — also in the web console),tools install-reqs,web,api,status,preflight,logs. Web:sentinel web(control console) andsentinel api(programmable per-endpoint HTTP API).
Security posture
The one invariant everything else serves: no execution path bypasses the leashed
worker + approval gate, and no Sentinel service runs as root except one tiny,
schema-constrained Leash-policy broker. The control plane (supervisor, web, API, cron)
runs as the leashed user; each run — head, team member, cron, endpoint — is a worker
subprocess of it under Leash (egress + fs) and a Landlock sandbox scoped to that run.
Sensitive tools pause for approval; autonomous (cron) runs skip anything effectful unless
the operator explicitly opts in. Worker subprocesses get a scrubbed environment (the
control plane's secrets — the web token, provider keys — never travel), provider/MCP
secrets are masked in every API response and live only in Muzzle, uploaded code is
AST-scanned, never imported in the control-plane process, and the key-injecting Muzzle
listeners bind loopback. The one operation that still needs root — changing Leash
egress policy — is brokered by the root sentinel-broker over a root:<leashed> socket
(a closed set of schema-validated verbs, peer-cred-gated, no arbitrary exec, audited). Guardrail
blocks are observable but never bypassable: a block is surfaced to the agent and the
operator as a read-only event — the Leash-decision correlation is a supervisor concern,
done once outside the run — so making a block legible never becomes a path around
enforcement.
Agent-influenced subprocesses run through a single bounded runner — wall-clock
timeout, whole-process-group kill (a forking runaway can't leave orphans behind), a
CPU ceiling, and an in-memory output cap — with the host cgroup owning the memory
ceiling. Every confined run emits a security_attestation event recording the posture
it actually ran under (profile, effective worker UID, live Landlock ABI, cgroup slice,
Muzzle routing), so a run's event stream is a verifiable audit record. Console access is now per-person: named accounts with scrypt-hashed passwords, revocable
per-user sessions, and role-gated routes (an auditor cannot mutate anything; only an
approver or admin can resolve an approval gate). The legacy shared token is retained
deliberately as a break-glass credential rather than the only way in. Released artifacts
are reproducible, SBOM'd and Sigstore-signed — see v1/docs/SUPPLY_CHAIN.md
for how to verify a download, and v1/docs/SECURITY_WHITEPAPER.md
/ v1/docs/THREAT_MODEL.md for the architecture and the
attacker model (including the residual risks we have not closed). The full security
and resource model is in SECURITY.md.
How it fits together
operator ── sentinel CLI / web console (token auth) ──┐
▼
Supervisor (root) ── spawns each run as its own subject (Leash + Landlock)
│
Agent worker (as the agent's own uid) ── model calls ──▶ Muzzle ──▶ provider
loop: model → tool calls → policy(auto / needs-approval / deny)
auto → run the tool (file/net/exec enforced by Leash + Landlock)
needs-approval → pause + enqueue → (operator approves) → resume
deny → tell the agent
delegate → spawn a scoped, sandboxed child run (agent / team member)
Where that runs
Those guarantees are Linux kernel features, so Sentinel runs on Linux — either installed
directly on a host, or inside the managed VM the macOS desktop app brings with it. The VM
installs from the same artifact with the same installer and profile, so it is a normal
Linux install and the CLI behaves identically. Only the reach differs: on macOS the host
sentinel has no config, so commands go through sentinel-runtime cli <command>, which
forwards into the guest.
CLI-runtime agents (codex, claude-code)
An agent can be one of those CLIs, running on a subscription rather than an API key,
while staying an ordinary Sentinel agent — scoped, teamed, workspace-bound, with its
granted tools bridged in as MCP servers. The credential lives in Sentinel's secrets store
and is materialized into a private HOME for the duration of one run.
Because the CLI drives its own loop and brings its own sandbox, Landlock and seccomp are
skipped and its model traffic bypasses Muzzle; the leashed user, Leash egress, the cgroup
and the workspace cwd still apply. A strict profile refuses such a run unless
allow_cli_runtime is set, and each run's attestation records that it had the reduced
boundary. See OPERATING.md for the setup and the full trade-off.
Roadmap
M0 supervised runs + approvals ✅ → M1 persistent agents + orchestration ✅ → small-model robustness ✅ → memory + context compaction ✅ → interactive chat shell ✅ → saved workflows ✅ → web console ✅ → teams ✅ → external model providers ✅ → uploaded tools (MCP tool host) ✅ → agent/team portability ✅ → security audit + hardening ✅ → legible guardrails (block visibility + agent adaptation) ✅ → next: public download + install, deeper multi-provider support.
Diff from previous
--- README@18 +++ README@19 @@ -69,6 +69,13 @@ then leave") or to **stay** in the chat so you can keep `@`-ing it, and let it leave when you are done. A guest always answers in its own sandbox, so inviting one never widens what it can reach, and the transcript records when it arrived and left. +- **Lazy context.** An agent's skills and tools can be carried as a one-line + catalogue it loads from instead of riding in every turn's prompt + (`sentinel agent lazy-context <name> on`). Measured on the bundled Blender + designer: **~8,619 tokens of system prompt per turn becomes ~465**, and the agent + pulls in a skill — which then stays for the rest of the conversation — when it + decides it needs one. It is a context decision, not a permission one: the grant + and the approval gates are unchanged. - **An append-only audit trail.** Every mutation records who asked, what for, and what it was about, written through the same symlink-safe privileged append as the rest of the product; `sentinel audit verify` checks it, and an action that cannot be recorded is