feat(pyrite): init as nixos host - #2497
Draft
cameronraysmith wants to merge 146 commits into
Draft
Conversation
current wip
…tall mechanics Record that nixos-anywhere 1.13.0 defaults to disko's diskoScript, which composes _legacyDestroy then _create then _mount, closing the open question about the default --disko-mode and removing D8's create-only premise. Change the pre-wipe primitive from sgdisk --zap-all plus wipefs -a to blkdiscard, since a zapped partition table leaves no lsblk children for disko's disk-deactivate.jq to reach zpool labelclear through, so the ZFS labels inside p2 survive and the next install reuses the old pool. Add decision record D13 for the install mechanics: the nixos-anywhere version resolution through clan-core's nixpkgs, the four per-phase invocations, the default-phases and --update-hardware-config behavior, and the flake-float exposure. Add a --phases disko verification step and a blockdev --getss check ahead of the irreversible install, and renumber the Phase 7 steps. Replace Phase 8's nixos-anywhere source-acquisition task with a hold-the-flake-steady task. Record the 4Kn dd bs=440 conjecture as unestablished rather than as rationale.
D14 adopts clan-core wifi clanService for wlp2s0 association and commits a dedicated fleet SSID credential as clan vars rather than the household network one. Both reference clans follow an unstated origination test, committing secrets the repository originates and withholding secrets it borrows, and a household credential published to a public repository fails that test while a fleet-owned SSID passes it, which also makes the service hardcoded share = true correct rather than merely tolerated. D14 records the counter-evidence rather than omitting it: a clan-core maintainer declines this service on four laptops of pyrite shape, and upstream share = true is undocumented behind a stub test. D15 states hardware.enableRedistributableFirmware and hardware.cpu.intel.updateMicrocode explicitly, closing the path D5 closure-minimization examples open toward false on the machine only NIC, because the discriminating axis is redistributability rather than freeness. Task 2.10 hand-written networkd unit is deleted because NetworkManager owns wlp2s0 and forces networking.useDHCP = false. The claim that nixos-generate-config misdetects this wifi as b43 is struck as false, since nixos-generate-config.pl enumerates 14e4:43a3 explicitly and emits the enableRedistributableFirmware import. Phase 5 gains a router prerequisite because the fleet SSID must broadcast before clan vars generate prompts for it.
D5 withdrew the skip-the-profile fallback on the ground that i915 reaches initrd only through the profile import chain, and that claim is false. nixos/modules/module-list.nix:70 imports hardware/facter into every NixOS configuration unconditionally and graphics/default.nix:33 assigns boot.initrd.kernelModules from the report at plain priority, so cinnabar imports no nixos-hardware profile and carries virtio_pci by exactly that route today. The fallback stays withdrawn on the SPI input path instead, where the profile is the sole supplier, because the facter keyboard module collects from usb_controller alone and this keyboard is SPI; the failure mode is therefore a passphrase prompt that renders and cannot be typed into rather than one that never appears. The boot.initrd.kernelModules source enumeration is reframed from a closed count to an open set, since it was wrong at four, wrong at five, and an attempt to close it at six surfaced af_packet and zfs as well, and the mkForce invariant is strengthened rather than weakened by the sources not being reliably enumerable. D16 affirms services.mbpfan.enable with aggressive = false, because mbpfan is gpl3 and in-tree and so sorts opposite to both D5 declines on the redistributability axis D5 itself establishes, and records that the same import also enables services.tlp. An open risk records that nixos-hardware is absent from flake.lock until task 1.1, so every profile claim is measured at an unpinned upstream HEAD and needs four nix eval checks re-run once the lock is taken.
…or alone D10 reverses: pyrite takes the tor server role through the unchanged nixos tag selector, so modules/clan/inventory/services/tor.nix is not touched by this change at all. An onion service makes a machine with no stable address reachable from anywhere it has internet, which is worth more on a laptop behind NAT than on the five cloud hosts that already have a static IP, and a v3 onion address is network-independent by construction. The not-a-relay refutation is strengthened with the block the earlier citation missed: nixos/modules/services/security/tor.nix:1298-1307 force-disables DirPort, ORPort, and PublishServerDescriptor outside the relay gate, which cinnabar confirms by evaluation. An earlier revision declined the role and attributed that to a user preference the record does not carry; the question was raised twice, both times mischaracterised as enrolling the machine as a Tor relay, and never answered, so the withdrawal is recorded rather than designed around silently. The precedent is one clan-core maintainer running this on four NixOS laptops through the identical selector and hand-adding a second onion service, and the decision records that n is one rather than presenting it as a survey. proposal.md gains its Capabilities section, which could not be written until the wireless and tor questions were both answered; all three capabilities are new because openspec/specs/ does not exist. Task 4.3 is deleted without renumbering, because design.md D5 and D14 cite tasks 4.7 and 4.4 by number and renumbering would silently falsify them.
…s, add D17 Audio stops being recorded as a binding decision at four sites and is argued on its merits with a revisit trigger in D11 form: the only out-of-tree module carrying the CS8409 amplifier init tops out below this repository pinned kernel, pinning the kernel backward is ruled out, and if mainline lands that init sound arrives on a routine kernel bump at no cost. The record does not support the disposition the artifacts carried; the one recorded utterance states a firm constraint against pinning the kernel back and a hedged postpone, and the only assent covering audio endorses a framing with a later decision point rather than the permanence the artifacts asserted. Hibernation loses the same unsourced token at two sites, where deferred alone is accurate and D6 logic is untouched; the three sourced uses naming the re-runnable install path are earned and stay. D17 records that clan.core.deployment.requireExplicitUpdate is inherited rather than set, because the option is inert under this fleet update practice: its filter lives only in the implicit branch of clan_cli/machines/update.py, updates here name one machine per invocation, and nothing in the repository automates a bare or tag-scoped run that would reach it. D17 carries a revisit trigger because a tag-scoped run is an implicit one, and records that setting the option fails silently rather than loudly. Task 5.3 keeps the passphrase recording step and gives it a destination outside the repository plus a verification, because the false clause was no escrow rather than the step itself: the committed var is encrypted to exactly one human key, so the pool is lost only if that key is lost together with every copy held elsewhere, and the recording is what keeps the second term of that conjunction non-empty. clan vars get appears in 5.3 as the verification and never as the recording, since reading back the var this step exists to duplicate would rest the pool on the single key whose loss is the failure mode.
plan.md's approach prose named Step 7.9 as the wipe-inclusive re-run that converts "it installed" into "the install path works". Task 7.9 is the cinnabar /pyrite.zt DNS record, a reversible edit; the re-run is 7.12. The sentence was correct when written at 8aa678b6 — 7.9 was the re-run then — and was orphaned when e1caf210 expanded Phase 7 from 9 tasks to 12 and updated plan.md's Task 7 step bodies without following through to the prose. plan.md:3-4 directs agentic workers to implement from this file, so the only statement of where the irreversibility proof lives pointed at a DNS record, inside the one phase that wipes macOS with no fallback. design.md:269 already says "Task 7.12 re-runs the install"; after this plan.md and design.md agree.
plan.md's prose block froze at 8aa678b6 while four later commits updated its task bodies and skipped it. Blame distributes 68 lines to 8aa678b6 and none of the later commits touch lines 8, 24, or 26; c4ddc2f0 never opened the file at all. Drop the D1-D12 range rather than widen it to D1-D17: the range has gone stale four times in one day, three of those in commits that edited this very file, and the sentence's only job is to send the reader to design.md, whose own ### D headings enumerate the set authoritatively. "The open question" becomes "the open risks", matching design.md's actual section header and its six subsections. Replace the Task 8 paragraph, which describes a Phase 8 that no longer exists: e1caf210 closed that open question (it became D13) and rewrote plan.md's own Task 8 heading and steps, stranding the paragraph that described the deleted phase. plan.md:26 currently contradicts plan.md:88. Add D14's wifi clanService to the Architecture summary and D16's mbpfan line to Task 2, neither of which reached plan.md.
… from the installed one The note reads as self-contradicting because it never distinguishes two different kernels. 7.1.3 is the installer ISO's, which imports latest-kernel.nix setting boot.kernelPackages = pkgs.linuxPackages_latest. 6.18.x is what an installed pyrite takes from the boot.kernelPackages default. Both statements are true at once; only the framing was missing. The pinned default is 6.18.37, not 6.18.38. Resolved the root flake's nixpkgs outPath via nix eval — node nixpkgs_9, the 26.11pre tarball, not catppuccin's bare nixpkgs node — and evaluated linuxPackages.kernel.version there. 6.18.38 is the value in a stale ghq HEAD clone. design.md:110 already carries 6.18.37; the note was the lone outlier. The conclusion is unaffected: 6.17 < 6.18.37 just as 6.17 < 6.18.38. The two "binding decision" tokens are not a duplication — :94 declares and :113 back-references one decision. Anchor :94 to where the decision lives (design.md:145) rather than reconciling them.
D17 records a considered "set nothing" for clan.core.deployment.requireExplicitUpdate. Its reasoning is sound and it stands, but the record does not show the option being put to the user, and the user's ruling was that it stands — which is not the same as having chosen it. D17 itself already says the fleet's omission "is answered by silence, which is indistinguishable from having been missed". The same standard applies to D17's own provenance, and the marker makes it honest without weakening the decision. The purpose decision is deliberately not marked: it was genuinely ratified at 2026-07-16T15:37:50Z.
The premise was inverted. linear.yaml's comment, and design.md's open risk, both assume 48b4123d589b is wrong for being a slug rather than a UUID and that both forms resolve. Neither half holds. `linear document list --project <slugId>` returns 9 documents; the same project's UUID returns 0 nodes with exit 0 — silently. Tested against a project that has documents, which is what makes the test severe; the original "both resolve" test used pyrite, which has none, so "resolves to empty" and "does not resolve" were indistinguishable. The slugId is therefore correct and the value stays. Reconciling toward the UUID, as the comment prescribes, would make the archive-time UPSERT title scan miss, fall through to create, and duplicate every capability document on every archive. attempt_log outcome "deferred" is not in the enum (dropped|failed|posted). The cause was Linear unavailable, which lifecycle.md:39 names as a dropped-outcome degradation path.
… the user's awareness "The user chose this knowingly" was not unsourced — the option that was selected named these costs, and the alternative that lacked them stood beside it. But sourced is not checkable. A reader holding this change can verify nothing about anyone's state of mind, because the evidence for it lives outside the artifacts; the technical comparison the claim rested on is recoverable from the change's own record. Both sites now rest on that comparison. ZFS native encryption forfeits a second key and leaves dataset structure in the clear; LUKS offers keyslots and metadata encryption, and D1's preceding bullet already records it as considered and rejected. design.md:160 back-references that bullet, and spec.md:149 adopts the "it is recorded that" register of its siblings at 147-148, stating a documentation requirement rather than a fact about the world. This is a wording repair, not a retraction. The keyslot facts around both sentences are untouched and D4 is not reopened. design.md and spec.md move together because striking one would desync design from spec. "pool layout" stays as an un-attributed ZFS fact: it was never part of the comparison, and grounding it is out of scope here.
Stock nixos-facter 0.4.4 — the version this repository's pinned nixpkgs ships — aborts on a MacBookPro14,1 with "unsupported bus type: Spi": Apple attaches the touchpad over SPI and hwinfo's input scan has no mapping for that bus, so the scan errors out. That blocks `clan machines update-hardware-config` on pyrite, the fleet's first bare-metal machine. Override the nixpkgs nixos-facter attribute through this repo's overlay accumulator, adding nix-community/nixos-facter PR #672 (issue #339, commit 4f27becd3b432eabf4f7faca7f14025c69684130) as a source patch. The commit adds a mouseBusIDs mapping that classifies SPI and other buses instead of aborting; it is unreleased as of 0.4.4. Guarded to Linux because the fix branch exposes no darwin package and the overlay applies fleet-wide, so darwin laptops keep stock 0.4.4. Self-retires when a nixos-facter release carrying the fix reaches nixpkgs. Built for x86_64-linux on the magnetite remote builder: the patch fetches at the recorded hash, applies cleanly, and the package builds.
…mpact section forward
…nd kexec/vars install mechanics
… facter risk with the shipped overlay
…Goal, and acceptance tasks
…apability surface
… task 7.15 The ratified purpose rests on pyrite being reachable for `clan machines update` over ZeroTier after the install — the path the whole "no console PSK forever" reasoning depends on. Every other Phase 7 step observes a machine on the bench; 7.15 is the only one that exercises the path the fleet uses afterward, run in D17's recorded bare form with no --target-host and no --build-host. It is appended after 7.14 rather than inserted because 7.12 destroys and recreates the pool, so the machine that must be reachable is the one 7.12 leaves behind; the ZeroTier identity and the ssh host key survive that re-install as committed vars, so 7.9's record and 7.10's entry stay valid across it. Severe by construction: it excludes the two ways the check goes green without proving reachability — a --target-host root@<lan-ip> run that bypasses 7.9 and the mesh (guarded by confirming getent hosts pyrite.zt returns the fddb: address), and a same-revision run that moves no closure (guarded by deploying a revision that changes pyrite's toplevel and confirming readlink /run/current-system moved). That second guard is invisible on pyrite because _update_nixos prints no diff where blackphos's darwin-rebuild did. Adapted from the pre-renumber verified draft; citations re-checked against the pinned clan-core (update.py:615, :637, :567-569) and the repo (admins.nix:24-30, the blackphos zero-diff log).
…d framing A register-only sweep removing five instances of embellished contraposition — "not X but Y" that invents a belief to correct, strawman negations, and inflated-reach metaphors. No decision, citation, number, or fact changes. - proposal.md apple-laptop-hardware-support bullet: drop "rather than an improvised recovery"; the concrete USB-C-only trap stays stated in tasks.md 6.7 and design.md. - design.md postCreateHook discussion: drop "not a trick"; the lib/default.nix:476 and :507-511 citations are untouched. - proposal.md Why: drop "not a demonstration", "more durably", and "the reusable half of the epic, inherited by every future physical machine". The terranix-cloud-only context, CAM-31 recon, encrypted-at-rest travel framing, and re-runnable-path-is-the-deliverable point all remain. - design.md overview: drop "which is the more durable of the two"; the no-non-terranix-install-path reason is preserved. - tasks.md 7.15: "the path the fleet uses forever after" becomes "the steady-state update path used for all subsequent updates".
… acceptance tasks plan.md was committed before the graphical-desktop-session work landed, so it carried no gnome/gdm/desktop/niri tokens while design.md, tasks.md, and proposal.md all did. plan.md:3 declares itself the task-by-task driver for the implementing session, so a stale plan would under-scope the desktop. Five appends, no renumbering, authored from the design.md/tasks.md content: - Goal: the local graphical desktop session, stock GNOME under GDM proven on this hardware from the installer ISO (mirrors Goal 5). - Architecture: the two system options the host module sets. - Task 2 Step 4: enable GDM+GNOME, system-level, no home-manager desktop config, no plymouth (D11 stands); folded into the host-module options step so the routing step stays last. See D19. - Task 7 Steps 8-10: mirror tasks.md 7.13/7.14/7.15 — the interactive GNOME login with is-active necessary-but-not-sufficient, the stage-1 prompt unperturbed by GDM, and the steady-state clan machines update as the travel-readiness acceptance. - Out of scope: the niri Wayland session and its shell assembly, deferred to a reversible follow-up. See D19.
… the ledger The T1 readiness gate fired: CAM-32's description was seeded from proposal.md business content and its state advanced Backlog->Todo, both verified live. Sync the openspec-linear-sync ledger in proposal.md frontmatter to match: linear_story_state and last_synced_state to Todo, last_synced_at to the post timestamp, and a `posted` attempt_log entry appended after the earlier `dropped` one — so the ledger shows the offline drop followed by the realized post rather than rewriting it. review_round and max_review_rounds are untouched; a forward Backlog->Todo bind does not move the review counter.
…aseline off the installer
The runbook jumped from `clan machines install` to post-install verification, so an operator reading only this page never learned that stage 1 asks for a FIDO2 PIN and a touch, that YubiKey-A must be seated, or that a second boot with the token absent is what proves the passphrase slot opens the container. Task 7.5 requires both paths and states why: the passphrase is this machine's only recovery credential, so an unverified fallback is indistinguishable from an absent one until the token is lost. Carries 7.5's substance onto the page and makes both boots an explicit precondition of the key-lifecycle section, which removes YubiKey-A and leaves the passphrase as the sole authorizing credential.
The runbook ordered the header capture ahead of the second-token enrollment, inverting the normative SHALL in specs/encrypted-zfs-root/spec.md — a backup is taken once the container's keyslots reach their intended state — and the ordering tasks.md 7.12a states. An operator working top to bottom completed a whole capture-encrypt-transfer-upload-delete cycle against a two-slot container and met the correction only as a trailing sentence, so a restore would silently un-enroll YubiKey-B. The reorder is within the key-lifecycle section rather than lifting enrollment out to follow 'Verifying the install' directly: the section intro establishes $part2 and the root-shell discipline both subsections depend on, so moving one subsection out would strand it. Section heading reordered to match. The buried re-take instruction is dropped in favour of the retention rule already stated, and 7.12a is removed from the re-take trigger list since it now precedes the first capture. Also adds the third precondition the enrollment block claimed but never gave: the first-boot section's two unlock paths, which is the gate tasks.md 7.12a states.
…it its own criterion Two defects in the blkdiscard fallback block. The first: a prior revision dropped 'zpool labelclear -f $disk-part2' on the ground that under a container p2 holds no ZFS labels to clear. That is true of a future reinstall and false of the disk in front of the operator today, which carries a live pre-D1 zroot (7.16). ZFS writes L2 and L3 at the tail of the vdev — vdev_label_offset (module/zfs/vdev_label.c:163-171) places them in the last 512 KiB, since vdev_label_t is 256 KiB (include/sys/vdev_impl.h:537-543) and VDEV_LABEL_END_SIZE is two of them (:561) — so the 32 MiB head overwrite cannot reach them and whole-disk wipefs does not touch partition-interior offsets. disko recreates the layout at identical offsets, zpool.nix:298's import succeeds, and the surviving-pool skip fires on a fallback that reported success at every step. Both disk states are served explicitly rather than picking one order that is wrong half the time: labelclear and luksErase are each a no-op in the other's state, the states are mutually exclusive, and the text says so. labelclear goes first because it is the only step reaching the tail, and its verifying 'zpool import' must run before the zap removes the part2 node. The second: the block pointed at the post-wipe verification as its pass criterion, but arm 1a requires the disk's first 64 MiB to read as zeroes, which this sequence never produces — it zeroes p2's head, not the ESP region. A correctly-executed fallback therefore failed its own criterion. Replaced with a fallback-specific one: zpool import empty, wipefs -n empty, sgdisk -p announcing new in-memory GPT entries.
…able The builder-gate rationale said the closure falls back to the installer ISO; 'Build host behaviour' said it falls back to stibnite's Rosetta builder. Both were stated unconditionally and they contradict, and the first was the sole rationale for a hard pre-wipe gate. Resolved against pinned nixos-anywhere 1.13.0 rather than either sentence. clan passes no --build-on unless asked (clan_lib/machines/install.py:193-194), so the default 'auto' applies, and checkBuildLocally (src/nixos-anywhere.sh:611-652) decides by attempting a trivial x86_64-linux derivation on the admin box: success gives 'local', failure gives 'remote', which in nixos-anywhere means the target machine. Both original sentences describe real outcomes of that probe under different builder availability, which is why neither is simply deleted. stibnite's nix-rosetta-builder does advertise x86_64-linux (modules/machines/darwin/stibnite/default.nix:205-207) but is stopped by default, so off the mesh with it stopped the fallback is the ISO. Gate retained, consequence restated, and the reader is told to start the Rosetta builder rather than reason past the gate.
`jj git push -b` pushes where the bookmark points and does not advance it. pyrite-baremetal-nixos sits well behind the chain tip, so an operator running the tree-transfer step as written publishes a revision predating the review fixes and clones it onto pyrite as the tree the VM test validates. The disko.nix and flake.lock hashes cannot catch this — a stale bookmark can carry a matching layout — so a commit-id equality against `jj log -r @-` is added alongside them as the criterion that can. Applied to both the runbook and tasks.md, which carried the same push without the bookmark move.
…in the restore block The pre-install secrets gate was labelled 'host: stibnite' but written as a bash heredoc with a trailing 'echo "exit=$?"'. stibnite's interactive shell is fish, which supports neither <<'SH' nor $?, so the block errors on paste and the result line reports nothing; in bash it reported the wrapper's status rather than the gate's. Rewritten as a file the operator saves and runs with an explicit bash, exit-status line dropped, and the PASS line named as the sole affirmative signal — the heredoc's one real service, keeping the loop's 'exit 1' from closing the operator's shell, is preserved by the saved-script form. Separately, the header-restore block is labelled as runnable in a rescue environment but referenced $part2 from an assignment in a different block on the installed system. Repeated inline, as the enrollment block already does.
…try check The runbook states that /dev/nvme0n1 is a kernel enumeration order rather than an identity, that it resolves on stibnite to a different disk, and that a measurement taken against it is not evidence about the disk even when read-only — while tasks.md 7.2 and 7.4 specified exactly that name. Rewritten to the _1 by-id path. 7.2's recorded result keeps its factual provenance: the measurement really was taken against /dev/nvme0n1, so the record says so and the re-confirmation the task already required is now bound to the by-id path. The runbook's 'Verifying the install' carried no geometry check at all though it claims to carry 7.4, so 7.4's 'sgdisk -p' is added as check 1 and the rest renumbered. The keyslot check now uses $part2 like its siblings rather than the by-partlabel name, with the equivalence stated so tasks.md 7.6's form is not read as a different device. Also corrects specs/bare-metal-install-path 'at least one device' to 'exactly one', which the next line of the same scenario, D25, tasks.md 7.2a and the runbook all already said. A reader taking the requirement text as the criterion proceeds with two tokens seated, --fido2-device=auto fails to resolve, and disko exits under set -efux after luksFormat.
…d disko comment disko.nix cited '7.12's reinstall' as the last occasion to influence root-dataset creation; D29 deleted 7.12 and tasks.md records the numbering gap where it stood, leaving this comment the last artifact asserting a second install exists. Now names 7.3's destroy-and-recreate. The prompt-count reasoning pointed at the 'ykman fido info' gate 'above' when it is thirty lines below. The revocation block lacked the 'in a root shell (sudo -i)' annotation every sibling block carries, though the section states every systemd-cryptenroll call needs root and that $part2 does not survive a change of shell. The prerequisites section made the USB-C keyboard a hard gate and said nothing about the FIDO2 token, though YubiKey-A must be seated through the whole disko phase and its identity was not established until four hundred lines later. Named there now, with the reason it cannot wait. The past-tense APFS observation is scoped to the 2026-07-19 run inside its own sentence and points forward to the correction, rather than relying on a reader reaching it fifty lines later.
7.2b carried the same bash-heredoc-plus-$? gate the runbook did, so the two artifacts would otherwise disagree on how the gate is invoked. Rewritten to the saved-script form, exit-status line dropped, and the paragraph crediting the heredoc for shell survival re-attributed to the saved-script form that now provides it.
…equisite The runbook's ten PIN mentions all concerned whether a PIN is set on the token; none concerned the operator knowing it. The prompt lands on stibnite mid-disko, after the wipe, so an operator who cannot answer it is left with a formatted container and no fallback OS. Name the PIN beside the token and the keyboard, state that nothing in the repository records it, and give the ykman retry count as the budget for wrong entries.
…rst boot around it clan passes --no-reboot only when asked, so nixos-anywhere runs its default reboot phase and pyrite reboots itself six seconds after the install phase. The first-boot section read as if the operator initiated that boot, and told the operator to remove the installer SSD before powering the machine on -- unperformable, since the ISO is the running root until the reboot fires. Wording chosen to leave the boot-device outcome undiagnosed rather than asserting which device the firmware picks: both outcomes get an action.
The runbook asserted an unbounded prompt timeout in three places; design.md explicitly declines to assert it, recording that systemd-cryptsetup's query timeout under this configuration was not verified and that boot.zfs.password Timeout = 0 no longer applies. Scoped the runbook to the design's non-claim rather than discharging it, since discharging needs the built initrd on the machine the install has not yet produced. The keyboard gate is unaffected: it stands harder under a bounded wait than under an infinite one.
…sphrase prompt The token-absent boot exists solely to prove the only recovery credential, and it opens with half a minute of silence and then a message that reads as an abort. An operator who power-cycles there leaves the fallback unproven. Verified in systemd's src/cryptsetup/cryptsetup.c: the 30 s default token timeout, the EAGAIN return, and the main loop's clearing of the fido2 auto flag on retry, which is what raises the passphrase prompt.
…belclear account spec.md, design.md D8, and tasks.md 6.1 forbade the zpool labelclear the runbook restored, on the ground that under a LUKS container p2 holds no ZFS labels to clear. That is true of a later reinstall and false of the disk in front of the operator, which carries a live pre-D1 zroot with tail labels. Amended all three to the two-state account: labelclear is the live arm today, luksErase on a reinstall, and the states are mutually exclusive. Safety of running both re-derived from zpool_clear_label rather than asserted -- it skips any label slot that does not unpack to a valid guid and known state, so on a LUKS-bearing p2 it writes nothing and cannot pre-empt luksErase.
Both still said an off-mesh install falls back to the Rosetta builder, 'always available' and 'does not block'. The runbook establishes the opposite: the builder is stopped by default, checkBuildLocally's probe therefore fails, and buildOn=remote puts the build on the just-wiped installer ISO over WiFi. Read after a failed pre-wipe gate, the old wording invites exactly the reasoning-past-the-gate the runbook forbids.
Rebind $part2 in the revocation block, which is the block most likely to be run months later in a fresh shell and was the only one still relying on an inherited assignment. Drop tasks.md 7.2b's pointer at a heredoc the next paragraph says was removed. Replace the two stale relative pointers with positional-independent forms -- the enrollment section by name rather than 'four hundred lines below', and the 32 MiB overwrite by its position in the fallback block rather than 'below', which the labelclear insertion inverted. Named forms chosen over corrected distances because this document has been reordered four times and every positional reference is a latent defect.
… rev The runbook cited clan_lib/machines/install.py at two different revisions: the --no-reboot, phase-list, and --build-on citations were keyed to a local clone's HEAD c266bcd93, while the --extra-files and --disk-encryption-keys citations were keyed to the flake.lock rev d332b69. Under no single revision were install.py:162-168 = --extra-files and install.py:162-163 = --no-reboot both true, so a reader following either citation read the other. Renumber all five sites to d332b69 and state the convention once in the provenance paragraph, alongside the existing nixos-anywhere 1.13.0 pin.
…st boot The reboot phase fires the first boot about six seconds after the install phase, unattended, and the stage-1 prompt's timeout under this configuration is unmeasured. The disko phase already requires the operator at the machine; say that the requirement does not end there.
…harged design.md stated three pre-install obligations that neither tasks.md nor the runbook imposed, so all three would have been silently skipped. The first is covered by task 3.11's VM test and the second is harmless by D24. The third is accepted as a residual rather than turned into a task: the crypttab generator produces the unit at boot, so it is not a file in the image and the directory listing the paragraph assumes does not answer it.
…sites ykman prints a WARNING and an ERROR line about PC/SC before its real output, because pcscd runs on neither the installer ISO nor the installed system and the CCID transport is therefore unavailable. The runbook tells the operator to treat gates as hard stops, so an ERROR line immediately above the correct answer invites a false abort on a check that passed. Annotate the pre-wipe gate and the second-token enrollment, and record the shared AAGUID where the indistinguishability of the two tokens is already argued.
pyrite was the only machine in the fleet with no explicit services.openssh.settings.MaxAuthTries, so it took the OpenSSH compiled-in default of 6, while cinnabar, magnetite, electrum, galena, scheelite and the four darwin hosts all set 20. The default has not yet caused a failure because OpenSSH promotes an identity that is both an IdentityFile and agent-held to the front of the offer list, and ~/.ssh/id_ed25519 is both: a live ssh -v pyrite.zt enumerates 19 candidates but sends exactly one "Offering public key" before "Server accepts key". That promotion stops applying once the key no longer exists on disk, which is the direction the intended posture goes: agent-held keys with no physical copy on the machine. srvos is not the source of this setting on any host; every host that has it sets it explicitly. The deliberate omission of srvos from pyrite is unrelated to this change.
cameronraysmith
force-pushed
the
pyrite-baremetal-nixos
branch
from
July 30, 2026 19:39
f786d41 to
02f2bc4
Compare
…rator The committed artifact was minted on 2026-07-18 by the pre-D27 generator, `xkcdpass --numwords 6 --random-delimiters --case random > $out/key`, and was never regenerated after nkotuxksspqo (2026-07-19) added `| tr -d "\n"` and the layout-stable hyphen delimiter. The generator declares no `validation`, so `validationHash` is null and clan's currency check is omitted entirely; a re-run treated the stale artifact as current. Both failure modes D27 identifies were live in the artifact rather than the script. A trailing newline is handed to `cryptsetup luksAddKey` verbatim, so slot 1 would have been enrolled as passphrase-plus-newline while `systemd-ask-password` strips the terminating newline from what is typed -- the slot exists and opens for nobody. Random punctuation additionally may not map at the initrd console. With `enrollRecovery = false` that slot is the only non-token credential, and it is the credential 7.12a needs in order to enroll the second YubiKey at all. Verified on the regenerated artifact by boolean checks that emit no secret-derived characters: no trailing newline, hyphen-only delimiters, five hyphens for six words, all printable ASCII. Regenerated with `CLAN_NO_COMMIT=1 clan vars generate pyrite --generator zfs --regenerate`. `--regenerate` is load-bearing: without it `full_closure` is false, `all_missing_closure` filters out generators that already exist, and the command reports success while changing nothing. `CLAN_NO_COMMIT=1` returns from `commit_files` before any git invocation, leaving the development join untouched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.