carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

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

  1. The spec under review: docs/superpowers/specs/2026-09-11-isolated-exec-design.md.
  2. The problem it answers, as filed: amadan discussion #126 on carlos/platform, reproduced in spec §1; its companion #127 (spec §7).
  3. 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.go files; - 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; especially carlos-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.
  4. The precedent the namespace recipe is taken from: /home/paulca/github.com/tito/titogo/deploy/systemd/titogo@.service and titogo-instance-dir@.service (read-only; another repo).
  5. The pinfra scripts the rollout depends on: /home/paulca/github.com/carlosframework/platform-infrastructure/scripts/update-platform-binary.sh and harden-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.md and 2026-08-28-memory-pressure-containment-design.md.
  6. 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 an AF_UNIX path, PartOf=, KillMode=mixed, OOMPolicy=kill, OOMScoreAdjust=, Slice=, systemctl set-property --runtime on a not-yet-started template instance, and which systemctl verbs 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-control service 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 with nosymfollow hold for lookups through the covering mount from the agent's namespace, and through systemd's BindPaths= 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 the openat2 helper 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.events inotify semantics for populated; 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.slice from a template with an escaped instance name; on-demand slice creation; set-property --runtime on a slice that does not exist yet; whether moving the edge into carlos.slice keeps 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 rollback does 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) against backend_exec.go:992-1009's stated invariant; the log socket as a replacement for the pipe (teeStd), including a grandchild holding it open and KillMode=mixed; the race between binding log.sock and PID 1 connecting; a wake that fails health and must stop; Bounce; the agent-start sweep (§6.7) and PartOf=.
  • 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=kill on the instance unit reproduce memory.oom.group=1? Does OOMScoreAdjust=500 reproduce ExecOOMScoreAdj? 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 what FleetDBsWithSkipped claims after the row changes; what an in-flight residence move (movespass.go) sees.
  • The trampoline (§6.2). instance-exec reads an agent-written JSON from the instance directory and execs. Is there a way the tenant influences that file? Is refusing argv[0] != /opt/carlos/live/<host> the right check? Anything the current exec.Command path 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:line and 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.