Status
What is built, what is written down, and what is serving anyone. This page
and every claim elsewhere on the site are generated from
status.json — no page hand-writes a
rung, and a build check fails if one drifts.
What the rungs mean
- live
- Serving the public.
- staged
- Deployed somewhere non-public.
- local
- Implemented, and its tests run on a developer machine.
- spec
- Written down. No code.
A capability cannot be described anywhere on this site as more than its rung here. That constraint is the point of the file: it is easier to keep one manifest honest than five pages consistent.
Capabilities
| Capability | Rung | What that means here | Where to check |
|---|---|---|---|
| Append-only world versions and audit | local | 0004 called world_versions immutable while 0007 granted UPDATE and DELETE on it. Now enforced by the verb matrix, proven by attempting the write and witnessing the row survived. | worker/migrations/0011_verb_matrix.sql · battery group I · CD_SABOTAGE=verbs |
| Column-level authority on mutable tables | local | 0011 granted table-wide UPDATE on memberships while its own comment said last-seen only; a viewer promoted itself to owner. Authority-bearing columns are now separately granted. | worker/migrations/0012_column_authority.sql · battery group J · CD_SABOTAGE=columns |
| ComputeDriven → provider action translation | local | The translation is a declared table checked in BOTH directions against a closed provider vocabulary, so a correct R2 grant (ListObjectsV1 + ListObjectsV2 for one ComputeDriven ListObjects) conforms and half of one is refused as an under-grant. R2's own words were verified first-party against Cloudflare's documentation, not recalled. | worker/src/r2native.mjs · worker/test/r2native.test.mjs |
| Concurrency battery (two real sessions) | local | A separate battery, because tenant-isolation.sh runs one session on one connection and every property it can express is sequential. Hand-steps two sessions to a deterministic interleaving rather than racing and hoping, and judges its sabotage modes PER GROUP so a control that fires for the wrong reason reports MISDIRECTED. | ./worker/test/concurrency.sh · CD_SABOTAGE=lock · CD_SABOTAGE=quota |
| Entitlement + storage quota admission | local | M1.5 (R24). Entitlement, a two-number byte ledger, and an expiring reservation with exactly-once finalize/abort/expiry. Admission is ONE statement: 20 simultaneous 100-byte requests against a 1000-byte limit admit exactly 10. Round 7 made it REQUEST-idempotent rather than merely accounting-idempotent (four concurrent retries all succeed with one reservation and zero errors), moved authority checks ABOVE replay, and bound the idempotency key to the whole request tuple. It is admission-safe and NOT hard-quota-safe — see byte_enforced_quota. | worker/migrations/0014 + 0016 · worker/src/admission.mjs · battery group K · concurrency.sh group P |
| Entrypoint composition battery | local | Drives the real fetch handler, not modules. Exists because a JWKS rate-cap that passed its own module test did not hold at the entrypoint. | worker/test/entrypoint.test.mjs |
| Function-level privilege separation | local | PUBLIC holds EXECUTE on nothing; only the api role may call a resolver. Fixed after a review found readonly could mint principals. | worker/migrations/0009_function_acl.sql · battery D12-D17 |
| Marketing + status surface | local | These pages. Built, not served. | cloud.computedriven.com/ |
| Object-scoped write authority (M2 shape) | local | One exact object key, not a prefix: a credential for .../chunk/abc cannot reach .../chunk/def. PutObject only. The intent carries expected bytes and a content digest and cannot over-commit its reservation CONCURRENTLY -- serialized on the reservation row with FOR UPDATE, the same lock finalize_storage() and abort_storage() have taken since 0014. An object key binds the whole promise {reservation, key, bytes, digest} (R43). THE STATE MACHINE IS THREE STATES AND ONE IS TERMINAL: offered, observed, client_abandoned. `observed` is provider truth and no local transition may walk it back (R54); `client_abandoned` is a DECLARATION that releases nothing, because abandoning a row in our database does not revoke a presigned URL already in the clients hands (R55). Only the reservation dying releases bytes, which is why the byte arithmetic has no state predicate at all. | worker/migrations/0019 + 0022 · concurrency groups Q and R · CD_SABOTAGE=offer · CD_SABOTAGE=observe |
| OIDC subject → principal → organization | local | Resolvers are implemented and tested, and idempotent under CONCURRENCY as well as in sequence. The mechanism CHANGED this round: 0013 used a PostgreSQL advisory lock, which Hyperdrive lists as unsupported — correct PostgreSQL that the production wire refuses. 0015 serializes on the unique partial index instead. No Keycloak has issued a real token to them. | worker/migrations/0015_provider_safe_identity.sql · battery groups F and L · ./worker/test/concurrency.sh group N |
| OIDC token verification (RS256, pinned) | local | Algorithm pinned, never read from the token; iat required, crit refused, forced JWKS refresh rate-capped. No realm has issued it a real token. | worker/src/jwt.mjs · worker/test/jwt.test.mjs |
| Organizations resolved by stable IdP id | local | Alias is for selection only. A token carrying aliases without ids is refused as a realm misconfiguration. | worker/src/authz.mjs organizationEntries() · battery group F |
| Pigsty schema + migrations | local | Applies clean, with a working negative control and a regression proof. THE VERSION IT WAS MEASURED ON IS A FACT ABOUT THE RUN, NOT ABOUT THIS SENTENCE: the battery block below records the migration count, the PostgreSQL major it ran against, and whether anything warned. R33 targets 17.x because Hyperdrive documents known support for 9.0-17.x, and the batteries now default to the target build when it is installed rather than to whatever the box happens to have. This note used to carry its own copy of all three numbers and went stale while every gate stayed green -- scripts/check-status-prose.mjs is why it cannot again. | worker/migrations/ · ./worker/test/tenant-isolation.sh |
| Principal / organization suspension | local | suspended and closed now refuse by name. They were decorative until 2026-08-21. | worker/migrations/0008_principal_status.sql · battery group G |
| Provider authority may not exceed scope | local | Two laws, not one: no widening (safety) AND no under-granting (functionality). Checked against the PROVIDER-NATIVE grant through a declared translation table, because comparing R2's action names to ComputeDriven's directly made a correct R2 adapter fail its own law. Closed vocabulary: an action R2 does not define is refused as unknown. No provider is bound to it yet. | worker/src/r2creds.mjs assertRefines() · worker/src/r2native.mjs · worker/test/r2native.test.mjs |
| Provider-observed storage accounting (R32) | local | R32 is PROPOSED, not ruled, and is built anyway so it can be ruled with a number. THREE THINGS EARLIER ROUNDS GOT WRONG HERE. The consumer was UNREACHABLE -- it runs as computedriven_ledger and cd.organizations had no policy admitting that role, so every event including every valid one was refused (measured: count returns 0 with a row present). An append-only log is not an inventory (R41): R2 fires object-create on OVERWRITE, so summing the log charged a replaced body twice. And the queue consumer sat OUTSIDE the lock protocol, so a provider observation could be overwritten by a request-path replay -- measured, group R: fresh write authority handed out for an object R2 already held. DELIVERY identity is the queue message id, read from the ENVELOPE so a producer cannot choose it; (bucket, key) is object identity; etag and event_time are object state (R57). PROVIDER-EVENT identity is not exposed at all -- R2's notification body carries account, action, bucket, object and eventTime and no event id -- so what we have is exactly-once processing of a queue MESSAGE, and this note called that 'event identity' for a day after R57 was narrowed. Still runs against FIXTURE events -- no bucket, no queue, no notification rule. | worker/migrations/0021 + 0022 · worker/src/reconcile.mjs · battery groups M and O · concurrency group R |
| R2 credential scope computation | local | The prefix is BUILT from the organization id rather than validated against it, so no argument combination reaches another tenant. A scope also carries an unforgeable provenance brand: grantForScope() used to accept any object with a string `prefix`, so a hand-built whole-bucket scope produced a signable grant. | worker/src/r2creds.mjs |
| Request path is Hyperdrive-compatible | local | Battery group L asks the DATABASE which functions the API role may execute and reads their bodies, so advisory locks, LISTEN/NOTIFY and PREPARE/DEALLOCATE on the request path fail a check rather than a deployment. Exists because a ruled fix went green locally and Hyperdrive refuses the mechanism it used. It proves the schema is compatible; it CANNOT prove Hyperdrive accepts it — that needs a real binding. | ./worker/test/tenant-isolation.sh group L · worker/test/live-falsifier.mjs cases F, G, H |
| Server-derived object permission | local | The client requests an operation; ROLE and WORLD STATUS decide what it may become. A viewer asking to write is refused, not downgraded. Entitlement and quota are now part of the decision too — a write permission requires a storage admission (R24). | worker/migrations/0010_membership_role.sql · worker/src/authz.mjs permissionFor() |
| status.json battery numbers are derived, not typed | local | Runs all five battery invocations and refuses to agree with a stale number. Exists because this file said “10 migrations” against a tree of twelve, and published a sabotage control (66/8) and a regression proof (28) from four migrations earlier — control results nobody re-runs casually are the ones that go stale. The RUNGS were already generated into the pages by tools/render-status.mjs --verify; this covers the counts, which were not. | scripts/cloud-status-counts.mjs · cloud.computedriven.com/tools/render-status.mjs --verify |
| Tenant isolation (fail-closed RLS) | local | Clean, with a working negative control and a regression proof against migrations 0008-0010 removed. Counts in the battery block. | ./worker/test/tenant-isolation.sh · CD_SABOTAGE=1 for the control |
| Transaction-entry tenant context | local | Context is established on entering the transaction. The brand proves a tenant was minted by deriveTenant() and not hand-built — it does NOT prove the id came from a successful authentication, since deriveTenant() is exported and trusted code could misuse it. | worker/src/tenant.mjs · worker/test/worker.test.mjs |
| Unforgeable verified-claims brand | local | verifyToken brands its result in a module-private WeakSet. Nothing exported can add to it, so a structural clone of verified claims is still refused. | worker/src/jwt.mjs assertVerified() |
| Verified claims are frozen, not just branded | local | The brand proved an object WAS ONCE verified; mutating it kept the brand. Deep-frozen before branding, including the nested organization claim. | worker/src/jwt.mjs deepFreeze() · battery |
| Commerce — checkout, payment, subscriptions | spec | M6, and now ONLY commerce. Entitlement and quota are authorization and moved ahead of M2 (R24); they are built and local — see storage_admission. What is missing here is checkout, payment and subscription lifecycle, plus the metered compute SKU that a storage tier cannot absorb. | CLOUD_V1.md §5 |
| ComputeDriven-hosted capacity (Hetzner) | spec | M5. No paid infrastructure has been provisioned. | CLOUD_V1.md §4 |
| Control-plane API surface | spec | Routes and refusal mapping exist and parse, and the credential route now takes the world lookup and the storage reservation in ONE transaction. connect() throws by design — no Hyperdrive driver adapter has been written. | worker/src/index.mjs · worker/wrangler.toml |
| Cross-request JWKS refresh admission | spec | The module-scope cache is best-effort in-isolate only. A durable limit needs a cross-request mechanism and is NOT claimed. | worker/src/index.mjs jwksFor() |
| Keycloak realm on Fly | spec | Ruled (R4) to Fly iad, database on PlanetScale. Not provisioned. | agents/C-infrastructure.md §3 |
| Live-provider falsifier (plan only) | spec | Questions no local test can answer — see falsifier_cases for how many — each written down with what it would falsify and what we would do about it BEFORE it is run, so a negative result stays informative instead of becoming something to explain away. It PROVISIONS NOTHING and refuses to run without real bindings. Creating the account, bucket and queue is a spend decision. The count is DERIVED as of round 7.3: this note said “Eight” against an array of nine for a full round, and the prose gate could not see it because a fact that lives only in prose has nothing to disagree with. | worker/test/live-falsifier.mjs --plan |
| LOCAL / FLEET placement | spec | M4. | CLOUD_V1.md §4 |
| Minting real R2 credentials | spec | The scope logic is local; the provider is not. No bucket, no account. FakeR2Provider is the only implementation and its keys say so. | worker/src/r2creds.mjs R2Provider · agents/D-worker-auth.md |
| Portal sign-in (BFF, server session) | spec | No portal exists. | CLOUD_V1.md §4 M1 |
| Provider-enforced byte ceiling on a write credential (R31) | spec | OPEN, AND DELIBERATELY NOT CLAIMED. R2 temporary credentials constrain bucket, operations, prefixes/objects and TTL and document no maximum byte count; presigned URLs document Content-Type as signed and ENFORCED and say nothing about Content-Length. So reserving 1 MB does not stop R2 accepting 500 MB — the database refusal at finalize is correct and too late. THE EXPERIMENT HAS BEEN RE-AIMED: the S3-compatibility page documents neither Content-Length nor x-amz-checksum-sha256 for PutObject, so the old case B would have measured our header choice rather than R2. It does document Content-MD5, which binds CONTENT and is strictly stronger than a byte ceiling — a bound digest refuses a longer body, a shorter body and a same-length different body alike. Two questions hide in one: does the signature bind the header, and does R2 verify the digest against the body. | CLOUD_V1.md §6.5 · worker/test/live-falsifier.mjs case B |
| Remote execution on a node | spec | A Cloud request is a request. The local runtime verifies and may refuse. | CLOUD_V1.md §7 |
| Restore onto a second machine | spec | M2. R23 is RULED: machine authority is NOT restored — data access is separately reacquired through a client-held User Recovery Key that unwraps the World Data Key. Revocation governs future Cloud access; it is not retroactive erasure, and we say so. | CLOUD_V1.md §6.2b · REVISION_REGISTER.md R23 |
| Super node identity + Fleet + revoke | spec | M3. | CLOUD_V1.md §4 |
| TRVM-governed Cloud transitions | spec | TRVM P4 is proven in its own tree and is wired to nothing here. Cloud behaviour is Postgres and application convention, and is called that. | TRVM/governance/round-11-ledger.md |
| Worker → Pigsty over Hyperdrive | spec | Ruled (R7). No binding exists; the schema has only ever been reached over a local socket. | CLOUD_V1.md §3 |
| World sync — chunk, hash, upload | spec | M2. The WRITE SHAPE is decided (R31 work): one bounded content-addressed chunk, one reservation, one EXACT object key, one short-lived artifact — so the session can last days while each grant lasts minutes. Multipart is NOT authorized; the claim that M2 would need it is retracted, because a 2 TB world is not a 2 TB object. No chunking implementation exists. | CLOUD_V1.md §6.6 · worker/migrations/0017 · worker/src/r2native.mjs |
What "not deployed" means here
No account has been created and no infrastructure has been provisioned by this work. There is no Keycloak realm, no Hyperdrive binding, no object bucket and no hosted machine. The schema has only ever been reached over a local socket, by a test that builds a throwaway database and destroys it again.
Where a subsystem is unavailable it is named spec rather than
stubbed out behind a mock that would report success. A mock that passes for
production is worse than a missing feature, because only one of the two
tells you the truth.