Secrets in gcontext
The split that makes gcontext safe: the cloud knows secret NAMES (and a description),
your machine holds secret VALUES. Values live in ~/.gcontext/.env (chmod 0600; set
GCONTEXT_SECRET_BACKEND=keychain to use the OS keychain instead) and are injected into
scripts as environment variables at run time. They are never uploaded, never returned by
a tool, and tool output is scrubbed of them as a backstop.
The workflow
- Register the names:
tool_secrets(action="register", name="STRIPE_API_KEY", description="read-only restricted key"). Usually an integration'smodule.yamlsecrets:list names them too, so declare them there when creating an integration. - Fill the values:
tool_setup_secrets()opens a local form in the user's browser where THE USER types the values. The agent never sees them. Values are written to~/.gcontext/.env(chmod 0600), which never leaves the machine. - Verify:
tool_secrets()(defaultaction="list") re-checks locally and returns{all_set, secrets: [{name, description, present}]}. Presence booleans (never values) are reported to the cloud so dashboard badges stay honest. - Use: in scripts,
os.environ["NAME"].tool_run_scriptandtool_run_shellinject the whole secret environment regardless of registration state.
tool_secrets(action="unregister", name=...) removes a name from the registry (the local
value, if any, is untouched).
Rules for agents
- Never ask the user to paste a secret value into the chat; call
tool_setup_secretsinstead. - In scripts: read via
os.environ, never hardcode, never print. The output scrubber replaces any known value with«NAME», but it cannot catch transformed values (base64, sliced); the real boundary is "don't print secrets". - Prefer read-only keys where a service offers them, and say so in the secret's description.
- Names are UPPER_SNAKE_CASE and specific (
STRIPE_API_KEY, notAPI_KEY).
Where things live on the user's machine
| What | Where |
|---|---|
| Secret values + placeholders | ~/.gcontext/.env (chmod 0600) |
Secret values (opt-in: GCONTEXT_SECRET_BACKEND=keychain) | OS keychain, one entry per name |
| Nothing else | The cloud stores names, descriptions, presence booleans |
The keychain backend is opt-in because under uvx the connector runs an ad-hoc-signed
python whose keychain identity changes on every environment rebuild, so macOS prompts
for permission on every read and "Always Allow" never sticks. On first start with the
default backend, any values left in the keychain by an older version are migrated into
the .env automatically and removed from the keychain.