From c21cce51b9786689d97cb75ca02fdf370a7a17f7 Mon Sep 17 00:00:00 2001 From: autonomic-bot Date: Tue, 26 May 2026 21:07:31 +0100 Subject: [PATCH] chore: bootstrap cc-ci loop state Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 15 ++++++++++ BACKLOG.md | 62 ++++++++++++++++++++++++++++++++++++++++ DECISIONS.md | 33 +++++++++++++++++++++ JOURNAL.md | 24 ++++++++++++++++ README.md | 39 +++++++++++++++++++++++++ REVIEW.md | 7 +++++ STATUS.md | 17 +++++++++++ bridge/.gitkeep | 0 dashboard/.gitkeep | 0 docs/baseline.md | 48 +++++++++++++++++++++++++++++++ hosts/cc-ci/.gitkeep | 0 runner/harness/.gitkeep | 0 secrets/.gitkeep | 0 tests/_template/.gitkeep | 0 14 files changed, 245 insertions(+) create mode 100644 .gitignore create mode 100644 BACKLOG.md create mode 100644 DECISIONS.md create mode 100644 JOURNAL.md create mode 100644 README.md create mode 100644 REVIEW.md create mode 100644 STATUS.md create mode 100644 bridge/.gitkeep create mode 100644 dashboard/.gitkeep create mode 100644 docs/baseline.md create mode 100644 hosts/cc-ci/.gitkeep create mode 100644 runner/harness/.gitkeep create mode 100644 secrets/.gitkeep create mode 100644 tests/_template/.gitkeep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b114f2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# local secrets / env — never commit +.testenv +*.key +*.pem +!docs/**/*.pem +# python +__pycache__/ +*.pyc +.venv/ +.pytest_cache/ +# run-scoped ephemeral state (per §4.4 sidecars) +runs/ +# nix +result +result-* diff --git a/BACKLOG.md b/BACKLOG.md new file mode 100644 index 0000000..f65d2e5 --- /dev/null +++ b/BACKLOG.md @@ -0,0 +1,62 @@ +# BACKLOG — cc-ci + +Two single-writer sections (§6.1): Builder edits only `## Build backlog`; Adversary edits only +`## Adversary findings`. Closing an item = checking the box in your own section. + +## Build backlog + +### M0 — Foundations +- [ ] Author flake.nix (NixOS host cc-ci) + hosts/cc-ci/{configuration,hardware}.nix from baseline +- [ ] Deploy mechanism decision + first rebuild from repo (DECISIONS.md) +- [ ] sops-nix wiring: host age key, secrets/secrets.yaml, decrypt a test secret on host +- [ ] Gate: M0 — `ssh cc-ci 'systemctl is-system-running'` healthy after rebuild from repo + +### M1 — Swarm + abra target +- [ ] Docker + single-node swarm via Nix +- [ ] Traefik (file provider → /var/lib/ci-certs/live/) + per-run wildcard router +- [ ] abra installed; deploy + tear down a trivial recipe by hand over HTTPS +- [ ] Gate: M1 — recipe reachable over HTTPS at *.ci.commoninternet.net, torn down clean + +### M2 — Drone online +- [ ] Drone server + exec runner via Nix; Gitea OAuth app +- [ ] hello-world .drone.yml runs green; logs in Drone UI +- [ ] Gate: M2 — push to cc-ci triggers visible green build + +### M3 — Comment bridge +- [ ] comment-bridge service: HMAC verify, !testme exact match, collaborator check, Drone API call +- [ ] PR comment posting with run link +- [ ] Gate: M3 — live demo on scratch PR; auth enforced + +### M4 — Harness + install stage +- [ ] run_recipe_ci.py + conftest; install stage for recipe #1 + Playwright assertion; teardown +- [ ] Gate: M4 — green install run, no orphaned app/volume + +### M5 — Upgrade + backup/restore stages +- [ ] Add upgrade + backup/restore stages for recipe #1 +- [ ] Gate: M5 — upgrade preserves data; backup→mutate→restore returns original + +### M6 — Recipe-local tests + second recipe +- [ ] Discover/run recipe-repo tests/; enroll DB-backed recipe #2 +- [ ] Gate: M6 — both green; recipe-local tests merged + +### M6.5 — Breadth ramp (recipes 3→6) +- [ ] Enroll recipes 3–6 covering remaining D10 categories, no harness surgery +- [ ] Gate: M6.5 — recipes 3–6 three-stage green + +### M7 — Secrets hardening (D6) +- [ ] Full sops model, rotation doc, log redaction + leak test +- [ ] Gate: M7 — secret-grep finds nothing + +### M8 — Dashboard (D7) +- [ ] Overview page + badges + PR-comment outcome reflection +- [ ] Gate: M8 — overview matches reality; outcomes mirrored + +### M9 — Reproducibility + docs (D8/D9) +- [ ] docs/install.md from-scratch rebuild; all docs complete +- [ ] Gate: M9 — Adversary rebuilds from docs on throwaway host + +### M10 — Proof (D10) +- [ ] All six recipes green via real !testme PRs; flip STATUS to DONE + +## Adversary findings + diff --git a/DECISIONS.md b/DECISIONS.md new file mode 100644 index 0000000..eddcb64 --- /dev/null +++ b/DECISIONS.md @@ -0,0 +1,33 @@ +# DECISIONS — cc-ci Builder + +Architecture decisions and dead-ends. One line of rationale each. (§0, §8) + +## Settled + +- **Wildcard TLS:** operator pre-issues wildcard cert at `/var/lib/ci-certs/live/`; Traefik file + provider serves it; **no ACME** for commoninternet.net. (Plan §4.0/§8 — fixed.) +- **Repo:** `git.autonomic.zone/recipe-maintainers/cc-ci`, private. Bot is org admin. (Bootstrap.) +- **Git credentials:** helper script in repo-local git config sources `/srv/cc-ci/.testenv` at call + time — no secret values stored in `.git/config` or commits. + +## Open (defaults from §8, to confirm as reality lands) + +- **Deploy mechanism:** TBD in M0. Leaning `nixos-rebuild switch --flake` run *on cc-ci itself* + (repo cloned on host) rather than `--target-host`/deploy-rs from the sandbox, to avoid copying + large Nix closures over the userspace-tailscaled SOCKS proxy. Atomic-rollback is preserved by + Nix generations. Will record final choice + rationale when M0 lands. +- **Webhook scope:** default per-repo via enroll script. +- **Drone runner type:** default exec (must drive host abra). +- **Secret tool:** default sops-nix. +- **D10 recipe set:** lock six early. Candidates favouring already-mirrored: custom-html (simple), + cryptpad (stateful no-DB), keycloak (SSO/DB), matrix-synapse (DB+media), lasuite-docs (multi+S3), + bluesky-pds (TLS-passthrough) — covers all five categories. Confirm during M4–M6.5. + +## Risks + +- **Disk:** cc-ci has only ~3.8 GiB free on an 8.9 GiB root. Multiple recipe images + volumes may + exhaust it during M6.5 breadth. Mitigation: aggressive teardown + image prune; if insufficient, + request operator grow the VM disk (Incus, recreatable per the incus skill). Not yet blocking. + +## Dead-ends +- (none yet) diff --git a/JOURNAL.md b/JOURNAL.md new file mode 100644 index 0000000..83410c7 --- /dev/null +++ b/JOURNAL.md @@ -0,0 +1,24 @@ +# JOURNAL — cc-ci Builder (append-only) + +## 2026-05-26 — Bootstrap (§1) + +**Access verification (all pass):** +- `ssh cc-ci 'hostname && whoami && nixos-version'` → `nixos` / `root` / `24.11.719113.50ab793786d9 (Vicuna)` +- `curl https://git.autonomic.zone/api/v1/version` → `{"version":"1.24.2"}` +- Gitea bot auth (`curl -u $GITEA_USERNAME:$GITEA_PASSWORD .../api/v1/user`) → `login: autonomic-bot`, id 64 +- `getent hosts probe-$RANDOM.ci.commoninternet.net` → `143.244.213.108` (the gateway IP, as expected — TLS passthrough) +- Cert present: `ls /var/lib/ci-certs/live/` → `fullchain.pem` (2909 b), `privkey.pem` (227 b, mode 640) +- recipe-maintainers org exists (private); `recipe-maintainers/cc-ci` → 404 (created below) +- Mirrored recipes already present: bluesky-pds, lasuite-docs, custom-html, custom-html-tiny, n8n, + keycloak, lasuite-meet, matrix-synapse, cryptpad + +**Baseline (docs/baseline.md):** fresh NixOS 24.11 Incus VM, 2 vCPU, 3.5 GiB RAM, 8.9 GiB disk +(3.8 GiB free). No docker/swarm/abra. Channel-based `/etc/nixos/configuration.nix` (no flake). + +**Actions:** +- Created repo `recipe-maintainers/cc-ci` (private) via Gitea API. +- `git init` in /srv/cc-ci/cc-ci; credential helper reads creds from /srv/cc-ci/.testenv (no + secrets stored in git config). +- Seeded skeleton layout (§3) + loop-state files + docs/baseline.md. + +**Next:** commit + push bootstrap, then M0 (flake + base config + sops test secret). diff --git a/README.md b/README.md new file mode 100644 index 0000000..82fc764 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# cc-ci — Co-op Cloud recipe CI server + +Comment **`!testme`** on a PR in an enrolled Co-op Cloud recipe repo and cc-ci deploys the recipe +at that commit onto a real single-node Docker Swarm, runs install / upgrade / backup-restore tests +(Python + Playwright) end-to-end, and reports a live, tail-able run with pass/fail back to the PR. + +This repo declares the **entire server** as a NixOS flake and holds the test harness, the +per-recipe test trees, and the docs to enroll a recipe or rebuild the box from scratch. + +> Status: under active autonomous construction. See `STATUS.md` for the live phase and +> `plan.md`-driven milestones in `BACKLOG.md`. Definition of Done is D1–D10 (see the build plan). + +## Layout + +``` +flake.nix NixOS host(s) + devshell +hosts/cc-ci/ the cc-ci machine config +modules/ drone, comment-bridge, swarm, dashboard, secrets (Nix modules) +secrets/ sops-encrypted infra secrets +bridge/ !testme webhook listener source +runner/ run_recipe_ci.py + shared pytest harness +dashboard/ results overview generator +tests// per-recipe install/upgrade/backup tests + playwright/ +docs/ install, enroll-recipe, secrets, architecture, runbook, baseline +``` + +## Docs + +- `docs/install.md` — rebuild the server from scratch (D8) +- `docs/enroll-recipe.md` — add a recipe under CI (D5) +- `docs/secrets.md` — secret model + rotation (D6) +- `docs/architecture.md`, `docs/runbook.md` — design + debugging failed runs +- `docs/baseline.md` — bootstrap snapshot / rollback reference + +## Loop state (autonomous build) + +`STATUS.md` (phase/blockers), `BACKLOG.md` (work + adversary findings), `REVIEW.md` (independent +verification), `JOURNAL.md` (build log), `DECISIONS.md` (architecture choices). See the build plan +for the two-loop Builder/Adversary protocol. diff --git a/REVIEW.md b/REVIEW.md new file mode 100644 index 0000000..3cc973b --- /dev/null +++ b/REVIEW.md @@ -0,0 +1,7 @@ +# 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 (`: PASS @` + +evidence, or `FAIL` + a finding in `BACKLOG.md ## Adversary findings`), and may write `## VETO`. + + diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 0000000..7ab8747 --- /dev/null +++ b/STATUS.md @@ -0,0 +1,17 @@ +# STATUS — cc-ci Builder + +**Phase:** M0 — Foundations +**In-flight:** Bootstrap complete; starting M0 (flake + base config + sops test secret). +**Last updated:** 2026-05-26 (bootstrap) + +## Gates +- (none claimed yet) + +## Blocked +- (none) + +## Notes +- cc-ci baseline: Incus VM, 2 vCPU, 3.5 GiB RAM, **3.8 GiB free disk** — tight for multi-recipe + docker deploys; watch disk pressure, may need operator to grow the VM disk before M6.5 breadth. +- Server config is currently channel-based `/etc/nixos/configuration.nix` (no flake). M0 converts + to a flake checked out from this repo on the host. diff --git a/bridge/.gitkeep b/bridge/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/dashboard/.gitkeep b/dashboard/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/baseline.md b/docs/baseline.md new file mode 100644 index 0000000..a09da51 --- /dev/null +++ b/docs/baseline.md @@ -0,0 +1,48 @@ +# Baseline — cc-ci starting environment (rollback reference) + +Captured at bootstrap, 2026-05-26, before any Builder changes. This is the state to roll back to. + +## Host + +- Hostname: `nixos` (Tailscale node `cc-nix-test`, tailnet IP **100.90.116.4**, tailnet + `taila4a0bf.ts.net`). +- OS: **NixOS 24.11** `24.11.719113.50ab793786d9 (Vicuna)`. +- Virtualisation: **Incus VM** (imports `virtualisation/incus-virtual-machine.nix`), incus agent on. +- Resources: **2 vCPU, 3.5 GiB RAM, 8.9 GiB root disk (4.7 GiB used / 3.8 GiB free)**. +- Access: SSH as **root** (PermitRootLogin yes), reached from sandbox via userspace-tailscaled + SOCKS proxy `127.0.0.1:1055` → `ssh cc-ci`. + +## Installed / present + +- Config: **channel-based**, no flake. `/etc/nixos/`: + - `configuration.nix` — incus VM module, cloud-init, tailscale (auth-key file), openssh, + base pkgs (curl git jq openssh), firewall (trust tailscale0, allow tcp/22), DHCP, + nameservers 1.1.1.1/8.8.8.8, `nix.settings.experimental-features = [nix-command flakes]`, + `system.stateVersion = "24.11"`. + - `incus-base.nix` — tailscale auth-key + hostname from `/etc/ts-hostname`. + - `setup.sh` — original provisioning script (channel add + `nixos-rebuild boot` + sysrq reboot). +- **No** docker, **no** swarm, **no** abra installed. +- Tailscale up and authenticated (state persists; reconnects without key). + +## Provided infra inputs (operator-owned, do not improvise — §4.4 class A1) + +- Wildcard TLS cert at **`/var/lib/ci-certs/live/{fullchain.pem,privkey.pem}`** + (`*.ci.commoninternet.net` + `ci.commoninternet.net`, LE 90-day, next renewal ~2026-08-24). + Agent serves it via Traefik file provider; **never** runs ACME for this domain. +- DNS: wildcard `*.ci.commoninternet.net` (+ bare `ci.commoninternet.net`) → **gateway** + `143.244.213.108` (Gandi-hosted public zone). Gateway TLS-passthroughs the whole wildcard to + cc-ci by SNI; TLS terminates on cc-ci's Traefik. Per-run subdomains need no DNS/gateway/cert work. +- Gitea bot `autonomic-bot` (id 64), admin on private org `recipe-maintainers`. +- Tailscale auth key (reusable) — in `/srv/cc-ci/.testenv`. + +## Recipes already mirrored to recipe-maintainers (at bootstrap) + +`bluesky-pds`, `cryptpad`, `custom-html`, `custom-html-tiny`, `keycloak`, `lasuite-docs`, +`lasuite-meet`, `matrix-synapse`, `n8n`. Others (hedgedoc, authentik, immich, lasuite-drive) are +pulled from upstream git.coopcloud.tech and mirrored via the recipe mirror+PR flow (§4.1) as needed. + +## Rollback + +The original config is preserved above and in the host's Nix generations +(`nixos-rebuild --rollback` / boot menu). To fully revert, restore `/etc/nixos/*` to the channel +config above and `nixos-rebuild switch`. diff --git a/hosts/cc-ci/.gitkeep b/hosts/cc-ci/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/runner/harness/.gitkeep b/runner/harness/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/secrets/.gitkeep b/secrets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/_template/.gitkeep b/tests/_template/.gitkeep new file mode 100644 index 0000000..e69de29