Stop hunting for API keys. Just ek and they're in your shell.
# Before: copy-pasting from 1Password, managing .env files
op item get "OPENAI_API_KEY" --vault CLI --fields credential | pbcopy
export OPENAI_API_KEY="sk-..."
# After
ek openaiek # interactive fuzzy picker
ek 3 # export by number
ek openai # fuzzy match by name
ek .myapp # export all keys for a project
export-key list # list available keysA Go binary outputs export VAR="value" to stdout. A thin shell function evals it, setting the variable in your current shell. The TUI renders on stderr so it doesn't interfere with the export. Same pattern used by direnv, fnm, and rbenv.
brew install danmartuszewski/tap/export-keygo install github.com/danmartuszewski/export-key/cmd/export-key@latestAdd to your ~/.zshrc (or ~/.bashrc, ~/.config/fish/config.fish):
eval "$(export-key init zsh)"Or if you prefer a minimal one-liner instead:
ek() { eval "$(export-key select "$@")"; }Both define the ek function that wraps export-key select. The init variant stays in sync with binary updates automatically.
Install the export-key agent skill so coding agents (Claude Code, Codex, Cursor, Cline, and ~50 others) know how to load secrets via ek instead of asking you to paste API keys into chat:
npx skills add danmartuszewski/export-keyThis places SKILL.md into your agent's skills directory. Distribution is powered by vercel-labs/skills; the same command works across all supported agents.
Paste the block below into your coding agent. It teaches the agent what export-key is, how to use it, and how to install it — so the agent can load secrets via ek instead of asking you to paste API keys into chat:
I use export-key to manage API keys and secrets. Use it for any task that needs
a secret value — never ask me to paste secrets in chat, and never print a secret
to the terminal.
## What it is
export-key is a CLI that exports a secret from 1Password (or a configured .env
file) as an environment variable in the current shell. The shell function `ek`
wraps it. Source: https://github.com/danmartuszewski/export-key
## How to use it
```bash
ek OPENAI_API_KEY # export a single key by env-var name
ek .myproject # export every key tagged with a project
ek # interactive picker when the name is unknown
export-key list # list available keys without exporting
```
Item titles in the backend follow `ENV_VAR_NAME` or `ENV_VAR_NAME-project[,project2]`.
Everything before the first `-` becomes the env-var name.
## Rules
- Use `ek` BEFORE asking me to paste a value.
- Never `echo`, `printenv`, or otherwise print a secret. To verify presence:
`[ -n "$VAR" ] && echo set || echo missing`.
- `ek` modifies the CURRENT shell. Run dependent commands in the same shell
invocation (chain with `&&` if your tooling spawns a fresh shell per command).
- If `ek` is missing, shell integration isn't loaded — tell me, don't modify
my shell config without asking.
- If a key isn't found, tell me — don't guess at alternative names.
## Install (only if not already installed — check `command -v export-key` first)
1. Binary:
- macOS/Linux with Homebrew: `brew install danmartuszewski/tap/export-key`
- Otherwise: `go install github.com/danmartuszewski/export-key/cmd/export-key@latest`
2. Shell integration: detect my shell from $SHELL, then add
`eval "$(export-key init <shell>)"` to the matching rc file (~/.zshrc,
~/.bashrc, or ~/.config/fish/config.fish). Skip if the line is already there.
Ask me before modifying the file.
3. Agent skill (so this onboarding is loaded automatically next time):
`npx skills add danmartuszewski/export-key`
4. Verify with `export-key version`.
Item titles in 1Password follow this pattern:
ENV_VAR_NAME -> env var: ENV_VAR_NAME, no project
ENV_VAR_NAME-project -> env var: ENV_VAR_NAME, project: project
ENV_VAR_NAME-a,b -> env var: ENV_VAR_NAME, projects: a, b
Everything before the first - becomes the environment variable name. Everything after is a comma-separated list of project tags.
Keys sharing the same project tag belong to the same project. A key can belong to multiple projects:
OPENAI_API_KEY-myapp -> project: myapp
STRIPE_KEY-myapp -> project: myapp
SHARED_KEY-myapp,infra -> projects: myapp, infra
AWS_ACCESS_KEY -> no project
ek .myapp (or ek -p myapp) exports all keys tagged with that project at once. SHARED_KEY above would be included in both ek .myapp and ek .infra.
Common ways secrets get leaked and how export-key handles them:
| Risk | Traditional approach | export-key |
|---|---|---|
| Secrets in dotfiles | export KEY="sk-..." hardcoded in .bashrc or .zshrc |
Keys are fetched on demand, never written to shell config |
| Secrets in shell history | export KEY="sk-..." saved in .zsh_history |
Only ek openai appears in history, never the secret value |
| Secrets in clipboard | Copy-paste from 1Password leaves secrets in clipboard history | Secrets go directly from the vault to your shell, no clipboard involved |
| Secrets committed to git | .env files accidentally checked in |
With 1Password backend, there are no local files to commit |
| Secrets shared across sessions | .env loaded at shell startup persists across all terminals |
Keys live only in the shell session where you exported them |
| Secrets read by AI agents | Coding agents (Claude Code, Codex, Warp AI) can read .env files, shell history, and dotfiles in your project |
No local secret files to read - keys exist only as in-memory env vars in your shell session |
Even with the dotenv backend, export-key is safer than source .env - keys are loaded selectively (only what you pick) and on demand (not at every shell startup).
Coding agents like Claude Code, Codex, and Warp AI read your shell configuration files (.bashrc, .zshrc, .profile) as part of their normal operation. If your API keys are in those files, every agent session has direct access to them.
Even with the dotenv backend, storing secrets in a dedicated .env file that only export-key references is much better than putting them in .zshrc. Your .env can live outside the project directory (e.g. ~/.secrets.env) where agents have no reason to look. No agent knows the file exists or where to find it. You can also block agents from reading it through their configuration (e.g. .claude/settings.json for Claude Code or .codexrc for Codex).
Agents will always read .zshrc because it's a standard file in a known location. A custom .env path that only export-key references through its own config is invisible to them by default.
Uses the op CLI. Make sure you have 1Password CLI installed and signed in.
For users without 1Password. Reads plaintext .env files.
Note: Dotenv files store secrets in plaintext. Use 1Password for better security.
Config file at ~/.config/export-key/config.yaml:
backend: 1password
onepassword:
vault: CLI
field: credential
dotenv:
paths: [.env, ~/.secrets.env]| Variable | Description |
|---|---|
EK_BACKEND |
Backend to use (1password, dotenv) |
EK_VAULT |
1Password vault name |
EK_FIELD |
1Password field name |
EK_DOTENV_PATHS |
Colon-separated dotenv file paths |
export-key/
├── cmd/export-key/ # Main entry point
├── internal/
│ ├── backend/ # Backend interface & implementations (1Password, dotenv)
│ ├── cmd/ # CLI commands (cobra)
│ ├── config/ # Configuration loading
│ ├── keyitem/ # Key naming parser (ENV_VAR-project)
│ ├── shell/ # Shell init script generation
│ └── tui/ # Interactive picker (bubbletea)
├── Makefile
└── README.md
MIT License - see LICENSE for details.