cat / goq
clone

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

fix/release-build-and-order
goq / scripts / tests / public-alpha-uat.sh
public-alpha-uat.sh 22.7K executable
1#!/usr/bin/env bash23set -euo pipefail45source_repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"6production_harness="$source_repo/scripts/public-alpha-uat.sh"7temporary_root="$(mktemp -d)"8trap 'rm -rf "$temporary_root"' EXIT910passes=011pass() {12 passes=$((passes + 1))13 printf 'ok %d - %s\n' "$passes" "$1"14}1516expect_failure() {17 local description="$1"18 shift19 if "$@" > /dev/null 2>&1; then20 printf 'not ok - %s unexpectedly succeeded\n' "$description" >&221 exit 122 fi23 pass "$description"24}2526fixture_repo="$temporary_root/repo"27mkdir -p "$fixture_repo/scripts" "$fixture_repo/release"28fixture_bin="$temporary_root/bin"29mkdir -m 700 "$fixture_bin"30# Exercise the Darwin-only production branch with fixture verifiers on any CI31# platform. The production harness itself has no test switch.32# shellcheck disable=SC201633printf '%s\n' '#!/usr/bin/env bash' 'if [[ "${1:-}" == -s ]]; then printf "Darwin\n"; else exec /usr/bin/uname "$@"; fi' \34 > "$fixture_bin/uname"35chmod 755 "$fixture_bin/uname"36# The generated GitHub CLI fixture exposes only the exact API and attestation37# operations used by the production harness.38# shellcheck disable=SC201639printf '%s\n' \40 '#!/usr/bin/env bash' \41 'set -euo pipefail' \42 'if [[ "${1:-}" == api && $# == 2 ]]; then' \43 ' case "$2" in' \44 ' /repos/FelineStateMachine/goq/releases/tags/*) cat "$UAT_GITHUB_RELEASE_JSON" ;;' \45 ' /repos/FelineStateMachine/goq/git/ref/tags/*) cat "$UAT_GITHUB_REF_JSON" ;;' \46 ' *) exit 1 ;;' \47 ' esac' \48 'elif [[ "${1:-}" == attestation && "${2:-}" == verify ]]; then' \49 ' if [[ -e "$UAT_ATTESTATION_LOG.fail" ]]; then exit 1; fi' \50 ' [[ -f "$3" ]]' \51 ' [[ " $* " == *" --repo FelineStateMachine/goq "* ]]' \52 ' [[ " $* " == *" --signer-workflow FelineStateMachine/goq/.github/workflows/portal-release.yml "* ]]' \53 ' [[ " $* " == *" --source-ref refs/tags/v1.2.3 "* ]]' \54 ' [[ " $* " == *" --source-digest $UAT_GITHUB_COMMIT "* ]]' \55 ' [[ " $* " == *" --deny-self-hosted-runners "* ]]' \56 ' printf "%s\n" "$3" >> "$UAT_ATTESTATION_LOG"' \57 'else' \58 ' exit 1' \59 'fi' \60 > "$fixture_bin/gh"61chmod 755 "$fixture_bin/gh"62export PATH="$fixture_bin:$PATH"63cp "$production_harness" "$fixture_repo/scripts/public-alpha-uat.sh"64chmod 755 "$fixture_repo/scripts/public-alpha-uat.sh"65printf '%s\n' 'untrusted comment: fixture key' 'RWRmaXh0dXJlUHVibGljS2V5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=' \66 > "$fixture_repo/release/sigil-minisign.pub"67chmod 644 "$fixture_repo/release/sigil-minisign.pub"6869# The single-quoted fixture lines intentionally defer expansion to the generated verifier.70# shellcheck disable=SC201671printf '%s\n' \72 '#!/usr/bin/env bash' \73 'set -euo pipefail' \74 'tag=""; archive=""; commit=""; key=""' \75 'while (($#)); do' \76 ' case "$1" in' \77 ' --tag) tag="$2"; shift 2 ;;' \78 ' --archive) archive="$2"; shift 2 ;;' \79 ' --source-commit) commit="$2"; shift 2 ;;' \80 ' --public-key-file) key="$2"; shift 2 ;;' \81 ' *) exit 1 ;;' \82 ' esac' \83 'done' \84 '[[ "$tag" == v1.2.3 ]]' \85 '[[ "$(basename -- "$archive")" == sigil-v1.2.3-linux-glibc2.17-x86_64.tar.gz ]]' \86 '[[ "$commit" =~ ^[0-9a-f]{40}$ ]]' \87 '[[ -s "$archive.sha256" && "$(<"$archive.minisig")" == fixture-signature ]]' \88 'grep -q fixture "$key"' \89 > "$fixture_repo/scripts/verify-sigil-release.sh"90chmod 755 "$fixture_repo/scripts/verify-sigil-release.sh"9192printf '%s\n' \93 '#!/usr/bin/env python3' \94 'import pathlib' \95 'import sys' \96 'args = sys.argv[1:]' \97 'def value(name):' \98 ' return args[args.index(name) + 1]' \99 'if not args or args[0] != "assets": raise SystemExit(1)' \100 'if value("--release-tag") != "v1.2.3": raise SystemExit(1)' \101 'asset_dir = pathlib.Path(value("--asset-dir"))' \102 'dmg = asset_dir / "Portal-1.2.3-arm64.dmg"' \103 'expected = {"Portal-1.2.3-arm64.dmg", "Portal-1.2.3-arm64.dmg.sha256", "Portal-1.2.3-arm64.json"}' \104 'if {entry.name for entry in asset_dir.iterdir()} != expected: raise SystemExit(1)' \105 'if dmg.read_text() != "verified portal dmg fixture\n": raise SystemExit(1)' \106 > "$fixture_repo/scripts/verify-portal-release.py"107chmod 755 "$fixture_repo/scripts/verify-portal-release.py"108109# The single-quoted fixture lines intentionally defer expansion to the generated verifier.110# shellcheck disable=SC2016111printf '%s\n' \112 '#!/usr/bin/env bash' \113 'set -euo pipefail' \114 'if [[ -n "${UAT_SIGNATURE_MARKER:-}" && -e "$UAT_SIGNATURE_MARKER.fail" ]]; then exit 1; fi' \115 '[[ "$1" == --dmg && "$3" == --expected-version && "$4" == 1.2.3 ]]' \116 '[[ "$(<"$2")" == "verified portal dmg fixture" ]]' \117 'if [[ -n "${UAT_SIGNATURE_MARKER:-}" ]]; then printf invoked > "$UAT_SIGNATURE_MARKER"; fi' \118 '[[ "$5" == --signing && "$6" == adhoc ]]' \119 'printf "portal_signature_verification=ok\\n"' \120 > "$fixture_repo/scripts/verify-macos-portal-signature.sh"121chmod 755 "$fixture_repo/scripts/verify-macos-portal-signature.sh"122123release_tag=v1.2.3124sigil_bin="$temporary_root/sigil"125wrong_sigil_bin="$temporary_root/wrong-sigil"126printf '%s\n' 'exact Sigil release candidate' > "$sigil_bin"127printf '%s\n' 'arbitrary executable' > "$wrong_sigil_bin"128chmod 600 "$sigil_bin" "$wrong_sigil_bin"129130archive_dir="$temporary_root/sigil-assets"131payload_root="$temporary_root/payload-root"132mkdir -p "$archive_dir" "$payload_root/payload/release"133cp "$sigil_bin" "$payload_root/payload/release/sigil"134sigil_archive="$archive_dir/sigil-v1.2.3-linux-glibc2.17-x86_64.tar.gz"135tar -czf "$sigil_archive" -C "$payload_root" payload136printf '%s\n' 'fixture checksum' > "$sigil_archive.sha256"137printf '%s\n' 'fixture-signature' > "$sigil_archive.minisig"138chmod 600 "$sigil_archive" "$sigil_archive.sha256" "$sigil_archive.minisig"139140portal_assets="$temporary_root/portal-assets"141mkdir -m 700 "$portal_assets"142printf '%s\n' 'verified portal dmg fixture' > "$portal_assets/Portal-1.2.3-arm64.dmg"143printf '%s\n' 'fixture checksum' > "$portal_assets/Portal-1.2.3-arm64.dmg.sha256"144printf '%s\n' '{}' > "$portal_assets/Portal-1.2.3-arm64.json"145chmod 600 "$portal_assets"/*146147git -C "$fixture_repo" init -q148git -C "$fixture_repo" config user.name 'UAT Fixture'149git -C "$fixture_repo" config user.email 'uat-fixture@example.invalid'150git -C "$fixture_repo" add scripts release151git -C "$fixture_repo" commit -qm 'fixture release verifiers'152git -C "$fixture_repo" tag "$release_tag"153154export UAT_GITHUB_COMMIT155UAT_GITHUB_COMMIT="$(git -C "$fixture_repo" rev-parse "$release_tag^{commit}")"156export UAT_GITHUB_RELEASE_JSON="$temporary_root/github-release.json"157export UAT_GITHUB_REF_JSON="$temporary_root/github-ref.json"158export UAT_ATTESTATION_LOG="$temporary_root/github-attestations.log"159160write_remote_ref_fixture() {161 local commit="${1:-$UAT_GITHUB_COMMIT}"162 printf '{"object":{"type":"commit","sha":"%s"}}\n' "$commit" > "$UAT_GITHUB_REF_JSON"163}164165write_remote_release_fixture() {166 local draft="${1:-false}"167 local prerelease="${2:-true}"168 local tag="${3:-$release_tag}"169 PYTHONDONTWRITEBYTECODE=1 python3 - \170 "$UAT_GITHUB_RELEASE_JSON" "$draft" "$prerelease" "$tag" \171 "$sigil_archive" "$sigil_archive.sha256" "$sigil_archive.minisig" \172 "$portal_assets/Portal-1.2.3-arm64.dmg" \173 "$portal_assets/Portal-1.2.3-arm64.dmg.sha256" \174 "$portal_assets/Portal-1.2.3-arm64.json" <<'PY'175import hashlib176import json177import pathlib178import sys179180output = pathlib.Path(sys.argv[1])181draft = sys.argv[2] == "true"182prerelease = sys.argv[3] == "true"183tag = sys.argv[4]184paths = [pathlib.Path(value) for value in sys.argv[5:]]185assets = []186for index, path in enumerate(paths, start=101):187 content = path.read_bytes()188 assets.append({189 "id": index,190 "name": path.name,191 "state": "uploaded",192 "size": len(content),193 "digest": "sha256:" + hashlib.sha256(content).hexdigest(),194 "url": f"https://api.github.com/repos/FelineStateMachine/goq/releases/assets/{index}",195 })196output.write_text(json.dumps({197 "id": 42,198 "tag_name": tag,199 "draft": draft,200 "prerelease": prerelease,201 "published_at": "2026-07-21T12:00:00Z",202 "html_url": f"https://github.com/FelineStateMachine/goq/releases/tag/{tag}",203 "assets": assets,204}))205PY206}207208write_remote_ref_fixture209write_remote_release_fixture210211harness="$fixture_repo/scripts/public-alpha-uat.sh"212signature_marker="$temporary_root/signature-verifier-invoked"213export UAT_SIGNATURE_MARKER="$signature_marker"214215manifest_value() {216 local bundle="$1"217 local key="$2"218 awk -F '\t' -v wanted="$key" '$1 == wanted { print $2 }' "$bundle/manifest.tsv"219}220221new_bundle() {222 local name="$1"223 local max_age="${2:-604800}"224 local bundle="$temporary_root/$name"225 "$harness" init \226 --evidence-dir "$bundle" \227 --release-tag "$release_tag" \228 --sigil-archive "$sigil_archive" \229 --sigil-bin "$sigil_bin" \230 --portal-assets "$portal_assets" \231 --max-age-seconds "$max_age" > /dev/null232 printf '%s\n' "$bundle"233}234235write_common() {236 local bundle="$1"237 local kind="$2"238 local observed="$3"239 local output="$4"240 {241 printf 'uat_schema=goq-public-alpha-evidence-v3\n'242 printf 'evidence_kind=%s\n' "$kind"243 printf 'observed_at_unix=%s\n' "$observed"244 printf 'git_commit=%s\n' "$(manifest_value "$bundle" git_commit)"245 printf 'release_tag=%s\n' "$(manifest_value "$bundle" release_tag)"246 printf 'sigil_sha256=%s\n' "$(manifest_value "$bundle" sigil_sha256)"247 printf 'portal_sha256=%s\n' "$(manifest_value "$bundle" portal_sha256)"248 } > "$output"249}250251write_fixture() {252 local bundle="$1"253 local kind="$2"254 local output="$3"255 local observed="${4:-$(date +%s)}"256 local hitch_p99="${5:-40}"257 write_common "$bundle" "$kind" "$observed" "$output"258 case "$kind" in259 cold-boot)260 printf '%s\n' 'cold_boot_result=pass' 'cold_boot_failure_count=0' \261 'cold_boot_insufficient_count=0' 'headless_connector_state=ok' \262 'gaming_autologin_session=ok' 'sigil_host_enabled=enabled' \263 'sigil_host_active=active' 'gamescope_pipewire_node=ok' \264 'gamescope_before_first_ssh=ok' 'sigil_unit_before_first_ssh=ok' \265 'sigil_ready_before_first_ssh=ok' >> "$output"266 ;;267 controller)268 printf '%s\n' 'physical_controller_attached_to_portal=pass' \269 'actual_game_controlled=pass' \270 'controller_coverage=abxy,dpad,sticks,triggers,shoulders,start-back' \271 'neutral_release_on_disconnect=pass' 'neutral_buttons=pass' \272 'neutral_axes=pass' 'session_seconds=600' >> "$output"273 ;;274 mouse)275 printf '%s\n' 'target_application=actual-game' 'left_click_consumed=pass' \276 'right_click_consumed=pass' 'consumption_observed_in_target=pass' \277 'click_attempts=10' >> "$output"278 ;;279 soak)280 printf '%s\n' 'duration_seconds=7200' 'samples=120' 'capture_fps_p50=59.8' \281 'presentation_fps_p50=59.2' 'frame_interval_p95_ms=20' \282 "hitch_p99_ms=$hitch_p99" 'video_queue_p95_frames=1' \283 'decode_queue_p95_frames=1' 'audio_queue_p95_ms=50' 'av_skew_p95_ms=30' \284 'max_queue_age_p95_ms=45' 'cpu_p95_percent=50' 'gpu_p95_percent=70' \285 'rss_p95_mib=512' 'transport_drops=2' 'frontend_drops=3' 'audio_drops=0' \286 'latency_first_window_p95_ms=30' 'latency_last_window_p95_ms=33' \287 'disconnects=0' >> "$output"288 ;;289 network-direct)290 printf '%s\n' 'path_mode=direct' 'nat_scenario=ordinary' 'session_seconds=900' \291 'rtt_p50_ms=10' 'rtt_p95_ms=20' 'input_ack_p95_ms=30' \292 'presentation_latency_p95_ms=60' 'packet_loss_percent=0.1' >> "$output"293 ;;294 network-relay)295 printf '%s\n' 'path_mode=relay' 'nat_scenario=difficult' 'session_seconds=900' \296 'rtt_p50_ms=40' 'rtt_p95_ms=80' 'input_ack_p95_ms=90' \297 'presentation_latency_p95_ms=120' 'packet_loss_percent=1.0' >> "$output"298 ;;299 reconnect)300 printf '%s\n' 'reconnect_cycles=10' 'reconnect_successes=10' \301 'reconnect_failures=0' 'state_preserved=pass' \302 'keyframe_recovery_p95_ms=900' >> "$output"303 ;;304 second-client)305 printf '%s\n' 'protocol_mode=legacy-exclusive-v1' 'second_client_attempts=3' \306 'second_client_rejections=3' 'authorized_primary_uninterrupted=pass' \307 'rejection_reason=legacy-active-client' >> "$output"308 ;;309 multi-viewer)310 printf '%s\n' 'protocol_mode=native-moq-v2' 'configured_viewer_capacity=3' \311 'concurrent_viewers=3' 'shared_media_generation=pass' \312 'shared_video_sources=1' 'shared_video_encoders=1' 'shared_audio_sources=1' \313 'shared_audio_encoders=1' 'shared_publishers=1' 'shared_adaptive_actuators=1' \314 'viewer_progress=pass' 'slot_0_holders_max=1' 'focus_handoff_p95_ms=250' \315 'slow_viewer_isolation=pass' 'same_peer_replacement=pass' \316 'live_view_revocation=pass' 'survivors_uninterrupted=pass' 'final_cleanup=pass' \317 'ordinary_service_restored=pass' >> "$output"318 ;;319 loopback-preflight)320 printf '%s\n' 'loopback_proof=ok' 'profile=release' \321 "host_sha256=$(manifest_value "$bundle" sigil_sha256)" \322 'control_v2=ok' 'viewers=3' 'reconnect_cycles=3' 'same_peer_replacement=ok' \323 'shared_generation=ok' 'slot_0_single_holder=ok' 'bounded_queues=ok' \324 'final_cleanup=ok' >> "$output"325 ;;326 *) printf 'unknown fixture kind: %s\n' "$kind" >&2; exit 1 ;;327 esac328 chmod 600 "$output"329}330331record_all_required() {332 local bundle="$1"333 local kind334 local evidence335 for kind in cold-boot controller mouse soak network-direct network-relay reconnect multi-viewer second-client; do336 evidence="$temporary_root/$kind-$RANDOM.evidence"337 write_fixture "$bundle" "$kind" "$evidence"338 "$harness" record --evidence-dir "$bundle" --kind "$kind" --file "$evidence" > /dev/null339 done340}341342verify_bundle() {343 "$harness" verify --evidence-dir "$1" --sigil-archive "$sigil_archive" \344 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"345}346347complete_bundle="$(new_bundle complete)"348[[ -f "$signature_marker" ]] || { printf 'macOS signature verifier was not invoked\n' >&2; exit 1; }349[[ "$(wc -l < "$UAT_ATTESTATION_LOG" | tr -d ' ')" == 3 ]] \350 || { printf 'all three Portal attestations were not verified\n' >&2; exit 1; }351record_all_required "$complete_bundle"352loopback_source="$temporary_root/loopback.evidence"353write_fixture "$complete_bundle" loopback-preflight "$loopback_source"354"$harness" record --evidence-dir "$complete_bundle" --kind loopback-preflight \355 --file "$loopback_source" > /dev/null356verify_bundle "$complete_bundle" | grep '^public_alpha_uat=pass$' > /dev/null357pass 'complete bundle verifies only after all release verifiers run'358359write_remote_ref_fixture 0000000000000000000000000000000000000000360expect_failure 'a published tag resolving to a different commit is rejected' \361 "$harness" init --evidence-dir "$temporary_root/wrong-remote-commit" \362 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \363 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"364write_remote_ref_fixture365366write_remote_release_fixture false true v1.2.4367expect_failure 'a GitHub release carrying a different tag is rejected' \368 "$harness" init --evidence-dir "$temporary_root/wrong-remote-tag" \369 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \370 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"371write_remote_release_fixture372373touch "$UAT_ATTESTATION_LOG.fail"374expect_failure 'missing or invalid Portal build provenance is rejected' \375 "$harness" init --evidence-dir "$temporary_root/attestation-failure" \376 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \377 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"378rm "$UAT_ATTESTATION_LOG.fail"379380write_remote_release_fixture true true381expect_failure 'a draft GitHub release is rejected' \382 "$harness" init --evidence-dir "$temporary_root/remote-draft" \383 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \384 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"385write_remote_release_fixture false false386expect_failure 'a published non-prerelease GitHub release is rejected' \387 "$harness" init --evidence-dir "$temporary_root/remote-not-prerelease" \388 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \389 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"390write_remote_release_fixture391392expect_failure 'verify rejects an executable different from the signed Sigil payload' \393 "$harness" verify --evidence-dir "$complete_bundle" --sigil-archive "$sigil_archive" \394 --sigil-bin "$wrong_sigil_bin" --portal-assets "$portal_assets"395396printf failed > "$signature_marker.fail"397expect_failure 'UAT fails closed when macOS platform verification cannot run' \398 "$harness" init \399 --evidence-dir "$temporary_root/signature-failure" --release-tag "$release_tag" \400 --sigil-archive "$sigil_archive" --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"401rm "$signature_marker.fail"402403expect_failure 'HEAD is rejected in place of an immutable release tag' \404 "$harness" init --evidence-dir "$temporary_root/head" --release-tag HEAD \405 --sigil-archive "$sigil_archive" --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"406407printf '%s\n' dirty > "$fixture_repo/untracked-release-residue"408expect_failure 'a dirty exact-tag worktree is rejected' \409 "$harness" init --evidence-dir "$temporary_root/dirty-tag" --release-tag "$release_tag" \410 --sigil-archive "$sigil_archive" --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"411rm "$fixture_repo/untracked-release-residue"412413arbitrary_dir="$temporary_root/arbitrary-sigil"414mkdir "$arbitrary_dir"415arbitrary_archive="$arbitrary_dir/sigil-v1.2.3-linux-glibc2.17-x86_64.tar.gz"416printf '%s\n' 'arbitrary text archive' > "$arbitrary_archive"417printf '%s\n' 'fixture checksum' > "$arbitrary_archive.sha256"418printf '%s\n' 'fixture-signature' > "$arbitrary_archive.minisig"419chmod 600 "$arbitrary_archive" "$arbitrary_archive.sha256" "$arbitrary_archive.minisig"420expect_failure 'an arbitrary text file cannot stand in for the signed Sigil archive' \421 "$harness" init --evidence-dir "$temporary_root/arbitrary-archive-bundle" \422 --release-tag "$release_tag" --sigil-archive "$arbitrary_archive" \423 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"424425expect_failure 'verify reruns Sigil archive verification instead of trusting stored hashes' \426 "$harness" verify --evidence-dir "$complete_bundle" --sigil-archive "$arbitrary_archive" \427 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"428429expect_failure 'a different Sigil executable is rejected against the archive payload' \430 "$harness" init --evidence-dir "$temporary_root/wrong-sigil-bundle" \431 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \432 --sigil-bin "$wrong_sigil_bin" --portal-assets "$portal_assets"433434substituted_sigil_dir="$temporary_root/substituted-sigil"435mkdir -m 700 "$substituted_sigil_dir"436substituted_sigil_archive="$substituted_sigil_dir/$(basename -- "$sigil_archive")"437tar -czf "$substituted_sigil_archive" -C "$payload_root" payload438printf '%s\n' 'fixture checksum' > "$substituted_sigil_archive.sha256"439printf '%s\n' 'fixture-signature' > "$substituted_sigil_archive.minisig"440chmod 600 "$substituted_sigil_archive" "$substituted_sigil_archive.sha256" \441 "$substituted_sigil_archive.minisig"442if [[ "$(shasum -a 256 "$substituted_sigil_archive" | awk '{print $1}')" == \443 "$(shasum -a 256 "$sigil_archive" | awk '{print $1}')" ]]; then444 printf '\n' >> "$substituted_sigil_archive.sha256"445fi446expect_failure 'a locally valid substituted Sigil asset set is rejected by published digests' \447 "$harness" init --evidence-dir "$temporary_root/substituted-sigil-bundle" \448 --release-tag "$release_tag" --sigil-archive "$substituted_sigil_archive" \449 --sigil-bin "$sigil_bin" --portal-assets "$portal_assets"450451arbitrary_portal="$temporary_root/arbitrary-portal"452mkdir -m 700 "$arbitrary_portal"453printf '%s\n' 'arbitrary DMG' > "$arbitrary_portal/Portal-1.2.3-arm64.dmg"454printf '%s\n' 'arbitrary checksum' > "$arbitrary_portal/Portal-1.2.3-arm64.dmg.sha256"455printf '%s\n' '{}' > "$arbitrary_portal/Portal-1.2.3-arm64.json"456chmod 600 "$arbitrary_portal"/*457expect_failure 'arbitrary Portal files are rejected by release policy verification' \458 "$harness" init --evidence-dir "$temporary_root/arbitrary-portal-bundle" \459 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \460 --sigil-bin "$sigil_bin" --portal-assets "$arbitrary_portal"461462expect_failure 'verify reruns Portal release and platform verification' \463 "$harness" verify --evidence-dir "$complete_bundle" --sigil-archive "$sigil_archive" \464 --sigil-bin "$sigil_bin" --portal-assets "$arbitrary_portal"465466substituted_portal="$temporary_root/substituted-portal"467mkdir -m 700 "$substituted_portal"468cp "$portal_assets"/* "$substituted_portal/"469printf '%s\n' '{"substituted":true}' > "$substituted_portal/Portal-1.2.3-arm64.json"470chmod 600 "$substituted_portal"/*471expect_failure 'a locally valid substituted Portal asset set is rejected by published digests' \472 "$harness" init --evidence-dir "$temporary_root/substituted-portal-bundle" \473 --release-tag "$release_tag" --sigil-archive "$sigil_archive" \474 --sigil-bin "$sigil_bin" --portal-assets "$substituted_portal"475476missing_bundle="$(new_bundle missing)"477expect_failure 'missing hardware gates fail closed' verify_bundle "$missing_bundle"478479stale_bundle="$(new_bundle stale 3600)"480stale_source="$temporary_root/stale.evidence"481write_fixture "$stale_bundle" controller "$stale_source" "$(( $(date +%s) - 3601 ))"482expect_failure 'stale evidence is rejected at ingestion' \483 "$harness" record --evidence-dir "$stale_bundle" --kind controller --file "$stale_source"484485sensitive_bundle="$(new_bundle sensitive)"486sensitive_source="$temporary_root/sensitive.evidence"487write_fixture "$sensitive_bundle" mouse "$sensitive_source"488printf 'node_id=0123456789abcdef\n' >> "$sensitive_source"489expect_failure 'node IDs are rejected before normalization' \490 "$harness" record --evidence-dir "$sensitive_bundle" --kind mouse --file "$sensitive_source"491492permissions_bundle="$(new_bundle permissions)"493permissions_source="$temporary_root/permissions.evidence"494write_fixture "$permissions_bundle" controller "$permissions_source"495chmod 666 "$permissions_source"496expect_failure 'group- or other-writable evidence is rejected' \497 "$harness" record --evidence-dir "$permissions_bundle" --kind controller --file "$permissions_source"498499threshold_bundle="$(new_bundle threshold)"500threshold_source="$temporary_root/threshold.evidence"501write_fixture "$threshold_bundle" soak "$threshold_source" "$(date +%s)" 51502expect_failure 'a soak percentile over threshold is rejected' \503 "$harness" record --evidence-dir "$threshold_bundle" --kind soak --file "$threshold_source"504505printf 'tamper=1\n' >> "$complete_bundle/records/controller.evidence"506expect_failure 'post-ingestion evidence tampering is rejected' verify_bundle "$complete_bundle"507508unexpected_bundle="$(new_bundle unexpected)"509record_all_required "$unexpected_bundle"510printf '%s\n' 'not allowed' > "$unexpected_bundle/untracked.txt"511chmod 600 "$unexpected_bundle/untracked.txt"512expect_failure 'unexpected bundle content is rejected' verify_bundle "$unexpected_bundle"513514printf '1..%d\n' "$passes"