# Census: db region forward, plan 1 (the box side and the wire, dark)

Status: ACCEPTED in full by the census owner on #280, 2026-10-22, as
`census-ruling-db-region-forward-1`. Every row now carries
`rev3=census-ruling-db-region-forward-1`, and the H3 removal names
`census-ruling-db-region-forward-1 (accepted by the census owner on #280,
2026-10-22)`. The ruling covers every row below: the H3 retirement of
`runQueryContext`, the J1/J4 legacy rows, the diagnostic probe row, the two
external unit rows, the re-frozen and re-pinned changed units (including the
shared `ensureSystemArmed`), the 9B8 nonrecord puts and the re-pinned
envelopes.

The census owner's rationale: every new read runs as the agent (carlos) or
the sandboxed DynamicUser worker, not root. The only root change is
`ensureSystemUnits` installing two fixed embedded units. The worker reads
only the files the agent sent and its private scratch. `SelfView`'s open of
host.env only confirms that the sandbox hides it.

The branch is `db-region-forward-1`, cut from `design-db-region-forward`
(e765d57d, which already holds main at 5f341f33). It builds plan 1 of
`docs/superpowers/specs/2026-10-22-db-region-forward-design.md` (rev 6):
`internal/dbquery` (the query runner moved out of `db.go`),
`internal/dbreads` (the wire, the box's db-reads agent and the db-read
worker), `dbdoor` planning over any replica reader, the two
`carlos-dbread` units installed and armed by `ensureSystemUnits`, and the
`db-read-1` capability. Nothing runs unless a console asks, and no console
asks until plan 2.

It adds one root-read overlay,
`internal/provision/testdata/plan14-root-reads-db-region-forward-1.tsv`,
loaded last in `TestPlan14RootReadCensus`, after `console-brand-name` (the
overlay main loads last at 5f341f33). Its record-write entries are layered
on `plans2WriteCensusInventory` in
`internal/provision/db_region_forward_census_test.go`.

## Root reads

Executing identities, named once:

- **the agent**: `/opt/carlos/carlos agent`, `carlos-edge.service`, user `carlos`.
- **the worker**: `carlos system db-worker`, `carlos-dbread@.service`, a
  `DynamicUser` with `PrivateNetwork=yes`, `RestrictAddressFamilies=AF_UNIX`,
  its own `PrivateTmp`, `/data`, `/var/lib`, `/opt/carlos` and `/run/carlos`
  empty, `/etc/carlos` inaccessible. It reads only the files the agent sent
  and its own scratch.
- **the CLI**: `carlos db query` on a person's machine, as the person.

| unit | change | executing identity | proposed disposition |
|---|---|---|---|
| `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 |
| `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 |
| `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 |
| `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 |
| `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 |
| `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 |
| `internal/dbreads/worker.go` `ServeWorker` | new: stats the restored copy in its private `/var/tmp` for the size cap | the worker | legacy, J4 |
| `internal/dbreads/worker.go` `receiveFile` | new: creates each planned file, exclusively, under its private `/var/tmp` | the worker | legacy, J4 |
| `internal/host/dbreads_linux.go` `dbReadUnits` | new: `systemctl list-units 'carlos-dbread@*.service'`, readable without privilege, for the orphan rule | the agent | legacy, J4 |
| `internal/host/dbreads_linux.go` `varTmpFree` | new: one `Statfs` of `/var/tmp` for the disk reservation | the agent | legacy, J4 |
| `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 |
| `internal/host/units/carlos-dbread@.service` `external` | new unit file | the worker's unit | legacy external unit |
| `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` |
| `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 |
| `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 |
| `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 |
| `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 |
| `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 |

Scanner notes for the ruling:

- `internal/dbdoor` `ListLevel`, `PlanFrom`, `finishPlan`, `listing.*` and
  `RestoreLocal`, which the plan expected to need rows, are not named by the
  scanner (their I/O goes through replica-client methods it does not
  match), so they have none. Their package envelope is re-pinned.
- `internal/console/apidb.go` `listReplicaLevel` and `dbClassRefusal` now
  delegate to `dbdoor.ListLevel` and `dbreads.ClassRefusal`; the scanner
  names neither, and the `internal/console` envelope is re-pinned.

Envelopes re-pinned: `.`, `internal/console`, `internal/dbdoor`,
`internal/dbquery` (new), `internal/dbreads` (new),
`internal/dbreads/dbreadstest` (new), `internal/host`, `internal/host/units`.

Seal: console-brand-name's `78e18e222a8178bbe16b638c5e3811b8f64c4c2664ffd6649eb4273e72fae24e`
is checked first; with this overlay's 12 frozen declarations it becomes
`fc73854854f9e75d122ebd0eacf330d126201aa9d5c4295c634cc1c204a9168d` with the
accepted ruling's markers (it was `20d70361...` while proposed, and
`d57f153e...` before the round 1 fixes below).

## Record writes (9B8)

| site | class | why |
|---|---|---|
| `internal/dbreads/agent.go` `Agent.elect` | nonrecord | the create-only `db-reads/<id>/picked` in the deployment bucket |
| `internal/dbreads/chunks.go` `putCreateOnly` | nonrecord | create-only sealed chunks and `result.json` under `db-reads/<id>/` |
| `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 |

No record writer and no `Delete` site is added.

## Not in this plan

The console route (`registerCLIRoutes` key-site, `claim-routes.txt`) and its
creates (`request.json`, the queue entry, `cancel`) are plan 2's.

## Round 1 fixes (Astra, 1760ea5f)

Three rows changed with the fixes; each keeps its disposition and appends a
`round 1` clause:

| unit | change | disposition |
|---|---|---|
| `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 |
| `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 |
| `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 |

One read the census does not see, named for the ruling:
`internal/host/dbreads.go` `dbReadsSocketWanted` reads `/etc/carlos/host.env`
as root (from `ensureSystemUnits`, the adopt pass) through the
`readClaimUnitFile` variable, which the scanner does not match, so it has no
row. It reads six db-reads keys and decides only whether the db-read socket
is armed; it refuses (keeps the socket dark) on anything it cannot read or
parse. The claim unit settings already read the same file the same way.

The check-B fix (`afterCheckB`) and the deadline fix (`serve`) add no read
the scanner names; the `internal/dbreads` and `internal/host` envelopes are
re-pinned.
