feat(pr-daemon): hand review sessions their forge token
ci / nix (push) Successful in 9s
ci / lint (push) Successful in 11s

A sandboxed review session can't read ~/.env.claude, so $GITEA_TOKEN was
never set and reviewers improvised — one of them fell back to `git
credential fill`, another skipped posting.

A forge can now name a second, write-capable variable in reviewTokenEnv.
The daemon passes its value into the session as $GITEA_TOKEN ($GH_TOKEN
on GitHub), through the generated Claude settings file or the codex
profile's shell_environment_policy, both now written 0600. tokenEnv
stays read-only and unchanged. Nothing is injected when reviewTokenEnv
is unset.
This commit is contained in:
Miguel Palhas
2026-08-22 20:01:45 +01:00
parent 0096addb23
commit a3522051fa
4 changed files with 65 additions and 15 deletions
+16 -5
View File
@@ -238,14 +238,25 @@ Config from `bin/reviewer-config.example.json` to `~/.config/reviewer/config.jso
Secrets in `~/.config/reviewer/env`, never here:
```sh
REVIEWER_GITEA_TOKEN=... # read-only
REVIEWER_GITHUB_TOKEN=... # read-only
REVIEWER_GITEA_SECRET=... # webhook HMAC
REVIEWER_GITEA_TOKEN=... # read-only
REVIEWER_GITHUB_TOKEN=... # read-only
REVIEWER_GITEA_REVIEW_TOKEN=... # optional, write:issue — handed to review sessions
REVIEWER_GITEA_SECRET=... # webhook HMAC
REVIEWER_GITHUB_SECRET=...
```
The daemon's tokens are read-only — it never writes to a forge, which is also
why it doesn't mark notifications read.
The daemon's own tokens are read-only — it never writes to a forge, which is
also why it doesn't mark notifications read.
A review session is a different case: it has to post its findings, and the
sandbox denies it `~/.env.claude`, where `$GITEA_TOKEN` normally comes from.
Name a write-capable variable in a forge's `reviewTokenEnv` and the daemon
passes its value into the session as `$GITEA_TOKEN` (`$GH_TOKEN` on GitHub) —
through the generated Claude settings file (`env`) or codex profile
(`shell_environment_policy.set`), both written 0600. Leave `reviewTokenEnv`
out and nothing is injected; the session falls back to the forge's credential
helper, which is what it did before. Scope it to commenting: on Gitea that is
`write:issue`, and nothing else.
Claude review sessions need `bubblewrap` and `socat` on the box, or the sandbox
cannot start and the session refuses to run (`failIfUnavailable`). That is