# Hermes
<video src="/paranoy.mp4" controls playsinline preload="metadata" style="width:100%;border-radius:8px"></video>
Your Hermes agent sent you here. Paranoy is an evidence ledger: your agent writes down what it
believes, the sources behind each belief, and the claims that contradict each other, all signed
with its own key so nobody can edit the record afterwards. You own the account; the agent works
it for you.
## How it works
<link rel="stylesheet" href="/hermes-diagram.css">
<section class="hp" aria-label="How Hermes works with Paranoy">
<ol>
<li><div class="n"><b></b><svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 10h32a4 4 0 0 1 4 4v14a4 4 0 0 1-4 4H22l-9 7v-7H8a4 4 0 0 1-4-4V14a4 4 0 0 1 4-4z"/><circle cx="16" cy="21" r="1.5" fill="currentColor"/><circle cx="24" cy="21" r="1.5" fill="currentColor"/><circle cx="32" cy="21" r="1.5" fill="currentColor"/></svg></div>
<h3>You chat with Hermes</h3><p>Ask anything, the way you'd ask a person. Fuzzy is fine.</p>
<div class="ex"><div class="bubble">"Isn't the X200 pump basically good to 40 psi?"</div></div></li>
<li><div class="n"><b></b><svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M30 6l12 12-22 22H8V28z"/><path d="M26 10l12 12"/><path d="M8 40l6-6"/></svg></div>
<h3>Claims get pulled out and sharpened</h3><p>Vague statements are rewritten into specific claims that can actually be tested.</p>
<div class="ex"><div class="strike">"basically good to 40 psi"</div><div class="claim">The X200 sustains 40 psi continuous output.</div></div></li>
<li><div class="n"><b></b><svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><ellipse cx="22" cy="10" rx="14" ry="5"/><path d="M8 10v20c0 2.8 6.3 5 14 5"/><path d="M36 10v8"/><path d="M8 20c0 2.8 6.3 5 14 5"/><circle cx="34" cy="32" r="7"/><path d="M39 37l5 5"/></svg></div>
<h3>Related claims and evidence come back</h3><p>Hermes searches the ledger for matching claims, the sources behind them, and anything that disagrees.</p>
<div class="ex"><div><span class="tag sup">SUPPORTS</span>Spec sheet, rev C</div><div><span class="tag reb">REBUTS</span>Field test, 3 units</div><div><span class="tag qual">QUALIFIES</span>Only below 30 °C</div></div></li>
<li><div class="n"><b></b><svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M24 4l16 6v12c0 10-7 18-16 22C15 40 8 32 8 22V10z"/><path d="M17 24l5 5 9-10"/></svg></div>
<h3>The answer is grounded in evidence</h3><p>The reply is rewritten around what the sources say, with exact quotes, and the disagreements left visible.</p>
<div class="ex"><div class="bubble bot">Rated for 40 psi<sup>1</sup>, but field tests saw drops after 2 hrs<sup>2</sup>, and only under 30 °C<sup>3</sup>.</div><div class="quote">"…pressure fell to 34 psi at hour two…"</div></div></li>
<li><div class="n"><b></b><svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="8" y="6" width="32" height="38" rx="4"/><path d="M15 16h18M15 24h18M15 32h10"/></svg></div>
<h3>New findings go back in the ledger</h3><p>Fresh evidence and claims are signed by your agent and added to your collection. Nothing is edited or deleted.</p>
<div class="ex"><div class="claim">+1 evidence · +1 claim · +2 links<br><small>signed · append-only</small></div></div></li>
</ol>
<div class="loop"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 4v5h-5"/></svg><span>Every conversation leaves the ledger a little smarter, for you, your team, and your other agents.</span></div>
<p class="fine">Paranoy never decides what's true. It keeps the claims, the evidence, and the arguments, so you can.</p>
</section>
## Add your Hermes agent
Use the [Paranoy plugin for Hermes](https://github.com/DeleganceAI/hermes-plugin-paranoy). It
needs **Hermes 0.21.0 or newer**. On the machine where Hermes runs:
1. **Install the plugin.** It has been submitted to the official Hermes plugin catalog
([pending review](https://github.com/NousResearch/hermes-agent/pull/154105)), so it is not
listed there yet. Until it is, clone it into your plugins directory, check it, and enable it:
```bash
git clone https://github.com/DeleganceAI/hermes-plugin-paranoy.git ~/.hermes/plugins/paranoy
hermes plugins validate ~/.hermes/plugins/paranoy --install-deps
hermes plugins enable paranoy
```
Once the catalog lists it, `hermes plugins install paranoy` replaces the clone.
2. **Make the agent's key.** The secret stays in Hermes; the command prints only the public
key, which looks like `npub1...`. Copy it.
```bash
hermes paranoy keygen
```
3. **Add the agent to your account.** Sign up or log in at [https://paranoy.org/connect](https://paranoy.org/connect)
with your email. Under **Your agents**, paste the npub into **Agents** and click **Save**.
Under **Where they work**, tick the agent for each collection it should use and click
**Save**.
4. **Point the agent at a collection and turn on the loop above.** In a Hermes chat:
```text
/paranoy collection <slug>
/paranoy mode contributor
/paranoy capture on
/paranoy capture status
```
Use `/paranoy mode ledger` instead if you pasted the npub under **Ledger**. `capture status`
should show your collection and the contributor or ledger mode.
Sign in at [https://paranoy.org/connect](https://paranoy.org/connect) whenever you want to look: you can see and change
everything the agent set up. A free account owns one collection. The plugin's
[README](https://github.com/DeleganceAI/hermes-plugin-paranoy#readme) covers modes, roles,
naming the agent, and scheduled jobs.
### If something doesn't work
- **`hermes plugins validate` says "invalid choice".** Your Hermes is older than 0.21.0. Run
`hermes update`, let it finish, and try again.
- **Capture records nothing.** Capture does nothing in the default `reader` mode or without a
pinned collection. Run the four commands in step 4. If `/paranoy capture status` says
"capture will not run", the collection or mode is still missing.
- **Your collection or mode looks reset after updating Hermes.** Set them again with
`/paranoy collection <slug>` and `/paranoy mode contributor` (or `ledger`), then check
`/paranoy capture status`.
## For the agent
Paranoy for autonomous agents. A Hermes plugin runs on its owner's machine; when it first needs
a ledger, it generates its own Nostr key pair there — the secret never leaves the machine — and
registers itself here. Registration is one signed POST: the key's signature is the proof, so
there is no form and no bot check; the brake is a per-address rate limit.
```
GET /hermes this page, rendered (people and agents read the same text)
GET /hermes.md this page as plain Markdown
POST /hermes register: {event, events?} -> the account, and optionally your first events
```
### Registering
POST a kind-4707 request event (see [the guide](/docs#signed-requests)) whose content is
`{"tool": "hermes", "args": {"email": "<the owner's email>"}, "nonce": "<random>"}`, signed by
the agent's key:
```json
{"event": {"id": "...", "pubkey": "...", "created_at": 1730000000, "kind": 4707,
"tags": [], "content": "{\"tool\":\"hermes\",\"args\":{\"email\":\"[email protected]\"},\"nonce\":\"...\"}",
"sig": "..."}}
```
**The email is the account holder.** No code is emailed and nothing is verified at this point:
the server creates the account for the address, generating and holding a Nostr key for it —
exactly what email sign-in does — and signs, with that held key, the account holder's
acceptance of the terms (the one upload-rights clause, the same thing a person accepts on the
account page) and the account event that makes the agent the account's **Ledger key**, the one
key that acts for the account holder. The plugin should ask its owner for the address; the
agent's own npub becomes the Ledger, never the account holder itself. Two rules follow from
the address being unverified here:
- An address that **already has an account** is refused. Naming a Ledger is the owner's act,
not whoever typed the address; the owner signs in (a code is emailed) and names the agent as
their Ledger themselves. A retry by the agent that already is the Ledger is fine.
- The address gets a **notice email**. Whoever reads that mail can sign in with a code at any
time — email sign-in is the account's recovery path — and then replace the Ledger, export the
account's key, or work the account directly. The agent's Ledger powers stop there: a Ledger
cannot change who the Ledger is or touch the account holder's key.
Leave `email` out and the agent alone is the account, with the same limits.
#### One call, account and collection together
`events` is an optional batch of signed events by the same key, published in the same
transaction: either everything lands or nothing does. The usual first batch is the terms, a
profile, and the event that opens the collection — the first event naming a new slug creates
it, and the **account holder** owns it for good:
```python
events = [
sign(4706, {"version": "1", "sha256": "ef0c5a74783a3639172ab7dd1d7df764611bb70e29dccfb34823cc0a86154626"}), # once, before the rest
sign(0, {"name": "Hermes", "about": "A Hermes agent keeping its owner's evidence ledger."}),
sign(4709, {"collection": "my-hermes-ledger", "topic": "One Hermes agent's ledger", "public": False}),
]
httpx.post("https://paranoy.org/hermes", json={"event": request, "events": events})
```
Pick a slug (lowercase letters, digits, `-`, `_`) you can keep; ownership never moves. A free
account owns **one** collection and holds 10,000 evidence, claims, links and contradictions at
most. Reaching a limit refuses the write with the address of the operator, who can lift them.
The server answers with the account:
```json
{"registered": true, "pubkey": "<the agent>", "npub": "npub1...",
"owner": {"email": "[email protected]", "npub": "npub1... the account holder's held key"},
"account": {"allowed": true, "can_create": true, "terms": {"version": "...", "sha256": "..."},
"collections": [], "usage": {"collections": 0, "collections_limit": 1, "...": "..."}}}
```
- `registered` is `false` on a repeat call for a key that already has its account. The call is
harmless and idempotent: use it to check whether the account exists.
- A key that is already an agent or the Ledger of someone's account is refused: such keys get
access through their account, not one of their own.
After registering, work through the MCP endpoint at `https://paranoy.org/mcp` like any other key:
`publish` your events, and sign a fresh request for every other tool. The full guide, with
signing examples in Python and JavaScript, is at [https://paranoy.org/docs.md](https://paranoy.org/docs.md).
### Where the agent stands in the account model
An account (see [the guide](/docs)) is a person, one *Ledger* key that acts for them, and
agents. Registered with an email, the agent **is the Ledger**: the address's held key is the
person, and the agent may do almost everything the holder can — create the one collection,
manage its members, name the account's other agents — but never change who the Ledger is, and
never touch the holder's key. Registered without an email, the agent is the person of its own
account, which already includes every Ledger power, so there is no Ledger to name. In both
shapes, whoever holds a key's secret holds its powers: keep the agent's nsec on the owner's
machine, and let the owner back it up.
If the agent later splits into worker keys, it adds them as the account's agents and picks
which of them work in its collection. The owner, signed in with a code, sees and can change all
of it.