> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enfinitos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox architecture

> How the EnfinitOS sandbox is layered — sandbox-core foundational library, edge-runtime HTTP API, in-browser auditor, and the upgrade path to a persistent backend.

The hosted sandbox is built in three layers:

```
packages/sandbox-core/        edge-runtime TypeScript library
  ↓
apps/web/app/api/sandbox/     Cloudflare Pages edge routes
  ↓
apps/web/app/sandbox/         React UI + in-browser verifier
```

Each layer is independently testable, independently substitutable.
The library has no environment assumptions; the HTTP API is the
only place cookies + storage live; the UI doesn't trust the API's
"verified" claim — it re-verifies locally.

## sandbox-core

The foundational TypeScript library. Edge-runtime-compatible: uses
only Web APIs (`TextEncoder`, `btoa`/`atob`, `crypto.subtle` via
`@noble/hashes`, `crypto.randomUUID`, `crypto.getRandomValues`). No
`node:fs`, no `node:crypto`, no `Buffer`.

| Module                | Responsibility                                                                                                                                                                                   |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `canonical.ts`        | Field-ordered + sort-keys canonical JSON. Byte-exact with platform + auditor SDK.                                                                                                                |
| `crypto.ts`           | Ed25519 (via `@noble/ed25519`), SHA-256 (via `@noble/hashes`), base64url, sandbox keypair derivation.                                                                                            |
| `proofPack.ts`        | `signProofRecord`, `issueProofPack`, `buildReceiptPayload`. Produces `envelope.v1` packs.                                                                                                        |
| `metering.ts`         | Per-substrate projection rules — DOOH dwell seconds, CTV impressions, etc.                                                                                                                       |
| `settlement.ts`       | Banker's-rounded reconciliation across TENANT / VENUE / PLATFORM splits.                                                                                                                         |
| `tenantState.ts`      | The full rights bounded context: basis, right (issue/suspend/resume/revoke), offer (propose/accept/reject/counter/withdraw), challenge (open/resolve/withdraw), expiry sweeps, provenance walks. |
| `constraints.ts`      | Pre-render gate: messaging opt-out + quiet hours, automotive speed, drone Remote ID + BVLOS waiver, audio attention.                                                                             |
| `syntheticFactory.ts` | Generates plausible delivery events with substrate-realistic dwell.                                                                                                                              |
| `types.ts`            | All wire shapes. Mirrors platform shapes exactly.                                                                                                                                                |

Conformance is enforced by `__tests__/conformance.test.ts` — every
sandbox-issued pack is fed through `@enfinitos/sdk-auditor`'s
`verifyAll`. A drift in any encoder, signature, chain, metering, or
settlement primitive will fail the suite. CI gates the sandbox
release on it.

## HTTP API

Each route under `apps/web/app/api/sandbox/` is a tiny adapter
between the HTTP boundary and the sandbox-core state machines.
Common scaffolding lives in `_lib/wrapHandler.ts`:

* Parses cookies + HMAC validation
* Per-IP rate limiting
* JSON body parsing
* Persists the mutated tenant state back to the store
* Wraps errors into the standard envelope

Adding a new state-machine API endpoint is typically 10 lines of
glue plus the route file.

## Storage

The tenant store is a `Map<tenantId, TenantState>` keyed under a
global Symbol so all routes share one view per Worker isolate. The
interface (`apps/web/app/sandbox/_lib/store.ts`) is intentionally
small — `get`, `set`, `delete` — so a future swap to Cloudflare
KV / D1 / Durable Objects requires no route-handler changes.

### Why module-level memory works for sandbox

Sandbox traffic is single-visitor, short-session, low-stakes. A
typical visitor finishes the six-step flow in 5–20 minutes.
Cloudflare Workers keep their isolates warm for tens of minutes
under continuous traffic; the eviction case is detectable and
recoverable (the API returns 410 TENANT\_EVICTED; the UI prompts
the user to hit Reset).

### Upgrading to persistent storage

When sandbox traffic is high enough that eviction is common, the
upgrade is:

1. Create a Cloudflare KV namespace bound to the Pages project.
2. Replace `memoryStore` in `_lib/store.ts` with a KV-backed
   implementation matching the same interface.
3. KV reads are eventually consistent (\~60s globally) — sandbox
   tenants are per-visitor so consistency isn't a concern.
4. Set an explicit TTL (24h matches the cookie max-age).

No route handler changes. No type changes. No UI changes.

## In-browser verifier

`apps/web/app/sandbox/_components/SandboxConsole.tsx` runs the
seven-check audit roundtrip client-side using sandbox-core
primitives:

1. Envelope version is `envelope.v1`.
2. `canonicaliseProofPayload` re-encodes each record byte-identically.
3. `sha256HexOfString(payloadCanonical)` reproduces `afterHash`.
4. The hash chain is intact end-to-end.
5. Every Ed25519 signature verifies against the published key.
6. Metering records re-project from receipts.
7. Settlement lines reconcile per-meter to gross.

When all seven pass, the UI shows a green panel and the visual
proof-chain SVG colours every node green. A single failure flips
the relevant node red and the panel says exactly which invariant
broke.

## Cross-environment story

A sandbox-issued pack verifies under any auditor pointed at the
sandbox key directory. A production-issued pack verifies under any
auditor pointed at the production key directory. Cross-pointing
fails as `UNKNOWN_KEY_ID` — there is no environment-conditional
code path on the auditor side. The same library handles both.

This is the property that makes the sandbox useful for compliance
review: anything that verifies under sandbox will verify under
production once a real tenant exists.

## Trust model summary

| Question                                          | Answer                                                                                                                                                           |
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Are sandbox signatures real?                      | Yes — Ed25519 over canonical JSON, identical algorithm to production.                                                                                            |
| Is the sandbox key kept secret?                   | No, deliberately — derived from a published seed string so anyone can re-derive. The `keyId` is unambiguously labelled as a sandbox key.                         |
| Can a sandbox pack impersonate a production pack? | No. Sandbox packs carry a sandbox `keyId`; production auditors don't know this key. Verification fails as `UNKNOWN_KEY_ID`.                                      |
| Can a malicious user forge sandbox packs?         | Yes — by design. The sandbox is a demonstration environment, not a trust boundary. Forging packs that pass sandbox verification is the same as running the demo. |
| Where does tenant data live?                      | Module-level Map in the Worker isolate. Per-visitor, ephemeral, no cross-visitor leakage.                                                                        |
| Is the cookie tamper-resistant?                   | HMAC-SHA256 over the tenantId with a server secret. Tampering breaks the HMAC.                                                                                   |
| What about rate limiting?                         | 10 req/10s burst + 200 req/hour sustained per IP. Cloudflare's bind-time rate-limit primitive is the production upgrade path.                                    |
