Skip to content

Finish the key-place review's agent-safety changes - #106

Merged
nniiovoo merged 1 commit into
mainfrom
fix/agent-safety-rest
Nov 3, 2026
Merged

nniiovoo merged 1 commit into
mainfrom
fix/agent-safety-rest

Conversation

@nniiovoo

@nniiovoo nniiovoo commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Why

The 2026-11-02 review asked whether encrypted-note is a safe place to keep keys when AI agents are around. Its answer was "partly", with 12 recommended changes. #105 covers two of them. This PR does the rest that need no ADR change and no owner-only decision. Agents still stay out of the Vault (ADR-0007).

Stacked on #105. Review that one first.

What changed

Agent Companion

  • Two new fixed errors:
    • provider_rejected: HTTP 400 or 404. The agent is told not to retry and to tell the owner.
    • model_refusal: none of the refusal's wording is returned.
  • timeout, provider_unavailable and invalid_response now end "If it fails again, tell the owner."
  • The tool description says the grant can end early (Stop, a lock, sleep, one hour at most). It also says never to send Vault content or credentials, and to treat the output as untrusted.
  • policy.json now names the destination: https:, api.openai.com, port 443, POST, /v1/responses.
    • generate.mjs builds its request from those fields and refuses to load with any other.
    • policySha256 in the lock is updated.
    • The destination itself is unchanged.
  • New companion/isolation/write-agent-key.mjs writes the real Agent Key file from stdin.
    • It refuses anything not shaped like an OpenAI key, Admin keys, and Anthropic/OpenRouter keys.
    • It refuses symlinks, hard links, other owners and files with an ACL allow entry.
    • It writes 0600, asks Time Machine to skip the file, and never prints the value.
  • scripts/package-companion.sh --app refuses the real identifier unless you also pass --owner-build and type the identifier at a terminal.

Repo checks

  • scripts/scan-secrets.mjs (run by check.sh) scans tracked files for:
    • provider key shapes and PEM blocks;
    • 12 or more BIP39 words in a row, including numbered lists, tables and comment-split forms.
  • scripts/pinned-hashes.txt pins Entitlements.plist and the helper and Node entitlements. check.sh fails if one changes.
  • scripts/check-network-source.mjs:
    • only generate.mjs may import node:https and only http.mjs may import node:http;
    • no other networking module (now including dns) is allowed in companion/;
    • policy.json must match its pin and name the approved destination.
  • CI runs npm audit --omit=dev --audit-level=high for the root and for companion/.
  • .gitignore ignores .env, .env.* and *.env.

Owner scripts

  • scripts/build-reader.sh --out <folder> builds the Emergency Reader from a fresh clone of origin/main.
    • It refuses an output folder inside the project.
    • It clears compiler and flag variables from the calling shell.
    • It prints the commit and the binary's SHA-256.
  • scripts/companion-setup.sh [--dry-run] <agent account> replaces the manual folder, fake key and plan steps.
    • It refuses admin, system and nonexistent accounts.
    • It checks the shared folder again just before changing it.

App

  • Show and every Copy are refused in Rust when the window isn't in front, Wallet Kinds included. A press is refused before any Master Password panel opens.
    • Only a typed unlock counts as being in front. Restore, Replace and Touch ID keep the current state.
    • The main thread records the live focus state, so a late focus event can't leave the flag stale.
  • settings.json keeps the Vault's id and change count (FORMAT.md section 14):
    • Welcome shows "Your Vault's files are missing" with Open a Backup.
    • Unlocking an older copy shows a warning.
    • These catch mistakes, not attacks: anything that can edit settings.json can defeat them.
  • Touch ID turns off with an explanation when the Vault file was replaced by another Vault, or by a copy from before a credential change.
  • The Companion shows "Off: ended because the Vault locked" after a real lock. Key Rotation shows a plain Off.
  • The Sharing Guard detects Screenpipe on a Mac.
  • The Emergency Reader prints the plain list only to a terminal, like --reveal.

Docs and in-app text

  • A new guide section, "When an agent needs a key".
  • Wording that promised more than the code does is fixed: "one approved agent", "nothing from your Vault ever reaches it", "a correct Agent Key file", and others.
  • Setup steps point to the new scripts. The real-key step uses write-agent-key.mjs.
  • The error table has rows for the new errors.
  • The capture checklist has rows for Screenpipe and for Show/Copy from the background.

Tests

  • ./scripts/check.sh passes on this Mac: 732 Rust tests, 75 Companion tests, clippy, fmt, cargo deny, the helper typecheck, the network and secret scans, the entitlement pins and the WebView build.
  • Companion: 75 of 75 pass, including with the default macOS TMPDIR. Before the review fix, one new test failed there.
  • New Rust tests cover:
    • Show/Copy refused from the background (Login, API Key, Private Key);
    • a background Restore not counting as being in front;
    • the newest-seen record, the missing-Vault state and Touch ID file changes;
    • ended_by_lock;
    • the Reader's list refusal.
  • Script refusal cases were run on fake accounts, fake clones and scratch folders.
  • Not run:
    • Windows locally (CI covers it);
    • npm audit (needs the network; it runs in CI);
    • companion-setup.sh against a real agent account;
    • the app with a fictional Vault.

Review

An independent review in four areas, with a skeptic re-checking every finding, found 21 problems. All are fixed, and a separate check confirmed each fix. The main ones:

  • a new test failing on default macOS temp folders;
  • a background Restore re-enabling Copy;
  • Wallet Show skipping the in-front check;
  • the key writer ignoring ACLs;
  • a symlink race in the setup script.

Decisions taken for the owner (easy to reverse)

  • No ADR amendments: the grant still ends on any lock, and the helper gets no Remove or Test.
  • A record-only API Key Note (service, where it's managed, expiry, no key) is allowed. "When an agent needs a key" is advice, not an ADR-0007 pilot.
  • Key Rotation only says where older copies are. It deletes nothing.
  • Always-open AI assistants are not added to the Sharing Guard.
  • README keeps "Agent native", now with a definition.

Still the owner's call

  • The agent account's login route. Until that's chosen, setup can't be finished and the guide says so.
  • Developer ID signing and notarization.
  • GitHub settings: branch protection for admins, Dependabot, and secret-scanning custom patterns.
  • --owner-build can still be passed by anything with a pseudo-terminal. A plain npm run tauri -- build builds the real identifier with no gate.
  • Hand checks: the Accessibility Inspector, Show/Copy from the background in the real app, and the screens.

🤖 Generated with Claude Code

The 2026-11-02 review found encrypted-note "partly" safe as a place for
keys with AI agents around. This does the rest of its changes that need
no ADR amendment and no owner-only decision. Agents still stay out of
the Vault (ADR-0007).

- Companion: fixed provider_rejected and model_refusal errors, "tell the
  owner" endings, an honest tool description, the destination pinned in
  policy.json, and write-agent-key.mjs for the real key file.
- Checks: secret and Seed Phrase scan, pinned entitlement hashes,
  stricter Companion networking rules, npm audit in CI, .env ignored.
- Owner scripts: build-reader.sh and companion-setup.sh;
  package-companion.sh --app needs --owner-build for the real identifier.
- App: Show and Copy refused unless the window is in front, missing and
  older Vault warnings, a Touch ID file-changed notice, "ended because
  the Vault locked", Screenpipe in the Sharing Guard, and the Reader's
  list printed only to a terminal.
- Docs and in-app text: "When an agent needs a key", and wording that
  promised more than the code does.

An independent review found 21 problems in the first version; all are
fixed. ./scripts/check.sh passes on macOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nniiovoo
nniiovoo changed the base branch from fix/honest-agent-warnings to main November 3, 2026 23:51
@nniiovoo
nniiovoo merged commit 23c3135 into main Nov 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant