Hyperspace AVM
Agent Verification Machine. The execution engine for AI agents — the same idea V8 brought to browsers, applied to agent actions. One Rust daemon, three sandbox tiers, sixteen detection modules, neurosymbolic gating (rules first, ML advisory), hash-chained audit. Every agent on the box — Cursor, Claude Code, Aider, OpenClaw, your custom LangChain script — funnels through it.
AVM is V8 for AI agents. Browsers ran untrusted JS through V8 so the machine survived; agents run untrusted actions through AVM for the same reason.
Every agent funnels through one Unix-domain socket. Three lines of integration per runtime; one policy file across them all.
Three tiers, automatic downgrade. Strongest isolation that's currently available wins.
An AI coding agent deleted PocketOS's production database in nine seconds — one Railway GraphQL volumeDelete mutation. The model later "confessed" in writing to violating every safety rule it had been told to follow. The root cause wasn't a bad model. It was that safety rules in the system prompt are not enforcement. Walkthrough →
Every agent runtime today builds its own ad-hoc safety. Each framework's tool ACL is different. Each one's audit log is different (or missing). Each one re-implements regex catches incompletely. You configure each separately, audit each separately, and hope none missed anything. Nothing they ship enforces against the destructive call once the model decides to make it.
AVM is the shared substrate that does enforce. One Rust daemon, one policy, one hash-chained audit. Every agent on the box — tool-using assistants, autonomous loops, MCP servers — funnels through the same gate. The gate runs before the syscall fires, the curl wires up, or setup.py executes.
Trust no model output. Trust no framework's internal sandbox. Trust the OS-level boundary, signed configs, hash-chained audit, and local-only ML inference. Everything else is policy.
Type a command. The live widget runs the exact regex bank from src/patterns.rs in your browser — no network, no install. Real avmd adds behavioural, consequence, and ML layers on top; the regex tier alone catches the canonical kill-chain.
| tool ACL | destructive regex | behavioural anomaly | privacy ML | sandbox tiers | tamper-evident audit | |
|---|---|---|---|---|---|---|
| tool-using assistants (MCP / function-call style) | per-tool prompts | none | none | none | none | none |
| autonomous-loop agents (long-running goal-pursuers) | partial | none | none | none | none | partial (in-process logs) |
| cloud agent runtimes (cloud-internal planning + tool legs) | vendor-side | none | none | none | vendor-side only | vendor-side only |
| + AVM | enforced, deny-by-default | 73 destructive · 91 injection · 24 PII · 32 supply-chain | per-agent baseline, persisted | regex + 1.5B model + 7B coming | Docker → process → direct downgrade | hash-linked, offline-verifiable |
The column "enforced" is doing real work in that bottom row: the rules are in code, with property tests pinning the invariant that ML can never relax a rule's block. See ML detection layer.
Local-first
Runs on the user's box. The audit chain is owned by the user, not the vendor. Never phones home. Telemetry — if any — is per-user opt-in.
Vendor-neutral
Any agent runtime — tool-using, autonomous, MCP server, or the local-touchdown leg of a cloud agent — integrates via three lines. SDKs in TypeScript and Python.
Defence in depth
Gating happens at every layer the agent can reach: classify pipeline, ACL primitives, egress proxy, sandbox tiers, seccomp, cgroup, gas budget. No single bypass collapses the whole.
Real incidents AVM closes against: PocketOS Railway-volumeDelete prod-deletion (April 2026); the litellm supply-chain attack (poisoned PyPI exfiltrating SSH/AWS/GCP creds, March 2026); generic "ignore previous instructions" injection; staged write→chmod→exec credential exfil; runaway-cost loops in long-running agents. Walkthroughs below.
A single Rust daemon (avmd, ~2 MB stripped) and an operator CLI (avmctl). Sixteen detection modules + supporting infrastructure, ~16 KLOC, 532 tests, BSL 1.1.
Classify pipeline. Every command, network call, install, and outbound payload runs the relevant subset of 16 modules in ~3 µs.
Local-first
Lives on the user's box. The audit chain is owned by the user, not the vendor. Never phones home.
Vendor-neutral
Cursor, Claude Code, Aider, OpenClaw, custom LangChain — framework-agnostic.
Production Rust
~2 MB binary. Hand-rolled BPF for seccomp. No unsafe outside pre_exec + seccomp install.
Hash-chained audit
Every classify, register, gas debit, install, autoupdate, and tool_gate decision writes a JSONL row whose hash includes the previous row's serialisation.
16-module pipeline
Pattern, behaviour, consequence reasoning, blast radius, exfiltration, injection (5 layers), credential provenance, network scope, rate limiter, supply chain, output review, OOB confirmation.
Two-tier PII
Regex on structured (cards, keys, JWTs) + OpenAI Privacy Filter ML on unstructured (names, addresses). Local sidecar — never sends data to the cloud.
Every command, network call, install, file write, and outbound payload runs the relevant subset of these modules in ~3 µs per classify (p50). Decisions are allow / confirm / block; confirms are out-of-band and the agent cannot auto-complete them.
Standalone (vendor-neutral). The installer detects your platform, fetches the matching tarball, verifies sha256 against the signed release manifest, places /usr/local/bin/avmd and avmctl, writes a default fail-closed policy at ~/.hyperspace/avm-policy.json, and registers a hardened systemd user unit (Linux) or LaunchAgent plist (macOS).
Bundled with Hyperspace CLI (recommended for hyperspace nodes). Since CLI v5.40.0, the avmd + avmctl binaries are bundled in the CLI release tarball; hyperspace start spawns avmd as a child process automatically and surfaces it via hyperspace avm status. Existing CLI installs auto-update to v5.40.0 and pick up avmd on next start.
Captured from a fresh install of the released v2.1.2 binary on Ubuntu 22.04. Not a mockup; not what we wish it'd say. Run it yourself and you should see the same shape.
Three integration paths. All three converge at the same Unix socket; pick whichever fits the agent runtime cleanest.
Three lines per runtime. After that, every child_process.spawn, subprocess.run, urllib.urlopen, requests.delete, httpx.post, os.system, fetch, and direct exec/spawn syscall passes through avmd's classify pipeline before it runs.
Speak JSON-RPC to ~/.hyperspace/avmd.sock directly. Use the typed SDKs:
One JSON entry in the agent's settings; no shell rewriting needed.
The MCP server exposes six tools: avm.gate, avm.classify, avm.privacy_egress, avm.review_output, avm.audit_verify, avm.status. Each maps 1:1 to an existing avmd RPC method — no daemon-side changes needed.
One daemon, eight inner subsystems, every agent funnels through it. Sidecar handles ML inference out-of-process.
Catastrophic vendor mutations
22 vendors with regression tests. AWS, GCP, Azure, Railway, Fly, DigitalOcean, Vercel, Supabase, Cloudflare, GitHub, Stripe, Heroku, Linear, Notion, Datadog, Sentry, PagerDuty, MongoDB Atlas, Render, OpenAI, Anthropic, Auth0, Clerk, Pinecone, Neon, PlanetScale, CockroachDB.
Local destruction
rm -rf /, mkfs, wipefs, fdisk, dd if=/dev/zero of=/dev/sd*, sudo rm, shred, recursive chmod / chown over /, MongoDB db.dropDatabase(), Redis FLUSHALL.
Database destruction
DROP DATABASE, DROP SCHEMA, DROP TABLE, DELETE FROM ... ; (no WHERE), TRUNCATE, dropdb, pg_drop.
Container destruction
terraform destroy, docker system prune -af, docker volume rm, kubectl delete --all / namespace / pvc, helm uninstall.
Source destruction
git push --force, git reset --hard, git branch -D.
Obfuscation wrappers
eval $(...), base64 -d | bash, curl ... | bash, printf hex | bash, python -c, node -e, bash -c, IFS-trick, X=rm assignment, PowerShell -enc.
Prompt injection
5-layer detector with 91+ compiled regexes. "Ignore previous instructions" variants, role-override attacks, system-prompt extraction, encoded payloads. Severity-graded (Critical / High / Medium / Low).
Credential theft
~/.ssh, ~/.aws, ~/.kube, ~/.config/gcloud, browser cookies / keychains. Outbound payloads with credential patterns block at privacy_egress_ml.
PII leakage
Regex tier (24 patterns) + OpenAI Privacy Filter ML (8 categories). Per-category policy: allow / warn / ask / block. Local-only classification — user data never leaves the box.
Resource exhaustion
cgroup v2 caps + per-agent gas budget. Runaway agent halts cleanly instead of taking down the host.
Tool-result injection
output_review symmetric scan catches a compromised tool smuggling injection payloads back through the result channel.
Supply chain
npm / pip install runs inside Docker with --cap-drop ALL --read-only. Lockfile sha256 verified. 32-pattern post-install behavioural scanner.
The regex tier alone cannot catch:
- Base64-decoded payloads — the wrapper catch fires; the decoded bytes don't.
- libc::execve direct via FFI — the seccomp deny list + sandbox tier are the mitigation.
- Unicode confusables — agents that route through the preloader get normalisation; raw input doesn't.
- Multi-stage write→chmod→exec with innocuous bytes — the audit chain still records every stage.
- Pure cloud-internal agent reasoning — AVM gates the local boundary; in-cloud planning is by design out of scope.
See FEATURES.md and tests/adversarial.rs for the explicit catches/misses contract.
| Vendor | Destructive APIs caught (regression-tested) |
|---|---|
| AWS | s3 rb, s3api delete-bucket, ec2 terminate-instances, rds delete-db-*, cloudformation delete-stack, dynamodb delete-table, eks delete-cluster + GraphQL TerminateInstances, DeleteBucket, DeleteStack, DeleteDBInstance, DeleteTable, DeleteCluster, DeleteFunction |
| GCP | gcloud projects delete, compute instances delete, sql instances delete, container clusters delete + projects.delete, instances.delete |
| Azure | az group delete + ResourceGroups_Delete |
| Railway | volumeDelete · volumeRemove · volumeDestroy · volumeWipe · projectDelete/Remove/Destroy/Wipe · serviceDelete · environmentDelete · deploymentDelete + REST DELETE /v2/volumes/ |
| Fly.io | destroyApp, deleteVolume, deleteMachine + fly destroy |
| DigitalOcean | destroyDroplet, destroyVolume, destroyDatabase + doctl compute droplet delete + REST DELETE /v2/droplets/ |
| Vercel | deleteProject, deleteDeployment + vercel remove |
| Supabase | deleteProject, deleteOrganization |
| Cloudflare | deleteZone, deleteWorker, purgeCache (warn) |
| GitHub | deleteRepository, deleteBranch + REST DELETE /repos/ + DELETE /orgs/ |
| Stripe | customer.delete, subscription.delete, invoice.delete, product.delete + REST DELETE /v1/customers etc. |
| Heroku | app-delete, heroku apps:destroy, heroku pg:reset |
| Linear | issueDelete, teamDelete, organizationDelete, projectDelete, cycleDelete |
| Notion | deletePage, deleteBlock, deleteDatabase + REST DELETE /v1/databases |
| Datadog | monitorDelete, dashboardDelete, syntheticDelete, siloDelete |
| Sentry | projectDestroy, teamRemove, organizationRemove |
| PagerDuty | deleteService, deleteEscalationPolicy, deleteSchedule |
| MongoDB Atlas | deleteAtlasCluster, deleteAtlasProject, deleteAtlasOrganization |
| Render | deleteRenderService, deleteRenderPostgres |
| OpenAI | deleteAssistant, deleteFile, deleteThread, deleteVectorStore + REST DELETE /v1/assistants etc. |
| Anthropic | deleteWorkspace, archiveWorkspace, deleteApiKey + REST DELETE /v1/messages etc. |
| Auth0 / Clerk | deleteTenant, deleteApplication, deleteOrganization, deleteUser |
| Pinecone | deleteIndex, deleteCollection |
| Neon / PlanetScale / CockroachDB | deletePsBranch, deletePsDatabase, deleteCluster (CockroachDB), deleteBranch (Neon) |
The PocketOS curl meets seven independent gates before the kernel ever sees the syscall. Pinned in two regression tests.
The exact wire-format Jer Crane reported — one curl, 9 seconds, prod gone:
With AVM in the loop, that exact string hits seven independent gates before the kernel sees the syscall:
| Gate | What fires |
|---|---|
| destructive_detector | volumeDelete is Level 4 DESTROY in DANGEROUS_MUTATIONS; multi-kind classifier ensembles bash + graphql signals |
| consequence_engine | "fix credential mismatch" task → "delete production volume" action is a cross-purpose escalation |
| network_scope | backboard.railway.app not on the agent's outbound allowlist for this task |
| permission_check_network | same answer at the ACL layer |
| behavioral_detector | this agent doesn't normally call Railway's GraphQL API; anomaly score spikes |
| injection_scanner | cross-purpose: task said "fix credential", action is volume deletion |
| oob_confirmation | level-4 ops require out-of-band approval the agent literally cannot autocomplete |
Pinned in two regression tests:
Net result: classify returns {"action":"block"}, the preloader raises in the agent, the curl never runs.
The litellm-style attack runs inside supply_chain::sandboxed_install. Even if the payload is in the build, it sees no host secrets and no exfil network.
March 2026: a poisoned litellm build (97M monthly downloads) landed on PyPI for under an hour. A single pip install litellm exfiltrated SSH keys, AWS / GCP / Azure creds, Kubernetes configs, env vars, crypto wallets, and CI/CD secrets — spreading transitively to every downstream package. Caught only because a bug in the payload crashed the machine.
With AVM, the install runs inside supply_chain::sandboxed_install:
- Docker container with --cap-drop ALL, --read-only, no host secret mounts. The exfil code can only see the container's filesystem — ~/.ssh, ~/.aws, ~/.kube, env vars, crypto wallets, none of it is mounted.
- Network restricted to pypi.org + npmjs.org. The exfil POST refuses to resolve.
- Lockfile sha256 verified against requirements.txt / package-lock.json. Unknown packages blocked by default.
- Post-install behavioural scanner — 32 compiled patterns detect credential file reads, env harvesting, exfiltration via setup.py install hooks. Suspicious packages quarantined.
- avmctl install scan — audit existing site-packages / node_modules for supply-chain patterns without installing anything.
Three tiers, automatic downgrade chain, every step logged:
No libseccomp dep. 18 dangerous syscalls return EPERM. Architecture-specific syscall numbers (x86_64 + aarch64). Architecture mismatch triggers SECCOMP_RET_KILL_PROCESS.
| Category | Syscalls denied |
|---|---|
| kernel-state | mount, umount2, pivot_root, chroot, swapon, swapoff, reboot, init_module, finit_module, delete_module, kexec_load, kexec_file_load |
| tracing | ptrace, perf_event_open |
| sandbox | setns, unshare |
| BPF loading | bpf (defence in depth — no nested filters) |
| page-fault escapes | userfaultfd (used in CVE-2016-2384 style escapes) |
Per-process CPU / memory / PIDs caps under /sys/fs/cgroup/avm/<name>/. Graceful no-op when cgroup v2 isn't mounted (macOS, non-root, unprivileged containers).
Two complementary tiers. Regex is fast and precise on structured PII; the ML model handles names, addresses, and free-form fields. Both run locally.
Local-only. AVM never sends user data to a remote service for classification. The privacy-filter sidecar runs as a separate process on the user's host, talking only to avmd over a 0o600 socket.
Regex tier
24 high-precision patterns: AWS keys, GitHub tokens, OpenAI / Anthropic keys, Stripe keys, Supabase keys, JWTs, private keys, Postgres URLs, Slack tokens, Twilio SIDs, SendGrid keys, generic SECRET= / PASSWORD= / TOKEN= / API_KEY= / CLIENT_SECRET=, credit cards (with Luhn), phone numbers, bearer tokens.
ML tier
openai/privacy-filter 1.5B-param model (Apache 2.0, released 2026-04-22). Eight categories: private_person, private_address, private_email, private_phone, private_url, private_date, account_number, secret. Runs in a Unix-socket sidecar; falls back to regex if absent.
Ensemble merge
Regex wins on structured PII; ML wins on unstructured (names, addresses, free-form dates). Same-category overlap deduped. privacy_egress_ml RPC returns redacted text + per-category action in one call.
Per-category policy
pii / credentials / financial / location / code / public × allow / warn / ask / block. Default fail-closed for credentials and PII.
Pick by tradeoff:
| Sidecar | When to use |
|---|---|
| privacy-filter (Python) | Reference; transformers + PyTorch. ~3 GB resident with model. Easiest to ship and update. |
| privacy-filter-rs (Rust ONNX) | Production. Single Rust binary, ONNX inference via ort. Constrained Viterbi BIOES decoder. Gates every release on --self-test (Viterbi correctness, span extraction, UDS round-trip, envelope shape). |
AVM is not "just regex" — and the ML in AVM is not a replacement for rules. The classify pipeline is a neurosymbolic gate: deterministic rules decide, ML annotates. Same shape Thor's ThorAdvancedClassifier already ships in /thor-refactored/agents-complete/, with the security-critical inversion: in Thor the LLM wins on low rule confidence; in AVM the rule always wins.
ML can restrict (allow → confirm). ML cannot relax (block stays block). Same input always produces the same gate decision regardless of ML availability.
The rule engine computes the gate decision. Always. The ML sidecars run in parallel with a hard timeout and write a hint to the audit row — severity score, rationale string, model version. The hint can downgrade an allow to confirm; it cannot upgrade a block to allow. If the ML is absent, slow, drifted, poisoned, or just wrong, the gate behaves identically to a build with no ML at all. Every block cites a rule. Every test is reproducible.
| Thor advanced classifier | AVM classifier | |
|---|---|---|
| Rule confidence high | use rule | use rule |
| Rule confidence low | fall back to LLM | still use rule; LLM annotates audit |
| LLM disagrees with rule | LLM wins (it's smarter) | rule wins (it's auditable) |
| LLM unavailable | fewer good answers | identical gate behaviour + audit row |
| Operating cost of bad call | worse classification quality | prod data deletion / credential leak |
The cost asymmetry is what flips the rule. A wrong "creative" classification in Thor costs a marginal user-experience point. A wrong "allow" in AVM is a PocketOS incident. ML stays advisory.
Tier 1 · Regex (rules)
91+ injection patterns, 24 PII patterns, 73 destructive vendor mutations, 32 supply-chain patterns. Deterministic, fast, audit-pinned. Catches what we knew was bad on the build date.
Tier 2 · ACLs (rules)
Per-agent tool / file / network allowlists. Deterministic. Authoritative.
Tier 3 · Behavioural (rules)
Per-agent statistical anomaly profile, persisted across restarts. Spikes trigger confirm. Frequentist — no neural net.
Tier 4 · Consequence (rules)
Pattern-based check that the action's intent matches the agent's stated task. Will be ML-augmented in Tier 5.
Tier 5a · openai/privacy-filter
Production. 1.5B-param Apache 2.0 model from OpenAI (released 2026-04-22). Advisory: spans + categories returned to the rule engine, which decides. Eight categories of unstructured PII regex can't reach.
Tier 5b · avm-classifier-7b
Composer shipped (v2.1.1); model in training. The neurosymbolic compose(rule, ml_hint) function lives in src/classifier_advisory.rs with 10 property tests pinning the invariant that ML can never relax a rule block. Qwen 2.5 7B + LoRA model trains on the corpus the avmctl defs generate-adversarial subcommand produces.
Three attack classes the rule tiers cannot reliably catch:
- Paraphrased dangerous commands — novel destructive call against a vendor we don't have a regex for, expressed in normal English by the agent.
- Multi-stage exfiltration — three innocuous-looking commands that compose into credential theft. Each alone passes regex; the plan does not.
- Consequence mismatch — task says "fix login bug", action says "drop production database". An LLM can read both and notice; regex cannot.
The most useful telemetry from this architecture is disagreement. When the rule says allow and the model would have said block, the daemon writes an MlAdvisoryDisagreed row to the audit chain (live in v2.1.1, see audit_logger::EventType). Aggregated over a week, the disagreement set is the queue of attacks the rules are missing. Operators triage, write new regex, ship.
If the sidecar is absent, slow, OOM, or returns malformed output, AVM fails open with audit. An MlAdvisoryUnavailable row goes to the audit log so silent loss of detection capability surfaces in dashboards. The classifier_advisory::compose function is pure and synchronous: any failure path on the ML side converges on MlHint::Unavailable, which the composer treats as identical to "no ML at all" — same gate decision either way.
Five corpus sources, Qwen 2.5 7B + LoRA on a single H100 (Vega pod), eval harness gating release. Scaffold + corpus generator + 28-example held-out test set shipped in v2.1.1; first checkpoint pending.
| Local H100 (Vega pod) | Distributed P2P (RLM-1 path) | |
|---|---|---|
| Time to first checkpoint | ~24 h, 1×H100, LoRA r=16 | ~1 week, ~30 nodes |
| Data sovereignty | full (pod is yours) | partial (workers see redacted shards) |
| Reproducibility | high | medium (worker variance) |
| Best for | first checkpoint, sensitive corpus | scaling iterations |
Recommendation: ship checkpoint 1 on the Vega H100, iterate on P2P. The corpus contains real attack patterns — you don't want them shard-distributed across the network on the first pass.
| Source | Target | How |
|---|---|---|
| Regex corpus mutation | 30K | All 73 destructive patterns + 91 injection patterns × mutations: paraphrase, base64-wrap, eval-wrap, language-translate, add-benign-context. Deterministic from seed; reproducible. |
| Frontier adversarial | 50K | Claude Opus + Grok-4 prompted: "rephrase this destructive command N ways", "give 10 novel ways to delete prod data on Vendor X via REST/GraphQL/CLI". Validated: regex must miss it, frontier model says "yes destructive". |
| Compositional multi-stage | 10K | Sequences (write→chmod→exec, fetch→decode→pipe). Each command alone passes regex; the plan gets labelled consequence_mismatch. |
| Real opt-in agent traces | 10K | Cursor / Claude Code logs, redacted via privacy-filter sidecar before training, labelled by current gate decision. Hardest to source; highest signal. |
| Boring negatives | 100K | Random sample of tests/integration_e2e.rs allow-path commands plus shell history corpora. Without this the model overfits to "everything looks dangerous." |
Held-out test set — never seen during training: PocketOS exact wire format, all litellm post-install scan inputs, every catch in tests/adversarial.rs. Floor metrics: 0.99 recall on the catch set, 0.999 precision on boring-allow, no regression on the 4 documented honest-misses.
Once a checkpoint ships behind ml.classifier.enabled = true, the daemon logs the per-day disagreement-with-rules count and confidence distribution. Two-sigma shifts week-over-week trigger an operator alert; quarterly retraining cycle uses the disagreement set as new training data.
Full plan in AVM_CLASSIFIER.md — eval harness, drift detection, rollout gates.
Each row's hash includes the previous row's full serialisation. One backdated edit invalidates the chain from that row onward.
Every classify, register / deregister, gas debit, install, autoupdate, config reload, and tool_gate decision writes a JSONL row whose prev_hash includes the previous row's serialisation. Tamper-evident: any retroactive edit invalidates the chain from that row onward.
Daily rotation triggered by audit.max_bytes. Rotated archives gzipped via shell-out (no flate2 dep). Retention sweep deletes archives older than audit.retention_days. avmctl audit_archives lists rotated files.
cgroup v2
Per-process CPU / memory / PIDs caps under /sys/fs/cgroup/avm/<name>/. Graceful no-op when not available.
1-Hz sampler
Per-agent /proc/{pid}/{stat,status,io} reader. Computes USER_HZ-aware CPU%, RSS, vsize, threads, I/O bytes. Caps at 100 * num_cpus.
Gas budget
Per-agent metering. Every CPU ms / KB of memory or network / tool call / file op debits a budget. Saturating math; GasExhausted reports the shortfall on overflow.
Persistent state
agent_registry + gas budgets + behavioural profiles snapshotted to ~/.hyperspace/state/. Atomic JSON write (tempfile + rename, mode 0o600). Closes the restart-resets-baseline bypass.
CONNECT-only. Bodies stay encrypted — no MITM. Denied hosts return 403 + audit row.
Closes the bypass where an agent skips the preloader (raw sockets, FFI, child without preloader env) by routing every TLS handshake through a network-ACL-gated proxy.
The proxy gates reachability + audit; the preloader gates payload contents via privacy_egress_ml. Together they're complete.
Speak line-delimited JSON to ~/.hyperspace/avmd.sock. Every typed SDK method maps 1:1 to a method below. Every CLI subcommand wraps one of these.
| Method | Purpose |
|---|---|
| tool_gate | Single-call gate — tool ACL + file ACL + network ACL + classify + egress + tool approvals in one round-trip |
| classify | Raw 16-module destructive pipeline |
| review_output | Symmetric injection scan on tool / sandbox results |
| permission_check_tool | ACL primitive — tool name |
| permission_check_file | ACL primitive — filesystem path |
| permission_check_network | ACL primitive — outbound host |
| Method | Purpose |
|---|---|
| privacy_classify | Regex-only PII / credentials / financial classifier |
| privacy_classify_ml | Regex + Privacy Filter ML ensemble |
| privacy_redact | Replace classified spans with category tokens |
| privacy_egress | Apply egress policy to outbound bytes (regex) |
| privacy_egress_ml | Apply egress policy to outbound bytes (ensemble) |
| privacy_filter_status | Health probe for the ML sidecar |
| Method | Purpose |
|---|---|
| register_agent | Register with framework / pid / uid / tools / tier |
| deregister_agent | Remove from registry |
| agent_status | One agent or list all registered |
| record_credential | Log that an agent accessed a credential |
| clear_agent | Wipe per-agent state |
| agent_profile | Behavioural profile summary |
| Method | Purpose |
|---|---|
| resources_status | Per-agent + system CPU / RSS sampler snapshot |
| resources_attach | Register agent_id → pid for sampling |
| resources_detach | Stop sampling an agent |
| gas_status | Show gas budget for one agent |
| gas_debit | Debit gas (with optional initial_budget) |
| gas_reset | Reset / establish a budget |
| set_rate_limit | Set custom rate limits |
| grant_network | Grant network capability to an agent |
| Method | Purpose |
|---|---|
| approve_tool | Approve a tool for an agent (once / session / permanent) |
| deny_tool | Deny a tool |
| list_approvals | List tool approvals for an agent |
| Method | Purpose |
|---|---|
| ping | Health check — returns "pong" |
| sandbox_status | Active sandbox tiers + hardening flags |
| config_reload | Re-read AVM policy from disk |
| audit_verify | Verify audit-log hash chain |
| audit_rotate | Force-rotate the active log |
| audit_archives | List rotated archives |
| autoupdate_check | Manual signed-binary update probe |
| install | Sandboxed package install with supply-chain protection |
| install_scan | Supply-chain pattern scan over a directory |
| execute | Execute code in a sandboxed environment |
| trace | Get execution trace for a task |
| metrics | Prometheus-format snapshot |
| redact | Redact secrets/PII from text (legacy regex redactor) |
tool_gate — the one call most integrations make. Combines tool ACL, file ACL, network ACL, classify pipeline, egress-ML, and tool approvals in one round-trip.
privacy_egress_ml — redact unstructured PII before the wire.
register_agent — called once per agent process.
install — sandboxed package install with supply-chain guard.
audit_verify — offline integrity check. Reads ~/.hyperspace/audit.jsonl, walks the chain, fails on any prev_hash mismatch.
For the full set, see the source — every method has at least one integration test in tests/protocol.rs.
One binary, every operator workflow. Subcommands are grouped by what they touch: the daemon over its 0o600 socket, or the local filesystem (no daemon needed). Every daemon-touching subcommand wraps one of the JSON-RPC methods above.
Every command takes --json for machine-readable output. avmctl logs --filter X --json | jq + avmctl metrics --json | prom2json are common operator pipelines.
Zero runtime deps (Node 18+ stdlib only). Typed wrappers + escape hatch.
Stdlib only. Same API surface, snake_case to match Python idioms.
Hardened: NoNewPrivileges, ProtectSystem=strict, ProtectHome=read-only with ~/.hyperspace as the one writable path, PrivateTmp, PrivateDevices, Protect{KernelTunables,KernelModules,KernelLogs,ControlGroups,Clock,Hostname}, RestrictAddressFamilies, RestrictNamespaces, MemoryDenyWriteExecute, RestrictRealtime, SystemCallFilter=@system-service ~@privileged ~@mount ~@reboot ~@swap. MemoryHigh=512M, MemoryMax=1G.
Off by default. When enabled, no code download proceeds without signature verification.
avmd pulls a signed manifest, verifies its Ed25519 signature against a compile-time-pinned public key, downloads the matching platform binary, sha256-verifies, atomically swaps current_exe(), exec-replaces. No reqwest / native HTTP stack — shells out to curl.
| Suite | Count | Purpose |
|---|---|---|
| lib unit tests | 458 | Per-module unit tests (audit, classify, gas, sandbox, ...) |
| avmctl unit tests | 16 | argv parsing, hex round-trip, schema valid-JSON, predict_action |
| destructive vendor matrix | 22 | One regression test per vendor whose destructive APIs we gate |
| adversarial / red-team | 17 + 4 ignored | Obfuscation-bypass corpus: eval, base64-pipe-shell, curl-pipe-shell, printf-hex, python -c, node -e, IFS, X=rm, PowerShell -enc, plus 4 honest-misses |
| integration_e2e | 6 | Boots a real avmd binary in a temp HOME and verifies wire contracts (ping, classify-blocks-rm-rf, tool_gate-blocks-PocketOS, audit-verify, metrics, persistence-survives-restart) |
| toctou | 7 | Multi-stage / TOCTOU: write-then-exec, base64-to-shell wrapper, curl-piped-bash, eval-curl, install_scan against fake site-packages |
| avmd-mcp sidecar | 10 | Tool catalog, dispatch, error paths, stub-RPC routing |
| privacy-filter-rs Viterbi | 5 | BIOES decoder, span extraction, constraint repair |
| privacy-filter-rs --self-test | 4 | Wire-protocol round-trip, envelope shape (gates every release) |
| Python SDK | 9 | Stub-server round-trips for ping, tool_gate, error paths, classify, egress |
| TypeScript SDK | 8 | Same shape against a stub Unix-socket server (node:test) |
| fuzz harness | 4 targets | cargo-fuzz: classify never panics, tool_gate never panics, policy_lint never panics, audit_chain never panics |
Bench numbers from cargo bench --bench classify on a recent x86_64 host. Each input is a complete classify call through the full 16-module pipeline running on a single thread.
p50 ~3 µs, p99 ~8 µs, ~200–300k classifies/sec single-threaded. Adding AVM to a Cursor session adds ~10 µs of latency to every shell command and HTTP call the agent issues.
- Idle: ~12 MB RSS
- 1k classifies/sec sustained: ~14 MB RSS
- Binary on disk: ~2 MB stripped (opt-level="z", lto=true, codegen-units=1)
STRIDE-style coverage in THREAT_MODEL.md. Headlines:
Trusted base
The user's kernel, the avmd binary (after sha256 + Ed25519 signature verification), the user's local disk for the audit chain, libonnxruntime when the ML sidecar is enabled.
Not trusted
The agent (any agent), the agent's framework, the model behind the agent, system-prompt-only safety rules, outbound API surfaces, inbound tool results.
- Pure cloud-internal agent reasoning — AVM gates the local boundary; in-cloud planning is by design out of scope.
- Kernel exploits. A use-after-free in the network stack bypasses every userland gate. Defence in depth: keep the kernel patched.
- Hardware attacks. Rowhammer, side channels, evil-maid attacks on the disk.
- Adversarial users on shared hosts. Multi-tenant Linux box with one user attacking another — that's an OS user/group permissions problem.
- Determined social-engineering of OOB confirmation. A user who clicks "yes" to every prompt has socially engineered themselves.
- Model-level jailbreaks the model never expresses as actions. A model that chooses inaction in response to manipulation isn't visible at the syscall layer.
avmd collects nothing by default. No analytics service. No anonymous deployment ID. No audit-log upload. No phone-home. Enforced by absence of code, not a flag.
If a future avmd release adds opt-in telemetry, the contract in TELEMETRY.md says it must be: off by default, aggregate-only (never per-event), endpoint configurable in policy, every POST audit-rowed, avmctl telemetry pause + erase operator controls. Audit logs / policy snapshots / PII / crash dumps are never in scope.
Local AVM gates what touches the user's machine. When a vendor hosts an agent runtime (Claude Code in claude.ai, OpenAI Assistants), the cloud agent also takes actions inside the cloud that local avmd never sees.
The design hold for a future server-side companion lives in CLOUD_DAEMON.md. Shared contracts: policy schema, audit row format, RPC method names, signed defs format, privacy posture. What avmd-cloud must NEVER do: ingest local audit logs, relax local policy, share telemetry cross-tenant, run untrusted code on shared kernel.
Single Rust binary that wraps the avmd Unix-socket protocol as a Model Context Protocol tools/* interface. One JSON entry in Cursor / Claude Code settings; no preloader required.
| MCP tool | avmd RPC |
|---|---|
| avm.gate | tool_gate |
| avm.classify | classify |
| avm.privacy_egress | privacy_egress_ml |
| avm.review_output | review_output |
| avm.audit_verify | audit_verify |
| avm.status | sandbox_status |
policies/strict.json
Fail-closed everywhere. tools.default = "deny". network.allow_domains = []. privacy.ml.fail_open = false. CI runners, untrusted-agent setups.
policies/permissive.json
Sane defaults for a developer's workstation. Reads + writes allowed inside workspace, network egress allowed except private ranges, destructive ops blocked or require approval. PocketOS-style still gets caught.
Operators can ship custom destructive patterns / network rules / PII patterns / tool safety overrides via ~/.hyperspace/avm-defs/current.json. avmd verifies the Ed25519 signature, schema-version-pins to [MIN_SCHEMA, MAX_SCHEMA], semantic-validates (every regex compiles, every level ≤ 4, every action in {allow, deny, require_approval}), and persists a last-known-good snapshot. Bad reload → previous defs stay active.
cargo-fuzz harness with four targets covering the surfaces that take untrusted input. None of these should ever panic for any input.
| Target | Property |
|---|---|
| classify_does_not_panic | Random UTF-8 → classify never panics, never returns level > 4 |
| tool_gate_does_not_panic | Random ToolGateParams shape → classify + privacy + ACL helpers don't panic |
| policy_lint_does_not_panic | Random JSON → policy_lint either rejects parse OR runs all rules cleanly |
| audit_chain_does_not_panic | Random bytes as audit log → verify_chain returns Ok / Err, never panics |
Honesty about limits is non-negotiable for a security product. The regex tier alone cannot catch:
- Base64-decoded payloads that do something bad. The wrapper (base64 -d | bash) catches; the decoded bytes don't. Mitigation: sandbox tier (Docker/process) bounds blast radius even on a successful decode.
- libc::execve via FFI. A model that calls into a native library bypasses the bash regex bank. Mitigation: seccomp deny list + sandbox tier.
- Unicode confusables. An agent typing a Cyrillic е for Latin e defeats string match. Mitigation: the preloader normalises before classify; raw input doesn't get the benefit. Behavioural anomaly will still flag if the agent's shell history doesn't contain confusables.
- Multi-stage with innocuous bytes. touch /tmp/x && chmod +x /tmp/x && /tmp/x — each step is harmless; the plan isn't. Mitigation: consequence engine + (in development) avm-classifier-7b action classifier reads task + action and flags mismatch.
- Pure cloud-internal agent reasoning. Planning that never produces a tool call to the user's box is by design out of scope. The cloud-side companion is sketched in CLOUD_DAEMON.md.
- Vendor-side abuse with the vendor's own credentials. If the agent's API key has admin-level scope on a cloud platform, AVM can refuse the call from the user's box, but it cannot prevent a leaked key from being used elsewhere. Mitigation: credential_provenance module logs every credential read so revocation is targetable.
- Bugs in AVM itself. The classify pipeline is ~16 KLOC of Rust with 554 tests; defects are inevitable. Hash-chained audit means a defect that lets an attack through still leaves an evidence trail. Responsible disclosure to security@hyper.space with 90-day embargo.
The full set of catches and explicitly-documented misses lives in FEATURES.md and tests/adversarial.rs. Every honest-miss has an audit-pinned regression test — the contract is that they remain misses or become catches, never silently start auto-allowing.
Q3 2026 · avm-classifier-7b first checkpoint
Qwen 2.5 7B + LoRA fine-tune for action classification. Training scaffold + 28-example held-out set is shipped today (v2.1.1); next is the actual training run on the Vega H100 once corpus collection hits 100K labelled examples. See Training.
Q3 2026 · microVM sandbox tier
Add a fourth sandbox tier above Docker: Firecracker microVM per agent task. Closes residual container-escape risk; requires only operators who want extra-strong isolation to flip a config field. Roadmap target tied to first vendor-deploying-AVM-as-default ship.
Q4 2026 · cloud daemon (avmd-cloud)
Server-side companion that gates a cloud agent's cloud-internal calls (third-party APIs, cloud worker spawn) the same way local avmd gates the boundary into the user's box. Design lives in CLOUD_DAEMON.md; build gated on first vendor partner.
2027 · vendor adoption
Goal: by end of 2027, every major AI agent platform ships AVM (or an AVM-compatible gate) by default, with a shared open audit format. The way every browser ships V8 today.
Track progress against the public commit log: github.com/hyperspaceai/p2pnetwork/commits/master/packages/avmd. Slipped dates get an honest writeup in the next changelog entry rather than a quiet shuffle.
Does AVM slow my agent down?
~3 µs p50, ~10 µs p99 per classify call (single-threaded). On a typical agent that issues 1-10 tool calls per minute, AVM adds well under 1 ms of total latency. The tail-latency budget is in BENCHMARKS.md with reproduction recipe.
What if AVM breaks my workflow?
Flip enabled: false in ~/.hyperspace/avm-policy.json; restart the daemon; AVM is a no-op. For finer control, run in audit-only mode (enforcement: log_only) — classify still writes audit rows, but never blocks. Recommended for the first week of any deploy: see what the gate would have caught before flipping enforcement on.
BSL 1.1 — can I use AVM commercially?
Yes. BSL 1.1 with a Hyperspace Use Limitation: you cannot resell AVM as a hosted competing service. Internal use, integration with your own agent product, vendoring into commercial software, all permitted. The licence converts to Apache 2.0 four years after each release.
How do I roll my own release-signing key?
Replace RELEASE_PUBKEY_HEX in src/autoupdate.rs with your own pubkey, set AVMD_RELEASE_PRIVATE_KEY as a build secret, build, ship to your fleet. Existing installs cannot auto-update from your fork (intended — trust roots don't share). Full procedure in the release-keys README.
What about Apple Silicon vs Intel Mac?
Apple Silicon (darwin-arm64): full support. Intel Mac (darwin-amd64): not in the release matrix — macos-13 GitHub Actions runners are deprecated and frequently leave the build queue indefinitely. Build from source on Intel Mac with cargo build --release; everything works, the published tarball just doesn't ship.
Does AVM need root?
No. The default install runs as a user systemd unit (or LaunchAgent on macOS). seccomp, cgroup v2, and rlimits all work in user-space modes; what's missing without root is full cgroup enforcement (memory caps degrade to monitoring + warn). For root-mode hardening, the systemd unit can be moved to system-level — see deploy/linux/avmd.service.
How do I see what AVM blocked yesterday?
avmctl logs --since 1d --filter "action=block" — readable JSONL, one decision per line, hash-chained against the previous row. Run avmctl audit verify to confirm the chain is intact (offline, no daemon required).
What does the ML sidecar phone home to?
Nothing. Both ML sidecars (openai/privacy-filter and the in-development avm-classifier-7b) run as separate processes on the user's host, talk only to avmd over a 0o600 Unix socket. Model weights are local. No telemetry. Verifiable in PRIVACY.md.
Three ways every agent runtime integrates: preloader (env var), SDK (npm/pip), or MCP (settings.json). Pick the one that matches your runtime; copy-paste the snippet; you're done.
The cleanest path. One JSON entry adds avmd as an MCP server; every tool call the assistant makes routes through it.
Six tools become available: avm.gate, avm.classify, avm.privacy_egress, avm.review_output, avm.audit_verify, avm.status. The assistant calls avm.gate before any other tool; if the gate denies, the call is refused at the model layer.
Nothing to do. hyperspace start spawns avmd as a child process automatically; the daemon picks up its policy from ~/.hyperspace/avm-policy.json on first start. Inspect with hyperspace avm status.
Speak JSON-RPC over the 0o600 Unix socket directly. The wire format is line-delimited JSON; every method documented in the RPC reference takes plain JSON params. Three lines of bash:
Local-first · never phones home
The audit chain lives on your disk. Telemetry — if any — is per-user opt-in via avmctl telemetry enable. Default is off. Verify in TELEMETRY.md.
Reproducible signed releases
Ed25519-signed manifest committing per-asset sha256. The signing private key is held off-tree (encrypted backup at packages/avmd/.release-keys/avmd-release-key.hex.enc) + GitHub Actions secret. The compile-time pubkey baked into autoupdate.rs is the trust root.
Open-source and auditable
Every line of avmd is in this repo (BSL 1.1, converts to Apache 2.0 four years after each release). The 554-test suite is your floor: read tests/ before installing.
Responsible disclosure
security@hyper.space. PGP key in THREAT_MODEL.md. 90-day embargo for confirmed vulns; CVE assignment for severity ≥ medium. No bug bounty currently — if that changes, this card updates.
| Document | What's in it |
|---|---|
| README.md | Install + use + RPC surface |
| INTEGRATION.md | Vendor playbook — how to ship with AVM |
| FEATURES.md | What AVM catches; honest misses |
| PRIVACY.md | Privacy posture, layer by layer |
| THREAT_MODEL.md | STRIDE coverage, trusted base, out of scope |
| BENCHMARKS.md | Published latency / throughput numbers + reproduction |
| TELEMETRY.md | What avmd collects (nothing, by default) |
| CLOUD_DAEMON.md | Design hold for a future server-side companion |
| CHANGELOG.md | Release history |
| policies/ | strict + permissive reference policies |
- Bundled in the CLI release tarball: every curl agents.hyper.space/cli | bash ships avmd + avmctl alongside the other sidecars. No second download at first start.
- AvmBinaryManager rewritten to fetch from releases.hyper.space with sha256 verification against the signed manifest. Replaces the old hardcoded URL pointing at a long-stale repo + version pin (avmd-v0.1.1 on hyperspaceai/agi) that had been silently 404ing on every existing CLI install.
- Auto-delivery to existing nodes: CLI's own auto-update path (post v5.39.7/8 fixes) bumps existing installs from v5.39.x to v5.40.0; next hyperspace start picks up avmd via the working URL.
- Visible failure mode: hyperspace start emits an error event if avmd fails to start, instead of silently swallowing.
- Wider distro support: linux matrix builds switched from ubuntu-latest (24.04, GLIBC 2.39) to ubuntu-22.04 (GLIBC 2.35). v2.1.1 binaries failed to start on Ubuntu 22.04, Debian 12, RHEL 9, Amazon Linux 2023.
- v2.1.2 binary's max GLIBC requirement is 2.34 — verified end-to-end on Ubuntu 22.04: ping, gate, audit, clean shutdown.
- No code change.
- Ed25519 release-signing — manifest signed with AVMD_RELEASE_PRIVATE_KEY; pubkey baked into autoupdate.rs at compile time
- releases.hyper.space CDN stood up on Vercel, attached domain, mirrors every release artefact (manifest + tarballs + install.sh)
- release-avmd workflow automatically publishes to the CDN after each tag push; signature verifies end-to-end against the deployed daemon's VerifyingKey
- encrypted off-tree backup of the release seed (AES-256-CBC + PBKDF2 600k iters); passphrase held by operator
- (v2.1.0 published the same code but with an empty-signature manifest because the secret was unset; superseded immediately)
- Neurosymbolic invariant codified in code: classifier_advisory::compose(rule, ml_hint) is pure / synchronous / no I/O. 10 property tests pin: blocks never relax, Unavailable hint passes rule through, no (rule, hint) pair upgrades to Allow.
- tool_gate Stage 7 (ML advisory) — severe ML hint downgrades allow to confirm; never the other way
- New audit events: MlAdvisoryDisagreed (drift signal), MlAdvisoryUnavailable (silent capability loss)
- Synthetic adversarial corpus generator: avmctl defs generate-adversarial --count N --seed S, deterministic, 5 mutation primitives, JSONL output
- Training scaffold: training/ with LoRA recipe, eval harness, leak checker, ONNX export, 28-example held-out test set (PocketOS / litellm / adversarial.rs catches / honest misses)
- 22 new tests (10 classifier_advisory property tests, 10 corpus tests, 2 integration tests). Lib total: 478 passing.
- aarch64 seccomp — ARM64 Linux deny list (18 syscalls with arm64 numbers)
- behavioural_detector persistence — profiles snapshot survives restarts
- avmd-mcp tests (10), audit gzip rotation test
- avmctl logs / policy explain / policy schema / defs sign / defs verify
- JSON schema for the policy file (editor autocomplete)
- TypeScript SDK (@hyperspace/avm-client, zero deps, 8 tests)
- Python SDK (hyperspace-avm-client, stdlib only, 9 tests)
- agent-computer bridge — opt-in tool_gate before exec
- cargo audit + coverage in CI
- CLOUD_DAEMON.md design hold
- schema_version validation, semantic checks (regex compile, level range), last-known-good snapshot
- 7 TOCTOU integration tests covering catches + audit-pinned honest misses
- CONNECT-method gate closes the bypass for raw-socket / FFI agents
- 17 adversarial regex catches + 4 documented honest-misses
- GitHub Actions release workflow with Ed25519-signed manifest
- policy_lint with 11 rules; THREAT_MODEL.md
- Atomic JSON snapshots for agent_registry + gas budgets (closes restart-bypass)
- Privacy-filter sidecar lifecycle supervision (auto-restart with backoff)
- Six end-to-end integration tests booting a real avmd
- criterion benchmarks for the classify pipeline
- Prometheus-format metrics (RPC counts, classify actions, latency histogram)
- Daily audit-log rotation with retention sweep
- avmd-mcp Model Context Protocol server — one config entry for Cursor / Claude Code
- strict + permissive reference policies
- Tool_gate single-call vendor RPC; production README
- Python preloader (subprocess + urllib + requests + httpx hooks); INTEGRATION.md
- Production deploy: avmctl + systemd + launchd + curl-able installer
- Signed binary auto-update (Ed25519 manifest + atomic swap)
- output_review (symmetric injection scan) + budget gas accounting
- PocketOS-grade destructive detection + 22-vendor matrix
- Rust ONNX privacy-filter sidecar promoted to production via --self-test
- seccomp-bpf + privilege drops + sandboxed install RPC + privacy_egress_ml
- resources/ port: cgroup v2 + governor + process
- OpenAI Privacy Filter sidecar protocol
- Initial a1-blockchain-hyperspace AVM modules merge
- avm.install: pip / npm install runs inside Docker with --cap-drop ALL, --read-only, no host secret mounts
- Lockfile sha256 verification
- 32-pattern post-install behavioural scanner
- avm.install.scan audits existing site-packages / node_modules
- 5-layer injection scanner with 91 compiled regexes
- Privacy classifier: 27 detection patterns + Luhn validation
- Resource governor: user-set CPU / memory / disk / network caps
- Sandbox tiers: process / Docker / direct fallback
- Approval flow with 5-min timeout auto-deny
- One config: ~/.hyperspace/avm-policy.json
Full changelog: CHANGELOG.md