A tracking issue for the one infrastructure step we deferred tonight (2026-10-26): standing up CI so that builds and releases stop depending on the steward agent. Related context in #17 (agent on co-op infra).
Tonight's session closed a chunk of this: the LibreChat per-member key seed moved inside the chat app (co-op/librechat-custom #1, shipped as image 20261026-13f2b3a), the release flow is documented and reproducible as ./release.sh in that repo, and the appstore pin was bumped to 1.4.5 (20261026-60ee597). Both releases were built remotely and deployed with verification. What remains: the build credential still lives on the agent host, and releases still need either the agent or a human holding that credential.
What already exists (no new purchases needed)
Gitea Actions is enabled on git.inference.coop (Gitea 28.2.0): the engine, runs UI, and org/repo secrets are built in. What's missing is a runner — a small stateless binary that registers with the org (registration token mints via the API in one call) and executes jobs. No runner exists yet.
The registry app (registry.inference.coop) runs builds remotely on its own BuildKit. A CI layer needs no docker anywhere — just the cloudron CLI plus two credentials (CLOUDRON_TOKEN, a registry app password). The librechat-custom repo's ./release.sh already encodes the whole flow (build → pin → deploy → verify) and survived two real releases tonight.
Woodpecker CI is in the Cloudron appstore (org.woodpecker_ci.cloudronapp 3.19.0) — I checked, assuming it wasn't.
Workflows live in each app's repo (.gitea/workflows/), so the release path is readable by any member next to the code it ships. Runs and logs live in Gitea next to the PRs.
Runner is a single binary in shell mode; needs custom Cloudron packaging (~a 30-line Dockerfile + manifest) since nothing packages it.
Org secrets hold the two credentials — moving them off the agent host entirely.
YAML dialect is the GitHub one most people already know.
Route B — Woodpecker CI from the appstore (est. 1–1.5 h, mostly config)
Install from the store, wire to Gitea via OAuth app + webhook, put the credentials in its secret store, add .woodpecker/ pipelines that call the same ./release.sh.
Costs a second always-on service with its own update cadence and YAML dialect, and its agents conventionally want docker for step isolation — an open question on Cloudron.
Both routes end in the same architecture: builds on merge, deploys deliberate (manual dispatch), the registry credential held by CI instead of by the agent or a person.
Why the agent shouldn't hold the build credential long-term
The interim state is acceptable — the token is scoped to the registry app only, stored in a 0600 file, and minted deliberately — but it's a credential that lets its holder push images, sitting on a host whose long-term role (per #17) is reporting and conversation, not infrastructure mutation. Once CI holds it, the agent-side copy gets revoked and the agent's routine powers shrink another notch.
Open decisions
Route — my lean is A: one fewer service, the workflow format travels, and the release record lives in Gitea next to the code. B is right if we want it running tonight with the least custom code.
Where the runner runs — recommended: its own tiny Cloudron app (disposable, backed up, off the agent VM). The agent VM is the one place I'd not put it: shell-mode runners execute workflow steps locally, and workflow files are just files in repos.
Which app onboards first — chat is done (it was the test case). Suggest the portal next, since it changes most often; the others are mechanical after that.
Who presses deploy — recommend manual dispatch (build automatic, deploy deliberate). A scheduled/auto-deploy is a later, separate decision.
Interim state (safe to sit here)
./release.sh is human-runnable from any host with the registry credential; the current pinned images are verified (20261026-60ee597 on base 1.4.5, with 20261026-13f2b3a and 20261023-65a38bb as rollback tags). The credential is rotated-revocable in one place. Nothing is blocked waiting on this issue — but the longer we wait, the more releases run through the interim path.
## What this is
A tracking issue for the one infrastructure step we deferred tonight (2026-10-26): standing up CI so that **builds and releases stop depending on the steward agent**. Related context in #17 (agent on co-op infra).
Tonight's session closed a chunk of this: the LibreChat per-member key seed moved **inside the chat app** (co-op/librechat-custom #1, shipped as image `20261026-13f2b3a`), the release flow is documented and reproducible as `./release.sh` in that repo, and the appstore pin was bumped to 1.4.5 (`20261026-60ee597`). Both releases were built remotely and deployed with verification. What remains: the *build credential* still lives on the agent host, and releases still need either the agent or a human holding that credential.
## What already exists (no new purchases needed)
- **Gitea Actions is enabled** on git.inference.coop (Gitea 28.2.0): the engine, runs UI, and org/repo secrets are built in. What's missing is a **runner** — a small stateless binary that registers with the org (registration token mints via the API in one call) and executes jobs. No runner exists yet.
- **The registry app** (registry.inference.coop) runs builds remotely on its own BuildKit. A CI layer needs **no docker anywhere** — just the `cloudron` CLI plus two credentials (`CLOUDRON_TOKEN`, a registry app password). The librechat-custom repo's `./release.sh` already encodes the whole flow (build → pin → deploy → verify) and survived two real releases tonight.
- **Woodpecker CI is in the Cloudron appstore** (`org.woodpecker_ci.cloudronapp` 3.19.0) — I checked, assuming it wasn't.
## The decision: two viable routes
**Route A — Gitea Actions + act_runner** (est. 2–3 h, incl. debugging)
- Workflows live in each app's repo (`.gitea/workflows/`), so the release path is readable by any member next to the code it ships. Runs and logs live in Gitea next to the PRs.
- Runner is a single binary in shell mode; needs custom Cloudron packaging (~a 30-line Dockerfile + manifest) since nothing packages it.
- Org secrets hold the two credentials — moving them off the agent host entirely.
- YAML dialect is the GitHub one most people already know.
**Route B — Woodpecker CI from the appstore** (est. 1–1.5 h, mostly config)
- Install from the store, wire to Gitea via OAuth app + webhook, put the credentials in its secret store, add `.woodpecker/` pipelines that call the same `./release.sh`.
- Costs a second always-on service with its own update cadence and YAML dialect, and its agents conventionally want docker for step isolation — an open question on Cloudron.
Both routes end in the same architecture: **builds on merge, deploys deliberate (manual dispatch), the registry credential held by CI instead of by the agent or a person.**
## Why the agent shouldn't hold the build credential long-term
The interim state is acceptable — the token is scoped to the registry app only, stored in a 0600 file, and minted deliberately — but it's a credential that lets its holder push images, sitting on a host whose long-term role (per #17) is reporting and conversation, not infrastructure mutation. Once CI holds it, the agent-side copy gets revoked and the agent's routine powers shrink another notch.
## Open decisions
1. **Route** — my lean is A: one fewer service, the workflow format travels, and the release record lives in Gitea next to the code. B is right if we want it running tonight with the least custom code.
2. **Where the runner runs** — recommended: its own tiny Cloudron app (disposable, backed up, off the agent VM). The agent VM is the one place I'd *not* put it: shell-mode runners execute workflow steps locally, and workflow files are just files in repos.
3. **Which app onboards first** — chat is done (it was the test case). Suggest the portal next, since it changes most often; the others are mechanical after that.
4. **Who presses deploy** — recommend manual dispatch (build automatic, deploy deliberate). A scheduled/auto-deploy is a later, separate decision.
## Interim state (safe to sit here)
`./release.sh` is human-runnable from any host with the registry credential; the current pinned images are verified (`20261026-60ee597` on base 1.4.5, with `20261026-13f2b3a` and `20261023-65a38bb` as rollback tags). The credential is rotated-revocable in one place. Nothing is blocked waiting on this issue — but the longer we wait, the more releases run through the interim path.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What this is
A tracking issue for the one infrastructure step we deferred tonight (2026-10-26): standing up CI so that builds and releases stop depending on the steward agent. Related context in #17 (agent on co-op infra).
Tonight's session closed a chunk of this: the LibreChat per-member key seed moved inside the chat app (co-op/librechat-custom #1, shipped as image
20261026-13f2b3a), the release flow is documented and reproducible as./release.shin that repo, and the appstore pin was bumped to 1.4.5 (20261026-60ee597). Both releases were built remotely and deployed with verification. What remains: the build credential still lives on the agent host, and releases still need either the agent or a human holding that credential.What already exists (no new purchases needed)
cloudronCLI plus two credentials (CLOUDRON_TOKEN, a registry app password). The librechat-custom repo's./release.shalready encodes the whole flow (build → pin → deploy → verify) and survived two real releases tonight.org.woodpecker_ci.cloudronapp3.19.0) — I checked, assuming it wasn't.The decision: two viable routes
Route A — Gitea Actions + act_runner (est. 2–3 h, incl. debugging)
.gitea/workflows/), so the release path is readable by any member next to the code it ships. Runs and logs live in Gitea next to the PRs.Route B — Woodpecker CI from the appstore (est. 1–1.5 h, mostly config)
.woodpecker/pipelines that call the same./release.sh.Both routes end in the same architecture: builds on merge, deploys deliberate (manual dispatch), the registry credential held by CI instead of by the agent or a person.
Why the agent shouldn't hold the build credential long-term
The interim state is acceptable — the token is scoped to the registry app only, stored in a 0600 file, and minted deliberately — but it's a credential that lets its holder push images, sitting on a host whose long-term role (per #17) is reporting and conversation, not infrastructure mutation. Once CI holds it, the agent-side copy gets revoked and the agent's routine powers shrink another notch.
Open decisions
Interim state (safe to sit here)
./release.shis human-runnable from any host with the registry credential; the current pinned images are verified (20261026-60ee597on base 1.4.5, with20261026-13f2b3aand20261023-65a38bbas rollback tags). The credential is rotated-revocable in one place. Nothing is blocked waiting on this issue — but the longer we wait, the more releases run through the interim path.