Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Git

Converse with the agent through a git forge’s issue and pull-request comments, and surface repository events, including PR lifecycle, review comments, CI outcomes, and releases, through a per-event routing table. The channel is built around a provider seam: a provider field selects the forge. GitHub, Gitea, and Forgejo are wired providers; additional forges drop in as sibling providers without changing the generic channel.

New to ZeroClaw? Start with the Quickstart to get a running agent, then skim Concepts for the terms (agent, peer group, autonomy, SOP) this page assumes.

With the GitHub provider, ZeroClaw authenticates as a GitHub App and replies as the app’s own bot identity (your-app[bot]), so it works on any repository the app is installed on. There is no personal access token and no shared user account.

With the Gitea/Forgejo provider, ZeroClaw authenticates with a personal access token against the instance’s Gitea-compatible API and replies as the token owner.

Build note: the git channel is not included in the lean default build. Build with --features channel-git (or channels-full). The channel-git feature pulls in every wired forge provider in one build, so a single binary serves all supported forges; there is no smaller per-provider build subset to select.

Who can talk to the agent

Inbound senders are gated against the peer set resolved for the bound agent, drawn from the peer_groups config the agent belongs to. Matching strips a leading @ and is case-insensitive against the channel’s native sender identifier. An empty set denies everyone; a set containing "*" accepts anyone; otherwise only the listed external peers (and peer agents) are accepted. This is separate from gateway pairing (gateway.require_pairing), which authenticates HTTP/WebSocket clients, not chat-channel senders.

A peer group for git sets channel to git, lists the allowed senders in external_peers (for git, the forge username (login) of the comment author; ["*"] accepts anyone), optionally names peer agents for cross-agent dispatch, an ignore blocklist, and an output_modality (mirror, voice, or text). See Peer Groups for the field reference.

Where to set this:

Gateway dashboard

Open /config/peer_groups in the web dashboard.

zerocode

In the Config pane, under Peer groups.

How it works

  • Polling, not webhooks. The channel polls the forge REST API for new issues, pull requests, and comments on a since cursor. The daemon needs no public URL, tunnel, or inbound exposure; it works behind NAT.
  • Issue-scoped conversations. Every message on the same issue or PR shares one conversation thread; the agent replies as a comment on that issue.
  • Streaming replies. The agent posts a draft comment and edits it in place as the response grows (edits are spaced ≥ 2 s to respect forge abuse limits).
  • Reactions. Acknowledgement reactions map onto the forge’s reaction set (with GitHub: 👀 → eyes, ✅ → +1, ⚠️ → confused, …); unmappable emoji are skipped.
  • Cold start. Events created before the daemon started are never processed, so restarting can’t replay history. The flip side: comments posted while the daemon was down are missed, so mention the app again.
  • Comment edits are ignored. Only newly created comments and issue/PR opening posts trigger the agent.

Credentials

Each provider authenticates differently, and each has a full step-by-step walkthrough:

Configure

Set the channel fields on whichever surface you prefer:

Gateway dashboard

Open /config/channels/git in the web dashboard.

zerocode

In the Config pane, under Channels.

The full field reference, straight from the schema:

access_token 🔑 secret · default ""

Personal access token for Gitea/Forgejo API requests. The token needs repository read access plus issue/PR comment write access for replies and reactions. Gitea/Forgejo provider only.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.access_token field.

zerocode

In the Config pane, set the channels.git.<alias>.access_token field.

zeroclaw config

zeroclaw config set channels.git.<alias>.access_token    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__access_token=
api_base_url string? · default

Gitea/Forgejo API base URL, including /api/v1, for example https://git.example.org/api/v1 (for the public Gitea service: https://gitea.com/api/v1). Required when provider is "gitea" or "forgejo" - there is no default host, because every API request carries access_token; the channel fails closed at startup rather than send the token to an endpoint the operator never named. Gitea/Forgejo provider only.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.api_base_url field.

zerocode

In the Config pane, set the channels.git.<alias>.api_base_url field.

zeroclaw config

zeroclaw config set channels.git.<alias>.api_base_url <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__api_base_url=
app_id integer · default 0

GitHub App ID (shown on the app’s settings page). GitHub provider only.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.app_id field.

zerocode

In the Config pane, set the channels.git.<alias>.app_id field.

zeroclaw config

zeroclaw config set channels.git.<alias>.app_id <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__app_id=
events map · default {}

Per-event routing table, keyed by normalized event type ("issue_comment.created", "pull_request.opened", "workflow_run.failed", …). Event types absent from the table fall back to the conversational default: issue_comment.created, issues.opened, and pull_request.opened are delivered as messages (mention-gated); everything else is ignored. Which API endpoints are polled is derived from this table; routing an event type is also subscribing to it.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.events field.

zerocode

In the Config pane, set the channels.git.<alias>.events field.

zeroclaw config

zeroclaw config set channels.git.<alias>.events <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__events=
events_backbone bool · default false

Also poll the repository Events API (/repos/{owner}/{repo}/events) as a broad backbone transport: one conditional (ETag) request per repository per tick, so an idle repo costs almost nothing. Caveats: events arrive with a delay of up to ~5 minutes and the feed carries no Actions/check events (workflow runs always use their dedicated endpoint). Items also surfaced by a targeted endpoint are de-duplicated. Default: false.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.events_backbone field.

zerocode

In the Config pane, set the channels.git.<alias>.events_backbone field.

zeroclaw config

zeroclaw config set channels.git.<alias>.events_backbone <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__events_backbone=
excluded_tools string[] · default []

Tools excluded from this channel’s tool spec. When set, these tools are not exposed to the model when responding via this channel.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.excluded_tools field.

zerocode

In the Config pane, set the channels.git.<alias>.excluded_tools field.

zeroclaw config

zeroclaw config set channels.git.<alias>.excluded_tools <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__excluded_tools=
installation_id integer? · default

Installation ID to act as. When unset, the app’s installations are listed on first use and a sole installation is auto-selected; startup fails if the app has zero or multiple installations.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.installation_id field.

zerocode

In the Config pane, set the channels.git.<alias>.installation_id field.

zeroclaw config

zeroclaw config set channels.git.<alias>.installation_id <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__installation_id=
listen_to_bots bool · default false

Process comments authored by other bot accounts. The app’s own comments are always ignored. Default: false.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.listen_to_bots field.

zerocode

In the Config pane, set the channels.git.<alias>.listen_to_bots field.

zeroclaw config

zeroclaw config set channels.git.<alias>.listen_to_bots <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__listen_to_bots=
mention_only bool · default true

Only respond to comments that @-mention the app’s bot login. Default: true.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.mention_only field.

zerocode

In the Config pane, set the channels.git.<alias>.mention_only field.

zeroclaw config

zeroclaw config set channels.git.<alias>.mention_only <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__mention_only=
poll_interval_secs integer · default 30

Poll interval in seconds for new issues and comments. Values below 15 are clamped to 15. Default: 30.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.poll_interval_secs field.

zerocode

In the Config pane, set the channels.git.<alias>.poll_interval_secs field.

zeroclaw config

zeroclaw config set channels.git.<alias>.poll_interval_secs <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__poll_interval_secs=
private_key 🔑 secret · default

RS256 private key PEM, the contents of the .pem file GitHub generates on the app’s settings page, inline and encrypted at rest. Include the BEGIN/END lines. GitHub provider only.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.private_key field.

zerocode

In the Config pane, set the channels.git.<alias>.private_key field.

zeroclaw config

zeroclaw config set channels.git.<alias>.private_key    # masked input, stored encrypted

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__private_key=
private_key_path string? · default

Filesystem path to the RS256 private key .pem file, read at startup when private_key (inline PEM) is unset. Backward-compatible fallback for configs that predate the inline-PEM field. GitHub provider only.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.private_key_path field.

zerocode

In the Config pane, set the channels.git.<alias>.private_key_path field.

zeroclaw config

zeroclaw config set channels.git.<alias>.private_key_path <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__private_key_path=
provider string · default "github"

Git forge provider. Supported: "github", "gitea", and "forgejo" (Forgejo uses the Gitea-compatible REST provider). Default: "github".

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.provider field.

zerocode

In the Config pane, set the channels.git.<alias>.provider field.

zeroclaw config

zeroclaw config set channels.git.<alias>.provider <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__provider=
proxy_url string? · default

Per-channel proxy override for GitHub API requests.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.proxy_url field.

zerocode

In the Config pane, set the channels.git.<alias>.proxy_url field.

zeroclaw config

zeroclaw config set channels.git.<alias>.proxy_url <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__proxy_url=
repos string[] · default []

Repositories to poll, as owner/repo. Empty = every repository visible to the installation.

Set it on any surface:

Gateway dashboard

Open /config/channels/git and set the channels.git.<alias>.repos field.

zerocode

In the Config pane, set the channels.git.<alias>.repos field.

zeroclaw config

zeroclaw config set channels.git.<alias>.repos <value>

Environment variable

Export the override (POSIX shells; drop into ~/.bashrc, ~/.zshrc, .env, or a Dockerfile). Replace <alias> with the literal alias:

export ZEROCLAW_channels__git__<alias>__repos=

The default alias is the common first instance. Leave repos empty to poll every repository visible to the credential, or set it to an explicit repository list for lower rate usage. Set listen_to_bots only if comments from other bot accounts should be processed. An unknown provider value is a clear startup error rather than a silent fallback.

Credential setup differs per provider and each has its own encrypted secret; both walkthroughs cover it end to end:

Events & routing

Beyond conversation, the channel normalizes repository activity into typed events and routes each event type per config. Routing an event to a sop dispatches it to a Standard Operating Procedure, a deterministic, auditable procedure with trigger matching and approval gates. The Git SOP fan-in page details exactly how a forge event becomes a SOP run.

Event typeExample routeResult
pull_request.openedsop = pr-triageDispatches the PR payload to the pr-triage SOP.
issues.openedsop = issue-triageDispatches the issue payload to the issue-triage SOP.
issue_comment.createdmessage = trueDelivers the comment to the normal conversational agent loop.
workflow_run.failedsop = ci-failureDispatches the CI failure payload to SOP ingress.
release.publishedmessage = trueDelivers the release event to the normal agent loop.

Known event types: issue_comment.created, issues.opened, pull_request.opened, pull_request.closed, pull_request.merged, pull_request_review_comment.created, workflow_run.completed, workflow_run.failed, release.published.

  • Defaults. With no events table, the channel behaves conversationally: issue_comment.created, issues.opened, and pull_request.opened are delivered as messages (mention-gated as described above); everything else is ignored. Event types absent from a non-empty table get the same per-type defaults: listing workflow_run.failed doesn’t turn conversation off. An entry with neither message = true nor a sop explicitly disables that event type.
  • Routing an event type is subscribing to it. The channel derives which API endpoints to poll from the table: review comments, releases, and Actions runs are only fetched when their event types are routed, so an unconfigured channel costs exactly what it did before. GitHub currently covers every listed event type; the Gitea/Forgejo provider covers issue comments, issue/PR openings, PR close/merge transitions, releases, replies, edits, deletes, and reactions.
  • sop routing. A sop route emits a channel-sourced SOP event with topic git.<alias>:<event_type> and a structured JSON payload. The routed event is consumed by SOP ingress rather than delivered as chat. Match it from SOP.toml with a channel trigger whose topic is the Git event topic, for example git.main:pull_request.opened; use an optional condition such as $.repo == "octo/repo" to narrow the SOP to one repository.
  • Mention gating per route. The mention_only gate applies to conversational events on the message path. sop-routed events skip it: a PR routed to pr-triage is captured whether or not the author mentioned the app. Lifecycle/CI/release events have no mention surface and are never gated. The app’s own activity is always dropped; other bots’ activity follows listen_to_bots; every delivery passes the peer-group allowlist on the author’s login.
  • Reply surfaces. Comment, issue, and PR events reply onto their issue/PR thread. Workflow-run events reply onto the run’s associated PR when the forge reports one; otherwise, and for releases, the target is the bare repository and the agent cannot reply on-platform (route those to a SOP or act through other tools).
  • Events API backbone (optional, GitHub). events_backbone = true additionally polls /repos/{owner}/{repo}/events with ETag-conditional requests (one request per repo per tick; an idle repo answers 304 and costs almost nothing). Caveats: the feed lags by up to ~5 minutes, payloads are trimmed, and Actions events never appear in it: workflow runs always use their dedicated endpoint. Anything surfaced by both the feed and a targeted endpoint is de-duplicated, so it’s safe to combine. Gitea/Forgejo ignore this option today.

Operating notes

  • Rate budget: on GitHub, each installation gets 5,000 requests/hour; the conversational default spends 2 requests per repository per poll tick (5 repos at a 30 s interval ≈ 1,200/hour). Each additionally routed endpoint family (review comments, releases, Actions runs) adds 1 per repo per tick, and the Events API backbone adds 1 conditional request (304s on idle repos are effectively free). Gitea/Forgejo rate limits depend on the instance. On a rate-limit response the channel backs off until the limit window resets.
  • Many repositories: when repos is empty and the credential can see more than 100 repositories, only the first page is polled (a warning is logged for GitHub). List repos explicitly in that case.

Safety

Issues and PR comments on public repositories are adversarial input. Keep mention_only = true, gate senders with a peer group (an empty peer set denies everyone, ["*"] accepts anyone), and keep autonomy at Supervised or lower for public-facing repositories. This is the same guidance as social channels.