carlos / platform Public

restore-verify cannot check currency for DynamicUser tenants: the drill runs as carlos, the replicator as root Issue open

Migrated from github.com/carlosframework/platform issue #219, opened 2026-08-16. GitHub issues are closed for this repo; see AGENTS.md, "Work is tracked on amadan, not GitHub".

Still outstanding: restore-verify runs as carlos and cannot read a DynamicUser tenant's replica, so it cannot judge currency.

---

Surfaced by the first live run of the currency check (#211), and reported honestly rather than papered over by #218.

The gap

ok    /var/lib/carlos-app/home.kass.training/instances/5vepbveytb8y.db
        (freshness unknown: source header unreadable: ... permission denied)

carlos-restore-verify.service runs User=carlos. carlos-litestream.service runs root, via a drop-in added because unit tenants' state directories are DynamicUser-owned under /var/lib/private (0700 root traversal). So the replicator can read those databases and the drill cannot.

Consequences:

  • Their restorability is still proven — the drill restores from S3, which needs no local read.
  • Their currency is unknowable to the drill, which now says so per line and in the summary (#218).
  • That is precisely the tenant whose replication died on 2026-08-11 (permission denied on the same tree). The check built to catch that class cannot see the tenant it happened to.

Options

  1. Give the drill the replicator's read access — the same root drop-in carlos-litestream.service carries. Simplest, and symmetrical: a pass that verifies what root replicates arguably needs what root can read. It is a privilege increase on a production unit, which is why I stopped here rather than doing it unattended.
  2. A narrower grant — SupplementaryGroups/ACLs won't help for /var/lib/private (root-only traversal by design), so this likely collapses into (1).
  3. Have the replicator publish position instead of the drill reading the source — larger, but avoids the privilege question entirely and would work for any tenant shape.

Related

  • #216 (generator takeover blocked on kass's db_path) touches the same tenant for a different reason.
  • The currency check itself: #211 (box-wide), #214 (hibernation), #218 (summary honesty).