sandbox: injected egress credentials + sandbox-jcp cross-workspace fix (re-land of #33) #36
Merged
dcharlot
merged 2 commits from 2026-07-21 08:19:55 -04:00
sandbox/cred-injection-2 into main 2026-07-20
sandbox-capability: injected egress credentials — the secret never enters the unit
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]>
sandbox-jcp: repair the jcp_schema::Grant literal — latent cross-workspace break
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]>