Files
Radix-Skills/skills/devops/gitea-issue-agent/references/identity-and-safety.md
T
dennis 236053cd7f Initial import: 10 Radix skills across 3 categories
Kategoriserer danske regnskabs-/ERP-skills, ERP-data-afstemnings-
skill og to devops-workflows (gitea-issue-agent, kanban-workflows)
som Radix-medlemmers AI-agenter kan dele.

Struktur:
  skills/<kategori>/<skill-navn>/SKILL.md
  skills/<kategori>/<skill-navn>/{references,templates,scripts}/

Indekseret af index.json (genereret af scripts/rebuild_index.py).
Kør `python3 scripts/rebuild_index.py --check` i CI for at fange
synkroniseringsfejl.
2026-06-15 14:41:04 +02:00

4.5 KiB

Identity and safety in the Gitea issue agent

Two identities, one Gitea server

The setup has two Gitea users that should remain separate:

Identity Token stored in Used for What it posts
Service-bruger (e.g. hermes-issues) GITEA_TOKEN in ~/.hermes/.env All plugin tool calls: scan, comment, create PR Comments and PRs attributed to the bot
Personal user (e.g. dennis) tea login + ~/.netrc Local git push of branches the agent created Commits and merges attributed to you

The plugin's gitea_agent_status reports authenticated_user. If it reports your personal login, the bot will post comments as you — fix the GITEA_TOKEN before scheduling any cron job.

Why the Hermes safety-guards exist (and why the agent can't bypass them)

Hermes has three layers of "agent cannot edit" protection, all by design:

  1. ~/.hermes/config.yaml — gates plugin load, model selection, toolsets, gateway config. An agent that could rewrite this could give itself new tools, change providers, or open outbound network channels. The patch tool explicitly refuses: Refusing to write to Hermes config file: ... Agent cannot modify security-sensitive configuration.

  2. ~/.hermes/.env — contains API keys, tokens, passwords. The write_file, patch, and execute_code tools all refuse to modify it.

  3. execute_code with file writes — when a script is going to write files under ~/.hermes/, the safety guard blocks it unconditionally: BLOCKED: execute_code script timed out without user response.

The right move when blocked: give the user the exact one-line edit (or cat > file <<EOF ... EOF block) and stop. Do not try alternative tools, do not retry, do not work around it. The user must run the edit themselves.

This is not a bug. It is the threat model.

Approval-gate design rationale

The agent should never implement, push, or open a PR for an issue that hasn't been approved. This protects against three classes of mistake:

  1. Misread issue — the user wanted "fix the X button", the agent rewrote X() in 14 files. Approval forces a human to look at the proposal first.
  2. Out-of-scope issue — the user wanted to push back, not fix. Approval makes "no" cheap (just don't reply).
  3. Bad agent day — the LLM hallucinated, the issue got a wrong proposal, the agent is in a weird state. Approval makes the loop recoverable without reverting commits.

The two state markers (proposal, pr_ready) survive Hermes restarts, plugin reloads, and Gitea restarts, because they live in the issue's comment history. The agent never has to remember anything to resume — it can re-scan and pick up where it left off.

Approval matching — why it requires a known login by default

The default approval regex is permissive enough to handle Danish ("godkend", "godkender") and English ("approve", "approved", "ok", "go") in either order with the agent mention. But without a known-login check, any user with write access to the repo could say "looks good, agent" in a comment and trigger a push. That's a prompt-injection vector — a malicious screenshot, or a co-worker joking, could authorize code execution.

Two safe modes:

  • Label-only mode (the default until GITEA_APPROVER_LOGIN is set): a maintainer applies one of three labels. Labels can only be applied by users with write access, so the trust surface is the maintainer set, not "anyone who can comment".
  • Login-gated mode (once GITEA_APPROVER_LOGIN is set): a comment from that specific user, matching the regex, AND after the latest proposal comment. This is the strict mode.

If the user wants both, both work — labels OR login-comment. If they want neither (auto-implement everything), they should set both GITEA_APPROVER_LOGIN and approve a meta-issue saying "always implement" — but that's not the default and not recommended.

The first run is always manual

Before scheduling a cron, the user must:

  1. Run a manual gitea_issue_agent_scan and confirm issues show up.
  2. Pick a real issue and walk it through: scan → fetch images → analyze → propose → wait → approve → implement → test → push → PR.
  3. Confirm the PR body links to the issue, the commit message is reasonable, and git log shows the expected author/email.
  4. Then schedule a cron. The cron prompt itself should be minimal — "scan, propose for new issues, implement approved" — with a clear hand-off to the skill for the details.