| 1 | # Census: db region forward, plan 1 (the box side and the wire, dark) |
| 2 | |
| 3 | Status: ACCEPTED in full by the census owner on #280, 2026-10-22, as |
| 4 | `census-ruling-db-region-forward-1`. Every row now carries |
| 5 | `rev3=census-ruling-db-region-forward-1`, and the H3 removal names |
| 6 | `census-ruling-db-region-forward-1 (accepted by the census owner on #280, |
| 7 | 2026-10-22)`. The ruling covers every row below: the H3 retirement of |
| 8 | `runQueryContext`, the J1/J4 legacy rows, the diagnostic probe row, the two |
| 9 | external unit rows, the re-frozen and re-pinned changed units (including the |
| 10 | shared `ensureSystemArmed`), the 9B8 nonrecord puts and the re-pinned |
| 11 | envelopes. |
| 12 | |
| 13 | The census owner's rationale: every new read runs as the agent (carlos) or |
| 14 | the sandboxed DynamicUser worker, not root. The only root change is |
| 15 | `ensureSystemUnits` installing two fixed embedded units. The worker reads |
| 16 | only the files the agent sent and its private scratch. `SelfView`'s open of |
| 17 | host.env only confirms that the sandbox hides it. |
| 18 | |
| 19 | The branch is `db-region-forward-1`, cut from `design-db-region-forward` |
| 20 | (e765d57d, which already holds main at 5f341f33). It builds plan 1 of |
| 21 | `docs/superpowers/specs/2026-10-22-db-region-forward-design.md` (rev 6): |
| 22 | `internal/dbquery` (the query runner moved out of `db.go`), |
| 23 | `internal/dbreads` (the wire, the box's db-reads agent and the db-read |
| 24 | worker), `dbdoor` planning over any replica reader, the two |
| 25 | `carlos-dbread` units installed and armed by `ensureSystemUnits`, and the |
| 26 | `db-read-1` capability. Nothing runs unless a console asks, and no console |
| 27 | asks until plan 2. |
| 28 | |
| 29 | It adds one root-read overlay, |
| 30 | `internal/provision/testdata/plan14-root-reads-db-region-forward-1.tsv`, |
| 31 | loaded last in `TestPlan14RootReadCensus`, after `console-brand-name` (the |
| 32 | overlay main loads last at 5f341f33). Its record-write entries are layered |
| 33 | on `plans2WriteCensusInventory` in |
| 34 | `internal/provision/db_region_forward_census_test.go`. |
| 35 | |
| 36 | ## Root reads |
| 37 | |
| 38 | Executing identities, named once: |
| 39 | |
| 40 | - **the agent**: `/opt/carlos/carlos agent`, `carlos-edge.service`, user `carlos`. |
| 41 | - **the worker**: `carlos system db-worker`, `carlos-dbread@.service`, a |
| 42 | `DynamicUser` with `PrivateNetwork=yes`, `RestrictAddressFamilies=AF_UNIX`, |
| 43 | its own `PrivateTmp`, `/data`, `/var/lib`, `/opt/carlos` and `/run/carlos` |
| 44 | empty, `/etc/carlos` inaccessible. It reads only the files the agent sent |
| 45 | and its own scratch. |
| 46 | - **the CLI**: `carlos db query` on a person's machine, as the person. |
| 47 | |
| 48 | | unit | change | executing identity | proposed disposition | |
| 49 | |---|---|---|---| |
| 50 | | `db.go` `runQueryContext` | still declared; reads nothing now: a one-line call to `dbquery.RunContext` | the CLI | retired: `@remove` plus `@removed ... H3 census-ruling-db-region-forward-1`, the form the carlos-run H3 removal used | |
| 51 | | `internal/dbquery/dbquery.go` `RunContext` | new: `runQueryContext`'s body moved unchanged but for the `immutable=1` open option the worker passes | the CLI and the worker; neither is root | legacy, accepted-legacy, J4 (`basis=root-input`, main's footing for the same body), both callers named | |
| 52 | | `internal/dbreads/agent.go` `nonce` | new: 16 bytes from `crypto/rand` for `picked`'s attempt nonce (the scanner reads `rand.Read` as a read) | the agent | legacy, J1/J4 route-and-input, as the offline move's agent units | |
| 53 | | `internal/dbreads/read.go` `sendFile` | new: reads one planned LTX file whole from the box's own region replica (read-only reader), between identity checks A and B, and frames it to the worker undecoded | the agent | legacy, J1/J4 route-and-input | |
| 54 | | `internal/dbreads/seal.go` `open` | new: AES-GCM open of a sealed chunk or summary (`cipher.AEAD.Open`, read by the scanner as `Open`) | the CLI (plan 2); no production caller in plan 1 | legacy, J4 | |
| 55 | | `internal/dbreads/worker.go` `SelfView` | new: `/proc/self/net/dev`, a listing of `/data`, an open of `/etc/carlos/host.env`, only to report what the sandbox hides | the worker | legacy, J4 | |
| 56 | | `internal/dbreads/worker.go` `ServeWorker` | new: stats the restored copy in its private `/var/tmp` for the size cap | the worker | legacy, J4 | |
| 57 | | `internal/dbreads/worker.go` `receiveFile` | new: creates each planned file, exclusively, under its private `/var/tmp` | the worker | legacy, J4 | |
| 58 | | `internal/host/dbreads_linux.go` `dbReadUnits` | new: `systemctl list-units 'carlos-dbread@*.service'`, readable without privilege, for the orphan rule | the agent | legacy, J4 | |
| 59 | | `internal/host/dbreads_linux.go` `varTmpFree` | new: one `Statfs` of `/var/tmp` for the disk reservation | the agent | legacy, J4 | |
| 60 | | `internal/host/units/carlos-dbread.socket` `external` | new unit file | written by root's `ensureSystemUnits`; bound by PID 1, owned by carlos | legacy external unit, as `carlos-instance-cmd@.service` took | |
| 61 | | `internal/host/units/carlos-dbread@.service` `external` | new unit file | the worker's unit | legacy external unit | |
| 62 | | `internal/dbreads/agent.go` `Agent.probe` | new: the readiness probe, a hello on its own worker connection; the scanner names no read in it, so this is a declaration row | the agent | **diagnostic**, J4: decides only whether the box publishes `db-read-1` | |
| 63 | | `internal/host/edgesfile.go` `edgesFileLoop` | changed legacy: `db-read-1` joins the capabilities while the armed loop's probe passes; the published draining flag is stored for the loop; read site unchanged, offset moved | the agent | re-frozen at main's disposition (`census-ruling-moves-offline`) with the change appended | |
| 64 | | `internal/host/agent.go` `fastConvergeLoop` | changed authority: the serial pass nudges the db-reads loop after the runs pass (a channel send) | the agent | re-pinned at main's B3 | |
| 65 | | `internal/host/agent.go` `runAgent` | changed authority: `armDBReads` beside `armRunsPass` in the activator's Configure step | the agent | re-pinned at main's B,C,D,F | |
| 66 | | `internal/host/systemunits.go` `ensureSystemUnits` | changed authority: installs the two units, asks one more `is-active`, arms the socket (never the template); the armed list is now `ensureSystemArmed`, which the claim census reads too | root (the adopt pass) | re-pinned at main's F,J4 | |
| 67 | | `system.go` `runSystem`, `systemVerb` | changed declarations (no reads): dispatch `carlos system db-worker` | the operator CLI and the worker unit | re-pinned at main's authority rows | |
| 68 | |
| 69 | Scanner notes for the ruling: |
| 70 | |
| 71 | - `internal/dbdoor` `ListLevel`, `PlanFrom`, `finishPlan`, `listing.*` and |
| 72 | `RestoreLocal`, which the plan expected to need rows, are not named by the |
| 73 | scanner (their I/O goes through replica-client methods it does not |
| 74 | match), so they have none. Their package envelope is re-pinned. |
| 75 | - `internal/console/apidb.go` `listReplicaLevel` and `dbClassRefusal` now |
| 76 | delegate to `dbdoor.ListLevel` and `dbreads.ClassRefusal`; the scanner |
| 77 | names neither, and the `internal/console` envelope is re-pinned. |
| 78 | |
| 79 | Envelopes re-pinned: `.`, `internal/console`, `internal/dbdoor`, |
| 80 | `internal/dbquery` (new), `internal/dbreads` (new), |
| 81 | `internal/dbreads/dbreadstest` (new), `internal/host`, `internal/host/units`. |
| 82 | |
| 83 | Seal: console-brand-name's `78e18e222a8178bbe16b638c5e3811b8f64c4c2664ffd6649eb4273e72fae24e` |
| 84 | is checked first; with this overlay's 12 frozen declarations it becomes |
| 85 | `fc73854854f9e75d122ebd0eacf330d126201aa9d5c4295c634cc1c204a9168d` with the |
| 86 | accepted ruling's markers (it was `20d70361...` while proposed, and |
| 87 | `d57f153e...` before the round 1 fixes below). |
| 88 | |
| 89 | ## Record writes (9B8) |
| 90 | |
| 91 | | site | class | why | |
| 92 | |---|---|---| |
| 93 | | `internal/dbreads/agent.go` `Agent.elect` | nonrecord | the create-only `db-reads/<id>/picked` in the deployment bucket | |
| 94 | | `internal/dbreads/chunks.go` `putCreateOnly` | nonrecord | create-only sealed chunks and `result.json` under `db-reads/<id>/` | |
| 95 | | `internal/dbreads/dbreadstest/dbreadstest.go` `Submit` | testsupport | test support imported only by `_test.go` files: the console's two creates for a test with no console, as leasetest's sites are | |
| 96 | |
| 97 | No record writer and no `Delete` site is added. |
| 98 | |
| 99 | ## Not in this plan |
| 100 | |
| 101 | The console route (`registerCLIRoutes` key-site, `claim-routes.txt`) and its |
| 102 | creates (`request.json`, the queue entry, `cancel`) are plan 2's. |
| 103 | |
| 104 | ## Round 1 fixes (Astra, 1760ea5f) |
| 105 | |
| 106 | Three rows changed with the fixes; each keeps its disposition and appends a |
| 107 | `round 1` clause: |
| 108 | |
| 109 | | unit | change | disposition | |
| 110 | |---|---|---| |
| 111 | | `internal/host/units/carlos-dbread.socket` `external` | listens at `/run/carlos/dbread/worker.sock`, under the `/run/carlos` tree every tenant unit hides (tenants run as carlos, the uid the worker serves), and is armed only where host.env arms db reads | re-frozen, legacy external unit | |
| 112 | | `internal/dbreads/worker.go` `ServeWorker` | refuses, before writing anything, a plan whose restored-size cap is above its own LimitFSIZE or whose files total more than twice that cap | re-frozen, legacy J4 | |
| 113 | | `internal/host/systemunits.go` `ensureSystemUnits` | arms the socket only when the installed host.env arms db reads (same rule as the capability) and stops and disables it elsewhere | re-pinned at main's F,J4 | |
| 114 | |
| 115 | One read the census does not see, named for the ruling: |
| 116 | `internal/host/dbreads.go` `dbReadsSocketWanted` reads `/etc/carlos/host.env` |
| 117 | as root (from `ensureSystemUnits`, the adopt pass) through the |
| 118 | `readClaimUnitFile` variable, which the scanner does not match, so it has no |
| 119 | row. It reads six db-reads keys and decides only whether the db-read socket |
| 120 | is armed; it refuses (keeps the socket dark) on anything it cannot read or |
| 121 | parse. The claim unit settings already read the same file the same way. |
| 122 | |
| 123 | The check-B fix (`afterCheckB`) and the deadline fix (`serve`) add no read |
| 124 | the scanner names; the `internal/dbreads` and `internal/host` envelopes are |
| 125 | re-pinned. |
| 126 | |