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
+26
-21
@@ -12,13 +12,21 @@ jobs:
|
||||
# defense against malicious-issue prompt injection — do not loosen it.
|
||||
if: >
|
||||
(github.event.comment == null && github.event.issue.user.login == 'ffaerber') ||
|
||||
(github.event.comment != null && github.event.comment.user.login == 'ffaerber' &&
|
||||
(github.event.comment != null &&
|
||||
(github.event.comment.user.login == 'ffaerber' ||
|
||||
github.event.comment.user.login == 'pm' ||
|
||||
github.event.comment.user.login == 'junior' ||
|
||||
github.event.comment.user.login == 'senior' ||
|
||||
github.event.comment.user.login == 'lead' ||
|
||||
github.event.comment.user.login == 'qa' ||
|
||||
github.event.comment.user.login == 'ops') &&
|
||||
!contains(github.event.comment.body, '🤖') &&
|
||||
(contains(github.event.comment.body, '@pm') ||
|
||||
contains(github.event.comment.body, '@junior') ||
|
||||
contains(github.event.comment.body, '@senior') ||
|
||||
contains(github.event.comment.body, '@lead') ||
|
||||
contains(github.event.comment.body, '@qa')))
|
||||
contains(github.event.comment.body, '@qa') ||
|
||||
contains(github.event.comment.body, '@ops')))
|
||||
runs-on: ci-runner
|
||||
steps:
|
||||
- name: Acknowledge with 👀
|
||||
@@ -121,27 +129,21 @@ jobs:
|
||||
run: bash "$SCRIPTS/skill-node1-ssh.sh"
|
||||
|
||||
- name: Set up `gitea-api` skill (let agents read/write issues, PRs, Actions across repos)
|
||||
# Mirrors the node1-ssh pattern: emit an opencode Skill file under
|
||||
# ~/.config/opencode/skills/ so any dev agent discovers the capability via OpenCode's
|
||||
# skill registry. The credential is the shared AGENT_TOKEN (a PAT whose scopes the
|
||||
# maintainer set at creation time — issue/repository/organization/misc read+write, cross-repo).
|
||||
# Only emitted when AGENT_TOKEN is actually present, so repos without it don't get a
|
||||
# broken skill. The token is passed via env and never inlined into shell.
|
||||
# Emits an opencode Skill file. The skill uses SELF_TOKEN — the running agent's OWN token
|
||||
# (e.g. TOKEN_PM for @pm), injected into the Run-agent step below — so each agent talks to
|
||||
# Gitea as itself. This step only writes the doc; permission.skill scopes who may load it.
|
||||
env:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }}
|
||||
run: bash "$SCRIPTS/skill-gitea-api.sh"
|
||||
|
||||
- name: Set up `gitea-admin` skill (@ops only — administer the Gitea instance)
|
||||
# Instance administration (orgs/users/repos/labels/secrets/scoped tokens). The SKILL.md is
|
||||
# written ONLY for @ops (skill-gitea-admin.sh gates on NAME), so the admin how-to never
|
||||
# reaches other agents; permission.skill also denies it to everyone but @ops. Uses the
|
||||
# dedicated admin token TOKEN_OPS (the `ops` user), NOT AGENT_TOKEN — this step only writes
|
||||
# the doc; the token itself is injected into the Run-agent step only when NAME==ops.
|
||||
# written ONLY for @ops (skill-gitea-admin.sh gates on NAME) and permission.skill also denies
|
||||
# it to every other agent. It uses SELF_TOKEN (which for @ops is TOKEN_OPS), injected into the
|
||||
# Run-agent step. This step only writes the doc.
|
||||
env:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
NAME: ${{ steps.prep.outputs.name }}
|
||||
TOKEN_OPS: ${{ secrets.TOKEN_OPS }}
|
||||
run: bash "$SCRIPTS/skill-gitea-admin.sh"
|
||||
|
||||
- name: Inspect / fetch image attachments (download only for vision agents)
|
||||
@@ -165,13 +167,11 @@ jobs:
|
||||
env:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
# AGENT_TOKEN powers the `gitea-api` skill (cross-repo issue/PR/Actions read+write).
|
||||
# It is already a required secret for the delegation step below; exposing it here too
|
||||
# lets the agent process itself call the Gitea API on demand.
|
||||
AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }}
|
||||
# The dedicated admin token for the `gitea-admin` skill — injected into the agent's process
|
||||
# ONLY when it is @ops, so no other agent ever holds an admin credential in its env.
|
||||
TOKEN_OPS: ${{ steps.prep.outputs.name == 'ops' && secrets.TOKEN_OPS || '' }}
|
||||
# SELF_TOKEN = the RUNNING agent's OWN token (TOKEN_PM for @pm, TOKEN_OPS for @ops, …).
|
||||
# Only this agent's token is placed in its process env, so no agent can act as another.
|
||||
# Powers the gitea-api / gitea-admin skills — each agent calls Gitea as itself. Falls back
|
||||
# to AGENT_TOKEN in repos that haven't been migrated to per-agent tokens (e.g. homelab).
|
||||
SELF_TOKEN: ${{ steps.prep.outputs.name == 'pm' && secrets.TOKEN_PM || steps.prep.outputs.name == 'junior' && secrets.TOKEN_JUNIOR || steps.prep.outputs.name == 'senior' && secrets.TOKEN_SENIOR || steps.prep.outputs.name == 'lead' && secrets.TOKEN_LEAD || steps.prep.outputs.name == 'qa' && secrets.TOKEN_QA || steps.prep.outputs.name == 'ops' && secrets.TOKEN_OPS || secrets.AGENT_TOKEN }}
|
||||
NAME: ${{ steps.prep.outputs.name }}
|
||||
MODEL: ${{ steps.prep.outputs.model }}
|
||||
VISION: ${{ steps.prep.outputs.vision }}
|
||||
@@ -197,6 +197,8 @@ jobs:
|
||||
env:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
GT: ${{ secrets.GITEA_TOKEN }}
|
||||
# Fallback PAT for triggering comments (delegation/autopilot) + merges in repos without
|
||||
# per-agent tokens. Where per-agent tokens exist, the agent's own token is used instead.
|
||||
AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }}
|
||||
TOKEN_PM: ${{ secrets.TOKEN_PM }}
|
||||
TOKEN_SENIOR: ${{ secrets.TOKEN_SENIOR }}
|
||||
@@ -224,6 +226,9 @@ jobs:
|
||||
env:
|
||||
SCRIPTS: ${{ runner.temp }}/agents-scripts
|
||||
GT: ${{ secrets.GITEA_TOKEN }}
|
||||
# Fallback PAT for triggering comments (delegation/autopilot) + merges in repos without
|
||||
# per-agent tokens. Where per-agent tokens exist, the agent's own token is used instead.
|
||||
AGENT_TOKEN: ${{ secrets.AGENT_TOKEN }}
|
||||
TOKEN_PM: ${{ secrets.TOKEN_PM }}
|
||||
TOKEN_SENIOR: ${{ secrets.TOKEN_SENIOR }}
|
||||
TOKEN_JUNIOR: ${{ secrets.TOKEN_JUNIOR }}
|
||||
|
||||
Reference in New Issue
Block a user