Files
teabot/docs/configuration.md
T
unkinben 748048be50
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Add fail-closed author allowlist gating job dispatch
Sessions run claude with --dangerously-skip-permissions and a prompt built
from issue/PR/comment text, so only trusted authors may supply that text.
teabot now dispatches a job only when the triggering event's author login is
on an allowlist; an empty allowlist dispatches nothing (fail-closed).

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-27 00:33:09 +10:00

6.2 KiB

Configuration

teabot reads a single YAML file, by default $XDG_CONFIG_HOME/teabot/config.yaml (i.e. ~/.config/teabot/config.yaml). Override the path with --config/-c. Generate a starting point with teabot config init, and validate the effective settings with teabot config show.

Top-level keys

Key Default Description
gitea_url https://git.unkin.net Base URL of the Gitea instance to poll.
repos (required) List of owner/name repositories to watch.
poll_interval 60s Delay between poll cycles (Go duration).
max_concurrent 2 Maximum job containers running at once.
job_timeout 30m Per-session wall-clock timeout.
job_image git.unkin.net/unkin/agent-dev:latest Container image each session runs in.
container_home /home/agent Home dir inside job_image (mount target).
claude_config_dir ~/.claude Host dir with Claude Code credentials.
anthropic_api_key (unset) If set, injected as ANTHROPIC_API_KEY.
anthropic_base_url (unset) If set, injected as ANTHROPIC_BASE_URL.
state_dir ~/.local/state/teabot Where processed-event state is persisted.
allowed_authors (empty → fail-closed) Gitea usernames whose events may trigger a job. See Author allowlist.
repo_allowed_authors (unset) Optional per-repo override of allowed_authors, keyed by owner/name.
personalities (required) Bot identities (see below).

~ and ~/ are expanded in path-valued keys.

Personalities

A personality is a distinct Gitea bot account. teabot must be able to act as different identities — e.g. an implementer that opens PRs and a separate reviewer that critiques them — so each personality points at its own tea config file rather than sharing your personal ~/.config/tea/config.yml.

personalities:
  - name: implementer
    tea_config: ~/.config/teabot/tea-implementer.yml
    role: implementer          # implementer | reviewer | both
    git_name: Teabot Implementer
    git_email: teabot-implementer@unkin.net
  - name: reviewer
    tea_config: ~/.config/teabot/tea-reviewer.yml
    role: reviewer
    git_name: Teabot Reviewer
    git_email: teabot-reviewer@unkin.net
Field Description
name Label used in logs and prompts.
tea_config Path to a tea config.yml holding this bot's login. teabot reads the API token + username from it and mounts it into the job container.
role implementer (issues), reviewer (pull requests), or both. Defaults to both.
git_name / git_email Commit identity set inside the container.

Create each tea config with the normal tea workflow, pointing HOME/ XDG_CONFIG_HOME at a scratch dir so it lands somewhere dedicated, or copy an existing config.yml and edit the token. The file format is exactly tea's own:

logins:
  - name: teabot-implementer
    url: https://git.unkin.net
    token: <bot-api-token>
    default: true
    user: teabot-implementer

teabot picks the login whose url matches gitea_url, else the default, else the first. The user field is the bot's username — teabot uses it for loop prevention (it never reacts to events authored by any personality's username).

At least one personality must be able to implement and at least one to review, or config validation fails.

Author allowlist (security)

Each job runs claude --print --dangerously-skip-permissions inside a container, and its prompt is built from event text — the issue title/body, PR description/diff, and comments. That text is attacker-controllable: anyone who can open an issue or comment on the repo could otherwise inject instructions into a session that has permissions disabled. The allowlist is the control that closes this: teabot dispatches a job only when the triggering event's author login is on the allowlist.

# Global allowlist.
allowed_authors:
  - benvin
  - trusted-colleague

# Optional per-repo override. A present entry fully REPLACES the global list for
# that repo; an empty list disables dispatch for it; an absent entry falls back
# to the global list.
repo_allowed_authors:
  unkin/teabot:
    - benvin

Semantics:

  • Fail-closed. An empty/absent allowlist dispatches nothing. teabot logs a warning at startup when no allowlist is configured anywhere.
  • Events from non-allowlisted authors are recorded as processed (so they don't re-trigger) and logged at info with the author name, but never spawn a container. They are not marked as engaged threads.
  • Comment follow-ups are gated on the new comment's author, not just the thread. A comment from a non-allowlisted user on a thread teabot previously acted on is ignored — otherwise an untrusted comment could reopen the injection path on a bot thread.
  • Matching is case-insensitive (Gitea usernames are unique case-insensitively).
  • This is separate from and in addition to loop prevention: bot personality logins are always excluded first, even if one were listed here.

Claude credentials

By default teabot uses your Claude subscription auth: it copies claude_config_dir (default ~/.claude) into a per-job scratch dir and mounts that copy read-write into the container, so the session can refresh tokens without ever mutating your real config.

Alternatively set anthropic_api_key (and optionally anthropic_base_url for a gateway such as LiteLLM). When present these are injected as environment variables and take precedence over the mounted subscription credentials.

SELinux

This host is Fedora, so Docker bind mounts are relabelled with :z. teabot does this automatically. If you run under a different security model, the relabel suffix is a field on DockerRunner (SELinuxLabel).

A note on comment follow-ups

teabot only continues comment threads on issues/PRs it acted on itself (opened a PR for, or reviewed). A comment on an unrelated thread is recorded but ignored. Combined with loop prevention (bot-authored comments are skipped), this means teabot will not, for example, keep answering an issue it decided not to implement — it engages a thread only after it has taken an action there.