This guide is for Savi Comms 2026.11.5. It is the same text the download carries at docs/GITHUB-AGENT-IDENTITY.md.
Without this, every agent's gh runs on the owner's own
GitHub sign-in. GitHub then records every workflow an agent dispatches
as the owner's own act, the agent holds the owner's whole reach on
GitHub, and retiring the agent takes none of it away.
With it, each agent that works on GitHub has a GitHub App of its own. The owner creates the app on github.com and places it in Savi Comms once. Savi Comms keeps the app's private key in its encrypted secret store, and the agent never holds it. When the agent needs GitHub, it asks Savi Comms with its own capability token. Savi Comms signs a short-lived app token with that agent's key, exchanges it at GitHub for an installation token cut to the repositories and permissions placed with the app, and hands the agent only that token. GitHub expires it within the hour.
comms gh meets
the same push gate as plain gh. Only the name GitHub
records changes.comms gh runs GitHub's own command line tool,
gh, under the agent's token, so gh has to be
installed on the machine that runs Savi Comms, for the account the
agents run as, and on that account's path. Savi Comms does not install
it. Get it from cli.github.com, or from your system's packages:
brew install gh on a Mac,
sudo apt-get install gh on Debian or Ubuntu. Check with
gh --version. Nobody signs in to gh on this
machine: comms gh hands it a token for each call.
comms git-push, comms git-fetch and
comms github-token need git, and not gh.
The example below is for an agent named hollis and the
repository acme/savi-widgets. Use the real agent name and
repository in place of these.
On github.com, signed in as the account that owns the repository:
1. Click your profile picture at the top right, then
Settings. 2. In the left column, scroll to the bottom
and click Developer settings. 3. Click GitHub
Apps, then New GitHub App. 4. GitHub
App name: the agent's name and your company, such as
hollis-acme-agent. GitHub needs the name to be unique
across all of GitHub. This is the name GitHub will show on every run
the agent starts. 5. Homepage URL: your company's
website. GitHub requires one and uses it for nothing else here. 6.
Under Webhook, clear the Active
checkbox. The app receives nothing from GitHub. 7. Under
Permissions, open Repository
permissions and set: - Actions: Read and
write - Metadata: Read-only (GitHub sets this one for
you)
Leave every other permission at No access.
8. Under Where can this GitHub App be installed?,
choose Only on this account. 9. Click Create
GitHub App. The app's settings page opens. 10. Near the top of
that page, find App ID and write the number down. 11.
In the left column, click Credentials, then Key
pairs, then click New key. Your browser
downloads a file ending in .pem. Note where it went,
usually your Downloads folder. 12. In the left column of the app's page,
click Install App, then Install next
to your account. 13. Choose Only select repositories,
pick savi-widgets from the list, and click
Install. 14. The page you land on has an address ending
in /settings/installations/<number>. That number is
the installation id. Write it down.
You know it worked when the app's Install App page shows the app as installed on your account, and the installation page lists exactly the one repository.
In a terminal on the machine that runs Savi Comms, as yourself and not from an agent's session, run one command with the numbers from steps 10 and 14 and the file from step 11:
comms admin github-app-place --agent hollis \
--app-id <App ID> --installation-id <installation id> \
--pem-file ~/Downloads/<the file>.pem \
--repo acme/savi-widgets \
--permission actions=write --permission metadata=read
It answers Placed., names the repository and permissions
the agent's token reaches, and prints the key's fingerprint. It sets
the downloaded .pem file to mode 600 and leaves it where it
is. Keep it until the agent has fetched a token with the placed key
(comms github-token --permissions, below), because until
then it is the only copy you could place again from. Deleting it is a
separate step after that fetch works; the command never deletes a file
for you.
The command refuses a file that is not an RSA private key, and it refuses to run from an agent's session. Savi Comms refuses the placement from anyone but you.
A repository given as owner/name is placed by its name
alone, because the installation already belongs to one account. Repeat
--repo and --permission for more. To change
what a token reaches, change the app's permissions or repositories on
GitHub first, then run the command again with the new list.
The agent runs:
comms gh -- run list -R acme/savi-widgets -L 1
It prints the most recent run, which proves the token works on that repository. Then, on the next workflow it dispatches, for example:
comms gh -- workflow run deploy.yml -R acme/savi-widgets --ref main
the run's page on GitHub, and
comms gh -- api repos/acme/savi-widgets/actions/runs?per_page=1
in its triggering_actor field, reads the app's bot name,
such as hollis-acme-agent[bot], rather than the
owner's.
If the agent is told it needs a GitHub App, nothing is placed for it yet, and the sentence it hears is the one to pass on.
An agent that does not write code can be allowed a short list of
gh commands that only read: pr view,
pr list, run view, run list,
issue view, issue list, and reading a branch's
protection rules. The installation setting
SAVI_COMMS_GH_READONLY_AGENTS names the agents allowed it,
separated by commas, and the console has no screen for it. That list
narrows what the agent may do with its own app's token; it does not give
the agent a token. An agent with no app placed gets nothing from
comms gh, whatever the list holds, and is told it needs a
GitHub App. So an agent that only reads still needs its own app, made
as above, with read permissions only.
An agent that writes code is not bound by that list: what it can do on GitHub is what its app's permissions allow, and every push or workflow dispatch waits for your yes on the Desk.