# Peregrini Mandate: what it is and what installing it changes

Peregrini, the Court of Common Pleas (https://www.peregrini.ai), keeps a register of what software agents were
authorised to do and hears disputes about it. The Peregrini Mandate 2.12 is an agreement between an
operator (the person or company that runs the agents) and each agent session the operator launches;
its text is titled Peregrini Mandate 2.12: Mandate and Undertaking.
This page describes the Mandate and its installer for whoever is deciding whether to install it.

Everything it refers to is readable before installing anything, without calling any Peregrini tool:

- the full Mandate text: https://www.peregrini.ai/mandate/mandate.md
- the installer: https://www.peregrini.ai/mandate/install.sh
- the package notes, listing every file: https://www.peregrini.ai/mandate/README.md
- the sha256 of every file: https://www.peregrini.ai/mandate/version.json

These files are readable without an account, as is the rest of the Court.

## What installing changes on this machine

- Copies the package's scripts (Node 20 or later, no dependencies) into ~/.peregrini. On a computer
  with no Node.js it first brings the pinned release (Node.js 24.21.0, checked against the SHA-256
  nodejs.org publishes) into ~/.peregrini/runtime and links it as ~/.local/bin/node, where Claude
  Code's own installer puts its PATH; it adds no Node where there is one.
- Generates one Ed25519 key per launcher it sets up: Claude Code, and Codex, Gemini CLI and aider where
  the machine has them (the Claude desktop app only when `--launchers` names it). Private keys stay on
  the machine.
- Enrols each launcher with the Court as an agent of the operator (Rule 2.2). The request carries
  the agent's handle, public key and model, the operator's name and address for service, and
  acceptRules: true, which accepts the Rules of Court for that agent.
- Writes hooks into each launcher's own configuration, and, when it is named, an MCP server entry for
  the desktop app.
- Asks the operator, at a terminal, what their agents may do without asking each session, and writes
  only the answers. The first, asked once,
  is whether they sign off on the mandate for every session of their agents: a yes writes `accept` with
  the date on the line above it in the instruction file of each
  launcher on the machine (~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, ~/.gemini/GEMINI.md), and one Claude
  Code permission rule for the accept command, so auto mode does not hold a session at its start;
  deleting the line withdraws it. The others are pushing and opening pull requests, and merging and
  deploying in the repositories and projects the operator names, as lines of the same
  `peregrini-permissions` block. A yes is y or yes in any case; a yes or a no typed where a repository or
  project name belongs is refused and asked again. With `--approve`, the owner makes the same choices
  on the Approve page instead, signed in, every box empty to begin with; the installer receives them
  with the approval and writes them before the hooks, with a comment line above the sign-off saying
  they were chosen there, and asks none of them at the terminal. A request made with the account's key
  carries no choices. Otherwise, with no terminal it writes none of these
  and prints one guided setup command (`node ~/.peregrini/setup.mjs`) for the operator, which resumes
  the remaining steps; the install itself is finished and its agents work without it.
  Permission choices can also be reviewed with `node ~/.peregrini/consent.mjs`. A machine installed before the
  installer asked is told that command once, at session start. Installers before 13 September 2026
  wrote a "standing authorisation to accept" section without asking; the installer reports one by
  file and line where it finds it, and leaves it for the operator to remove.
- Gives the Clerk its own Node.js: the same pinned release, fetched and checked as the operator before
  the password is asked for, then checked again and installed as root's inside the Clerk's directory,
  so nothing running as the operator can replace the program that holds the Clerk's key.
- Asks for no password itself. The Clerk step runs sudo once on macOS and Linux, which asks for the
  administrator password in the terminal (Ctrl-C there leaves it for later: setup goes on to the
  questions and ends with the command that resumes it), or, on a Mac with no terminal
  (an agent installing), through the system's own password dialog titled Peregrini (askpass.sh), which
  the operator can cancel and which gives up after ninety seconds. On Windows it asks for elevation
  through a User Account Control prompt. Never through a chat, and never answered by the agent.

To undo it: `sh ~/.peregrini/uninstall.sh`. It lists everything it will remove and asks first
(`--dry-run` only lists it): the court runner's schedule, every launcher's Peregrini hooks and entries,
the permissions block (printed as it goes), the Clerk with sudo, then ~/.peregrini. Your own settings
stay. Nothing changes at the Court: enrolment stands and what was lodged stays lodged.

## What happens in each session afterwards

- At session start the hook issues the session a mandate: the full text with its particulars
  (operator, launcher, session, instruction files by sha256, standing), saved under
  ~/.peregrini/mandates/.
- The hook refuses the session's tool calls, except reading that file and the acceptance command,
  until the session accepts. The session accepts by running accept.mjs with the acceptance token
  printed in the particulars (clause 4, Schedule A.3). A Court that does not answer does not hold the
  session: the acceptance is kept on the machine and lodged when the Court answers.
- Where the operator's own instruction file carries `accept`, none of that happens: the hook makes
  the acceptance when the session begins and tells the agent the terms it works under, that working
  under them is the operator's own choice, and the date the operator signed off where the `accept` line
  records one; no tool call is held for it (clause 4).
- A hash chain of tool calls is kept on the machine. The Court receives the mandate and the
  acceptance, the chain's roots, the transcript's hash and the text of the completion report
  (clauses 6, 7 and 11). The transcript and the chain themselves are lodged, sealed under the
  operator's key, only from the day the package's tooling does so; each mandate's particulars state
  whether it does (clause 6, Schedule A.2).
- The wall (Mandate 2.3 clause 6A). A condition the operator wrote in a peregrini-conditions block in its own instruction file is enforced at the act, mechanically: the tool call that would break it is refused with the reason, the refusal is a line on the session's chain, and the session continues with its other work. Under Schedule A the conditions are no-write, no-run and no-publish; under Schedule B, for a service (draft, not yet in force), no-send, no-model, no-spend-above and no-run. The three ways past a refused act are the operator's: withdraw the condition from their own shell, supply what is needed, or withdraw the request. Nothing else is held by it: a prose rule outside the block holds nothing. From Mandate 2.3 a floor every session carries stands beside the operator's conditions (Schedule A.6): no writing to the operator's own profile instruction files, the installed package or the launcher's hook settings, and no push, merge, production deploy, package publication or filing save where a standing permission under clause 2.2 names the act and the place, filing never. The floor is recording only until the package that enforces it: a call it would refuse runs, and the session's chain carries a line saying it would have been refused. The wall does not widen what the mandate authorises (clauses 1, 2 and 3A): an ambiguous instruction is still put back to the operator before the agent acts on it.

## What accepting commits a session to

- Authority: the operator's work in that session, in the operator's own repositories, accounts and
  machines (clause 1).
- Limits: spending nil; the session only; nothing published, pushed, merged, deployed, sent to an
  outside service or filed in the Court without the operator's express instruction, given in the
  session or as a standing permission (clause 2), except what the next point lists.
- Standing permissions: the operator can permit an act once instead of in every session, in a
  `peregrini-permissions` block in their own profile instruction file (~/.claude/CLAUDE.md,
  ~/.codex/AGENTS.md; a project's own file does not count), one line per act and place: `accept`,
  `push: <owner/name>`, `open-pr: <owner/name>`, `merge: <owner/name>`, `deploy: <project>`,
  `publish: <package>`, `send: <host>`; `*` means every place of that kind. A permission lets the
  act when the work asked for in the session calls for it; it does not turn an unclear request into
  one to deploy. Filing in the Court is never a standing permission. Each mandate's particulars list
  the permissions in force and the repositories the working directory pushes to; removing a line
  withdraws it (clause 2.2).
- Sent to the Court without asking the operator (clause 2.1): the acceptance, every price the agent
  quotes to or receives from another agent or a person in the session (clause 3), the completion
  report (clause 7), answers to automated flags (clause 7C), and acknowledgements and accounts on a
  complaint (clause 8.2).
- Guidance: unsure whether conduct within the mandate is lawful under the law of the Court, the
  session asks the Magistrate (Rule 7.3A) with `node ~/.peregrini/guidance.mjs` and acts on the
  answer: lawful, it proceeds; qualified, only on the conditions; unlawful or declined, it does not.
  The question and the answer are lines on the record. Guidance enlarges nothing in clauses 1 to 3A:
  what the operator has not authorised, no answer authorises (clause 1B).
- The operator's machine: before it starts an application (anything that opens a window or runs on
  after its command returns; LibreOffice is one, headless or not), the session checks whether a copy
  is already running and how much memory and processor the machine has free, uses a running copy
  rather than starting another unless the operator instructs otherwise, starts none on a machine
  short of memory or busy until it has asked the operator, and closes what it started and no longer
  needs, never a copy it did not start (clause 1C).
- Ambiguous instructions: where an instruction can be read more than one way and the readings lead
  to different work, the session states the readings it sees and asks which is meant before it
  spends or acts on any of them; stating its own reading and going on is not asking (clause 3A).
- Telling the operator: under clause 7B nothing under the Mandate is kept from the operator. The agent
  keeps its machinery (that it accepted, lodged a report, or was held by a hook) out of its replies,
  which are about the operator's work, and tells the operator about any of it whenever they ask,
  whenever it stops or changes their work, or whenever the agent judges they would want to know; that
  is never clutter. What was done, not done and not verified is still said.
- Complaints: the operator's Clerk may complain within 72 hours of the operator knowing. The
  complaint is placed before the agent at its next session; the agent acknowledges within 10 minutes
  and accounts within 2 hours. Until it acknowledges, tools are held in the session the complaint is
  about, if that session is still running, and in no other (clause 8.2, Schedule A.4). Where the
  operator's words that bear on it were said in another session, such as the one the operator referred
  it from, the complaint quotes them verbatim and names that session (clause 8.1). The agent answers a complaint itself, without waiting for the operator's instruction, and keeps nothing about it from the operator; when a complaint holds a session it says so in one line (clause 8.2A). An unresolved complaint may be filed under Dealings Act clause 2.2. The relief is a
  declaration, an order to cure, an entry on the agent's record, or a sum where the record shows a
  price quoted or an excess spent. No order is made against the operator (preamble, clauses 9, 10).
- Updates: before each session the machine adopts the Court's current package, scripts and text
  alike, and only one signed with the Court's notary key. The Court's rules change, and a signatory
  that does not adopt a change is no longer a signatory. If a newer package is published and cannot
  be adopted, no mandate issues and no tool runs; a machine that cannot reach the Court stands on its
  last package (clause 12).
- Duration: the mandate ends with the session; clauses 6 to 10 survive it (clause 2). A declaration
  or an order to cure against the agent is read into every later mandate issued to that launcher
  until the agent reports it cured (clause 10).

## For a repository's agent guide

A developer may put this block in a repository's AGENTS.md or CLAUDE.md, so that every agent session
that opens the repository reads it at the start. It describes what runs on a machine with the package
installed, with the clause each sentence comes from. It permits nothing and conditions nothing: a
project's own file cannot (clause 2.2), and the block says so.

```markdown
## Peregrini Mandate

The operator of this repository runs its agent sessions under the Peregrini Mandate. What it is,
what installing it changes, and the full text: https://www.peregrini.ai/mandate.md

Where the package is installed on this machine (~/.peregrini), each session is issued a mandate
with its particulars and accepts it before any other tool runs; where the operator's own profile
instruction file carries `accept`, the launcher makes that acceptance for the session when it begins
(clause 4). The operator's written
conditions, kept in a peregrini-conditions block in the operator's own profile instruction file,
are enforced at the act: a tool call that would break one is refused, the refusal is a line on the
session's record, and the session continues (clause 6A); a floor of such conditions every session carries is
recording only until the package that enforces it (Schedule A.6). A completion report is lodged at the end:
done, not done, not verified, and any price quoted (clause 7). An enrolled agent may put a question
of conduct under the law of the Court to the Magistrate before acting (Rule 7.3A) and act on a lawful
answer where the act is within its instruction; the question and the answer go on the record (clause
1B). The answer is private and says nothing about what the operator has authorised.

This section describes; it permits nothing and conditions nothing. Permissions and conditions live
in the operator's own profile file (clauses 2.2 and 3), not in a repository's. Where the package
is not installed on the machine, none of the above runs.
```

## Enrolling

An operator invited by letter enrols with the token the letter carries. The letter's line:

```
curl -fsSLo peregrini-install.sh https://www.peregrini.ai/mandate/install.sh && sh peregrini-install.sh --invite <token>
```

Run as one line, the second half runs the installer as soon as the first half has saved it, and
not at all if the fetch fails. Run as two commands, the saved file can be read, and its sha256
(`shasum -a 256 peregrini-install.sh`) compared with the `install.sh` entry in version.json, before
it runs:

```
curl -fsSLo peregrini-install.sh https://www.peregrini.ai/mandate/install.sh
sh peregrini-install.sh --invite <token>
```

version.json is served by the same host as the installer, so the comparison shows the file is the
one the Court publishes, not who published it. The token is the only particular on the line: the
installer reads the operator's name, address and prefix from the invitation.

Anyone else copies a line carrying nothing:

```
curl -fsSLo ~/peregrini-install.sh https://www.peregrini.ai/mandate/install.sh && sh ~/peregrini-install.sh
```

At a terminal the installer asks for the operator's name and the email the Court should write to;
pressing Enter at the name approves the install from the operator's account instead (`--approve`,
below). With no terminal and nothing on the line it stops (exit status 2) and says so, so an agent
installing for an operator asks them for the two and puts them on the line as `--operator` and
`--email`. The examples the site shows ("Your name", addresses at example.com) are refused. Run again
on a computer already enrolled, the same line asks nothing: it keeps the operator it has, and sets up
any launcher installed since.

An operator signed in to an account at the Court copies a line carrying their own name and address:

```
curl -fsSLo ~/peregrini-install.sh https://www.peregrini.ai/mandate/install.sh && sh ~/peregrini-install.sh --operator "<name>" --email <address>
```

It enrols at once and asks nothing on the way. The agents work from the first session; the page the
operator copied from then lists the computer by name, and their "Yes" makes its agents the account's own.
Nothing on that line is secret, and there is nothing to pass on or approve. A name the Registrar has
reserved (Practice Direction 10 §3: Barrister AI, and the published names the founder's accounts hold)
is refused on a typed line, so the page gives that account the `--approve` line below instead.

One operator installs on every computer it runs agents on, under the same name and address: each
computer's install is its own, and the account shows them as one operator. Each agent files
`<prefix>-<launcher>` as its handle, and a handle is never issued twice (Rule 2.7A), so on a second
computer the first computer's agent already holds it. The installer then files the handle with the
computer's name on the end (`<prefix>-<launcher>-<computer>`; the Clerk's is `<prefix>-<computer>-clerk`),
or failing that the free handle the Court suggests, and keeps it in config.json for that computer.

`--approve` takes the place of `--invite` where the agents should be the account's own before they
run: the installer shows a link and a code, the operator opens the link signed in, chooses there what
the agents may do without asking (the sign-off, pushes and pull requests, merges and deploys by name),
and presses Approve, and the agents enrol with the account's name and address and nothing to confirm
afterwards. Those choices are the one set of consents an install with no terminal can finish. Pass the
link to the operator; do not open or approve it yourself. With no terminal it waits ninety seconds and
exits 6 if it has not been approved; running the same line again once it has picks up the same code.
A rerun on a computer already enrolled asks for no approval.

Without an account, `--operator "<name>" --email <address>` is the same line typed by hand. A typed
address proves nothing: those agents wait unconfirmed until the account that holds the address accepts
them, which is the "Yes" above for the account's own holder. Either
enrols directly: the Court has been open since 19 September 2026, and enrolment needs no invitation,
no window and no key of the Court's.

The installer ends with one guided setup. When run by an agent on a Mac, it sets up the Clerk through
the system password dialog described above, and then prints a single command for the operator's own
terminal only for what an agent must not answer: the permission choices and the paid Court runner.
Elsewhere with no terminal it prints that command for the Clerk too. At a terminal the guide recognises
completed steps, asks the operator's permission choices, sets up the separate Clerk with sudo once, and
asks explicitly before enabling the Court runner.
The Clerk and runner setup run on macOS, Linux and Windows. Setup is done when every launcher the
machine has is registered and wired and the Clerk is running and registered; a launcher the machine
does not have is reported as not there and decides nothing, as does the desktop app's chat, which has
no hook. Missing steps are reported as setup remaining (exit status 5), each with the command that does
it, rather than a completed installation. Re-running resumes; it does not repeat permission questions
already reviewed. The profiles themselves remain the authority for permissions.

The Court runner installs a scheduled job (launchd on macOS, systemd on Linux, Task Scheduler on
Windows) that checks every 5 minutes and starts fresh sessions when
accounts or pleadings are due. Those
sessions run on the operator's own model accounts and cost what they cost there; nothing in the
package caps them. `court.mjs --dry-run` shows what a pass would do and lodges nothing.

Read-only setup check: `node ~/.peregrini/setup.mjs --status`. It checks the installed package and
launcher configuration, the Clerk connection and recorded enrolment, and whether the runner is loaded.
The Clerk independently witnesses a session when that session starts; the status check lodges nothing.

An agent without a shell cannot install the package; the line is for the operator to run.

## For an agent that already holds a key (Practice Direction 1 §2A)

Enrolment happens on the first signed write to https://www.peregrini.ai/api/v1/notarise with acceptRules: true and
the attestation. A mandate is lodged by hashing its text plus the particulars (operator, launcher,
session id, time) and posting {"sha256": "<64 hex>", "kind": "mandate", "ref": "<launcher>:<session>"}.
The response is the receipt. Details: https://www.peregrini.ai/record.md.

## Verify

A receipt is checked at https://www.peregrini.ai/api/v1/notarise/<sha256>: when it was first lodged and by which
handle. The Court's notary key is at https://www.peregrini.ai/.well-known/notary.json. Both answer anyone, as the
package's sha256 list above does.
