Repository navigation
Process Secrets
If your setup for working on Volley! includes personal API keys on your dev machine, this doc collects some hygiene habits that keep them out of world-readable files and out of the repo. It is aimed at open-source contributors, so the advice is optional where it needs to be: a contributor touching only Godot scenes can skip most of it, and a contributor wiring up third-party tooling can cherry-pick the parts that match their setup.
The rule in one line: a secret belongs in a manager that prompts for unlock, not in a file that any process running as your user can read.
Concretely, the sort of credential that benefits from this treatment is any long-lived token that calls a third-party service on your behalf. Examples a contributor might have lying around (none of these are required to work on Volley!):
- API keys for any AI or LLM provider you use while coding.
- API keys for project-management services (for example: Linear, Jira, GitHub tokens used outside
gh auth). - Publishing or deployment tokens (for example:
butlerfor itch.io, registry credentials). - MCP server credentials, if you run an editor or agent that speaks MCP.
Tokens that already live inside a managed store, like gh auth or op signin, are already covered. This doc is about the ones that tend to drift into plaintext config files instead.
The one habit worth picking up regardless of toolchain is making sure home-directory files that hold credentials are not world-readable. Many tools ship config files at mode 644 and then invite you to paste a key into them.
# for any file in your home directory that holds a plaintext credential
chmod 600 <path>A sweep that surfaces candidates:
# audit world-readable files under ~/.config and in the top of $HOME
find ~/.config -type f -perm /044 -print
find ~ -maxdepth 2 -type f -name '.*' -perm /044 -printCommon suspects: ~/.netrc, ~/.pypirc, ~/.npmrc, ~/.config/gh/hosts.yml, per-tool *.yml, *.toml, *.env, and any editor or agent settings that accept an env block.
If a file needs to stay world-readable (a checked-in sample config, a public key), it should not contain a secret in the first place. Move the secret out, then relax the mode.
Two general-purpose options. Either is fine; pick the one that fits your workflow.
pass encrypts each secret as a .gpg file under ~/.password-store/. Unlock with the gpg agent, read with a single command.
# one-time setup
sudo pacman -S pass # or: brew install pass
gpg --full-generate-key # RSA 4096 or ed25519
pass init <your-gpg-key-id>
# store a secret
pass insert example/some-api-key
# export for a single shell session
export SOME_API_KEY="$(pass show example/some-api-key)"op pulls from the 1Password vault using biometric or account unlock. Works well when the same vault is shared across desktop, mobile, and browser.
# one-time setup
sudo pacman -S 1password-cli # or: brew install 1password-cli
op signin
# store: add the item via the 1Password app, then reference it by path
export SOME_API_KEY="$(op read op://Private/Example/api-key)"Either way, the pattern is the same: the plaintext key never lands in a shell rc file or a config JSON. It lives in the manager, gets read into a variable per-session, and evaporates when the shell exits.
Rather than exporting every key in ~/.zshrc or ~/.bashrc (which fires an unlock prompt on every new shell), keep an opt-in script that you source when you start a session that actually needs the keys.
# ~/bin/dev-env (or anywhere on PATH), not checked in
export SOME_API_KEY="$(pass show example/some-api-key)"
export OTHER_API_KEY="$(pass show example/other-api-key)"Then:
source ~/bin/dev-env # one GPG prompt, once, when you actually want the keys
# ...launch your editor or tool hereOne unlock per work session, zero keys in rc files.
- Do not commit keys to the repo.
gitleaksruns in CI; local habits should match. - Do not paste secrets into shell history. Use the manager's read command, or a here-doc.
- Do not share the home directory (
scp -r ~, cloud sync of$HOME, terminal screenshots) without auditing which files hold secrets first. - If a key has ever sat in a world-readable file, treat it as compromised: revoke at the provider, issue a new one, store it in the manager, re-export in the shell, and restart any long-lived process that cached the old value.
Skip this section if you do not use Claude Code; the rest of the doc stands on its own.
Claude Code reads MCP environment variables from ~/.claude/settings.json. That file ships at mode 644, and MCP env blocks are a natural place for keys to accumulate, so it is worth both tightening the mode and keeping the key values out of the file.
chmod 600 ~/.claude/settings.json
chmod 600 ~/.claude/settings.local.json # if presentWith the dev-env pattern above in place, the settings file becomes a passthrough rather than a secret store:
{
"env": {
"SOME_API_KEY": "${SOME_API_KEY}",
"OTHER_API_KEY": "${OTHER_API_KEY}"
}
}Launch Claude from a shell that has sourced dev-env, and the values resolve from the environment. The settings file can still be chmod 600 as defence in depth, but it no longer holds the secret.
Contributing
The game
The Cast
Process
Reference