Appearance
Collect Secrets
Get a secret value from a human into a destination (a GitHub repo secret, a Cloudflare Worker secret, or a gitignored .dev.vars file) without leaking it anywhere along the way. Use the collocated collect-secrets.sh tool instead of hand-rolling a paste script per setup.
When to use
Any setup flow that needs a human-held secret to reach a destination: GitHub Actions auth tokens, R2 / GCS keys, Cloudflare Worker secrets, OAuth tokens, database URLs. The problem of handing a secret to automation safely recurs with the same shape every time, so solve it once here and call it from everywhere.
The obvious-but-wrong paths this replaces:
- Pasting into the chat → the value lands in the transcript forever.
- Letting the agent read
~/.config/gcloud/,.env, or shell history → credential-exploration, correctly blocked by the permission classifier. gh secret set --body "$VAL"→ the value goes on the command line, where it leaks intopsoutput and shell history. Always pipe over stdin instead.
The pattern
sh
# GitHub repo secret(s)
./skills/security/collect-secrets.sh \
--destination gh --repo OWNER/REPO NAME [NAME...]
# Cloudflare Worker secret(s)
./skills/security/collect-secrets.sh \
--destination wrangler [--env ENV] NAME [NAME...]
# Gitignored .dev.vars (the file is the intended on-disk destination)
./skills/security/collect-secrets.sh \
--destination dev-vars [--file PATH] NAME [NAME...]For each NAME the tool prompts once with hidden input (read -rs), reads the value from stdin, and pipes it straight to the destination. It then prints a length-only confirmation (set NAME (N chars)) so you can sanity-check a paste without revealing it.
Guarantees
- Hidden input, one secret at a time; empty input re-prompts, end-of-input aborts.
- Never echoed, never placed on a command line (so it stays out of
psand shell history), never written to disk beyond the intended destination. - stdin-piped to the destination: the value reaches
gh/wranglerover stdin, never as an argument. The destination reads its own stdin from the internal pipe, so it never competes with the hidden read. - Length-only confirmation: the only feedback is the character count.
- Idempotent and re-runnable:
gh/wrangleroverwrite the secret;dev-varsreplaces the key in place (no duplicate lines). The.dev.varsfile is written with mode600, and the tool warns if it is not gitignored.
Pluggable destinations
gh: a GitHub repo secret, viagh secret set NAME --repo OWNER/REPOreading the value over stdin.wrangler: a Cloudflare Worker secret, viawrangler secret put NAME(optionally--env ENV) reading the value over stdin.dev-vars: a gitignored.dev.varsfile, withNAME=valueappended or replaced in place at mode600.
Add a destination by writing one function that reads the value from stdin and must not place it on argv.