ResearchSecurityIsolation Architecture

Agent Isolation Architecture: The Four-Layer Security Model

Status: Open — research complete, design approved, implementation sequenced

What This Document Is

This is the master security architecture document for jackin. It explains why jackin needs a layered isolation model, how we arrived at each decision through research and verification, what each layer solves, and how the layers compose into a complete security story equivalent to — and in operator experience, better than — Docker Sandboxes.

Every design decision here was reached through a deliberate research trail starting from the question: "we use Docker today and it seems fine — why would we need to change anything?" This document answers that question honestly.


Part 1: How We Arrived Here — The Research Trail

The Starting Point

jackin launched as a Docker-first orchestration layer for AI agents. The initial security model was practical and correct for a proof-of-concept:

  • explicit workspace bind mounts (only operator-approved paths)
  • host Docker socket excluded from agent containers
  • per-agent Docker network
  • TLS-authenticated DinD sidecar for Docker workflows inside the agent

That model is already better than a developer container with the host socket mounted. The question in June 2026 was: "is it good enough, and if not, where are the real risks?"

Step 1: Researching OrbStack Isolated Machines

The first candidate for improvement was OrbStack isolated machines — a macOS-native feature that creates machines without the default full-Mac-filesystem passthrough. The hypothesis was that an isolated machine would give each jackin session a stronger boundary.

What we found: OrbStack isolated machines share OrbStack's Linux kernel. They provide namespace isolation within the existing OrbStack VM — cgroups and namespaces, not a hypervisor boundary per workload. For the current audience (100% macOS ARM operators who already use OrbStack as their Docker engine), the role container already lives inside OrbStack's Linux VM. An isolated machine would add a namespace layer on the same kernel.

Conclusion: OrbStack isolated machine backend deferred. Namespace isolation within a shared kernel does not address the kernel escape risk that motivates VM-level separation.

Step 2: Researching the Actual Threat

After the OrbStack deferral, the question became: what exactly is the threat that shared-kernel containers cannot address?

Research into the 2025–2026 CVE landscape established concrete evidence:

runc vulnerabilities (November 2025): Three high-severity CVEs allowing container escape to the host system, affecting Docker, containerd, and every managed Kubernetes service. The attack path starts inside a container and reaches the host kernel.

CVE-2026-34040: Docker authorization bypass via oversized HTTP requests — allows unauthorized creation of containers with full host access. Fixed in Docker Engine 29.3.1.

CVE-2024-1086: Use-after-free in Linux's netfilter subsystem. Confirmed actively exploited in ransomware campaigns (CISA advisory, October 2025). Reachable from inside a container.

DinD --privileged: jackin runs a privileged DinD sidecar today. A --privileged container has near-full access to the host kernel — in jackins case, OrbStack's Linux VM kernel. Recent attack chains have started from legitimate AI agent setups that followed Docker's own security recommendations.

The shared-kernel insight: The attack surface for all of these is the kernel. Every jackin session shares one OrbStack Linux VM kernel. A successful kernel exploit in one session reaches all sessions. This is not a configuration problem — it is structural.

Step 3: Mapping the Full Problem Space

Concurrent with the kernel research, a second problem emerged from operator experience: credential exposure.

The current jackin model injects agent credentials (Claude API key, GitHub token, SSH keys) into the role container as environment variables or synced files. This means:

  • The agent process can printenv and read raw API keys
  • The agent can read credential files from the agent home mount
  • The agent retains credential values in its context window across prompts
  • Multiple agents sharing a workspace can access each other's credentials if one is compromised
  • An agent prompted to exfiltrate data can extract raw token values

This is not a kernel-level problem. It exists regardless of whether the backend is Docker, OrbStack isolated, or a full microVM. A VM boundary gives each session its own kernel — but if the API key is an environment variable inside that VM, the agent can still read it.

The two problems are independent and require independent solutions.

Step 4: Researching the Microvm Landscape

With the kernel boundary problem defined, we researched what provides a true per-workload kernel boundary on macOS ARM.

Firecracker: requires KVM (Linux-only). On macOS ARM, needs nested virtualization inside a Linux VM — not officially supported by the Firecracker project. Not viable.

gVisor: syscall interception in userspace — no KVM required. But confirmed broken on OrbStack macOS ARM (orbstack/orbstack#2362/tmp → /private/tmp symlink causes runsc to crash immediately). Claude Code hangs indefinitely in gVisor on macOS ARM (anthropics/claude-code#35454). Not viable on current platform stack.

smolvm: uses Hypervisor.framework on macOS and libkrun on Linux. Pre-1.0. Has a documented Docker-in-VM path (v0.7.0, requires Alpine base + ext4 bind mount + virtio-net). Deferred in June 2026 — Apple Container is the primary macOS 26 VM track (zero install friction, gRPC/vsock attach, Virtualization.framework). smolvm remains the fallback if Apple Container Phase 0 fails the Docker-inside test. See smolvm backend research.

Docker Sandboxes: productized microVM runner from Docker Inc. (launched January 2026). Runs each agent session in a dedicated VM using a proprietary VMM on Apple's Hypervisor.framework. Has a built-in credential proxy and network proxy. However: not a backend API for jackin to build on — it is a complete agent runner that replaces jackins role (see Part 3).

Apple Container framework (macOS 26 Tahoe, WWDC 2025, apple/container v0.11.0 March 2026): each OCI container runs in its own dedicated lightweight VM via Virtualization.framework, natively on Apple Silicon. Sub-second start times. Own kernel per container. Own IP per container via vmnet. OCI-compatible. CLI with Docker-compatible surface (container run, container exec, container stop, container rm). PID 1 is vminitd — a minimal Swift static binary that exposes a gRPC API over vsock for process management and I/O streaming. Zero install friction on macOS 26.

Key finding for Apple Container: --privileged is not supported. The documented alternative is --cap-add <CAP> for specific capabilities. This means the current jackin DinD sidecar (which requires --privileged) cannot run inside an apple/container VM without changes. Rootless DinD — which replaces --privileged with specific capability grants — is the compatibility path, but requires the Docker runtime hardening contract work to complete first.

Step 5: Recognizing That VM Isolation Alone Is Not Enough

After establishing Apple Container as the right VM primitive, a third problem surfaced: even inside a perfectly isolated VM with its own kernel, an agent's individual tool calls are unconstrained.

When an agent runs npm install, that subprocess inherits every secret in the container's environment, can write to every writable path, and can reach every network endpoint. The VM boundary prevents kernel escapes — it does not prevent an agent from reading a secret it was given or writing to a path it should not touch.

This is the gap that process-level sandboxing fills. Research into Zerobox — the open-source library powering OpenAI Codex CLI's sandbox mode — confirmed that per-operation isolation is achievable with 10 ms overhead per invocation, using OS-native primitives (Bubblewrap + Seccomp + Landlock on Linux, Apple Seatbelt on macOS).

Step 6: The jackin-exec Insight

The credential exposure problem requires a different mechanism than both VM isolation and process sandboxing. The question is: how does an agent run ssh sentry without jackin injecting the SSH key into the container's environment?

The answer: a two-tier workspace env model and a jackin-exec subcommand of jackin-capsule.

Two env tiers — same env map, one new flag:

  • { op = "...", path = "..." } (default): injected into container at launch, all processes see the resolved value
  • { op = "...", path = "...", on_demand = true }: NOT injected at launch, NOT visible via printenv; appears in a TUI picker dialog when the agent calls jackin-exec, operator selects which credentials to attach, host resolves via op read at that moment, injected ephemerally for the command duration

jackin-exec = jackin-capsule exec: a symlink, not a separate binary. The agent calls jackin-exec gh pr create; the capsule subcommand shows the on-demand picker, resolves selected credentials via the host process, injects them as temp env vars, runs the command, scans output for leaked values, deletes temp values. Raw credential values never appear in the container's process environment.

This is the same architecture Docker Sandboxes uses for its host-side credential proxy — except jackin builds it explicitly, with a TUI-native picker dialog and full operator control.


Part 2: The Four-Layer Security Model

The research trail produced four independent, complementary isolation layers. Each layer closes a gap the others cannot. All four are needed for the complete picture.

┌─────────────────────────────────────────────────────────────────┐
│  Layer 4: Network Egress Policy                                  │
│  (per session, domain allowlist, enforcement-quality reporting)  │
├─────────────────────────────────────────────────────────────────┤
│  Layer 3: jackin-exec Credential Injection                       │
│  (per command, secrets never in agent context)                   │
├─────────────────────────────────────────────────────────────────┤
│  Layer 2: Apple Container VM Boundary                            │
│  (per session, own kernel via Virtualization.framework)          │
├─────────────────────────────────────────────────────────────────┤
│  Layer 1: Docker Hardening Contract                              │
│  (per session, rootless DinD, capability policy, read-only root) │
├─────────────────────────────────────────────────────────────────┤
│  Cross-layer: Process-Level Sandboxing (zerobox)                 │
│  (per operation, filesystem + network + env restriction)         │
└─────────────────────────────────────────────────────────────────┘

What Each Layer Closes

ThreatWithout any layerLayer 1 (Docker hardening)Layer 2 (Apple Container VM)Layer 3 (jackin-exec)Cross-layer (zerobox)Layer 4 (egress policy)
DinD --privileged → kernel access❌ open✅ rootless DinD eliminates✅ own kernel, --privileged blocked by design
Kernel escape via CVE❌ open → reaches OrbStack VM kernel → all sessionsPartial (fewer caps, hardened profile)✅ kernel escape reaches only this VM
One session reaching another session's state❌ shared OrbStack VM kernelPartial✅ separate kernel per session
Agent reads raw API key / SSH key❌ env var visible✅ jackin-exec never injects to env
Agent's tool call reads unintended file✅ per-op filesystem policy
Agent's tool call calls unintended endpointPartial (per-agent network)Partial (profile reporting)✅ per-op network allowlist✅ session-level allowlist
Agent exhausts CPU/memory/PIDs✅ resource limits in hardened profile
Agent exfiltrates data over networkPartial (reporting only)Partial (per-op deny)✅ session allowlist enforced
Mutable root filesystem✅ read-only root in hardened profile

The Tiers at a Glance

Overhead    Granularity       Layer
─────────────────────────────────────────────────────────────────
< 10 ms     per operation     Process sandbox (zerobox + jackin-exec)
100–500 ms  per session       Container hardening (Docker profiles)
< 1 s       per session       Apple Container VM boundary

Part 3: Why Apple Container, Not Docker Sandboxes

This comparison matters because Docker Sandboxes and Apple Container appear to address the same problem. They do not. Understanding the difference is essential for understanding why jackin builds on Apple Container instead of integrating with Docker Sandboxes.

What Docker Sandboxes Is

Docker Sandboxes (launched January 2026) is a complete agent runner. It runs Claude Code, Codex CLI, GitHub Copilot CLI, and other supported agents in a dedicated microVM. Each sandbox has:

  • A dedicated VM via a proprietary VMM (Hypervisor.framework on macOS)
  • A built-in private Docker daemon
  • A host-side credential proxy (secrets never enter the sandbox)
  • A host-side network policy proxy (domain allowlist, raw TCP/UDP blocked)
  • Explicit workspace mounts (same absolute host path)

Docker Sandboxes is designed to answer: "run this agent for me, safely." The operator hands the agent to Docker, and Docker manages the isolation, credentials, and network policy.

What Apple Container Is

Apple Container (apple/container, macOS 26 Tahoe) is a VM primitive. It runs any OCI container image in its own VM via Virtualization.framework. It provides:

  • A VM per container with its own kernel
  • An OCI-compatible run/exec/stop/rm/ps CLI
  • A structured gRPC/vsock API via vminitd for I/O streaming and signal forwarding
  • Per-container IP address via vmnet

Apple Container is designed to answer: "here is a VM primitive, build on it." jackin builds its own credential proxy (jackin-exec), network policy (egress policy), session model, role system, and TUI on top of this primitive.

Why jackin Needs a Primitive, Not a Product

jackin is itself an orchestration layer. It manages roles, workspaces, session reconnect, TUI, multiple agent runtimes, and operator UX. Integrating with Docker Sandboxes would mean jackin becomes subordinate to Docker's product decisions — operators would use Docker Sandboxes to run agents, and jackin would have no role.

More concretely: Docker Sandboxes CLI (as of May 2026) has no events stream, no --format json, no per-sandbox stdout/log hooks. It is intentionally designed as an agent runner, not a backend API. It cannot serve as the programmatic substrate for jackins lifecycle management.

The Comparison Table

CapabilityDocker Sandboxesjackin + Apple Container (full stack)
VM per session (own kernel)✅ proprietary VMM✅ Virtualization.framework
Private Docker daemon (no --privileged)✅ built-in✅ rootless DinD inside VM (requires Docker hardening)
Credentials never in sandbox✅ host-side credential proxy (built-in)✅ jackin-exec (jackin builds this)
Network domain allowlist✅ host-side proxy (built-in)✅ egress policy (jackin builds this)
Per-operation filesystem/network policy✅ zerobox (jackin builds this)
TUI-native operator approval UIjackin owns the terminal
Role system (Dockerfile-based)❌ fixed agent runners✅ full role authoring
Session reconnect / eject / purge
Multiple agent runtimes (Claude, Codex, Amp, Kimi, OpenCode)7 fixed✅ unlimited
Workspace-aware behavior
Operator-controlled secret bindings✅ jackin-exec workspace bindings
macOS 26 install frictionRequires Docker Desktop✅ zero — built into macOS 26
Open source (VM layer)❌ proprietary VMMapple/container (open source)

jackin + Apple Container full stack reaches Docker Sandboxes security parity while preserving jackins own operator experience. Docker Sandboxes is the benchmark for what the security story should look like; Apple Container is the primitive jackin uses to build that story on its own terms.


Part 4: The Progressive Enhancement Path

The full four-layer model is built incrementally. Each step is independently valuable. Operators benefit immediately from each layer as it lands.

Today: Current Docker + OrbStack (Baseline)

macOS ARM → OrbStack Linux VM → Docker daemon → --privileged DinD + role container

Risks:

  • DinD --privileged → kernel access to OrbStack VM
  • Shared kernel → all sessions share one attack surface
  • Credentials in env vars → agent can read raw API keys
  • No per-operation access control
  • Open network egress

Step 1: Docker Hardening Contract (In Progress)

macOS ARM → OrbStack Linux VM → Docker daemon → rootless DinD + role container
                                                  (capability policy, read-only root,
                                                   resource limits, profile reporting)

What changes:

  • DinD sidecar moves to rootless mode (no --privileged) or uses minimal capability set
  • hardened profile: --cap-drop=ALL + minimal caps, read-only root, resource limits
  • Session contract output: operators see exactly what was enforced

What remains:

  • Shared OrbStack VM kernel (structural)
  • Credentials still in env vars
  • No per-operation isolation
  • Open egress

See: Docker runtime hardening contract

Step 2: Apple Container VM Boundary

macOS ARM → apple/container VM (own kernel, Virtualization.framework)
              → rootless DinD + role container + jackin-capsule

What changes:

  • Each jackin session gets its own Linux kernel via Apple's hypervisor
  • Kernel exploit in one session cannot reach other sessions or the host
  • OrbStack VM no longer in the critical path for session isolation
  • Zero install friction on macOS 26 (built into the OS)
  • gRPC/vsock attach via vminitd — structured I/O and signal forwarding for TUI sessions

What remains:

  • Credentials still in env vars (jackin-exec not yet present)
  • No per-operation isolation
  • Open egress

Prerequisite: Docker hardening's rootless DinD must validate successfully inside apple/container VMs (specifically: does CAP_SYS_ADMIN work inside apple/container for rootless DinD mount namespaces?). This is the Phase 0 gate.

See: smolvm backend research (contains the Apple Container comparison and Phase 0 plan)

Step 3: jackin-exec Credential Injection

macOS ARM → apple/container VM (own kernel)
              → rootless DinD + role container + jackin-capsule
                → agent calls: jackin-exec ssh sentry
                  → jackin-capsule intercepts
                  → operator approves in TUI (optional)
                  → SSH key resolved from host vault at execution time
                  → command runs with injected key (temp file, /jackin/run/)
                  → output scanned for secret patterns, redacted
                  → sanitized output returned to agent
                  → temp key file deleted

What changes:

  • Agent never receives raw credential values in env vars or files
  • Operator gets TUI-native approval dialog for sensitive commands
  • Workspace secret bindings map command patterns to host-side vaults (op://, env:, file:)
  • Output filtering redacts PEM blocks, AWS key patterns, configured regexes

What remains:

  • Agent's general tool calls (npm install, etc.) still unconstrained inside container
  • Open egress for non-jackin-exec commands

See: Process-level sandboxing (jackin-exec design), Container credential exposure, Host bridge

Step 4: Process-Level Sandboxing (zerobox) + Network Egress

macOS ARM → apple/container VM (own kernel)
              → rootless DinD + role container + jackin-capsule
                → every agent tool call:
                    → zerobox: filesystem policy (declared mounts only)
                               network policy (declared hosts only)
                               env policy (no leaked secrets)
                    → jackin-exec: credential injection for sensitive commands
                → session-level egress policy: allowlist enforced at VM boundary

What changes:

  • Every subprocess wrapped by zerobox: reads/writes only declared mount paths, reaches only declared network hosts, cannot see env vars not explicitly passed
  • Session-level network allowlist enforced at VM egress
  • Full audit trail: every operation, every allow/deny decision, logged to diagnostics run file

This is the complete picture. No remaining open risks in the standard macOS ARM + trusted-operator threat model.

See: Process-level sandboxing, Network egress policy


Part 5: What Each Layer Costs

Understanding the cost of each layer prevents premature optimization and guides sequencing.

LayerOperator frictionImplementation complexityPrerequisites
Docker hardening contractLow — profiles are opt-in, compat preserves todayMedium — profiles, capability policy, rootless DinD researchNone — in progress
Apple Container VM boundaryZero on macOS 26 (built-in)High — new backend abstraction, lifecycle API, Phase 0 empirical testingDocker hardening (rootless DinD) must validate first
jackin-exec credential injectionLow — system prompt instructs agent; approval dialog is contextualMedium — binary, vsock protocol to capsule, workspace bindings schemajackin-capsule vsock/Unix socket extension
zerobox per-operation sandboxingLow — transparent to agentMedium — zerobox in construct image, policy derivation from mountsDepends on zerobox compatibility inside apple/container VM
Network egress policyLow — deny/allowlist declarativeMedium — egress policy schema, enforcement at VM boundaryApple Container VM boundary (egress enforced at VM level)

Part 6: What Remains Open

Phase 0 Gate (Must Resolve Before Apple Container Implementation)

The most critical open question is whether rootless DinD can run inside an apple/container VM:

  1. Does apple/container allow --cap-add CAP_SYS_ADMIN? (needed for mount namespaces in rootless DinD)
  2. Does rootless DinD pass docker build, Compose, and Testcontainers inside the apple/container VM?
  3. Does jackin-capsule run correctly as non-PID-1 (JACKIN_CAPSULE_FORCE_DAEMON=1) with proper SIGTERM propagation, exit-code visibility, and Ctrl+C passthrough?

If rootless DinD fails inside apple/container: smolvm is the fallback (documented Docker-in-VM path exists, though constrained). See smolvm backend research for the full fallback plan.

jackin-exec Design Decisions

  1. Binary name: jackin-exec proposed — needs final decision before baking into construct image (renaming later breaks existing role system prompts and MCP tool descriptions)
  2. MCP vs binary vs PATH-shadowing: binary + MCP recommended; PATH-shadowing noted but not recommended (lacks transparency)
  3. Approval UX for high-frequency sessions: "Approve All Similar" and "Always for Session" modes need TUI design — see TUI design decisions

Known Apple Container Limitations (v0.11.0)

LimitationStatusImpact
--privileged not supportedBy design (use --cap-add)Requires rootless DinD — the Phase 0 gate
Multi-container bridge networking rough edgesUpstream v0.11.0 known issueAffects DinD inner container networking; monitor for fix
DNS hiccuping after macOS sleep/wakeUpstream v0.11.0 known issueOperator must reconnect after sleep; jackin should detect and prompt
No health checks (--health-cmd)Not implementedjackin-capsule liveness probes instead
Apple Silicon onlyBy designAcceptable — jackin is macOS ARM only

Part 7: The Threat Model This Architecture Addresses

This architecture is designed for jackins current threat model: a trusted operator running semi-trusted AI agents against potentially untrusted external code on a personal macOS ARM machine. It is explicitly NOT designed for:

  • Hosted or multi-tenant workloads (strangers' code running on shared infrastructure)
  • Enterprise regulatory compliance (HIPAA, PCI-DSS) where third-party audited attestations are required
  • Adversarial operators trying to escape the sandbox themselves

For the current threat model, the four layers close the practical failure modes:

Failure modeClosed by
Agent exploits kernel CVE to escape containerApple Container VM boundary
Agent reads secret injected as env varjackin-exec credential injection
Agent writes to wrong mounted pathzerobox per-operation policy
Agent calls unexpected network endpointzerobox + egress policy
Agent runs code that exhausts CPU/memoryDocker hardening resource limits
Agent exfiltrates data to unexpected hostEgress policy session allowlist
Agent remembers raw API key across promptsjackin-exec (key never in context)
DinD privilege escalation to host kernelApple Container VM boundary + rootless DinD

Part 8: Implementation Sequence

Work proceeds in this order. Each step is a prerequisite for the next where noted.

1. Docker runtime hardening contract (in progress, another agent)
   └─ Gate: rootless DinD must validate on macOS ARM + OrbStack
   └─ Also delivers: capability policy, read-only root, resource limits, session contract output
   └─ These improvements benefit operators immediately, regardless of backend

2. Apple Container Phase 0 (empirical, after rootless DinD validates)
   └─ Gate: does rootless DinD work inside apple/container VM?
   └─ Does jackin-capsule run as non-PID-1 with correct signal behavior?
   └─ Does interactive TUI session attach work via vminitd gRPC/vsock?
   └─ If PASS → Apple Container Phase 1-5 (backend adapter implementation)
   └─ If FAIL → smolvm Phase 0 (fallback, Docker-inside via documented TSI recipe)

3. jackin-exec (parallel with or after Apple Container Phase 0)
   └─ Can start before Apple Container lands — works on current Docker backend too
   └─ Requires jackin-capsule vsock/Unix extension for wrapper ↔ capsule communication
   └─ Delivers credential isolation for current operators immediately

4. Network egress policy (after Apple Container or smolvm backend lands)
   └─ Enforced at VM egress boundary — more reliable than Docker network tricks
   └─ See: [Network egress policy](/roadmap/network-egress-policy/)

5. zerobox per-operation sandboxing (after jackin-exec, parallel with egress)
   └─ Works inside current Docker backend too — no backend dependency
   └─ Compatibility testing needed: does zerobox work inside apple/container VM?
   └─ See: [Process-level sandboxing](/reference/research/security/process-sandboxing/process-level-sandboxing/)

The key source files an implementing agent reads before touching this architecture:

ItemRole in this architecture
Docker runtime hardening contractLayer 1 — prerequisite for Apple Container (rootless DinD must validate). Independently valuable for current Docker backend operators.
Apple Container backendLayer 2 — dedicated item with Phase 0-4, AppleContainerResources struct, schema migration, JACKIN_CAPSULE_FORCE_DAEMON code task.
jackin-execLayer 3 — dedicated item with workspace secret bindings schema migration, implementation phases, MCP tool spec, operator approval UI.
Process-level sandboxingCross-layer — zerobox per-operation filesystem/network sandboxing only (jackin-exec is separate).
Network egress policyLayer 4 — session-level outbound policy, enforced at VM egress when Apple Container backend lands.
Container credential exposureThe problem that jackin-exec (Layer 3) solves.
Host bridge — secrets and approved host actionsHost-side resolver that jackin-exec calls for op:// credentials.
Session contract and explain modeHow the operator sees which layers are active and what residual risks remain.
Selectable sandbox backendsUmbrella for Docker and Apple Container as the two runtime families; backend selection model, instance registry design.
smolvm backend researchDeferred — fallback if Apple Container Phase 0 fails Docker-inside test.
OrbStack isolated machine backendDeferred — shared-kernel namespace, not hypervisor boundary. Historical research preserved.

On this page