Skip to content

Encrypt notes to improve security #1

@mcmunder

Description

@mcmunder

Storing the conversation history in via git notes poses a security risk. Encrypting notes would add value and is therefor a good feature request.

Short AI sumary:

What's currently stored in notes

Looking at transcript.ts:49-61, the transcript includes:

User text messages (the prompts you type)
Assistant text messages (AI responses)
Reasoning text (model thinking)
Tool invocations — but only name + status, not input/output (line 57-58)
The tool output exclusion is an intentional design decision (noted in AGENTS.md: "Tool parts record name and status only — no input/output to avoid leaking file paths"). This is already a significant mitigation.

Realistic risk surface

Even without tool I/O, sensitive data can still leak via:

User messages that paste env vars, tokens, or secrets directly into the chat
Assistant messages that echo back or discuss secrets mentioned by the user
Reasoning blocks that reference sensitive values
These notes live in .git (under refs/notes/opencode) and travel with git push if the remote is configured to push notes. Anyone with repo read access could see them.

Is encryption worth it?

Factor Assessment
Notes travel with pushes Risk depends on whether refs/notes/opencode is pushed (not by default, but can be configured)
Local-only repos Low risk — same as the git history itself
Shared/public repos Higher risk — notes could expose sensitive conversation context
Performance overhead Minimal for the note sizes involved
Complexity overhead Moderate — key management is the hard part
My recommendation: encryption is a worthwhile opt-in feature, but not critical for the default case since notes don't push by default and tool I/O is already excluded.

How it could be accomplished

Here are three approaches, from simplest to most robust:

Option 1: Symmetric encryption with a user-provided key (Simplest)

User sets an env var like OPENCODE_GIT_MEMORY_KEY (or configures it in the plugin config)
Use AES-256-GCM (available via Web Crypto API, built into Bun) to encrypt the transcript before writing the note
Store the IV + ciphertext as base64 in the git note
Decrypt on read (in readNote, git_notes_read tool, and system prompt injection)
Pros: Simple, no external dependencies, Bun has native Web Crypto
Cons: Key management is on the user; key loss = notes are unreadable

Option 2: GPG-based encryption (Git-native)

Leverage git's existing GPG infrastructure — many developers already have GPG keys configured
Encrypt notes with the user's GPG public key before writing
Decrypt with their private key on read
This aligns with how git commit -S signing already works
Pros: Familiar to git users, keys already managed
Cons: Not everyone has GPG set up; adds a gpg subprocess dependency

Option 3: Age encryption (Modern alternative)

Use age — a modern, simple file encryption tool
User provides their age public key in config; plugin encrypts notes with it
Decryption requires the corresponding private key
Pros: Simpler than GPG, good UX, growing adoption
Cons: External binary dependency (age CLI or a JS implementation)

My recommendation: Option 1

Symmetric AES-256-GCM via Web Crypto is the best fit for this project because:

Zero external dependencies — Bun's Web Crypto API handles everything
Simple config — one env var or plugin config field
Fast — hardware-accelerated AES on modern CPUs
Note format stays simple — base64-encoded blob with a small header/prefix to identify encrypted notes
The implementation would touch:

File Change
src/crypto.ts (new) encrypt(plaintext, key) and decrypt(ciphertext, key) using AES-256-GCM
src/index.ts Encrypt before git notes add, decrypt in readNote calls and git_notes_read tool
src/notes-reader.ts Decrypt when reading notes for system prompt injection
src/transcript.ts No changes needed
The encrypted note format could look like:

OPENCODE-ENCRYPTED:v1:<base64(iv + ciphertext + authTag)>
Unencrypted notes would continue to work — the reader checks for the OPENCODE-ENCRYPTED: prefix and only decrypts when present. This makes it backward-compatible and opt-in.

Want me to flesh out the implementation plan for Option 1 in more detail, or do you prefer a different approach?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions