Skip to content

Compiled Policy

The Compiled Policy is what Policy turns into.

A signed JSON file with everything one specific Sandbox needs to enforce: which network destinations are allowed, which paths are off-limits, which tools the agent can call, what the interpreter is allowed to import, the budget, the compliance tags.

Each Provider reads it and renders the config its target tool expects — Claude Code's settings.json, OpenShell's YAML profile, Monty's host-function spec.

Compiled froma Policy + a Sandbox-spec, by the OPA cascade
Consumed byevery Provider the Runtime selected
Formatsigned JSON (Ed25519); one per Sandbox; read-only mount into the agent
Updated whenPolicy changes — pushed live to active Sandboxes within seconds, no agent restart

You write a Policy (in Rego, or via the wizard). The OPA cascade evaluates it for one Sandbox. The output is the Compiled Policy — JSON, signed with TapPass's Ed25519 key, versioned with a monotonically-increasing policy_version.

The Compiled Policy then travels to the host. Each Provider in this Sandbox's Runtime reads the slices it cares about and renders them into the format its tool understands.

Same Compiled Policy → different rendering per Provider. Same shape across all ecosystems; only the rendering differs. That's what makes one Policy govern Claude Code + OpenShell + Monty + the LLM gateway from a single source.

Sometimes called the "Keyring" in older docs and on-disk paths (keyring.json). Same artifact.

{
"version": 1017,
"issued_at": "2026-04-28T12:00:00Z",
"expires_at": "2026-04-28T12:05:00Z",
"identity": {
"agent_id": "claude-code@team-eng.acme.com",
"tier": "worker",
"chain": ["orchestrator@team-eng.acme.com"],
"sandbox_id": "sbx_a1b2c3",
"runtime_id": "claude-code-laptop-strict"
},
"network": {
"allow_domains": ["api.anthropic.com", "github.com", "pypi.org"],
"deny_categories": ["paste_services", "webhooks", "dyndns"]
},
"filesystem": {
"workspace": "/workspace",
"read_only": ["/app/read-only-mounts"],
"deny_paths": ["~/.ssh", "~/.aws", "~/.kube", "/etc/shadow"]
},
"tools": {
"allow": ["Bash(git:*)", "Bash(uv:*)", "WebFetch(domain:github.com)", "Skill(*)"],
"deny": ["Bash(curl:*)", "Bash(ssh:*)", "Bash(rm -rf:*)"]
},
"interpreter": {
"host_functions": ["http_get", "http_post", "json_parse"],
"memory_mb": 128,
"cpu_time_ms": 5000,
"stack_depth": 256
},
"budget": {
"tokens_per_day": 500000,
"dollars_per_month": 200,
"tool_calls_per_minute": 60
},
"compliance_tags": ["SOC2:CC6.1", "ISO42001:6.2.3", "EU-AI-Act:Article-15"],
"signature": "ed25519:<TapPass key>:<sig>"
}

Organized by aspect (network / filesystem / tools / interpreter / budget / compliance), not by enforcement layer. The same aspects get enforced at different rings depending on which provider you assign to each ring — that mapping is the Provider's job, not the Compiled Policy's.

Compiled Policy aspectWhere it gets enforced
network.allow_domains, deny_categorieskernel ring (egress allowlist via openshell / nono / gvisor / etc.) — and gateway (LLM API egress)
filesystemkernel ring (Landlock / sandbox-exec rules)
tools.allow, denyharness ring (settings.json allow/deny) — and MCP broker (per-call check)
interpreter.host_functions, memory_mb, …interpreter ring (Monty / V8 / Wasmtime profiles)
budget.tokens_per_day, dollars_per_monthgateway (per-call budget check)
compliance_tagsaudit metadata (every action signed and tagged with these)

The same Compiled Policy fans out to different enforcement positions depending on the runtime. A cursor-laptop runtime might enforce tools.allow only at the harness ring (Cursor's config) without an MCP broker, while a claude-code-laptop runtime enforces it at both harness ring (Claude Code's settings.json) and MCP broker (per-call) — defense in depth.

[derive] Policy cascade evaluates → Compiled Policy (server-side; deterministic; ecosystem-agnostic)
[mint tokens] gateway_token + mcp_session_token issued (5-min TTL)
[sign] TapPass Ed25519 signature over the whole bundle
[push] Signed payload sent to tappass-host over sync channel
(push primary; pull on boot/reconnect; reconciler closes the loop)
Host validates: signature, monotonic version, sandbox_id, expires_at
[receive] Host writes new keyring.json (atomic rename)
Mounts read-only into agent's sandbox namespace
[providers render] Each provider in this sandbox's runtime renders target-specific config:
• claude-code provider → managed-settings.json + ANTHROPIC_BASE_URL
• openshell provider → YAML profile + L7 proxy reload
• monty provider → host-function manifest
• llm-gateway-anthropic provider → token + endpoint
• mcp-broker → per-server policy
[apply] Each provider's apply path runs (hot-reload / restart / file rewrite per its update model)
[agent observes] Canonical case (sdk-direct provider): tappass-agent SDK reads Compiled Policy via inotify; rebinds clients.
Other ecosystems: provider's reload mechanism (Claude Code's settings reload, etc.)
[expire] Tokens hit TTL within 5 minutes
[refresh] Sync push delivers fresh Compiled Policy before tokens expire
(or fail-closed: tokens expire → agent stops being able to act)

Engines that operate on the Compiled Policy

Section titled “Engines that operate on the Compiled Policy”
EngineWhat it doesStatus
Compiler (Compiled Policy emitter)Writes the Compiled Policy from Policy + Sandbox-spec + Runtimeconcept (policy-to-sandbox-config-builder)
Live policy push channelDelivers signed payloads (push)concept (live-policy-push-channel)
Pull endpointAgents fetch their Compiled Policy at boot / reconnectconcept (extends sync channel)
ReconcilerCompares desired vs. applied version per agent; re-pushes; flags driftconcept (extends sync channel)
ProvidersPer-target translators that consume the Compiled Policyconcept (provider card)
Runtime instantiatorComposes providers for one runtime; invokes eachconcept (within host-runtime-cli)
Compiled Policy inspectorOperator read-only viewconcept (within tappass-cli)

There are no quick-start manifests — manifests are always derived from a Policy + Sandbox-spec + Runtime. The quick-starts live one level up:

  • Quick-start Policies → Compliance packs (EU AI Act, OWASP LLM, …)
  • Quick-start Sandbox-specs → templates per agent shape (refund-processor, code-reviewer, …)
  • Quick-start Runtimes → curated v1 set (claude-code-laptop, librechat-server, …)

The Compiled Policy's shape is fixed (schema-versioned, forward-compat-guaranteed); its contents are the deterministic output of the compiler.

PersonaSurfaceWhat you can do
Operatortappass sandbox manifest <id>inspect any sandbox's Compiled Policy (read+write for ops)
Host ownertappass-host inspect <name>inspect the local Compiled Policy + which providers rendered each section
Agent (canonical sdk-direct path)Keyring.load() from tappass-agent SDKread its own Compiled Policy (read-only)
Agent (CLI)tappass-agent statussanitized view (scopes shown, tokens redacted)

The Compiled Policy file on the host machine: /var/run/tappass/<sandbox_id>/keyring.json. Owned by the host UID, mode 0440, read-only mount into the agent's namespace.

  • derived fromPolicy + Sandbox-spec + Runtime
  • shaped byCascade (org/project/agent merged before derivation)
  • delivered viaSync (signed unidirectional push + pull + reconcile)
  • rendered byProviders (per-target translators; one per ring + cross-cutting)
  • enforced at → 3 rings (harness / kernel / interpreter) + cross-cutting LLM gateway + MCP broker
  • bound toSandbox (one Compiled Policy per sandbox)
TopicFile
Strategic frameTapPass Strategy Memo v3 §07 — Pillar 2: Control Plane
Visiongoverned-agents-architecture.mdto be re-aligned with strategy memo terminology (rings vs. layers)
Compilerpolicy-to-sandbox-config-builder
Sync deliverylive-policy-push-channel
Provider consumersprovider card
AspectStatus
Compiled Policy schema (canonical IR)concept (Q2-Q3 2026) — JSON schema published with the compiler
Compiler (Policy → Compiled Policy)concept (Q3 2026; critical-path L)
Sync delivery (push + pull + reconcile)concept (Q3 2026)
Provider taxonomyconcept (Q3-Q4 2026 per ecosystem demand)
sdk-direct (canonical agent path)concept (within tappass-agent SDK)
  • Compiled Policy ≠ Policy. Policy is the source (Rego rules); Compiled Policy is the evaluated, signed IR — what providers consume. The Policy produces the Compiled Policy via the OPA cascade.
  • Compiled Policy ≠ ecosystem-specific config. The Compiled Policy is the canonical IR. The ecosystem-specific config (Claude Code's settings.json, Cursor's config, OpenShell YAML) is what the Provider renders from the Compiled Policy. Same Compiled Policy → different rendering per provider.
  • Compiled Policy organized by aspect, not by ring. Earlier drafts described a "5-layer keyring" with one slice per ring; the strategy memo's correction is that the Compiled Policy carries content (network/fs/tools/interpreter/budget/compliance), and rings are where it gets enforced via different providers. Cleaner separation.
  • Read-only on the agent's side. The host process (or provider) writes; the agent reads. The agent has no API to modify the Compiled Policy — that's the non-escalation property.
  • Why two names? "Compiled Policy" is the formal Terraform-aligned name. "Keyring" is the operational alias I've been using and that survives in places (file path: keyring.json; SDK class: Keyring). Same thing.