carlos / platform Public
Census proposal: install-console --upgrade
Status: RULED (platform-d8, 2026-10-20): census-ruling-install-console-upgrade, accepted as proposed (U1 to U4 at da0a33b7; U5 to U7 for the delta to b913bb65). Astra approved b913bb65. Branch install-console-upgrade, cut from main at 54ae7fb9.
What changed
claims install-console was a first install only: an occupied slot holding another build refused, so a guard-armed box's protected console could never be replaced. platform-d8's harness found this rolling dev's console to v0.28.5. This branch adds --upgrade --from <sha256>. Without --upgrade, install-console and its output are unchanged.
runSystemClaims(changed): two new flags,--upgradeand--from.upgradeFlagsRefused(new, no read site) admits them only together and only for install-console. Anywhere else they refuse with errActivation, as unknown flags did before. The upgrade output addsupgraded,fromandprevious_sha.runInstallConsole(changed, no read site): the identity, published-identity, build and platform checks are unchanged. A non-emptyfromcallsupgradeProtectedConsoleinstead ofinstallProtectedConsole.consoleSlotSHA(new):consoleSlotHolds's read moved here. The shape check is the same and is now applied to the opened descriptor as well (same device and inode). It returns the slot's sha256, or "" when absent.consoleSlotHoldskeeps its behaviour and now has no read site (@remove).upgradeProtectedConsole(new): the source is verified against the identity's console sha256 before anything is opened. The directory is checked asconsoleDirdoes but never created. Under the directory's exclusive flock: -consoleandconsole.prevmust each be absent or a root:root 0755 regular file with one link.console.prev's bytes are not checked. -consoleholding the target: Already, nothing changes. -consoleholding--from: upgrade. -consoleabsent andconsole.prevholding--from: a run cut off between its two renames; this run completes it. - Anything else refuses and changes nothing.
The upgrade writes console.next (exclusive, 0755, fsync), renames console to console.prev, renames console.next to console, fsyncs the directory, and reads both slots back through hostfloor.OpenProtected. A leftover console.next that is a regular file is removed first; anything else at that name refuses.
writeConsoleTemp(new): the exclusive no-follow create ofconsole.next, chmod 0755, root:root and one link on the descriptor, fsync.
Root-read census (TestPlan14RootReadCensus)
Overlay internal/provision/testdata/plan14-root-reads-install-console-upgrade.tsv, loaded last, after plan14-root-reads-armed-box-updates.tsv. No accepted-legacy row, so the seal stands.
| Unit | Executing identity | Class / tasks | Basis | |---|---|---|---| | activation_command_linux.go:runSystemClaims (changed) | operator CLI, uid 0 | authority G,J4 (fix1's) | the two flags, admitted only together for install-console | | activation_artifacts_linux.go:consoleSlotHolds (@remove) | | | its read moved into consoleSlotSHA | | activation_artifacts_linux.go:consoleSlotSHA (new) | box installer, uid 0 | authority G,J4 (consoleSlotHolds's) | absent, or root:root 0755 one-link regular file, checked on the entry and the descriptor, hashed bounded | | activation_artifacts_linux.go:upgradeProtectedConsole (new) | box installer, uid 0 (claims install-console --upgrade) | authority G,J4 (caller's) | as above | | activation_artifacts_linux.go:writeConsoleTemp (new) | box installer, uid 0 | authority G,J4 (caller's) | exclusive create of one fixed name in the locked directory |
The internal/host source envelope is re-pinned. No other envelope moves.
No box agent, update, recovery or edge path reaches a new or changed unit. guard-exec, hostfloor.CheckComponent and installProtectedConsole are unchanged.
Record-write census (TestPlan9B8RecordWriteCensus)
Unchanged and passing. The upgrade writes local files only and adds no bucket writer.
Round 2 (Astra's review of da0a33b7, MERGE: block)
Same overlay and marker (rev3=PROPOSED-install-console-upgrade). platform-d8 accepted da0a33b7; this delta needs a new ruling.
- F1, source aliasing (MEDIUM). An upgrade refuses a
--sourceinside the console directory by path, before reading anything. Under the lock, it also refuses a source whose opened descriptor has the device and inode ofconsole,console.prevorconsole.next, which catches a bind mount or any other path to the same file. To return that descriptor's stat,readConsoleSource's read moved unchanged intoreadConsoleSourceFile(new).readConsoleSourcewraps it for the first install and now has no read site (@remove). - F2, Already skipped the directory fsync (MEDIUM). A non-dry Already now fsyncs the held directory and returns any failure. A run cut off after its second rename, before its fsync, is therefore made durable by the rerun. The fsync goes through
consoleUpgradeSync, a package variable with no read site that tests replace. - F3, prefix digest (LOW).
consoleDigest(new) hashes a held slot through EOF. It refuses a slot over 512 MiB by fstat size and by the bytes read (consoleCounter.Read, new), and never hashes a prefix.consoleSlotSHAand the upgrade's readback use it. The first install's slot checks go throughconsoleSlotSHA, so they get the same refusal. The first install's own readback is unchanged.
Rows: consoleSlotSHA and upgradeProtectedConsole are re-pinned. readConsoleSourceFile, consoleDigest and consoleCounter.Read are new. readConsoleSource gets @remove. All are authority G,J4, the class of the rows they come from or their caller's. The internal/host envelope is re-pinned. There is no legacy row, so the seal stands, and 9B8 is unchanged.