Savi Comms

This guide is for Savi Comms 2026.11.5.  It is the same text the download carries at docs/GITHUB-AGENT-IDENTITY.md.

An agent's own GitHub identity

What it is

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.

What the machine needs

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.

Creating one app for one agent and one repository

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.

Placing it in Savi Comms

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.

How the agent checks it

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 only reads

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.