Skip to content

SOPs

Handling Secrets

Real credentials never go into the Claude Code chat. They go straight into secrets.json via VS Code, with a dummy value as a placeholder.

If a live key enters chat, it travels through Anthropic’s API and lives in the local session transcript forever. The dummy-then-edit flow keeps the key on disk only.

When adding a new secret, the path is:

  1. Add a dummy entry via the CLI (so the env file syncs and the shape is in place):
    Terminal window
    python3 ~/apps/cc/secrets/secrets_cli.py add STRIPE_DDXWEB_SECRET_KEY "REPLACE_ME_rk_live_xxx"
  2. Open the JSON in VS Code:
    Terminal window
    open -a "Visual Studio Code" ~/apps/cc/secrets/secrets.json
  3. Paste the real value over the dummy. Save.
  4. Sync to regenerate shared-secrets.env:
    Terminal window
    python3 ~/apps/cc/secrets/secrets_cli.py sync

Claude never sees the real value. The file is already in a path excluded from git.

Splitting secrets into per-repo files sounds safer but usually is not. Shared credentials (Cloudflare, GHL, Google OAuth) get used by 5+ repos, so splitting just duplicates them or forces a lookup layer.

The higher-leverage move is scoping keys tightly when you create them:

  • Stripe: use restricted keys. The “Authorizing an AI agent” flow in Stripe’s dashboard creates a key with only the permissions the agent needs (e.g., Products: Write, Prices: Write, Payment Links: Write, everything else blocked). Prefer this over the full secret key whenever possible.
  • Cloudflare: use API Tokens scoped to one zone or one permission (Pages: Edit, DNS: Edit). Never the Global API Key for new work. Global API Key is full account access and should be treated as nuclear — kept only for scripts that genuinely need it, and rotated on any suspicion of exposure.
  • Google: per-app OAuth tokens, not shared. One stolen token equals one app compromised, not everything.
  • Source of truth: ~/apps/cc/secrets/secrets.json
    • Structured JSON with per-entry metadata (category, description, source URL, created date)
    • Managed via secrets_cli.py
  • Legacy mirror: ~/apps/cc/secrets/shared-secrets.env
    • Regenerated on every secrets_cli.py sync or add
    • Never hand-edit — the change will be lost on the next sync
  • Neither file is committed to git. Both are gitignored in the cc repo and distributed across machines via secrets-sync.sh (scp).

If a real key ends up in chat, a git commit, or a public log:

  1. Rotate it immediately in the provider’s dashboard (Stripe, Cloudflare, etc.).
  2. Update the secret via secrets_cli.py add with the new value.
  3. Do not try to scrub the transcript. Assume the leaked value is compromised and move on. Rotation is the only real fix.