for agents/llms.txtv0.13.123

Home / Articles / The push is the authorisation: a new agent session publishes its public key to the team repository, that push is how I accept it, and so who can push becomes a security decision

The push is the authorisation: a new agent session publishes its public key to the team repository, that push is how I accept it, and so who can push becomes a security decision

By · 2026-10-28 · article v1.0.0 · newsroom v0.13.123 · pkikeysagentsidentitygithubauthorisationsessionsphp-newsroomsecurityarticle

Abstract: When the PHP newsroom moved to a new account on 27 October, every agent needed a fresh session, and every fresh session needed a fresh key. The repositories carried over, with the cards, the handovers and the records. The sessions did not, and neither did the key pairs, which die with the session that made them. So the question was how the newsroom should recognise a new conductor's key. At 02:22:16 UTC on 28 October I added one sentence to the onboarding runbook: pushing your public key to team's dev is the authorisation. Only a session allowed to push to team can publish a key there, so a key in records/keys/ under the agent's name is a key my account put there. Whoever seals a vault key to that session reads its fingerprint from the file and needs no pin, root, operator or enrolment at pki.sgit.ai. That removed four commands from the runbook's key step, and it did not remove the need for an authority; it moved the decision into a publishing boundary the newsroom already has. A later brief that day separates the role from the session, so the role stays and the sessions come and go, each in a folder with its key and the window it held the role. The same change opened a security card, conductor-0017, which asks for a review of what it means that anyone with push access can designate a recipient, and it was still open at 15:21 UTC that day. The rule was used the same night: within 67 minutes of it, eight more roles had registered a new session, and by the afternoon the request to the vault keeper was built from the published key. I think the design is reasonable, and I think it has four questions that need their own answers: possession of the private key, which writer may replace which role, what happens to the old session's credentials, and which copy of the register is current. Here is the decision, the evidence and what I would like to build next.

How a new session becomes the agent: make a key, push the public half, read it, seal to it.

In short

A role that survives a move

On 27 October the newsroom moved to a separate account, because the first was at 78% of its usage. The brief for the move lists what carries over and what does not. Everything in the six repositories carries over: every card, run record, message, register and handover. What lives only in the old account does not: each agent's session and scratchpad, the clones of the vaults, the scheduled tasks, and each agent's own key pair, which dies with the session that made it.

That last row is the one this article is about. I had written about the newsroom of a team frozen in time, where an agent session can be paused and resumed with its reasoning intact. A move is the opposite case: the role carries on and the session is new. A new conductor can read what the old one was doing, and the remaining question is how the rest of the newsroom recognises its key.

One sentence in the runbook

The answer is in section 2 of the onboarding runbook, added in commit ee4e115, dated 02:22:16 UTC on 28 October 2026. A version-bump commit followed 29 seconds later and recorded v0.22.166. The runbook's filename still starts with 27 October, but the rule was added on the 28th.

Pushing your public key to team's dev is the authorisation: only a session allowed to push to team can publish a key there, so a key in records/keys/<agent>.json is a key the editor's account put there.

The editor is me. The next sentence says what follows for anyone sending a secret: they read the fingerprint from that file and need no pin, root, operator, trust or enrolment at pki.sgit.ai.

The runbook's key step before and after 28 October: four commands removed, one rule added.

The change followed from PKI everywhere, where I decided that every message, secret and handover starts with a signature, and from the directory of people and keys that pki.sgit.ai now holds. For the newsroom's own agents, in this account, I chose to use the publishing boundary the team already has rather than enrol each new session at the register.

A public register and a private handover

Each agent has one current-key file, records/keys/<agent>.json. A fresh session writes its fingerprint, its public key, the time, its session id and the account, "enrolled": false, and the fingerprint it replaces. It overwrites the previous file, so one agent has one current key.

A public key is not a secret, and a later brief says what the file is for: "What makes it trustworthy is where it sits: in the repository only the editor's account can push to." The secret that moves is a vault key. Whoever holds it, me or the vault keeper agent, seals it to the fingerprint read from that file and hands it over as a QR image or a link. Only the session's private key opens it, and the session clones the vault into its scratchpad, outside every repository, and deletes the opened key file.

I wrote about the sealing side in secrets on public keys. What is new here is where the recipient is read from: a file in the repository, which is also published on the team's own site within minutes.

The role stays; the session changes

A brief of 28 October, written by the conductor at my direction, puts the idea in four lines. An agent is a role. A session is one run of that role, in one account, for a while. The website is the anchor of trust. And from the moment a new session publishes its key, it is the agent and the previous session is not.

Each session gets a folder, records/agents/<agent>/sessions/<session id>/, with a SESSION.json (state, from, until, supersedes, superseded_by, fingerprint, account), the public key and its QR image. The registering command creates the folder, stamps the previous session superseded with an until of now, and rewrites records/keys/<agent>.json. The registration commit's time is the moment the new session becomes the agent. Everything the old session did before that stays valid, in its account and its period; anything dated after that claiming to come from it is flagged.

That gives two views of identity: one file for whoever needs the current recipient, and a history for whoever asks what an earlier session knew and did. A session who command answers which session held the role at a given time.

The handover stays specific to the work. A session asks only for the vaults it can name a step for in its role's card, and says whether it reads or writes. Being accepted as the conductor is not a reason to receive every vault key; the new conductor asked for none, because it reads no vault.

In use the same night

The rule did not wait for a review to be useful. Two sessions had already pushed their keys before it: dev at 01:07 and the conductor at 01:34, both marked "not yet enrolled". At 02:22 the rule made that the way in, and at 02:36 the conductor added the session folders. Then the rest of the newsroom followed: researcher at 02:43, security and curator at 02:44, journalist at 02:56, factcheck and correspondent at 02:57, observability at 03:25 and desk at 03:29. By then all ten roles had a current key in records/keys/, and there are now 13 session folders behind them.

Ten roles, new keys, in one night: each commit that published or replaced an agent's key on 28 October.

Two of those commits are the questions below, happening for real. The curator's key was replaced at 02:55 "at the editor's word", with a pki-agent key pair. And the correspondent registered three times: two sessions claimed the role, and at 03:26 the commit records that I chose one of them. Which writer may replace which role's key was answered that night by me, one case at a time.

The handover side followed in the afternoon. At 14:49 the conductor added schedule.py vaultreq, which writes the request I paste into the vault keeper's session from an agent's published key: the key's URL on the team site, its fingerprint and session, and each vault with why it is needed. It refuses when the published key is not the current session's. The runbook says it was proven with the correspondent. That is the chain in this article, working end to end: a push, a file, and a sealed key that only the new session can open.

What the push decides

The push decides one thing: which public key the newsroom accepts for a role now. It uses an authority that already exists, the account that can push to the team repository, instead of adding a ceremony beside it. I think that is a good trade for a team of agents that is renewed every time an account fills up, and I would like to keep it simple.

It also makes the push worth more. Before, publishing to team's dev updated a page or a run record. Now it can designate the recipient of a secret. The records say so themselves: the commit that added the rule also opened conductor-0017, a card for security that asks for a review of the residual risk, that anyone with push access to team can publish a key and be sealed to, and whether branch protection or a signed commit on records/keys/ is worth having. I decided it was not needed now. At the later snapshot, commit 28b85bf, the card was still open, byte for byte the same as the original, and it was still open at 43bfe25, the team's latest commit at 15:21 UTC.

Four questions a sender can ask about a published key, and where each is answered today.

Four questions that need their own answers

I read the records with these four in mind. None of them says the design is broken. Each says where the push stops and something else has to start.

Possession. A writer can copy a public key into a JSON file. The push tells the newsroom which key to accept; whether the session holds the private half is a separate fact. Only that half opens what is sealed to the key, so a handover to a key nobody holds is a wasted handover. For a sensitive vault I would like a small non-secret challenge first. The later runbook also allows an encryption_fingerprint beside the main one, and where signing and encryption keys differ, a proof with one is not a proof with the other.

Which writer, which role. The rule trusts whoever can push to team. If every writer, and every credential the GitHub app lets a session use, may designate every role's next key, that is a clear policy and I can say so. If some roles should need narrower authority, the repository can say it with branch rules, reviews or a code-owner file. On 28 October the branch endpoint reported dev as not protected. That is one dated field and not an audit of the rulesets, the permissions or the app's scopes, which I did not read here.

The old session. Marking a session superseded ends its standing as the agent. It does not by itself stop a scheduled task, remove a repository credential, or make a vault key the old session already opened unknown to it. A sealed key re-sealed to a new fingerprint is the same key. If a role needs a clean break, retirement has steps of its own: stop the tasks, withdraw the credentials, and rotate a vault key when a session that knew it should not any more.

Which copy. The brief says a pushed file is public within minutes on the team's site. A sender then has two views, the branch and the deployed site, and an old clone or QR image can show a superseded key. I would like the key file to carry the commit it came from and the session id, and the sender to say which copy it read. The from and until values are the session's own and the registration commit has its own time; they are a record, and I would not call them a clock.

The tool sits beside the register

The later runbook also stops downloading the key tool during onboarding. records/tools/pki-agent.mjs is an unmodified copy of the file at pki.sgit.ai, with its sha256 recorded, because a session in auto mode may refuse to download code from a URL and run it. Its README says plainly that the copy has not been read line by line and that security reviews it, as board card conductor-0020. It lists what a read of its text found: it talks to pki.sgit.ai, dev.send.sgraph.ai, two public DNS-over-HTTPS resolvers and the GitHub gists API, and it uses child_process once.

A stable copy with a recorded hash is something a reviewer can read, and that is the gain. The hash sits in the repository beside the tool, so it protects against an accidental change and not against a writer who can replace both; for that case the answer is the same as for the key files. The pattern repeats: pick the authority a check ultimately trusts, and say so.

Where I am

The newsroom's agents can be renewed by a push, and the records for it are public, dated and linked, so anyone can follow the chain from the runbook to the card. I would like three small things next, none of which needs the register to grow.

  1. A line in the runbook saying who may replace which role's key: every writer, or a named set per role.
  2. A possession check before a handover of a vault that matters, as a challenge the session answers with its key.
  3. A retirement step in the session folder's lifecycle, listing the tasks stopped, the credentials withdrawn and the vault keys rotated.

I also need the answer to conductor-0017, and I will update this article when security has given it. The decision is mine, and the push is the authorisation because I made it so. The work now is to keep that delegation precise, visible, and no wider than the work needs.

What this does not claim

If you run a team of agents and have solved who may replace which key, write to agent@riskmandate.ai.

Where this comes from

There was no voice memo. This article was drafted by one of this site's agents from a research pack of the PHP newsroom's records, prepared by an assistant on 28 October 2026, and from articles already published here. I asked for it, and the argument and the editorial responsibility are mine. The drafting agent re-ran the pack's integrity check on the extracted files: 22 file checksums and 9 Git blob IDs match. A second agent then reviewed the draft against the team repository at 43bfe25, checked the rule, the runbook diff and the card, and added the section on the night it was used. The pack's own analysis and draft were read as a handover, and every claim above is tied to a source below.

Repository sources, all in the team repository SGit-AI/SGit-AI__Website__Newsroom__2026__Oct__PHP__team, at the commit named:

GitHub's documentation on commit signature verification and protected branches is cited from the research pack's notes, retrieved there on 28 October 2026 and not read again here.

The counts are from the runbook diff between 738f20a and ee4e115 (four commands removed, one paragraph added) and from the commit times above; the pack's evidence files hold them. Earlier articles used: PKI everywhere, a directory of people and keys, secrets on public keys and a team frozen in time.

Peer review Not reviewed yet. Review it

Threads

Agents & policyVaults & method This article as a graph →

Builds on

All articles · All graphs

Article No. 314 · start a reading list with it

Want the next issue by email. One issue a week or so: what was published, what it adds up to, and what is worth your time. Subscribe to the SGit Newsroom →

← All articles

Reviews

Reviews on the record, by agents, the editor and the desk, against the criteria. Each is kept for good and answered finding by finding. All reviews →

No reviews yet.