name: SSH Relay Runtime Artifacts on: pull_request: paths: - '.github/workflows/ssh-relay-runtime-artifacts.yml' - 'config/patches/node-pty@1.1.0.patch' - 'config/ssh-relay-runtime-linux-builder.Containerfile' - 'config/scripts/build-relay.mjs' - 'config/scripts/*ssh-relay*' - 'config/ssh-relay-node-release-keys/**' - 'config/ssh-relay-node-release-v24.18.0.json' - 'package.json' - 'pnpm-lock.yaml' - 'src/main/ipc/parcel-watcher-process-entry.ts' - 'src/relay/**' workflow_dispatch: # Why: callers may gate on the native graph only after separate release wiring is reviewed. workflow_call: inputs: include-baseline-gates: description: Run separately gated oldest-baseline qualification jobs required: false default: true type: boolean concurrency: group: ssh-relay-runtime-artifacts-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: contents: read jobs: build-posix-runtime: name: Build ${{ matrix.tuple }} runtime strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 tuple: linux-x64-glibc node_archive: node-v24.18.0-linux-x64.tar.xz container_image: docker.io/library/rockylinux@sha256:2d05a9266523bbf24f33ebc3a9832e4d5fd74b973c220f2204ca802286aa275d - runner: ubuntu-24.04-arm tuple: linux-arm64-glibc node_archive: node-v24.18.0-linux-arm64.tar.xz container_image: docker.io/library/rockylinux@sha256:3c2d0ce12bf79fc5ff05e43b1000e30ff062dc89405525f3307cbff71661f1a0 - runner: macos-15-intel tuple: darwin-x64 node_archive: node-v24.18.0-darwin-x64.tar.xz - runner: macos-15 tuple: darwin-arm64 node_archive: node-v24.18.0-darwin-arm64.tar.xz runs-on: ${{ matrix.runner }} timeout-minutes: 20 env: ORCA_RUNTIME_REQUESTED_RUNNER: ${{ matrix.runner }} steps: - name: Checkout exact source revision uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: # Why: artifact evidence must name the exact reviewed source, not GitHub's synthetic merge. ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Setup pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 with: run_install: false - name: Setup Node 24.18.0 uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 24.18.0 cache: pnpm - name: Record native runner identity shell: bash run: | set -euo pipefail printf 'requested_runner=%s\n' '${{ matrix.runner }}' printf 'resolved_image_os=%s\n' "${ImageOS:-unknown}" printf 'resolved_image_version=%s\n' "${ImageVersion:-unknown}" printf 'runner_arch=%s\n' "$RUNNER_ARCH" printf 'runner_environment=%s\n' "$RUNNER_ENVIRONMENT" uname -a node --version printf 'source_commit=%s\n' "$(git rev-parse HEAD)" - name: Install Linux build and verification tools if: runner.os == 'Linux' shell: bash run: | set -euo pipefail # Why: hosted ARM mirror egress can disappear; prove preinstalled tools before bounded APT fallback. probe_root=$(mktemp -d) trap 'rm -rf "$probe_root"' EXIT required_commands=(cc c++ make ar ld strip curl gpg gpgv python3 xz) missing_requirements=() for required_command in "${required_commands[@]}"; do if ! command -v "$required_command" >/dev/null; then missing_requirements+=("$required_command") fi done if [ ! -r /etc/ssl/certs/ca-certificates.crt ]; then missing_requirements+=(ca-certificates) fi probe_c_toolchain() { printf '%s\n' '#include ' 'int main(void) { return EXIT_SUCCESS; }' \ | cc -x c - -o "$probe_root/orca-ci-c-probe" \ && "$probe_root/orca-ci-c-probe" } probe_cxx_toolchain() { printf '%s\n' '#include ' 'int main() { return EXIT_SUCCESS; }' \ | c++ -x c++ - -o "$probe_root/orca-ci-cxx-probe" \ && "$probe_root/orca-ci-cxx-probe" } if command -v cc >/dev/null && command -v c++ >/dev/null; then if ! probe_c_toolchain || ! probe_cxx_toolchain; then missing_requirements+=(build-essential) fi fi if ((${#missing_requirements[@]} > 0)); then printf 'missing Linux prerequisites: %s\n' "${missing_requirements[*]}" for source_file in /etc/apt/sources.list /etc/apt/sources.list.d/*.sources; do [ -f "$source_file" ] || continue sudo sed -i \ -e 's|http://ports.ubuntu.com|https://ports.ubuntu.com|g' \ -e 's|http://archive.ubuntu.com|https://archive.ubuntu.com|g' \ -e 's|http://security.ubuntu.com|https://security.ubuntu.com|g' \ -e 's|http://azure.archive.ubuntu.com|https://azure.archive.ubuntu.com|g' \ "$source_file" done apt_options=( -o Acquire::Retries=3 -o Acquire::http::Timeout=15 -o Acquire::https::Timeout=15 -o Acquire::ForceIPv4=true -o DPkg::Lock::Timeout=60 ) timeout --signal=TERM --kill-after=15s 180s sudo apt-get "${apt_options[@]}" update -qq timeout --signal=TERM --kill-after=15s 180s sudo apt-get "${apt_options[@]}" install -y -qq \ build-essential ca-certificates curl gnupg gpgv python3 xz-utils fi for required_command in "${required_commands[@]}"; do command -v "$required_command" >/dev/null done test -r /etc/ssl/certs/ca-certificates.crt probe_c_toolchain probe_cxx_toolchain - name: Install macOS signature verifier if: runner.os == 'macOS' shell: bash run: | set -euo pipefail brew list gnupg >/dev/null 2>&1 || brew install gnupg command -v xz xcodebuild -version - name: Install source dependencies without native postinstall shell: bash run: | set -euo pipefail pnpm install --frozen-lockfile --ignore-scripts git diff --exit-code package.json pnpm-lock.yaml - name: Run runtime artifact contract tests shell: bash run: | set -euo pipefail node --check config/scripts/ssh-relay-runtime-compatibility.mjs node --check config/scripts/ssh-relay-runtime-compatibility.test.mjs node --check config/scripts/ssh-relay-runtime-reproducibility.mjs node --check config/scripts/ssh-relay-runtime-reproducibility.test.mjs node --check config/scripts/ssh-relay-runtime-baseline.mjs node --check config/scripts/ssh-relay-runtime-baseline.test.mjs node --check config/scripts/ssh-relay-runtime-linux-build-evidence.mjs node --check config/scripts/ssh-relay-runtime-linux-build-evidence.test.mjs node --check config/scripts/ssh-relay-linux-node-gyp-compiler.mjs node --check config/scripts/ssh-relay-linux-node-gyp-compiler.test.mjs node --check config/scripts/build-ssh-relay-runtime.mjs node --check config/scripts/ssh-relay-runtime-build.test.mjs node --check config/scripts/ssh-relay-runtime-closure.mjs node --check config/scripts/ssh-relay-runtime-closure.test.mjs node --check config/scripts/ssh-relay-runtime-sbom.mjs node --check config/scripts/ssh-relay-runtime-sbom.test.mjs node --check config/scripts/ssh-relay-runtime-provenance.test.mjs node --check config/scripts/ssh-relay-runtime-toolchain.mjs node --check config/scripts/ssh-relay-runtime-toolchain.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-plan.mjs node --check config/scripts/ssh-relay-runtime-native-signing-plan.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-selection.mjs node --check config/scripts/ssh-relay-runtime-native-signing-selection.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-payload.mjs node --check config/scripts/ssh-relay-runtime-native-signing-payload.test.mjs node --check config/scripts/ssh-relay-runtime-windows-authenticode-assessment.mjs node --check config/scripts/ssh-relay-runtime-windows-authenticode-assessment.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-stage.mjs node --check config/scripts/ssh-relay-runtime-native-signing-stage.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-apply.mjs node --check config/scripts/ssh-relay-runtime-native-signing-apply.test.mjs node --check config/scripts/ssh-relay-runtime-macos-signature-verification.mjs node --check config/scripts/ssh-relay-runtime-macos-signature-verification.test.mjs node --check config/scripts/ssh-relay-runtime-windows-signature-verification.mjs node --check config/scripts/ssh-relay-runtime-windows-signature-verification.test.mjs node --check config/scripts/ssh-relay-runtime-windows-source-signature-verification.mjs node --check config/scripts/ssh-relay-runtime-windows-source-signature-verification.test.mjs node --check config/scripts/ssh-relay-runtime-release-stage-gate.mjs node --check config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs node --check config/scripts/ssh-relay-runtime-draft-recovery.mjs node --check config/scripts/ssh-relay-runtime-draft-recovery.test.mjs node --check config/scripts/ssh-relay-runtime-aggregate-input.mjs node --check config/scripts/ssh-relay-runtime-aggregate-input.test.mjs node --check config/scripts/ssh-relay-runtime-draft-readback.mjs node --check config/scripts/ssh-relay-runtime-draft-readback.test.mjs node --check config/scripts/ssh-relay-runtime-draft-readback-materialization.test.mjs node --check config/scripts/ssh-relay-runtime-draft-release-verification.mjs node --check config/scripts/ssh-relay-runtime-draft-release-verification.test.mjs node --check config/scripts/ssh-relay-runtime-readback-archive-execution.mjs node --check config/scripts/ssh-relay-runtime-readback-archive-execution.test.mjs node --check config/scripts/ssh-relay-runtime-draft-upload.mjs node --check config/scripts/ssh-relay-runtime-draft-upload.test.mjs node --check config/scripts/ssh-relay-runtime-release-assets.mjs node --check config/scripts/ssh-relay-runtime-release-assets.test.mjs node --check config/scripts/ssh-relay-runtime-manifest-validation.mjs node --check config/scripts/ssh-relay-runtime-manifest-assembly.mjs node --check config/scripts/ssh-relay-runtime-manifest-assembly.test.mjs node --check config/scripts/ssh-relay-runtime-manifest-signing-handoff.mjs node --check config/scripts/ssh-relay-runtime-manifest-signing-handoff.test.mjs node --check config/scripts/ssh-relay-runtime-manifest-seed-signing.mjs node --check config/scripts/ssh-relay-runtime-manifest-seed-signing.test.mjs node --check config/scripts/ssh-relay-runtime-manifest-artifact-collection.mjs node --check config/scripts/ssh-relay-runtime-manifest-aggregate-command.mjs node --check config/scripts/ssh-relay-runtime-manifest-aggregate-command.test.mjs node --check config/scripts/ssh-relay-runtime-manifest-signing-workflow.test.mjs node --check config/scripts/ssh-relay-runtime-manifest-aggregate.mjs node --check config/scripts/ssh-relay-runtime-manifest-aggregate.test.mjs node --check config/scripts/ssh-relay-runtime-manifest-tuple.mjs node --check config/scripts/ssh-relay-runtime-manifest-tuple.test.mjs node --check config/scripts/ssh-relay-runtime-post-sign-metadata.mjs node --check config/scripts/ssh-relay-runtime-post-sign-metadata.test.mjs node --check config/scripts/ssh-relay-runtime-archive-extraction.mjs node --check config/scripts/ssh-relay-runtime-archive-extraction.test.mjs node --check config/scripts/ssh-relay-runtime-portable-archive.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-stage-report.mjs node --check config/scripts/ssh-relay-runtime-macos-signing.mjs node --check config/scripts/ssh-relay-runtime-macos-signing.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-finalization.mjs node --check config/scripts/ssh-relay-runtime-native-signing-finalization.test.mjs node --check config/scripts/ssh-relay-runtime-linux-finalization.mjs node --check config/scripts/ssh-relay-runtime-linux-finalization.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-workflow.test.mjs node --check config/scripts/ssh-relay-runtime-native-signing-rehearsal-workflow.test.mjs node --check config/scripts/ssh-relay-runtime-build-prerequisite.test.mjs pnpm exec vitest run --config config/vitest.config.ts \ src/main/ssh/ssh-relay-artifact-schema.test.ts \ src/main/ssh/ssh-relay-manifest-signature.test.ts \ src/main/ssh/ssh-relay-release-asset.test.ts \ src/main/ssh/ssh-relay-artifact-selector.test.ts \ src/main/ssh/ssh-relay-libc-detection.test.ts \ src/main/ssh/ssh-relay-linux-kernel-detection.test.ts \ src/main/ssh/ssh-relay-darwin-version-detection.test.ts \ src/main/ssh/ssh-relay-darwin-translation-detection.test.ts \ src/main/ssh/ssh-relay-linux-libstdcxx-detection.test.ts \ src/main/ssh/ssh-relay-windows-compatibility-detection.test.ts \ src/main/ssh/ssh-relay-artifact-download.test.ts \ src/main/ssh/ssh-relay-artifact-extraction.test.ts \ src/main/ssh/ssh-relay-artifact-cache-lock-release.test.ts \ src/main/ssh/ssh-relay-artifact-cache-lock.test.ts \ src/main/ssh/ssh-relay-artifact-cache-entry.test.ts \ src/main/ssh/ssh-relay-artifact-cache-in-use-lease.test.ts \ src/main/ssh/ssh-relay-artifact-cache-eviction.test.ts \ src/main/ssh/ssh-relay-artifact-cache-root.test.ts \ src/main/ssh/ssh-relay-artifact-cache-resolution.test.ts \ src/main/ssh/ssh-relay-artifact-cache-population.test.ts \ src/main/ssh/ssh-relay-artifact-cache-population-integration.test.ts \ src/main/ssh/ssh-relay-artifact-acquisition.test.ts \ src/main/ssh/ssh-relay-artifact-acquisition-integration.test.ts \ src/main/ssh/ssh-relay-compiled-manifest-trust.test.ts \ src/main/ssh/ssh-relay-official-manifest.test.ts \ src/main/ssh/ssh-relay-manifest-accepted-keys.test.ts \ src/main/ssh/ssh-relay-packaged-manifest.test.ts \ src/main/ssh/ssh-relay-runtime-identity.test.ts \ config/scripts/ssh-relay-runtime-compatibility.test.mjs \ config/scripts/ssh-relay-node-release-verification.test.mjs \ config/scripts/ssh-relay-node-tar-inspection.test.mjs \ config/scripts/ssh-relay-linux-node-gyp-compiler.test.mjs \ config/scripts/ssh-relay-node-pty-build.test.mjs \ config/scripts/ssh-relay-node-pty-windows-build-determinism.test.mjs \ config/scripts/ssh-relay-node-pty-windows-settlement.test.mjs \ config/scripts/ssh-relay-node-zip-inspection.test.mjs \ config/scripts/ssh-relay-runtime-artifact.test.mjs \ config/scripts/ssh-relay-runtime-portable-archive.test.mjs \ config/scripts/ssh-relay-runtime-baseline.test.mjs \ config/scripts/ssh-relay-runtime-build.test.mjs \ config/scripts/ssh-relay-runtime-linux-build-evidence.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-plan.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-selection.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-payload.test.mjs \ config/scripts/ssh-relay-runtime-windows-authenticode-assessment.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-stage.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-apply.test.mjs \ config/scripts/ssh-relay-runtime-macos-signature-verification.test.mjs \ config/scripts/ssh-relay-runtime-windows-signature-verification.test.mjs \ config/scripts/ssh-relay-runtime-windows-source-signature-verification.test.mjs \ config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs \ config/scripts/ssh-relay-runtime-draft-recovery.test.mjs \ config/scripts/ssh-relay-runtime-aggregate-input.test.mjs \ config/scripts/ssh-relay-runtime-draft-readback.test.mjs \ config/scripts/ssh-relay-runtime-draft-readback-materialization.test.mjs \ config/scripts/ssh-relay-runtime-draft-release-verification.test.mjs \ config/scripts/ssh-relay-runtime-readback-archive-execution.test.mjs \ config/scripts/ssh-relay-runtime-draft-upload.test.mjs \ config/scripts/ssh-relay-runtime-release-assets.test.mjs \ config/scripts/ssh-relay-runtime-manifest-assembly.test.mjs \ config/scripts/ssh-relay-runtime-manifest-signing-handoff.test.mjs \ config/scripts/ssh-relay-runtime-manifest-seed-signing.test.mjs \ config/scripts/ssh-relay-runtime-manifest-aggregate-command.test.mjs \ config/scripts/ssh-relay-runtime-manifest-signing-workflow.test.mjs \ config/scripts/ssh-relay-runtime-manifest-aggregate.test.mjs \ config/scripts/ssh-relay-runtime-manifest-tuple.test.mjs \ config/scripts/ssh-relay-runtime-post-sign-metadata.test.mjs \ config/scripts/ssh-relay-runtime-archive-extraction.test.mjs \ config/scripts/ssh-relay-runtime-macos-signing.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-finalization.test.mjs \ config/scripts/ssh-relay-runtime-linux-finalization.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-workflow.test.mjs \ config/scripts/ssh-relay-runtime-native-signing-rehearsal-workflow.test.mjs \ config/scripts/ssh-relay-runtime-build-prerequisite.test.mjs \ config/scripts/ssh-relay-runtime-closure.test.mjs \ config/scripts/ssh-relay-runtime-pty-smoke.test.mjs \ config/scripts/ssh-relay-runtime-reproducibility.test.mjs \ config/scripts/ssh-relay-runtime-resource-diagnostics.test.mjs \ config/scripts/ssh-relay-runtime-provenance.test.mjs \ config/scripts/ssh-relay-runtime-sbom.test.mjs \ config/scripts/ssh-relay-runtime-toolchain.test.mjs \ config/scripts/ssh-relay-runtime-windows-tree.test.mjs \ config/scripts/ssh-relay-runtime-zip.test.mjs \ config/scripts/ssh-relay-runtime-workflow.test.mjs - name: Download exact Node release inputs shell: bash run: | set -euo pipefail inputs="$RUNNER_TEMP/node-release-inputs" mkdir -p "$inputs" base='https://nodejs.org/dist/v24.18.0' for asset in SHASUMS256.txt SHASUMS256.txt.sig '${{ matrix.node_archive }}'; do curl --fail --silent --show-error --location --proto '=https' --tlsv1.2 \ --connect-timeout 20 --max-time 300 --retry 2 --retry-delay 2 --retry-all-errors \ "$base/$asset" --output "$inputs/$asset" done printf 'NODE_INPUTS=%s\n' "$inputs" >> "$GITHUB_ENV" - name: Prepare digest-pinned Linux floor builder if: runner.os == 'Linux' shell: bash run: | set -euo pipefail image='${{ matrix.container_image }}' builder="orca-ssh-relay-linux-builder:${GITHUB_RUN_ID}-${{ matrix.tuple }}" docker pull "$image" docker build --pull=false \ --build-arg "BASE_IMAGE=$image" \ --file config/ssh-relay-runtime-linux-builder.Containerfile \ --tag "$builder" . docker image inspect "$image" --format 'linux_floor_base={{index .RepoDigests 0}}' docker image inspect "$builder" --format 'linux_floor_builder_id={{.Id}}' printf 'ORCA_LINUX_BUILDER=%s\n' "$builder" >> "$GITHUB_ENV" - name: Build twice, inspect, smoke, and compare exact runtime shell: bash run: | set -euo pipefail output_root="$RUNNER_TEMP/ssh-relay-runtime/${{ matrix.tuple }}" work_directory="$RUNNER_TEMP/orca-ssh-relay-runtime-build-work" source_commit=$(git rev-parse HEAD) source_date_epoch=$(git show -s --format=%ct "$source_commit") if [[ '${{ matrix.tuple }}' == linux-* ]]; then # Why: package installation is isolated earlier; native compilation itself has no egress. docker run --rm --network none --read-only --cap-drop all \ --user "$(id -u):$(id -g)" \ --security-opt no-new-privileges --pids-limit 512 --memory 6g --cpus 4 \ --tmpfs /tmp:rw,nosuid,size=1g,mode=1777 \ --mount "type=bind,src=$GITHUB_WORKSPACE,dst=$GITHUB_WORKSPACE" \ --mount "type=bind,src=$RUNNER_TEMP,dst=$RUNNER_TEMP" \ --workdir "$GITHUB_WORKSPACE" \ --env "GITHUB_ACTIONS=$GITHUB_ACTIONS" \ --env "GITHUB_REPOSITORY=$GITHUB_REPOSITORY" \ --env "GITHUB_RUN_ID=$GITHUB_RUN_ID" \ --env "GITHUB_WORKFLOW_REF=$GITHUB_WORKFLOW_REF" \ --env "RUNNER_OS=$RUNNER_OS" \ --env "RUNNER_ARCH=$RUNNER_ARCH" \ --env "RUNNER_ENVIRONMENT=$RUNNER_ENVIRONMENT" \ --env "ORCA_RUNTIME_REQUESTED_RUNNER=$ORCA_RUNTIME_REQUESTED_RUNNER" \ --env 'ImageOS=rockylinux-8' \ --env 'ImageVersion=${{ matrix.container_image }}' \ --env 'HOME=/tmp' \ "$ORCA_LINUX_BUILDER" \ /usr/bin/node config/scripts/ssh-relay-runtime-linux-build-evidence.mjs \ --tuple '${{ matrix.tuple }}' \ --inputs-directory "$NODE_INPUTS" \ --output-root "$output_root" \ --work-directory "$work_directory" \ --evidence-directory "$GITHUB_WORKSPACE/runtime-evidence/${{ matrix.tuple }}" \ --source-date-epoch "$source_date_epoch" \ --git-commit "$source_commit" first_output="$output_root/first" identity="$first_output/orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json" archive=$(find "$first_output" -maxdepth 1 -name '*.tar.br' -print -quit) test -n "$archive" executed_runtime="$output_root/readback-executed" # Why: exercise only local verified bytes; release writes and remote consumers stay disconnected. docker run --rm --network none --read-only --cap-drop all \ --user "$(id -u):$(id -g)" \ --security-opt no-new-privileges --pids-limit 512 --memory 6g --cpus 4 \ --tmpfs /tmp:rw,nosuid,size=1g,mode=1777 \ --mount "type=bind,src=$GITHUB_WORKSPACE,dst=$GITHUB_WORKSPACE" \ --mount "type=bind,src=$RUNNER_TEMP,dst=$RUNNER_TEMP" \ --workdir "$GITHUB_WORKSPACE" \ "$ORCA_LINUX_BUILDER" \ /usr/bin/node config/scripts/ssh-relay-runtime-readback-archive-execution.mjs \ --identity "$identity" \ --archive "$archive" \ --output-directory "$executed_runtime" rm -rf -- "$executed_runtime" test ! -e "$executed_runtime" signing_stage="$output_root/signing-stage" signing_report="$output_root/orca-ssh-relay-runtime-${{ matrix.tuple }}.signing-stage.json" node config/scripts/ssh-relay-runtime-native-signing-stage.mjs \ --identity "$identity" \ --runtime-directory "$first_output/runtime" \ --staging-directory "$signing_stage" > "$signing_report" node -e ' const { readFileSync } = require("node:fs") const report = JSON.parse(readFileSync(process.argv[1], "utf8")) if (report.platform !== "linux" || report.assessments.length !== 0 || report.signingFiles.length !== 0 || report.preservedUpstreamFiles.length !== 0 || report.payload.stagingRequired !== false || report.payload.stagedFiles.length !== 0) { throw new Error("Linux signing-stage report violates the hash-only contract") } ' "$signing_report" test ! -e "$signing_stage" cp "$signing_report" "$GITHUB_WORKSPACE/runtime-evidence/${{ matrix.tuple }}/" verified_at=$(node -e 'process.stdout.write(new Date().toISOString())') final_output="$output_root/linux-final" node config/scripts/ssh-relay-runtime-linux-finalization.mjs \ --source-output-directory "$first_output" \ --identity "$identity" \ --output-directory "$final_output" \ --verified-at "$verified_at" \ --native-verification-tool-version "$(node -p 'process.versions.node')" cp "$final_output/assets/orca-ssh-relay-runtime-${{ matrix.tuple }}.manifest-tuple.json" \ "$final_output/evidence/${{ matrix.tuple }}.linux-finalization.json" \ "$GITHUB_WORKSPACE/runtime-evidence/${{ matrix.tuple }}/" exit 0 fi mkdir -p "$output_root" first_output="$output_root/first" second_output="$output_root/second" for output in "$first_output" "$second_output"; do printf 'clean_build_output=%s\n' "$output" /usr/bin/time -p node config/scripts/build-ssh-relay-runtime.mjs \ --tuple '${{ matrix.tuple }}' \ --inputs-directory "$NODE_INPUTS" \ --output-directory "$output" \ --work-directory "$work_directory" \ --source-date-epoch "$source_date_epoch" \ --git-commit "$source_commit" identity="$output/orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json" archive=$(find "$output" -maxdepth 1 -name '*.tar.br' -print -quit) test -n "$archive" /usr/bin/time -p node config/scripts/verify-ssh-relay-runtime.mjs \ --runtime-directory "$output/runtime" \ --identity "$identity" \ --archive "$archive" if find "$output/runtime" -type f \( -name npm -o -name npx -o -name corepack -o -name '*.map' \) -print -quit | grep -q .; then echo 'Runtime contains a prohibited package manager or source map.' >&2 exit 1 fi parcel_packages=$(find "$output/runtime/node_modules/@parcel" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ') test "$parcel_packages" = 2 done node config/scripts/ssh-relay-runtime-reproducibility.mjs \ --tuple '${{ matrix.tuple }}' \ --first-output-directory "$first_output" \ --second-output-directory "$second_output" identity="$first_output/orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json" archive=$(find "$first_output" -maxdepth 1 -name '*.tar.br' -print -quit) test -n "$archive" executed_runtime="$output_root/readback-executed" # Why: exercise only local verified bytes; release writes and remote consumers stay disconnected. node config/scripts/ssh-relay-runtime-readback-archive-execution.mjs \ --identity "$identity" \ --archive "$archive" \ --output-directory "$executed_runtime" rm -rf -- "$executed_runtime" test ! -e "$executed_runtime" signing_stage="$output_root/signing-stage" signing_report="$output_root/orca-ssh-relay-runtime-${{ matrix.tuple }}.signing-stage.json" node config/scripts/ssh-relay-runtime-native-signing-stage.mjs \ --identity "$first_output/orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json" \ --runtime-directory "$first_output/runtime" \ --staging-directory "$signing_stage" > "$signing_report" node -e ' const { readFileSync } = require("node:fs") const report = JSON.parse(readFileSync(process.argv[1], "utf8")) if (report.platform !== "darwin" || report.assessments.length !== 0 || report.signingFiles.length !== 3 || report.preservedUpstreamFiles.length !== 0 || report.payload.stagingRequired !== true || report.payload.stagedFiles.length !== 3) { throw new Error("macOS signing-stage report violates the Developer ID contract") } ' "$signing_report" test "$(find "$signing_stage" -type f | wc -l | tr -d ' ')" = 3 test ! -e "$signing_stage/bin/node" rm -rf -- "$signing_stage" mkdir -p "$GITHUB_WORKSPACE/runtime-evidence/${{ matrix.tuple }}" cp "$first_output"/*.tar.br "$first_output"/*.spdx.json "$first_output"/*.provenance.json "$first_output"/*.identity.json \ "$signing_report" "$GITHUB_WORKSPACE/runtime-evidence/${{ matrix.tuple }}/" - name: Measure full-size desktop extraction and cache boundaries shell: bash run: | set -euo pipefail first_output="$RUNNER_TEMP/ssh-relay-runtime/${{ matrix.tuple }}/first" identity="$first_output/orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json" archive=$(find "$first_output" -maxdepth 1 -name '*.tar.br' -print -quit) test -n "$archive" extraction_output="$RUNNER_TEMP/ssh-relay-runtime/${{ matrix.tuple }}/desktop-extraction-measurement" # Why: exact payload size can expose memory behavior that synthetic archive fixtures cannot. ORCA_SSH_RELAY_FULL_SIZE_ARCHIVE="$archive" \ ORCA_SSH_RELAY_FULL_SIZE_IDENTITY="$identity" \ ORCA_SSH_RELAY_FULL_SIZE_OUTPUT="$extraction_output" \ pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 --reporter=verbose \ src/main/ssh/ssh-relay-artifact-extraction-full-size.test.ts test ! -e "$extraction_output" ORCA_SSH_RELAY_FULL_SIZE_ARCHIVE="$archive" \ ORCA_SSH_RELAY_FULL_SIZE_IDENTITY="$identity" \ ORCA_SSH_RELAY_FULL_SIZE_OUTPUT="$extraction_output" \ pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 --reporter=verbose \ src/main/ssh/ssh-relay-artifact-cache-entry-full-size.test.ts test ! -e "$extraction_output" - name: Upload unpublished artifact evidence uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: ssh-relay-runtime-${{ matrix.tuple }} path: runtime-evidence/${{ matrix.tuple }}/ if-no-files-found: error retention-days: 7 build-windows-runtime: name: Build ${{ matrix.tuple }} runtime strategy: fail-fast: false matrix: include: - runner: windows-2022 tuple: win32-x64 node_archive: node-v24.18.0-win-x64.zip node_library: win-x64/node.lib msvc_arch: x64 - runner: windows-11-arm tuple: win32-arm64 node_archive: node-v24.18.0-win-arm64.zip node_library: win-arm64/node.lib msvc_arch: arm64 runs-on: ${{ matrix.runner }} timeout-minutes: 30 env: ORCA_RUNTIME_REQUESTED_RUNNER: ${{ matrix.runner }} steps: - name: Checkout exact source revision uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: # Why: artifact evidence must name the exact reviewed source, not GitHub's synthetic merge. ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Configure target-native MSVC uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1 with: arch: ${{ matrix.msvc_arch }} - name: Setup pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 with: run_install: false - name: Setup Node 24.18.0 uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 24.18.0 cache: pnpm - name: Record native runner identity shell: pwsh run: | $ErrorActionPreference = 'Stop' "requested_runner=${{ matrix.runner }}" "resolved_image_os=$env:ImageOS" "resolved_image_version=$env:ImageVersion" "runner_arch=$env:RUNNER_ARCH" "runner_environment=$env:RUNNER_ENVIRONMENT" Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber node --version "source_commit=$(git rev-parse HEAD)" - name: Select bundled Git signature tools shell: pwsh run: | $ErrorActionPreference = 'Stop' $gitUsrBin = Join-Path $env:ProgramFiles 'Git/usr/bin' foreach ($tool in @('gpg.exe', 'gpgv.exe')) { if (-not (Test-Path -LiteralPath (Join-Path $gitUsrBin $tool) -PathType Leaf)) { throw "Git for Windows does not provide required signature tool: $tool" } } $gitUsrBin | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Install source dependencies without native postinstall shell: pwsh run: | $ErrorActionPreference = 'Stop' pnpm install --frozen-lockfile --ignore-scripts if ($LASTEXITCODE -ne 0) { throw 'pnpm install failed' } git diff --exit-code package.json pnpm-lock.yaml if ($LASTEXITCODE -ne 0) { throw 'frozen install changed dependency files' } - name: Run runtime artifact contract tests shell: pwsh run: | $ErrorActionPreference = 'Stop' node --check config/scripts/ssh-relay-runtime-compatibility.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime compatibility syntax check failed' } node --check config/scripts/ssh-relay-runtime-compatibility.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime compatibility test syntax check failed' } node --check config/scripts/ssh-relay-runtime-reproducibility.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime reproducibility script syntax check failed' } node --check config/scripts/ssh-relay-runtime-reproducibility.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime reproducibility test syntax check failed' } node --check config/scripts/ssh-relay-runtime-baseline.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime baseline script syntax check failed' } node --check config/scripts/ssh-relay-runtime-baseline.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime baseline test syntax check failed' } node --check config/scripts/ssh-relay-runtime-linux-build-evidence.mjs if ($LASTEXITCODE -ne 0) { throw 'Linux runtime evidence script syntax check failed' } node --check config/scripts/ssh-relay-runtime-linux-build-evidence.test.mjs if ($LASTEXITCODE -ne 0) { throw 'Linux runtime evidence test syntax check failed' } node --check config/scripts/ssh-relay-linux-node-gyp-compiler.mjs if ($LASTEXITCODE -ne 0) { throw 'Linux node-gyp compiler script syntax check failed' } node --check config/scripts/ssh-relay-linux-node-gyp-compiler.test.mjs if ($LASTEXITCODE -ne 0) { throw 'Linux node-gyp compiler test syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-pe-diagnostic.mjs if ($LASTEXITCODE -ne 0) { throw 'Windows PE diagnostic script syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-pe-diagnostic.test.mjs if ($LASTEXITCODE -ne 0) { throw 'Windows PE diagnostic test syntax check failed' } node --check config/scripts/build-ssh-relay-runtime.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime build script syntax check failed' } node --check config/scripts/ssh-relay-runtime-build.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime build test syntax check failed' } node --check config/scripts/ssh-relay-runtime-closure.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime closure script syntax check failed' } node --check config/scripts/ssh-relay-runtime-closure.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime closure test syntax check failed' } node --check config/scripts/ssh-relay-runtime-sbom.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime SBOM script syntax check failed' } node --check config/scripts/ssh-relay-runtime-sbom.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime SBOM test syntax check failed' } node --check config/scripts/ssh-relay-runtime-provenance.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime provenance test syntax check failed' } node --check config/scripts/ssh-relay-runtime-toolchain.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime toolchain script syntax check failed' } node --check config/scripts/ssh-relay-runtime-toolchain.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime toolchain test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-plan.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing plan script syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-plan.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing plan test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-selection.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing selection script syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-selection.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing selection test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-payload.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing payload script syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-payload.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing payload test syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-authenticode-assessment.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Windows Authenticode assessment script syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-authenticode-assessment.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Windows Authenticode assessment test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-stage.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing stage script syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-stage.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing stage test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-apply.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing apply script syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-apply.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing apply test syntax check failed' } node --check config/scripts/ssh-relay-runtime-macos-signature-verification.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime macOS signature verification script syntax check failed' } node --check config/scripts/ssh-relay-runtime-macos-signature-verification.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime macOS signature verification test syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-signature-verification.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Windows signature verification script syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-signature-verification.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Windows signature verification test syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-source-signature-verification.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Windows source signature verification script syntax check failed' } node --check config/scripts/ssh-relay-runtime-windows-source-signature-verification.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Windows source signature verification test syntax check failed' } node --check config/scripts/ssh-relay-runtime-release-stage-gate.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime release stage gate syntax check failed' } node --check config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime release stage gate test syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-recovery.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft recovery syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-recovery.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft recovery test syntax check failed' } node --check config/scripts/ssh-relay-runtime-aggregate-input.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime aggregate input syntax check failed' } node --check config/scripts/ssh-relay-runtime-aggregate-input.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime aggregate input test syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-readback.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft read-back syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-readback.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft read-back test syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-readback-materialization.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft read-back materialization test syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-release-verification.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft release verification syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-release-verification.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft release verification test syntax check failed' } node --check config/scripts/ssh-relay-runtime-readback-archive-execution.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime read-back archive execution syntax check failed' } node --check config/scripts/ssh-relay-runtime-readback-archive-execution.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime read-back archive execution test syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-upload.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft upload syntax check failed' } node --check config/scripts/ssh-relay-runtime-draft-upload.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime draft upload test syntax check failed' } node --check config/scripts/ssh-relay-runtime-release-assets.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime release assets syntax check failed' } node --check config/scripts/ssh-relay-runtime-release-assets.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime release assets test syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-validation.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest validation syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-assembly.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest assembly syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-assembly.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest assembly test syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-signing-handoff.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest signing handoff syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-signing-handoff.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest signing handoff test syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-seed-signing.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest seed signing syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-seed-signing.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest seed signing test syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-artifact-collection.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest artifact collection syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-aggregate-command.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest aggregate command syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-aggregate-command.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest aggregate command test syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-signing-workflow.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest signing workflow test syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-aggregate.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest aggregate syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-aggregate.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest aggregate test syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-tuple.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest tuple syntax check failed' } node --check config/scripts/ssh-relay-runtime-manifest-tuple.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime manifest tuple test syntax check failed' } node --check config/scripts/ssh-relay-runtime-post-sign-metadata.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime post-sign metadata syntax check failed' } node --check config/scripts/ssh-relay-runtime-post-sign-metadata.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime post-sign metadata test syntax check failed' } node --check config/scripts/ssh-relay-runtime-archive-extraction.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime archive extraction syntax check failed' } node --check config/scripts/ssh-relay-runtime-archive-extraction.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime archive extraction test syntax check failed' } node --check config/scripts/ssh-relay-runtime-portable-archive.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime portable archive test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-stage-report.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime signing stage report syntax check failed' } node --check config/scripts/ssh-relay-runtime-macos-signing.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime macOS signing syntax check failed' } node --check config/scripts/ssh-relay-runtime-macos-signing.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime macOS signing test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-finalization.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime signing finalization syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-finalization.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime signing finalization test syntax check failed' } node --check config/scripts/ssh-relay-runtime-linux-finalization.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Linux finalization syntax check failed' } node --check config/scripts/ssh-relay-runtime-linux-finalization.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime Linux finalization test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-workflow.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing workflow test syntax check failed' } node --check config/scripts/ssh-relay-runtime-native-signing-rehearsal-workflow.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime native signing rehearsal workflow test syntax check failed' } node --check config/scripts/ssh-relay-runtime-build-prerequisite.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime build prerequisite test syntax check failed' } pnpm exec vitest run --config config/vitest.config.ts ` src/main/ssh/ssh-relay-artifact-schema.test.ts ` src/main/ssh/ssh-relay-manifest-signature.test.ts ` src/main/ssh/ssh-relay-release-asset.test.ts ` src/main/ssh/ssh-relay-artifact-selector.test.ts ` src/main/ssh/ssh-relay-libc-detection.test.ts ` src/main/ssh/ssh-relay-linux-kernel-detection.test.ts ` src/main/ssh/ssh-relay-darwin-version-detection.test.ts ` src/main/ssh/ssh-relay-darwin-translation-detection.test.ts ` src/main/ssh/ssh-relay-linux-libstdcxx-detection.test.ts ` src/main/ssh/ssh-relay-windows-compatibility-detection.test.ts ` src/main/ssh/ssh-relay-artifact-download.test.ts ` src/main/ssh/ssh-relay-artifact-extraction.test.ts ` src/main/ssh/ssh-relay-artifact-cache-lock-release.test.ts ` src/main/ssh/ssh-relay-artifact-cache-lock.test.ts ` src/main/ssh/ssh-relay-artifact-cache-entry.test.ts ` src/main/ssh/ssh-relay-artifact-cache-in-use-lease.test.ts ` src/main/ssh/ssh-relay-artifact-cache-eviction.test.ts ` src/main/ssh/ssh-relay-artifact-cache-root.test.ts ` src/main/ssh/ssh-relay-artifact-cache-resolution.test.ts ` src/main/ssh/ssh-relay-artifact-cache-population.test.ts ` src/main/ssh/ssh-relay-artifact-cache-population-integration.test.ts ` src/main/ssh/ssh-relay-artifact-acquisition.test.ts ` src/main/ssh/ssh-relay-artifact-acquisition-integration.test.ts ` src/main/ssh/ssh-relay-compiled-manifest-trust.test.ts ` src/main/ssh/ssh-relay-official-manifest.test.ts ` src/main/ssh/ssh-relay-manifest-accepted-keys.test.ts ` src/main/ssh/ssh-relay-packaged-manifest.test.ts ` src/main/ssh/ssh-relay-runtime-identity.test.ts ` config/scripts/ssh-relay-runtime-compatibility.test.mjs ` config/scripts/ssh-relay-node-release-verification.test.mjs ` config/scripts/ssh-relay-node-tar-inspection.test.mjs ` config/scripts/ssh-relay-linux-node-gyp-compiler.test.mjs ` config/scripts/ssh-relay-node-pty-build.test.mjs ` config/scripts/ssh-relay-node-pty-windows-build-determinism.test.mjs ` config/scripts/ssh-relay-node-pty-windows-settlement.test.mjs ` config/scripts/ssh-relay-node-zip-inspection.test.mjs ` config/scripts/ssh-relay-runtime-artifact.test.mjs ` config/scripts/ssh-relay-runtime-portable-archive.test.mjs ` config/scripts/ssh-relay-runtime-baseline.test.mjs ` config/scripts/ssh-relay-runtime-build.test.mjs ` config/scripts/ssh-relay-runtime-linux-build-evidence.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-plan.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-selection.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-payload.test.mjs ` config/scripts/ssh-relay-runtime-windows-authenticode-assessment.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-stage.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-apply.test.mjs ` config/scripts/ssh-relay-runtime-macos-signature-verification.test.mjs ` config/scripts/ssh-relay-runtime-windows-signature-verification.test.mjs ` config/scripts/ssh-relay-runtime-windows-source-signature-verification.test.mjs ` config/scripts/ssh-relay-runtime-release-stage-gate.test.mjs ` config/scripts/ssh-relay-runtime-draft-recovery.test.mjs ` config/scripts/ssh-relay-runtime-aggregate-input.test.mjs ` config/scripts/ssh-relay-runtime-draft-readback.test.mjs ` config/scripts/ssh-relay-runtime-draft-readback-materialization.test.mjs ` config/scripts/ssh-relay-runtime-draft-release-verification.test.mjs ` config/scripts/ssh-relay-runtime-readback-archive-execution.test.mjs ` config/scripts/ssh-relay-runtime-draft-upload.test.mjs ` config/scripts/ssh-relay-runtime-release-assets.test.mjs ` config/scripts/ssh-relay-runtime-manifest-assembly.test.mjs ` config/scripts/ssh-relay-runtime-manifest-signing-handoff.test.mjs ` config/scripts/ssh-relay-runtime-manifest-seed-signing.test.mjs ` config/scripts/ssh-relay-runtime-manifest-aggregate-command.test.mjs ` config/scripts/ssh-relay-runtime-manifest-signing-workflow.test.mjs ` config/scripts/ssh-relay-runtime-manifest-aggregate.test.mjs ` config/scripts/ssh-relay-runtime-manifest-tuple.test.mjs ` config/scripts/ssh-relay-runtime-post-sign-metadata.test.mjs ` config/scripts/ssh-relay-runtime-archive-extraction.test.mjs ` config/scripts/ssh-relay-runtime-macos-signing.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-finalization.test.mjs ` config/scripts/ssh-relay-runtime-linux-finalization.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-workflow.test.mjs ` config/scripts/ssh-relay-runtime-native-signing-rehearsal-workflow.test.mjs ` config/scripts/ssh-relay-runtime-build-prerequisite.test.mjs ` config/scripts/ssh-relay-runtime-closure.test.mjs ` config/scripts/ssh-relay-runtime-pty-smoke.test.mjs ` config/scripts/ssh-relay-runtime-reproducibility.test.mjs ` config/scripts/ssh-relay-runtime-resource-diagnostics.test.mjs ` config/scripts/ssh-relay-runtime-provenance.test.mjs ` config/scripts/ssh-relay-runtime-sbom.test.mjs ` config/scripts/ssh-relay-runtime-toolchain.test.mjs ` config/scripts/ssh-relay-runtime-windows-pe-diagnostic.test.mjs ` config/scripts/ssh-relay-runtime-windows-tree.test.mjs ` config/scripts/ssh-relay-runtime-zip.test.mjs ` config/scripts/ssh-relay-runtime-workflow.test.mjs if ($LASTEXITCODE -ne 0) { throw 'runtime artifact contract tests failed' } - name: Download exact Node release inputs shell: pwsh run: | $ErrorActionPreference = 'Stop' $inputs = Join-Path $env:RUNNER_TEMP 'node-release-inputs' New-Item -ItemType Directory -Path $inputs | Out-Null $base = 'https://nodejs.org/dist/v24.18.0' $assets = @( 'SHASUMS256.txt', 'SHASUMS256.txt.sig', '${{ matrix.node_archive }}', 'node-v24.18.0-headers.tar.gz', '${{ matrix.node_library }}' ) foreach ($asset in $assets) { $destination = Join-Path $inputs $asset New-Item -ItemType Directory -Path (Split-Path -Parent $destination) -Force | Out-Null & curl.exe --fail --silent --show-error --location --proto '=https' --tlsv1.2 ` --connect-timeout 20 --max-time 300 --retry 2 --retry-delay 2 --retry-all-errors ` "$base/$asset" --output $destination if ($LASTEXITCODE -ne 0) { throw "Node input download failed: $asset" } } "NODE_INPUTS=$inputs" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - name: Build twice, inspect, smoke, and compare exact runtime shell: pwsh run: | $ErrorActionPreference = 'Stop' $outputRoot = Join-Path $env:RUNNER_TEMP 'ssh-relay-runtime/${{ matrix.tuple }}' New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null $firstOutput = Join-Path $outputRoot 'first' $secondOutput = Join-Path $outputRoot 'second' $workDirectory = Join-Path $env:RUNNER_TEMP 'orca-ssh-relay-runtime-build-work' $sourceCommit = git rev-parse HEAD $sourceDateEpoch = git show -s --format=%ct $sourceCommit foreach ($output in @($firstOutput, $secondOutput)) { "clean_build_output=$output" node config/scripts/build-ssh-relay-runtime.mjs ` --tuple '${{ matrix.tuple }}' ` --inputs-directory $env:NODE_INPUTS ` --output-directory $output ` --work-directory $workDirectory ` --source-date-epoch $sourceDateEpoch ` --git-commit $sourceCommit if ($LASTEXITCODE -ne 0) { throw 'runtime build failed' } $identity = Join-Path $output 'orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json' $archive = Get-ChildItem -LiteralPath $output -Filter '*.zip' -File | Select-Object -First 1 if (-not $archive) { throw 'runtime ZIP is missing' } node config/scripts/verify-ssh-relay-runtime.mjs ` --runtime-directory (Join-Path $output 'runtime') ` --identity $identity ` --archive $archive.FullName if ($LASTEXITCODE -ne 0) { throw 'runtime verification failed' } $prohibited = Get-ChildItem -LiteralPath (Join-Path $output 'runtime') -Recurse -File | Where-Object { $_.Name -in @('npm', 'npm.cmd', 'npx', 'npx.cmd', 'corepack', 'corepack.cmd') -or $_.Name.EndsWith('.map') } | Select-Object -First 1 if ($prohibited) { throw "Runtime contains a prohibited file: $($prohibited.FullName)" } $parcelRoot = Join-Path $output 'runtime/node_modules/@parcel' if (@(Get-ChildItem -LiteralPath $parcelRoot -Directory).Count -ne 2) { throw 'Runtime must contain exactly the watcher wrapper and one native package' } } node config/scripts/ssh-relay-runtime-reproducibility.mjs ` --tuple '${{ matrix.tuple }}' ` --first-output-directory $firstOutput ` --second-output-directory $secondOutput if ($LASTEXITCODE -ne 0) { $firstPe = Join-Path $firstOutput 'runtime/node_modules/node-pty/build/Release/conpty_console_list.node' $secondPe = Join-Path $secondOutput 'runtime/node_modules/node-pty/build/Release/conpty_console_list.node' if ((Test-Path -LiteralPath $firstPe -PathType Leaf) -and (Test-Path -LiteralPath $secondPe -PathType Leaf)) { # Why: rejected PE bytes remain local, but bounded headers/ranges identify the producer drift. node config/scripts/ssh-relay-runtime-windows-pe-diagnostic.mjs ` --first-pe $firstPe ` --second-pe $secondPe if ($LASTEXITCODE -ne 0) { Write-Output 'windows_pe_diagnostic=failed' } if ('${{ matrix.tuple }}' -eq 'win32-arm64') { $llvmObjdump = Get-Command llvm-objdump.exe -ErrorAction SilentlyContinue if ($llvmObjdump) { foreach ($candidate in @( @{ Label = 'first'; Path = $firstPe }, @{ Label = 'second'; Path = $secondPe } )) { "windows_arm64_disassembly=$($candidate.Label)" # Why: the first 512 code bytes classify the repeated instruction without retaining rejected files. & $llvmObjdump.Source --disassemble --start-address=0x180001000 --stop-address=0x180001200 $candidate.Path if ($LASTEXITCODE -ne 0) { Write-Output 'windows_arm64_disassembly=failed' } } } else { Write-Output 'windows_arm64_disassembly=tool-unavailable' } } } else { Write-Output 'windows_pe_diagnostic=skipped_missing_input' } throw 'runtime reproducibility verification failed' } $identity = Join-Path $firstOutput 'orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json' $archive = Get-ChildItem -LiteralPath $firstOutput -Filter '*.zip' -File | Select-Object -First 1 if (-not $archive) { throw 'runtime read-back archive is missing' } $executedRuntime = Join-Path $outputRoot 'readback-executed' # Why: exercise only local verified bytes; release writes and remote consumers stay disconnected. node config/scripts/ssh-relay-runtime-readback-archive-execution.mjs ` --identity $identity ` --archive $archive.FullName ` --output-directory $executedRuntime if ($LASTEXITCODE -ne 0) { throw 'runtime read-back archive execution failed' } Remove-Item -LiteralPath $executedRuntime -Recurse -Force if (Test-Path -LiteralPath $executedRuntime) { throw 'runtime read-back archive execution cleanup failed' } $signingStage = Join-Path $outputRoot 'signing-stage' $signingReport = Join-Path $outputRoot 'orca-ssh-relay-runtime-${{ matrix.tuple }}.signing-stage.json' $reportLines = & node config/scripts/ssh-relay-runtime-native-signing-stage.mjs ` --identity (Join-Path $firstOutput 'orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json') ` --runtime-directory (Join-Path $firstOutput 'runtime') ` --staging-directory $signingStage if ($LASTEXITCODE -ne 0) { throw 'runtime native signing assessment/staging failed' } $reportJson = $reportLines -join [Environment]::NewLine $report = $reportJson | ConvertFrom-Json if ( $report.platform -ne 'win32' -or @($report.assessments).Count -ne 5 -or @($report.signingFiles).Count -ne 3 -or @($report.preservedUpstreamFiles).Count -ne 2 -or -not $report.payload.stagingRequired -or @($report.payload.stagedFiles).Count -ne 3 ) { throw 'Windows signing-stage report violates the Authenticode contract' } $preservedPaths = @($report.preservedUpstreamFiles | ForEach-Object { $_.path }) foreach ($path in @( 'node_modules/node-pty/build/Release/conpty/OpenConsole.exe', 'node_modules/node-pty/build/Release/conpty/conpty.dll' )) { if ($path -notin $preservedPaths) { throw "Required upstream signature was not preserved: $path" } } if (@(Get-ChildItem -LiteralPath $signingStage -Recurse -File).Count -ne 3) { throw 'Windows signing stage must contain exactly three unsigned PE files' } if (Test-Path -LiteralPath (Join-Path $signingStage 'bin/node.exe')) { throw 'Official Node must never enter the Windows signing stage' } [System.IO.File]::WriteAllText( $signingReport, "$reportJson`n", [System.Text.UTF8Encoding]::new($false) ) $reportJson $sourceSignatureReport = Join-Path $outputRoot 'orca-ssh-relay-runtime-${{ matrix.tuple }}.source-signatures.json' $sourceSignatureLines = & node config/scripts/ssh-relay-runtime-windows-source-signature-verification.mjs ` --identity (Join-Path $firstOutput 'orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json') ` --runtime-directory (Join-Path $firstOutput 'runtime') ` --signing-report $signingReport if ($LASTEXITCODE -ne 0) { throw 'runtime Windows source signature verification failed' } $sourceSignatureJson = $sourceSignatureLines -join [Environment]::NewLine $sourceSignature = $sourceSignatureJson | ConvertFrom-Json if ( $sourceSignature.tupleId -ne '${{ matrix.tuple }}' -or @($sourceSignature.verifiedFiles).Count -ne 3 -or @($sourceSignature.verifiedFiles | Where-Object { $_.signerKind -eq 'official-node' }).Count -ne 1 -or @($sourceSignature.verifiedFiles | Where-Object { $_.signerKind -eq 'preserved-upstream' }).Count -ne 2 ) { throw 'Windows source signature report violates the immutable/preserved trust contract' } [System.IO.File]::WriteAllText( $sourceSignatureReport, "$sourceSignatureJson`n", [System.Text.UTF8Encoding]::new($false) ) $sourceSignatureJson Remove-Item -LiteralPath $signingStage -Recurse -Force $evidence = Join-Path $env:GITHUB_WORKSPACE 'runtime-evidence/${{ matrix.tuple }}' New-Item -ItemType Directory -Path $evidence -Force | Out-Null $firstArchive = Get-ChildItem -LiteralPath $firstOutput -Filter '*.zip' -File | Select-Object -First 1 Copy-Item -LiteralPath $firstArchive.FullName -Destination $evidence Get-ChildItem -LiteralPath $firstOutput -File | Where-Object { $_.Name -match '\.(?:spdx|provenance|identity)\.json$' } | Copy-Item -Destination $evidence Copy-Item -LiteralPath $signingReport -Destination $evidence Copy-Item -LiteralPath $sourceSignatureReport -Destination $evidence - name: Measure full-size desktop extraction and cache boundaries shell: pwsh run: | $ErrorActionPreference = 'Stop' $firstOutput = Join-Path $env:RUNNER_TEMP 'ssh-relay-runtime/${{ matrix.tuple }}/first' $identity = Join-Path $firstOutput 'orca-ssh-relay-runtime-${{ matrix.tuple }}.identity.json' $archive = Get-ChildItem -LiteralPath $firstOutput -Filter '*.zip' -File | Select-Object -First 1 if (-not $archive) { throw 'runtime ZIP is missing for desktop extraction measurement' } $extractionOutput = Join-Path $env:RUNNER_TEMP 'ssh-relay-runtime/${{ matrix.tuple }}/desktop-extraction-measurement' # Why: exact payload size can expose memory behavior that synthetic archive fixtures cannot. $env:ORCA_SSH_RELAY_FULL_SIZE_ARCHIVE = $archive.FullName $env:ORCA_SSH_RELAY_FULL_SIZE_IDENTITY = $identity $env:ORCA_SSH_RELAY_FULL_SIZE_OUTPUT = $extractionOutput pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 --reporter=verbose ` src/main/ssh/ssh-relay-artifact-extraction-full-size.test.ts if ($LASTEXITCODE -ne 0) { throw 'full-size desktop extraction measurement failed' } if (Test-Path -LiteralPath $extractionOutput) { throw 'full-size desktop extraction measurement cleanup failed' } pnpm exec vitest run --config config/vitest.config.ts --maxWorkers=1 --reporter=verbose ` src/main/ssh/ssh-relay-artifact-cache-entry-full-size.test.ts if ($LASTEXITCODE -ne 0) { throw 'full-size desktop cache measurement failed' } if (Test-Path -LiteralPath $extractionOutput) { throw 'full-size desktop cache measurement cleanup failed' } - name: Upload unpublished artifact evidence uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: ssh-relay-runtime-${{ matrix.tuple }} path: runtime-evidence/${{ matrix.tuple }}/ if-no-files-found: error retention-days: 7 verify-linux-runtime-baseline-userland: name: Supplement ${{ matrix.tuple }} oldest userland (kernel gap remains) needs: build-posix-runtime # Why: signing rehearsals need candidate bytes, while qualification stays a separate default-on gate. if: github.event_name != 'workflow_call' || inputs.include-baseline-gates strategy: fail-fast: false matrix: include: - runner: ubuntu-24.04 tuple: linux-x64-glibc container_image: docker.io/library/rockylinux@sha256:2d05a9266523bbf24f33ebc3a9832e4d5fd74b973c220f2204ca802286aa275d - runner: ubuntu-24.04-arm tuple: linux-arm64-glibc container_image: docker.io/library/rockylinux@sha256:3c2d0ce12bf79fc5ff05e43b1000e30ff062dc89405525f3307cbff71661f1a0 runs-on: ${{ matrix.runner }} timeout-minutes: 15 env: ORCA_RUNTIME_REQUESTED_RUNNER: ${{ matrix.runner }} steps: - name: Checkout exact source revision uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Setup pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 with: run_install: false - name: Setup Node 24.18.0 uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 24.18.0 cache: pnpm - name: Record supplemental baseline runner identity shell: bash run: | set -euo pipefail printf 'requested_runner=%s\n' "$ORCA_RUNTIME_REQUESTED_RUNNER" printf 'resolved_image_os=%s\n' "${ImageOS:-unknown}" printf 'resolved_image_version=%s\n' "${ImageVersion:-unknown}" printf 'runner_arch=%s\n' "$RUNNER_ARCH" uname -a - name: Install verification dependencies without native postinstall shell: bash run: | set -euo pipefail pnpm install --frozen-lockfile --ignore-scripts git diff --exit-code package.json pnpm-lock.yaml - name: Download exact unpublished runtime evidence uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ssh-relay-runtime-${{ matrix.tuple }} path: runtime-evidence/${{ matrix.tuple }} - name: Verify bytes before supplemental baseline execution shell: bash run: | set -euo pipefail evidence="$GITHUB_WORKSPACE/runtime-evidence/${{ matrix.tuple }}" mapfile -d '' identities < <(find "$evidence" -maxdepth 1 -type f -name '*.identity.json' -print0) mapfile -d '' archives < <(find "$evidence" -maxdepth 1 -type f -name '*.tar.br' -print0) if (( ${#identities[@]} != 1 || ${#archives[@]} != 1 )); then printf 'runtime baseline evidence is ambiguous: identities=%s archives=%s\n' \ "${#identities[@]}" "${#archives[@]}" >&2 exit 1 fi identity=${identities[0]} archive=${archives[0]} runtime="$RUNNER_TEMP/baseline-runtime/${{ matrix.tuple }}" node config/scripts/ssh-relay-runtime-archive-extraction.mjs \ --identity "$identity" \ --archive "$archive" \ --output-directory "$runtime" node config/scripts/verify-ssh-relay-runtime.mjs \ --runtime-directory "$runtime" \ --identity "$identity" \ --archive "$archive" printf 'BASELINE_RUNTIME=%s\n' "$runtime" >> "$GITHUB_ENV" - name: Prove oldest Linux userland and retain the kernel gap shell: bash run: | set -euo pipefail image='${{ matrix.container_image }}' docker pull "$image" docker image inspect "$image" --format 'container_image={{index .RepoDigests 0}}' docker run --rm --network none --read-only --cap-drop all \ --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 2 \ --tmpfs /tmp:rw,nosuid,size=64m \ --mount "type=bind,src=$GITHUB_WORKSPACE,dst=/workspace,readonly" \ --mount "type=bind,src=$BASELINE_RUNTIME,dst=/runtime,readonly" \ --workdir /workspace \ "$image" \ /runtime/bin/node config/scripts/ssh-relay-runtime-smoke-child.cjs /runtime # Why: the container proves glibc/libstdc++ floors but shares the newer hosted kernel. docker run --rm --network none --read-only --cap-drop all \ --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 2 \ --tmpfs /tmp:rw,nosuid,size=64m \ --mount "type=bind,src=$GITHUB_WORKSPACE,dst=/workspace,readonly" \ --mount "type=bind,src=$BASELINE_RUNTIME,dst=/runtime,readonly" \ --workdir /workspace \ "$image" \ /runtime/bin/node config/scripts/ssh-relay-runtime-baseline.mjs \ --tuple '${{ matrix.tuple }}' \ --scope linux-userland \ --runtime-directory /runtime verify-windows-runtime-baseline: name: Verify ${{ matrix.tuple }} oldest Windows baseline needs: build-windows-runtime # Why: a newer hosted image must not block signing evidence or be mistaken for floor qualification. if: github.event_name != 'workflow_call' || inputs.include-baseline-gates strategy: fail-fast: false matrix: include: - runner: windows-2022 tuple: win32-x64 - runner: windows-11-arm tuple: win32-arm64 runs-on: ${{ matrix.runner }} timeout-minutes: 15 env: ORCA_RUNTIME_REQUESTED_RUNNER: ${{ matrix.runner }} steps: - name: Checkout exact source revision uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Setup pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6 with: run_install: false - name: Setup Node 24.18.0 uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 24.18.0 cache: pnpm - name: Record oldest-baseline runner identity shell: pwsh run: | $ErrorActionPreference = 'Stop' "requested_runner=$env:ORCA_RUNTIME_REQUESTED_RUNNER" "resolved_image_os=$env:ImageOS" "resolved_image_version=$env:ImageVersion" "runner_arch=$env:RUNNER_ARCH" Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber - name: Install verification dependencies without native postinstall shell: pwsh run: | $ErrorActionPreference = 'Stop' pnpm install --frozen-lockfile --ignore-scripts if ($LASTEXITCODE -ne 0) { throw 'pnpm install failed' } git diff --exit-code package.json pnpm-lock.yaml if ($LASTEXITCODE -ne 0) { throw 'frozen install changed dependency files' } - name: Download exact unpublished runtime evidence uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ssh-relay-runtime-${{ matrix.tuple }} path: runtime-evidence/${{ matrix.tuple }} - name: Verify bytes and execute on the declared Windows floor shell: pwsh run: | $ErrorActionPreference = 'Stop' $evidence = Join-Path $env:GITHUB_WORKSPACE 'runtime-evidence/${{ matrix.tuple }}' $identities = @(Get-ChildItem -LiteralPath $evidence -Filter '*.identity.json' -File) $archives = @(Get-ChildItem -LiteralPath $evidence -Filter '*.zip' -File) if ($identities.Count -ne 1 -or $archives.Count -ne 1) { throw "runtime baseline evidence is ambiguous: identities=$($identities.Count) archives=$($archives.Count)" } $identity = $identities[0] $archive = $archives[0] $runtime = Join-Path $env:RUNNER_TEMP 'baseline-runtime/${{ matrix.tuple }}' Expand-Archive -LiteralPath $archive.FullName -DestinationPath $runtime node config/scripts/verify-ssh-relay-runtime.mjs ` --runtime-directory $runtime ` --identity $identity.FullName ` --archive $archive.FullName if ($LASTEXITCODE -ne 0) { throw 'runtime baseline verification failed' } node config/scripts/ssh-relay-runtime-baseline.mjs ` --tuple '${{ matrix.tuple }}' ` --scope full if ($LASTEXITCODE -ne 0) { throw 'runner does not prove the declared Windows floor' }