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 deniedon the same tree). The check built to catch that class cannot see the tenant it happened to.
Options
- Give the drill the replicator's read access — the same root drop-in
carlos-litestream.servicecarries. 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. - A narrower grant —
SupplementaryGroups/ACLs won't help for/var/lib/private(root-only traversal by design), so this likely collapses into (1). - 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).