Use a scoped secret in either shell mode
Create a vaulted value, approve its project mapping, compare one-command and ambient loading without ever printing it, and watch trust invalidate the moment the file changes.Outcome and prerequisites
You will create a disposable value in the vault, map it to one project, and exercise both ways Synapse can hand it to something — without the value ever appearing on screen. Then you will break the approval on purpose and watch every path refuse.
- The
synapseCLI installed. Ifsynapse vault backendsayskeychain, unlock your login Keychain first. - An empty temporary folder. Do not run this in a real project.
- About twenty-five minutes.
Where the value actually lives
Worth being precise about before you start, because the whole design follows from it.
| Thing | Stored where |
|---|---|
| The secret value | The value store, and only there. That is vault.db — sealed, one envelope per secret, under the key in vault.key — or macOS Keychain if the machine is set to it. synapse vault backend says which. |
| Its name, its variable name, its account reference | brain.db, Synapse's SQLite database. Never the value. |
| Which variable a folder should get | .synapse.yaml in the project, which holds a reference and never a value. |
| Whether that file is trusted | A SHA-256 digest of its exact bytes, recorded when you approve it. |
No secret value is ever written to brain.db, to YAML, to an MCP response, or to a log, and neither store keeps one in plaintext. A value is read out of the vault at the moment a child process is launched, and goes nowhere else unless you ask for it with synapse secret copy, which puts it on the clipboard and still never prints it.
-
Create a vault and a value
mkdir -p "$HOME/tmp/synapsetutorial" cd "$HOME/tmp/synapsetutorial" synapse vault create tutorial synapse secret set tutorial demo SYNAPSE_TUTORIAL_TOKENEnter a disposable value at the hidden prompt and confirm it. A vault is an organizational label; the three arguments are the vault, the secret's name inside it, and the environment variable it will become.
Created vault tutorial Saved tutorial.demo in the encrypted vaultNow list what exists. Metadata only — there is no command that prints a value, because there is no reason for one to exist:
synapse secret list tutorialtutorial.demo SYNAPSE_TUTORIAL_TOKEN scoped -
Map it to this folder and approve the file
synapse scope init .Replace the generated
.synapse.yamlwith:version: 1 scope: project env: SYNAPSE_TUTORIAL_TOKEN: tutorial.demo deny: []tutorial.demois a reference, not a value. This file is safe to commit — that is the point of it holding a reference. Look at the file, then approve its exact bytes:synapse scope status . synapse allow synapse status .Allowed /Users/example/tmp/synapsetutorial/.synapse.yamlFolder: . Available: SYNAPSE_TUTORIAL_TOKEN Ambient: ready /Users/example/tmp/synapsetutorial/.synapse.yaml [project · approved]Ambient: readymeans a shell hook would activate here. It does not mean one is installed. -
Hand it to exactly one command
This is the narrower of the two boundaries and the one to prefer. Verify presence without printing content:
synapse run -- sh -c 'test -n "$SYNAPSE_TUTORIAL_TOKEN" && echo "tutorial token available"'The child prints only the fixed sentence. Confirm the parent shell was never touched:
test -z "$SYNAPSE_TUTORIAL_TOKEN" && echo "parent unchanged"Synapse read the stored value, set it on that one child, and the child exited with it. Nothing was exported into your session.
-
Activate the whole directory
For a permanent setup, use Settings → Shell environments → Enable shell hook and open a new terminal. To try it immediately in this shell:
# zsh eval "$(synapse hook zsh)" # bash: eval "$(synapse hook bash)" # fish: synapse hook fish | sourceThe approved scope activates at once. Now walk in and out of it:
test -n "$SYNAPSE_TUTORIAL_TOKEN" && echo "ambient token available" cd .. test -z "$SYNAPSE_TUTORIAL_TOKEN" && echo "ambient token unloaded" cd synapsetutorial test -n "$SYNAPSE_TUTORIAL_TOKEN" && echo "ambient token restored"All three print. Leaving the directory unloads exactly the variables the hook loaded — and if one of them had a value before activation, leaving restores that original value rather than unsetting it. The hook tracks which keys it owns precisely so it can put things back as it found them.
-
Break the approval and watch everything refuse
Approval is bound to the exact bytes of the file. A blank line is a different file:
echo "" >> .synapse.yaml synapse status .Folder: . Available: none Ambient: blocked /Users/example/tmp/synapsetutorial/.synapse.yaml [project · pending] warning: /Users/example/tmp/synapsetutorial/.synapse.yaml: Scope has not been approvedThree things happen at once. Ambient mode unloads at the next prompt.
synapse runrefuses before launching anything. Andsynapse launchrefuses to start a coding tool at all:synapse run -- sh -c 'echo should-not-run'Error: vault scope is not ready: /Users/example/tmp/synapsetutorial/.synapse.yaml: Scope has not been approvedIt refuses rather than running with the variable missing, because a command that runs against a half-resolved environment fails in ways that look like success. Read what changed, then re-approve:
synapse scope status . synapse allowBoth presence tests work again. This is what makes a committed
.synapse.yamlsafe: a teammate's edit, or a malicious one, arrives untrusted and stays that way until a human looks at it. -
Prove that deny wins
Scopes resolve from the root of your filesystem down to the current folder, so a narrower file can add variables. It can never add back one a broader scope denied. Move the variable into
deny:version: 1 scope: project env: {} deny: - SYNAPSE_TUTORIAL_TOKENsynapse allow synapse status .Availableno longer lists the name. Create a folder-level scope beneath this one that tries to map it again, approve that too, and it still will not appear. Deny is a ceiling, not a default. -
See why global is not the same as ambient
A secret can be marked global, meaning it is available without any scope file naming it:
synapse secret global tutorial.demo on synapse secret list tutorialtutorial.demo is now globaltutorial.demo SYNAPSE_TUTORIAL_TOKEN globalNow move to a folder with no
.synapse.yamlat all and check:cd ~ && synapse status .Folder: . Available: SYNAPSE_TUTORIAL_TOKEN Ambient: inactiveRead those two lines together, because the difference between them is a deliberate safety property. The name is available —
synapse runhere would provide it. But ambient is inactive: a shell hook will not load it, because an ambient environment never activates from a global mapping alone. Turning on a global secret cannot silently populate every shell you open.synapse secret global tutorial.demo off -
Check what a connected tool can actually see
Go back to the tutorial folder and ask a connected coding tool to call
vaultstatus. It reports the variable names available here, the trust state of each scope, and whether ambient mode is ready — and no values, because that tool has no code path that reads one.The same is true of a preview.
synapse launch claude --printshowsDEMO_TOKEN=<from the vault>rather than a redacted value, because a preview calls a different function that lists names and never opens the vault at all. There is no value in the process to leak.
Clean up
synapse deny
synapse secret forget tutorial.demo
synapse vault delete tutorial
cd .. && rm -rf synapsetutorial
Forgot tutorial.demo
Deleted vault tutorial
The next prompt unloads the ambient value. Forgetting the secret removes the stored value and Synapse's record of it, from both stores rather than only the one in use — so a machine that has switched backends leaves nothing behind in the one it stopped reading.
What you can rely on
- Secret values never reach
brain.db, YAML, an MCP response, or a log, and are never at rest in plaintext. - Secrets are never accepted as command arguments — only from a TTY prompt or stdin.
- An ambient shell never activates from a global mapping alone, nor from an unapproved, changed, or incomplete scope.
- Unloading restores values that existed before activation rather than unsetting them.
- Any scope warning refuses
runandlaunchoutright rather than proceeding with part of the environment.
Next step
Continue with Keep one skill library across every tool to finish the daily driver level.