chore: bootstrap cc-ci loop state

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 21:07:31 +01:00
commit c21cce51b9
14 changed files with 245 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -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-*

62
BACKLOG.md Normal file
View File

@ -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 36 covering remaining D10 categories, no harness surgery
- [ ] Gate: M6.5 — recipes 36 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
<!-- Adversary-only section. Builder must not edit below this line. -->

33
DECISIONS.md Normal file
View File

@ -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 M4M6.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)

24
JOURNAL.md Normal file
View File

@ -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).

39
README.md Normal file
View File

@ -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 D1D10 (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/<recipe>/ 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.

7
REVIEW.md Normal file
View File

@ -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 (`<id>: PASS @<ts>` +
evidence, or `FAIL` + a finding in `BACKLOG.md ## Adversary findings`), and may write `## VETO`.
<!-- Adversary verdicts below -->

17
STATUS.md Normal file
View File

@ -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.

0
bridge/.gitkeep Normal file
View File

0
dashboard/.gitkeep Normal file
View File

48
docs/baseline.md Normal file
View File

@ -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`.

0
hosts/cc-ci/.gitkeep Normal file
View File

0
runner/harness/.gitkeep Normal file
View File

0
secrets/.gitkeep Normal file
View File

0
tests/_template/.gitkeep Normal file
View File