Skip to content

release(installer): cut r29 (agent.sparkswarm.ai) #424

release(installer): cut r29 (agent.sparkswarm.ai)

release(installer): cut r29 (agent.sparkswarm.ai) #424

name: Install hardening
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read
id-token: write
attestations: write
jobs:
install-hardening:
name: install-hardening
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Validate installer syntax
shell: bash
run: |
set -euo pipefail
bash -n install.sh
pwsh -NoProfile -Command '$tokens = $null; $errors = $null; [System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path "./install.ps1"), [ref] $tokens, [ref] $errors) | Out-Null; if ($errors.Count) { $errors | ForEach-Object { Write-Error $_ }; exit 1 }'
- name: Validate checksums and manifests
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
import hashlib
import json
import pathlib
import sys
root = pathlib.Path(".")
expected = {}
checksums_text = root / "install/checksums.txt"
if b"\r" in checksums_text.read_bytes():
sys.exit("install/checksums.txt must use LF line endings so shasum -c works on macOS")
for line in checksums_text.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line:
continue
digest, relpath = line.split(maxsplit=1)
expected[relpath] = digest
for relpath, digest in expected.items():
actual = hashlib.sha256((root / relpath).read_bytes()).hexdigest()
if actual != digest:
sys.exit(f"{relpath} checksum mismatch: {actual} != {digest}")
checksums = json.loads((root / "install/checksums.json").read_text(encoding="utf-8"))
commands = json.loads((root / "install/commands.json").read_text(encoding="utf-8"))
manifest = json.loads((root / "install/release-manifest.json").read_text(encoding="utf-8"))
json_hashes = {entry["path"]: entry["sha256"] for entry in checksums["files"]}
command_hashes = commands["checksums"]["sha256"]
if json_hashes != expected:
sys.exit("checksums.json does not match checksums.txt")
if command_hashes != expected:
sys.exit("commands.json checksums do not match checksums.txt")
manifest_ref = manifest["sparkCli"].get("ref", manifest["sparkCli"]["commit"])
if commands["source"]["ref"] != manifest_ref:
sys.exit("commands.json source ref does not match release manifest")
if commands["source"].get("commit") and commands["source"]["commit"] != manifest["sparkCli"]["commit"]:
sys.exit("commands.json source commit does not match release manifest")
if not all(len(value) == 64 for value in manifest["runtimes"]["uv"]["assets"].values()):
sys.exit("release manifest uv asset hashes must be sha256 digests")
for relpath, digest in expected.items():
for haystack in [
(root / "install/checksums.json").read_text(encoding="utf-8"),
(root / "install/commands.json").read_text(encoding="utf-8"),
(root / "install/attestations.md").read_text(encoding="utf-8"),
]:
if digest not in haystack:
sys.exit(f"{digest} missing from published verification metadata")
PY
shasum -a 256 -c install/checksums.txt
node scripts/check-security-release-surface.mjs
- name: Validate trust boundary
shell: bash
run: |
set -euo pipefail
spark_ref="$(python3 -c 'import json; s=json.load(open("install/release-manifest.json"))["sparkCli"]; print(s.get("ref", s["commit"]))')"
grep -q "SPARK_ALLOW_DEV_SOURCE" install.sh
grep -q "AllowDevSource" install.ps1
grep -q "Refusing non-canonical Spark CLI source" install.sh
grep -q "Refusing non-canonical Spark CLI source" install.ps1
grep -q "Refusing custom git ref without --allow-dev-source" install.sh
grep -q "Refusing custom git ref without -AllowDevSource" install.ps1
grep -q "Strict-Transport-Security" nginx.conf
grep -q "location = /install" nginx.conf
grep -q "location = /install/commands.json" nginx.conf
grep -q "location = /commands.json" nginx.conf
grep -q "location = /docs" nginx.conf
grep -q "location = /install/attestations.md" nginx.conf
grep -q "location = /install/signatures.md" nginx.conf
grep -q "location = /install/release-manifest.json" nginx.conf
grep -q "location = /SECURITY.md" nginx.conf
grep -q "location = /.well-known/security.txt" nginx.conf
grep -q "location = /llms.txt" nginx.conf
grep -q "location = /llms-full.txt" nginx.conf
grep -q "location = /.well-known/llms.txt" nginx.conf
grep -q "X-Llms-Txt" nginx.conf
grep -q "location = /docs/install-safety/" nginx.conf
grep -q "location = /docs/suites/" nginx.conf
grep -q "location = /docs/self-improvement/" nginx.conf
grep -q "location = /docs/lifecycle/" nginx.conf
grep -q "location = /docs/install-safety.md" nginx.conf
grep -q "location = /docs/suites.md" nginx.conf
grep -q "location = /docs/self-improvement.md" nginx.conf
grep -q "location = /docs/lifecycle.md" nginx.conf
grep -q "location \\^~ /docs/pages/" nginx.conf
grep -q "Install Spark without guessing" docs/install-safety/index.html
grep -q "The starter kit in normal words" docs/suites/index.html
grep -q "Turn Spark on, check it, and stop it safely" docs/lifecycle/index.html
grep -q "Make agents better with scored loops" docs/self-improvement/index.html
grep -q "Spark Suites" docs/suites.md
grep -q "Spark Self-Improvement" docs/self-improvement.md
grep -q "Spark Agent Full Context" llms-full.txt
grep -q "Canonical: https://agent.sparkswarm.ai/.well-known/security.txt" .well-known/security.txt
grep -q "Contact: mailto:" .well-known/security.txt
grep -q "gh attestation verify" install/commands.json
grep -q "cosign verify-blob" install/commands.json
grep -q "cosign verify-blob" install/signatures.md
grep -q "release-manifest.json" index.html
grep -q "$spark_ref" install.sh
grep -q "$spark_ref" install.ps1
grep -q "uv archive checksum mismatch" install.sh
grep -q "uv archive checksum mismatch" install.ps1
! grep -q "astral.sh/uv/install.sh | sh" install.sh
! grep -q "Invoke-Expression" install.ps1
grep -q "actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26" .github/workflows/install-hardening.yml
grep -q 'try_files $uri $uri/ =404;' nginx.conf
! grep -q 'try_files $uri $uri/ /index.html;' nginx.conf
! grep -q "add_header Cache-Control" nginx.conf
! git grep -n -I -E '301a16435504eaf2|9d32b1d17df505|docs\.sparkswarm\.ai|sparkswarm\.ai/docs/insight' -- . ':!.github/workflows/install-hardening.yml'
- name: Validate command docs
shell: bash
run: |
set -euo pipefail
node scripts/check-docs-readiness.mjs
node scripts/check-command-docs.mjs
git clone --depth 1 https://github.com/vibeforge1111/spark-cli "$RUNNER_TEMP/spark-cli"
SPARK_CLI_PATH="$RUNNER_TEMP/spark-cli" node scripts/smoke-command-docs-cli.mjs
- name: Build container
shell: bash
run: docker build -t spark-agent-site-hardening .
- name: Attest release manifest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26
with:
subject-path: install/release-manifest.json
- name: Attest installer provenance
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26
with:
subject-checksums: install/checksums.txt
installer-smoke:
name: installer-smoke (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Bash installer dry-run
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
bash -n install.sh
SPARK_PREFIX="$RUNNER_TEMP/spark-installer-dry-run" bash install.sh --dry-run
- name: PowerShell installer dry-run
if: runner.os == 'Windows'
shell: pwsh
run: |
$tokens = $null
$errors = $null
[System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path "./install.ps1"), [ref] $tokens, [ref] $errors) | Out-Null
if ($errors.Count) {
$errors | ForEach-Object { Write-Error $_ }
exit 1
}
powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1 -Prefix "$env:RUNNER_TEMP\spark-installer-dry-run" -DryRun