cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/architecture-review-findings
goq / docs / sigil-appliance-enrollment-reset-v1.md
sigil-appliance-enrollment-reset-v1.md 3.2K rendered
1# Sigil appliance enrollment reset v123`sigil appliance enrollment-reset` is the local mutation used by appliance4management surfaces to revoke the one enrolled Portal without rotating the5Sigil host identity:67```bash8sigil appliance enrollment-reset \9 --config "$HOME/.config/sigil-spark/host.toml" \10 --expected-host-fingerprint '12345678…90abcdef' \11 --json12```1314The caller must first stop `sigil-host.service` and wait for it to become15inactive. The command always acquires the durable16`<state_path>/daemon-v1.lock` without waiting. When a valid private17`XDG_RUNTIME_DIR` is available, it also acquires18`$XDG_RUNTIME_DIR/sigil-spark/daemon-global-v1.lock`; an unsafe configured19runtime directory fails closed. If the variable is unset, reset remains20available from SSH or a recovery shell using only the per-state lock. A live21daemon for that state still blocks the operation. The command never stops or22starts systemd itself. This keeps service policy in the Decky backend and23prevents a reset from leaving a live remote session attached.2425Authorization writers have an explicit second level in that hierarchy:26`daemon-v1.lock` (and the global lifecycle lock when configured) is acquired27before `<state_path>/authorization-v1.lock`. The daemon holds the authorization28writer lease for its complete lifetime and serves handshakes from its validated29in-memory state. An offline reset temporarily becomes the sole writer only30after it owns the lifecycle scope. Writer-lease acquisition uses nonblocking31`flock` with a fixed 250 ms bound, so an abandoned or hung management process32cannot stall daemon startup or recovery indefinitely; terminating a crashed33owner releases the kernel lease and a retry loads the last complete atomic34state document. Live handshake contention fails closed immediately and may be35retried by Portal.3637Status, `sigil enrollment show`, and invitation creation remain atomic38read-only operations and do not take the writer lease. The authorization-v139document, replay ledger, enrollment epoch, file names, permissions, and CLI40schemas are unchanged, so no state migration is required.4142The expected fingerprint is the redacted value returned by43`sigil appliance status`. It is an accidental-target confirmation interlock,44not a new authentication factor. Existing same-user file ownership and mode45checks remain the authority boundary.4647Success writes exactly one JSON object:4849```json50{51 "schema_version": 1,52 "operation": "enrollment_reset",53 "host_fingerprint": "12345678…90abcdef",54 "had_enrollment": true,55 "previous_epoch": 4,56 "current_epoch": 5,57 "invitations_invalidated": true58}59```6061Every successful invocation advances the authorization epoch, even when no62Portal is enrolled, because an unredeemed invitation may still exist outside63the host. The host identity stays byte-for-byte unchanged. Nonexpired replay64records remain intact, and all invitations from earlier epochs become stale.65A retry after a lost success response may advance the epoch again; that is a66safe additional invalidation.6768The legacy `sigil enrollment revoke` spelling is a strict alias. It also69requires `--expected-host-fingerprint` and `--json`, so there is no70confirmation-free or live-daemon bypass.