name: Full replacement certification
run-name: Full replacement certification ${{ inputs.source_sha || github.sha }}
on:
push:
branches: [main]
workflow_dispatch:
inputs:
source_sha:
description: Current main commit to certify; keep main quiescent, or leave blank
required: false
type: string
permissions:
actions: write
contents: read
statuses: write
concurrency:
group: full-replacement-certification
cancel-in-progress: false
jobs:
certify:
runs-on: ubuntu-24.04
timeout-minutes: 360
steps:
- name: Check out the requested source
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 with:
ref: ${{ inputs.source_sha || github.sha }}
fetch-depth: 1
- name: Verify exact source identity
id: source
shell: bash
env:
REQUESTED_SHA: ${{ inputs.source_sha || github.sha }}
run: |
set -euo pipefail
sha=$(git rev-parse HEAD)
[[ $REQUESTED_SHA =~ ^[0-9a-f]{40}$ ]]
test "$sha" = "$REQUESTED_SHA"
echo "sha=$sha" >> "$GITHUB_OUTPUT"
- name: Publish certification pending status
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
payload=$(printf '{"state":"pending","context":"certification/full","description":"Full replacement certification running","target_url":"%s"}' "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}")
curl --fail-with-body --silent --show-error -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/${SOURCE_SHA}" \
--data "$payload" >/dev/null
- name: Dispatch prerequisite proof workflows
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import json
import os
import urllib.request
api = os.environ["GITHUB_API_URL"]
repository = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GH_TOKEN"]
sha = os.environ["SOURCE_SHA"]
workflows = {
"reproducible-release.yml": "replacement/reproducible-release",
"replacement-security-gates.yml": "replacement/security",
"replacement-upstream-test-75.yml": "replacement/upstream-test-75",
"replacement-upstream-test-89-mdns.yml": "replacement/upstream-test-89-mdns",
"replacement-boot-proof.yml": "replacement/boot-proof",
}
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"X-GitHub-Api-Version": "2022-11-28",
}
for workflow, context in workflows.items():
status_request = urllib.request.Request(
f"{api}/repos/{repository}/statuses/{sha}",
data=json.dumps(
{
"state": "pending",
"context": context,
"description": "Fresh exact-source proof requested",
}
).encode(),
method="POST",
headers=headers,
)
with urllib.request.urlopen(status_request, timeout=30) as response:
if response.status != 201:
raise SystemExit(
f"pending status failed for {context}: HTTP {response.status}"
)
dispatch_request = urllib.request.Request(
f"{api}/repos/{repository}/actions/workflows/{workflow}/dispatches",
data=json.dumps(
{"ref": "main", "inputs": {"source_sha": sha}}
).encode(),
method="POST",
headers=headers,
)
with urllib.request.urlopen(dispatch_request, timeout=30) as response:
if response.status != 204:
raise SystemExit(
f"dispatch failed for {workflow}: HTTP {response.status}"
)
PY
- name: Wait for security, upstream, and boot gates
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import json
import os
import time
import urllib.request
api = os.environ["GITHUB_API_URL"]
repository = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GH_TOKEN"]
sha = os.environ["SOURCE_SHA"]
contexts = [
"replacement/reproducible-release",
"replacement/security",
"replacement/upstream-test-75",
"replacement/upstream-test-89-mdns",
"replacement/boot-proof",
]
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
}
deadline = time.monotonic() + 3 * 60 * 60
while time.monotonic() < deadline:
request = urllib.request.Request(
f"{api}/repos/{repository}/commits/{sha}/status",
headers=headers,
)
with urllib.request.urlopen(request, timeout=30) as response:
payload = json.load(response)
latest = {}
for item in payload.get("statuses", []):
context = item.get("context")
if context not in contexts:
continue
previous = latest.get(context)
if previous is None or item.get("created_at", "") > previous["created_at"]:
latest[context] = {
"created_at": item.get("created_at", ""),
"state": item.get("state"),
}
latest = {context: item["state"] for context, item in latest.items()}
print(latest, flush=True)
failed = [
context
for context in contexts
if latest.get(context) in {"failure", "error"}
]
if failed:
raise SystemExit("prerequisite proof failed: " + ", ".join(failed))
if all(latest.get(context) == "success" for context in contexts):
break
time.sleep(30)
else:
raise SystemExit("timed out waiting for exact-source prerequisite statuses")
PY
- name: Dispatch the exact-SHA security proof
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import json
import os
import urllib.request
api = os.environ["GITHUB_API_URL"]
repository = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GH_TOKEN"]
sha = os.environ["SOURCE_SHA"]
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"X-GitHub-Api-Version": "2022-11-28",
}
status = urllib.request.Request(
f"{api}/repos/{repository}/statuses/{sha}",
data=json.dumps(
{
"state": "pending",
"context": "replacement/security-proof",
"description": "Fresh bound security proof requested",
}
).encode(),
method="POST",
headers=headers,
)
with urllib.request.urlopen(status, timeout=30) as response:
if response.status != 201:
raise SystemExit(f"security proof pending status failed: {response.status}")
dispatch = urllib.request.Request(
f"{api}/repos/{repository}/actions/workflows/replacement-security-proof.yml/dispatches",
data=json.dumps({"ref": "main", "inputs": {"source_sha": sha}}).encode(),
method="POST",
headers=headers,
)
with urllib.request.urlopen(dispatch, timeout=30) as response:
if response.status != 204:
raise SystemExit(f"security proof dispatch failed: {response.status}")
PY
- name: Wait for the bound security proof
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import json
import os
import time
import urllib.request
api = os.environ["GITHUB_API_URL"]
repository = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GH_TOKEN"]
sha = os.environ["SOURCE_SHA"]
context = "replacement/security-proof"
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
}
deadline = time.monotonic() + 30 * 60
while time.monotonic() < deadline:
request = urllib.request.Request(
f"{api}/repos/{repository}/commits/{sha}/status",
headers=headers,
)
with urllib.request.urlopen(request, timeout=30) as response:
statuses = json.load(response).get("statuses", [])
matching = [item for item in statuses if item.get("context") == context]
state = max(
matching,
key=lambda item: item.get("created_at", ""),
default={},
).get("state")
print({context: state}, flush=True)
if state == "success":
break
if state in {"failure", "error"}:
raise SystemExit("bound security proof failed")
time.sleep(20)
else:
raise SystemExit("timed out waiting for bound security proof status")
PY
- name: Dispatch final readiness certificate
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import json
import os
import urllib.request
api = os.environ["GITHUB_API_URL"]
repository = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GH_TOKEN"]
sha = os.environ["SOURCE_SHA"]
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"X-GitHub-Api-Version": "2022-11-28",
}
status = urllib.request.Request(
f"{api}/repos/{repository}/statuses/{sha}",
data=json.dumps(
{
"state": "pending",
"context": "replacement/readiness",
"description": "Fresh readiness certificate requested",
}
).encode(),
method="POST",
headers=headers,
)
with urllib.request.urlopen(status, timeout=30) as response:
if response.status != 201:
raise SystemExit(f"readiness pending status failed: {response.status}")
dispatch = urllib.request.Request(
f"{api}/repos/{repository}/actions/workflows/replacement-readiness-certificate.yml/dispatches",
data=json.dumps({"ref": "main", "inputs": {"source_sha": sha}}).encode(),
method="POST",
headers=headers,
)
with urllib.request.urlopen(dispatch, timeout=30) as response:
if response.status != 204:
raise SystemExit(f"readiness dispatch failed: {response.status}")
PY
- name: Wait for the final readiness certificate
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import json
import os
import time
import urllib.request
api = os.environ["GITHUB_API_URL"]
repository = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GH_TOKEN"]
sha = os.environ["SOURCE_SHA"]
context = "replacement/readiness"
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
}
deadline = time.monotonic() + 60 * 60
while time.monotonic() < deadline:
request = urllib.request.Request(
f"{api}/repos/{repository}/commits/{sha}/status",
headers=headers,
)
with urllib.request.urlopen(request, timeout=30) as response:
statuses = json.load(response).get("statuses", [])
matching = [item for item in statuses if item.get("context") == context]
state = max(
matching,
key=lambda item: item.get("created_at", ""),
default={},
).get("state")
print({context: state}, flush=True)
if state == "success":
break
if state in {"failure", "error"}:
raise SystemExit("final readiness certificate failed")
time.sleep(20)
else:
raise SystemExit("timed out waiting for final readiness status")
PY
- name: Download and verify bound readiness evidence
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import io
import json
import os
import urllib.request
import zipfile
api = os.environ["GITHUB_API_URL"]
repository = os.environ["GITHUB_REPOSITORY"]
token = os.environ["GH_TOKEN"]
sha = os.environ["SOURCE_SHA"]
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {token}",
"X-GitHub-Api-Version": "2022-11-28",
}
runs_request = urllib.request.Request(
f"{api}/repos/{repository}/actions/workflows/replacement-readiness-certificate.yml/runs?event=workflow_dispatch&per_page=30",
headers=headers,
)
with urllib.request.urlopen(runs_request, timeout=30) as response:
runs = json.load(response).get("workflow_runs", [])
matching_runs = [
run
for run in runs
if run.get("head_sha") == sha and run.get("conclusion") == "success"
]
if not matching_runs:
raise SystemExit("no successful readiness workflow for source SHA")
run_id = max(matching_runs, key=lambda run: run.get("created_at", ""))["id"]
artifacts_request = urllib.request.Request(
f"{api}/repos/{repository}/actions/runs/{run_id}/artifacts?per_page=30",
headers=headers,
)
with urllib.request.urlopen(artifacts_request, timeout=30) as response:
artifacts = json.load(response).get("artifacts", [])
expected_name = f"replacement-readiness-{sha}"
matching = [artifact for artifact in artifacts if artifact.get("name") == expected_name]
if not matching:
raise SystemExit("bound readiness artifact missing")
artifact = max(matching, key=lambda item: item.get("created_at", ""))
download = urllib.request.Request(artifact["archive_download_url"], headers=headers)
with urllib.request.urlopen(download, timeout=60) as response:
payload = response.read()
with zipfile.ZipFile(io.BytesIO(payload)) as archive:
member = next(
(
name
for name in archive.namelist()
if name.endswith("replacement-readiness.json")
),
None,
)
if member is None:
raise SystemExit("readiness JSON missing from bound artifact")
readiness = json.loads(archive.read(member))
if readiness.get("source_commit") != sha:
raise SystemExit("readiness source commit mismatch")
if readiness.get("certified") is not True:
raise SystemExit("readiness artifact is not certified")
with open("replacement-readiness.json", "w", encoding="utf-8") as handle:
json.dump(readiness, handle, sort_keys=True, indent=2)
handle.write("\n")
PY
- name: Emit full certification evidence
shell: bash
env:
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
python3 - <<'PY'
import hashlib
import json
import os
from pathlib import Path
sha = os.environ["SOURCE_SHA"]
readiness = json.loads(Path("replacement-readiness.json").read_text())
outputs = {
"schema": 2,
"certified": True,
"source_commit": sha,
"source_tree": readiness.get("source_tree"),
"source_archive_sha256": readiness.get("source_archive_sha256"),
"daemon_sha256": readiness.get("daemon_sha256"),
"client_sha256": readiness.get("client_sha256"),
"nss_sha256": readiness.get("nss_sha256"),
"readiness_evidence": readiness,
}
Path("full-replacement-certification.json").write_text(
json.dumps(outputs, sort_keys=True, indent=2) + "\n"
)
digest = hashlib.sha256(
Path("full-replacement-certification.json").read_bytes()
).hexdigest()
Path("full-replacement-certification.sha256").write_text(
f"{digest} full-replacement-certification.json\n"
)
PY
- name: Upload full certification evidence
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with:
name: full-replacement-certification-${{ steps.source.outputs.sha }}
path: |
full-replacement-certification.json
full-replacement-certification.sha256
if-no-files-found: error
retention-days: 90
- name: Publish certification success status
if: success()
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
payload=$(printf '{"state":"success","context":"certification/full","description":"Full replacement certification passed","target_url":"%s"}' "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}")
curl --fail-with-body --silent --show-error -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/${SOURCE_SHA}" \
--data "$payload" >/dev/null
- name: Publish certification failure status
if: failure()
env:
GH_TOKEN: ${{ github.token }}
SOURCE_SHA: ${{ steps.source.outputs.sha }}
run: |
set -euo pipefail
payload=$(printf '{"state":"failure","context":"certification/full","description":"Full replacement certification failed","target_url":"%s"}' "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}")
curl --fail-with-body --silent --show-error -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/${SOURCE_SHA}" \
--data "$payload" >/dev/null