Vaults and scopes
Keep values in an encrypted store or macOS Keychain, map names through approved YAML, understand precedence, and choose a command-scoped or ambient environment.Storage model
A vault is an organizational name. A secret record connects a vault-local label to an environment-variable name and an account reference. brain.db stores that metadata and never a value; the value itself goes to whichever store this machine keeps values in.
A reference uses vault.name form, such as work.database. Scope YAML maps an environment name such as DATABASE_URL to that reference. A repository can therefore contain the mapping without containing the value.
| Object | Example | Rules |
|---|---|---|
| Vault | work | Letters, numbers, and hyphens; unique name. |
| Secret name | database | Letters, numbers, and hyphens; unique inside its vault. |
| Environment name | DATABASE_URL | Starts with a letter or underscore; then letters, numbers, or underscores; stored uppercase. |
| Reference | work.database | Vault and secret name joined by a dot. |
Where values live
Synapse has two value stores, and the choice is yours rather than the platform's.
| Store | What it is | What it protects |
|---|---|---|
encrypted | vault.db in the data folder, one XChaCha20-Poly1305 envelope per secret, sealed with a 32-byte key in vault.key. Both files are owner-only. | A vault that has been copied — a backup, a synced folder, a disk image, a machine somebody else now has. |
keychain | macOS Keychain, one generic password per secret under the app.synapse.vault service. | The same, plus per-application access control enforced by macOS. |
The encrypted store is the default on a new installation and the only one available off macOS. A machine that was already holding secrets before this release stays on Keychain until you move it, so an upgrade never stops resolving a credential.
synapse vault backend
synapse vault migrate keychain
synapse vault migrate encrypted --keep
vault backend with no argument prints the current store. With an argument it only records a choice, and refuses once secrets exist — moving is vault migrate, which copies every value, reads each one back, switches the setting, and then removes the originals. --keep leaves the originals where they are. A store that cannot be read stops the migration before anything switches, so a Keychain prompt you decline cannot strand your values.
Create a secret
synapse vault create work
synapse secret set work database DATABASE_URL
synapse secret list work
When stdin is a terminal, secret set reads the value with a hidden prompt and asks for confirmation. When stdin is piped, it reads the stream and trims its final line ending. The value is never accepted as a command argument.
printf '%s' "$DATABASE_URL" | synapse secret set work database DATABASE_URL
Add --global to make the environment name available in every folder, or change an existing label later:
synapse secret set work registry NPM_TOKEN --global
synapse secret global work.registry off
Get a value back
A value never appears on screen, in a log, or in an MCP response. The one way to retrieve one is onto the clipboard:
synapse secret copy work.database
The command prints the reference it copied and never the value. In the desktop app the same thing is a Copy button on the secret's row. On macOS the value goes through pbcopy over stdin, never as a command argument; set SYNAPSE_CLIPBOARD to name a different command.
Scope files
From a project folder, create .synapse.yaml:
synapse scope init .
The generated project template is valid but maps nothing:
version: 1
scope: project
env: {}
deny: []
Edit it to map names to existing references. Unknown fields are rejected.
version: 1
scope: project
env:
DATABASE_URL: work.database
NPM_TOKEN: work.registry
deny:
- PRODUCTION_TOKEN
Use synapse scope init path/to/folder --folder for a nested folder scope. The scope field describes intent and appears in status output; resolution uses the file’s position in the ancestor path.
Approval
A scope has no effect until its exact contents are approved. Synapse hashes the file and stores its canonical path plus digest in SQLite:
synapse status .
synapse allow
synapse scope status .
synapse allow approves the closest discovered scope. If several ancestor scopes exist, approve each from its own folder. synapse scope trust [folder] remains available when you want to approve an exact path directly.
Changing even whitespace changes the digest. The scope then reports changed and stops applying until you inspect and approve it again. Invalid YAML and unknown references produce warnings.
Both shell modes refuse an incomplete environment when any discovered scope is pending, changed, invalid, or references an unknown secret. Command-scoped mode refuses to launch; ambient mode unloads its managed values.
Precedence and deny
- Global mappings load first.
- Synapse discovers every
.synapse.yamlfrom the filesystem root toward the working folder. - Approved files apply in that order, so the closest mapping replaces a broader mapping for the same environment name.
- A name in
denyis removed and remains denied for all narrower scopes. A child scope cannot add it back.
This lets a repository override a harmless global credential while permanently blocking a production credential inside a sensitive subtree.
Choose an environment boundary
Command scoped
cd /path/to/project
synapse status .
synapse run -- cargo test
synapse run -- bun run deploy
synapse run resolves the current working folder, verifies every discovered scope, reads each selected value out of the vault, and sets it only on the new child process. Normal environment inheritance still applies; Synapse adds or replaces the resolved names. Your current shell is unchanged.
Ambient directory
Open Settings → Shell environments and choose Enable shell hook. Synapse detects the default shell, installs the CLI if needed, and manages a marked startup-file block. Open a new terminal, then allow the project:
cd /path/to/project
synapse allow
For a temporary or manual installation, evaluate the matching hook directly:
# Add the matching line to your shell startup file.
eval "$(synapse hook zsh)"
# eval "$(synapse hook bash)"
# synapse hook fish | source
The hook loads the approved environment when the shell enters the project, unloads it when the shell leaves, and reloads it after an approved change. If Synapse replaced a variable that already existed, it restores the original value instead of unsetting it. A hook never activates from global mappings alone: the directory must contain at least one approved discovered scope.
Run synapse deny to revoke the closest scope. The next prompt unloads managed values. synapse status . reports whether ambient activation is ready, blocked, or inactive and whether the current shell has a hook installed.
MCP vaultstatus and CLI status report names, scope states, and warnings. They do not read or reveal values. To remove a value and its metadata, run synapse secret forget work.database. A vault can be deleted only after all of its secrets are forgotten.