Skip to main content

Encrypted, namespaced secret store for AI coding agents

Project description

cubby

Encrypted, namespaced secret store for AI coding agents — secrets reach the command, never the agent's context.

License: MIT Python 3.11+ Dependencies: zero


AI coding agents are great at running commands — but everything they read from a command's output lands in their context and transcripts. Paste a database password into a prompt once and it's logged forever.

cubby fixes that. Secrets live encrypted on disk (via age). The agent never reads a value — it runs cubby run -- <command>, and cubby injects the secrets straight into the command's environment. The agent sees the command's result, never the secret itself.

How it works

flowchart LR
    agent["AI agent"]
    cubby["cubby run"]
    store[("encrypted<br/>namespace")]
    child["child process<br/>(psql, curl, …)"]

    agent -- "cubby run -- &lt;cmd&gt;" --> cubby
    store -- "decrypt in memory" --> cubby
    cubby -- "secrets as env vars" --> child
    child -- "output only" --> agent

The decrypted value exists only in the environment of the child process. It is never printed, never written to disk in cleartext, and never enters the agent's context.

Install

brew install age          # required: age + age-keygen (Linux: github.com/FiloSottile/age/releases)
git clone https://github.com/perlyer/cubby.git && cd cubby && ./install.sh

install.sh puts cubby on your PATH, runs cubby init, and offers to install the integration into any AI coding agents it finds. Non-interactive:

./install.sh --key-mode keychain --agent claude-code,codex

--key-mode keychain (macOS) stores the age key in the login Keychain so it unlocks automatically at login; the default --key-mode file keeps it in ~/.config/cubby/identity (mode 0600).

Install from PyPI

pipx install cubby-secrets

This puts the cubby command on your PATH (the distribution is named cubby-secrets because cubby was already taken on PyPI; the command itself is cubby). It does not set up the AI-agent integration — run cubby agent add <agent> for that, or use the git clone + ./install.sh flow above, which does it for you.

Updating

./update.sh

update.sh checks out the latest release tag in your clone, re-links cubby on your PATH, and refreshes the agent integrations. It aborts if the working tree has uncommitted changes. Your secret store in ~/.config/cubby/ is never touched.

Usage

Command What it does
cubby init First-run setup — generates the age key, creates the config
cubby set <name> Store a secret (hidden prompt, or --stdin); --env VAR sets its env var
cubby get <name> Show metadata — --reveal prints plaintext, --copy copies it (humans only)
cubby list List secret names in the namespace
cubby rm <name> Delete a secret
cubby rename <old> <new> Rename a secret
cubby cp <name> <ns> Copy a secret to another namespace
cubby mv <name> <ns> Move a secret to another namespace
cubby rotate <name> Replace a secret's value (tracks a rotation count)
cubby ttl [<name> [<dur>]] Show or change a secret's expiry
cubby run -- <cmd> Run a command with the namespace's secrets in its environment (--only/--except to scope)
cubby import <type> <src> Bulk import — dotenv, aws, json, 1password, ns
cubby map Show or change the environment variable each secret is injected as
cubby ns add|list|rm|use|rename Manage namespaces
cubby agent add|list|rm|refresh Manage AI-agent integrations
cubby doctor Check the install, key, config and namespaces for problems
cubby completion <shell> Print a shell completion script (bash/zsh/fish)
cubby audit Show or manage the opt-in audit log
cubby export <file> Write a passphrase-encrypted backup of the whole store
cubby restore <file> Restore the store from a backup bundle

A typical session:

$ cubby set db-password
value for 'db-password': 
cubby: secret 'db-password' set in namespace 'work'

$ cubby get db-password
name:      db-password
namespace: work
env var:   DB_PASSWORD (default)
length:    18
updated:   2026-05-17T09:14:02+00:00
expires:   never
rotated:   never

$ cubby run -- psql -h 127.0.0.1 -U appuser -d appdb
psql (16.2)
appdb=>

cubby get never prints the value without --reveal; cubby set reads it from a hidden prompt, so it never lands in shell history or argv.

Namespaces

A namespace is a workspace or environment (work, personal, …). The active namespace is resolved per command: -n <name> flag → $CUBBY_NS → working-directory prefix match → default.

cubby ns add work --cwd-prefix ~/projects/work
cubby ns                  # show the active namespace and why it was chosen

Each namespace is a separate encrypted file. Under cubby run every secret is injected as an environment variable — by default the UPPER_SNAKE of its name (db-passwordDB_PASSWORD). To inject it under a different name, use cubby set <name> --env VAR or cubby map <name> VAR (e.g. db-passwordPGPASSWORD); cubby map with no arguments lists the current mapping.

A secret can carry an optional expiry. cubby set <name> --ttl 30d stores it with a 30-day TTL; cubby ttl <name> 90d changes the expiry later without re-entering the value, and cubby ttl <name> none clears it. An expired secret is never deleted — cubby run still injects it, with a warning, and cubby doctor flags it. cubby rotate <name> replaces a secret's value and, if it had a TTL, gives it a fresh one.

Agent integration

cubby agent installs a small integration — a skill or instructions file, plus a permissions allowlist where the agent supports one — so the agent reaches for cubby run instead of reading secrets in plaintext.

cubby agent list                 # adapters and their status
cubby agent add claude-code      # install the integration for one agent
cubby agent rm claude-code       # remove it

Supported agents: claude-code, codex, gemini, cursor, copilot.

Claude Code users can alternatively use the native plugin marketplace:

/plugin marketplace add perlyer/cubby
/plugin install cubby

The integration is a convention — it asks the agent to use cubby run. For an enforced guardrail, also block cubby get --reveal and cubby get --copy in your agent's permission system; see Hardening for AI agents.

Security

cubby keeps secrets encrypted at rest and out of an agent's context — but it is a guardrail, not a sandbox. It does not protect against a compromised machine or an agent that runs arbitrary code. Read SECURITY.md for the full threat model and how to report a vulnerability.

Audit log

cubby can keep a local log of every time a secret value leaves the store — a cubby run, a cubby get --reveal, or a cubby get --copy. It is off by default; turn it on with cubby audit --enable. The log records a timestamp, the event, the namespace, and (for run) the command — never a secret value. cubby audit shows it, cubby audit --clear erases it. It lives at ~/.config/cubby/audit.log.

The log self-rotates at ~1 MB: when it reaches that size the current file is moved to audit.log.1 and a fresh one is started, so it never grows without bound. cubby audit --all shows both the current log and the rotated history.

Backup

Losing the age identity makes every namespace unrecoverable, so back the store up. cubby export backup.age writes a single passphrase-encrypted bundle containing the identity, config, and every namespace; age prompts for the passphrase. Because the bundle is passphrase-encrypted it is safe to store off-machine.

cubby restore backup.age rebuilds the store from a bundle on a fresh machine. It refuses to overwrite an existing store unless you pass --force. The restored store always uses file key-mode — if the bundle was created in keychain mode, cubby restore reports this so you can re-migrate to the Keychain afterwards.

Shell completion

cubby completion bash (or zsh / fish) prints a completion script for cubby's commands. For bash, add eval "$(cubby completion bash)" to ~/.bashrc; for zsh, add eval "$(cubby completion zsh)" to ~/.zshrc; for fish, cubby completion fish > ~/.config/fish/completions/cubby.fish.

Contributing

Contributions welcome — including new agent adapters (one file each). See CONTRIBUTING.md.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cubby_secrets-0.7.0.tar.gz (50.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cubby_secrets-0.7.0-py3-none-any.whl (32.3 kB view details)

Uploaded Python 3

File details

Details for the file cubby_secrets-0.7.0.tar.gz.

File metadata

  • Download URL: cubby_secrets-0.7.0.tar.gz
  • Upload date:
  • Size: 50.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for cubby_secrets-0.7.0.tar.gz
Algorithm Hash digest
SHA256 2233fe2d97ab2381c18bc9db23b5300eeb138791194a8f124cc7938cdbf92010
MD5 879e0c85f49e97410d978079558036d8
BLAKE2b-256 744303e54d65b3736b117874f5b844a2a6e28454273ca5c8951f71c7a99310be

See more details on using hashes here.

File details

Details for the file cubby_secrets-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: cubby_secrets-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 32.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for cubby_secrets-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f42d988cc6f404ba51f46a71e58db3784f43113693cd11414b20c70960e80356
MD5 7ecdacee9aeab245fc1a01a90335b30f
BLAKE2b-256 568fa7a4f9350afc9a7e6fd5636cdbaea7ad02a2bc8c16c9af6ce760a4866fca

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page