Identity Setup¶
This guide walks through creating and configuring the GitHub App identities that VERGIL tooling uses for AI agents. Each contributor registers two GitHub Apps — one per agent role — under their personal account.
For the model these identities serve, see Identity Architecture. For how credentials are selected at runtime, see Credential Management. For repo-level onboarding (installing tools, hooks, CI), see Consuming Repo Setup.
The two agent identities¶
Every AI agent is represented by a GitHub App whose name encodes the
human who owns it and the agent's role: <username>-vergil-<role>.
The App's installation is the agent's only credential, and the agent's
capability is bounded entirely by the App's declared permission shape.
There are no agent user accounts and no collaborator grants — the bot
identity (<username>-vergil-<role>[bot]) that appears on commits and
PRs comes from the App itself.
| App | Role | Shape |
|---|---|---|
<username>-vergil-user |
Daily development (Driver) | write code, read PRs |
<username>-vergil-audit |
PR review (Officials) | read code, write PRs (inverted) |
A third role — <username>-vergil-admin — is a reserved slot and is
not provisioned. Do not create it.
The two Apps have deliberately inverted permission shapes. The user App can write code but only read PRs; the audit App can only read code but can write (review/comment on) PRs. This split is the core of the permission model — set the permissions exactly as documented.
Why GitHub Apps¶
- No shadow-ban risk — Apps are first-class GitHub citizens
- Short-lived tokens — 1-hour installation tokens, minted on demand, instead of long-lived PATs
- Multi-org from one identity — install the same App on every org you manage
- Server-side merge control — branch protection recognizes the App identity
- Dynamic token resolution — the tooling acquires tokens per-org at runtime from the App's private key; no static token configuration
Prerequisites¶
- A GitHub account that is an owner of the organizations where the agents will operate
- A
~/.config/vergil/keys/directory on your development machine
Part 1 — The user App (<username>-vergil-user)¶
Step 1.1 — Register the App¶
-
Go to github.com → your profile icon → Settings → Developer settings (bottom of the left sidebar) → GitHub Apps → New GitHub App.
-
Fill in the registration form:
| Field | Value |
|---|---|
| GitHub App name | <username>-vergil-user |
| Description | AI agent identity (user / Driver role) owned by <username>. Daily development agent running in an isolated VM; authenticates via installation tokens for commits, pushes, and issues. |
| Homepage URL | your GitHub profile URL |
| Webhook | uncheck "Active" (no webhook needed) |
- Under Repository permissions, set exactly these and leave everything else at No access:
| Permission | Access |
|---|---|
| Contents | Read and write |
| Issues | Read and write |
| Pull requests | Read-only |
| Actions | Read-only |
| Checks | Read-only |
| Commit statuses | Read-only |
| Repository security advisories | Read and write |
| Metadata | Read-only (mandatory; usually auto-selected) |
| Workflows | No access — leave untouched |
!!! note "Repository security advisories is for security records"
vrg-security-record creates, reads and updates the private
draft advisories in each org's .github that hold security
findings, and vrg-intake-route and vrg-queue planning read
and retarget them (see Security Records).
Each org's automation App (<org>-automation, see
Identity Architecture)
needs the same Read and write grant for the scheduled sweep,
which closes and reopens advisories. The audit App does not
get it: it is not in use today, and the decision is deferred
until the audit identity is revived. Adding this permission to an
existing App changes its permission set, so each installation
must accept the change (the org or account owner approves it
from the installation's settings page) before the new grant takes
effect.
!!! note "Actions, Checks and Commit statuses are for reading CI"
vrg-pr-await and pr-watch read a PR's check runs, commit
statuses and workflow runs. On a private repo, an App
without these grants gets HTTP 403 "Resource not accessible by
integration". On a public repo the reads succeed anyway, which
hides a missing grant. All three are read-only and grant no
ability to trigger, re-run or change CI.
!!! warning "Pull requests is Read-only on purpose"
The user agent does not open or comment on PRs. Its workflow
ends at "ready for PR"; the human submits the PR from the host.
pull_requests: read is the server-side hard gate that makes
gh pr create fail for this identity even if the soft gate is
bypassed. The agent still needs read to see PR and CI status.
!!! warning "Never grant Workflows"
No agent identity may push changes under .github/workflows/.
Leaving Workflows at No access is what makes GitHub reject
such a push server-side. This is deliberate containment, not an
oversight.
- Under Where can this GitHub App be installed?, select Any account — this lets you install it on your organizations, not just your personal account.
!!! warning "Cannot change this easily later" If you select "Only on this account", the App can only be installed on your personal account. To change it afterward: App settings → Advanced → Danger zone → Make public. Once public and installed elsewhere, this cannot be reverted.
- Click Create GitHub App. You land on the App's settings page, which shows the App ID and Client ID near the top.
Step 1.2 — Record the App ID and Client ID¶
On the General settings page, record both identifiers:
- App ID — a short number (e.g.,
3940487). - Client ID — a string beginning with
Iv(e.g.,Iv23li...).
The tooling authenticates using the App ID today (it becomes the
JWT iss claim via VRG_APP_ID). GitHub is gradually migrating
GitHub App authentication from the numeric App ID toward the Client
ID; that migration is only partially rolled out, so the App ID is
still what the tooling uses. Record both now so the eventual
switch to Client ID needs no return trip to the GitHub UI.
You do not need installation IDs — the tooling resolves those dynamically at runtime.
Step 1.3 — Generate a private key¶
- Ensure the keys directory exists:
-
On the App settings page, scroll to Private keys → click Generate a private key. Your browser downloads a
.pemfile. -
Move it into the keys directory, keeping the original filename:
GitHub names the file <app-name>.<YYYY-MM-DD>.private-key.pem
(for example, wphillipmoore-vergil-user.2026-06-02.private-key.pem).
Keep the name — the date is useful metadata for key rotation.
No install prompt is expected here
On a freshly created App you may see no "install this App" banner after generating the key. Installation is a separate, deliberate step (next) — do not wait for a prompt.
Step 1.4 — Install the App¶
- On the App's settings page, left sidebar → Install App.
- GitHub lists every account you can install on — your personal account and each organization you own.
- Click Install next to each account that owns repositories this agent will operate on. A single App installed on multiple accounts is normal and expected — it works across every org you manage. Without an installation on a given account, the tooling cannot mint a token for that account's repos.
- Choose repository access:
- All repositories — covers every repo in that account, including ones added later (low friction).
- Only select repositories — tighter; you return here to add more.
- Click Install. You land on the installation's configuration page (its URL contains an installation ID, which you do not need to record — the tooling resolves it at runtime).
The user App is now fully provisioned. Configuring
identities.toml happens in Part 3, after the audit App exists.
Part 2 — The audit App (<username>-vergil-audit)¶
The audit App follows the same flow as the user App, with two differences: the name, and an inverted permission shape (it reads code and writes PRs, the mirror of the user App).
Step 2.1 — Register the App¶
Follow Step 1.1, changing:
- GitHub App name:
<username>-vergil-audit - Description: AI agent identity (audit / Officials role) owned by
<username>. PR-review agent running in an isolated VM; reads code and writes PR reviews/comments via installation tokens. - Repository permissions — note these are inverted from the user App:
| Permission | Access |
|---|---|
| Contents | Read-only |
| Issues | Read-only |
| Pull requests | Read and write |
| Actions | Read-only |
| Checks | Read-only |
| Commit statuses | Read-only |
| Repository security advisories | No access (deferred) |
| Metadata | Read-only (mandatory) |
| Workflows | No access — leave untouched |
Actions, Checks and Commit statuses are the same read-only CI grants as the user App (see the note in Step 1.1); they are not inverted. Repository security advisories stays at No access: the audit identity is not in use today, and whether it needs the grant is decided when it is revived.
Everything else (Webhook unchecked, Any account, the "Make public" warning) is identical to Step 1.1.
Step 2.2 — Record the App ID and Client ID¶
Same as Step 1.2 — record both for the audit App.
Step 2.3 — Generate a private key¶
Same as Step 1.3. The downloaded file is named
<username>-vergil-audit.<YYYY-MM-DD>.private-key.pem; move it into
~/.config/vergil/keys/.
Step 2.4 — Install the App¶
Same as Step 1.4 — install the audit App on the same set of accounts as the user App.
Part 3 — Configure identities.toml¶
~/.config/vergil/identities.toml is the host-side configuration that
tells VM provisioning which App credentials to inject into each agent
VM. The dual-identity model uses one stanza per role.
default_identity = "vergil-user"
vergil = "v2.0" # vergil-tooling version for the human host
vergil-vm = "v2.1" # VM template tag (from the vergil-vm repo)
[identities.vergil-user]
vm_instance = "vergil-user"
auth_type = "app"
app_id = <user-app-id>
private_key_path = "~/.config/vergil/keys/<username>-vergil-user.<date>.private-key.pem"
vergil = "v2.1" # pin this VM's vergil-tooling to 2.1 (set once 2.1 is released)
# client_id = "Iv..." # recorded for the App ID → Client ID migration; not yet read by the tooling
[identities.vergil-audit]
vm_instance = "vergil-audit"
auth_type = "app"
app_id = <audit-app-id>
private_key_path = "~/.config/vergil/keys/<username>-vergil-audit.<date>.private-key.pem"
vergil = "v2.1"
# client_id = "Iv..."
Notes:
app_idis the identifier the tooling uses today (it becomesVRG_APP_IDinside the VM). The Client ID is recorded only as a comment until the App ID → Client ID migration lands in the tooling.private_key_pathis relative to your home directory when it starts with~, otherwise absolute.- Per-identity
vergilpin. Settingvergil = "v2.1"on the agent identities lets the agent VMs run new tooling while the human host stays on a stablev2.0(the top-level default). Only set thev2.1pin once 2.1 has been released. VRG_IDENTITY_MODE(user/audit) is derived from the identity's stanza name at provisioning time — a name containinguserprovisions as user mode,auditas audit mode; a name containing neither (or both) fails provisioning. The mode is written to~/.config/vergil/identity-modein the VM and exported from the shell profile. It selects the identity-aware allowlists at runtime; it is a soft-gate ergonomic, not the security boundary (the App credential is).- Query identity with
vrg-whoami, never a single environment variable.VRG_IDENTITY_MODEis only the first of five fallback steps (env var → mode file →app.pem→VRG_APP_ID→ human); an unset value means "fall through," not "default to human." Ask the authoritative resolver instead:vrg-whoamiprints the resolved role,vrg-whoami --modeemits a single token for scripting (export VRG_IDENTITY_MODE="$(vrg-whoami --mode)"), andvrg-whoami --explainreports which signal resolved and warns when signals disagree.
Trusted authors¶
An optional top-level [trusted_authors] stanza lists the GitHub logins
whose issues and comments automation may trust: your own login, and the
logins of your agent identities (the Apps' bot accounts).
[trusted_authors]
human = ["<your-login>"]
agents = ["<user-app-bot-login>", "<audit-app-bot-login>"]
- Both lists are required and non-empty, every entry is a non-empty string, and no login may appear in both lists.
- Store the bare login. GitHub reports an issue opened by an App as
app/<login>but that App's comments as<login>; anapp/-prefixed entry is rejected, and consumers strip a leadingapp/before comparing. - Delivery into VMs.
vrg-vmwrites the stanza into every agent VM as~/.config/vergil/trusted-authors.toml(mode 600) on every path that writes vergil config:create,start,restart,rebuild, the cloud credentials stage, andupdate. When the host has no stanza,vrg-vmremoves that file, so a VM never trusts a stale list. - Read it through
vrg-trusted-authors, never by hand. It reads the VM file if present, else the host stanza, and printshuman: <login>andagent: <login>lines (--jsonprints{"human": [...], "agents": [...]}). With nothing configured it exits 1 with an error naming both paths; it never prints a default. Skills and doctrine name only this command, never the logins themselves. - An invalid stanza makes every
vrg-vmcommand fail loudly when it loadsidentities.toml.
The stanza is used first by issue-implement, which starts work on an issue
only when a trusted author opened it, and by epic-create's design-review
rounds.
It seeds the system-wide rule for every tool and skill that reads issues,
planned in
vergil-project/.github#579.
Verification checklist¶
After both Apps are registered, installed, and configured:
- [ ] Two private key files exist in
~/.config/vergil/keys/(one per App), with their generation dates in the filenames - [ ]
identities.tomlhas avergil-userand avergil-auditstanza, each with the correct App ID and key path - [ ] Each App shows the correct inverted repository permissions (user: contents/issues write, PRs read; audit: contents/issues read, PRs write; both hold Actions, Checks and Commit statuses read-only; neither holds Workflows)
- [ ] The user App holds Repository security advisories: Read and write, the audit App does not, and every installation has accepted the change
- [ ] Each org's automation App (
<org>-automation) holds Repository security advisories: Read and write, and its installation has accepted the change - [ ] Both Apps are installed on every account that owns repos the agents will operate on
- [ ] The reserved
<username>-vergil-adminApp was not created
Full end-to-end verification requires the agent VMs to be provisioned (vergil-vm) and vergil-tooling 2.1 to be released and pinned.
Related¶
- Identity Architecture — the model these Apps implement
- Credential Management — how installation tokens are minted and selected at runtime
- Consuming Repo Setup — repo-level onboarding