# Census: db region forward, plan 3 (`carlos db pull` and `pull --at` for an instance in another region)

Status: ACCEPTED in full by the census owner on #280, 2026-10-23, as
`census-ruling-db-region-forward-3`, for every row below and the round 1 fix
rows. Every row now carries `rev3=census-ruling-db-region-forward-3`; the
`@legacy` fingerprints were recomputed and the seal moved to
`79cb56b4c7d3a8e155d1eff1902715d818fcf4c09b1d8a82921c413b116938a9`.

The census owner's rationale: the box-side reads stay the agent's or the
sandboxed worker's; the CLI reads only its own spool; the `admitDBRead`
re-pin is the same write class; Astra matched every digest.

The branch is `db-region-forward-3`, cut from amadan `main` at 0d9e81dc. It
builds plan 3 of `docs/superpowers/specs/2026-10-22-db-region-forward-design.md`
(rev 7): the box's worker holds a pull's planned LTX files, verifies their
checksums and sends them back after the agent's check B; the agent seals them
as the `ltx` stream in 1 MiB chunks, with the plan in the summary; the console
forwards and audits the new op; the CLI spools the sealed chunks to a private
directory, checks the file list, the count and the SHA-256, restores locally
with `dbdoor.RestoreLocal` and saves the copy with today's `copyOut`. The
box never restores for a pull.

It adds one root-read overlay,
`internal/provision/testdata/plan14-root-reads-db-region-forward-3.tsv`,
loaded last in `TestPlan14RootReadCensus`, after `static-redirects` (the
overlay main loads last at 0d9e81dc). Its record-write entry is layered on
`plans3aWriteCensusInventory` in
`internal/provision/db_region_forward_3_census_test.go`.

## Root reads

Executing identities, named once:

- **the worker**: `carlos system db-worker` under `carlos-dbread@.service`, a
  DynamicUser with no network, its own PrivateTmp, `/data` and `/etc/carlos`
  hidden.
- **the CLI**: `carlos db pull` on a person's machine, as the person.

The scanner names no agent, console or host unit: the agent's pull branch in
`attempt`, `NewAgent`'s `MaxPull`, the console's op check and audit detail,
and `CARLOS_DB_PULL_MAX_BYTES` in `dbReadsSettings` make no read it counts
(plan 1's R8 again).

| unit | change | executing identity | proposed disposition |
|---|---|---|---|
| `internal/dbreads/worker.go` `holdAndSend` | new: opens each planned LTX file it received and verified, in its own private `/var/tmp`, after check B, and copies it back over its socket | the worker | legacy, accepted-legacy, J4 (`root-input`, as `receiveFile`) |
| `internal/dbreads/worker.go` `verifyLTX` | new: opens one received LTX file and decodes it whole to check its checksums, before ready | the worker | legacy, accepted-legacy, J4 |
| `internal/dbreads/worker.go` `ServeWorker` | changed: accepts a pull header and hands it to `holdAndSend` after the same bounded receive; the `Stat` is unchanged | the worker | re-frozen at `census-ruling-db-region-forward-1` with this change appended |
| `dbforward.go` `dbPullForward` | new: the same bounded 64 KiB read of a refused reads-route answer as `dbQueryForward` (`io.ReadAll` over the response body, not a file); creates and removes the pull's private 0700 directory | the CLI | legacy, accepted-legacy, J4 (main's footing for `db.go`'s units) |
| `dbforward.go` `fileSink.chunk` | new: reads back one sealed chunk it spooled itself, 0600 in that directory, admitted by the chunk bound before it was written | the CLI | legacy, accepted-legacy, J4 |
| `dbforward.go` `openDBPull` | new: creates each planned LTX file exclusively (`O_EXCL`, 0600) in that directory, only after the whole file list is checked; then restores, checks and removes them | the CLI | legacy, accepted-legacy, J4 |
| `internal/dbdoor/plan_from.go` `ctxReadCloser.Read` | new (Astra round 1, finding 2): `RestoreLocal` wraps its local replica's reads so each checks the restore's context first; litestream's compaction checks none of its own | the CLI and the worker | legacy, accepted-legacy, J4 (as main's `downloaded.Read`) |
| `dbforward.go` `dbQueryForward` | changed: keeps its chunks in a `memSink` and opens them after `readDBForward` returns the result; the same bounded refusal read | the CLI | re-frozen at `census-ruling-db-region-forward-2` with this change appended |

Round 1 (Astra on cc77ca95): `ServeWorker` now checks every received file's
LTX header against its plan entry through `verifyLTX`, for a query as well as a
pull, and the plan's end against its target; `holdAndSend` only sends the checked
files back; `openDBPull` compares each rebuilt file's header with its manifest
entry and checks its context per chunk. Their rows carry a round 1 clause; no
read is added to them.

Envelopes re-pinned: `.`, `internal/console`, `internal/dbdoor`,
`internal/dbreads`, `internal/host`. Frozen declarations are added, so the seal moves:
static-redirects' `65857c29…` is checked first, the proposed seal was
`36ecd2eca043f1d0056f086be47ba1fb887dcdc2eda67427e698c2b6d1b9031b` (79cb56b4… once the markers flipped).

## Record writes (9B8)

`internal/console/apidbforward.go` `Server.admitDBRead` is re-digested and
stays `nonrecord`: it admits a pull beside a query and audits it with its own
detail; its writes are the same conditional creates under `db-reads/` in the
deployment bucket. No write site is added.

## Unchanged

Claim routes (no new route: a pull uses plan 2's reads route), `go.mod`.
