cat / goq
clone

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

release/v0.1.0-pins
goq / docs / public-alpha-uat.md
public-alpha-uat.md 14.5K rendered
1# Public-alpha release-candidate UAT23Issue #6 is a hardware acceptance gate, not a unit-test checkbox. This guide4turns operator observations and diagnostics into a bounded evidence bundle for5one published Sigil asset set, one published Portal asset set, and one immutable6`vVERSION` tag. The harness validates supplied evidence; it does not run the7hardware exercise, infer observations, or claim that the gate passed.89Use the actual release candidates from a clean checkout of the exact tag. `HEAD`,10a branch name, a locally rebuilt binary, a different DMG, or a moving ref is not11accepted even if its source is equivalent. Initialization and verification must12run on macOS because the Portal DMG is assessed with the platform security tools.13They also require an authenticated GitHub CLI with network access to the public14`FelineStateMachine/goq` repository; downloaded or mirrored files cannot be used15as an offline substitute for the published release identity checks.1617## 1. Initialize the bundle1819Choose an absolute path on an encrypted operator-controlled volume. Do not put20the evidence bundle in the repository or on the shared host.2122```bash23uat_dir="$PWD-private/goq-uat-rc1"24./scripts/public-alpha-uat.sh init \25 --evidence-dir "$uat_dir" \26 --release-tag v0.1.0-rc.1 \27 --sigil-archive /absolute/path/sigil-v0.1.0-rc.1-linux-glibc2.17-x86_64.tar.gz \28 --sigil-bin /absolute/path/to/sigil \29 --portal-assets /absolute/path/to/portal-v0.1.0-rc.1-assets30```3132`init` creates a mode `0700` directory and an atomic, mode `0600`, tab-separated33`manifest.tsv`. It records the resolved 40-character tagged commit, release tag,34the hashes of the signed Sigil archive/checksum/signature/public key, the exact35Sigil payload hash, all three Portal asset hashes, creation time, and the36evidence freshness window. The default window is seven days;37`--max-age-seconds` can set 3600 through 2592000 seconds.3839Before creating the bundle the harness performs all of these fail-closed checks:4041- `refs/tags/vVERSION` resolves exactly to the clean checkout's `HEAD`.42- GitHub's `FelineStateMachine/goq` tag ref resolves to that same 40-character43 commit, including through an annotated-tag chain, and the release carrying44 that exact tag is published, non-draft, and marked as a prerelease.45- The published release contains exactly the expected six Sigil and Portal46 filenames. Every supplied file's size and SHA-256 must equal GitHub's uploaded47 asset metadata; a locally valid or identically named substitution is rejected.48- `scripts/verify-sigil-release.sh` validates the archive, checksum, detached49 Minisign signature, source commit, and product contents against the reviewed50 repository key at `release/sigil-minisign.pub`.51- The supplied installed/tested `sigil` executable is byte-for-byte identical52 to `payload/release/sigil` inside that verified archive.53- The Portal directory contains exactly `Portal-VERSION-arm64.dmg`, its54 `.dmg.sha256`, and `Portal-VERSION-arm64.json`.55- `scripts/verify-portal-release.py assets` validates those three assets against56 the same clean exact tag.57- `gh attestation verify` validates all three Portal files against the exact58 `FelineStateMachine/goq/.github/workflows/portal-release.yml` signer workflow,59 `refs/tags/vVERSION`, tagged source commit, and GitHub-hosted runner boundary.60- `scripts/verify-macos-portal-signature.sh --signing adhoc` validates the DMG,61 the mandatory arm64 ad-hoc signature, bundle identity/version, arm64-only62 executable, and containment boundary. It also proves the negative: no63 certificate authority chain, no Apple TeamIdentifier, and no notarization64 ticket. Portal is not notarized, so there is no Gatekeeper result to record.6566If the reviewed Sigil key is still the `unconfigured` sentinel, Minisign or67macOS verification tools are unavailable, the GitHub API or attestations cannot68be verified, the macOS verifier is absent, or any verification cannot run,69initialization fails. There is no70candidate, offline, or test bypass in the UAT command.7172The bundle format is intentionally append-only at the command boundary. To73replace a mistaken record, initialize a new bundle. Do not edit the manifest or74normalized records.7576## 2. Evidence envelope7778Every input is a mode `0600` regular file at an absolute path. It begins with79these fields, copied from `manifest.tsv` where applicable:8081```text82uat_schema=goq-public-alpha-evidence-v383evidence_kind=controller84observed_at_unix=176000000085git_commit=0123456789abcdef0123456789abcdef0123456786release_tag=v0.1.0-rc.187sigil_sha256=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef88portal_sha256=abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678989```9091Use a current `date +%s` taken when that exercise finishes. An input is rejected92when a required field is missing, duplicated, stale, future-dated, or bound to93anything other than the initialized release candidates.9495Do not include node IDs, host/peer IDs, invitations, secrets, private keys, or96identity seeds. The harness rejects recognizable secret-bearing fields before97normalizing the allowlisted keys, caps each source at 1 MiB, and rejects98symlinks and group/other-writable inputs.99100Record an observation only after its exercise has genuinely completed:101102```bash103chmod 600 /absolute/path/to/controller.evidence104./scripts/public-alpha-uat.sh record \105 --evidence-dir "$uat_dir" \106 --kind controller \107 --file /absolute/path/to/controller.evidence108```109110Supported kinds are `cold-boot`, `controller`, `mouse`, `soak`,111`network-direct`, `network-relay`, `reconnect`, `multi-viewer`,112`second-client`, and the optional `loopback-preflight`. `second-client` is the113legacy-exclusive check; native-MoQ v2 must admit the bounded three-viewer114contract instead of rejecting viewer two.115116## 3. Required hardware exercises117118Append the listed kind-specific fields after the common envelope. `pass` means119the operator actually observed that result with the initialized release120candidates.121122### Physically headless cold boot123124Disconnect every display, remove any interactive SSH session, cold boot the125Bazzite host, and wait before making the first SSH connection. Run the existing126read-only inventory check with `--cold-boot`. Its output may be used directly127after the common envelope; unrecognized inventory fields are omitted during128normalization.129130```bash131ssh tank@umpc 'cd /path/to/goq && ./scripts/bazzite-inventory.sh --cold-boot' \132 >> /absolute/path/to/cold-boot.evidence133```134135The evidence must contain:136137```text138cold_boot_result=pass139cold_boot_failure_count=0140cold_boot_insufficient_count=0141headless_connector_state=ok142gaming_autologin_session=ok143sigil_host_enabled=enabled144sigil_host_active=active145gamescope_pipewire_node=ok146gamescope_before_first_ssh=ok147sigil_unit_before_first_ssh=ok148sigil_ready_before_first_ssh=ok149```150151`gaming_autologin_session=ok` means logind reported an active, local session152for the gaming user whose exact PAM service name ends in `-autologin`, such as153`sddm-autologin` or `gdm-autologin`; an SSH or manual desktop login does not154qualify.155156This proves Gamescope and Sigil became ready before first SSH, not merely that157they were running after an operator repaired the boot.158159### Physical controller and neutral release160161Attach a physical controller to the Portal client and control an actual game162for at least five minutes. Exercise every named control class. Disconnect or163end the session while controls are active and observe the host returning both164buttons and axes to neutral.165166```text167physical_controller_attached_to_portal=pass168actual_game_controlled=pass169controller_coverage=abxy,dpad,sticks,triggers,shoulders,start-back170neutral_release_on_disconnect=pass171neutral_buttons=pass172neutral_axes=pass173session_seconds=300174```175176### Mouse buttons in Gamescope/an actual game177178Pointer motion or hover changes are insufficient. In a bounded target slug,179observe at least five attempts and confirm both buttons are consumed by the180Gamescope target application or actual game:181182```text183target_application=actual-game184left_click_consumed=pass185right_click_consumed=pass186consumption_observed_in_target=pass187click_attempts=10188```189190### Sustained A/V and resource soak191192Run an uninterrupted session for at least 3600 seconds and collect at least 60193samples. Report measured percentiles, not estimates:194195```text196duration_seconds=3600197samples=60198capture_fps_p50=59.8199presentation_fps_p50=59.2200frame_interval_p95_ms=20201hitch_p99_ms=40202video_queue_p95_frames=1203decode_queue_p95_frames=1204audio_queue_p95_ms=50205av_skew_p95_ms=30206max_queue_age_p95_ms=45207cpu_p95_percent=50208gpu_p95_percent=70209rss_p95_mib=512210transport_drops=2211frontend_drops=3212audio_drops=0213latency_first_window_p95_ms=30214latency_last_window_p95_ms=33215disconnects=0216```217218The fail-closed bounds are: capture and presentation p50 at least 55 fps;219frame interval p95 at most 25 ms; hitch p99 at most 50 ms; video and decoder220queues p95 at most two frames; audio queue and maximum queue age p95 at most221100 ms; A/V skew p95 at most 50 ms; CPU p95 at most 90%; GPU p95 at most 95%;222RSS p95 at most 2048 MiB; and zero disconnects. Last-window latency p95 must be223no more than 120% of the first window and may grow by no more than 5 ms. Drop224counts remain visible evidence rather than being silently discarded.225226### Direct and difficult-NAT relay paths227228These are separate records and separate sessions of at least ten minutes. Do229not relabel an ordinary direct connection as a relay exercise.230231`network-direct`:232233```text234path_mode=direct235nat_scenario=ordinary236session_seconds=600237rtt_p50_ms=10238rtt_p95_ms=20239input_ack_p95_ms=30240presentation_latency_p95_ms=60241packet_loss_percent=0.1242```243244`network-relay` uses the same timing fields with:245246```text247path_mode=relay248nat_scenario=difficult249```250251Both records reject packet loss over 5%. The relay run must be forced or252observed under the documented difficult-NAT setup and confirmed as relayed by253Portal diagnostics.254255### Reconnect and multi-viewer admission256257Perform at least ten reconnects with the exact candidates. Each must restore258the intended session state and resume from a keyframe:259260```text261reconnect_cycles=10262reconnect_successes=10263reconnect_failures=0264state_preserved=pass265keyframe_recovery_p95_ms=900266```267268Keyframe recovery p95 must not exceed 2000 ms.269270Connect three exact-candidate Portals concurrently over native MoQ control v2.271Exercise controller focus transfer, one constrained viewer, same-peer272replacement, and live grant revocation while collecting shared-resource and273cleanup evidence:274275```text276protocol_mode=native-moq-v2277configured_viewer_capacity=3278concurrent_viewers=3279shared_media_generation=pass280shared_video_sources=1281shared_video_encoders=1282shared_audio_sources=1283shared_audio_encoders=1284shared_publishers=1285shared_adaptive_actuators=1286viewer_progress=pass287slot_0_holders_max=1288focus_handoff_p95_ms=250289slow_viewer_isolation=pass290same_peer_replacement=pass291live_view_revocation=pass292survivors_uninterrupted=pass293final_cleanup=pass294ordinary_service_restored=pass295```296297The configured capacity must be 3 through 8, at least three viewers must be298simultaneous, and focus handoff p95 must not exceed 1000 ms. Shared resource299counts and the maximum slot-0 holder count must each be exactly one. Revoking300one viewer or detaching a constrained viewer must not change survivor media301generation or interrupt their A/V/input diagnostics.302303Run a separate legacy-exclusive session. While its authorized primary remains304playable, attempt a second legacy client at least three times:305306```text307protocol_mode=legacy-exclusive-v1308second_client_attempts=3309second_client_rejections=3310authorized_primary_uninterrupted=pass311rejection_reason=legacy-active-client312```313314## 4. Optional loopback preflight315316The existing loopback proof is useful preflight evidence, but it does not317replace any physical gate. Run it from the exact committed tree using release318mode. Its output includes an operationally sensitive `node_id`, so remove that319line before ingestion and add the common envelope:320321```bash322./scripts/loopback-proof.sh --profile release --control-v2 --viewers 3 \323 --primary-frames 600 --reconnect-cycles 3 \324 | sed '/^node_id=/d' \325 >> /absolute/path/to/loopback-preflight.evidence326chmod 600 /absolute/path/to/loopback-preflight.evidence327```328329The normalized record requires:330331```text332loopback_proof=ok333profile=release334host_sha256=<same value as sigil_sha256>335control_v2=ok336viewers=3337reconnect_cycles=3338same_peer_replacement=ok339shared_generation=ok340slot_0_single_holder=ok341bounded_queues=ok342final_cleanup=ok343```344345Slot-0 handoff has its own proof. It is the only preflight that exercises the346transfer itself, so run it alongside the record above when the physical gate347will measure handoff latency:348349```bash350./scripts/loopback-proof.sh --profile release --control-v2 --viewers 3 \351 --focus-handoffs 3 --assert-neutral-before-successor \352 --primary-frames 600 --reconnect-cycles 3 \353 | sed '/^node_id=/d' \354 >> /absolute/path/to/loopback-preflight.evidence355```356357Its normalized record requires:358359```text360loopback_proof=ok361focus_handoffs=3362slot_0_single_holder=ok363neutral_before_successor=ok364final_cleanup=ok365```366367`neutral_before_successor=ok` proves ordering only, not latency. The p95368handoff budget above is still a physical-gate measurement.369370The harness ingests the output by value and never mutates the Bazzite host.371372## 5. Verify and retain373374Verification requires the exact same signed asset sets and tested Sigil375executable again. It reruns every release, Minisign, and macOS platform check;376it also reruns the live GitHub release/tag/digest and Portal provenance checks.377Stored hashes alone are not sufficient:378379```bash380./scripts/public-alpha-uat.sh verify \381 --evidence-dir "$uat_dir" \382 --sigil-archive /absolute/path/sigil-v0.1.0-rc.1-linux-glibc2.17-x86_64.tar.gz \383 --sigil-bin /absolute/path/to/sigil \384 --portal-assets /absolute/path/to/portal-v0.1.0-rc.1-assets385```386387Success prints `public_alpha_uat=pass` plus the release tag, commit, artifact hashes, and388required gate count. Verification rejects missing records, expired timestamps,389changed tags, wrong artifacts, modified normalized evidence, unsafe modes,390symlinks, unpublished or non-prerelease releases, mismatched remote asset391digests, invalid Portal attestations, and unexpected bundle files.392393That output means the supplied bundle satisfies the machine-checkable contract.394It is not evidence that an exercise occurred unless the inputs came from the395real operator run. Preserve the verified directory read-only as release396evidence, separately from publish credentials and product artifacts.397398Run the fixture suite before changing the contract:399400```bash401shellcheck scripts/public-alpha-uat.sh scripts/tests/public-alpha-uat.sh402./scripts/tests/public-alpha-uat.sh403```