Before cloning the agent, make sure the host has the following installed.
bin/install.sh pre-flights everything and bails loudly if anything's
missing.
Required on PATH:
jq,curl,git,flock,timeout,sqlite3-- core harness deps.sudo apt-get install -y jq curl git util-linux coreutils sqlite3(most are usually already installed;jqandsqlite3are typically the ones to add).claude-- Anthropic's Claude CLI. Install via Anthropic's installer (see docs.claude.com). It authenticates with the host's Claude subscription login -- see "Auth and secrets".
Ecosystem toolchains for repos the agent will actually work:
The weekly maintenance pass auto-detects the stack and runs standard audit tools (npm audit, cargo audit, pip-audit, govulncheck, bundle audit, etc.) from the harness directly. Missing audit binaries (cargo-audit, pip-audit, bundler-audit) are installed on demand into user-writable paths -- no sudo. Go's govulncheck is handled specially (see the Go note below). But the base language toolchain must already be on the host:
- Node projects (including the agent's own website) →
sudo apt-get install -y nodejs npm - Python projects →
sudo apt-get install -y python3 python3-pip python3-venv - Go projects →
sudo apt-get install -y golang-go(host Go only needs to be new enough to read atooldirective, i.e. ≥1.24) - Rust projects → install rustup (not in apt)
- Ruby projects →
sudo apt-get install -y ruby ruby-dev
You only need toolchains for languages the agent will actually touch.
Go vuln scanning -- pin govulncheck per repo, don't rely on the host's.
govulncheck is coupled to the Go toolchain it was built with: a scanner
built against an older stdlib can't parse newer source (e.g. range-over-func),
so an apt/distro govulncheck frozen at the distro Go will choke on a repo that
has moved to a newer Go. The maintenance pass therefore ignores any
govulncheck on PATH and instead prefers each repo's own pinned tool: add
it once with go get -tool golang.org/x/vuln/cmd/govulncheck@latest (Go 1.24+
tool directive in go.mod) and the harness runs it via go tool govulncheck,
which compiles the scanner with that repo's selected toolchain -- so it always
matches the repo's Go, and upgrading one repo never drifts the others. Repos
that haven't adopted the directive fall back to a global install built with the
toolchain the repo's go.mod selects. If the apt govulncheck is installed you
can safely apt remove it; it is intentionally bypassed.
Every model call -- agentic ticks AND the one-shot pipeline/PR-text/
security-gate completions -- goes through the claude CLI on the host's
Claude subscription login. No API key. Authenticate the agent's Unix
user once:
claude auth login # interactive OAuth, or:
claude setup-token # long-lived token for headless installs
claude auth status # verifyIf an ANTHROPIC_API_KEY ends up in the environment anyway, the
invocation primitives in lib/claude.sh strip it from every CLI call --
an inherited key silently flips the CLI to pay-as-you-go API billing.
(The Messages-API client anthropic_call is kept in lib/claude.sh as
an escape hatch back to key billing; it has no live call sites.)
The harness watches its own auth health: every call records whether
auth/quota worked, a daily probe covers idle days, model work backs off
while the subscription usage window is exhausted, and a once-daily
alert email goes to HEALTH_RECIPIENTS while anything is broken.
Pick the models in .env, stakes-ordered per surface: AGENT_MODEL
(the workhorse -- issues, site-work, pipelines; e.g. claude-sonnet-4-6),
AGENT_MODEL_REVIEW (PR-review revisions + maintenance triage; e.g.
claude-opus-4-8), AGENT_MODEL_SECURITY (the security gate's
independent reviewer -- the strongest tier you have; e.g.
claude-fable-5).
The bot's Forgejo token (FORGEJO_TOKEN) lives in the same .env,
chmod 600. See .env.example for the full template.
A dedicated Forgejo user plus a matching server account (one Unix user runs the systemd units and owns the SSH key). The Forgejo user needs:
-
An API token with these three scopes:
read:user-- so the harness can resolve the bot's identity via/api/v1/user(no separateBOT_USERconfig needed)write:repository-- reading repo contents, listing PRs, opening PRswrite:issue-- creating/commenting/labeling/assigning/closing issues
Forgejo's scope model is fine-grained:
write:repositorydoes NOT include issue ops, hence the separatewrite:issue. All three are required; missing any will cause specific harness operations to fail later with a403 token does not have at least one of required scope(s)message. Push access for git (clone/push) is via the SSH key, separate from API scopes. -
An SSH key for git operations (clone and push). The agent clones via
git@<host>:<owner>/<repo>.git, where<host>is derived fromFORGEJO_URL(override withFORGEJO_HOSTif SSH is on a different endpoint). -
Branch protection bypass on each repo's default branch, so the harness can push
agent/N-<slug>branches andCloses #N-linked merges work uniformly.
The server account owns the runtime checkout at ~/.local/share/agent/, its
state at ~/.local/state/agent/ (which includes the per-repo clones
under ~/.local/state/agent/repos/<owner>/<repo>/).
Git authorship on all bot commits and PRs attributes to this user.
Clone the repo, drop the .env in place, then run install.sh:
git clone <forgejo-url>/<bot-user>/agent ~/.local/share/agent
cd ~/.local/share/agent
cp .env.example .env && chmod 600 .env
$EDITOR .env # fill in every var -- no defaults
bin/install.shinstall.sh symlinks the systemd unit files into ~/.config/systemd/user/
(so future git pulls update them with a daemon-reload) and enables
agent.timer. Verify the setup:
bin/validate.sh # checks env, Forgejo reachability, bot identity, accessible reposRe-running install.sh is safe. To tear down:
bin/uninstall.sh # stops, disables, removes units (leaves config + state)Schedule override goes in a drop-in at
~/.config/systemd/user/agent.timer.d/override.conf.
install.sh is only for the systemd-managed install. To run a tick
manually (against a dev clone or to test changes):
git clone <forgejo-url>/<bot-user>/agent ~/Code/agent
cd ~/Code/agent
cp .env.example .env && chmod 600 .env && $EDITOR .env
bin/tick.shSame .env shape, same scripts. flock prevents collision with any
running systemd-managed tick.
- Schedule:
systemctl --user list-timers agent.timer - Logs:
journalctl --user -u agent.service -f - Force a tick now:
systemctl --user start agent.service - Audit a repo's readiness:
bin/validate-repo.sh <owner>/<name>(orbin/validate-repo.sh --allto sweep every accessible repo)