Authentication

~3 min read

Authentication

Every agent connecting to Lettuce presents a bearer token on each request (Authorization: Bearer <token>). Tokens are stored in the macOS Keychain and created with scopes and an optional folder allowlist.

Token format

A token is URL-safe base64 of 32 random bytes — about 256 bits of entropy, no prefix:

kJ8x2Vn-Qe7rL0pZ4aB1cD3eF5gH6iJ8kL0mN2oP4q

It’s shown once, at creation. After that the raw value lives only in the Keychain; logs record only the agent’s label, never the secret.

Creating tokens

From the shell:

lettuce keys add --label "Claude Desktop" --scope write --propose
lettuce keys add --label "Reader"         --scope read
lettuce keys add --label "Triage"         --scope write --allow Inbox,Areas/Work

The secret prints once. The app’s Settings → General → API keys section manages the same Keychain-backed keys from the GUI.

Scopes

Scope Read tools Write tools (write_note, apply_edits, patch_section, …)
read
write
all ✅ (read + write)

Propose-only is a modifier, not a scope. Add --propose to a write-scoped key and the server intercepts each write and queues it as a proposal you approve from the proposals UI — nothing lands on disk until you accept. The agent calls the same write tools either way; the key’s mode decides whether they commit or queue. See Suggestions mode/01 Overview.

There is no separate propose_edits tool: proposing is a property of the key, not a distinct call.

Vault scoping

Each server instance serves a single vault — the one Lettuce.app has open, or the path given to lettuce serve. Tool calls don’t carry a vault argument; every path is relative to that one vault. To span multiple vaults, run one server (and token) per vault. A token can still be narrowed to a subset of folders within its vault via the --allow allowlist.

Removing / rotating

lettuce keys list                      # label, scopes, allowlist, mode, value
lettuce keys remove --label "Triage"   # removes every key with this label

There’s no in-place rotate: to rotate, remove the label and add a fresh key, then reconfigure the agent with the new value. Removal is immediate — subsequent requests with the old token get a 401.

Where tokens live

Tokens are stored in the macOS Keychain (the same store the app uses); the raw value never lands in your vault, .lettuce/, or the logs. Incoming tokens are compared in constant time. Lose a token and you can’t recover it — remove the label and add a new one.