Isolated exec, plan 4: the nested layout and `carlos layout migrate`/`revert` (#126)
Every exec tenant on a box has run "flat": a child of the edge, uid carlos, in the edge's mount namespace with /data writable. Any tenant could read or write every other tenant's database, env and config files, the box's replication credentials and TLS keys, hijack sibling sockets, and kill siblings or the edge. The nested layout gives each instance its own unit, PID namespace and view of /data. This lands the box-side pass that moves instances into it under a layout intent, journalled and recoverable, with a revert path. On top of the reviewed plan 4 work (branch isolated-exec-layout), this adds: - The edge unit drops RestrictSUIDSGID=, which made openat2 return ENOSYS for the agent. instancefs.SelfCheck runs in the agent's own unit and is the nested gate's first question, so the blocker can't silently return. - Migration can't be pre-planted by a flat tenant: unplanned entries in an instance's destination are quarantined to /data/layout/aside/<host>/, files with a second hard link are refused, and the result is checked again after the renames, before the row moves. Leftover env/corrupt files are kept outside every instance namespace, and a blocker at the aside path is displaced rather than taken as a reason to hold. - An awake tenant can't defer its own migration forever. After a 1h grace it is stopped through the normal fence and moved. A move refused for a reason the tenant could have planted keeps the host fenced and down (held) until the reason clears. Holds are persisted and re-derived from the files at agent start, and retried every 10 minutes. - `carlos status --layout` warns when a box runs nested instances beside flat, foreign or external ones, because nested isolation isn't in force there until none remain. Nothing changes on a box until an operator records a nested layout intent. Rollout (spec §8.6) needs separate approval. Plan 5 (the console door and `--force`) follows. Reviewed by Fable over five rounds (F1-F3, G1-G3, follow-ups, merge with main); CI green on every tip. Pre-squash history: isolated-exec-layout and tenant-iso-layout @468e4974.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cli.go+55 −0cli_test.go+92 −0docs/site/cli.md+30 −0docs/superpowers/plans/2026-09-25-isolated-exec-4-layout-amendments.md+133 −0docs/superpowers/specs/2026-09-11-isolated-exec-design.md+2 −1internal/activator/maintenance.go+63 −1internal/activator/maintenance_test.go+136 −0internal/appconfig/syncexec.go+35 −1internal/appconfig/syncexec_test.go+58 −0internal/console/apistatuslayout.go+117 −0internal/console/apistatuslayout_test.go+188 −0internal/console/authcli.go+4 −0internal/console/instancepage.go+37 −1internal/console/instancepage_test.go+53 −1internal/console/svcroutes_test.go+7 −2internal/console/templates/app_instance.html+1 −0internal/edge/storedoor.go+9 −1internal/edge/storedoor_test.go+25 −0internal/host/agent.go+85 −20internal/host/agent_isolated_test.go+27 −4internal/host/agent_layout_test.go+629 −0internal/host/layoutleg.go+396 −0internal/host/movespass.go+7 −7internal/host/movespass_test.go+30 −0internal/host/systemapply.go+106 −1internal/host/systemapply_test.go+170 −0internal/host/units/carlos-edge.service+20 −1internal/host/units_isolation_test.go+26 −2internal/instancefs/dirs_linux.go+158 −0internal/instancefs/dirs_linux_test.go+231 −0internal/instancefs/instancefs.go+24 −0internal/instancefs/instancefs_linux.go+22 −0internal/instancefs/instancefs_linux_test.go+23 −0internal/instancefs/instancefs_other.go+32 −0internal/layout/journal.go+339 −0internal/layout/journal_linux.go+16 −0internal/layout/journal_linux_test.go+46 −0internal/layout/journal_other.go+13 −0internal/layout/journal_test.go+344 −0internal/layout/links_linux.go+20 −0internal/layout/links_other.go+9 −0internal/layout/pass.go+1212 −0internal/layout/pass_linux_test.go+1768 −0internal/layout/plan.go+194 −0internal/layout/plan_test.go+160 −0internal/layout/preflight.go+449 −0internal/layout/preflight_test.go+561 −0internal/layout/rawdir_test.go+109 −0internal/ops/litestream.go+23 −4internal/ops/ops.go+1 −1internal/ops/ops_test.go+81 −1internal/provision/provision.go+28 −0internal/provision/provision_test.go+33 −0internal/provision/reconcile.go+30 −9internal/provision/reconcile_test.go+150 −0internal/registry/registry.go+35 −0internal/registry/registry_test.go+32 −0internal/release/layoutintent.go+172 −0internal/release/layoutintent_test.go+98 −0internal/status/contract_test.go+25 −1internal/status/snapshot.go+45 −1internal/status/snapshot_test.go+51 −0status_instances.go+117 −0status_instances_test.go+112 −0
CI passed — run details
cli.goLoading diff…
cli_test.goLoading diff…
docs/site/cli.mdLoading diff…
docs/superpowers/plans/2026-09-25-isolated-exec-4-layout-amendments.mdLoading diff…
docs/superpowers/specs/2026-09-11-isolated-exec-design.mdLoading diff…
internal/activator/maintenance.goLoading diff…
internal/activator/maintenance_test.goLoading diff…
internal/appconfig/syncexec.goLoading diff…
internal/appconfig/syncexec_test.goLoading diff…
internal/console/apistatuslayout.goLoading diff…
internal/console/apistatuslayout_test.goLoading diff…
internal/console/authcli.goLoading diff…
internal/console/instancepage.goLoading diff…
internal/console/instancepage_test.goLoading diff…
internal/console/svcroutes_test.goLoading diff…
internal/console/templates/app_instance.htmlLoading diff…
internal/edge/storedoor.goLoading diff…
internal/edge/storedoor_test.goLoading diff…
internal/host/agent.goLoading diff…
internal/host/agent_isolated_test.goLoading diff…
internal/host/agent_layout_test.goLoading diff…
internal/host/layoutleg.goLoading diff…
internal/host/movespass.goLoading diff…
internal/host/movespass_test.goLoading diff…
internal/host/systemapply.goLoading diff…
internal/host/systemapply_test.goLoading diff…
internal/host/units/carlos-edge.serviceLoading diff…
internal/host/units_isolation_test.goLoading diff…
internal/instancefs/dirs_linux.goLoading diff…
internal/instancefs/dirs_linux_test.goLoading diff…
internal/instancefs/instancefs.goLoading diff…
internal/instancefs/instancefs_linux.goLoading diff…
internal/instancefs/instancefs_linux_test.goLoading diff…
internal/instancefs/instancefs_other.goLoading diff…
internal/layout/journal.goLoading diff…
internal/layout/journal_linux.goLoading diff…
internal/layout/journal_linux_test.goLoading diff…
internal/layout/journal_other.goLoading diff…
internal/layout/journal_test.goLoading diff…
internal/layout/links_linux.goLoading diff…
internal/layout/links_other.goLoading diff…
internal/layout/pass.goLoading diff…
internal/layout/pass_linux_test.goLoading diff…
internal/layout/plan.goLoading diff…
internal/layout/plan_test.goLoading diff…
internal/layout/preflight.goLoading diff…
internal/layout/preflight_test.goLoading diff…
internal/layout/rawdir_test.goLoading diff…
internal/ops/litestream.goLoading diff…
internal/ops/ops.goLoading diff…
internal/ops/ops_test.goLoading diff…
internal/provision/provision.goLoading diff…
internal/provision/provision_test.goLoading diff…
internal/provision/reconcile.goLoading diff…
internal/provision/reconcile_test.goLoading diff…
internal/registry/registry.goLoading diff…
internal/registry/registry_test.goLoading diff…
internal/release/layoutintent.goLoading diff…
internal/release/layoutintent_test.goLoading diff…
internal/status/contract_test.goLoading diff…
internal/status/snapshot.goLoading diff…
internal/status/snapshot_test.goLoading diff…
status_instances.goLoading diff…
status_instances_test.goLoading diff…