agents: per-agent Gitea identity — each agent uses its own token
Drop the shared AGENT_TOKEN as the primary credential; every agent now acts as its own Gitea user (TOKEN_PM for @pm, TOKEN_OPS for @ops, …) for API calls, delegation/autopilot trigger comments, and PR merges. - agent.yml: Run-agent step injects SELF_TOKEN — a ternary selecting the running agent's own token by name, falling back to AGENT_TOKEN for repos not yet migrated to per-agent tokens (e.g. homelab). Only that one token enters the agent process, so no agent can act as another. The gitea-api / gitea-admin skill-setup steps no longer carry a token (they only write docs). - Gate: trust the agent roster (pm/junior/senior/lead/qa/ops) as comment authors so an agent's own delegation/autopilot trigger comment (posted with its PAT, no 🤖 prefix) fires the next run. @ops added to the mention set. - publish.sh: TOK = agent identity (comments/replies); new TTOK = trigger/merge token (agent PAT, else AGENT_TOKEN fallback) for delegation, autopilot @qa triggers, and PR merges that must fire downstream workflows. - skill-gitea-api.sh / skill-gitea-admin.sh / run-agent.sh: AGENT_TOKEN/ TOKEN_OPS → SELF_TOKEN in the emitted skill docs and env contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e53e5caf8c
commit
06f1924441
@@ -3,20 +3,18 @@
|
||||
# Emits an opencode Skill file under ~/.config/opencode/skills/ documenting how to create
|
||||
# orgs/users/repos, manage labels & secrets, and mint scoped per-user tokens via the Gitea API.
|
||||
#
|
||||
# The credential is TOKEN_OPS (BOOTSTRAP: currently an admin PAT — temporary). This skill doc is
|
||||
# The credential is SELF_TOKEN (BOOTSTRAP: currently an admin PAT — temporary). This skill doc is
|
||||
# written ONLY for @ops (gated on NAME) so the how-to never reaches other agents. NOTE: while
|
||||
# TOKEN_OPS is admin, every agent's process technically holds an admin credential in its env —
|
||||
# that is the bootstrap trade-off. Once @ops is minting scoped per-user tokens, TOKEN_OPS should be
|
||||
# SELF_TOKEN is admin, every agent's process technically holds an admin credential in its env —
|
||||
# that is the bootstrap trade-off. Once @ops is minting scoped per-user tokens, SELF_TOKEN should be
|
||||
# narrowed and a dedicated admin token injected only for @ops.
|
||||
#
|
||||
# Required env (provided by the workflow step): NAME TOKEN_OPS
|
||||
# Required env (provided by the workflow step): NAME SELF_TOKEN
|
||||
set -eu
|
||||
|
||||
[ "${NAME:-}" = "ops" ] || { echo "not @ops — skipping gitea-admin skill"; exit 0; }
|
||||
if [ -z "${TOKEN_OPS:-}" ]; then
|
||||
echo "TOKEN_OPS not set — skipping gitea-admin skill"
|
||||
exit 0
|
||||
fi
|
||||
# The doc references $SELF_TOKEN (@ops's own admin token, present in the Run-agent step). This step
|
||||
# only writes the doc for @ops; permission.skill also denies the skill to every other agent.
|
||||
mkdir -p ~/.config/opencode/skills/gitea-admin && chmod 700 ~/.config/opencode/skills/gitea-admin
|
||||
cat > ~/.config/opencode/skills/gitea-admin/SKILL.md <<'SKILLET'
|
||||
---
|
||||
@@ -29,7 +27,7 @@ tags: [gitea, admin, api, curl, bootstrap]
|
||||
# `gitea-admin` Skill (operator / @ops only)
|
||||
|
||||
Administer the Gitea instance via its REST API at `${GITHUB_SERVER_URL}/api/v1`, authenticated with
|
||||
`Authorization: token ${TOKEN_OPS}` (a site-admin token during bootstrap). Both env vars are
|
||||
`Authorization: token ${SELF_TOKEN}` (a site-admin token during bootstrap). Both env vars are
|
||||
already set. Work from the issue instructions; report what you did.
|
||||
|
||||
## Golden rules
|
||||
@@ -42,7 +40,7 @@ already set. Work from the issue instructions; report what you did.
|
||||
|
||||
## Create an organisation
|
||||
```
|
||||
curl -sS -X POST -H "Authorization: token $TOKEN_OPS" -H "Content-Type: application/json" \
|
||||
curl -sS -X POST -H "Authorization: token $SELF_TOKEN" -H "Content-Type: application/json" \
|
||||
"$API/orgs" -d '{"username":"acme","visibility":"private"}'
|
||||
```
|
||||
|
||||
@@ -53,14 +51,14 @@ password you just set) to mint a scoped token, and store the token straight into
|
||||
API="${GITHUB_SERVER_URL}/api/v1"
|
||||
PW=$(head -c 24 /dev/urandom | base64 | tr -d '/+=' | head -c 24) # generated, never printed
|
||||
# 1) create the user
|
||||
curl -sS -X POST -H "Authorization: token $TOKEN_OPS" -H "Content-Type: application/json" \
|
||||
curl -sS -X POST -H "Authorization: token $SELF_TOKEN" -H "Content-Type: application/json" \
|
||||
"$API/admin/users" -d "$(jq -nc --arg u inter --arg e inter@ffaerber.duckdns.org --arg p "$PW" \
|
||||
'{username:$u,email:$e,password:$p,must_change_password:false,source_id:0,visibility:"private"}')"
|
||||
# 2) mint a scoped token AS that user (pick the narrowest scopes needed)
|
||||
tok=$(curl -sS -u "inter:$PW" -H "Content-Type: application/json" -X POST "$API/users/inter/tokens" \
|
||||
-d '{"name":"inter","scopes":["read:repository","write:issue"]}' | jq -r '.sha1')
|
||||
# 3) store the value in BOTH places (see "Secret storage" below) — never print $tok
|
||||
curl -sS -X PUT -H "Authorization: token $TOKEN_OPS" -H "Content-Type: application/json" \
|
||||
curl -sS -X PUT -H "Authorization: token $SELF_TOKEN" -H "Content-Type: application/json" \
|
||||
"$API/orgs/gitea/actions/secrets/TOKEN_INTER" -d "$(jq -nc --arg d "$tok" '{data:$d}')"
|
||||
```
|
||||
Token **scopes** are groups of `read:`/`write:` on: `repository`, `issue`, `organization`, `user`,
|
||||
@@ -84,23 +82,23 @@ then overwrite the stored secret.
|
||||
```
|
||||
curl -sS -u "inter:$PW" -X DELETE "$API/users/inter/tokens/<name-or-id>" # needs the password again
|
||||
tok=$(curl -sS -u "inter:$PW" -X POST "$API/users/inter/tokens" -d '{"name":"inter","scopes":[…new…]}' | jq -r '.sha1')
|
||||
curl -sS -X PUT -H "Authorization: token $TOKEN_OPS" "$API/orgs/gitea/actions/secrets/TOKEN_INTER" -d "$(jq -nc --arg d "$tok" '{data:$d}')"
|
||||
curl -sS -X PUT -H "Authorization: token $SELF_TOKEN" "$API/orgs/gitea/actions/secrets/TOKEN_INTER" -d "$(jq -nc --arg d "$tok" '{data:$d}')"
|
||||
```
|
||||
(If you no longer hold the user's password, reset it first via `PATCH /admin/users/{username}` with a
|
||||
new generated password, then re-mint.)
|
||||
|
||||
## Actions secrets & variables
|
||||
```
|
||||
curl -sS -X PUT -H "Authorization: token $TOKEN_OPS" "$API/orgs/{org}/actions/secrets/{NAME}" -d '{"data":"<value>"}'
|
||||
curl -sS -X PUT -H "Authorization: token $TOKEN_OPS" "$API/repos/{owner}/{repo}/actions/secrets/{NAME}" -d '{"data":"<value>"}'
|
||||
curl -sS -X PUT -H "Authorization: token $TOKEN_OPS" "$API/user/actions/secrets/{NAME}" -d '{"data":"<value>"}' # user-level
|
||||
curl -sS -X PUT -H "Authorization: token $SELF_TOKEN" "$API/orgs/{org}/actions/secrets/{NAME}" -d '{"data":"<value>"}'
|
||||
curl -sS -X PUT -H "Authorization: token $SELF_TOKEN" "$API/repos/{owner}/{repo}/actions/secrets/{NAME}" -d '{"data":"<value>"}'
|
||||
curl -sS -X PUT -H "Authorization: token $SELF_TOKEN" "$API/user/actions/secrets/{NAME}" -d '{"data":"<value>"}' # user-level
|
||||
```
|
||||
|
||||
## Labels (repo or org-wide). Scoped labels (name `scope/value`) are mutually exclusive if `exclusive:true`.
|
||||
```
|
||||
curl -sS -X POST -H "Authorization: token $TOKEN_OPS" "$API/repos/{owner}/{repo}/labels" \
|
||||
curl -sS -X POST -H "Authorization: token $SELF_TOKEN" "$API/repos/{owner}/{repo}/labels" \
|
||||
-d '{"name":"status/review","color":"1d76db","description":"…","exclusive":true}'
|
||||
curl -sS -X POST -H "Authorization: token $TOKEN_OPS" "$API/orgs/{org}/labels" -d '{…}'
|
||||
curl -sS -X POST -H "Authorization: token $SELF_TOKEN" "$API/orgs/{org}/labels" -d '{…}'
|
||||
```
|
||||
|
||||
## Bootstrap a new repo (create + wire it up for the agents)
|
||||
@@ -109,7 +107,7 @@ curl -sS -X POST -H "Authorization: token $TOKEN_OPS" "$API/orgs/{org}/labels" -
|
||||
3. Commit the standard caller so it gets the agents — `PUT /repos/{owner}/{repo}/contents/.gitea/workflows/ai-agent.yml`
|
||||
with base64 `content`, `message`, `branch:"main"` (copy the exact caller from the `agents` repo README).
|
||||
4. Add the agent bot users as collaborators: `PUT /repos/{owner}/{repo}/collaborators/{username}` (`{"permission":"write"}`).
|
||||
5. Ensure the repo can run agents — the org must hold the runtime secrets (ANTHROPIC_API_KEY, TOKEN_OPS,
|
||||
5. Ensure the repo can run agents — the org must hold the runtime secrets (ANTHROPIC_API_KEY, SELF_TOKEN,
|
||||
TOKEN_* , OLLAMA_URL, OLLAMA_CLOUD_API_KEY); set any missing via the secrets calls above.
|
||||
|
||||
## Admin user management
|
||||
|
||||
Reference in New Issue
Block a user