cc-ci host: the orchestrator is an opencode agent; no Claude on the box; /secrets convention
Operator 2026-09-07 (evening): Claude sessions stay on notplants-orchestrator; on the cc-ci host the orchestrator is an opencode agent (agents.toml: opencode/glm-5.2) steered from https://oc.ci.commoninternet.net, next to the upgrader/report sessions. claude-install and CLAUDE_BIN are gone from the modules; launch-upgrader/report print the real UI URL; README "Operating the orchestrator" rewritten and a "Weekly upgrade run" section added. Secrets: only cc-ci's, under /secrets/files with runtime paths symlinked (README §4 table), nothing from other projects — tangled key and the tailscale line dropped from the host. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FqkQq3CDmFWcQ7u1LzoyRz
This commit is contained in:
@@ -9,7 +9,7 @@ module from the [cc-ci](https://git.autonomic.zone/recipe-maintainers/cc-ci) rep
|
||||
| | where |
|
||||
|---|---|
|
||||
| Orchestrator loops, timers (weekly upgrader, hourly supervisor) | `nix/modules/cc-ci.nix` → `nixosModules.cc-ci-orchestrator` |
|
||||
| The host contract those need (loops user, claude/opencode CLIs, opencode web UI) | `nix/modules/orchestrator-host.nix` → `nixosModules.orchestrator-host` |
|
||||
| The host contract those need (loops user, opencode CLI, opencode web UI) | `nix/modules/orchestrator-host.nix` → `nixosModules.orchestrator-host` |
|
||||
| The CI server (swarm, traefik, drone, runner, `!testme` bridge, dashboard, reports, acme-dns) | cc-ci repo `nix/modules/` → `nixosModules.cc-ci-server` (flake input `cc-ci`) |
|
||||
| The machine: hardware, networking, root keys, firewall + fail2ban | `nix/hosts/cc-ci/` → `nixosConfigurations.cc-ci` |
|
||||
| Plans, launch tooling, loop prompts, journal | `cc-ci-plan/` (see `AGENTS.md` for roles) |
|
||||
@@ -205,22 +205,24 @@ git clone https://git.autonomic.zone/recipe-maintainers/cc-ci.git cc-ci-adv #
|
||||
mkdir -p .cc-ci-logs .sops
|
||||
```
|
||||
|
||||
Then the files below (`install -m600 -o loops -g users`):
|
||||
Secrets live in **`/secrets/files/`** (owned by loops, mode 700 on the directory), the same
|
||||
convention as the notplants-orchestrator box, with the runtime paths symlinked to them so one
|
||||
`ls /secrets/files` is the complete inventory. **Only cc-ci's secrets go there** — nothing from
|
||||
other projects on the box they were copied from (no tailscale, lichen, tangled, b1, borg, …):
|
||||
secrets do not get spread around. `/secrets/README.txt` lists each file and its purpose.
|
||||
|
||||
| file | what | source |
|
||||
| runtime path → `/secrets/files/…` | what | source |
|
||||
|---|---|---|
|
||||
| `/srv/cc-ci/.testenv` | `TS_AUTH_KEY`, `GITEA_PASSWORD` (autonomic-bot), `DOCKERHUB_USERNAME/TOKEN`, model API keys | old host `/secrets/files/cc-ci.testenv`; fresh: create each credential |
|
||||
| `/srv/cc-ci/upgrader.env` | `LOOP_TIER`, `LOOP_MODEL`, `REPORT_MODEL` for the weekly run (no secrets) | old host, or copy the example in `AGENTS.md` |
|
||||
| `/srv/cc-ci/.sops/master-age.txt` | the same master age key as 4b (skills that re-key secrets use it) | old host |
|
||||
| `~loops/.ssh/cc-ci-root-ed25519` (+`.pub`) | `ssh cc-ci` as root — to loopback on this host | old host; fresh: `ssh-keygen -t ed25519` and add the pub to `nix/hosts/cc-ci/ssh-keys` |
|
||||
| `~loops/.ssh/autonomic-bot-gitea-ed25519` (+`.pub`) | pushes recipe branches / PRs as `autonomic-bot` | old host; fresh: new key added to the bot's Gitea account |
|
||||
| `~loops/.ssh/tangled-ed25519` | optional, tangled.org mirrors | old host |
|
||||
| `~loops/.claude/` | Claude Code auth + settings + the orchestrator session history | old host (`rsync -a`); fresh: `claude auth login` as loops (device code, interactive) |
|
||||
| `~loops/.local/share/opencode/auth.json`, `~loops/.config/opencode/` | opencode provider auth (the weekly upgrader runs on opencode) | old host; fresh: `opencode auth login` |
|
||||
| `/etc/nginx/oc-htpasswd` | basic auth for the opencode UI (`https://oc.ci.commoninternet.net`, via traefik); **nginx refuses to start without it**, and its config check runs as the `nginx` user, so `root:nginx 0640` (the `nginx` group exists after the first rebuild — fix ownership then and `systemctl restart nginx`) | old host (`/secrets/files/oc-basic-auth.txt` has the plaintext), or a new `oc:<bcrypt>` line via `mkpasswd -m bcrypt` |
|
||||
| `/srv/cc-ci/.testenv` → `cc-ci.testenv` | `GITEA_PASSWORD` (autonomic-bot), `DOCKERHUB_USERNAME/TOKEN`, model API keys used by opencode providers. No `TS_AUTH_KEY` (no tailscale here). | old host's copy minus the tailscale line; fresh: create each credential |
|
||||
| `/srv/cc-ci/upgrader.env` (not a secret, lives in the checkout) | `LOOP_TIER`, `LOOP_MODEL`, `REPORT_MODEL` for the weekly run | old host, or copy the example in `AGENTS.md` |
|
||||
| `/srv/cc-ci/.sops/master-age.txt` → `master-age.txt` | the same master age key as 4b (skills that re-key secrets use it); root's `/var/lib/sops-nix/key.txt` is a root-only copy of it | old host |
|
||||
| `~loops/.ssh/cc-ci-root-ed25519` (+`.pub`) → same names | `ssh cc-ci` as root — to loopback on this host | old host; fresh: `ssh-keygen -t ed25519` and add the pub to `nix/hosts/cc-ci/ssh-keys` |
|
||||
| `~loops/.ssh/autonomic-bot-gitea-ed25519` (+`.pub`) → same names | pushes recipe branches / PRs as `autonomic-bot`; root's `/root/.ssh/` copy clones the private cc-ci-secrets submodule | old host; fresh: new key added to the bot's Gitea account |
|
||||
| `~loops/.local/share/opencode/auth.json` → `opencode-auth.json`; `~loops/.config/opencode/opencode.jsonc` (config, not secret) | opencode provider auth + config — the orchestrator AND the weekly upgrader are opencode agents; there is no Claude on this host | old host; fresh: `opencode auth login` as loops |
|
||||
| `/etc/nginx/oc-htpasswd` (root:nginx copy; plaintext in `/secrets/files/oc-basic-auth.txt`) | basic auth for the opencode UI (`https://oc.ci.commoninternet.net`, via traefik); **nginx refuses to start without it**, and its config check runs as the `nginx` user, so `root:nginx 0640` (the `nginx` group exists after the first rebuild — fix ownership then and `systemctl restart nginx`) | old host (`/secrets/files/oc-basic-auth.txt` has the plaintext), or a new `oc:<bcrypt>` line via `mkpasswd -m bcrypt` |
|
||||
|
||||
`~loops/.ssh/config` is written by the activation script on first rebuild (`Host cc-ci` →
|
||||
`127.0.0.1`, `git.autonomic.zone`, `tangled.org`); it is not overwritten if present.
|
||||
`127.0.0.1`, `git.autonomic.zone`); it is not overwritten if present.
|
||||
|
||||
## 5. Build and activate
|
||||
|
||||
@@ -340,29 +342,38 @@ stopped on the new host while its directory is copied:
|
||||
|
||||
---
|
||||
|
||||
# Operating the orchestrator session
|
||||
# Operating the orchestrator
|
||||
|
||||
The steering session is a long-lived interactive Claude Code session under tmux with
|
||||
`--remote-control`, so it can be watched and steered from **claude.ai/code** (or the mobile app).
|
||||
`cc-ci-orchestrator.service` relaunches it on boot via `cc-ci-plan/agents.py up orchestrator`
|
||||
(backend + model in `cc-ci-plan/agents.toml`).
|
||||
The cc-ci orchestrator on this host is an **opencode agent** (`cc-ci-plan/agents.toml`:
|
||||
backend `opencode`, model `opencode/glm-5.2`), launched on boot by `cc-ci-orchestrator.service`
|
||||
via `cc-ci-plan/agents.py up orchestrator` into tmux session `cc-ci-orchestrator`, attached to
|
||||
the shared opencode web server. Steer it from the web UI, **https://oc.ci.commoninternet.net**
|
||||
(basic auth, user `oc`), where the weekly upgrader's and report's sessions show up too.
|
||||
There is no Claude Code on this host: Claude sessions run on the notplants-orchestrator box and
|
||||
operate the CI from there over ssh (`Host cc-ci` in that box's loops ssh config → this host).
|
||||
|
||||
```bash
|
||||
# attach on the host
|
||||
sudo -iu loops tmux attach -t cc-ci-orchestrator
|
||||
# or resume the conversation by hand in a fresh tmux pane
|
||||
claude --resume autonomous-orchestrator --remote-control "autonomous-orchestrator" --dangerously-skip-permissions
|
||||
# already inside a live session and just want the web surface? /remote-control
|
||||
# on the host
|
||||
sudo -iu loops tmux attach -t cc-ci-orchestrator # the agent's terminal
|
||||
systemctl restart cc-ci-orchestrator # relaunch it (fresh opencode session)
|
||||
sudo -iu loops tmux ls # cc-ci-orchestrator, cc-ci-upgrader (+watchdog) during the weekly run
|
||||
```
|
||||
|
||||
`--resume <name|id>` selects the *conversation* to restore; the `--remote-control "<name>"` value is
|
||||
only the web display label. Don't pass `--fork-session` unless you mean to branch.
|
||||
## Weekly upgrade run
|
||||
|
||||
`cc-ci-upgrade-all.timer` (Thu 22:00 America/New_York) starts `launch-upgrader.py`, which runs
|
||||
`/upgrade-all` as an opencode agent (`upgrader.env`: `LOOP_MODEL`/`REPORT_MODEL`) in tmux
|
||||
`cc-ci-upgrader` with a usage-limit watchdog; the hourly `cc-ci-upgrade-supervisor.timer` drives a
|
||||
stalled run to completion. Start one by hand with `systemctl start cc-ci-upgrade-all`; follow
|
||||
`/srv/cc-ci/.cc-ci-logs/cc-ci-upgrader.log`; it ends with `UPGRADE RUN COMPLETE`, a summary in
|
||||
`.cc-ci-logs/upgrades/upgrade-all-<date>.md`, and a report published to
|
||||
`https://report.ci.commoninternet.net/week-<date>.html`.
|
||||
|
||||
## Kick off / supervise the loops
|
||||
|
||||
```bash
|
||||
cd /srv/cc-ci/cc-ci-plan
|
||||
./launch.sh start # Builder + Adversary loops (interactive --remote-control in tmux) + watchdog
|
||||
./launch.sh start # Builder + Adversary loops + watchdog (all phases are DONE; this just confirms)
|
||||
./launch.sh status # session + DONE state
|
||||
./launch.sh logs builder|adversary|watchdog
|
||||
./launch.sh stop
|
||||
|
||||
Reference in New Issue
Block a user