# Brain governance

Date: 2026-08-26

Encryption decides who can read the brain at rest. Governance decides what the
agent holding the key is allowed to do with it while it runs. Aeonora does both;
this document covers the second.

Every tool call that touches brain content resolves to one governed action, gets
one decision, and leaves one receipt.

## Actions

| Action | Covers |
|---|---|
| `read-memory` | `query`, `read_note`, `list_notes` |
| `write-memory` | `remember` |
| `import-memory` | `import_brain` |
| `export-memory` | `restore_to` — decrypting the brain to plaintext on disk |

`describe` and `verify` return metadata rather than brain content and are not
gated. Everything else is.

## Presets

| Preset | read | write | import | export |
|---|---|---|---|---|
| `open` | allow | allow | allow | allow |
| `guarded` (default) | allow | require-human | allow | require-human |
| `readonly` | allow | deny | deny | deny |

```bash
aeonora policy                 # show the active preset and any live grants
aeonora policy readonly        # set one
```

`guarded` is the default because a brain that silently accepts writes from any
agent session is not governed, and a read-only brain is not useful enough to
stay installed.

## require-human means the call fails

A `require-human` decision denies the tool call. It does not warn and proceed.
The agent receives the refusal, the action name, and the command its owner
runs — never a way to lift the refusal itself.

```
remember refused by Aeonora governance (require-human): policy preset "guarded"
requires owner approval for write-memory
This needs the owner's approval. Ask them to run:  aeonora grant write-memory
```

The owner lifts it out of band:

```bash
aeonora grant write-memory            # 1 use, expires in 15 minutes
aeonora grant export-memory 60 3      # 3 uses, expires in 60 minutes
aeonora revoke write-memory           # take it back early
aeonora revoke                        # take everything back
```

Grants are time-boxed (1–1440 minutes) and use-counted. `gate()` spends a use on
the call it authorises, so an approval cannot be replayed. Expired and spent
grants are dropped on read rather than lingering as a decision that would
silently succeed later.

A grant lifts `require-human`. **Nothing lifts a `deny`** — not a grant, not a
preset. To turn a denial into a permission the owner changes the preset, which
is itself recorded.

## The constitution outranks the preset

The decision runs in order: signed manifest first, then preset, then grants.

`packages/aeonora/policy.mjs` is unchanged and still the root of authority, so
the manifest a controller signed cannot be widened locally. A preset can only
narrow it, or satisfy its `humanApproval` requirement via a grant. A suspended
agent (`active: false`) reads nothing whatever the preset says, and a capability
the manifest withheld cannot be granted back by editing `policy.json`.

Manifests signed before governance existed carry `read-memory` and
`write-memory` only. `import-memory` and `export-memory` inherit their
constitutional permission from the coarser action they are a form of — import
from write, export from read — so every manifest already in the wild stays valid
without a re-signature.

## Receipts

Every decision appends one line to `receipts.jsonl` in the workspace, whether
the call ran or not: a denial you cannot see is indistinguishable from a call
that never happened.

```bash
aeonora receipts                # last 20 decisions
aeonora receipts --limit 100
aeonora receipts --verify       # exit 0 intact, exit 1 broken
```

Receipts record the **shape** of what was touched, never the content — a log
that quotes the note it protected is a second plaintext copy of the brain. A
read records how many terms were searched; a write records the target path and
byte count.

Each receipt carries the commitment of the one before it, so the log is
hash-chained from a genesis value. This is tamper **evidence**, not tamper
proofing: a local file can always be rewritten, but rewriting it consistently
means recomputing every commitment after the edit. `--verify` reports the exact
sequence number where the chain first breaks, and detects both edited and
deleted entries.

## What governance does not cover

**Plaintext exports.** `restore_to`, and the adapter install path that writes
`.aeonora/brain/` into a harness, produce decrypted Markdown on disk. Aeonora
cannot apply policy to a file the harness opens directly. Both are treated
honestly rather than hidden:

- the export is a governed action, so it needs approval under `guarded` and
  leaves a receipt naming the destination;
- `describe` reports `plaintextExportsOnDisk` and `lastExport`;
- the bridge file written into the harness states that the copy beside it is not
  governed and directs the agent to the MCP tools instead.

**The model provider.** Unchanged from the custody claim: results returned by a
governed tool enter the model's context window. Governance decides whether the
agent may read a note. It cannot decide what the provider does with the note
once the agent has read it. `describe` continues to report
`plaintextReachesModelProvider: true`.

**Anything outside the MCP server.** Governance is enforced at the one interface
that decrypts the brain. It is not a sandbox and does not constrain the harness,
the filesystem, or a second process holding the same key.

## Files

All in the workspace (`AEONORA_WORKSPACE`, default `~/.aeonora/agent`):

| File | Contents |
|---|---|
| `policy.json` | active preset and its rules |
| `grants.json` | live grants, mode `0600` |
| `receipts.jsonl` | hash-chained decision log, append-only |

## Verification

`node packages/aeonora/governance-self-check.mjs` covers the decision table,
constitutional precedence, grant expiry and spend, revocation, and receipt-chain
tamper detection for both edited and deleted entries.

`node packages/mcp/self-check.mjs` proves the gate is wired into a real spawned
server over real stdio JSON-RPC: that `guarded` refuses an ungranted write, that
the refused write never reaches the brain, that a single-use grant is spent after
one write, that a `deny` preset offers no approval path, and that receipts record
shape rather than content.

Both run in `npm run check`.
