sandbox: injected egress credentials — the secret never enters the unit (+ sandbox-jcp regression fix) #33

Merged
dcharlot merged 2 commits from sandbox/credential-injection into main 2026-07-20 19:50:47 -04:00
Owner

The Notion sweep found their Workers run on Vercel Firecracker microVMs with network-level credential injection — credentials never enter the execution environment — plus dynamic egress policy. Strong isolation, but no energy meter and no attestation. TX's Sandbox standard already had the egress allowlist and the meter (S5 + RAPL → signed joule receipt); what it lacked was credential injection. This ingests exactly that, and adds the two properties the incumbent pattern ships without.

The pattern, as a capability

A NetworkGrant may now carry credentials: Vec<EgressCredential>. Each names:

  • an authorized egress endpoint,
  • a secret_ref handle the enforcement boundary resolves in its own keystore outside the sandbox,
  • an Injection mode (Bearer / Header / Query / BasicAuth) — the shape, never the secret.

The unit makes authenticated calls without ever holding the key. A compromised or hallucinating agent inside the sandbox cannot read, log, or exfiltrate a credential it was never given. The grant — and any receipt over it — carries only the reference.

Two invariants

  • injection ⊆ egress. Enforcer::credential_for(endpoint) returns a credential only when the endpoint is both on the egress allowlist and has a declared credential — injection can never exceed egress even if the grant is malformed. injection_within_egress() flags such a grant at mint; the enforcer is defensive regardless.
  • reference, not value. EgressCredential::commitment() content-addresses the (endpoint, secret_ref, mode) decision, so an attestation records which credential was injected for which call without exposing the secret — the audit primitive Notion/Vercel lack.

Backward compatible

credentials skips serialization when empty → a grant with none is byte-identical to one minted before the field existed. Published sandbox conformance vectors unchanged and still verify (asserted by test).

⚠️ Also in this PR: a latent regression fix (separate commit c8dd25f6)

sandbox-jcp path-depends on jcp-schema across a workspace boundary, and its Grant literal was missing budget_funds + caveats — the fields added by the dual-ceilings and caveats work. Those swept every Grant site inside the jcp workspace, but sandbox-jcp is in the separate sandbox workspace, so a clean build of the sandbox tree has been broken since (masked by a warm cache; a fresh checkout surfaces it). Fixed. Lesson: a required-field addition to a shared schema must be verified against every workspace that path-depends on it.

5 new tests, sandbox-capability clippy-clean, full sandbox workspace green (one pre-existing unrelated warning in sandbox-conformance/vectors.rs, untouched).

The Notion sweep found their Workers run on **Vercel Firecracker microVMs with network-level credential injection** — credentials never enter the execution environment — plus dynamic egress policy. Strong isolation, but **no energy meter and no attestation**. TX's Sandbox standard already had the egress allowlist and the meter (S5 + RAPL → signed joule receipt); what it lacked was credential injection. This ingests exactly that, and adds the two properties the incumbent pattern ships without. ### The pattern, as a capability A `NetworkGrant` may now carry `credentials: Vec<EgressCredential>`. Each names: - an authorized egress `endpoint`, - a `secret_ref` **handle** the enforcement boundary resolves in its own keystore **outside the sandbox**, - an `Injection` mode (`Bearer` / `Header` / `Query` / `BasicAuth`) — the *shape*, never the secret. The unit makes authenticated calls **without ever holding the key**. A compromised or hallucinating agent inside the sandbox cannot read, log, or exfiltrate a credential it was never given. The grant — and any receipt over it — carries only the reference. ### Two invariants - **injection ⊆ egress.** `Enforcer::credential_for(endpoint)` returns a credential only when the endpoint is *both* on the egress allowlist and has a declared credential — injection can never exceed egress even if the grant is malformed. `injection_within_egress()` flags such a grant at mint; the enforcer is defensive regardless. - **reference, not value.** `EgressCredential::commitment()` content-addresses the `(endpoint, secret_ref, mode)` decision, so an attestation records *which* credential was injected for *which* call **without exposing the secret** — the audit primitive Notion/Vercel lack. ### Backward compatible `credentials` skips serialization when empty → a grant with none is byte-identical to one minted before the field existed. Published sandbox conformance vectors unchanged and still verify (asserted by test). ### ⚠️ Also in this PR: a latent regression fix (separate commit `c8dd25f6`) `sandbox-jcp` path-depends on `jcp-schema` across a workspace boundary, and its `Grant` literal was missing `budget_funds` + `caveats` — the fields added by the dual-ceilings and caveats work. Those swept every Grant site *inside* the jcp workspace, but sandbox-jcp is in the *separate* sandbox workspace, so a clean build of the sandbox tree has been broken since (masked by a warm cache; a fresh checkout surfaces it). Fixed. **Lesson: a required-field addition to a shared schema must be verified against every workspace that path-depends on it.** 5 new tests, sandbox-capability clippy-clean, full sandbox workspace green (one pre-existing unrelated warning in `sandbox-conformance/vectors.rs`, untouched).
sandbox-jcp path-depends on jcp-schema across a workspace boundary
(../../../../jcp/jcp-rs/crates/jcp-schema), and its Grant literal was missing
`budget_funds` and `caveats` — the two fields added to Grant by the dual-ceilings
and caveats work in the JCP workspace. Those changes swept every Grant
construction site inside the jcp workspace (the compiler forced it), but
sandbox-jcp lives in the SEPARATE sandbox workspace, so its literal went
unswept and a clean build of the sandbox workspace has been broken since.

It went unnoticed because the two workspaces are tested independently and a warm
target cache masked it in the sandbox tree. A fresh checkout surfaces it
immediately. Adds the two fields (both default: no money ceiling, no caveats).

Lesson: a required-field addition to a shared schema must be verified against
EVERY workspace that path-depends on it, not only the schema's own.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Ingests the credential-injection pattern the microVM-sandbox field converged on
(Notion's Workers run on Vercel Firecracker microVMs with network-level
credential injection), and adds the two things that pattern ships without: the
secret held only by REFERENCE, and an audit commitment — both bound into the
Sandbox standard's existing capability model.

A NetworkGrant may now carry `credentials: Vec<EgressCredential>`. Each names an
authorized egress `endpoint`, a `secret_ref` HANDLE the enforcement boundary
resolves in its own keystore OUTSIDE the sandbox, and an `Injection` mode
(Bearer / Header / Query / BasicAuth) describing how the resolved secret is
applied to the request. The unit makes authenticated calls without ever holding
the key: a compromised or hallucinating agent inside the sandbox cannot read,
log, or exfiltrate a credential it was never given. The grant — and any receipt
over it — carries only the reference, never the material.

Two invariants:
- injection ⊆ egress. `Enforcer::credential_for(endpoint)` returns a credential
  only when the endpoint is BOTH on the egress allowlist and has a declared
  credential, so injection can never reach further than egress even if the grant
  is malformed. `NetworkGrant::injection_within_egress()` flags such a grant at
  mint time; the enforcer is defensive regardless.
- reference, not value. `EgressCredential::commitment()` content-addresses the
  (endpoint, secret_ref, mode) decision, so an attestation can record WHICH
  credential was injected for WHICH call without exposing the secret — the audit
  primitive Notion/Vercel lack.

Backward compatible: `credentials` skips serialization when empty, so a grant
with none is byte-identical to one minted before the field existed. The
published sandbox conformance vectors are unchanged and still verify (empty
credential set omitted; asserted by test).

Five tests: injection only for authorized egress; injection can never exceed
egress even for a malformed grant; the reference is recorded while no secret
material ever appears in the encoding; commitments distinguish credentials
without the secret; empty sets are omitted. sandbox-capability clippy-clean; full
sandbox workspace green (one pre-existing warning in sandbox-conformance/vectors.rs,
unrelated and untouched).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!33
No description provided.