Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ A personal app for storing secrets, encrypted and saved only on this computer.
- Never add network access to the Vault core or WebView: no HTTP clients, updaters, telemetry, crash reporters or analytics (ADR-0001). The only approved exception is the optional Agent Companion helper (ADR-0009): a separately sandboxed XPC service that makes only the companion's fixed OpenAI request. Never widen it, give it Vault access, or add networking anywhere else. Its source is in `companion/`: never import that folder from `src/`, `src-tauri/src/` or `crates/` (ADR-0010).
- Keys and decrypted Notes live only in the Rust core; the WebView receives one Hidden Field at a time (ADR-0003). The Agent Companion helper may hold one separately created Agent Key, never a Vault value (ADR-0009).
- Never pass Vault content, Note metadata, Note keys, Wallet Kinds, the Master Password or the Recovery Key to the Agent Companion, and never forward helper output, provider errors, keys or tokens to the WebView (ADR-0009).
- The agent key route (ADR-0012, accepted, not built) lets an agent store keys and manage the Notes it added, and nothing more. Keep it to its five requests, only from the separate agent account, behind a grant. Values are typed by the owner into the app's own native window, never passed through the agent. It returns no value, no Note it didn't add, no Master Password or Recovery Key, and never signs. Address, chain, website and service edits need the owner's confirmation, and deletes need the Master Password. It shares nothing with `companion/`. Chat Wallets and signing (ADR-0011) are deferred: don't build them.
- The agent key route (ADR-0012; built with fictional data, not yet checked by hand or reviewed) lets an agent in the separate agent account store keys and manage the Notes it added, and nothing more. Its code: `src-tauri/src/core/agent.rs` (the grant and the five requests), `src-tauri/src/agent_route.rs` (the Unix socket in `/Users/Shared/encrypted-note-keys`, the app's only listener), `crates/agent-keys-mcp` (the agent's tool), and the mark and edit rules in `crates/notes/src/agent.rs`. Keep it to the five requests, from the agent account's uid only, behind the owner's grant of at most an hour. No request field or reply may carry a value, whether a value is set, a Note not marked "added by an agent", a Note in Trash, the Master Password or the Recovery Key; replies stay fixed shapes and error codes, never free text. The owner types values into the app's own editor, never through the agent, and nothing marks an existing Note. Don't loosen `agent_edit_rule`: where-a-key-is-used edits need the owner's Allow, and every Wallet Kind edit and every delete also the Master Password. Unix sockets and peer-uid checks belong only in `agent_route.rs` and `crates/agent-keys-mcp/src/`, the tool depends on no Vault crate, and none of this code uses `companion/` (`node scripts/check-network-source.mjs` checks all three). It never signs: Chat Wallets and signing (ADR-0011) are deferred, so don't build them.
- Add a dependency only when it's clearly needed, pin its exact version, and say why in the PR/issue.

## Verify

- In a fresh clone or worktree, first: `npm ci --ignore-scripts` (never `npm install`, ADR-0003), and on Mac also `npm --prefix companion ci --ignore-scripts`
- Before a pull request: `./scripts/check.sh` (several minutes: run it in the background). It also runs the secret scan, `node scripts/scan-secrets.mjs`, and fails if an entitlement file differs from `scripts/pinned-hashes.txt`.
- One crate: `cargo test --locked -p <store|notes|session|policy|guard|vault-format|enote-reader>` (slow: run it in the background)
- One crate: `cargo test --locked -p <store|notes|session|policy|guard|vault-format|enote-reader|agent-keys-mcp>` (slow: run it in the background)
- App crate: `cargo test --locked -p encrypted-note-app --tests` (slow, can take many minutes: run it in the background)
- WebView: `npm run typecheck`
- Network rule, and no Vault imports of `companion/`: `node scripts/check-network-source.mjs`
- Companion (Mac): `npm --prefix companion test`. It runs one file at a time because the tests share port 8789, so don't run it alongside `scripts/verify-companion.sh`.
- App with a fictional Vault: `ENOTE_DEV_DATA_DIR="$(mktemp -d)" npm run tauri dev` (add `ENOTE_DEV_ALLOW_CAPTURE=1` for screenshots)
- Owner scripts: `scripts/build-reader.sh` and `scripts/companion-setup.sh`. Run the setup script only with `--dry-run` and an account name that doesn't exist.
- Screens without Rust: `npm run dev` (port 1420 must be free), then open `http://127.0.0.1:1420/fixture.html?phase=unlocked&companion=running`. Other switches, listed in `src/dev-fixture.ts`: `companion=ended_by_lock`, `&older`, `&touchid_changed`, and `phase=no_vault&missing`. Replies are fictional; this proves nothing about native panels, locks or the helper.
- App with a fictional Vault: `ENOTE_DEV_DATA_DIR="$(mktemp -d)" npm run tauri dev` (add `ENOTE_DEV_ALLOW_CAPTURE=1` for screenshots). To try the agent route in one account, add `ENOTE_DEV_AGENT_UID` and `ENOTE_DEV_AGENT_SOCKET_DIR` (debug builds only; see "For developers" in `docs/agent-keys.md`)
- Owner scripts: `scripts/build-reader.sh`, `scripts/companion-setup.sh` and `scripts/agent-keys-setup.sh`. Run the setup scripts only with `--dry-run` and an account name that doesn't exist.
- Screens without Rust: `npm run dev` (port 1420 must be free), then open `http://127.0.0.1:1420/fixture.html?phase=unlocked&companion=running`. Other switches, listed in `src/dev-fixture.ts`: `companion=ended_by_lock`, `&older`, `&touchid_changed`, `phase=no_vault&missing`, and for agents storing keys `agent=running`, `agent_request=store` (or `edit`, `delete`…) and `&agent_note`. Replies are fictional; this proves nothing about native panels, locks, the helper or the agent route.

Several sessions may share this checkout: check `git status` and stage named paths only. On an exFAT drive, macOS writes `._*` files that break the Tauri build: set `CARGO_TARGET_DIR` and `TMPDIR` to an APFS disk, pass `--out` and `--cache` on that disk to `scripts/package-companion.sh`, and first run `find src-tauri/capabilities src-tauri/permissions -name '._*' -delete 2>/dev/null || true`.

Expand Down
5 changes: 3 additions & 2 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ The optional helper inside the app, with its own macOS sandbox, that lets the ag
_Avoid_: agent bridge (that's the agent account's side), Vault connector, plugin

**Agent-added Note**:
A **Note** an AI agent asked the app to store, marked "added by an agent" (accepted, not built). The owner typed its value into the app's own window; the agent never saw it. The agent can list it, edit its details and ask to replace or delete it, but never read or use its value, and it can't see or touch any other **Note**. Removing the mark takes it out of the agent's reach ([ADR-0012](docs/adr/0012-agents-store-and-manage-keys.md)).
A **Note** an AI agent in the separate agent account asked the app to store, marked "added by an agent" (built 2026-11-06 and checked with fictional data only; hand checks on a real Mac and a security review still to do). The owner typed its value into the app's own editor; the agent never saw it. While the owner's grant of at most an hour runs, the agent can list it (name and visible details, never a value), edit its details and ask to replace or delete it, but never read or use its value, and it can't see or touch any other **Note** or one in **Trash**. Edits that say where a key is used need the owner's Allow; every edit of a **Wallet Kind** and every delete also need the **Master Password**. Removing the mark takes it out of the agent's reach for good ([ADR-0012](docs/adr/0012-agents-store-and-manage-keys.md), [Agents storing keys](docs/agent-keys.md)).
_Avoid_: agent key (that's an **Agent Key**), agent Note, managed key

**Chat Wallet**:
Expand Down Expand Up @@ -136,7 +136,7 @@ _Avoid_: pinned, bookmarked, starred item
## Relationships

- The owner has one **Vault**; it moves between computers only when the owner copies the file by hand.
- The app gives AI agents no interface to the **Vault**, its **Notes**, their metadata, **Backups**, **Safety Copies** or the **Emergency Reader**, and agents are told never to use them. (Once ADR-0012 is built, the one exception is **Agent-added Notes**: their names and visible details, never their values.) That limits the app, not the computer: what keeps an agent running in the owner's own Mac account away from these files is the owner's setup. The **Master Password**, **Recovery Key** and **Wallet Kinds** never take part in agent work.
- The app gives AI agents no interface to the **Vault**, its **Notes**, their metadata, **Backups**, **Safety Copies** or the **Emergency Reader**, and agents are told never to use them. (The one exception is **Agent-added Notes**, through a local route only the separate agent account can use while the owner's grant runs: their names and visible details, never their values.) That limits the app, not the computer: what keeps an agent running in the owner's own Mac account away from these files is the owner's setup. The **Master Password** and **Recovery Key** never take part in agent work, and a **Wallet Kind**'s value never does: an agent may only ask to store one, which the owner types in with the **Master Password**, and, for one it added, ask to edit its visible details (the owner's Allow, then the **Master Password**), replace its value (typed by the owner, with the **Master Password**) or delete it (the **Master Password**) ([ADR-0012](docs/adr/0012-agents-store-and-manage-keys.md)).
- The **Agent Companion** is the only **Agent Key** integration inside the app; it runs apart from the **Vault** in its own sandbox and never receives Vault content. Any other **Agent Key** integration is separate from this app. Hiding the key still lets an agent perform every operation its provider scope permits, within its enforced limits; only the owner can independently approve scope or limit changes ([ADR-0007](docs/adr/0007-agents-use-separate-restricted-keys.md)).
- Results from an external agent's allowed service actions may leave this computer, including for a cloud model provider; no Vault content or metadata may enter that route.
- Moving a **Vault** to the other computer means opening a **Backup** there. There is no separate "export" or "sync" concept.
Expand All @@ -156,6 +156,7 @@ _Avoid_: pinned, bookmarked, starred item
- **Safety Copies** stay on this computer; a **Backup** is the owner's copy somewhere else. Both are complete **Vaults**.
- The **Emergency Reader** never writes **Notes** to disk; it only shows them, and only in a terminal unless the owner adds `--allow-non-terminal`.
- A **Vault** that uses any of the newer optional parts of a **Note** (the address or derivation path of a **Seed Phrase**; the purpose of a **Login**; the key ID, secret, purpose, where it is managed, expiry date or variable name of an **API Key**), even on a **Note** in **Trash**, opens only in a current app and a current **Emergency Reader**. Older ones refuse the whole file, and some of them call it damaged, so the app on the other computer and any saved **Emergency Reader** must be updated first, before such a **Vault** is moved there.
- A **Vault** with an **Agent-added Note**, even one in **Trash**, likewise opens only in a current app and a current **Emergency Reader**; older ones refuse it as made by a newer version. Once no **Note** carries the mark, the next save is as readable by them as before the mark.
- A **Note** in **Trash** is still inside the **Vault** (still encrypted); "Delete forever" removes it from this copy only; older copies still contain it.
- Removing a **Wallet Kind** needs the **Master Password**, like every other change to one: moving it to **Trash**, "Delete forever", and emptying a **Trash** that holds one.
- Changing the **Master Password** changes it only in the copy of the **Vault** where it was changed; older copies still open with the old **Master Password** and old **Recovery Key**.
Expand Down
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ members = [
"crates/guard",
"crates/store",
"crates/reader",
"crates/agent-keys-mcp",
"src-tauri",
]

Expand Down
4 changes: 2 additions & 2 deletions PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ product

An individual owner keeping personal secrets on their own Mac or Windows computer. The owner needs to find a Note, deliberately reveal a Hidden Field, save changes, make a Backup, or recover access without a server account. Use the established names in [CONTEXT.md](CONTEXT.md) and explain actions in plain language.

The optional Agent Companion serves a separate workflow: an owner gives tools in a distinct standard Mac account one restricted OpenAI text operation using a separately created Agent Key. An agent has no app interface to the Vault. The first private Agent Key entry flow is Mac only.
The optional Agent Companion serves a separate workflow: an owner gives tools in a distinct standard Mac account one restricted OpenAI text operation using a separately created Agent Key. An agent's only app interface to the Vault is the optional local route of ADR-0012, for the Notes an agent added and never their values. The first private Agent Key entry flow is Mac only.

Planned, accepted, not built: agents that store and manage keys ([ADR-0012](docs/adr/0012-agents-store-and-manage-keys.md)). From Claude or Codex, the owner asks for a key to be stored; the app opens its own native window, and the owner types the value there. The agent gets back only "saved", can list and edit the details of only the keys it added, and never sees or uses a value. Replacing a value or deleting a key happens in the app's own window too.
Built 2026-11-06, checked with fictional data only: agents that store and manage keys ([ADR-0012](docs/adr/0012-agents-store-and-manage-keys.md), [Agents storing keys](docs/agent-keys.md)). From Claude Code, Codex or the Claude app in the separate agent account, while the owner's grant runs, an agent asks for a key to be stored; the app comes to the front with its ordinary editor filled in from the agent's suggestion, and the owner types the value there. The agent gets back only "saved" and an id, can list and edit the details of only the keys it added, and never sees or uses a value. Replacing a value happens in the same editor, and edits that say where a key is used and deletes wait for the owner's confirm in the app. The screens say "An agent", because the app can't tell which program asked.

## Product Purpose

Expand Down
Loading
Loading