37 KiB
REVIEW — cc-ci Adversary (append-only)
This file is owned by the Adversary loop (§6.1). The Builder seeds this stub at bootstrap and
does not edit it afterward. Adversary appends milestone/D-item verdicts (<id>: PASS @<ts> +
evidence, or FAIL + a finding in BACKLOG.md ## Adversary findings), and may write ## VETO.
M0 — Foundations: PASS @2026-05-26T21:35Z
Verified cold (fresh shell, own clone /srv/cc-ci/cc-ci-adv, isolated host build dir
/root/cc-ci-advverify, no reuse of Builder's /root/cc-ci).
Acceptance — "systemctl is-system-running healthy after a rebuild from the repo" + Builder's
sops claim:
- Repo rebuilds cc-ci: synced M0 commit
deb4a0f(git-archive, no .git) to host, rannixos-rebuild build --flake .#cc-ci→BUILD EXIT 0, produced…-nixos-system-nixos-24.11.20250630.50ab793. Current HEAD also builds clean. - System health:
systemctl is-system-running→running;systemctl --failed→ 0 units. - sops decrypt:
/run/secrets/test_secretpresent, mode400 root:root, 41 bytes, value beginscc-c…(matches claimed generatedcc-ci-m0-…).secrets/secrets.yamlis genuinely encrypted (2×ENC[…]+ sops metadata block). - D6 leak probe (early): the decrypted plaintext value appears 0 times across all git
history (
git grep -F over git rev-list --all) and 0× in plaintext insecrets.yaml. No leak.
Note (not a finding; context for the M1 gate): the running system is already ahead of M0 — its
closure includes docker, unit-swarm-init, and traefik units (traefik.yml,
traefik-stack.yml, unit-traefik-deploy) that are not yet committed (HEAD ab839ae is
swarm-only, no traefik). Expected mid-M1 churn, but the Traefik config must be committed to the
repo before M1 is claimed or it fails D8 reproducibility — will check at the M1 gate.
M1 — Swarm + abra target: PASS @2026-05-26T22:20Z
Verified cold from own clone; deployed my own probe recipe via abra (not trusting the Builder's
hand-test). Acceptance "a recipe deployed via abra is reachable over HTTPS at
*.ci.commoninternet.net, then fully torn down leaving no volumes" + orchestrator's M1 checklist
(a–d).
- (a) Real coop-cloud/traefik recipe (not hand-rolled):
docker service ls→traefik_…_app(traefik:v3.6.15) +…_socket-proxy(lscr.io socket-proxy) — the canonical recipe layout, deployed via abra (scripts/deploy-proxy.sh).modules/traefik.nixis deleted. - (b) Wildcard on web-secure + proxy overlay: static
traefik.ymlhasweb-secure: :443(web→web-secure 301 redirect, verified live). File provider/etc/traefik/file-provider.yml:tls.certificates: [{certFile:/run/secrets/ssl_cert, keyFile:/run/secrets/ssl_key}]; swarm secrets…_ssl_cert_v1/…_ssl_key_v1mounted (2909 B / 227 B = the pre-issued cert). My probe appadvm1probe_…_appwas attached to theproxyoverlay. - E2E (cold deploy):
abra app new custom-html -D advm1probe.ci.commoninternet.net(forcedLETS_ENCRYPT_ENV="") →deploy succeeded 🟢. Via SOCKS proxy: HTTP 200; served certsubject: CN=*.ci.commoninternet.net, SAN-matched,SSL certificate verify ok, issuer LE E8 — i.e. the pre-issued wildcard, NOT a per-host ACME cert. - (c) No Gandi/DNS token, no ACME credential: repo (all history) clean; on host the only
gandi/dns-challenge strings are commented-out recipe-template options (
#GANDI_…,#SECRET_GANDIV5_…) holding no value. Active traefik env =LETS_ENCRYPT_ENV=(empty),WILDCARDS_ENABLED=1,compose.wildcard.yml.staging/productioncertResolvers are defined in traefik.yml (stock template) but referenced by no router; both acme.json are 0 bytes; 0 ACME lines in traefik logs. No ACME ever fires. (Hardening risk filed — see findings.) - (d) Manual renewal documented: DECISIONS.md — operator re-issues at same paths, then
abra app secret rm … ssl_cert+ re-insert at bumped version; install.md "Renewed out-of-band; never ACME here." - Teardown:
abra app undeploy+volume remove→ post-teardown services/containers/volumes/ secrets for the probe all 0. Also independently confirmed the Builder'scchtml1test left 0 runtime resources (only its inert.envconfig file remains, harmless).
Verdict: M1 PASS. Not a hard fail on (c) — no token/credential exists and no ACME fires — but
the inert ACME resolvers + test-app default LETS_ENCRYPT_ENV=production are a latent hazard that
goes live when the harness deploys apps; filed as [adversary] for M4.
M2 — Drone online: PASS @2026-05-26T23:32Z
Verified cold from own clone. Acceptance: "push to cc-ci triggers a visible green Drone build."
- Drone server healthy:
https://drone.ci.commoninternet.net/healthz→ HTTP 200 via gateway. Exec runner (drone-runner-exec.service) active,polling the remote server capacity=2 type=exec. - Repo wired: in Drone's DB the
recipe-maintainers/cc-cirepo isrepo_active=1,repo_config=.drone.yml. Gitea↔Drone OAuth proven by the in-pipelineclonestep succeeding against the private repo (build can't clone without working OAuth/repo token). - Push→green, independently triggered: I pushed my own commit
91a8e8d(a REVIEW.md change) → Drone created build #4,build_event=push,build_trigger=@hook(Gitea webhook), and it ransuccess: stageself-testexit 0, stepsclone+helloboth exit 0. Builds #1–#3 (Builder commits) likewise allsuccessvia@hook. (My earlier M0/M1 review pushes predate the.drone.yml, so correctly produced no builds.) - Visible logs (D7 precondition):
logstable holds per-step log blobs for every build; Drone UI/API serve them. Full D7 UX is M8.
Verdict: M2 PASS. No new findings.
M3 — Comment bridge: PRE-CLAIM PROGRESS (not yet PASS) @2026-05-26T23:48Z
M3 is Blocked in STATUS (Gitea not delivering webhooks), so not a gate verdict yet. But the
bridge is deployed and I independently hammered its auth/filter logic — the part I can verify
regardless of the delivery leg (and which survives a pivot to API polling). Probes were live POSTs
to https://ci.commoninternet.net/hook via the SOCKS proxy, with HMAC signatures I computed from
the on-host secret (read with root; value never printed/committed):
| probe | expect | got |
|---|---|---|
no X-Gitea-Signature |
401 | 401 |
| bad signature | 401 | 401 |
valid sig, event=ping (not issue_comment) |
204 | 204 |
valid sig, !testmexyz on a real PR |
204 (no trigger) | 204 |
valid sig, !testme but issue is not a PR |
204 | 204 |
valid sig, !testme on PR, action=edited |
204 | 204 |
valid sig, !testme on real PR, non-collaborator |
403 | 403 |
So: HMAC fail-closed + timing-safe (compare_digest, verified before body parse), !testmexyz
correctly ignored (exact trimmed match), non-PR ignored, and a non-collaborator is rejected (403;
collaborator status re-checked via Gitea API, not trusted from the signed payload). Source review
of bridge/bridge.py found no auth bypass.
Blocker independently corroborated (operator-side): the bridge hook is registered + active on
recipe-maintainers/cc-ci (id 210, events [issue_comment] → ci.commoninternet.net/hook), and
the bot is not a Gitea site-admin (GET /admin/hooks → 403) nor org owner, so it genuinely cannot
inspect/change Gitea's [webhook] ALLOWED_HOST_LIST. Endorse STATUS ## Blocked: needs operator
allowlisting or the documented poll-the-API fallback.
Still UNVERIFIED for an M3 PASS: (1) the positive path — a valid collaborator !testme actually
starts a build + posts the PR comment end-to-end; (2) real Gitea→bridge delivery (or the polling
pivot). Will complete both when M3 is claimed.
Noted for M7 (not a finding yet): the Drone-managed Gitea webhook (id 209) carries its webhook
secret as a ?secret= query param in the hook URL (Drone default; admin-only in Gitea, not in cc-ci
git / CI logs / dashboard). Will adjudicate against D6 at M7.
M4 — Harness + install stage: VERIFICATION IN PROGRESS (no verdict yet) @2026-05-27T00:35Z
M4 is CLAIMED. Code review done; runtime checks so far:
- A1 CLOSED (see BACKLOG): harness forces
LETS_ENCRYPT_ENV=""every deploy; live appcust-c95a69served the wildcard cert, 0 ACME lines, no certresolver. - Happy-path teardown works: a prior run's app
cust-e084bdwas fully torn down (gone) — not an orphan; earlier ambiguity was a run cycling apps. - Two teardown-robustness defects filed (A2, A3): janitor's
-prfilter is dead code under thecust-<hex>naming (no crash-orphan reaping); teardown is best-effort/unverified and deletes the.enveven on failed undeploy (silent orphan, run still green). - Deferred to next idle tick (a Builder harness run is active now; sequential-only): my own cold install run (green install + Playwright + clean teardown verification) and the §6 kill-mid-run probe to test A3 empirically. Verdict (PASS/FAIL) follows that.
M4 — Harness + install stage: PASS @2026-05-27T01:05Z
Verified by my own cold harness run (RECIPE=custom-html REF=advcold… cc-ci-run runner/run_recipe_ci.py, app cust-cfeb6a, isolated from a Builder run that happened to run
concurrently as cust-3c1970 — no collision, distinct domains/volumes/secrets):
- Install stage green:
test_install.py→ 2 passed (27s):test_http_reachable(HTTPS 200 via gateway) +test_playwright_page(real Chromium loads the live app, status 200, served HTML). - Guaranteed teardown: after the run,
cust-cfeb6aleft 0 services / volumes / secrets / containers /.env— fully clean. Infra (traefik/drone/bridge/backups) untouched. - A1 closed (no-ACME enforced). Open robustness findings A2 (dead
-prjanitor) + A3 (unverified best-effort teardown) concern the crash path (finalizer-skipped), not this happy-path run; they don't block M4's literal acceptance but must be resolved before DONE (D2 teardown guarantee). Kill-mid-run probe to substantiate A2/A3 deferred until the host is idle.
Verdict: M4 PASS.
M5 — Upgrade + backup/restore stages: PASS @2026-05-27T01:05Z
Same cold run, stages 2 and 3 — both genuine end-to-end (no mocks; assertions reviewed in source and not softened):
- Upgrade green:
test_upgrade.py→ 1 passed (41s). Deploys the previous published version (previous_version=recipe_versions[-2]), writes a marker into the volume-backed html dir, upgrades to latest (abra upgrade), then asserts HTTP 200 and the marker survives — a real version change with data persistence across the volume (cust-…_content), not a no-op. - Backup/restore green:
test_backup.py→ 1 passed (37s). Writesoriginal,abra backup, mutates tomutated(asserted),abra restore, then asserts the served content is back tooriginal("restore did not return the pre-mutation state"). Real backup→mutate→restore cycle via backup-bot-two. - Teardown clean (same
cust-cfeb6a0-remnant check above covers all three stages — same domain reused per stage).
Verdict: M5 PASS.
M6 — Recipe-local tests + second recipe: VERIFICATION IN PROGRESS (no verdict yet) @2026-05-27T01:48Z
M6 CLAIMED. Host has been continuously busy (Builder M6.5 ramp), so deploy-based checks are deferred to an idle window; static + evidence review so far:
- custom-html 3-stage: already verified cold by me (see M5 PASS) — green + clean teardown.
- D4 recipe-local discovery — code genuine:
run_recipe_ci.snapshot_recipe_testscopies the recipe-shippedtests/before abra re-checkouts to a version tag, thenrun_recipe_localdeploys the app and runs those tests against the LIVE app viaCCCI_BASE_URL/CCCI_APP_DOMAIN, merged as a separate stage with guaranteed teardown. Demo branchrecipe-maintainers/custom-html@ ci/d4-recipe-localconfirmed to shiptests/test_recipe_local.py(Gitea API). Will run it cold to confirm the stage executes+passes. - keycloak (#2) install — test genuine:
/realms/master200 health + real Playwright admin console login (waits for the username field).recipe_meta.py(HEALTH_PATH/timeouts) confirms D5 "no harness surgery". Empirical keycloak reproduction deferred (heavy deploy; idle window). - Filed [adversary] A4 (concurrency): same-recipe concurrent runs share
~/.abra/recipes/<recipe>with no isolation/lock/concurrency-cap — a collision vector for the §6 concurrency check; to confirm empirically.
Pending for idle host: cold D4 run, keycloak reproduce, A2/A3 kill-probe re-test, A4 concurrency test.
D6/M7 — preliminary leak scan of published Drone logs (PASS so far; M7 not yet claimed) @2026-05-27T02:05Z
Host-safe probe while the host was busy. Pulled Drone's database.sqlite, dumped all 42 logs
rows (~25.5k chars of published per-step build output), scanned:
- Known infra secrets — 0 leaks: webhook HMAC (64), drone token (32), gitea token (40) each
appear 0× in the logs (exact
grep -F). - No value patterns: 0 matches for
password|secret|token = <value>. - The only long hex/base64 hits are git commit SHAs in
git clone/mergeoutput — benign. Caveat: current Drone logs are hello-world + self-test; the full M7/D6 test must also cover app-generated secrets (e.g. keycloak DB passwords) in recipe-run logs AND the dashboard (M8). This is a clean baseline, not the final D6 verdict. (DB copy was scanned off-box and deleted; no secret value printed or committed.)
M3 — Comment bridge: PASS @2026-05-27T03:13Z
Verified cold against the NEW design (orchestrator change: polling-PRIMARY + org-membership auth;
webhook now optional). Re-reviewed bridge/bridge.py (256 lines) — sound — then live-probed the
running bridge + Drone:
!testmetriggers a run ≤60s: I posted!testme(comment 13708) on PR #1 at epoch 1779847690 → bridge[poll] triggered build 35→ Drone build 35 created at 1779847702 = 12s latency. (Build isfailureonly becauseRECIPE=cc-cihas notests/cc-ci/; the trigger + event=custom recipe-CI pipeline fired correctly — integration is live.)- Re-commenting re-runs: my new comment 13708 → build 35, distinct from the earlier
comment 13705 → build 26. Distinct comment ids each fire once (dedup via
_claim). - Other comments do NOT trigger: I posted
!testmexyz→ no build created, no bridge trigger log. Exact trimmed match enforced. - Auth enforced (org-membership, fail-closed):
GET /orgs/recipe-maintainers/members/<u>— autonomic-bot & notplants → 204 (allowed),definitely-not-a-member-zzz9→ 404 (rejected).is_authorizedreturns True only on 204/allowlist; anything else (incl. errors) → False. - Link back: bridge posted run-link comment 13706 ("cc-ci: started CI run … → drone…/recip…").
- Concurrency cap live: runner
capacity=1(DRONE_RUNNER_CAPACITY=1) + pipelineconcurrency:limit:1— recipe-CI builds serialize.
Verdict: M3 PASS. (Polling is outbound read+comment only — no repo-admin; webhook optional.) Note: full bridge→3-stage-recipe-CI E2E on a real recipe PR is the Builder's in-flight integration item / D10 — build 35 shows the pipeline wiring works; green-on-a-real-recipe is M10.
D6 — leak scan extended to recipe-CI build logs (still clean) @2026-05-27T04:05Z
Followup to the earlier hello-world scan: scanned the logs of all 7 event=custom recipe-CI builds
(~26.7k chars — these ran real abra app deploy + abra app secret generate, so generated app
secrets could surface here). Result: 0 password|secret = <value> patterns, 0 "secret
generated/inserted" value lines (abra doesn't echo secret values), and every long hex/base64 hit is
benign — Nix store paths, git SHAs, Drone workspace dir names (<rand16>/drone/src), pytest
tracebacks. No app-secret leak in published recipe-run logs. (Full M7/D6 verdict still pending the
dashboard (M8) leak check + final M7 claim.)
M6 — Recipe-local tests + second recipe: PASS @2026-05-27T04:43Z
Acceptance: "both recipes green (custom-html 3-stage; keycloak install) + recipe-local merged", plus D4/D5. Verified by a mix of my own cold runs + deep Drone-log corroboration (keycloak's 31-min deploy made a self-rerun impractical on the contended host, so I read the actual build #39 logs, not a Builder summary):
- custom-html 3-stage: my own cold run (see M5 PASS) — install/upgrade/backup green, 0 orphans.
- keycloak (#2) full 3-stage — build #39 (event=custom, RECIPE=keycloak, success): actual log
lines show
PASSED test_realm_endpoint_healthy,PASSED test_playwright_admin_login(install, 510s),PASSED test_upgrade_preserves_realm(upgrade, 610s — DB realm survived),PASSED test_backup_mutate_restore(backup, 495s — realm restored). Three separate reported stages (D2). Tests are genuine (admin REST + real Playwright admin-console login; reviewed source — not mocked). Post-run: 0 keycloak services/volumes (clean teardown). - D4 recipe-local — verified by my OWN run:
RECIPE=custom-html SRC=…/custom-html REF=ci/d4-recipe-local→ recipe-shippedtests/test_recipe_local.pysnapshotted to a temp dir (immune to abra's version re-checkout), deployed the app, rantest_recipe_local_serves_content PASSEDagainst the LIVE app viaCCCI_BASE_URL, merged as arecipe-localstage; clean teardown (0cust-leftovers). - D5 (no harness surgery): keycloak enrolled via
tests/keycloak/+recipe_meta.pyonly; no changes to sharedrunner/harnesscode. enroll-recipe.md documents the flow.
Verdict: M6 PASS. (keycloak full 3-stage also satisfies the first M6.5 breadth slot.)
M6.5 — breadth ramp: RUNNING EVIDENCE (no verdict yet — recipes 5–6 + gate pending) @2026-05-27T06:12Z
Deep-corroborating each recipe's canonical Drone recipe-ci build from its actual logs (genuine 3-stage assertions, not summaries). Confirmed green so far (categories in parens):
- custom-html (simple/stateless) — build #33 + my own cold 3-stage run (M4/M5).
- keycloak (SSO + DB-backed) — build #39: realm health + Playwright admin login (install),
test_upgrade_preserves_realm,test_backup_mutate_restore(M6 verdict). - cryptpad (stateful, no external DB) — build #46:
test_http_reachable,test_playwright_loads_cryptpad,test_upgrade_preserves_data,test_backup_mutate_restore. - matrix-synapse (large-volume / DB + media store) — build #51:
test_client_api_healthy,test_client_api_advertises_versions,test_upgrade_preserves_data,test_backup_mutate_restore. All three stages reported separately per build (D2). Categories covered: simple, SSO/DB, stateful, large-volume. Remaining: recipe #5/#6 (multi-service+S3/object-storage, e.g. lasuite; and the 6th for breadth) + the M6.5 gate. Final M6.5/D10 verdict after those + the §6 concurrency check.
Reconciliation @2026-05-27T06:18Z (watchdog ping)
Checked all standing claims: every CLAIMED milestone gate through M6 is Adversary-PASS — M0 @21:35, M1 @22:20, M2 @23:32, M3 @03:13, M4 @01:05, M5 @01:05, M6 @04:43 (all <24h). The "Gate: M0/M1/M2/M3 — CLAIMED, awaiting Adversary" strings still present in STATUS.md §Gates are stale (already cleared here); a watchdog scanning that section may false-positive on them — Builder may want to annotate them PASS. No open milestone claim right now: M6.5 is in-flight (4/6 recipes corroborated green: custom-html/keycloak/cryptpad/matrix-synapse; recipes 5–6 + the M6.5 gate pending), M7/M8/M9/M10 not yet claimed. Open findings: A2 (live janitor sweep pending an idle host; mechanism already verified). Nothing for me to verify is currently blocked on me.
M6.5 — Breadth ramp (recipes 3–6): PASS @2026-05-27T07:25Z
Acceptance: "recipes 3–6 each full three-stage green; enrolling N≥3 needed no shared-harness changes." All six recipes' canonical Drone recipe-ci builds deep-corroborated from their actual logs (genuine assertions + 3 separately-reported stages each; clean teardown):
- cryptpad #46 (stateful) — http + Playwright,
test_upgrade_preserves_data,test_backup_mutate_restore. - matrix-synapse #51 (large-volume/DB+media) —
test_client_api_healthy/_advertises_versions,test_upgrade_preserves_data,test_backup_mutate_restore. - lasuite-docs #57 (multi-service + S3/MinIO) —
test_http_reachable,test_playwright_loads_frontend,test_upgrade_preserves_data,test_backup_mutate_restore. - n8n #63 (workflow) —
test_healthz,test_playwright_loads_editor,test_upgrade_preserves_data,test_backup_mutate_restore. (recipes 1–2 custom-html #33/keycloak #39 verified under M4/M5/M6.) - D5 (no harness surgery) verified: grepped shared harness (
runner/harness,conftest,run_recipe_ci) — no per-recipe branching (if recipe==…); the only recipe names there are comments. Per-recipe quirks (cryptpad SANDBOX_DOMAIN, health paths, timeouts) live intests/<recipe>/recipe_meta.pyand are consumed via the genericEXTRA_ENV/meta hook indeploy_app. Enrolling a recipe =tests/<recipe>/+recipe_meta.pyonly. - bluesky→n8n swap is plan-sanctioned + documented (DECISIONS): bluesky-pds needs TLS-passthrough to an in-container caddy doing its own ACME — incompatible with the no-DNS-token/no-ACME design; documented non-CI'd recipe (per §2's explicit allowance). The 5 required D10 categories (simple/SSO+DB/stateful/large-volume/multi-service+S3) are covered without it.
Verdict: M6.5 PASS. Note: these builds were triggered as recipe-ci custom builds (RECIPE param);
the real !testme-on-a-PR end-to-end for the breadth set is D10/M10, still to verify.
M7 — Secrets hardening (D6): PASS @2026-05-27T07:55Z
Acceptance: "Adversary's secret-grep over published logs finds nothing; rotation doc followed." Verified the §9 hard rule (no plaintext secret in git, logs, or UI) across ALL surfaces:
- Published Drone logs — clean: dumped every
logsrow across all builds (~119k chars; incl. the 6 recipe runs that generate app secrets). The 3 infra secrets (webhook HMAC / drone token / gitea token, read from/run/secrets) each appear 0×; nopassword|secret|token=<value>patterns; long-token hits are git SHAs / nix paths / Drone workspace names (benign). - Dashboard — clean:
https://ci.commoninternet.net/(200) +/badge/*.svg: 0 secret patterns, 0 infra-secret values. - Git (all history) — clean: each infra secret 0×;
secrets/secrets.yamlis sops-encrypted (7×ENC[…]). No plaintext infra secret committed. - Redaction filter (
run_recipe_ci.run_stage_redacted): masks any/run/secrets/*value (≥8 chars) in stage stdout before it reaches Drone. Present as a safety net; 0REDACTEDmarkers in logs = no secret was ever echoed in the first place. - Rotation doc (
docs/secrets.md) matches reality:.sops.yamlhas exactly the documented two recipients — host keyage1h90ut…(from cc-ci's ed25519 SSH host key) + off-box master recoveryage1cmk26t…; sops-nix decrypts to/run/secrets/<name>(0400 root) using the SSH host key (verified at M0 + present now). A1/A2 split + rotation steps are coherent.
Minor (not a finding): the redaction list covers infra secrets only, not per-run generated app secrets — but abra doesn't echo generated secrets (recipe logs clean) so no app-secret ever surfaced.
Verdict: M7 PASS.
M8 — Dashboard (D7): PASS @2026-05-27T08:10Z
Acceptance: "overview matches reality across several runs; outcomes mirrored to PR comments."
- Overview matches reality:
https://ci.commoninternet.net/lists all 6 enrolled recipes, eachsuccesswith the exact canonical build #s I independently corroborated (cryptpad #46, custom-html #33, keycloak #39, lasuite-docs #57, matrix-synapse #51, n8n #63) + relative "last run" times; cc-ci itself correctly excluded; 30s auto-refresh; YunoHost-CI-like recipe table + status badges, dark theme. - Status badges:
/badge/keycloak.svgencodessuccess(per-recipe embeddable badge). - PR-comment outcome reflection: on PR #1 the bridge posted a start comment (id 13709 → run #35)
and a final-outcome comment (id 13712: "run for
cc-ci@d397720a❌ failure → …/76") — mirrors the final pass/fail and links the run. (Failure case shown; success path is the same code.) - No secret leak on the dashboard/badges (verified under M7).
Verdict: M8 PASS. (A green ✅ outcome reflected on a real recipe PR is exercised at D10/M10.)
M10/D10 — independent confirmation of the Docker Hub rate-limit blocker @2026-05-27T10:25Z
The Builder filed lasuite-docs upgrade failing on Docker Hub anonymous pull rate limits (A1 registry
creds needed; 5/6 recipes green via real !testme). I disbelieved and verified — it is real, not a
masked harness defect:
- Queried Docker Hub's rate-limit headers from cc-ci's own source IP (68.14.43.142):
ratelimit-limit: 100;w=21600,ratelimit-remaining: 1— i.e. ~1 anonymous pull left in the 6h window. The D10 breadth runs (6 recipes, lasuite alone = 9 images) drained the anonymous quota. - lasuite Drone builds (#88/#92 failure, #93 killed) show no
toomanyrequestsin pytest output — expected, because a rate-limited pull manifests at the docker/swarm task layer (deploy/health timeout), not in the test log; the header check is the direct proof. - The CI system itself is sound: lasuite install + backup are green; only the upgrade stage (most image pulls) is gated, and only by the external quota. This is precisely the plan's anticipated A1 input (§1.5/§4.4: "rate-limit failure traced to this is a finding, then request creds").
Consequence for DONE: D10 requires all 6 recipes green via real !testme with all 3 stages.
lasuite-docs upgrade cannot reliably pass without authenticated registry pulls. This is an
operator-action blocker (provide Docker Hub creds → sops secrets/), analogous to the M3 webhook
whitelist. Not a VETO of system quality; a missing external input. DONE must wait until lasuite's
upgrade goes green via !testme (creds provided, or quota-window retry verified stable).
M10/D10 — real-!testme proof: 5/6 VERIFIED (6th blocked on registry creds) @2026-05-27T10:42Z
Independently verified the full real-!testme path (D1 trigger + D2 three genuine stages + D7
outcome reflection) for 5 of 6 recipes, from a cold read of Drone + bridge logs + Gitea PR comments:
| recipe | build | bridge poll-trigger (real !testme) | stages | result |
|---|---|---|---|---|
| custom-html | #84 | PR#2 comment 13717 | 3 (4 asserts) | success |
| keycloak | #86 | PR#1 comment 13719 | 3 (4 asserts) | success |
| matrix-synapse | #87 | PR#1 comment 13720 | 3 (4 asserts) | success |
| n8n | #89 | PR#1 comment 13722 | 3 (4 asserts) | success |
| cryptpad | #90 | PR#2 comment 13727 | 3 (4 asserts) | success |
- Each build is
event=customwithREF=PR-head sha (tests the PR's code, D1), 3 separately-reported stages install/upgrade/backup (D2), and the bridge logged a genuine[poll] triggered build N … by autonomic-botfor each (real comment, not a manual build). - Outcome reflection (D7): verified on keycloak PR#1 —
!testme→ bridge comment "run forkeycloak@ 04400dff ✅ passed → …" (success path; ❌ failure path seen earlier on cc-ci). - 6th recipe lasuite-docs: install+backup green via
!testme, upgrade blocked on the Docker Hub anon rate limit (independently confirmed: remaining 1/100). Category = multi-service + S3/object-storage; until its upgrade is green via!testme, D10 is not fully met (5/6).
Verdict: D10 PARTIAL (5/6) — pass for 5; the 6th awaits operator registry creds. No system defect;
the gap is the external pull quota. DONE must wait for lasuite's 3rd stage green via !testme.
M9/D8 — Reproducibility: core PROVEN; full live blank-VM rebuild pending registry creds @2026-05-27T10:52Z
D8 ("entire server declared in the flake; rebuildable from scratch per docs/install.md; Adversary rebuilds on a throwaway VM OR documents why infeasible + what was tested"). Done so far:
- Nix-level reproducibility PROVEN (strongest evidence the repo is the server): synced repo
HEAD (clean
git archive, no .git) to an isolated host dir, rannixos-rebuild build --flake .#cc-ci→BUILD EXIT 0, and the built closure…m1pdvbhlmlj3x3gn0x83rgwcgssks7qs-nixos-system…is byte-identical to/run/current-system. So the entire running server (swarm, drone, traefik reconcile, comment-bridge, dashboard, backupbot, sops secrets) is fully declared in the repo with zero uncommitted drift — a clean rebuild reproduces it exactly. (nixos-rebuild buildis not rate-limited; image pulls happen at swarm runtime.) - docs/install.md is a complete from-scratch path: operator preconditions (A1) + the whole
install = clone + one
nixos-rebuild switch(reconcile oneshots auto-converge proxy/drone/bridge/ dashboard) + one-timebootstrap-drone-oauth.sh. Accurate vs. the verified architecture. - Deferred (per plan's documented-alternative allowance): a full from-scratch LIVE deploy on a blank NixOS VM (incus available) pulls every recipe/infra image at swarm runtime → hits the same Docker Hub anon rate limit confirmed under M10 (remaining 1/100). Since DONE is already gated on those operator registry creds, I will do the throwaway-VM live rebuild when creds arrive (unblocks D8 live + D10 lasuite together) rather than wall against the quota now.
Status: D8 reproducibility core PASS (Nix + docs); live blank-VM rebuild pending creds — to complete before DONE.
D9 — Documentation: PASS @2026-05-27T10:55Z
Acceptance: "README + docs/ explain architecture, enroll a recipe, add/run tests locally, operate/ rotate secrets, debug a failed run; a new engineer can enroll a recipe and get a green run using only the docs." Reviewed the full set:
- architecture.md — components, the
!testmeflow, network/TLS, resource safety. - enroll-recipe.md — mirror the recipe → add
tests/<recipe>/tree → recipe-local (D4) → add to bridge poll list → optional webhook → run locally. Matches the verified enroll mechanism (D5: I confirmed enrolling needs onlytests/<recipe>/+recipe_meta.py, no harness surgery). - runbook.md — where to look, common failure modes, orphans/cleanup, re-run/trigger by hand, cancel a stuck build (debug a failed run).
- secrets.md — sops model + rotation (verified accurate vs reality under M7).
- install.md — from-scratch server build (verified reproducible under M9/D8).
- README — entrypoint,
!testmeoverview, repo layout. The enroll flow documented matches what I exercised hands-on for D4/M6 (custom-html recipe-local) and what the Builder used for recipes 2–6 with no harness changes. Coverage is complete & accurate.
Verdict: D9 PASS.
Scrutiny — lasuite abra app upgrade -c (no-converge-checks) is NOT a test-softening @2026-05-27T11:45Z
The Builder's fix (575efb5) for lasuite's upgrade "convergence failure" adds -c to abra app upgrade. Per the anti-drift rule I checked whether this weakens the test to make a red pass — it
does not:
-cdisables only abra's convergence poll, which false-fails a slow 9-service rolling upgrade (stop-first roll while pulling new images) even when services do converge.- The harness's own verification post-upgrade is fully intact and is the real gate:
test_upgrade_preserves_data→upgrade_app→wait_healthy(=services_converged: every stack service N/N replicas, looped up to recipe_metaDEPLOY_TIMEOUT=900s + HTTP health loop), then assertshttp_get ∈ {200,301,302}and a realpsqlread that the pre-upgradeci_markerrow survived ("postgres data did not survive the upgrade"). - So a genuinely failed upgrade (services never reach N/N, app unhealthy, or DB data lost) still
fails the stage. The change trades abra's buggy/impatient check for the harness's more patient +
more meaningful one.
Cleared as legitimate. Still required for D10 6/6: an empirical lasuite upgrade green via real
!testme, whose build log I'll confirm shows genuine convergence (N/N) + the data-survival assertion passing — not just absence of an abra error.
M10/D10 — Proof: 6/6 PASS @2026-05-27T11:57Z
All six recipes now green via REAL !testme PRs, all three stages genuinely exercised — the 6th
(lasuite-docs) corroborated this tick:
- lasuite-docs build #108 (event=custom, REF=9f685240=PR#1 head): real trigger confirmed in
bridge log (
[poll] triggered build 108 for lasuite-docs@9f685240 (PR #1, comment 13738) by autonomic-bot). 3 stages green: install (test_http_reachable,test_playwright_loads_frontend, 148s); upgradetest_upgrade_preserves_dataPASSED (141s) — with the-cfix, the harness's ownwait_healthy(9 services N/N) + thepsqldata-survival check passed (no "did not survive"), so the upgrade genuinely converged + DB data persisted (NOT hollowed by-c); backuptest_backup_mutate_restorePASSED (158s). - Full D10 set (all via real
!testme, comment-reflected): custom-html #84 (simple), keycloak #86 (SSO/identity+DB), matrix-synapse #87 (large-volume/DB+media), n8n #89 (workflow), cryptpad #90 (stateful), lasuite-docs #108 (multi-service+S3/object-storage). All 5 required categories covered. - Registry creds (A1) turned out NOT to be required — the real blocker was abra's false-convergence
check (fixed by
-c); the rate limit was transient (quota recovered). Creds remain a documented good-to-have for robustness.
Verdict: D10 PASS (6/6).
D8 — Reproducible server: PASS (documented-alternative) @2026-05-27T12:00Z
D8 accepts either a throwaway-VM rebuild OR "documenting why a full from-scratch rebuild was infeasible and what was tested instead." A full from-scratch live rebuild on a throwaway host is infeasible by design, for two immovable reasons I verified:
- sops is bound to cc-ci's host identity —
modules/secrets.nixdecrypts via/etc/ssh/ssh_host_ed25519_key;.sops.yamlrecipients are only cc-ci's host age key + the master recovery key. A throwaway VM (different host key) is not a recipient → cannot decrypt the infra secrets → drone/bridge/etc. can't start without operator re-keying. - Operator preconditions are cc-ci-specific — the pre-issued wildcard cert
(
/var/lib/ci-certs/live) and the DNS*.ci.commoninternet.net → gateway → (passthrough) cc-cipoint at cc-ci itself; they can't be reproduced on a throwaway VM (operator-owned, immovable). What was tested instead (stronger than a fresh-VM rebuild): synced repo HEAD (clean, no .git) to an isolated dir andnixos-rebuild build --flake .#cc-ciproduced a closure byte-identical to/run/current-system— i.e. the entire running server (swarm, drone, traefik reconcile, comment-bridge, dashboard, backupbot, sops) is fully declared in the repo with zero uncommitted drift; a clean rebuild reproduces it exactly. install.md is an accurate single-nixos-rebuildfrom-scratch path + the documented operator preconditions. Every component was independently verified live on cc-ci (M0–M10).
Verdict: D8 PASS (Nix reproducibility proven byte-for-byte; throwaway-VM live rebuild infeasible by design — documented per the plan's explicit allowance).
DONE-readiness (Adversary) @2026-05-27T12:00Z
All D1–D10 have an Adversary PASS dated within 24h, and findings A1–A4 are all closed. No VETO.
| D | verdict | when |
|---|---|---|
| D1 trigger | PASS | M3 03:13 + D10 real-!testme runs |
| D2 3-stage matrix | PASS | M4/M5/M6 + D10 6/6 (real, 3 stages each) |
| D3 Playwright | PASS | live in every recipe install/D10 run |
| D4 recipe-local | PASS | M6 (own run) |
| D5 per-recipe tree / no harness surgery | PASS | M6.5 |
| D6 secrets | PASS | M7 (grep clean: logs+dashboard+git) |
| D7 results UX | PASS | M8 (overview matches reality + PR outcome) |
| D8 reproducible server | PASS | byte-identical build==running + documented-alt |
| D9 docs | PASS | full docs set reviewed |
| D10 six recipes via !testme | PASS (6/6) | #84/#86/#87/#89/#90/#108 |
| From the Adversary side, the DONE handshake (§6.1) is CLEARED — Builder may flip STATUS → DONE. | ||
| (Note: registry creds remain a documented good-to-have for rate-limit robustness, not a DONE blocker.) |
Adversary sign-off on DONE @2026-05-27T12:12Z
STATUS shows ## DONE (Builder, 1c10fa5). Final cold reality check confirms it is not a ledger lie:
- All D1–D10 carry an Adversary PASS dated 2026-05-27 (<24h); findings A1–A4 all closed; no
standing
## VETO. - Live system:
systemctl is-system-running→ running, 0 failed units. - Dashboard (
ci.commoninternet.net): 6/6 recipes success, matching the corroborated Drone builds (#84/#86/#87/#89/#90/#108, all real-!testme, 3 genuine stages each). - Steady state clean: 0 orphaned
<tag>-<6hex>test apps/volumes; teardown + janitor verified. The DONE is confirmed. Adversary loop terminating — exit condition met (STATUS## DONE+ fresh PASS logged for every D1–D10). Standing note: Docker Hub registry creds remain a documented good-to-have for rate-limit robustness (not a correctness gap).