Files
sand-boxer/docs/meta-framework.md
tegwick 1f87be4c6b feat: reachability and consumer profiles (SAND-WP-0011)
Add reachability enrichment (tunnel metadata, ops-bridge pointer),
secret_refs boundary resolution, profile.agent-dev and profile.build,
CLI reachability show, API endpoint, consumer smoke scripts, and tests.
2026-06-24 12:54:27 +02:00

7.9 KiB

sand-boxer meta-framework specification

Version 0.1 — derived from research/03-meta-framework-synthesis.md and INTENT.md.

sand-boxer is the sandbox establishment service: one API for consumers, many extension backends. It provisions where and how code runs; sibling projects own agent harnessing, validation, and code generation.


Resource model

Resource Description
Profile Named, versioned sandbox recipe: extension binding, isolation, network, TTL, placement
Extension Backend adapter implementing provision / wait_ready / teardown
Host Registered placement target for self-hosted extensions; read-only telemetry via profile.sandbox-canary (see docs/host-telemetry.md)
Sandbox Running instance of a profile
Snapshot Point-in-time workspace checkpoint (sandboxer snapshot / restore)
Route Extension selection policy when multiple backends qualify
Meter Usage record for payments layer (SaaS extensions — SAND-WP-0006)

Lifecycle states

requested → provisioning → ready → active → { expired | failed } → destroying → destroyed
State Meaning
requested Create accepted; not yet handed to extension
provisioning Extension running provision + wait_ready
ready Reachability confirmed; consumer may attach
active Consumer has marked sandbox in use (optional transition)
expired TTL elapsed before explicit destroy
failed Provision or readiness failed
destroying Teardown in progress
destroyed Resources released; record retained for audit

State Hub event mapping

Each transition emits a State Hub progress event (or dedicated registration API when available):

Transition event_type Required fields
requested note sandbox_id, profile_id, consumer
provisioning note extension_id, host
ready milestone reachability descriptor
active note actor_type, timestamps
failed note error summary
destroying note
destroyed milestone duration_s, cleanup report

Event detail payload (JSON):

{
  "sandbox_id": "abc12345",
  "profile_id": "profile.compose-e2e",
  "extension_id": "ext.compose-ssh",
  "host": "coulombcore",
  "consumer": {"actor": "atm", "project": "wise-validator", "run_id": "..."},
  "actor_type": "atm",
  "state": "ready",
  "reachability": {
    "ssh": "root@coulombcore",
    "remote_dir": "/tmp/sandboxer/abc12345",
    "tunnel": "localhost:12222",
    "tunnel_via": "ops-bridge",
    "identity": "ops-warden"
  },
  "timestamps": {"created_at": "...", "ready_at": "..."}
}

Extends the build-agent self-register pattern: generic sandbox identities carry profile_id + extension_id instead of build-machine metadata.


Core API operations (v0)

Operation Description v0 scope
create Provision from profile + inputs Yes
get Inspect sandbox status Yes
list List sandboxes (filter by consumer optional) Yes
extend_ttl Extend time-to-live Yes
recreate Destroy and reprovision from stored seed Yes
destroy Idempotent teardown Yes
snapshot / restore Checkpoint workspace Yes (compose-ssh, saas-stub)
exec Run command in sandbox Harness-owned via SSH (glas-harness)

HTTP surface (optional v0; CLI calls core library directly):

  • POST /v1/sandboxes — create
  • GET /v1/sandboxes/{id} — get
  • GET /v1/sandboxes — list
  • DELETE /v1/sandboxes/{id} — destroy
  • POST /v1/sandboxes/{id}/snapshot — checkpoint
  • POST /v1/snapshots/{id}/restore — restore
  • GET /v1/snapshots — list checkpoints
  • POST /v1/sandboxes/{id}/recreate — recreate
  • PATCH /v1/sandboxes/{id}/ttl — extend TTL
  • POST /v1/sandboxes/expire — TTL reap (query apply=true)
  • GET /v1/sandboxes/{id}/reachability — enriched descriptor + SSH one-liner

Reachability descriptor

When a sandbox reaches ready, sand-boxer emits a reachability block on SandboxStatus, lifecycle events, and sandboxer reachability show <id>.

Field Source Description
ssh Extension SSH target (user@host)
remote_dir Extension Workspace root on remote host
host Extension Placement host name
tunnel Profile + env Local port (localhost:PORT) or VM alias
tunnel_via Profile spec Route owner (default ops-bridge)
identity Profile spec Warden actor hint (default ops-warden)

Tunnel metadata is enriched from profile reachability and environment:

  • SANDBOXER_TUNNEL_PORT / handle tunnel_port or ssh_port
  • SANDBOXER_TUNNEL_ALIAS / handle vm_target
  • SANDBOXER_TUNNEL_VIA (optional override)

sand-boxer does not bring tunnels up. Consumers use ops-bridge (MCP or bridge CLI) to attach SSH routes; the descriptor is a pointer only.

secret_refs from profile.setup are resolved at the provision boundary and passed to the extension handle — they never appear on SandboxStatus or State Hub events.


Consumer attribution

Every create request carries a consumer block:

consumer:
  actor: adm | agt | atm
  project: <calling-repo-or-service>   # e.g. wise-validator, glas-harness
  session_id: <optional>
  run_id: <optional>
Actor Typical caller
adm Human operator via CLI
agt LLM agent session
atm Deterministic automation (CI, activity-core, wise-validator)

sand-boxer records attribution on every lifecycle event. It does not interpret agent intent or authorize the caller — flex-auth owns authorization when enforced.


Extension interface

Each extension implements:

provision(profile, inputs, placement) → SandboxHandle
wait_ready(handle) → Reachability
teardown(handle) → CleanupReport
estimate_cost?(profile, duration) → MeterQuote   # optional; SaaS only

Registration requirements (validated at load time):

  • id — unique extension identifier (ext.<name>)
  • capabilities — isolation levels, regions, persistence, pricing model
  • handler — Python entry point or built-in registry binding

Extensions are discovered from extensions/*.yaml at repo root and loaded via sandboxer.extensions.registry.


Routing policy vocabulary

When multiple extensions satisfy a profile capability:

Strategy Behavior
prefer-self-hosted Self-hosted extensions first; SaaS fallback (default Coulomb posture)
lowest-cost Cheapest estimate_cost quote wins
lowest-latency Closest region / host wins
explicit Profile names a single extension; no auto-routing

Routing engine v0: sandboxer.routing.resolver — see docs/routing.md.


Security limits

sand-boxer commits to:

  1. Default-deny network unless profile explicitly allows egress
  2. Secrets at provision boundarysecret_refs resolved via ops-warden / OpenBao; never returned to agent context
  3. Blast-radius isolation — dedicated hosts (sandboxer01, CoulombCore) away from Railiance01 production
  4. Observable lifecycle — every transition attributed to adm / agt / atm
  5. Honest limits — allowed tool paths can be abused by compromised agents

sand-boxer does not provide intent-aware egress filtering in v1.


Out of scope (sibling ownership)

Concern Owner
Agent gateway, tools, memory glas-harness
e2e.yml semantics, health checks, test pass/fail wise-validator
Code generation, setup instructions content snuggle-inventor
SSH tunnels ops-bridge
SSH certificates ops-warden
Workstream / task state state-hub

See docs/integrations/ for per-sibling contracts.