macos-keychain-creds
Skill1 starsChanged 2 months agoMITView on GitHub
General-purpose credential storage and retrieval backed by the macOS Keychain. Use this skill whenever a task needs a secret — an API token, personal access token, API key, password, webhook secret, or client secret — for any service (GitHub, GitLab, OpenAI, Slack, Stripe, an internal API, anything). It stores a secret the user pastes (`pbpaste | store_cred.sh SERVICE`), verifies it is still valid, lists what is stored, hands it to a command through the environment without ever printing it (`use_cred.sh SERVICE -- curl ...`), and deletes it. Use it proactively when a command fails with 401/403/"missing token"/"not authenticated", or when about to ask the user to paste a token into the chat — store it in the Keychain instead. Also use it to set up credentials that other skills or scripts will read later.
- Reads credentials
In this repository2 other files it gives its agents.
- notion-api-macos-keychainSkill
- slide-creatorSkill
The file
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 3 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 44
Never ask the user to type or paste a secret into the chat: Ask them to copy it to the clipboard, then run pbpaste | store_cred.sh.
- Very specific: line 47
Never print a secret: Not with echo, not in a debug line, not in an error message.
- Distinctive: line 58
Ask the user to copy the secret to the clipboard, then:
- Very specific: line 67
Use a short lowercase name.
- Very specific: line 132
Run check_cred.sh before a task that depends on a credential, and after any 401/403.
- Distinctive: line 146
When a task needs a credential:
- Very specific: line 150
If missing or invalid, tell the user exactly where to get the secret (see references/recipes.md for the common ones and the scopes each …
- Very specific: line 153
Do the work through use_cred.sh.
- Distinctive: line 155
If the user pastes a secret into the chat anyway, store it, then tell them plainly that the value is in the transcript and should be …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
macOS Keychain Credentials
Store secrets in the macOS Keychain, then use them without ever seeing them.
The Keychain is the system secret store: entries are encrypted at rest, unlocked with the login password, and readable only by the logged-in user. This skill wraps it so a secret has exactly one safe path in (the user's clipboard) and one safe path out (the environment of a child process).
| Concern | How this skill handles it |
| Where secrets live | macOS Keychain, one generic-password entry per service |
| Path in | stdin only (pbpaste | store_cred.sh) — never argv, never a file |
| Path out | exported into a child process by use_cred.sh — never stdout |
| Which secrets exist | ~/.config/macos-keychain-creds/registry.tsv (non-secret metadata only) |
| Naming | SERVICE is any label the user picks, e.g. github-token |
scripts/keychain.sh and scripts/registry.sh hold the shared helpers, so nothing else has to be
installed.
Throughout, skill_dir means the directory containing this SKILL.md:
skill_dir="<directory containing this SKILL.md>"The rules that matter
- Never ask the user to type or paste a secret into the chat. Ask them to copy it to the
clipboard, then run
pbpaste | store_cred.sh. A secret pasted into the conversation is in the transcript forever. - Never print a secret. Not with
echo, not in a debug line, not in an error message. Useuse_cred.shso the value goes straight into the child process's environment. - Never put a secret in argv.
curl -H "Authorization: Bearer sk-..."puts the token in the process list, where any process on the machine can read it. Reference the exported variable inside single quotes instead, so the calling shell never expands it. - Never write a secret to a file — not
.env, not a config file, not a scratch file, and never into the repository. - Never enable shell tracing (
set -x) around a command holding a secret.
Storing a credential
Ask the user to copy the secret to the clipboard, then:
pbpaste | "$skill_dir/scripts/store_cred.sh" github-token \
--description "GitHub PAT, repo + read:org" \
--validate 'curl -sf -o /dev/null -H "Authorization: Bearer $CRED" https://api.github.com/user'SERVICE(github-tokenhere) is the Keychain service name and the handle used everywhere else. Use a short lowercase name;<service>-tokenor<service>-keyreads well.--validateis a shell command that must succeed before the secret is stored. It runs with the secret exported as$CRED, so a bad token can never overwrite a working one. It is saved in the registry, socheck_cred.shcan re-run it later. Always supply one when the service has a cheap "who am I" endpoint — seereferences/recipes.mdfor ready-made ones.--env-var NAMEoverrides the exported variable name, which otherwise comes from the service name (github-token→GITHUB_TOKEN).--account ACCOUNTstores a second credential for the same service, e.g. a work and a personal account. It defaults to the macOS username.
Re-running the same command replaces the value and keeps the registered metadata: that is how a token is rotated.
Using a credential
use_cred.sh reads the Keychain and execs the command with the secret in its environment, as
both $CRED and the registered variable name:
"$skill_dir/scripts/use_cred.sh" github-token -- \
bash -c 'curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/user' | jq .loginSingle quotes around the inner command are load-bearing. With double quotes, the calling shell
expands $GITHUB_TOKEN before curl starts — putting the secret into argv and into the agent's
transcript.
Define a helper once per shell session for repeated calls against one API:
gh_api() {
"$skill_dir/scripts/use_cred.sh" github-token -- \
bash -c 'curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"$@"' _ "$@"
}
gh_api https://api.github.com/user/repos?per_page=5 | jq -r '.[].full_name'Two credentials at once: nest the calls.
"$skill_dir/scripts/use_cred.sh" github-token -- \
"$skill_dir/scripts/use_cred.sh" openai-key -- ./deploy.shA CLI that reads a secret from stdin can take it straight from the pipe:
"$skill_dir/scripts/get_cred.sh" github-token --unsafe-print | gh auth login --with-tokenget_cred.sh writes the raw secret to stdout, which is why it demands --unsafe-print. Only ever
use it on the left side of a pipe into a program that consumes it. Never run it as a bare command,
never capture it into a shell variable that later gets echoed, and never use it to "check" a value.
Checking and listing
"$skill_dir/scripts/check_cred.sh" github-token # exit 0 valid, 1 missing, 3 present but invalid
"$skill_dir/scripts/list_creds.sh" # every registered credential and its status
"$skill_dir/scripts/list_creds.sh" --validate # also re-runs each validation commandRun check_cred.sh before a task that depends on a credential, and after any 401/403. Exit 3 means
the secret is expired or revoked — ask the user for a fresh one and store it the same way.
Deleting a credential
Deleting is unrecoverable: the secret cannot be read back afterwards. Confirm the exact service with the user first, then:
"$skill_dir/scripts/delete_cred.sh" github-token --yesWorkflow
When a task needs a credential:
list_creds.sh— is something already stored for this service?check_cred.sh SERVICE— is it still valid?- If missing or invalid, tell the user exactly where to get the secret (see
references/recipes.mdfor the common ones and the scopes each needs), ask them to copy it to the clipboard, then runpbpaste | store_cred.sh SERVICE --validate '...'. - Do the work through
use_cred.sh.
If the user pastes a secret into the chat anyway, store it, then tell them plainly that the value is in the transcript and should be rotated.
Troubleshooting
| Symptom | Likely cause | Action |
no Keychain entry for service | Never stored, or stored under a different account | list_creds.sh, then store it |
check_cred.sh exits 3 | Token expired, revoked, or missing a scope | Store a fresh token |
| 401 from an API that just worked | Token rotated on the provider's side | Store a fresh token |
| 403 with a valid token | Token lacks the scope or the resource is not shared | Re-issue with the scopes in references/recipes.md |
multiline secrets are not supported | Clipboard holds a PEM key or a whole config block | Store the key as a file with correct permissions and keep only its passphrase here |
empty secret on stdin | Clipboard is empty or held an image | Ask the user to copy the secret again |
did not store the expected value | The secret contains characters the Keychain CLI mangles | Report it; do not retry with the secret in argv |
Registry shows a credential as missing | Keychain entry deleted outside this skill | Store it again, or delete_cred.sh --yes to drop the stale row |
Registry
~/.config/macos-keychain-creds/registry.tsv (override with MACOS_KEYCHAIN_CREDS_HOME or
MACOS_KEYCHAIN_CREDS_REGISTRY) records service, account, env var, description, and validation
command — no secrets. It exists because the Keychain cannot be listed by prefix without an
interactive security dump-keychain prompt. It is safe to read, and safe to back up.
References
references/recipes.md— copy-paste setup and usage for GitHub, GitLab, OpenAI, Anthropic, Slack, Stripe, npm, Docker registries, and generic Bearer/Basic/header APIs.references/agent-integration.md— reading these credentials from another skill, a script, or a hook, and choosing betweenuse_cred.shandget_cred.sh.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

