9.1 KiB
Gitea Actions Runner Substrate
Last reviewed: 2026-06-07
Status: implementation contract v1 and attended operating runbook. This document does not contain runner registration tokens, package credentials, kubeconfigs, SSH keys, or decrypted secret values.
Purpose
Railiance needs a forge-owned Gitea Actions runner substrate so source and app
workflows can build, publish, and verify artifacts without each repo inventing
its own runner assumptions. The immediate blocker is inter-hub: its deployment
workflow currently targets self-hosted and haskelseed, but no forge-owned
runner deployment or health evidence existed after the first Gitea ownership
move.
This runbook implements the first supported runner model and keeps the separation clear:
railiance-forgeowns runner placement, registration, labels, health, and recovery.- Source and app repos own workflow files and app-specific build/deploy logic.
- Cluster deploy authority is not bundled into the build runner unless a separate human-reviewed approval says so.
Primary upstream reference: https://docs.gitea.com/usage/actions/act-runner
First Supported Runner
Use one attended Gitea act_runner on haskelseed as the compatibility runner
for the inter-hub unblock, unless an operator explicitly chooses a separate
runner host before registration.
Initial contract:
| Field | Value |
|---|---|
| Runner name | railiance-haskelseed-build-01 |
| Runner scope | coulomb organization runner, or repository runner if org scope is not approved |
| Initial host | haskelseed |
| Capacity | 1 |
| Runtime | Docker-backed or host-backed act_runner, confirmed during attended install |
| Cluster deploy authority | Not included in baseline runner approval |
| Registry publish authority | Allowed only through repo/workflow-scoped Gitea secrets |
The first runner exists to prove scheduling, build tooling, and registry publish
readiness. It must not silently become a cluster deployment runner. If a
workflow needs live cluster access, add a separate label and approval note such
as cluster-deploy or shift deployment to an app-owned release path.
Label Contract
Register the first runner with compatibility labels for the inter-hub workflow and semantic labels for future workflows:
self-hosted:host,haskelseed:host,linux:host,linux_amd64:host,container-build:host,registry-publish:host
Rules:
self-hostedandhaskelseedare compatibility labels for the first unblock.- New workflows should prefer semantic labels such as
linux,container-build, andregistry-publish. registry-publishmeans the runner may execute jobs that receive repo-scoped registry credentials from Gitea secrets.- Do not add
cluster-read,cluster-dry-run,cluster-deploy, ors5-release-checkuntil the cluster/platform credential path is reviewed. - Keep capacity at
1until haskelseed resource contention and workload safety are understood.
Gitea runner labels are supplied at registration with --labels and, on modern
Gitea/act_runner versions, can also be adjusted in the generated runner config.
Use the generated config from the installed runner version as the source of
truth for exact YAML keys.
Secret Boundaries
Runner registration and publish credentials are live secrets. They must be created and handled outside Git.
Allowed in this repo:
- secret names;
- operator prompts;
- paths such as
/run/secrets/railiance/gitea-act-runner-registration-token; - non-secret evidence such as runner name, labels, service status, workflow id, commit SHA, image tag, and digest.
Forbidden in this repo:
- registration token values;
- Gitea package or registry tokens;
- tokenized URLs;
- SSH private keys;
- kubeconfigs or OpenBao tokens;
- complete environment dumps from runner jobs.
Attended Install
Run these steps on the selected runner host. Adjust only the installation path and package manager details for the host; do not change the label contract without updating this document and State Hub.
-
Prepare the host:
sudo install -d -o root -g root -m 0755 /etc/act_runner sudo install -d -o act_runner -g act_runner -m 0750 /var/lib/act_runner sudo install -d -o act_runner -g act_runner -m 0750 /var/cache/act_runner -
Install
act_runnerusing the current Gitea-supported binary or package source for the host. Record the version, but do not commit downloaded binaries into this repo. -
Generate the runner config on the host:
sudo -u act_runner /usr/local/bin/act_runner generate-config \ | sudo tee /etc/act_runner/config.yaml >/dev/null sudo chmod 0640 /etc/act_runner/config.yaml sudo chown root:act_runner /etc/act_runner/config.yaml -
Edit the generated config so capacity is
1and labels match this document. If the generated config for the installed version uses a different section name, keep the generated structure and only change the corresponding values. -
Place the runner registration token in the approved secret path, for example:
/run/secrets/railiance/gitea-act-runner-registration-token -
Register non-interactively without printing the token:
sudo -u act_runner sh -lc ' set -eu token="$(cat /run/secrets/railiance/gitea-act-runner-registration-token)" exec /usr/local/bin/act_runner --config /etc/act_runner/config.yaml \ register --no-interactive \ --instance https://gitea.coulomb.social/ \ --token "$token" \ --name railiance-haskelseed-build-01 \ --labels self-hosted:host,haskelseed:host,linux:host,linux_amd64:host,container-build:host,registry-publish:host ' -
Install the matching service template, adjusted to the host paths if needed. For systemd hosts, use
runner/act-runner.service.example:sudo cp runner/act-runner.service.example /etc/systemd/system/act_runner.service sudo systemctl daemon-reload sudo systemctl enable --now act_runnerFor Alpine/OpenRC hosts such as haskelseed, use
runner/act-runner.openrc.example:sudo install -m 0755 runner/act-runner.openrc.example /etc/init.d/act_runner sudo rc-update add act_runner default sudo rc-service act_runner startIf reusing the current haskelseed root registration at
/root/.runner, userunner/act-runner-haskelseed.openrc.exampleinstead. This is less portable than the dedicatedact_runneruser layout, but it matches the existing registration state without printing or replacing the token. -
Inspect without exposing secrets:
systemctl status act_runner --no-pager journalctl -u act_runner -n 100 --no-pager rc-service act_runner status tail -n 100 /var/log/act_runner.log -
From this repo, run:
make runner-status # If using the current ops-bridge haskelseed path: RUNNER_HOST=192.168.178.135 \ RUNNER_SSH_USER=root \ RUNNER_SSH_KEY=/home/worsch/.ssh/id_ops \ make runner-status
Recovery
Stop or drain:
sudo systemctl stop act_runner
sudo systemctl disable act_runner
Replace a runner:
- Stop the old service.
- Revoke or rotate the old registration token in Gitea.
- Move the old
/var/lib/act_runner/.runneraside for evidence, not reuse. - Register the replacement with the same approved label contract.
- Run the smoke workflow before re-enabling consumer workflows.
Disable a bad registration:
- Stop the service immediately.
- Remove or disable the runner in Gitea admin/org settings.
- Rotate any repo secrets that were available to failed jobs if exposure is plausible.
- Record a State Hub note with runner name, labels, time window, and affected workflows.
Smoke Workflow
The repo includes .gitea/workflows/forge-runner-smoke.yaml. It is deliberately
small: it checks scheduling, basic host context, Docker availability if present,
and verifies that baseline cluster/registry secret environment variables are not
accidentally present.
Do not rerun inter-hub production deploys until this smoke workflow has passed
on the approved runner and the result is recorded in
docs/gitea-actions-runner-evidence.md.
Current Blockers
- Direct non-interactive SSH to the bare
haskelseedalias timed out from this environment on 2026-06-07. The current ops-bridge path reaches haskelseed atroot@192.168.178.135with/home/worsch/.ssh/id_ops. - Haskelseed has
act_runner v0.6.1-1-g8e6b3be9installed and/root/.runnerregistered ashaskelseed. On 2026-06-07 the OpenRC service was installed fromrunner/act-runner-haskelseed.openrc.example, labels were updated to the first compatibility contract, and the runner daemon declared successfully. - Gitea created
forge-runner-smoke.yaml #1for commit19ee47fe82, but the run is stillWaiting; authenticated Gitea Actions inspection is needed to decide whether this is approval, scheduling, or runner scope. skopeois not installed in this environment, so registry tag inspection must run from a host withskopeoor use an approved equivalent.- Local
make check-toolsstill lackskubectl,helm, andsops; those are separate operator-tool prerequisites for deploy-capable Gitea work.