carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

| Prior finding | Disposition at d70f1109 | |---|---| | R1-1 | Resolved for the reported attack. Protected socket resolution and bearer authentication replace uid-only authorization. Retain the §5.4 ruling. | | R1-2 | Resolved. Log sockets move outside tenant mounts. | | R1-3 | Resolved for the reported symlink attacks. nosymfollow, confined opens and exclusive temporary files address them. The new recovery marker introduces separate authority exposure, R2-1. | | R1-4 | Partially resolved. EOF no longer means exit, and Exited() is nonblocking. Completion evidence and another blocking call under the activator mutex remain defective, R2-4–R2-6. | | R1-5 | Partially resolved. Timeout, cancellation and stop-before-cleanup are specified. Safe completion depends on R2-4/R2-5; seed preparation also regresses, R2-12. | | R1-6 | Resolved. Each role has its own log listener. | | R1-7 | Resolved. BindsTo= removes dependency-propagated restarts; the sweep includes activating units and pending jobs. | | R1-8 | Partially resolved. All roles share the tier. Whole-family OOM behavior remains incomplete: a sidecar OOM can leave the main process running, whereas the previous shared memory.oom.group=1 killed both. | | R1-9 | Resolved for per-host metrics. Hierarchy, aggregation and published key are corrected. Moving the edge creates another discovery omission, R2-13. | | R1-10 | Resolved. The arbitrary split is withdrawn and the shared outer wall retained. | | R1-11 | Resolved. Migration excludes externally supervised and tunnel/unit-backed routes. | | R1-12 | Partially resolved. The fence covers the named consumers, but recovery mishandles optional artifacts, committed CAS operations and already-nested rows, R2-7. | | R1-13 | Partially resolved. Reverse migration replaces the incorrect restore story. The actual downgrade and recovery execution paths remain uncovered, R2-9. | | R1-14 | Resolved. Socket activation, installer changes and active-state checks are explicitly required. The new mount introduces a separate boot failure, R2-2. | | R1-15 | Partially resolved. The unsafe fallback is withdrawn. The replacement probe can report readiness without proving IMDS enforcement, R2-14. | | R1-16 | Resolved. Last-value-wins deduplication and an executed-child test are specified. | | R1-17 | Resolved. Nested discovery, registry filtering and hibernation exclusions are accurately described. | | R1-18 | Partially resolved. Remote status and CLI documentation are specified. Repair still uses the local-only carlos route; its prescribed --db flag does not exist (cli.go:469). Pre-roll inspection also depends on a snapshot field that old boxes cannot publish. Define the remote repair operation and compatibility rollout. | | R1-19 | Resolved. The unsupported UnitBackend claim is removed. |

R2-1 — P1 — §§5.4, 3.1, 8.1: Retain the authenticated control service, but move authoritative recovery metadata outside tenant mounts.

The new .migrating file lives in the tenant’s writable directory. Recovery explicitly repeats filesystem operations using the paths recorded in that file. A tenant can forge a marker directing the agent to move a sibling’s database or env file. These are ordinary paths: nosymfollow does not prevent the operation. A later CAS refusal is too late because renames happen first.

Evidence: spec:242, spec:1008, spec:1022.

Correction: keep the authoritative transaction record outside every tenant mount. Treat a tenant-local marker only as an untrusted refusal signal. Validate every source and destination against trusted host-specific metadata before moving anything. Test a forged sibling path and assert that the victim remains untouched.

R2-2 — P1 — §3.2: The socket-tree mount creates a boot ordering cycle.

run-carlos-instances.mount declares After=systemd-tmpfiles-setup.service. A local mount receives a default ordering before local-fs.target, while tmpfiles starts after that target. The resulting cycle is:

mount → local-fs.target → tmpfiles → mount

systemd must break the boot transaction. Dropping the required mount blocks the control service and tenant starts. Installing the mount on an already-booted box does not exercise this failure.

Evidence: spec:331, systemd mount dependencies, tmpfiles unit.

Correction: specify an acyclic directory-preparation and mount sequence, including shutdown ordering. Verify a cold boot with both mounts and the readiness probe.

R2-3 — P1 — §5.3: Moving property-setting to the slice makes the first service start fail at reset-failed.

The revised sequence sets properties on the host slice, then resets the service, then starts it. The first operation loads only the slice. systemd’s ResetFailedUnit deliberately does not load an unknown service, so a never-loaded template instance can fail before reaching start. The specified error handling returns 502.

Evidence: spec:661; ResetFailedUnit versus SetUnitProperties, lines 781–788.

Correction: load and retain the service before resetting it, or tolerate precisely the missing-unit reset outcome. Test a first wake with neither the service nor slice previously loaded.

R2-4 — P1 — §§5.3, 6.5: Invocation polling can lose the completion record permanently.

systemd unloads unreferenced inactive units and discards their execution state. Nothing specified pins these services or retains their results. A clean exit can disappear before the next show, leaving the original invocation permanently unmatched and done open. Successful seeds are particularly exposed: /seed reads their invocation only after the oneshot finishes.

A replacement invocation has the same unresolved outcome. The observer ignores it forever; Restart=no does not prohibit an explicit restart by another authorized actor.

Evidence: spec:862, systemd unit garbage collection, unit reference operations.

Correction: retain invocation identity and completion evidence until the agent acknowledges them, using a persistent unit reference or broker-owned records. Define recovery for a replaced invocation. Test immediate successful exit, delayed observation and an external restart.

R2-5 — P1 — §6.5: failed does not prove that every writer has stopped.

The fallback poll accepts a matching invocation in inactive or failed without requiring an empty cgroup. systemd explicitly enters failed state when processes remain after its final SIGKILL timeout. The proposed observer can therefore close done, permitting seed cleanup or lease release while a process remains.

Evidence: spec:869; systemd service implementation, lines 4020–4022.

Correction: require proven cgroup emptiness as a completion condition, including on the fallback path. Define what a vanished cgroup proves and keep uncertainty from authorizing cleanup or lease release.

R2-6 — P1 — §§6, 6.2: Sidecar restart introduces blocking systemd work under the global activator mutex.

sweepOnce calls StartSidecar(context.Background(), ...) while holding a.mu. Its existing justification is that spawning is quick. The replacement performs synchronous HTTP and systemctl operations, with no specified deadline. A stalled control operation now prevents unrelated hosts from waking or stopping.

Making Exited() nonblocking does not address this second call site, and the statement that activator.go remains unchanged is untenable.

Evidence: activator.go:1170, activator.go:1196, spec:812.

Correction: reserve the sidecar start under the mutex, perform bounded control work outside it, then reconcile completion with any intervening stop. Test a stalled sidecar start while another host wakes and stops.

R2-7 — P1 — §8.1: The new recovery algorithm cannot complete common restart states.

Three specified branches fail:

  • Migration moves only artifacts that exist, but recovery treats an artifact absent at both source and destination as an error. Ordinary hosts lack several optional artifacts, including sidecar env and quarantined DB files.
  • A crash after the row CAS but before marker removal retries the original CAS against an already-updated row, producing a permanent conflict.
  • A clean subsequent startup sends an already-nested row through the “not flat defaults” branch into the hand-set-path refusal.

Evidence: spec:1001, spec:1013, spec:1022.

Correction: record artifact presence and migration direction, recognize a committed destination row, and classify settled nested rows before testing legacy defaults. Test every crash boundary and a second clean agent restart.

R2-8 — P1 — §8.1: Migration ignores existing flat-file ownership collisions.

For routes x and x.config, /data/x.config.env belongs to the latter’s instance env. Config sync already refuses to write that pathname for x. Migration nevertheless moves every existing <host>.config.env into that host’s directory. Processing x first places the sibling’s credentials in x’s config layer and removes them from their owner.

Preserving the reaper’s ambiguity guard does not protect migration.

Evidence: syncexec.go:389, spec:1013.

Correction: preflight ownership of every source pathname against the complete registry before renaming. Refuse ambiguous transactions or preserve the established owner. Apply equivalent checks when reversing into a flat directory containing excluded routes.

R2-9 — P1 — §8.3: The first upgrade’s rollback executes the old binary, which cannot invoke the new reverse migration.

The installed binary starts system apply. That running process installs the candidate, restarts the edge and gates it. If the new edge migrates data and then fails its gate, the old process executes its existing rollback → restoreFiles, restoring the old executable without reversing the layout. Boot recovery deliberately executes carlos.prev when an update journal exists.

Furthermore, carlos system rollback repoints a channel through the console API. Its resulting downgrade follows ordinary box-side installation; adding a leg only to a local failure handler does not cover it.

Evidence: systemapply.go:366, systemapply.go:743, recovery unit:17, system.go:466.

Correction: specify a compatibility release or an explicit transition protocol understood by the updater and recovery executable before enabling migration. Cover channel downgrade, failed-gate rollback and interrupted recovery. Reverse migration must also quiesce activation and replication, rather than merely stop tenant units.

R2-10 — P2 — §§3.1, 6.1, 9: The new socket directories exclude enrolled cross-user consumers.

Existing instances deliberately expose their listening sockets to members of carlos-sockets: /run/carlos supplies the inherited group, and the backend normalizes sockets to 0660. The new layout assigns carlos:carlos to the parent and 0700 to each host directory. An enrolled router cannot traverse the host directory; preserving the socket chmod does not help.

Evidence: bootstrap.go:41, backend_exec.go:744, spec:244.

Correction: preserve the explicit socket-consumer contract through directory traversal and socket-group permissions, while retaining tenant namespace isolation. Test a migrated instance through an enrolled non-carlos router.

R2-11 — P2 — §§4.3, 6.6: The slice configuration drops the existing swap ceiling.

The current cgroup code sets memory.swap.max equal to the memory tier. Its comment records a real allocator escaping the intended limit through zram before that setting was added. The proposed slice receives only MemoryMax and MemoryHigh.

Evidence: cgroup.go:313, spec:578, systemd MemorySwapMax documentation.

Correction: set MemorySwapMax to the tier limit on the shared host slice. Verify the delivered limit under swap pressure.

R2-12 — P2 — §§6.1–6.2: First-boot seeds lose admin-token preparation.

The new preparation sequence calls EnsureAdminToken only for run. Today Seed explicitly creates the token before reading its environment so that initialization receives the same credential as the serving process. Under the proposed sequence, the first seed runs before that credential exists.

Evidence: backend_exec.go:346, spec:733.

Correction: prepare the admin token and merged environment for both seed and run, retaining the seed’s removal of control-channel variables. Verify that seed and subsequent run receive the same token.

R2-13 — P2 — §§4.3, 6.6, 9: Moving the edge into carlos.slice removes it from metrics discovery.

Current platform-unit discovery scans system.slice and its immediate template slices. The new scan root covers carlos-instances.slice, but the relocated carlos-edge.service is its sibling. Neither scan finds the edge.

The prior accounting design also defines the edge reading as the hierarchical total including children. Relocating discovery alone would change that meaning.

Evidence: metrics_probes.go:49, metrics_loop.go:659, accounting design §4, spec:569.

Correction: explicitly discover the relocated edge and define how the previous aggregate reading is preserved or replaced. Test platform metrics as well as member per-host metrics.

R2-14 — P2 — §8.4: The readiness probe can pass without proving IMDS isolation.

The probe accepts any failed IPv4 connection to IMDS. A routing failure or timeout also satisfies that assertion when the systemd IP filter is ineffective. It never tests the separately denied IPv6 metadata address. systemd documents that IP filtering depends on kernel/BPF support.

Thus the recorded ready verdict is stronger than the test establishes.

Evidence: spec:1079, systemd IP filtering documentation.

Correction: distinguish enforced denial from unrelated network failure, test both address families, and keep unavailable evidence from producing ready. Include a negative test where filtering is disabled while the destination is unreachable.

Verdict: request changes.