projection: begin the exact-path public epoch

The public tree and its history contain only the listed paths. Earlier projection history remains preserved internally.

Source-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Policy-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Tree-Digest: 180ae530c1058a2a5c89837bdce2d323ae83e669e38590ca72e75b8d92b7262f
This commit is contained in:
Fimeg 2026-09-08 21:59:33 -04:00
commit 67e26be2d9
838 changed files with 157302 additions and 0 deletions

View file

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
RedFlagSetup.msi — native Windows installer for the RedFlag server (INSTALL-002).
Built with `wixl` (msitools), NOT the official WiX Toolset .NET CLI. The
official `wix build` genuinely does not work when the compiler itself runs
on Linux — reproduced empirically: even a single-character Directory/@Name
fails WIX0389 "not a relative path" on every WiX version tried (4.0.5,
4.0.6, 5.0.0, 5.0.2, 6.0.0, 6.0.1). The tool's own output says why:
"warning WIX0000: The WiX Toolset only supports Windows... All behavior
after this point is undefined." That's not a disclaimer to route around
with a workaround — the validator itself is broken cross-platform. wixl is
a from-scratch, Linux-native reimplementation of the (older, WiX v3-era)
MSI-building grammar, built for exactly this "generate a real Windows MSI
from Linux CI" case, and produces a structurally valid MSI (verified with
msiinfo tables / msiinfo suminfo) with no cross-platform path bugs. This
file therefore uses the WiX v3 schema/dialect (Product+Package as separate
elements, explicit Directory nesting, no StandardDirectory shorthand) —
wixl's grammar, not v4's.
Scope (v1): install the already-built, already-signed server binary as a
Windows service, with a forward-only upgrade guard. It does NOT bundle
PostgreSQL — docker-compose doesn't either (postgres:16-alpine is a
separate container); a native install is expected to point at a reachable
Postgres the same way the docker path does via config/.env. The installer's
job ends where the docker quick-start's does today: "service is running,
go to http://localhost:<port>/setup and finish configuration in the
browser" (README.md's existing docker flow, same UX, different transport).
Config: the Go binary only ever reads OS environment variables
(server/internal/config/config.go). Docker gets those from compose's
env_file:; a native install has no compose layer, so this installer drops
a template env file under ProgramData and the service reads it via
config.loadNativeConfigFile() — same manual "generate keys, paste into the
env file, restart" flow the docker README already documents, not a new UX.
NOT YET BUILT into this file: install-time manifest signature verification
(fetch the release manifest, verify Ed25519 signature, check the server
binary's SHA256 before executing) — the cold-start TOFU problem flagged in
docs/tasks/INSTALL-002-native-os-installers.md. This installer currently
trusts whatever binary CI hands it, same trust level as unzipping a release
tarball by hand. That verification layer is the next slice, not this one.
VERIFIED: built successfully with wixl 0.106 against a real cross-compiled
redflag-server-windows-amd64.exe on 2026-07-01 — msiinfo confirms a valid
MSI (Directory/Component/Feature/ServiceInstall/ServiceControl tables all
populated correctly). NOT yet verified: actually running the installer on
a real Windows host (install, service starts, uninstall, upgrade-over
itself). That's the next check, ideally on real hardware or a VM.
-->
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!--
UpgradeCode must never change for the life of this product — it's the
identity that makes the MajorUpgrade forward-only guard below mean
anything across releases. Generated once, fixed forever after.
-->
<Product Id="*"
Name="RedFlag Server"
Language="1033"
Version="$(var.RedFlagVersion)"
Manufacturer="Fimeg"
UpgradeCode="8f3c9e6a-4b1d-4a7f-9c2e-1d6a8b2f5e91">
<Package InstallerVersion="500" Compressed="yes" InstallScope="perMachine"
Description="RedFlag self-hosted update management — server install" />
<!--
Forward-only, no downgrade — ETHOS doctrine elsewhere in this codebase
(refresh-token rotation, capability tokens) applied to the installer
itself. Windows Installer only compares the first three version
fields for upgrade detection — pass $(var.RedFlagVersion) as the
3-part form from CI, same 3-vs-4-part reconciliation bump-version.sh
already does for desktop/Cargo.toml vs tauri.conf.json.
-->
<MajorUpgrade DowngradeErrorMessage="A newer version of RedFlag Server is already installed. Forward-only upgrades only." />
<MediaTemplate EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLFOLDER" Name="RedFlag">
<Component Id="ServerBinary" Guid="7a2d5e9c-3f81-4c6b-9e0a-2b7d4f8c1a63">
<File Id="RedFlagServerExe"
Source="dist/redflag-server-windows-amd64.exe"
Name="redflag-server.exe"
KeyPath="yes" />
<!--
LocalSystem for v1 — matches "get it working" over
least-privilege for now. The Linux side went through the same
arc (helper started as a plain child before the systemd-run
privilege split). Revisit alongside SEC-030 once a Windows
privileged-executor model exists to hand mutation off to
instead of running the whole server as SYSTEM.
-->
<ServiceInstall Id="RedFlagServerService"
Name="RedFlagServer"
DisplayName="RedFlag Server"
Description="RedFlag self-hosted update management server"
Type="ownProcess"
Start="auto"
ErrorControl="normal"
Account="LocalSystem" />
<ServiceControl Id="RedFlagServerServiceControl"
Name="RedFlagServer"
Start="install"
Stop="both"
Remove="uninstall"
Wait="yes" />
</Component>
</Directory>
</Directory>
<!--
ProgramData, not Program Files: this holds the mutable,
operator-edited config (DB connection, admin creds, signing key
once generated) — it must survive upgrades/reinstalls, which
Program Files contents don't reliably. Only the .example template
ships here; the real redflag.env is created by the operator (or a
future first-run helper), never overwritten by this installer once
it exists.
-->
<Directory Id="CommonAppDataFolder">
<Directory Id="REDFLAGDATA" Name="RedFlag">
<Component Id="ConfigTemplate" Guid="1c6e8a2d-9b47-4f3e-a1c8-6d3e9b4a2f70">
<File Id="RedFlagEnvExample"
Source="config/redflag.env.example"
Name="redflag.env.example"
KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="ServerFeature" Level="1">
<ComponentRef Id="ServerBinary" />
<ComponentRef Id="ConfigTemplate" />
</Feature>
<!--
Desktop tray feature slots in here later (Casey, 2026-06-30: "checkbox
for adding the desktop alongside it" plus future screen-capture work
from the session-broker RAF). Deliberately not stubbed as a disabled
checkbox — a checkbox that does nothing when checked is worse than no
checkbox. Wire it once the desktop/Tauri artifact has a real merge
path into this installer, per Casey's "keep it separate for now."
-->
</Product>
</Wix>

View file

@ -0,0 +1,64 @@
# Native Windows CI entry point. Run from an x64 MSVC developer environment
# with Rust, Go and Qt 6 on PATH. Produces the complete Desktop payload;
# setup must package the entire directory, not just redflag-desktop.exe.
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)][string]$OutputDirectory,
[Parameter(Mandatory = $true)][ValidatePattern('^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?$')][string]$Version
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
if ($env:OS -ne 'Windows_NT') { throw 'This build requires a native Windows runner.' }
foreach ($tool in @('cargo', 'rustc', 'go', 'cl', 'windeployqt')) {
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) { throw "Missing build tool: $tool" }
}
$rustInfo = & rustc -vV
if ($LASTEXITCODE -ne 0 -or -not ($rustInfo -match 'host: x86_64-pc-windows-msvc')) {
throw 'Select the x86_64-pc-windows-msvc Rust toolchain.'
}
$root = (Resolve-Path (Join-Path $PSScriptRoot '../..')).Path
$expectedVersion = $Version
$output = [IO.Path]::GetFullPath($OutputDirectory)
if (Test-Path $output) { throw 'Output directory must be new; refusing to mix payload versions.' }
New-Item -ItemType Directory -Path $output | Out-Null
Push-Location (Join-Path $root 'desktop')
$previousVersion = $env:REDFLAG_RELEASE_VERSION
try {
$env:REDFLAG_RELEASE_VERSION = $Version
& cargo build --release --locked
if ($LASTEXITCODE -ne 0) { throw 'Desktop build failed.' }
Copy-Item 'target/release/redflag-desktop.exe' $output
} finally { $env:REDFLAG_RELEASE_VERSION = $previousVersion; Pop-Location }
Push-Location (Join-Path $root 'agent')
try {
& go build -trimpath -ldflags "-X github.com/Fimeg/RedFlag/agent/internal/version.Version=$Version" -o (Join-Path $output 'redflag-agent.exe') ./cmd/agent/
if ($LASTEXITCODE -ne 0) { throw 'Agent build failed.' }
} finally { Pop-Location }
$desktop = Join-Path $output 'redflag-desktop.exe'
& windeployqt --release --qmldir (Join-Path $root 'desktop/qml') --dir $output $desktop
if ($LASTEXITCODE -ne 0) { throw 'Qt runtime deployment failed.' }
foreach ($relative in @('Qt6Core.dll', 'Qt6Gui.dll', 'Qt6Qml.dll', 'Qt6Quick.dll', 'platforms/qwindows.dll')) {
if (-not (Test-Path (Join-Path $output $relative))) { throw "Qt payload is missing $relative" }
}
Copy-Item (Join-Path $root 'LICENSE') (Join-Path $output 'LICENSE.txt')
Copy-Item (Join-Path $root 'THIRD_PARTY_LICENSES.md') $output
# Remove the build toolchain from DLL lookup for the version check. A clean
# Windows VM must additionally launch QML and exercise the Agent pipe.
$buildPath = $env:PATH
try {
$env:PATH = "$env:SystemRoot\System32;$env:SystemRoot"
$reportedVersion = & $desktop --version
if ($LASTEXITCODE -ne 0 -or $reportedVersion -ne "RedFlag v$expectedVersion") {
throw 'Staged Desktop failed its version check without the Qt build PATH.'
}
} finally { $env:PATH = $buildPath }
$files = @(Get-ChildItem $output -Recurse -File | Sort-Object FullName | ForEach-Object {
[ordered]@{
path = $_.FullName.Substring($output.Length + 1).Replace('\', '/')
sha256 = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
size = $_.Length
}
})
[ordered]@{ desktop_version = $reportedVersion; files = $files } |
ConvertTo-Json -Depth 4 | Set-Content (Join-Path $output 'payload.json') -Encoding UTF8
Write-Output "Desktop payload staged at $output; this is not yet an installer or an installation test."

66
installer/windows/build-msi.sh Executable file
View file

@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Canonical CI entry point for the RedFlag Server MSI custody build.
set -euo pipefail
[ "$#" -eq 6 ] || {
echo "usage: $0 <server-exe> <redflag-version> <output-msi> <proof-json> <source-commit> <source-ref>" >&2
exit 2
}
SERVER_EXE="$1"
REDFLAG_VERSION="$2"
OUTPUT_MSI="$3"
PROOF_JSON="$4"
SOURCE_COMMIT="$5"
SOURCE_REF="$6"
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
MSI_VERSION=$(printf '%s\n' "$REDFLAG_VERSION" | cut -d. -f1-3)
for tool in jq msiinfo msiextract wixl; do
command -v "$tool" >/dev/null || { echo "$tool not found" >&2; exit 2; }
done
[ -f "$SERVER_EXE" ] || { echo "no such Server executable: $SERVER_EXE" >&2; exit 2; }
SERVER_EXE=$(realpath "$SERVER_EXE")
OUTPUT_MSI=$(realpath -m "$OUTPUT_MSI")
PROOF_JSON=$(realpath -m "$PROOF_JSON")
mkdir -p "$ROOT/installer/windows/dist" "$(dirname "$OUTPUT_MSI")" "$(dirname "$PROOF_JSON")"
cp "$SERVER_EXE" "$ROOT/installer/windows/dist/redflag-server-windows-amd64.exe"
(
cd "$ROOT/installer/windows"
wixl Product.wxs -D RedFlagVersion="$MSI_VERSION" -o "$OUTPUT_MSI"
)
CAB_FILE=$(find "$(dirname "$OUTPUT_MSI")" "$ROOT/installer/windows" \
-maxdepth 2 -type f -name '*.cab' -print -quit)
if [ -n "$CAB_FILE" ]; then
echo "[FAIL] external cabinet produced"
find "$(dirname "$OUTPUT_MSI")" "$ROOT/installer/windows" \
-maxdepth 2 -type f -name '*.cab' -print
exit 1
fi
"$ROOT/installer/windows/verify-msi.sh" \
"$OUTPUT_MSI" \
"$ROOT/installer/windows/dist/redflag-server-windows-amd64.exe" \
"$ROOT/installer/windows/config/redflag.env.example" \
--redflag-version "$REDFLAG_VERSION" \
--msi-version "$MSI_VERSION" \
--source-commit "$SOURCE_COMMIT" \
--source-ref "$SOURCE_REF" \
--proof-record "$PROOF_JSON"
FIXTURE="$ROOT/installer/windows/testdata/run3181-hollow.msi"
if "$ROOT/installer/windows/verify-msi.sh" "$FIXTURE" \
"$ROOT/installer/windows/dist/redflag-server-windows-amd64.exe" \
"$ROOT/installer/windows/config/redflag.env.example" > /tmp/hollow-msi.log 2>&1; then
echo "[FAIL] hollow MSI regression fixture passed custody verification"
cat /tmp/hollow-msi.log
exit 1
fi
grep -E '^\[FAIL\]|^RESULT:' /tmp/hollow-msi.log
echo "Hollow MSI regression fixture rejected, as required."
echo "Installer custody proof record:"
jq . "$PROOF_JSON"

View file

@ -0,0 +1,43 @@
# ============================================================
# RedFlag Server — native Windows configuration
# Copy this file to: redflag.env (same folder), edit it, then
# restart the "RedFlag Server" service (services.msc or:
# net stop RedFlagServer && net start RedFlagServer
# ============================================================
# This is the native-install equivalent of config/.env.example
# in the main repo — same variables, same semantics. Docker gets
# these from docker-compose's env_file:; this binary reads this
# file directly (server/internal/config/config.go loadNativeConfigFile).
# --- PostgreSQL ---
# Point this at a Postgres instance you provide — same as the docker
# stack's postgres:16-alpine container, just not bundled by this installer.
REDFLAG_DB_HOST=localhost
REDFLAG_DB_PORT=5432
REDFLAG_DB_NAME=redflag
REDFLAG_DB_USER=redflag
REDFLAG_DB_PASSWORD=CHANGE_ME_pick_a_strong_db_password
# --- Server ---
REDFLAG_SERVER_HOST=0.0.0.0
REDFLAG_SERVER_PORT=8080
# --- Admin Account ---
# After first-run setup these are persisted in the database;
# these values re-apply on service restart.
REDFLAG_ADMIN_USER=admin
REDFLAG_ADMIN_EMAIL=admin@example.com
REDFLAG_ADMIN_PASSWORD=CHANGE_ME_pick_a_strong_admin_password
REDFLAG_JWT_SECRET=CHANGE_ME_random_string_at_least_32_characters_long
# --- Agent Registration ---
REDFLAG_TOKEN_EXPIRY=24h
REDFLAG_MAX_TOKENS=100
REDFLAG_MAX_SEATS=50
# --- Ed25519 Signing Key ---
# Leave blank on first run. Go to http://localhost:8080/setup and click
# "Generate Keys". Copy the private key here, then restart the service.
# Without this, agents cannot receive signed commands or upgrade themselves.
# BACKUP THE PRIVATE KEY. Losing it means re-enrolling every agent.
REDFLAG_SIGNING_PRIVATE_KEY=

Binary file not shown.

179
installer/windows/verify-msi.sh Executable file
View file

@ -0,0 +1,179 @@
#!/usr/bin/env bash
# Prove that a RedFlag Server MSI contains the exact bytes staged for it.
# MSI compressed size is diagnostic only; it is never an acceptance gate.
set -uo pipefail
MSI="${1:-}"
STAGED_EXE="${2:-}"
STAGED_ENV="${3:-}"
shift "$(( $# >= 3 ? 3 : $# ))"
REDFLAG_VERSION=""
MSI_VERSION=""
PROOF_RECORD=""
SOURCE_COMMIT="${GITHUB_SHA:-}"
SOURCE_REF="${GITHUB_REF:-}"
while [ "$#" -gt 0 ]; do
[ "$#" -ge 2 ] || { echo "missing value for $1" >&2; exit 2; }
case "$1" in
--redflag-version) REDFLAG_VERSION="${2:-}"; shift 2 ;;
--msi-version) MSI_VERSION="${2:-}"; shift 2 ;;
--proof-record) PROOF_RECORD="${2:-}"; shift 2 ;;
--source-commit) SOURCE_COMMIT="${2:-}"; shift 2 ;;
--source-ref) SOURCE_REF="${2:-}"; shift 2 ;;
*) echo "unknown argument: $1" >&2; exit 2 ;;
esac
done
[ -n "$MSI" ] && [ -n "$STAGED_EXE" ] && [ -n "$STAGED_ENV" ] || {
echo "usage: $0 <msi> <staged-server-exe> <staged-env-example> [options]" >&2
exit 2
}
for f in "$MSI" "$STAGED_EXE" "$STAGED_ENV"; do
[ -f "$f" ] || { echo "no such file: $f" >&2; exit 2; }
done
for tool in msiinfo msiextract sha256sum; do
command -v "$tool" >/dev/null || { echo "$tool not found" >&2; exit 2; }
done
if [ -n "$PROOF_RECORD" ]; then
[ -n "$REDFLAG_VERSION" ] && [ -n "$MSI_VERSION" ] || {
echo "--proof-record requires --redflag-version and --msi-version" >&2; exit 2; }
command -v jq >/dev/null || { echo "jq not found" >&2; exit 2; }
fi
FAIL=0
EMBEDDED=false
ok() { echo "[ok] $*"; }
bad() { echo "[FAIL] $*"; FAIL=1; }
MSI_SIZE=$(stat -c%s "$MSI")
MSI_SHA=$(sha256sum "$MSI" | cut -d' ' -f1)
STAGED_SERVER_SIZE=$(stat -c%s "$STAGED_EXE")
STAGED_SERVER_SHA=$(sha256sum "$STAGED_EXE" | cut -d' ' -f1)
STAGED_CONFIG_SIZE=$(stat -c%s "$STAGED_ENV")
STAGED_CONFIG_SHA=$(sha256sum "$STAGED_ENV" | cut -d' ' -f1)
echo "=== package under test ==="
echo " path: $MSI"
echo " size: $MSI_SIZE bytes <- diagnostic only, never a gate"
echo " sha256: $MSI_SHA"
echo "=== tables ==="
for t in Property Directory Component File Feature FeatureComponents \
ServiceInstall ServiceControl Upgrade Media; do
if msiinfo export "$MSI" "$t" >/dev/null 2>&1; then ok "table $t present"
else bad "table $t missing"; fi
done
carries() {
if msiinfo export "$MSI" "$1" 2>/dev/null | grep -F "$2" >/dev/null; then ok "$1 carries $2"
else bad "$1 does not carry $2"; fi
}
carries ServiceInstall RedFlagServer
carries ServiceControl RedFlagServer
carries Component ServerBinary
carries Component ConfigTemplate
carries File redflag-server.exe
carries File redflag.env.example
echo "=== version identity ==="
PRODUCT_VERSION=$(msiinfo export "$MSI" Property 2>/dev/null |
awk -F'\t' '$1 == "ProductVersion" {print $2; exit}' | tr -d '\r')
if [ -n "$MSI_VERSION" ]; then
[ "$PRODUCT_VERSION" = "$MSI_VERSION" ] && ok "MSI ProductVersion is $MSI_VERSION" || \
bad "MSI ProductVersion '${PRODUCT_VERSION:-missing}' != expected $MSI_VERSION"
fi
if [ -n "$REDFLAG_VERSION" ]; then
if ! command -v go >/dev/null; then
bad "go is required to inspect the staged Server build version"
else
SERVER_VERSION=$(go version -m "$STAGED_EXE" 2>/dev/null |
sed -n 's/.*versions\.AgentVersion=\([^[:space:]]*\).*/\1/p' | head -1)
[ "$SERVER_VERSION" = "$REDFLAG_VERSION" ] && \
ok "staged Server build version is $REDFLAG_VERSION" || \
bad "staged Server build version '${SERVER_VERSION:-missing}' != release $REDFLAG_VERSION"
fi
fi
echo "=== cabinet custody ==="
msiinfo export "$MSI" Media 2>/dev/null | sed 's/^/ media| /'
CAB=$(msiinfo export "$MSI" Media 2>/dev/null |
awk -F'\t' 'NR > 3 && $4 != "" {print $4; exit}')
if [ -z "$CAB" ]; then
bad "Media table names no cabinet at all"
elif [ "${CAB#\#}" = "$CAB" ]; then
bad "Media names external cabinet '$CAB'"
else
STREAM="${CAB#\#}"
if msiinfo streams "$MSI" 2>/dev/null | grep -Fx "$STREAM" >/dev/null; then
EMBEDDED=true
ok "Media names embedded cabinet '$CAB' and storage carries '$STREAM'"
else
bad "embedded cabinet stream '$STREAM' is absent"
fi
fi
echo "=== extraction (MSI copied alone to an empty directory) ==="
ISO=$(mktemp -d)
OUT=$(mktemp -d)
trap 'rm -rf "$ISO" "$OUT"' EXIT
cp "$MSI" "$ISO/package.msi"
if (cd "$ISO" && msiextract -C "$OUT" package.msi >/dev/null 2>&1); then
ok "msiextract succeeded without a sibling cabinet"
else
bad "msiextract failed without a sibling cabinet"
fi
find "$OUT" -type f -printf ' %s bytes %P\n' 2>/dev/null | sort || true
EXTRACTED_SERVER=""
EXTRACTED_CONFIG=""
compare() {
local name="$1" staged="$2" destination="$3" got gs ss gh sh
got=$(find "$OUT" -type f -name "$name" -print -quit 2>/dev/null)
if [ -z "$got" ]; then bad "$name is not in the extracted payload"; return; fi
printf -v "$destination" '%s' "$got"
gs=$(stat -c%s "$got"); ss=$(stat -c%s "$staged")
gh=$(sha256sum "$got" | cut -d' ' -f1); sh=$(sha256sum "$staged" | cut -d' ' -f1)
[ "$gs" = "$ss" ] && ok "$name size matches staged ($gs bytes)" || bad "$name size $gs != staged $ss"
[ "$gh" = "$sh" ] && ok "$name sha256 matches staged ($gh)" || bad "$name sha256 $gh != staged $sh"
}
echo "=== payload identity ==="
compare redflag-server.exe "$STAGED_EXE" EXTRACTED_SERVER
compare redflag.env.example "$STAGED_ENV" EXTRACTED_CONFIG
if [ "$FAIL" -ne 0 ]; then
echo "RESULT: payload custody NOT proven"
exit 1
fi
EXTRACTED_SERVER_SHA=$(sha256sum "$EXTRACTED_SERVER" | cut -d' ' -f1)
EXTRACTED_CONFIG_SHA=$(sha256sum "$EXTRACTED_CONFIG" | cut -d' ' -f1)
if [ -n "$PROOF_RECORD" ]; then
mkdir -p "$(dirname "$PROOF_RECORD")"
WIXL_VERSION=$(wixl --version 2>&1 | head -1 || true)
MSIINFO_VERSION=$(msiinfo --version 2>&1 | head -1 || true)
MSIEXTRACT_VERSION=$(msiextract --version 2>&1 | head -1 || true)
GO_VERSION=$(go version 2>&1 || true)
jq -n \
--arg source_commit "$SOURCE_COMMIT" --arg source_ref "$SOURCE_REF" \
--arg redflag_version "$REDFLAG_VERSION" --arg msi_product_version "$PRODUCT_VERSION" \
--arg msi_file "$(basename "$MSI")" --arg msi_sha256 "$MSI_SHA" --argjson msi_size "$MSI_SIZE" \
--arg staged_server_sha256 "$STAGED_SERVER_SHA" --argjson staged_server_size "$STAGED_SERVER_SIZE" \
--arg extracted_server_sha256 "$EXTRACTED_SERVER_SHA" \
--arg staged_config_sha256 "$STAGED_CONFIG_SHA" --argjson staged_config_size "$STAGED_CONFIG_SIZE" \
--arg extracted_config_sha256 "$EXTRACTED_CONFIG_SHA" \
--arg wixl "$WIXL_VERSION" --arg msiinfo "$MSIINFO_VERSION" \
--arg msiextract "$MSIEXTRACT_VERSION" --arg go "$GO_VERSION" \
'{record_type:"redflag-installer-custody-proof",signed_release_manifest:false,
source:{commit:$source_commit,ref:$source_ref},redflag_version:$redflag_version,
msi:{filename:$msi_file,product_version:$msi_product_version,size:$msi_size,sha256:$msi_sha256},
staged:{server:{size:$staged_server_size,sha256:$staged_server_sha256},config:{size:$staged_config_size,sha256:$staged_config_sha256}},
extracted:{server:{sha256:$extracted_server_sha256},config:{sha256:$extracted_config_sha256}},
cabinet_embedded:true,verifier_result:"passed",
tools:{wixl:$wixl,msiinfo:$msiinfo,msiextract:$msiextract,go:$go}}' > "$PROOF_RECORD"
ok "wrote installer proof record $PROOF_RECORD"
fi
echo "RESULT: payload custody proven"
echo "BOUNDARY: this is not a Windows installation or publisher-authentication proof"