cat / goq
clone

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

fix/docs-activation-truth
goq / docs / sigil-appliance-status-v2.md
sigil-appliance-status-v2.md 5.5K rendered
1# Sigil appliance status v223`sigil appliance status` is the local, read-only contract for appliance4management surfaces such as the planned Decky Loader plugin. Decky remains an5unprivileged controller over the existing `sigil-host.service`; it does not6launch Sigil as a child, host streaming itself, or add a network listener.78```bash9sigil appliance status \10 --config ~/.config/sigil-spark/host.toml \11 --json --schema-version 212```1314The command validates the owner-only configuration and identity, inspects the15authorization document without creating files or taking its writer lock, and16combines that durable state with Sigil's bounded runtime heartbeat. Output is a17single JSON document with `schema_version: 2`.1819Without `--schema-version 2`, the command continues to emit the original v120shape so independently updated scripts and management clients do not break.2122The public document includes only:2324- Sigil version and overall `ready`, `active`, `degraded`, or `unavailable`25 state.26- Redacted host fingerprint plus bounded enrollment count.27- Stable opaque viewer handles, canonically ordered coarse grants, enrollment28 times, per-viewer authorization revisions, and the global committed revision.29- Runtime freshness, daemon state, uptime, session mode, bounded active-viewer30 count, configured capacity, media generation, roster revision, focus31 occupancy, recovering-viewer count, authorization revision, and a closed set32 of error codes.33- The exact loaded configuration revision, fresh daemon instance ID, and sticky34 `reached_ready` evidence used by the transactional configuration gate.35- The current configuration revision and any pending transaction summary.3637It never includes complete endpoint IDs, invitation material, replay-ledger38entries, session nonces, addresses, PipeWire object names, filesystem paths, or39free-form error text.4041Authorization status is read from the strict v2 document without taking the42daemon's writer lease. Schema v2 reports the bounded viewer list and committed43revision. Schema v1 removes those new fields and retains its original44single-enrollment projection for compatibility. A valid development v145document can be inspected before migration, but only a sole writer converts it;46once v2 exists, stale v1 bytes are ignored.4748## Runtime authority4950While serving, Sigil holds an exclusive nonblocking lock at51`<state_path>/daemon-v1.lock`. A second daemon using the same state directory52fails before capture, input, or network initialization. The zero-length lock53file remains in place; the open file descriptor is the lifetime guard. When an54XDG runtime directory is available, Sigil also holds55`$XDG_RUNTIME_DIR/sigil-spark/daemon-global-v1.lock`. That per-user lock enforces56the product's one-daemon boundary even if two commands reference different57state directories. Configured production service mode requires a valid private58`XDG_RUNTIME_DIR` and fails before capture or networking if it is unavailable.59Only the explicit direct test-pattern proof may fall back to its per-state lock60without publishing a heartbeat.6162Offline enrollment reset and configuration rollback may fall back to the63durable per-state lock when `XDG_RUNTIME_DIR` is unset. Configuration `set` and64`commit` still require the service's exact runtime authority; management shells65without the environment may supply its private XDG root explicitly with66`--runtime-dir` (Sigil appends `sigil-spark`). Configured service startup itself67continues to require `XDG_RUNTIME_DIR`.6869Sigil atomically publishes70`$XDG_RUNTIME_DIR/sigil-spark/daemon-status-v1.json` at most once per two-second71heartbeat, plus explicit lifecycle transitions. Keeping the heartbeat in the72per-user runtime directory prevents high-frequency process state from entering73durable backups or surviving a reboot. The status file and its child directory74must be regular, owner-only objects. Reads and writes are bounded, refuse75symlinks, and use no-follow opens. Heartbeat I/O runs outside Tokio's async76worker threads.7778The runtime publisher writes strict heartbeat version 3. Its multi-viewer79fields are internally bounded: active count cannot exceed configured capacity80or the hard ceiling of eight, recovering count cannot exceed active count, and81generation/roster revisions are nonzero when present. `inactive`,82`legacy_exclusive`, and `moq_multi_viewer` have distinct fail-closed field83shapes. Schema-v1 appliance output removes these fields; it never invents a84roster or focus generation for a legacy heartbeat.8586The status command treats a missing runtime document as `offline`. A heartbeat87older than ten seconds is `stale`; stale data never claims a live session or88live uptime. Clean shutdown writes `stopping`, shuts down the Iroh router, and89then writes `stopped`. A crash leaves a bounded document that naturally becomes90stale, while the runtime directory itself is discarded at reboot. Every91runtime document is bound to the current host identity before its state is92combined with durable enrollment data.9394Runtime status v3 remains able to read version-1 and version-2 heartbeats after95an upgrade, but those legacy heartbeats cannot report multi-viewer details;96version 1 also carries no configuration revision and never satisfies the97configuration health gate. Stale or absent heartbeats do not expose an instance98ID, loaded revision, ready claim, active count, focus, or generation state.99100The Decky backend merges this document with `systemctl --user` unit state.101Service control, enrollment reset, and identity factory reset remain separate102contracts. Transactional configuration is defined by the103[configuration v1 contract](sigil-appliance-config-v1.md).