From 184c900132b43b9c3c2fc8c2f188d252652d593b Mon Sep 17 00:00:00 2001 From: Mando Escamilla Date: Thu, 16 Jul 2026 15:19:07 -0500 Subject: [PATCH 1/8] Sign Windows release artifacts with Azure Artifact Signing --- .github/workflows/release.yml | 113 +++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 74e47dc6..639c2f91 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,27 +1,32 @@ name: Release -# Builds and (when the signing secrets are configured) signs the distributable artifacts: +# Builds and signs the distributable artifacts: # - npm: per-OS prebuilt binaries published under @agentdeploymentco/argus* # - macOS: a signed + notarized Argus.app / .dmg -# - Windows: NSIS installers (.exe) for x64 and ARM64 (Authenticode signing not wired up yet) +# - Windows: Azure Artifact Signing-signed NSIS installers (.exe) for x64 and ARM64 # # Two modes, decided by what triggered the run: # - Tag push: build everything, upload to a DRAFT GitHub Release, and publish it only after # every platform leg has succeeded (the publish-release job below). # - workflow_dispatch (any branch): build-only dry run — no tag, no release; the bundles are # uploaded as workflow artifacts so they can be downloaded and tested on real machines. -# All signing is gated on secrets being present, so this workflow runs end-to-end (producing -# UNSIGNED artifacts) before any certificate exists, and starts signing the moment the secrets are -# added. No secret value ever lives in this file — only references to `secrets.*`. +# Windows signing uses GitHub Actions OIDC with Azure Artifact Signing. The Windows jobs fail +# clearly if the Azure identity or signing configuration is missing, so a tagged release cannot +# silently publish unsigned installers. No secret value ever lives in this file — only references +# to `secrets.*`. # -# Required secrets to activate signing (set in repo/org settings; never commit their values): +# Required secrets (set in repo/org settings; never commit their values): # NPM_TOKEN npm automation token (npm publish) # APPLE_CERTIFICATE base64 of the Developer ID Application .p12 # APPLE_CERTIFICATE_PASSWORD its export password # APPLE_SIGNING_IDENTITY e.g. "Developer ID Application: … (TEAMID)" # APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID notarytool credentials (app-specific password) -# WINDOWS_CERTIFICATE / WINDOWS_CERTIFICATE_PASSWORD Authenticode signing cert (base64 + pwd; -# reserved — the Windows jobs don't sign yet) +# AZURE_CLIENT_ID / AZURE_TENANT_ID / AZURE_SUBSCRIPTION_ID +# GitHub OIDC identity used by Azure Artifact Signing +# Required repository or organization variables: +# AZURE_ARTIFACT_SIGNING_ENDPOINT Region-specific codesigning.azure.net endpoint +# AZURE_ARTIFACT_SIGNING_ACCOUNT Artifact Signing account name +# AZURE_ARTIFACT_SIGNING_PROFILE Certificate profile name # TAURI_SIGNING_PRIVATE_KEY / TAURI_SIGNING_PRIVATE_KEY_PASSWORD # updater signing key (separate from OS certs) @@ -97,6 +102,30 @@ jobs: - name: Stage sidecar + web run: bash scripts/stage-desktop-sidecar.sh ${{ matrix.stage-args }} + - name: Azure login for Artifact Signing + if: runner.os == 'Windows' + uses: azure/login@v3 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + # Sign the CLI sidecar before Tauri embeds it in the installed application. The ARM64 + # sidecar is signed on this x64 Windows runner because Artifact Signing does not support + # Windows ARM runners. + - name: Sign Windows sidecar + if: runner.os == 'Windows' + uses: azure/artifact-signing-action@v2 + with: + endpoint: ${{ vars.AZURE_ARTIFACT_SIGNING_ENDPOINT }} + signing-account-name: ${{ vars.AZURE_ARTIFACT_SIGNING_ACCOUNT }} + certificate-profile-name: ${{ vars.AZURE_ARTIFACT_SIGNING_PROFILE }} + files-folder: ${{ github.workspace }}\desktop\src-tauri\binaries + files-folder-filter: exe + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + - name: import Apple Developer Certificate if: runner.os == 'macOS' # Prevents keychain from locking automatically for 3600 seconds. @@ -122,7 +151,7 @@ jobs: echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV echo "Certificate imported." - - name: Build (and sign, if secrets present) the desktop app + - name: Build the desktop app uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -183,6 +212,72 @@ jobs: exit 1 fi + - name: Locate Windows installers + if: runner.os == 'Windows' + id: windows_installers + shell: pwsh + run: | + $installers = @(Get-ChildItem -Path "$env:GITHUB_WORKSPACE\desktop\src-tauri\target" -Filter "*-setup.exe" -Recurse | + Select-Object -ExpandProperty FullName + ) + if ($installers.Count -eq 0) { + throw "No Windows installer was found to sign." + } + "files< Date: Thu, 16 Jul 2026 16:16:17 -0500 Subject: [PATCH 2/8] Use production environment for release builds --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 639c2f91..a50e085c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,6 +71,7 @@ jobs: args: "--target aarch64-pc-windows-msvc --bundles nsis" name: ${{ matrix.name }} runs-on: ${{ matrix.platform }} + environment: production env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} From 4f53900fc9f32c04ac3ad5d5b5fcdd1600343061 Mon Sep 17 00:00:00 2001 From: Mando Escamilla Date: Thu, 16 Jul 2026 16:31:12 -0500 Subject: [PATCH 3/8] Read Artifact Signing configuration from secrets --- .github/workflows/release.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a50e085c..fb5f0575 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ name: Release # APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID notarytool credentials (app-specific password) # AZURE_CLIENT_ID / AZURE_TENANT_ID / AZURE_SUBSCRIPTION_ID # GitHub OIDC identity used by Azure Artifact Signing -# Required repository or organization variables: +# Required repository or organization secrets: # AZURE_ARTIFACT_SIGNING_ENDPOINT Region-specific codesigning.azure.net endpoint # AZURE_ARTIFACT_SIGNING_ACCOUNT Artifact Signing account name # AZURE_ARTIFACT_SIGNING_PROFILE Certificate profile name @@ -118,9 +118,9 @@ jobs: if: runner.os == 'Windows' uses: azure/artifact-signing-action@v2 with: - endpoint: ${{ vars.AZURE_ARTIFACT_SIGNING_ENDPOINT }} - signing-account-name: ${{ vars.AZURE_ARTIFACT_SIGNING_ACCOUNT }} - certificate-profile-name: ${{ vars.AZURE_ARTIFACT_SIGNING_PROFILE }} + endpoint: ${{ secrets.AZURE_ARTIFACT_SIGNING_ENDPOINT }} + signing-account-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_ACCOUNT }} + certificate-profile-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_PROFILE }} files-folder: ${{ github.workspace }}\desktop\src-tauri\binaries files-folder-filter: exe file-digest: SHA256 @@ -232,9 +232,9 @@ jobs: if: runner.os == 'Windows' uses: azure/artifact-signing-action@v2 with: - endpoint: ${{ vars.AZURE_ARTIFACT_SIGNING_ENDPOINT }} - signing-account-name: ${{ vars.AZURE_ARTIFACT_SIGNING_ACCOUNT }} - certificate-profile-name: ${{ vars.AZURE_ARTIFACT_SIGNING_PROFILE }} + endpoint: ${{ secrets.AZURE_ARTIFACT_SIGNING_ENDPOINT }} + signing-account-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_ACCOUNT }} + certificate-profile-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_PROFILE }} files: ${{ steps.windows_installers.outputs.files }} file-digest: SHA256 timestamp-rfc3161: http://timestamp.acs.microsoft.com From 9d65ed651a53a22f657eecb2c03ab306f793a37c Mon Sep 17 00:00:00 2001 From: Mando Escamilla Date: Fri, 17 Jul 2026 09:39:16 -0500 Subject: [PATCH 4/8] Add flat Windows release archives --- .github/workflows/release.yml | 59 +++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb5f0575..9bb1f880 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release # Builds and signs the distributable artifacts: # - npm: per-OS prebuilt binaries published under @agentdeploymentco/argus* # - macOS: a signed + notarized Argus.app / .dmg -# - Windows: Azure Artifact Signing-signed NSIS installers (.exe) for x64 and ARM64 +# - Windows: standalone CLI ZIPs plus Azure Artifact Signing-signed NSIS installers (.exe) # # Two modes, decided by what triggered the run: # - Tag push: build everything, upload to a DRAFT GitHub Release, and publish it only after @@ -56,6 +56,7 @@ jobs: # Windows x64, built natively on the x64 runner. - name: Windows x64 slug: windows-x64 + archive_arch: amd64 platform: "windows-latest" rust-targets: "" stage-args: "--build" @@ -65,6 +66,7 @@ jobs: # `bun build --compile --target=bun-windows-arm64` (stage-desktop-sidecar.sh --target). - name: Windows ARM64 slug: windows-arm64 + archive_arch: arm64 platform: "windows-latest" rust-targets: "aarch64-pc-windows-msvc" stage-args: "--target aarch64-pc-windows-msvc" @@ -260,6 +262,44 @@ jobs: Write-Host "$($artifact.Name): $($signature.Status) ($($signature.SignerCertificate.Subject))" } + # Package the signed CLI sidecar with the web app so Windows users can run Argus without + # installing the desktop shell. Keep both files at the archive root for easy extraction. + - name: Package Windows CLI archive + if: runner.os == 'Windows' + shell: pwsh + run: | + $archiveDir = Join-Path $env:RUNNER_TEMP "argus-cli-archive" + $releaseDir = Join-Path $env:GITHUB_WORKSPACE "release-artifacts" + Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $archiveDir + New-Item -ItemType Directory -Force -Path $archiveDir, $releaseDir | Out-Null + + $binaries = @(Get-ChildItem -Path "$env:GITHUB_WORKSPACE\desktop\src-tauri\binaries" -Filter "*.exe") + if ($binaries.Count -ne 1) { + throw "Expected one Windows CLI binary, found $($binaries.Count)." + } + Copy-Item -LiteralPath $binaries[0].FullName -Destination (Join-Path $archiveDir "argus.exe") + Copy-Item -Recurse -LiteralPath "$env:GITHUB_WORKSPACE\desktop\src-tauri\web" -Destination (Join-Path $archiveDir "web") + + $version = (Get-Content "$env:GITHUB_WORKSPACE\desktop\src-tauri\tauri.conf.json" -Raw | ConvertFrom-Json).version + $archive = Join-Path $releaseDir "argus_${version}_windows_${{ matrix.archive_arch }}.zip" + Compress-Archive -Path (Join-Path $archiveDir "argus.exe"), (Join-Path $archiveDir "web") -DestinationPath $archive -Force + Write-Host "Created $archive" + + - name: Upload Windows CLI archive to release + if: startsWith(github.ref, 'refs/tags/') && runner.os == 'Windows' + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: | + $version = (Get-Content "desktop/src-tauri/tauri.conf.json" -Raw | ConvertFrom-Json).version + $tag = "argus-v$version" + $archive = Get-ChildItem -Path "$env:GITHUB_WORKSPACE\release-artifacts" -Filter "*.zip" | + Select-Object -First 1 -ExpandProperty FullName + if (-not $archive) { + throw "No Windows CLI archive was found to upload." + } + gh release upload $tag --repo $env:GITHUB_REPOSITORY --clobber $archive + # tauri-action uploads the unsigned bundle while creating the draft release. Replace that # asset with the signed file before publish-release makes the draft public. workflow_dispatch # has no release, so its upload-artifact step below receives the signed file directly. @@ -279,6 +319,21 @@ jobs: } gh release upload $tag --repo $env:GITHUB_REPOSITORY --clobber $installers + # Actions downloads are ZIP archives. Stage the release files at the archive root so + # workflow_dispatch users do not need to navigate the Rust target directory structure. + - name: Stage Windows installer for workflow artifact + if: ${{ !startsWith(github.ref, 'refs/tags/') && runner.os == 'Windows' }} + shell: pwsh + run: | + $destination = Join-Path $env:GITHUB_WORKSPACE "release-artifacts" + $installers = @(Get-ChildItem -Path "$env:GITHUB_WORKSPACE\desktop\src-tauri\target" -Filter "*-setup.exe" -Recurse) + if ($installers.Count -eq 0) { + throw "No Windows installer was found to package." + } + foreach ($installer in $installers) { + Copy-Item -LiteralPath $installer.FullName -Destination $destination -Force + } + # Dry-run mode only: expose the bundles on the workflow run so they can be # downloaded and installed on real machines before any tag exists. - name: Upload bundles as workflow artifacts @@ -290,7 +345,7 @@ jobs: path: | desktop/src-tauri/target/**/bundle/macos/*.app.tar.gz* desktop/src-tauri/target/**/bundle/dmg/*.dmg - desktop/src-tauri/target/**/bundle/nsis/*-setup.exe* + release-artifacts/* # Tag pushes create the release as a draft; flip it public only when every platform leg # succeeded, so a failed leg can never leave a half-populated public release (and the From 740dc98ca927fc23dbcd931b789c0a1c98956fe5 Mon Sep 17 00:00:00 2001 From: Mando Escamilla Date: Fri, 17 Jul 2026 10:38:42 -0500 Subject: [PATCH 5/8] Limit workflow artifacts to desktop installers --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9bb1f880..7ed7e1c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -334,18 +334,18 @@ jobs: Copy-Item -LiteralPath $installer.FullName -Destination $destination -Force } - # Dry-run mode only: expose the bundles on the workflow run so they can be - # downloaded and installed on real machines before any tag exists. - - name: Upload bundles as workflow artifacts + # Dry-run mode only: expose the desktop installers on the workflow run so they can be + # downloaded and tested on real machines before any tag exists. Standalone Windows CLI + # ZIPs are published only on tagged releases, not as workflow artifacts. + - name: Upload desktop installers as workflow artifacts if: ${{ !startsWith(github.ref, 'refs/tags/') }} uses: actions/upload-artifact@v4 with: name: argus-${{ matrix.slug }} if-no-files-found: error path: | - desktop/src-tauri/target/**/bundle/macos/*.app.tar.gz* desktop/src-tauri/target/**/bundle/dmg/*.dmg - release-artifacts/* + release-artifacts/*-setup.exe # Tag pushes create the release as a draft; flip it public only when every platform leg # succeeded, so a failed leg can never leave a half-populated public release (and the From b5aadf67d0d9d200d7017017e8108f91f9534275 Mon Sep 17 00:00:00 2001 From: Mando Escamilla Date: Fri, 17 Jul 2026 10:46:29 -0500 Subject: [PATCH 6/8] Use Node 24 artifact upload action --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7ed7e1c5..97c73ec0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -339,7 +339,7 @@ jobs: # ZIPs are published only on tagged releases, not as workflow artifacts. - name: Upload desktop installers as workflow artifacts if: ${{ !startsWith(github.ref, 'refs/tags/') }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: argus-${{ matrix.slug }} if-no-files-found: error From f1d9fc2de59dd6b816d2fc8cdab176ec96f622ab Mon Sep 17 00:00:00 2001 From: Mando Escamilla Date: Fri, 17 Jul 2026 10:51:02 -0500 Subject: [PATCH 7/8] Upgrade GitHub workflow actions --- .github/workflows/ci.yml | 2 +- .github/workflows/docs.yml | 8 ++++---- .github/workflows/release.yml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 305daf77..95b8aea3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: name: Typecheck & test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: bun-version: latest diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f3d379d8..5e73b63b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -29,7 +29,7 @@ jobs: name: Build VitePress site runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: # Release events check out the release tag by default. The generated changelog is # committed to the default branch, so always build and update main. @@ -38,7 +38,7 @@ jobs: - uses: oven-sh/setup-bun@v2 with: bun-version: latest - - uses: actions/configure-pages@v5 + - uses: actions/configure-pages@v6 - run: bun install --frozen-lockfile - name: Refresh changelog env: @@ -67,7 +67,7 @@ jobs: # browser. Absent -> analytics stays a silent no-op. Same var name as # the adc.co site, so one org value can feed both. PUBLIC_POSTHOG_PROJECT_TOKEN: ${{ vars.PUBLIC_POSTHOG_PROJECT_TOKEN }} - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@v5 with: path: docs/.vitepress/dist @@ -80,4 +80,4 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 97c73ec0..82f98986 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,7 +83,7 @@ jobs: # cross-volume moves fail when `bun build --compile --target=bun-windows-arm64` populates it. BUN_INSTALL_CACHE_DIR: ${{ github.workspace }}/.bun-cache steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: bun-version: latest @@ -360,7 +360,7 @@ jobs: GH_TOKEN: ${{ github.token }} HAS_UPDATER_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY != '' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Publish the draft release run: | set -euo pipefail From e24f26318fc308490566a0b960449d7f4cd1c5f3 Mon Sep 17 00:00:00 2001 From: Mando Escamilla Date: Fri, 17 Jul 2026 11:18:36 -0500 Subject: [PATCH 8/8] Upload desktop installers without archive wrappers --- .github/workflows/release.yml | 45 ++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 82f98986..fb8928ed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -319,33 +319,60 @@ jobs: } gh release upload $tag --repo $env:GITHUB_REPOSITORY --clobber $installers - # Actions downloads are ZIP archives. Stage the release files at the archive root so - # workflow_dispatch users do not need to navigate the Rust target directory structure. + # Stage the Windows installer with a stable name so archive:false can upload the raw file. - name: Stage Windows installer for workflow artifact if: ${{ !startsWith(github.ref, 'refs/tags/') && runner.os == 'Windows' }} shell: pwsh run: | $destination = Join-Path $env:GITHUB_WORKSPACE "release-artifacts" + $filename = "argus-${{ matrix.slug }}-setup.exe" $installers = @(Get-ChildItem -Path "$env:GITHUB_WORKSPACE\desktop\src-tauri\target" -Filter "*-setup.exe" -Recurse) if ($installers.Count -eq 0) { throw "No Windows installer was found to package." } + if ($installers.Count -ne 1) { + throw "Expected one Windows installer, found $($installers.Count)." + } foreach ($installer in $installers) { - Copy-Item -LiteralPath $installer.FullName -Destination $destination -Force + Copy-Item -LiteralPath $installer.FullName -Destination (Join-Path $destination $filename) -Force } + # The artifact action can upload one file without wrapping it in a ZIP. Give the DMG a + # stable name because archive:false uses the file name as the artifact name. + - name: Stage macOS DMG for workflow artifact + if: ${{ !startsWith(github.ref, 'refs/tags/') && runner.os == 'macOS' }} + shell: bash + run: | + set -euo pipefail + destination="$GITHUB_WORKSPACE/release-artifacts/argus-macos-universal.dmg" + mkdir -p "$(dirname "$destination")" + dmg_count="$(find desktop/src-tauri/target -path '*/bundle/dmg/*.dmg' -type f | wc -l | tr -d '[:space:]')" + if [ "$dmg_count" -ne 1 ]; then + echo "Expected one macOS DMG, found $dmg_count." >&2 + exit 1 + fi + dmg="$(find desktop/src-tauri/target -path '*/bundle/dmg/*.dmg' -type f -print -quit)" + cp "$dmg" "$destination" + echo "Staged $destination" + # Dry-run mode only: expose the desktop installers on the workflow run so they can be # downloaded and tested on real machines before any tag exists. Standalone Windows CLI # ZIPs are published only on tagged releases, not as workflow artifacts. - - name: Upload desktop installers as workflow artifacts - if: ${{ !startsWith(github.ref, 'refs/tags/') }} + - name: Upload macOS DMG as workflow artifact + if: ${{ !startsWith(github.ref, 'refs/tags/') && runner.os == 'macOS' }} + uses: actions/upload-artifact@v7 + with: + if-no-files-found: error + path: release-artifacts/argus-macos-universal.dmg + archive: false + + - name: Upload Windows installer as workflow artifact + if: ${{ !startsWith(github.ref, 'refs/tags/') && runner.os == 'Windows' }} uses: actions/upload-artifact@v7 with: - name: argus-${{ matrix.slug }} if-no-files-found: error - path: | - desktop/src-tauri/target/**/bundle/dmg/*.dmg - release-artifacts/*-setup.exe + path: release-artifacts/argus-${{ matrix.slug }}-setup.exe + archive: false # Tag pushes create the release as a draft; flip it public only when every platform leg # succeeded, so a failed leg can never leave a half-populated public release (and the