cat / goq
clone

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

refactor: establish Portal and Sigil product names

Dami committed 23cb438
commit
23cb438492bf630ab8332c9905b082096254fcd8
parent
7e35f388eaba74ee3e1a44ca0050327a43016bcd
author
Dami <dami.etoile@gmail.com>
26 files changed, +848 -192
MCargo.lock44+++---
MREADME.md55+++++---
Mcrates/sigil-host/Cargo.toml13++-
Mcrates/sigil-host/src/main.rs6+-
Mcrates/sigil-protocol/Cargo.toml2+-
Mcrates/sigil-protocol/src/lib.rs2+-
Mdocs/fresh-bazzite-host.md84+++++++------
Mflake.nix2+-
Mscripts/50-sigil-spark-audio.conf2+-
Mscripts/install-bazzite-package.sh106++++++++++++++--
Mscripts/loopback-proof.sh14+-
Mscripts/package-bazzite-release.sh15++-
Mscripts/package-macos-client.sh16+-
Mscripts/rollback-bazzite-release.sh163++++++++++++++++++++++---
Mscripts/sigil-host.service5+-
Mscripts/stage-bazzite-release.sh156+++++++++++++++++++++---
Ascripts/tests/bazzite-host-executable-layout.sh78++++++++++++
Mscripts/tests/bazzite-inventory-cold-boot.sh16+-
Ascripts/tests/bazzite-release-activation.sh195++++++++++++++++++++++++++++++
Mscripts/tests/package-bazzite-provenance.sh20+++-
Mscripts/verify-demo-build.sh4+-
Msrc-tauri/Cargo.toml4+-
Msrc-tauri/src/commands/network.rs2+-
Msrc-tauri/src/commands/state.rs26++--
Msrc-tauri/src/main.rs4+-
Msrc-tauri/tauri.conf.json6+-
M Cargo.lock +22 -22
M README.md +34 -21
@@ -1,9 +1,9 @@
1-# Sigil Spark1+# goq.sh22 3-Sigil Spark is a native Tauri client and bare-metal Linux host for one-to-one,4-low-latency Steam streaming over iroh.3+goq.sh is one-to-one, low-latency Steam streaming over iroh: Portal is the4+installed Tauri client, and Sigil is the pure Rust bare-metal Linux host.55 6-The project starts from Sigil's working remote-desktop implementation. Native6+The project starts from an inherited working remote-desktop implementation. Native77 iroh connectivity, hardware video encoding, WebCodecs decoding, FIDO-derived88 identity, and remote input are already present. The next phase replaces the99 desktop-oriented hot path with a headless Gamescope pipeline.
@@ -11,13 +11,13 @@ desktop-oriented hot path with a headless Gamescope pipeline.
1111 ## Target architecture1212 1313 ```text14-Bare-metal Linux host14+Sigil · bare-metal Linux host1515 Gamescope headless1616 -> PipeWire video/audio1717 -> hardware H.264 + Opus1818 -> iroh/MoQ1919 20-Tauri client20+Portal · Tauri client2121 native iroh endpoint2222 -> binary Tauri channel2323 -> WebCodecs video
@@ -35,10 +35,11 @@ Steam and games.
3535 3636 - A shared, bounded and versioned protocol crate owns handshakes, H.264 media,3737 input messages, limits, and ALPNs.38-- The Linux host is a pure Rust binary with no Tauri or webview dependency.38+- The Linux host is the pure Rust `sigil` binary with no Tauri or webview39+ dependency.3940 - The Bazzite path captures the exact Gamescope PipeWire node and uses AMD4041 GstVA H.264 at the fixed 1280×800/60 first target.41-- The client delivers encoded frames to WebCodecs through a raw Tauri binary42+- Portal delivers encoded frames to WebCodecs through a raw Tauri binary4243 channel. The handoff is capped at four frames, the decode queue and4344 presentation queue at two, and a bounded watchdog recovers a suspended4445 webview animation-frame callback without presenting a frame twice. Its
@@ -47,7 +48,7 @@ Steam and games.
4748 - Linux `uinput` supports bounded relative-mouse and keyboard injection plus a4849 separate Xbox-style virtual gamepad, with strict device4950 ownership/mode/ACL preflight and neutralization when a session ends.50-- The installed Tauri application is client-only: it contains no host daemon,51+- The installed Portal application is client-only: it contains no host daemon,5152 host registration, capture, encoder, or desktop input-injection path.5253 - FIDO2 `hmac-secret` identity derivation remains in the normal client flow.5354 Debug builds have an explicit, visibly labeled direct-node bypass for test
@@ -68,7 +69,7 @@ authentication.
6869 6970 ## Immediate milestones7071 71-1. **Done:** split the bare-metal host into a pure Rust binary; keep Tauri client-only.72+1. **Done:** split Sigil into a pure Rust host binary; keep Portal client-only.7273 2. **Hardware-proven:** capture the Gamescope PipeWire node without an XDG portal or physical display; cold-boot service proof remains.7374 3. **Hardware-proven:** encode H.264 at 1280x800/60 with bounded buffers and no B-frames.7475 4. **Done:** replace base64 WebCodecs delivery with a bounded binary Tauri channel.
@@ -121,6 +122,11 @@ binaries, their dynamic libraries, and bounded startup commands validate. Host
121122 identity, hardware-specific configuration, and service activation remain122123 separate gates in the Bazzite runbook.123124 125+The thin binary stager is only for unmanaged development layouts. It refuses126+activation when package-managed service, audio, rollback, or udev links follow127+`current`; use the complete runtime package installer for those hosts so the128+active release always includes matching assets and rollback metadata.129+124130 Create the Bazzite runtime package from a committed source revision. Product125131 mode exports clean `HEAD` and builds the host and probe itself with locked126132 `cargo-zigbuild` in an isolated target directory; it never accepts externally
@@ -142,17 +148,24 @@ scripts/package-bazzite-release.sh \
142148 scripts/package-bazzite-release.sh \143149 --output /tmp/sigil-spark-host-prebuilt-dev.tar.gz \144150 --allow-dirty --allow-unsigned \145- --host-binary /absolute/path/to/sigil-host \151+ --host-binary /absolute/path/to/sigil \146152 --probe-binary /absolute/path/to/sigil-probe147153 ```148154 149-The package contains only the two Linux binaries, installer/rollback tools,150-service/audio/udev assets, license, checksums, and build provenance. It cannot151-contain the source tree, `.env` files, identities, host configuration, or test152-evidence. Its release ID is the SHA-256 of the complete installed-file checksum153-manifest. Identical inputs produce a byte-identical archive. The manifest marks154-product binaries as built from clean `HEAD`; caller-supplied development155-binaries are explicitly marked as having unverified provenance.155+The package contains the primary `sigil` host executable, its byte-identical156+`sigil-host` compatibility copy, the `sigil-probe` diagnostic, installer and157+rollback tools, service/audio/udev assets, license, checksums, and build158+provenance. It cannot contain the source tree, `.env` files, identities, host159+configuration, or test evidence. Its release ID is the SHA-256 of the complete160+installed-file checksum manifest. Identical inputs produce a byte-identical161+archive. The manifest marks product binaries as built from clean `HEAD`;162+caller-supplied development binaries are explicitly marked as having163+unverified provenance.164+165+During the legacy rollback window, `sigil-host.service` deliberately starts166+the byte-identical `sigil-host` compatibility filename. Interactive and167+documented host commands use `sigil`; retaining the service filename allows an168+older installed rollback helper to validate and reactivate a newer release.156169 157170 Verify the detached signature against the separately trusted public key before158171 extracting, then run `payload/stage-this-release.sh` as the gaming user. Install
@@ -160,8 +173,8 @@ and upgrade never restart PipeWire or start/enable the service. Roll back an
160173 installed upgrade with `sigil-spark-host-rollback`; add `--restart` only when a161174 service interruption is intended.162175 163-The macOS Tauri build currently produces an arm64 DMG for development. A public164-client release additionally requires Developer ID signing with hardened176+The macOS Portal build currently produces an arm64 DMG for development. A public177+Portal release additionally requires Developer ID signing with hardened165178 runtime, notarization, stapling, and strict Gatekeeper verification; ad-hoc166179 development signatures are not a distributable package. With Apple credentials167180 configured as described by the official
@@ -171,7 +184,7 @@ configured as described by the official
171184 scripts/package-macos-client.sh --output-dir /absolute/release/directory172185 ```173186 174-Run the current client/host application:187+Run Portal against Sigil during development:175188 176189 ```bash177190 source ~/.cargo/env
M crates/sigil-host/Cargo.toml +11 -2
M crates/sigil-host/src/main.rs +3 -3
M crates/sigil-protocol/Cargo.toml +1 -1
M crates/sigil-protocol/src/lib.rs +1 -1
M docs/fresh-bazzite-host.md +48 -36
@@ -1,7 +1,7 @@
11 # Fresh Bazzite AMD host setup22 33 This runbook takes a dedicated AMD x86_64 machine from a fresh Bazzite install4-to a remotely managed Sigil Spark host. It also defines how `slate` is used as4+to a remotely managed Sigil host. It also defines how `slate` is used as55 the temporary Linux stand-in while Steps 0–2 are being built.66 77 The dedicated Bazzite machine is the only target for Gamescope, PipeWire,
@@ -12,7 +12,7 @@ changes on `slate`.
1212 1313 | Machine | Role | Allowed work |1414 | --- | --- | --- |15-| Development Mac | Tauri client and primary source tree | Client development, tests, evidence collection |15+| Development Mac | Portal client and primary source tree | Client development, tests, evidence collection |1616 | `tank@slate` | Temporary Linux stand-in | Protocol tests, pure Rust host, synthetic H.264, iroh transport, reconnect tests |1717 | Dedicated Bazzite AMD host | Final appliance | Everything above plus Gamescope, PipeWire, hardware H.264, audio, and `uinput` |1818
@@ -25,15 +25,15 @@ Step 3.
2525 The repository provides these interfaces:2626 2727 ```text28-sigil-host identity init --output <path>29-sigil-host identity show --identity <path>30-sigil-host config check --config <path>31-sigil-host capture probe --source test-pattern --frames <count> --expect-size 1280x80032-sigil-host capture probe --source gamescope-pipewire --config <path> --frames <count>33-sigil-host serve --identity <path> --source test-pattern34-sigil-host serve --config <path>28+sigil identity init --output <path>29+sigil identity show --identity <path>30+sigil config check --config <path>31+sigil capture probe --source test-pattern --frames <count> --expect-size 1280x80032+sigil capture probe --source gamescope-pipewire --config <path> --frames <count>33+sigil serve --identity <path> --source test-pattern34+sigil serve --config <path>3535 sigil-probe --node-id <host-node-id> --frames <count>36-sigil-spark --dev-connect <host-node-id>36+portal --dev-connect <host-node-id>3737 ```3838 3939 The Gamescope source uses GStreamer's `pipewiresrc` and GstVA H.264 encoder,
@@ -309,8 +309,8 @@ Use these locations consistently:
309309 310310 | Content | Location |311311 | --- | --- |312-| Host releases | `~/.local/libexec/sigil-spark/releases/<commit>/sigil-host` |313-| Current binary | `~/.local/libexec/sigil-spark/current/sigil-host` |312+| Host releases | `~/.local/libexec/sigil-spark/releases/<commit>/sigil` (`sigil-host` is a compatibility copy) |313+| Current binary | `~/.local/libexec/sigil-spark/current/sigil` |314314 | Configuration | `~/.config/sigil-spark/host.toml` |315315 | Read-only host identity | `~/.local/share/sigil-spark/identity/host.key` |316316 | Writable runtime state | `~/.local/state/sigil-spark/runtime/` |
@@ -422,26 +422,32 @@ sigil_root="$HOME/.local/libexec/sigil-spark"
422422 sigil_release="$sigil_root/releases/$sigil_rev"423423 install -d -m 0755 "$sigil_release"424424 install -m 0755 \425- "$HOME/Developer/sigil-spark-revisions/$sigil_short/target/release/sigil-host" \426- "$sigil_release/sigil-host"427-sha256sum "$sigil_release/sigil-host"428-ldd "$sigil_release/sigil-host" | tee "$HOME/sigil-host-${sigil_short}.ldd"429-if grep -q 'not found' "$HOME/sigil-host-${sigil_short}.ldd"; then425+ "$HOME/Developer/sigil-spark-revisions/$sigil_short/target/release/sigil" \426+ "$sigil_release/sigil"427+install -m 0755 "$sigil_release/sigil" "$sigil_release/sigil-host"428+sha256sum "$sigil_release/sigil" "$sigil_release/sigil-host"429+ldd "$sigil_release/sigil" | tee "$HOME/sigil-${sigil_short}.ldd"430+if grep -q 'not found' "$HOME/sigil-${sigil_short}.ldd"; then430431 echo "Host runtime dependency missing." >&2431432 exit 1432433 fi433-"$sigil_release/sigil-host" --version434+"$sigil_release/sigil" --version434435 cd "$sigil_root"435436 test ! -e current || test -L current436437 ln -sfnT "releases/$sigil_rev" current437-sha256sum current/sigil-host438-current/sigil-host --version438+sha256sum current/sigil current/sigil-host439+current/sigil --version439440 ```440441 441442 For a demo deployment built and hashed on the development Mac, transfer both442-`sigil-host` and `sigil-probe` plus `scripts/stage-bazzite-release.sh`, then use443+`sigil` and `sigil-probe` plus `scripts/stage-bazzite-release.sh`, then use443444 the stager instead of the manual install block above:444445 446+The thin stager is restricted to an unmanaged development layout. If the447+service, audio, rollback, or udev assets are package-managed links that follow448+`current`, it fails before staging and instructs you to build the complete449+runtime package and run `payload/stage-this-release.sh` instead.450+445451 ```bash446452 scripts/stage-bazzite-release.sh \447453 --release-id <commit-or-source-snapshot-sha256> \
@@ -517,7 +523,7 @@ as an all-or-none pair with both development flags, and their manifest records
517523 scripts/package-bazzite-release.sh \518524 --output /tmp/sigil-spark-host-prebuilt-dev.tar.gz \519525 --allow-dirty --allow-unsigned \520- --host-binary /absolute/path/to/sigil-host \526+ --host-binary /absolute/path/to/sigil \521527 --probe-binary /absolute/path/to/sigil-probe522528 ```523529
@@ -530,13 +536,13 @@ the public node ID:
530536 umask 077531537 identity="$HOME/.local/share/sigil-spark/identity/host.key"532538 if ! test -e "$identity"; then533- "$HOME/.local/libexec/sigil-spark/current/sigil-host" identity init \539+ "$HOME/.local/libexec/sigil-spark/current/sigil" identity init \534540 --output "$identity"535541 fi536542 test ! -L "$identity"537543 chmod 0600 "$identity"538544 stat -c '%a %U %G %n' "$identity"539-"$HOME/.local/libexec/sigil-spark/current/sigil-host" identity show \545+"$HOME/.local/libexec/sigil-spark/current/sigil" identity show \540546 --identity "$identity"541547 ```542548
@@ -565,11 +571,11 @@ ffmpeg_path = "$ffmpeg_path"
565571 EOF566572 chmod 0600 "$config"567573 stat -c '%a %U %G %n' "$config"568-"$HOME/.local/libexec/sigil-spark/current/sigil-host" config check \574+"$HOME/.local/libexec/sigil-spark/current/sigil" config check \569575 --config "$config"570576 install -m 0600 "$config" \571577 "$HOME/.local/libexec/sigil-spark/current/host.toml"572-"$HOME/.local/libexec/sigil-spark/current/sigil-host" serve \578+"$HOME/.local/libexec/sigil-spark/current/sigil" serve \573579 --config "$config" --max-runtime-seconds 10574580 ```575581
@@ -589,7 +595,7 @@ Create `~/.config/systemd/user/sigil-host.service`:
589595 590596 ```ini591597 [Unit]592-Description=Sigil Spark streaming host598+Description=Sigil streaming host593599 Wants=pipewire.socket594600 After=pipewire.socket595601 ConditionPathExists=%h/.config/sigil-spark/host.toml
@@ -598,6 +604,8 @@ StartLimitIntervalSec=0
598604 [Service]599605 Type=simple600606 Environment=DISPLAY=:0607+# Compatibility window: the service uses the byte-identical legacy filename608+# so an older rollback helper can still validate and reactivate this release.601609 ExecStart=%h/.local/libexec/sigil-spark/current/sigil-host serve --config %h/.config/sigil-spark/host.toml602610 Restart=on-failure603611 RestartSec=1
@@ -736,7 +744,7 @@ On the Mac, connect with the node ID printed by `slate`:
736744 ```bash737745 cd /Users/dami/Developer/sigil-spark738746 source "$HOME/.cargo/env"739-cargo run -p sigil-spark -- --dev-connect <slate-node-id>747+cargo run -p portal -- --dev-connect <slate-node-id>740748 ```741749 742750 Before opening the UI, prove the same v1 media and input session headlessly:
@@ -821,7 +829,7 @@ At cutover, confirm there is no running probe before removing only the exact
821829 run-specific paths:822830 823831 ```bash824-pgrep -a sigil-host && {832+pgrep -a sigil && {825833 echo "Stop the foreground Sigil probe before cleanup." >&2826834 exit 1827835 }
@@ -1164,7 +1172,7 @@ device plus all low-latency properties used by the pipeline, and—when audio is
11641172 configured—prints the exact resolved sink target:11651173 11661174 ```bash1167-"$HOME/.local/libexec/sigil-spark/current/sigil-host" config check \1175+"$HOME/.local/libexec/sigil-spark/current/sigil" config check \11681176 --config "$probe_config"11691177 # Expected with audio enabled:11701178 # audio_pipewire_target_object=<current numeric target>
@@ -1175,7 +1183,7 @@ Node enumeration and preflight are not enough. Consume 300 frames and verify a
11751183 decodable H.264 keyframe, the configured output size, and a changing sequence:11761184 11771185 ```bash1178-"$HOME/.local/libexec/sigil-spark/current/sigil-host" capture probe \1186+"$HOME/.local/libexec/sigil-spark/current/sigil" capture probe \11791187 --source gamescope-pipewire \11801188 --config "$probe_config" \11811189 --frames 300 \
@@ -1269,7 +1277,7 @@ systemctl --user stop sigil-host.service
12691277 install -m 0600 \12701278 "$HOME/.config/sigil-spark/host-gamescope-probe.toml" \12711279 "$HOME/.config/sigil-spark/host.toml"1272-"$HOME/.local/libexec/sigil-spark/current/sigil-host" config check \1280+"$HOME/.local/libexec/sigil-spark/current/sigil" config check \12731281 --config "$HOME/.config/sigil-spark/host.toml"12741282 systemctl --user start sigil-host.service12751283 systemctl --user status sigil-host.service --no-pager
@@ -1363,7 +1371,7 @@ On the first SSH login after reboot:
13631371 ```bash13641372 systemctl --user status sigil-host.service --no-pager13651373 sudo journalctl -b -o short-monotonic --no-pager | \1366- grep -Ei 'sigil-host|sshd.*accepted|gamescope|steam'1374+ grep -Ei 'sigil|sshd.*accepted|gamescope|steam'13671375 ```13681376 13691377 The Sigil unit start must predate the first accepted SSH login; otherwise SSH
@@ -1425,9 +1433,13 @@ sigil_root="$HOME/.local/libexec/sigil-spark"
14251433 previous_commit="<validated-full-commit>"14261434 previous_config="$sigil_root/releases/$previous_commit/host.toml"14271435 cd "$sigil_root"1428-test -x "releases/$previous_commit/sigil-host"1436+previous_host="releases/$previous_commit/sigil"1437+if [[ ! -x "$previous_host" ]]; then1438+ previous_host="releases/$previous_commit/sigil-host"1439+fi1440+test -x "$previous_host"14291441 test -f "$previous_config"1430-"releases/$previous_commit/sigil-host" config check --config "$previous_config"1442+"$previous_host" config check --config "$previous_config"14311443 test ! -e current || test -L current14321444 ln -sfnT "releases/$previous_commit" current14331445 install -m 0600 "$previous_config" "$HOME/.config/sigil-spark/host.toml"
@@ -1436,7 +1448,7 @@ systemctl --user is-active --quiet sigil-host.service
14361448 ```14371449 14381450 Keep a validated backup of `host.toml` with each application release and run1439-`sigil-host config check` before restoring it. An OSTree rollback does not roll1451+`sigil config check` before restoring it. An OSTree rollback does not roll14401452 back binaries or configuration in the user's home directory, and it does not14411453 necessarily remove the SSH drop-in under `/etc`.14421454
M flake.nix +1 -1
M scripts/50-sigil-spark-audio.conf +1 -1
M scripts/install-bazzite-package.sh +94 -12
@@ -7,10 +7,12 @@ usage() {
77 cat <<'EOF'88 Usage: install-bazzite-package.sh [--payload-dir PATH]99 10-Verify and install one Sigil Spark Bazzite host runtime package. The release is11-fully staged and validated before the current symlink changes. Package-owned12-user assets follow current, so upgrades and rollbacks keep binaries, service,13-and PipeWire configuration on the same release.10+Verify and install one Sigil Bazzite host runtime package. The release is11+fully staged and validated before the current symlink changes. New packages12+contain the primary `sigil` executable and a byte-identical `sigil-host`13+compatibility copy. Package-owned user assets follow current, so upgrades and14+rollbacks keep binaries, service, and PipeWire configuration on the same15+release.1416 1517 This command never creates or replaces host identity/configuration, restarts1618 PipeWire, starts/enables the host service, changes groups, or writes /etc.
@@ -66,6 +68,7 @@ if ! diff -u \
6668 release/assets/70-sigil-remote-input.rules \6769 release/assets/sigil-host.service \6870 release/release-manifest.json \71+ release/sigil \6972 release/sigil-host \7073 release/sigil-probe \7174 release/tools/rollback-bazzite-release.sh \
@@ -81,6 +84,7 @@ if ! diff -u \
8184 assets/70-sigil-remote-input.rules \8285 assets/sigil-host.service \8386 release-manifest.json \87+ sigil \8488 sigil-host \8589 sigil-probe \8690 tools/rollback-bazzite-release.sh | LC_ALL=C sort) \
@@ -113,6 +117,80 @@ lock_path="$install_root/.install.lock"
113117 verify_unit="$install_root/.verify-package-$release_id-$$.service"114118 next_managed_links=()115119 120+# BEGIN release activation transaction helpers121+activation_mutated=false122+activation_committed=false123+activation_reload_service=false124+original_current_present=false125+original_current_target=""126+original_previous_present=false127+original_previous_target=""128+restore_current_temp="${current_link}.restore-$$"129+restore_previous_temp="${previous_link}.restore-$$"130+131+snapshot_activation_links() {132+ if [[ -L "$current_link" ]]; then133+ original_current_present=true134+ original_current_target="$(readlink "$current_link")"135+ else136+ original_current_present=false137+ original_current_target=""138+ fi139+ if [[ -L "$previous_link" ]]; then140+ original_previous_present=true141+ original_previous_target="$(readlink "$previous_link")"142+ else143+ original_previous_present=false144+ original_previous_target=""145+ fi146+}147+148+restore_activation_link() {149+ local link="$1"150+ local present="$2"151+ local target="$3"152+ local restore_temp="$4"153+154+ rm -f -- "$restore_temp"155+ if [[ "$present" == true ]]; then156+ ln -s "$target" "$restore_temp" || return 1157+ mv -Tf -- "$restore_temp" "$link" || return 1158+ elif [[ -L "$link" ]]; then159+ rm -f -- "$link" || return 1160+ elif [[ -e "$link" ]]; then161+ return 1162+ fi163+}164+165+restore_activation_links() {166+ if ! $activation_mutated || $activation_committed; then167+ return 0168+ fi169+170+ restore_activation_link \171+ "$current_link" "$original_current_present" "$original_current_target" \172+ "$restore_current_temp" || true173+ restore_activation_link \174+ "$previous_link" "$original_previous_present" "$original_previous_target" \175+ "$restore_previous_temp" || true176+ if $activation_reload_service; then177+ systemctl --user daemon-reload >/dev/null 2>&1 || true178+ fi179+}180+181+activate_release_links() {182+ activation_mutated=true183+ if [[ -L "$next_previous" ]]; then184+ mv -Tf -- "$next_previous" "$previous_link" || return 1185+ fi186+ mv -Tf -- "$next_current" "$current_link" || return 1187+ if $activation_reload_service; then188+ systemctl --user daemon-reload || return 1189+ fi190+ activation_committed=true191+}192+# END release activation transaction helpers193+116194 install -d -m 0755 "$HOME/.local" "$HOME/.local/libexec" "$install_root" "$releases_root"117195 exec 9>"$lock_path"118196 flock -x 9
@@ -120,12 +198,15 @@ flock -x 9
120198 cleanup() {121199 local status=$?122200 trap - EXIT INT TERM HUP201+ restore_activation_links123202 if [[ -d "$staging_path" ]]; then124203 find "$staging_path" -type f -delete 2>/dev/null || true125204 find "$staging_path" -depth -type d -empty -delete 2>/dev/null || true126205 fi127206 [[ ! -L "$next_current" ]] || rm -f -- "$next_current"128207 [[ ! -L "$next_previous" ]] || rm -f -- "$next_previous"208+ [[ ! -L "$restore_current_temp" ]] || rm -f -- "$restore_current_temp"209+ [[ ! -L "$restore_previous_temp" ]] || rm -f -- "$restore_previous_temp"129210 for next_managed_link in "${next_managed_links[@]}"; do130211 [[ ! -L "$next_managed_link" ]] || rm -f -- "$next_managed_link"131212 done
@@ -144,18 +225,20 @@ validate_release_tree() {
144225 cd "$path"145226 sha256sum -c SHA256SUMS146227 )147- for binary in sigil-host sigil-probe; do228+ for binary in sigil sigil-host sigil-probe; do148229 [[ -f "$path/$binary" && ! -L "$path/$binary" && -x "$path/$binary" ]] \149230 || die "$binary is not a regular executable"150231 mode="$(stat -Lc '%a' "$path/$binary")"151232 (( (8#$mode & 8#022) == 0 )) || die "$binary is group/world writable"152233 done153- ldd_output="$(ldd "$path/sigil-host")"234+ cmp -s "$path/sigil" "$path/sigil-host" \235+ || die "sigil-host compatibility executable differs from sigil"236+ ldd_output="$(ldd "$path/sigil")"154237 printf '%s\n' "$ldd_output"155238 grep -q 'not found' <<<"$ldd_output" && die "host binary has an unresolved shared library"156- timeout --signal=TERM --kill-after=2s 5s "$path/sigil-host" --version239+ timeout --signal=TERM --kill-after=2s 5s "$path/sigil" --version157240 timeout --signal=TERM --kill-after=2s 5s "$path/sigil-probe" --help >/dev/null158- awk -v executable="$path/sigil-host" '241+ awk -v executable="$path/sigil" '159242 /^ExecStart=/ {160243 print "ExecStart=" executable " serve --config=%h/.config/sigil-spark/host.toml"161244 next
@@ -200,6 +283,8 @@ fi
200283 if [[ -e "$previous_link" || -L "$previous_link" ]]; then201284 [[ -L "$previous_link" ]] || die "previous activation is not a symlink"202285 fi286+snapshot_activation_links287+activation_reload_service=true203288 204289 config_root="$HOME/.config"205290 pipewire_dir="$config_root/pipewire/pipewire-pulse.conf.d"
@@ -296,11 +381,8 @@ install_managed_links
296381 ln -s "releases/$release_id" "$next_current"297382 if [[ -n "$current_release_id" && "$current_release_id" != "$release_id" ]]; then298383 ln -s "releases/$current_release_id" "$next_previous"299- mv -Tf -- "$next_previous" "$previous_link"300384 fi301-mv -Tf -- "$next_current" "$current_link"302-303-systemctl --user daemon-reload385+activate_release_links304386 305387 printf 'package_release=%s\n' "$release_id"306388 printf 'current=%s\n' "$(readlink -f "$current_link")"
M scripts/loopback-proof.sh +7 -7
@@ -22,7 +22,7 @@ usage() {
2222 cat <<'EOF'2323 Usage: scripts/loopback-proof.sh [options]2424 25-Build and exercise the exact sigil-host and sigil-probe binaries over a real25+Build and exercise the exact sigil and sigil-probe binaries over a real2626 loopback Iroh connection. The proof covers the decodable first media frame,2727 input acknowledgment, single-active-client rejection, and clean reconnects.2828
@@ -338,7 +338,7 @@ case "$target_root" in
338338 /*) ;;339339 *) target_root="$workspace_dir/$target_root" ;;340340 esac341-host_bin="$target_root/$profile_dir/sigil-host"341+host_bin="$target_root/$profile_dir/sigil"342342 probe_bin="$target_root/$profile_dir/sigil-probe"343343 [[ -x "$host_bin" ]] || die "built host binary is missing: $host_bin"344344 [[ -x "$probe_bin" ]] || die "built probe binary is missing: $probe_bin"
@@ -366,7 +366,7 @@ RUST_LOG=info "$host_bin" serve \
366366 host_pid=$!367367 start_watchdog "$host_pid" "$host_runtime_seconds" "$tmp_root/host.timeout"368368 host_watchdog_pid="$watchdog_pid"369-wait_for_log_line "$host_log" 'status=ready' "$host_pid" 'sigil-host' || die "host did not become ready"369+wait_for_log_line "$host_log" 'status=ready' "$host_pid" 'sigil' || die "host did not become ready"370370 host_node_id="$(sed -n 's/^node_id=//p' "$host_log" | tail -n 1)"371371 [[ "$host_node_id" == "$node_id" ]] || die "ready host node ID does not match its identity"372372
@@ -418,9 +418,9 @@ validate_probe_log "$primary_log" "$primary_frames" || {
418418 die "primary probe evidence is incomplete"419419 }420420 421-wait_for_log_count "$host_log" "$media_release_log" 1 "$host_pid" 'sigil-host' \421+wait_for_log_count "$host_log" "$media_release_log" 1 "$host_pid" 'sigil' \422422 || die "primary session was not released"423-wait_for_log_count "$host_log" 'input client released' 1 "$host_pid" 'sigil-host' \423+wait_for_log_count "$host_log" 'input client released' 1 "$host_pid" 'sigil' \424424 || die "primary input session was not drained"425425 426426 cycle=1
@@ -436,9 +436,9 @@ while [[ "$cycle" -le "$reconnect_cycles" ]]; do
436436 sed -n '1,240p' "$reconnect_log" >&2 || true437437 die "reconnect cycle $cycle evidence is incomplete"438438 }439- wait_for_log_count "$host_log" "$media_release_log" "$((cycle + 1))" "$host_pid" 'sigil-host' \439+ wait_for_log_count "$host_log" "$media_release_log" "$((cycle + 1))" "$host_pid" 'sigil' \440440 || die "reconnect cycle $cycle was not released"441- wait_for_log_count "$host_log" 'input client released' "$((cycle + 1))" "$host_pid" 'sigil-host' \441+ wait_for_log_count "$host_log" 'input client released' "$((cycle + 1))" "$host_pid" 'sigil' \442442 || die "reconnect input cycle $cycle was not drained"443443 cycle=$((cycle + 1))444444 done
M scripts/package-bazzite-release.sh +10 -5
@@ -26,7 +26,7 @@ tree, host identity, hardware configuration, environment files, or evidence.
2626 2727 Options:2828 --output PATH New .tar.gz bundle path (required; must not exist)29- --host-binary PATH Development-only prebuilt generic Linux sigil-host29+ --host-binary PATH Development-only prebuilt generic Linux sigil3030 --probe-binary PATH Development-only prebuilt generic Linux sigil-probe3131 --minisign-key PATH Create detached PACKAGE.minisig with this secret key3232 --allow-dirty Permit development packaging from a dirty worktree
@@ -173,10 +173,10 @@ else
173173 CARGO_TARGET_DIR="$build_target" cargo zigbuild --locked -p sigil-host --bins \174174 --target "$linux_target" --release175175 )176- host_binary="$build_target/$linux_output_target/release/sigil-host"176+ host_binary="$build_target/$linux_output_target/release/sigil"177177 probe_binary="$build_target/$linux_output_target/release/sigil-probe"178178 [[ -f "$host_binary" && -x "$host_binary" ]] \179- || die "product build did not produce sigil-host"179+ || die "product build did not produce sigil"180180 [[ -f "$probe_binary" && -x "$probe_binary" ]] \181181 || die "product build did not produce sigil-probe"182182 binary_provenance="self-built-clean-head"
@@ -186,6 +186,8 @@ fi
186186 payload="$temp_root/payload"187187 release_tree="$payload/release"188188 install -d -m 0700 "$payload" "$release_tree" "$release_tree/assets" "$release_tree/tools"189+install -m 0755 "$host_binary" "$release_tree/sigil"190+# Retain one byte-identical compatibility executable for existing automation.189191 install -m 0755 "$host_binary" "$release_tree/sigil-host"190192 install -m 0755 "$probe_binary" "$release_tree/sigil-probe"191193 install -m 0755 "$script_dir/rollback-bazzite-release.sh" \
@@ -225,7 +227,9 @@ import sys
225227 ) = sys.argv[1:]226228 manifest = {227229 "format": 2,228- "product": "sigil-spark-host",230+ "product": "sigil-host",231+ "primary_executable": "sigil",232+ "compatibility_executable": "sigil-host",229233 "version": version,230234 "target": target,231235 "profile": "release",
@@ -246,6 +250,7 @@ chmod 0644 "$release_tree/release-manifest.json"
246250 release_sums="$release_tree/SHA256SUMS"247251 : >"$release_sums"248252 for relative in \253+ sigil \249254 sigil-host \250255 sigil-probe \251256 assets/50-sigil-spark-audio.conf \
@@ -313,7 +318,7 @@ package_sha256="$(sha256_file "$output_path")"
313318 printf '%s %s\n' "$package_sha256" "$(basename -- "$output_path")" >"$output_path.sha256"314319 if [[ -n "$minisign_secret_key" ]]; then315320 minisign -S -s "$minisign_secret_key" -m "$output_path" -x "$output_path.minisig" \316- -t "Sigil Spark host $product_version" \321+ -t "Sigil host $product_version" \317322 -c "release $release_id"318323 signature_status="detached-minisign"319324 else
M scripts/package-macos-client.sh +8 -8
@@ -87,7 +87,7 @@ source "$HOME/.cargo/env"
8787 )8888 8989 bundle_root="$repo_dir/target/release/bundle"90-app_path="$bundle_root/macos/Sigil Spark.app"90+app_path="$bundle_root/macos/Portal.app"9191 [[ -d "$app_path" ]] || die "Tauri app bundle is missing"9292 dmg_count="$(find "$bundle_root/dmg" -maxdepth 1 -type f -name '*.dmg' -print | wc -l | tr -d ' ')"9393 [[ "$dmg_count" -eq 1 ]] || die "expected exactly one DMG artifact"
@@ -107,23 +107,23 @@ xcrun stapler validate "$app_path"
107107 xcrun stapler validate "$dmg_path"108108 spctl --assess --type open --context context:primary-signature --verbose=4 "$dmg_path"109109 110-executable="$app_path/Contents/MacOS/sigil-spark"110+executable="$app_path/Contents/MacOS/portal"111111 [[ -x "$executable" ]] || die "client executable is missing"112112 architectures="$(lipo -archs "$executable")"113113 grep -Eq "(^| )$expected_arch( |$)" <<<"$architectures" \114114 || die "client executable does not contain $expected_arch"115115 identifier="$(plutil -extract CFBundleIdentifier raw "$app_path/Contents/Info.plist")"116-[[ "$identifier" == com.sigil.spark ]] || die "unexpected bundle identifier: $identifier"117-if find "$app_path" -type f \( -name sigil-host -o -name sigil-probe -o -name host.toml -o -name '*.key' \) \116+[[ "$identifier" == sh.goq.portal ]] || die "unexpected bundle identifier: $identifier"117+if find "$app_path" -type f \( -name sigil -o -name sigil-host -o -name sigil-probe -o -name host.toml -o -name '*.key' \) \118118 | grep -q .119119 then120120 die "client bundle contains a forbidden host or credential artifact"121121 fi122122 123123 version="$(plutil -extract CFBundleShortVersionString raw "$app_path/Contents/Info.plist")"124-artifact_name="Sigil-Spark-${version}-${expected_arch}.dmg"124+artifact_name="Portal-${version}-${expected_arch}.dmg"125125 artifact_path="$output_dir/$artifact_name"126-manifest_path="$output_dir/Sigil-Spark-${version}-${expected_arch}.json"126+manifest_path="$output_dir/Portal-${version}-${expected_arch}.json"127127 [[ ! -e "$artifact_path" && ! -e "$manifest_path" && ! -e "$artifact_path.sha256" ]] \128128 || die "release output already exists"129129 install -m 0644 "$dmg_path" "$artifact_path"
@@ -137,9 +137,9 @@ import sys
137137 path, version, expected_arch, architectures, digest = sys.argv[1:]138138 manifest = {139139 "format": 1,140- "product": "sigil-spark-client",140+ "product": "portal-client",141141 "version": version,142- "bundle_identifier": "com.sigil.spark",142+ "bundle_identifier": "sh.goq.portal",143143 "expected_arch": expected_arch,144144 "architectures": architectures.split(),145145 "sha256": digest,
M scripts/rollback-bazzite-release.sh +142 -21
@@ -9,8 +9,10 @@ Usage: rollback-bazzite-release.sh [--to RELEASE_ID] [--restart]
99 1010 Atomically switch ~/.local/libexec/sigil-spark/current to the requested1111 installed release. Without --to, switch to the release recorded by the12-previous symlink. The release is validated before activation. The former13-current release becomes previous, making a second rollback reversible.12+previous symlink. The release is validated before activation, including13+legacy releases that predate the primary `sigil` executable and contain only14+`sigil-host`. The former current release becomes previous, making a second15+rollback reversible.1416 1517 The host service is not restarted unless --restart is explicit.1618 EOF
@@ -46,7 +48,7 @@ done
4648 [[ "$(uname -s)" == Linux ]] || die "this rollback helper must run on Linux"4749 [[ "$(uname -m)" == x86_64 ]] || die "expected x86_64, got $(uname -m)"4850 [[ "$(id -u)" -ne 0 ]] || die "run as the dedicated gaming user, not root"49-for command in flock ldd sha256sum systemd-analyze timeout; do51+for command in cmp flock ldd sha256sum systemctl systemd-analyze timeout; do5052 command -v "$command" >/dev/null 2>&1 || die "$command is required"5153 done5254
@@ -85,6 +87,99 @@ fi
8587 [[ "$target_release_id" != "$current_release_id" ]] || die "target release is already current"8688 8789 target_path="$releases_root/$target_release_id"90+next_current="$install_root/.current-rollback-$$"91+next_previous="$install_root/.previous-rollback-$$"92+verify_unit="$install_root/.verify-rollback-$target_release_id-$$.service"93+94+# BEGIN release activation transaction helpers95+activation_mutated=false96+activation_committed=false97+activation_reload_service=false98+original_current_present=false99+original_current_target=""100+original_previous_present=false101+original_previous_target=""102+restore_current_temp="${current_link}.restore-$$"103+restore_previous_temp="${previous_link}.restore-$$"104+105+snapshot_activation_links() {106+ if [[ -L "$current_link" ]]; then107+ original_current_present=true108+ original_current_target="$(readlink "$current_link")"109+ else110+ original_current_present=false111+ original_current_target=""112+ fi113+ if [[ -L "$previous_link" ]]; then114+ original_previous_present=true115+ original_previous_target="$(readlink "$previous_link")"116+ else117+ original_previous_present=false118+ original_previous_target=""119+ fi120+}121+122+restore_activation_link() {123+ local link="$1"124+ local present="$2"125+ local target="$3"126+ local restore_temp="$4"127+128+ rm -f -- "$restore_temp"129+ if [[ "$present" == true ]]; then130+ ln -s "$target" "$restore_temp" || return 1131+ mv -Tf -- "$restore_temp" "$link" || return 1132+ elif [[ -L "$link" ]]; then133+ rm -f -- "$link" || return 1134+ elif [[ -e "$link" ]]; then135+ return 1136+ fi137+}138+139+restore_activation_links() {140+ if ! $activation_mutated || $activation_committed; then141+ return 0142+ fi143+144+ restore_activation_link \145+ "$current_link" "$original_current_present" "$original_current_target" \146+ "$restore_current_temp" || true147+ restore_activation_link \148+ "$previous_link" "$original_previous_present" "$original_previous_target" \149+ "$restore_previous_temp" || true150+ if $activation_reload_service; then151+ systemctl --user daemon-reload >/dev/null 2>&1 || true152+ fi153+}154+155+activate_release_links() {156+ activation_mutated=true157+ if [[ -L "$next_previous" ]]; then158+ mv -Tf -- "$next_previous" "$previous_link" || return 1159+ fi160+ mv -Tf -- "$next_current" "$current_link" || return 1161+ if $activation_reload_service; then162+ systemctl --user daemon-reload || return 1163+ fi164+ activation_committed=true165+}166+# END release activation transaction helpers167+168+cleanup() {169+ local status=$?170+ trap - EXIT INT TERM HUP171+ restore_activation_links172+ [[ ! -L "$next_current" ]] || rm -f -- "$next_current"173+ [[ ! -L "$next_previous" ]] || rm -f -- "$next_previous"174+ [[ ! -L "$restore_current_temp" ]] || rm -f -- "$restore_current_temp"175+ [[ ! -L "$restore_previous_temp" ]] || rm -f -- "$restore_previous_temp"176+ [[ ! -f "$verify_unit" ]] || rm -f -- "$verify_unit"177+ exit "$status"178+}179+trap cleanup EXIT INT TERM HUP180+snapshot_activation_links181+activation_reload_service=true182+88183 [[ -d "$target_path" && ! -L "$target_path" ]] || die "target release is not an installed directory"89184 [[ "$(stat -Lc '%u' "$target_path")" -eq "$(id -u)" ]] || die "target release is not owned by the current user"90185 [[ -f "$target_path/SHA256SUMS" && ! -L "$target_path/SHA256SUMS" ]] \
@@ -95,35 +190,61 @@ target_path="$releases_root/$target_release_id"
95190 cd "$target_path"96191 sha256sum -c SHA256SUMS97192 )98-for binary in sigil-host sigil-probe; do99- path="$target_path/$binary"193+194+# BEGIN release host executable compatibility helper195+release_host_executable() {196+ local release_path="$1"197+198+ if [[ -e "$release_path/sigil" || -L "$release_path/sigil" ]]; then199+ [[ -f "$release_path/sigil" && ! -L "$release_path/sigil" \200+ && -x "$release_path/sigil" ]] || return 1201+ [[ -f "$release_path/sigil-host" && ! -L "$release_path/sigil-host" \202+ && -x "$release_path/sigil-host" ]] || return 1203+ cmp -s "$release_path/sigil" "$release_path/sigil-host" || return 1204+ printf '%s\n' "$release_path/sigil"205+ return 0206+ fi207+ if [[ -f "$release_path/sigil-host" && ! -L "$release_path/sigil-host" \208+ && -x "$release_path/sigil-host" ]]; then209+ printf '%s\n' "$release_path/sigil-host"210+ return 0211+ fi212+ return 1213+}214+# END release host executable compatibility helper215+216+host_executable="$(release_host_executable "$target_path")" \217+ || die "target release has no valid host executable layout"218+release_binaries=("$host_executable" "$target_path/sigil-probe")219+if [[ "$host_executable" == "$target_path/sigil" ]]; then220+ release_binaries+=("$target_path/sigil-host")221+fi222+for path in "${release_binaries[@]}"; do223+ binary="$(basename -- "$path")"100224 [[ -f "$path" && ! -L "$path" && -x "$path" ]] || die "target $binary is not a regular executable"101225 mode="$(stat -Lc '%a' "$path")"102226 (( (8#$mode & 8#022) == 0 )) || die "target $binary is group/world writable"103227 done104228 105-ldd_output="$(ldd "$target_path/sigil-host")"229+ldd_output="$(ldd "$host_executable")"106230 printf '%s\n' "$ldd_output"107231 grep -q 'not found' <<<"$ldd_output" && die "target host binary has an unresolved shared library"108-timeout --signal=TERM --kill-after=2s 5s "$target_path/sigil-host" --version232+timeout --signal=TERM --kill-after=2s 5s "$host_executable" --version109233 timeout --signal=TERM --kill-after=2s 5s "$target_path/sigil-probe" --help >/dev/null110-systemd-analyze --user verify "$target_path/assets/sigil-host.service"111-112-next_current="$install_root/.current-rollback-$$"113-next_previous="$install_root/.previous-rollback-$$"114-cleanup() {115- local status=$?116- trap - EXIT INT TERM HUP117- [[ ! -L "$next_current" ]] || rm -f -- "$next_current"118- [[ ! -L "$next_previous" ]] || rm -f -- "$next_previous"119- exit "$status"120-}121-trap cleanup EXIT INT TERM HUP234+awk -v executable="$host_executable" '235+ /^ExecStart=/ {236+ print "ExecStart=" executable " serve --config=%h/.config/sigil-spark/host.toml"237+ next238+ }239+ { print }240+' "$target_path/assets/sigil-host.service" >"$verify_unit"241+chmod 0600 "$verify_unit"242+systemd-analyze --user verify "$verify_unit"243+rm -f -- "$verify_unit"122244 123245 ln -s "releases/$target_release_id" "$next_current"124246 ln -s "releases/$current_release_id" "$next_previous"125-mv -Tf -- "$next_previous" "$previous_link"126-mv -Tf -- "$next_current" "$current_link"247+activate_release_links127248 128249 printf 'current=%s\n' "$(readlink -f "$current_link")"129250 printf 'previous=%s\n' "$(readlink -f "$previous_link")"
M scripts/sigil-host.service +4 -1
M scripts/stage-bazzite-release.sh +139 -17
@@ -14,9 +14,13 @@ Verify and atomically stage prebuilt x86_64 Linux Sigil binaries beneath:
1414 1515 ~/.local/libexec/sigil-spark/releases/<release-id>/1616 17-The script changes the `current` symlink only after both installed binaries18-pass their hashes, dynamic-library check, and bounded startup checks. It does19-not create an identity, install configuration, or start/enable a service.17+The script changes the `current` symlink only after the installed `sigil`18+executable, its byte-identical `sigil-host` compatibility copy, and19+`sigil-probe` pass their hashes, dynamic-library check, and bounded startup20+checks. It does not create an identity, install configuration, or start/enable21+a service. It refuses to replace `current` when package-managed assets follow22+that link; build the runtime package and use `payload/stage-this-release.sh`23+for upgrades on a package-managed host.2024 EOF2125 }2226
@@ -73,6 +77,7 @@ done
7377 [[ "$host_sha256" =~ ^[0-9a-f]{64}$ ]] || die "host SHA-256 must be 64 lowercase hexadecimal characters"7478 [[ "$probe_sha256" =~ ^[0-9a-f]{64}$ ]] || die "probe SHA-256 must be 64 lowercase hexadecimal characters"7579 80+command -v cmp >/dev/null 2>&1 || die "cmp is required"7681 command -v sha256sum >/dev/null 2>&1 || die "sha256sum is required"7782 command -v ldd >/dev/null 2>&1 || die "ldd is required"7883 command -v timeout >/dev/null 2>&1 || die "timeout is required"
@@ -103,9 +108,118 @@ releases_root="$install_root/releases"
103108 release_path="$releases_root/$release_id"104109 staging_path="$releases_root/.stage-$release_id-$$"105110 current_link="$install_root/current"106-next_link="$install_root/.current-$release_id-$$"111+next_current="$install_root/.current-$release_id-$$"107112 previous_link="$install_root/previous"108-next_previous_link="$install_root/.previous-$release_id-$$"113+next_previous="$install_root/.previous-$release_id-$$"114+115+# BEGIN release activation transaction helpers116+activation_mutated=false117+activation_committed=false118+activation_reload_service=false119+original_current_present=false120+original_current_target=""121+original_previous_present=false122+original_previous_target=""123+restore_current_temp="${current_link}.restore-$$"124+restore_previous_temp="${previous_link}.restore-$$"125+126+snapshot_activation_links() {127+ if [[ -L "$current_link" ]]; then128+ original_current_present=true129+ original_current_target="$(readlink "$current_link")"130+ else131+ original_current_present=false132+ original_current_target=""133+ fi134+ if [[ -L "$previous_link" ]]; then135+ original_previous_present=true136+ original_previous_target="$(readlink "$previous_link")"137+ else138+ original_previous_present=false139+ original_previous_target=""140+ fi141+}142+143+restore_activation_link() {144+ local link="$1"145+ local present="$2"146+ local target="$3"147+ local restore_temp="$4"148+149+ rm -f -- "$restore_temp"150+ if [[ "$present" == true ]]; then151+ ln -s "$target" "$restore_temp" || return 1152+ mv -Tf -- "$restore_temp" "$link" || return 1153+ elif [[ -L "$link" ]]; then154+ rm -f -- "$link" || return 1155+ elif [[ -e "$link" ]]; then156+ return 1157+ fi158+}159+160+restore_activation_links() {161+ if ! $activation_mutated || $activation_committed; then162+ return 0163+ fi164+165+ restore_activation_link \166+ "$current_link" "$original_current_present" "$original_current_target" \167+ "$restore_current_temp" || true168+ restore_activation_link \169+ "$previous_link" "$original_previous_present" "$original_previous_target" \170+ "$restore_previous_temp" || true171+ if $activation_reload_service; then172+ systemctl --user daemon-reload >/dev/null 2>&1 || true173+ fi174+}175+176+activate_release_links() {177+ activation_mutated=true178+ if [[ -L "$next_previous" ]]; then179+ mv -Tf -- "$next_previous" "$previous_link" || return 1180+ fi181+ mv -Tf -- "$next_current" "$current_link" || return 1182+ if $activation_reload_service; then183+ systemctl --user daemon-reload || return 1184+ fi185+ activation_committed=true186+}187+# END release activation transaction helpers188+189+# BEGIN package-managed activation guard190+refuse_package_managed_activation() {191+ local -a managed_links=(192+ "$HOME/.config/pipewire/pipewire-pulse.conf.d/50-sigil-spark-audio.conf"193+ "$HOME/.config/systemd/user/sigil-host.service"194+ "$HOME/.local/bin/sigil-spark-host-rollback"195+ "$HOME/.local/share/sigil-spark/package-assets/70-sigil-remote-input.rules"196+ )197+ local -a managed_targets=(198+ "$install_root/current/assets/50-sigil-spark-audio.conf"199+ "$install_root/current/assets/sigil-host.service"200+ "$install_root/current/tools/rollback-bazzite-release.sh"201+ "$install_root/current/assets/70-sigil-remote-input.rules"202+ )203+ local index204+ local link_target205+ local resolved_link206+ local resolved_target207+208+ for index in "${!managed_links[@]}"; do209+ if [[ -L "${managed_links[$index]}" ]]; then210+ link_target="$(readlink "${managed_links[$index]}")"211+ resolved_link="$(readlink -f "${managed_links[$index]}" 2>/dev/null || true)"212+ resolved_target="$(readlink -f "${managed_targets[$index]}" 2>/dev/null || true)"213+ if [[ "$link_target" == "${managed_targets[$index]}" \214+ || ( -n "$resolved_link" && "$resolved_link" == "$resolved_target" ) ]]; then215+ die "package-managed activation detected at ${managed_links[$index]}; build the runtime package and use payload/stage-this-release.sh"216+ fi217+ fi218+ done219+}220+# END package-managed activation guard221+222+refuse_package_managed_activation109223 110224 for directory in "$HOME/.local" "$HOME/.local/libexec" "$install_root" "$releases_root"; do111225 if [[ -e "$directory" ]]; then
@@ -121,33 +235,41 @@ done
121235 cleanup() {122236 local exit_status=$?123237 trap - EXIT INT TERM HUP238+ restore_activation_links124239 if [[ -d "$staging_path" ]]; then125- rm -f -- "$staging_path/sigil-host" "$staging_path/sigil-probe"240+ rm -f -- "$staging_path/sigil" "$staging_path/sigil-host" "$staging_path/sigil-probe"126241 rmdir -- "$staging_path" 2>/dev/null || true127242 fi128- [[ ! -L "$next_link" ]] || rm -f -- "$next_link"129- [[ ! -L "$next_previous_link" ]] || rm -f -- "$next_previous_link"243+ [[ ! -L "$next_current" ]] || rm -f -- "$next_current"244+ [[ ! -L "$next_previous" ]] || rm -f -- "$next_previous"245+ [[ ! -L "$restore_current_temp" ]] || rm -f -- "$restore_current_temp"246+ [[ ! -L "$restore_previous_temp" ]] || rm -f -- "$restore_previous_temp"130247 exit "$exit_status"131248 }132249 trap cleanup EXIT INT TERM HUP133250 134251 if [[ -e "$release_path" || -L "$release_path" ]]; then135252 [[ -d "$release_path" && ! -L "$release_path" ]] || die "unsafe existing release path: $release_path"136- [[ "$(sha256sum "$release_path/sigil-host" | awk '{print $1}')" == "$host_sha256" ]] || die "existing release host hash differs"253+ [[ "$(sha256sum "$release_path/sigil" | awk '{print $1}')" == "$host_sha256" ]] || die "existing release host hash differs"254+ [[ "$(sha256sum "$release_path/sigil-host" | awk '{print $1}')" == "$host_sha256" ]] || die "existing release compatibility host hash differs"137255 [[ "$(sha256sum "$release_path/sigil-probe" | awk '{print $1}')" == "$probe_sha256" ]] || die "existing release probe hash differs"138256 else139257 install -d -m 0755 "$staging_path"258+ install -m 0755 "$host_binary" "$staging_path/sigil"140259 install -m 0755 "$host_binary" "$staging_path/sigil-host"141260 install -m 0755 "$probe_binary" "$staging_path/sigil-probe"142- [[ "$(sha256sum "$staging_path/sigil-host" | awk '{print $1}')" == "$host_sha256" ]] || die "installed host hash differs"261+ [[ "$(sha256sum "$staging_path/sigil" | awk '{print $1}')" == "$host_sha256" ]] || die "installed host hash differs"262+ [[ "$(sha256sum "$staging_path/sigil-host" | awk '{print $1}')" == "$host_sha256" ]] || die "installed compatibility host hash differs"143263 [[ "$(sha256sum "$staging_path/sigil-probe" | awk '{print $1}')" == "$probe_sha256" ]] || die "installed probe hash differs"144264 mv -- "$staging_path" "$release_path"145265 fi146266 147-ldd_output="$(ldd "$release_path/sigil-host")"267+cmp -s "$release_path/sigil" "$release_path/sigil-host" \268+ || die "sigil-host compatibility executable differs from sigil"269+ldd_output="$(ldd "$release_path/sigil")"148270 printf '%s\n' "$ldd_output"149271 grep -q 'not found' <<<"$ldd_output" && die "host binary has an unresolved shared library"150-timeout --signal=TERM --kill-after=2s 5s "$release_path/sigil-host" --version272+timeout --signal=TERM --kill-after=2s 5s "$release_path/sigil" --version151273 timeout --signal=TERM --kill-after=2s 5s "$release_path/sigil-probe" --help >/dev/null152274 153275 current_release_id=""
@@ -166,16 +288,16 @@ fi
166288 if [[ -e "$previous_link" || -L "$previous_link" ]]; then167289 [[ -L "$previous_link" ]] || die "previous activation is not a symlink"168290 fi291+snapshot_activation_links169292 170-ln -s "releases/$release_id" "$next_link"293+ln -s "releases/$release_id" "$next_current"171294 if [[ -n "$current_release_id" && "$current_release_id" != "$release_id" ]]; then172- ln -s "releases/$current_release_id" "$next_previous_link"173- mv -Tf -- "$next_previous_link" "$previous_link"295+ ln -s "releases/$current_release_id" "$next_previous"174296 fi175-mv -Tf -- "$next_link" "$current_link"297+activate_release_links176298 177299 printf 'release_id=%s\n' "$release_id"178-printf 'host_sha256=%s\n' "$(sha256sum "$release_path/sigil-host" | awk '{print $1}')"300+printf 'host_sha256=%s\n' "$(sha256sum "$release_path/sigil" | awk '{print $1}')"179301 printf 'probe_sha256=%s\n' "$(sha256sum "$release_path/sigil-probe" | awk '{print $1}')"180302 printf 'current=%s\n' "$(readlink -f "$current_link")"181303 if [[ -L "$previous_link" ]]; then
A scripts/tests/bazzite-host-executable-layout.sh +78 -0
@@ -0,0 +1,78 @@
1+#!/usr/bin/env bash2+3+set -euo pipefail4+5+script_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"6+repo_dir="$(CDPATH='' cd -- "$script_dir/../.." && pwd -P)"7+rollback="$repo_dir/scripts/rollback-bazzite-release.sh"8+temp_parent="${TMPDIR:-/tmp}"9+temp_parent="${temp_parent%/}"10+temp_root="$(mktemp -d "$temp_parent/sigil-host-layout.XXXXXX")"11+helper_script="$temp_root/release-host-executable.sh"12+13+cleanup() {14+ local status=$?15+ trap - EXIT INT TERM HUP16+ case "$temp_root" in17+ "$temp_parent"/sigil-host-layout.??????) rm -rf -- "$temp_root" ;;18+ esac19+ exit "$status"20+}21+trap cleanup EXIT INT TERM HUP22+23+sed -n \24+ '/^# BEGIN release host executable compatibility helper$/,/^# END release host executable compatibility helper$/p' \25+ "$rollback" | sed '1d;$d' >"$helper_script"26+grep -Fq 'release_host_executable()' "$helper_script"27+28+# shellcheck source=/dev/null29+source "$helper_script"30+31+make_executable() {32+ local path="$1"33+ local marker="$2"34+ printf '#!/usr/bin/env bash\n# %s\nexit 0\n' "$marker" >"$path"35+ chmod 0755 "$path"36+}37+38+legacy="$temp_root/legacy"39+install -d -m 0700 "$legacy"40+make_executable "$legacy/sigil-host" legacy41+[[ "$(release_host_executable "$legacy")" == "$legacy/sigil-host" ]]42+43+current="$temp_root/current"44+install -d -m 0700 "$current"45+make_executable "$current/sigil" current46+cp "$current/sigil" "$current/sigil-host"47+chmod 0755 "$current/sigil-host"48+[[ "$(release_host_executable "$current")" == "$current/sigil" ]]49+50+missing_alias="$temp_root/missing-alias"51+install -d -m 0700 "$missing_alias"52+make_executable "$missing_alias/sigil" current53+if release_host_executable "$missing_alias" >/dev/null; then54+ printf 'FAIL: new release without sigil-host compatibility copy was accepted\n' >&255+ exit 156+fi57+58+mismatched_alias="$temp_root/mismatched-alias"59+install -d -m 0700 "$mismatched_alias"60+make_executable "$mismatched_alias/sigil" current61+make_executable "$mismatched_alias/sigil-host" different62+if release_host_executable "$mismatched_alias" >/dev/null; then63+ printf 'FAIL: mismatched sigil-host compatibility copy was accepted\n' >&264+ exit 165+fi66+67+unsafe_primary="$temp_root/unsafe-primary"68+install -d -m 0700 "$unsafe_primary"69+make_executable "$unsafe_primary/elsewhere" current70+ln -s elsewhere "$unsafe_primary/sigil"71+cp "$unsafe_primary/elsewhere" "$unsafe_primary/sigil-host"72+chmod 0755 "$unsafe_primary/sigil-host"73+if release_host_executable "$unsafe_primary" >/dev/null; then74+ printf 'FAIL: symlink primary executable was accepted as a legacy layout\n' >&275+ exit 176+fi77+78+printf 'bazzite_host_executable_layout_tests=ok\n'
M scripts/tests/bazzite-inventory-cold-boot.sh +8 -8
@@ -71,8 +71,8 @@ assert_contains "$wrong_user_session_output" \
7171 7272 pass_journal='[0.500000] host systemd[1]: Reached target Basic System7373 [1.000000] host gamescope-session-plus[100]: starting Gamescope74-[2.000000] host systemd[1000]: Started sigil-host.service - Sigil Spark streaming host75-[3.000000] host sigil-host[200]: INFO sigil host ready74+[2.000000] host systemd[1000]: Started sigil-host.service - Sigil streaming host75+[3.000000] host sigil[200]: INFO sigil host ready7676 [4.000000] host sshd-session[300]: Accepted publickey for tank'7777 journalctl() {7878 printf '%s\n' "$pass_journal"
@@ -85,8 +85,8 @@ assert_not_contains "$pass_output" 'cold_boot_failure='
8585 assert_not_contains "$pass_output" 'cold_boot_evidence_insufficient='8686 8787 late_journal='[301.000000] host gamescope-session-plus[100]: starting Gamescope88-[302.000000] host systemd[1000]: Started sigil-host.service - Sigil Spark streaming host89-[303.000000] host sigil-host[200]: INFO sigil host ready88+[302.000000] host systemd[1000]: Started sigil-host.service - Sigil streaming host89+[303.000000] host sigil[200]: INFO sigil host ready9090 [304.000000] host sshd-session[300]: Accepted publickey for tank'9191 journalctl() {9292 printf '%s\n' "$late_journal"
@@ -98,8 +98,8 @@ assert_contains "$late_output" \
9898 bad_order_journal='[0.500000] host systemd[1]: Reached target Basic System9999 [1.000000] host sshd-session[300]: Accepted publickey for tank100100 [2.000000] host gamescope-session-plus[100]: starting Gamescope101-[3.000000] host systemd[1000]: Started sigil-host.service - Sigil Spark streaming host102-[4.000000] host sigil-host[200]: INFO sigil host ready'101+[3.000000] host systemd[1000]: Started sigil-host.service - Sigil streaming host102+[4.000000] host sigil[200]: INFO sigil host ready'103103 journalctl() {104104 printf '%s\n' "$bad_order_journal"105105 }
@@ -113,8 +113,8 @@ assert_contains "$bad_order_output" \
113113 114114 missing_ssh_journal='[0.500000] host systemd[1]: Reached target Basic System115115 [1.000000] host gamescope-session-plus[100]: starting Gamescope116-[2.000000] host systemd[1000]: Started sigil-host.service - Sigil Spark streaming host117-[3.000000] host sigil-host[200]: INFO sigil host ready'116+[2.000000] host systemd[1000]: Started sigil-host.service - Sigil streaming host117+[3.000000] host sigil[200]: INFO sigil host ready'118118 journalctl() {119119 printf '%s\n' "$missing_ssh_journal"120120 }
A scripts/tests/bazzite-release-activation.sh +195 -0
@@ -0,0 +1,195 @@
1+#!/usr/bin/env bash2+# The test sources production helpers dynamically, so ShellCheck cannot see3+# their references to the fixture overrides and shared state variables.4+# shellcheck disable=SC2034,SC23295+6+set -euo pipefail7+8+script_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"9+repo_dir="$(CDPATH='' cd -- "$script_dir/../.." && pwd -P)"10+temp_parent="${TMPDIR:-/tmp}"11+temp_parent="${temp_parent%/}"12+temp_root="$(mktemp -d "$temp_parent/sigil-release-activation.XXXXXX")"13+14+cleanup() {15+ local status=$?16+ trap - EXIT INT TERM HUP17+ case "$temp_root" in18+ "$temp_parent"/sigil-release-activation.??????) rm -rf -- "$temp_root" ;;19+ esac20+ exit "$status"21+}22+trap cleanup EXIT INT TERM HUP23+24+# Production targets GNU mv on Bazzite. Translate its atomic no-target-directory25+# form to the equivalent operation for this macOS-hosted shell test.26+mv() {27+ if [[ "${1:-}" == -Tf && "${2:-}" == -- && "$#" -eq 4 ]]; then28+ command mv -f -- "$3" "$4"29+ else30+ command mv "$@"31+ fi32+}33+34+extract_activation_helper() {35+ local source_path="$1"36+ local output_path="$2"37+ sed -n \38+ '/^# BEGIN release activation transaction helpers$/,/^# END release activation transaction helpers$/p' \39+ "$source_path" | sed '1d;$d' >"$output_path"40+ grep -Fq 'activate_release_links()' "$output_path"41+}42+43+install_helper="$temp_root/install-activation.sh"44+stage_helper="$temp_root/stage-activation.sh"45+rollback_helper="$temp_root/rollback-activation.sh"46+extract_activation_helper "$repo_dir/scripts/install-bazzite-package.sh" "$install_helper"47+extract_activation_helper "$repo_dir/scripts/stage-bazzite-release.sh" "$stage_helper"48+extract_activation_helper "$repo_dir/scripts/rollback-bazzite-release.sh" "$rollback_helper"49+cmp "$install_helper" "$stage_helper"50+cmp "$install_helper" "$rollback_helper"51+52+success_root="$temp_root/success"53+install -d -m 0700 "$success_root"54+(55+ current_link="$success_root/current"56+ previous_link="$success_root/previous"57+ next_current="$success_root/.next-current"58+ next_previous="$success_root/.next-previous"59+ ln -s releases/old "$current_link"60+ ln -s releases/older "$previous_link"61+ ln -s releases/new "$next_current"62+ ln -s releases/old "$next_previous"63+ # shellcheck source=/dev/null64+ source "$install_helper"65+ snapshot_activation_links66+ activate_release_links67+ [[ "$(readlink "$current_link")" == releases/new ]]68+ [[ "$(readlink "$previous_link")" == releases/old ]]69+ restore_activation_links70+ [[ "$(readlink "$current_link")" == releases/new ]]71+ [[ "$(readlink "$previous_link")" == releases/old ]]72+)73+74+reload_failure_root="$temp_root/reload-failure"75+install -d -m 0700 "$reload_failure_root"76+(77+ current_link="$reload_failure_root/current"78+ previous_link="$reload_failure_root/previous"79+ next_current="$reload_failure_root/.next-current"80+ next_previous="$reload_failure_root/.next-previous"81+ ln -s releases/old "$current_link"82+ ln -s releases/older "$previous_link"83+ ln -s releases/new "$next_current"84+ ln -s releases/old "$next_previous"85+ # shellcheck source=/dev/null86+ source "$install_helper"87+ reload_calls=088+ systemctl() {89+ reload_calls=$((reload_calls + 1))90+ [[ "$reload_calls" -gt 1 ]]91+ }92+ snapshot_activation_links93+ activation_reload_service=true94+ if activate_release_links; then95+ printf 'FAIL: activation unexpectedly survived daemon-reload failure\n' >&296+ exit 197+ fi98+ restore_activation_links99+ [[ "$(readlink "$current_link")" == releases/old ]]100+ [[ "$(readlink "$previous_link")" == releases/older ]]101+ [[ "$reload_calls" -eq 2 ]]102+)103+104+empty_root="$temp_root/empty"105+install -d -m 0700 "$empty_root"106+(107+ current_link="$empty_root/current"108+ previous_link="$empty_root/previous"109+ next_current="$empty_root/.next-current"110+ next_previous="$empty_root/.next-previous"111+ ln -s releases/new "$next_current"112+ # shellcheck source=/dev/null113+ source "$install_helper"114+ systemctl() { return 1; }115+ snapshot_activation_links116+ activation_reload_service=true117+ if activate_release_links; then118+ printf 'FAIL: first activation unexpectedly survived daemon-reload failure\n' >&2119+ exit 1120+ fi121+ restore_activation_links122+ [[ ! -e "$current_link" && ! -L "$current_link" ]]123+ [[ ! -e "$previous_link" && ! -L "$previous_link" ]]124+)125+126+guard_helper="$temp_root/package-managed-guard.sh"127+sed -n \128+ '/^# BEGIN package-managed activation guard$/,/^# END package-managed activation guard$/p' \129+ "$repo_dir/scripts/stage-bazzite-release.sh" | sed '1d;$d' >"$guard_helper"130+grep -Fq 'refuse_package_managed_activation()' "$guard_helper"131+132+guard_home="$temp_root/guard-home"133+install_root="$guard_home/.local/libexec/sigil-spark"134+service_link="$guard_home/.config/systemd/user/sigil-host.service"135+install -d -m 0700 "$(dirname -- "$service_link")"136+ln -s "$guard_home/unmanaged.service" "$service_link"137+(138+ HOME="$guard_home"139+ # shellcheck source=/dev/null140+ source "$guard_helper"141+ die() {142+ printf 'guard rejected: %s\n' "$*" >&2143+ exit 1144+ }145+ refuse_package_managed_activation146+)147+148+ln -sfn "$install_root/current/assets/sigil-host.service" "$service_link"149+guard_log="$temp_root/guard.log"150+if (151+ HOME="$guard_home"152+ # shellcheck source=/dev/null153+ source "$guard_helper"154+ die() {155+ printf 'guard rejected: %s\n' "$*" >&2156+ exit 1157+ }158+ refuse_package_managed_activation159+) >"$guard_log" 2>&1; then160+ printf 'FAIL: thin stager accepted a package-managed activation\n' >&2161+ exit 1162+fi163+grep -Fq 'build the runtime package and use payload/stage-this-release.sh' "$guard_log"164+165+canonical_root="$temp_root/canonical-guard"166+canonical_home="$canonical_root/var/home/tank"167+install -d -m 0700 "$canonical_home"168+ln -s var/home "$canonical_root/home"169+spelled_home="$canonical_root/home/tank"170+spelled_install_root="$spelled_home/.local/libexec/sigil-spark"171+canonical_service_target="$canonical_home/.local/libexec/sigil-spark/current/assets/sigil-host.service"172+canonical_service_link="$canonical_home/.config/systemd/user/sigil-host.service"173+install -d -m 0700 "$(dirname -- "$canonical_service_target")" \174+ "$(dirname -- "$canonical_service_link")"175+printf '[Service]\n' >"$canonical_service_target"176+ln -s "$canonical_service_target" "$canonical_service_link"177+canonical_guard_log="$temp_root/canonical-guard.log"178+if (179+ HOME="$spelled_home"180+ install_root="$spelled_install_root"181+ # shellcheck source=/dev/null182+ source "$guard_helper"183+ die() {184+ printf 'guard rejected: %s\n' "$*" >&2185+ exit 1186+ }187+ refuse_package_managed_activation188+) >"$canonical_guard_log" 2>&1; then189+ printf 'FAIL: thin stager missed a canonically equivalent managed link\n' >&2190+ exit 1191+fi192+grep -Fq 'build the runtime package and use payload/stage-this-release.sh' \193+ "$canonical_guard_log"194+195+printf 'bazzite_release_activation_tests=ok\n'
M scripts/tests/package-bazzite-provenance.sh +19 -1
@@ -19,7 +19,7 @@ cleanup() {
1919 }2020 trap cleanup EXIT INT TERM HUP2121 22-host_binary="$temp_root/sigil-host"22+host_binary="$temp_root/sigil"2323 probe_binary="$temp_root/sigil-probe"2424 printf '#!/usr/bin/env bash\nexit 0\n' >"$host_binary"2525 printf '#!/usr/bin/env bash\nexit 0\n' >"$probe_binary"
@@ -76,11 +76,29 @@ with tarfile.open(sys.argv[1], "r:gz") as archive:
7676 if manifest_file is None:7777 raise SystemExit("release manifest is missing")7878 manifest = json.load(manifest_file)79+ primary = archive.extractfile("payload/release/sigil")80+ compatibility = archive.extractfile("payload/release/sigil-host")81+ service = archive.extractfile("payload/release/assets/sigil-host.service")82+ if primary is None or compatibility is None:83+ raise SystemExit("primary or compatibility host executable is missing")84+ if service is None:85+ raise SystemExit("service asset is missing")86+ if primary.read() != compatibility.read():87+ raise SystemExit("sigil-host compatibility executable differs from sigil")88+ service_text = service.read().decode("utf-8")89+ if "current/sigil-host serve" not in service_text:90+ raise SystemExit("service does not retain the compatibility executable")91+ if "current/sigil serve" in service_text:92+ raise SystemExit("service bypasses the legacy compatibility executable")7993 8094 if manifest.get("binary_provenance") != "caller-supplied-unverified":8195 raise SystemExit("caller-supplied binary provenance is not marked unverified")8296 if manifest.get("binary_provenance_verified") is not False:8397 raise SystemExit("caller-supplied binary provenance must not be verified")98+if manifest.get("primary_executable") != "sigil":99+ raise SystemExit("primary executable is not sigil")100+if manifest.get("compatibility_executable") != "sigil-host":101+ raise SystemExit("compatibility executable is not sigil-host")84102 PY85103 86104 printf 'package_bazzite_provenance_tests=ok\n'
M scripts/verify-demo-build.sh +2 -2
M src-tauri/Cargo.toml +2 -2
M src-tauri/src/commands/network.rs +1 -1
M src-tauri/src/commands/state.rs +12 -14
@@ -279,19 +279,19 @@ where
279279 let mut args = args.into_iter().map(Into::into);280280 281281 // The first value is the executable path. Unknown values are left for the282- // platform/Tauri runtime, but Sigil-owned flags are parsed strictly.282+ // platform/Tauri runtime, but Portal-owned flags are parsed strictly.283283 let _ = args.next();284284 while let Some(arg) = args.next() {285285 if arg == "--daemon" {286286 return Err(287- "--daemon is not supported by the client application; run the separate sigil-host binary"287+ "--daemon is not supported by Portal; run the separate sigil executable"288288 .to_string(),289289 );290290 }291291 292292 let arg = arg293293 .into_string()294- .map_err(|_| "Sigil command-line arguments must be valid UTF-8".to_string())?;294+ .map_err(|_| "Portal command-line arguments must be valid UTF-8".to_string())?;295295 let node_id = if arg == "--dev-connect" {296296 Some(297297 args.next()
@@ -599,23 +599,22 @@ mod tests {
599599 #[test]600600 fn parses_debug_direct_node_flag() {601601 let node_id = test_node_id();602- let options =603- parse_launch_options(["sigil-spark", "--dev-connect", &node_id], true).unwrap();602+ let options = parse_launch_options(["portal", "--dev-connect", &node_id], true).unwrap();604603 605604 assert_eq!(options.dev_connect_node_id.unwrap().to_string(), node_id);606605 }607606 608607 #[test]609608 fn rejects_legacy_daemon_mode() {610- let error = parse_launch_options(["sigil-spark", "--daemon"], true).unwrap_err();611- assert!(error.contains("separate sigil-host binary"));609+ let error = parse_launch_options(["portal", "--daemon"], true).unwrap_err();610+ assert!(error.contains("separate sigil executable"));612611 }613612 614613 #[test]615614 fn parses_equals_form() {616615 let node_id = test_node_id();617616 let flag = format!("--dev-connect={node_id}");618- let options = parse_launch_options(["sigil-spark", &flag], true).unwrap();617+ let options = parse_launch_options(["portal", &flag], true).unwrap();619618 620619 assert_eq!(options.dev_connect_node_id.unwrap().to_string(), node_id);621620 }
@@ -623,8 +622,7 @@ mod tests {
623622 #[test]624623 fn rejects_direct_node_when_debug_mode_is_disabled() {625624 let node_id = test_node_id();626- let error =627- parse_launch_options(["sigil-spark", "--dev-connect", &node_id], false).unwrap_err();625+ let error = parse_launch_options(["portal", "--dev-connect", &node_id], false).unwrap_err();628626 629627 assert!(error.contains("debug build or the explicit demo-direct-node feature"));630628 }
@@ -632,7 +630,7 @@ mod tests {
632630 #[test]633631 fn app_state_accepts_direct_node_only_in_debug_builds() {634632 let node_id = test_node_id();635- let result = AppState::from_args(["sigil-spark", "--dev-connect", &node_id]);633+ let result = AppState::from_args(["portal", "--dev-connect", &node_id]);636634 637635 assert_eq!(result.is_ok(), development_direct_node_available());638636 }
@@ -649,12 +647,12 @@ mod tests {
649647 #[test]650648 fn rejects_missing_invalid_and_duplicate_node_ids() {651649 assert!(652- parse_launch_options(["sigil-spark", "--dev-connect"], true)650+ parse_launch_options(["portal", "--dev-connect"], true)653651 .unwrap_err()654652 .contains("requires an iroh node ID")655653 );656654 assert!(657- parse_launch_options(["sigil-spark", "--dev-connect", "not-a-node-id"], true)655+ parse_launch_options(["portal", "--dev-connect", "not-a-node-id"], true)658656 .unwrap_err()659657 .contains("Invalid iroh node ID")660658 );
@@ -663,7 +661,7 @@ mod tests {
663661 assert!(664662 parse_launch_options(665663 [666- "sigil-spark",664+ "portal",667665 "--dev-connect",668666 &node_id,669667 "--dev-connect",
M src-tauri/src/main.rs +2 -2
M src-tauri/tauri.conf.json +3 -3