You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"Wipe but keep enrollment" has no built-in mechanism on Linux (no recovery partition, no A/B slots). A factory-reset-equivalent that preserves the device identity requires a fresh OS install. Design discussed and converged 2026-07-03.
Converged design (summary)
Admin provides an ISO (HTTPS URL + SHA-256), autoinstall file(s), and a kernel cmdline. The agent stages everything, boots the distro installer via a GRUB one-shot entry, the autoinstall drives a fresh install, and a one-time reinstall token lets the fresh agent re-enroll as the same device ULID — assignments re-run and re-provision automatically.
Key decisions:
Name: ACTION_TYPE_DEVICE_REINSTALL / DeviceReinstallParams — it is a reinstall, not disk erasure (LUKS + CRL already covers lost/stolen).
Instant, dispatch-only, single-device-only.
GRUB first behind the standard sys/boot Backend interface (kexec later, additive enum field then — no boot_method field in v1). Staging under /boot/pm-reinstall/ (works on LUKS; /boot is readable by GRUB). One-shot via grub-editenv next_entry → falls back to the intact old OS if the installer kernel never comes up.
Autoinstall delivery: agent packs templated autoinstall file(s) into a newc cpio and concatenates it onto the installer initrd (/pm-autoinstall/…; {autoinstall_path} cmdline variable). file:// against the old rootfs cannot work post-boot — initrd embedding is the mechanism. repeated AutoinstallFile {path, content} because NoCloud needs user-data + meta-data.
Token reaches the fresh OS via agent-side template substitution of {enrollment_token} / {control_url} / {device_id} inside the autoinstall content (documented per-distro late-command snippets).
ISO layout is admin-owned: iso_kernel_path / iso_initrd_path params, no probing.
stage_only dry-run: full download → verify → extract → template → report, no reboot. Canary story.
URL stays opaque HTTPS + sha256 — future S3/artifact storage integrates by minting the URL, zero proto changes (same shape as DEB/RPM/APP_IMAGE/AGENT_UPDATE).
Server-side work items
Proto (sdk): ACTION_TYPE_DEVICE_REINSTALL + DeviceReinstallParams (iso_url, iso_sha256, iso_kernel_path, iso_initrd_path, kernel_cmdline, repeated autoinstall_files, stage_only, enrollment_token) with full @gotags validate tags
Dispatch-surface lockdown: reject the type in CreateAction, AddActionToSet, definitions/action-sets, DispatchToGroup, DispatchToMultiple — single-device DispatchInstantAction only. Self-discovering deny test across ALL dispatch surfaces (AddActionToSet-bypass lesson, cf. fix(api): reject system actions in AddActionToSet (#477 guard gap) #485)
DispatchInstantAction path: reject client-supplied enrollment_token, server generates + injects it, signs the envelope
Reinstall token: new registration-token kind, hashed at rest, single-use, bound to existing device ULID, fixed 24 h TTL, janitor expiry
Enrollment mapping: reinstall-token enrollment maps to the existing device — supersede + CRL-revoke old cert fingerprint, close open executions, preserve ULID/labels/groups/assignments
State machine via events: ReinstallDispatched → ReinstallStaged → ReinstallExecuting (terminal agent event pre-reboot) → DeviceReinstalled | ReinstallExpired; device UI state "reinstalling"
New permission device:reinstall (+ scoped variant); UI step-up confirmation (type device name); audit events for dispatch + re-enrollment
Redaction: params.enrollmentToken + params.autoinstallFiles[].content in actionRedactionSchemas
Problem
"Wipe but keep enrollment" has no built-in mechanism on Linux (no recovery partition, no A/B slots). A factory-reset-equivalent that preserves the device identity requires a fresh OS install. Design discussed and converged 2026-07-03.
Converged design (summary)
Admin provides an ISO (HTTPS URL + SHA-256), autoinstall file(s), and a kernel cmdline. The agent stages everything, boots the distro installer via a GRUB one-shot entry, the autoinstall drives a fresh install, and a one-time reinstall token lets the fresh agent re-enroll as the same device ULID — assignments re-run and re-provision automatically.
Key decisions:
ACTION_TYPE_DEVICE_REINSTALL/DeviceReinstallParams— it is a reinstall, not disk erasure (LUKS + CRL already covers lost/stolen).sys/bootBackend interface (kexec later, additive enum field then — noboot_methodfield in v1). Staging under/boot/pm-reinstall/(works on LUKS;/bootis readable by GRUB). One-shot viagrub-editenv next_entry→ falls back to the intact old OS if the installer kernel never comes up./pm-autoinstall/…;{autoinstall_path}cmdline variable).file://against the old rootfs cannot work post-boot — initrd embedding is the mechanism.repeated AutoinstallFile {path, content}because NoCloud needsuser-data+meta-data.{enrollment_token}/{control_url}/{device_id}inside the autoinstall content (documented per-distro late-command snippets).iso_kernel_path/iso_initrd_pathparams, no probing.stage_onlydry-run: full download → verify → extract → template → report, no reboot. Canary story.Server-side work items
ACTION_TYPE_DEVICE_REINSTALL+DeviceReinstallParams(iso_url, iso_sha256, iso_kernel_path, iso_initrd_path, kernel_cmdline, repeated autoinstall_files, stage_only, enrollment_token) with full@gotags validatetagsDispatchInstantActionpath: reject client-suppliedenrollment_token, server generates + injects it, signs the envelopedevice:reinstall(+ scoped variant); UI step-up confirmation (type device name); audit events for dispatch + re-enrollmentparams.enrollmentToken+params.autoinstallFiles[].contentin actionRedactionSchemasCross-repo (tracked separately when work starts)
sys/boot— Backend enum (GRUB first: Preflight = bootloader detection fail-closed on non-GRUB, grubenv writability incl. btrfs/zfs/bootgotcha, disk space; Stage = /boot/pm-reinstall + cpio append; Execute = next_entry + reboot), newc cpio writer, container testsNon-goals
Process
Spec-driven: spec 19 in
docs/content/06-specs/with acceptance criteria + rejection-paths table + test requirements, approved before implementation.