From 92e106b8c629584b6a26f62f8f5f7c0e2d810488 Mon Sep 17 00:00:00 2001 From: Nickfosts Hermes Date: Mon, 27 Jul 2026 01:37:38 -0500 Subject: [PATCH 1/3] docs: close quickstart onboarding safety gaps Signed-off-by: Nickfosts Hermes --- docs/QUICKSTART.md | 29 +++++++++++++++++++++-------- scripts/test_quickstart.py | 21 +++++++++++++++++++++ scripts/validate.sh | 4 +++- 3 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 scripts/test_quickstart.py diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index f7356346..a93224f4 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -40,7 +40,9 @@ jobs through the Docker socket. [Adding a host](ADDING-A-HOST.md) sections 1-3. The installer itself additionally requires a systemd-based host with `python3`, `tar`, `install`, `flock`, and standard coreutils — it fails closed with a - named missing command if one is absent. + named missing command if one is absent. The Docker host architecture + must be `amd64` or `arm64`; the runner image build rejects every other + architecture. 2. On the **fleet host**, clone the public engine repository and check out the exact reviewed engine commit you intend to pin — later steps copy templates from it and run its installer as root, so it must be @@ -107,7 +109,7 @@ jobs through the Docker socket. repository name separately with `--repository` so `allowed_repositories` names a repository that exists. Push both branches to a new **private** GitHub repository, open a pull request - from `initialize-fleet` to `main`, and **merge it**. Managed controller managed controller + from `initialize-fleet` to `main`, and **merge it**. Managed controller lifecycle is permitted only from a reviewed, merged private configuration commit. Resolve and record that merge commit SHA (`RESOLVED_CONFIG_COMMIT` below); do not install from an unmerged @@ -167,7 +169,12 @@ onboarding never touches a fleet host. 2. Confirm the repository you passed as `--repository` to `init.sh` is in the pool's `allowed_repositories` (the initializer put it there; add it only if you skipped initialization), and validate with - `./scripts/validate.sh --strict`. + `./scripts/validate.sh --strict`. Immediately before authorization, + audit every queued Actions job for this repository. Cancel every queued job + whose complete `runs-on` expression could match the pool's shared label; + do not authorize until that matching queue is empty. If you cannot prove + that condition, use a dedicated empty proof repository and runner group + rather than exposing the pool to an unknown first job. 3. Authorize the repository in the GitHub runner group. Full contract: [Adding a project](ADDING-A-PROJECT.md). @@ -180,8 +187,9 @@ Changes state: yes, transiently — the job creates a runner container and may create Docker containers, networks, and volumes; the proof below verifies all of it is cleaned up. -1. Dispatch the staged proof workflow and record its run ID from the - run URL (`GITHUB_RUN_ID` scopes the residue checks below). Its explicit +1. Dispatch the staged proof workflow and record its repository, run ID, + and run attempt from the run metadata (`GITHUB_RUN_ID` and + `GITHUB_RUN_ATTEMPT` scope the residue checks below). Its explicit `permissions: contents: read` and `timeout-minutes: 5` keep the job from inheriting a read-write `GITHUB_TOKEN` default or occupying the single runner past the ordinary-CI ceiling. Do not copy a nested @@ -211,10 +219,15 @@ verifies all of it is cleaned up. # and Docker prefixes container names with '/', so anchor accordingly # and scope to this run ID (host-wide queries would also match a # concurrent unrelated run): + REPOSITORY=OWNER/REPOSITORY RUN_ID= - sudo docker ps -a --filter "name=^/ci-.*-${RUN_ID}-" --format '{{.Names}}' - sudo docker network ls --filter "name=^ci-.*-${RUN_ID}-" --format '{{.Name}}' - sudo docker volume ls -q --filter "name=^ci-.*-${RUN_ID}-" + RUN_ATTEMPT= + REPO_COMPONENT="$(printf '%s' "${REPOSITORY#*/}" \ + | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9_-' '-' | cut -c1-12)" + PROJECT_PREFIX="ci-${REPO_COMPONENT}-${RUN_ID}-${RUN_ATTEMPT}-" + sudo docker ps -a --filter "name=^/${PROJECT_PREFIX}" --format '{{.Names}}' + sudo docker network ls --filter "name=^${PROJECT_PREFIX}" --format '{{.Name}}' + sudo docker volume ls -q --filter "name=^${PROJECT_PREFIX}" # Run a fresh health evaluation, then check installed state: sudo systemctl start ci-fleet-health.service sudo systemctl is-failed ci-fleet-health.service # expect: inactive/failed must NOT be failed diff --git a/scripts/test_quickstart.py b/scripts/test_quickstart.py new file mode 100644 index 00000000..f0a7343a --- /dev/null +++ b/scripts/test_quickstart.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +from pathlib import Path + +raw_quickstart = (Path(__file__).resolve().parents[1] / "docs" / "QUICKSTART.md").read_text() +quickstart = " ".join(raw_quickstart.split()) + +required = ( + "host architecture must be `amd64` or `arm64`", + "Cancel every queued job", + "REPOSITORY=OWNER/REPOSITORY", + "RUN_ATTEMPT=", + 'PROJECT_PREFIX="ci-${REPO_COMPONENT}-${RUN_ID}-${RUN_ATTEMPT}-"', + '--filter "name=^/${PROJECT_PREFIX}"', + '--filter "name=^${PROJECT_PREFIX}"', +) +for text in required: + assert text in quickstart, f"quickstart safety contract missing: {text}" + +assert quickstart.index("Cancel every queued job") < quickstart.index("3. Authorize the repository") +assert "managed controller managed controller" not in quickstart +print("quickstart_contract=PASS") diff --git a/scripts/validate.sh b/scripts/validate.sh index 229577d3..8a1f94e9 100755 --- a/scripts/validate.sh +++ b/scripts/validate.sh @@ -12,10 +12,12 @@ python3 -m py_compile \ scripts/health.py \ scripts/scan_committed_secrets.py \ scripts/test_desired_state.py \ - scripts/test_health.py + scripts/test_health.py \ + scripts/test_quickstart.py python3 .github/actions/plan/test_plan.py python3 scripts/test_desired_state.py python3 scripts/test_health.py +python3 scripts/test_quickstart.py python3 .github/actions/plan/plan.py --plan examples/project/scripts/ci/plan.json --group fast >/dev/null python3 .github/actions/plan/plan.py --plan examples/project/scripts/ci/plan.json --group full >/dev/null scripts/test-capacity-preflight.sh From 17a1fb172521b8eb18b5e8d5fdc4878a71711e0f Mon Sep 17 00:00:00 2001 From: Nickfosts Hermes Date: Mon, 27 Jul 2026 01:46:00 -0500 Subject: [PATCH 2/3] docs: audit the complete shared-label queue Signed-off-by: Nickfosts Hermes --- docs/QUICKSTART.md | 11 ++++++----- scripts/test_quickstart.py | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index a93224f4..1bc64e61 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -170,11 +170,12 @@ onboarding never touches a fleet host. in the pool's `allowed_repositories` (the initializer put it there; add it only if you skipped initialization), and validate with `./scripts/validate.sh --strict`. Immediately before authorization, - audit every queued Actions job for this repository. Cancel every queued job - whose complete `runs-on` expression could match the pool's shared label; - do not authorize until that matching queue is empty. If you cannot prove - that condition, use a dedicated empty proof repository and runner group - rather than exposing the pool to an unknown first job. + audit every queued Actions job across every repository already authorized + for the runner group and this repository. Cancel every queued job whose + complete `runs-on` expression could match the pool's shared label; do not + authorize until that matching queue is empty. If you cannot prove that + condition, use a dedicated empty proof repository and runner group rather + than exposing the pool to an unknown first job. 3. Authorize the repository in the GitHub runner group. Full contract: [Adding a project](ADDING-A-PROJECT.md). diff --git a/scripts/test_quickstart.py b/scripts/test_quickstart.py index f0a7343a..9bdd9fce 100644 --- a/scripts/test_quickstart.py +++ b/scripts/test_quickstart.py @@ -7,6 +7,7 @@ required = ( "host architecture must be `amd64` or `arm64`", "Cancel every queued job", + "across every repository already authorized for the runner group", "REPOSITORY=OWNER/REPOSITORY", "RUN_ATTEMPT=", 'PROJECT_PREFIX="ci-${REPO_COMPONENT}-${RUN_ID}-${RUN_ATTEMPT}-"', From f8b329dfd7cf2b7c5fde7558134c555b700d6f92 Mon Sep 17 00:00:00 2001 From: Nickfosts Hermes Date: Mon, 27 Jul 2026 02:01:50 -0500 Subject: [PATCH 3/3] docs: make first-job proof race-safe Signed-off-by: Nickfosts Hermes --- docs/QUICKSTART.md | 30 ++++++++++-------------------- scripts/test_quickstart.py | 7 ++----- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index 1bc64e61..a587cc24 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -166,6 +166,11 @@ onboarding never touches a fleet host. steps: - run: echo "fleet proof on ${RUNNER_NAME:-unknown}" ``` + This controlled-first-job path is valid only when the staged proof is + the only workflow that can target the shared label. If the repository is + active or has any other matching trigger, stop: use a dedicated empty + proof repository and runner group for Steps 1-3, then onboard the active + repository through [Adding a project](ADDING-A-PROJECT.md). 2. Confirm the repository you passed as `--repository` to `init.sh` is in the pool's `allowed_repositories` (the initializer put it there; add it only if you skipped initialization), and validate with @@ -185,12 +190,11 @@ Full contract: [Adding a project](ADDING-A-PROJECT.md). Runs on: dispatched from GitHub, but steps execute inside an ephemeral runner on your fleet host with host-root-equivalent Docker access. Changes state: yes, transiently — the job creates a runner container and -may create Docker containers, networks, and volumes; the proof below -verifies all of it is cleaned up. +the proof below verifies that it is cleaned up. The supplied proof job does +not invoke project Compose or create project containers, networks, or volumes. -1. Dispatch the staged proof workflow and record its repository, run ID, - and run attempt from the run metadata (`GITHUB_RUN_ID` and - `GITHUB_RUN_ATTEMPT` scope the residue checks below). Its explicit +1. Dispatch the staged proof workflow and record its run ID from the run + metadata. Its explicit `permissions: contents: read` and `timeout-minutes: 5` keep the job from inheriting a read-write `GITHUB_TOKEN` default or occupying the single runner past the ordinary-CI ceiling. Do not copy a nested @@ -215,20 +219,6 @@ verifies all of it is cleaned up. sudo docker ps -aq \ --filter label=io.randomdevelopment.ci-fleet.managed=true \ --filter label=io.randomdevelopment.ci-fleet.kind=runner - # No resources owned by the dispatched proof run remain. Compliant - # jobs name Compose projects ci-----, - # and Docker prefixes container names with '/', so anchor accordingly - # and scope to this run ID (host-wide queries would also match a - # concurrent unrelated run): - REPOSITORY=OWNER/REPOSITORY - RUN_ID= - RUN_ATTEMPT= - REPO_COMPONENT="$(printf '%s' "${REPOSITORY#*/}" \ - | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9_-' '-' | cut -c1-12)" - PROJECT_PREFIX="ci-${REPO_COMPONENT}-${RUN_ID}-${RUN_ATTEMPT}-" - sudo docker ps -a --filter "name=^/${PROJECT_PREFIX}" --format '{{.Names}}' - sudo docker network ls --filter "name=^${PROJECT_PREFIX}" --format '{{.Name}}' - sudo docker volume ls -q --filter "name=^${PROJECT_PREFIX}" # Run a fresh health evaluation, then check installed state: sudo systemctl start ci-fleet-health.service sudo systemctl is-failed ci-fleet-health.service # expect: inactive/failed must NOT be failed @@ -239,7 +229,7 @@ verifies all of it is cleaned up. The health service is a timer-driven oneshot — `systemctl status` alone only shows its previous run, so start it explicitly after the - proof job. Every resource listing above must be empty and the check + proof job. The runner-container listing above must be empty and the check must report `CHECK_OK`. The [Live pilot runbook](LIVE-PILOT.md) documents the full isolated first-job proof including read-only job permissions. diff --git a/scripts/test_quickstart.py b/scripts/test_quickstart.py index 9bdd9fce..55a82a24 100644 --- a/scripts/test_quickstart.py +++ b/scripts/test_quickstart.py @@ -8,15 +8,12 @@ "host architecture must be `amd64` or `arm64`", "Cancel every queued job", "across every repository already authorized for the runner group", - "REPOSITORY=OWNER/REPOSITORY", - "RUN_ATTEMPT=", - 'PROJECT_PREFIX="ci-${REPO_COMPONENT}-${RUN_ID}-${RUN_ATTEMPT}-"', - '--filter "name=^/${PROJECT_PREFIX}"', - '--filter "name=^${PROJECT_PREFIX}"', + "the only workflow that can target the shared label", ) for text in required: assert text in quickstart, f"quickstart safety contract missing: {text}" assert quickstart.index("Cancel every queued job") < quickstart.index("3. Authorize the repository") +assert "PROJECT_PREFIX=" not in raw_quickstart assert "managed controller managed controller" not in quickstart print("quickstart_contract=PASS")