carlos / platform Public
Review brief: isolated exec design
You are GPT-6 reviewing a design spec independently of its author. Your output is a findings report, not an edit. Do not modify any file.
What to read
- The spec under review:
docs/superpowers/specs/2026-09-11-isolated-exec-design.md. - The problem it answers, as filed: amadan discussion #126 on carlos/platform, reproduced in spec §1; its companion #127 (spec §7).
- The code it fits into, in this worktree: -
internal/activator/backend_exec.go(Start 557-773, spawnInCgroup 154-156, childProc 992-1033, instanceEnv 775-795, childEnv 802-814, teeStd 123-137, Seed 332-440, StartSidecar 879-912, Restore 220-290, PurgeLocalDBArtifacts 1041-1057); -internal/activator/activator.go(Proc/Rep 194-207, doWake 772-900, Bounce 946-956, stop 962-1047, sweepOnce 1082); -internal/activator/backend_unit.go(systemctl 94-103, package doc); -internal/activator/admintoken.go(6-14, 111-195); -internal/edge/purge.go(53-107),internal/edge/edge.go(449-451, 536-538); -internal/cgroup/cgroup.go(37-62, 219, 242-285, 398-403) and the_linux.gofiles; -internal/lease/lease.go(257-299); -internal/appconfig/syncexec.go(1-31, 465, 566-628, 860-956, 977-1024); -internal/provision/provision.go(36-135, 246-262, 995-1012),internal/provision/reconcile.go(280-301, 374-436, 461-464, 862-892); -internal/registry/registry.go(58, 152-156, 413, 1057-1075); -internal/ops/litestream.go(273-428); -internal/host/units/*(all of them; especiallycarlos-edge.service,carlos-app@.service,carlos-app@.socket,carlos-stage.*,carlos-restart.*,carlos-litestream.service); -internal/host/bootstrap.go(33-34, 116-180),internal/host/systemapply.go(339-398, 517-589),internal/host/systemunits.go(34-83); -internal/host/movespass.go(100-132, 285),internal/host/agent.go(186-192, 285, 305-309, 1771),internal/schedule/runner.go(461),internal/host/schedule_wiring.go(163, 343-369); -cli.go(83-199, 1622-1625); -AGENTS.md. - The precedent the namespace recipe is taken from:
/home/paulca/github.com/tito/titogo/deploy/systemd/titogo@.serviceandtitogo-instance-dir@.service(read-only; another repo). - The pinfra scripts the rollout depends on:
/home/paulca/github.com/carlosframework/platform-infrastructure/scripts/update-platform-binary.shandharden-host.sh(read-only; another repo), and the two earlier designs the spec moves resources out of:/home/paulca/github.com/carlosframework/platform-infrastructure/platform-design/superpowers/specs/2026-08-07-exec-child-accounting-design.mdand2026-08-28-memory-pressure-containment-design.md. - systemd's documented semantics (systemd.exec(5), systemd.service(5), systemd.resource-control(5), systemd.socket(5)) for every directive the spec relies on:
TemporaryFileSystem=,BindPaths=,PrivatePIDs=,ProtectProc=,RestrictNamespaces=,StandardOutput=file:on anAF_UNIXpath,PartOf=,KillMode=mixed,OOMPolicy=kill,OOMScoreAdjust=,Slice=,systemctl set-property --runtimeon a not-yet-started template instance, and whichsystemctlverbs an unprivileged caller may run without polkit.
Rounds after the first
From round 2 on, the prior rounds' findings files (CODEX-FINDINGS-ISOLATED-EXEC.md, then -R2.md, ...) are committed beside the spec, and the spec's §13 records how each finding was taken. Start your report with a disposition table: one row per prior finding, stating whether the revision resolves it, resolves it partially (say what is missing), or does not resolve it. Then report only findings that are new in this revision, numbered R<n>-1 onward for the current round. Do not repeat a resolved finding; do not re-argue a ruling.
Decisions already taken (report a mechanism that fails one; do not re-argue one)
- Paul approved approach 3 on 2026-09-11: the activator keeps ownership; the process becomes a systemd template unit; still uid
carlos; namespace isolation; no console change; migration by rename. - Approaches 1 and 2 (spec §12) are rejected.
- Ruled with codex R1-1 (2026-09-11): the root channel is the
carlos-instance-controlservice with a bearer token; polkit is not installed and not relied on. Report a way the token or the channel fails; do not re-open the polkit comparison.
Things to check hard in revision 2
nosymfollow(§3.2). Does a bind mount of a directory onto itself withnosymfollowhold for lookups through the covering mount from the agent's namespace, and through systemd'sBindPaths=bind inside the instance's namespace? Is there any path the agent, the edge, PID 1 (StandardOutput=file:) or litestream resolves under the instance trees that is not on that mount? Is theopenat2helper redundant, insufficient, or wrong in any flag?- The token (§5.4). Where does it live, who writes it, who reads it, and can an instance obtain it or cause the agent to present it on the instance's behalf?
- Exit observation (§6.5).
cgroup.eventsinotify semantics forpopulated; the invocation id match; the fallback poll; what happens to a proc whose unit is restarted by something other than the agent. - Slices (§4.3).
Slice=carlos-instances-%i.slicefrom a template with an escaped instance name; on-demand slice creation;set-property --runtimeon a slice that does not exist yet; whether moving the edge intocarlos.slicekeeps the 2026-08-28 wall's semantics. - The fence (§8.1) and the reverse migration (§8.3). Every consumer that must honour the marker; the recovery table; the "two databases" refusal; what
carlos system rollbackdoes today and whether a leg can be added before the binary swap. - The probe and the gate (§8.4). Can the probe pass while an instance would not be isolated? Can it fail while instances would be?
What matters most
- The isolation claim in §4.1. From inside the unit, can an instance reach a sibling's database, env file, config file, listening socket, control socket, log socket, pid, or the control service socket? Can it reach IMDS? Can it reach
/data/registry.db,/data/litestream.env, the console DB? Check each directive does what the spec says, and check the order systemd applies them in (bind sources, tmpfs, ro). - The lifecycle in §6. Exit observation with
Restart=no; the stop ordering (stop → done → close listeners) againstbackend_exec.go:992-1009's stated invariant; the log socket as a replacement for the pipe (teeStd), including a grandchild holding it open andKillMode=mixed; the race between bindinglog.sockand PID 1 connecting; a wake that fails health and must stop;Bounce; the agent-start sweep (§6.7) andPartOf=. - Memory (§4.3, §6.6). The wall moves from one unit to a slice plus a smaller edge cap. Is 60/20/20 defensible against the 2026-08-28 design's reasoning? Does
OOMPolicy=killon the instance unit reproducememory.oom.group=1? DoesOOMScoreAdjust=500reproduceExecOOMScoreAdj? Does the sampler's new scan root give the console the same per-host numbers? - Migration (§8). Idempotence between the file renames and the row CAS; the ineligible-row policy (refuse to wake, name the fix); the rollback story (old binary restores flat, nested copy retained); the reconciler's drift log; the box-wide litestream generator's flat glob (
internal/ops/litestream.go:354) and whatFleetDBsWithSkippedclaims after the row changes; what an in-flight residence move (movespass.go) sees. - The trampoline (§6.2).
instance-execreads an agent-written JSON from the instance directory and execs. Is there a way the tenant influences that file? Is refusingargv[0] != /opt/carlos/live/<host>the right check? Anything the currentexec.Commandpath does that the trampoline drops (working dir, fds,WaitDelay,Pdeathsig)? - Every consumer (§9). AGENTS.md's rule: everything that PARSES the shape. Find a consumer the list misses.
- Claims about the code. Every
file:lineand every "already exists" or "unchanged" claim: check it. Report each one that is wrong. - Operability. Can a member, with no box access, tell that their instance is isolated, see why it refused to wake, and read its logs as before? Can an operator roll this back?
- Scope. Anything in the spec that is not needed for the isolation property and could be cut; anything needed that is missing.
How to report
Number findings R1-1 onward. For each: severity (P1 breaks the isolation property, breaks hibernation, loses data, or makes the change unbuildable as written; P2 a contract, claim or migration defect to fix before the plan; P3 advisory), the section, the problem, the evidence with citations, a proposed correction. Put the §5.4 advisement first, as its own finding, with a clear recommendation. End with a verdict: accept, accept with corrections, or request changes. Do not soften, do not pad, do not restate the spec.