Secrets Layout¶
Where each credential lives. Use this when "where's the X key" comes up
and you need to know whether to look in ~/.zshrc, a wrangler secret,
a config file, or somewhere else.
Conventions¶
~/.zshrcexports → all interactive shells + cron jobs (sourced viaeval "$(grep -E '^export NAME=' ~/.zshrc)"early in scripts)wrangler secret put NAME→ Worker-only, encrypted at rest by CF- App-specific config files → app-only access patterns
Do NOT commit any of these to git. (.gitignore should exclude
.zshrc, ~/.GarminDb/, ~/.cloudflared/*.json, ~/.config/rclone/.)
~/.zshrc exports¶
| Var | Used by | Notes |
|---|---|---|
INTERVALS_ICU_KEY |
warehouse.py refresh-icu, scanners/comment_scan.py, scripts/cache_for_worker.py |
API key for athlete i153321. Regenerate at intervals.icu/settings#api if 401s appear. |
ANTHROPIC_API_KEY |
comment_scan.py, data-ingestion/research.py (when not Parallel API), Modal jobs, Worker (also as Worker secret) |
Standard Anthropic API key |
VOYAGE_API_KEY |
kb/embed.py |
Voyage embeddings, free tier 50M tokens/mo |
R2_ACCESS_KEY_ID |
daily_sync.sh, rclone |
R2 S3-compatible cred |
R2_SECRET_ACCESS_KEY |
same | |
R2_ENDPOINT |
same | account-specific URL like https://a20a70bee90d635ffad79328f3edcd5f.r2.cloudflarestorage.com |
TELEGRAM_BOT_TOKEN |
evening_checkin.py, telegram_listener.py (deprecated), Worker (also as Worker secret) |
Bot @otq_moonbot |
TELEGRAM_CHAT_ID |
same | 8196786680 |
RESEND_API_KEY |
notify.py |
Email via Resend |
Cloudflare Worker secrets¶
Set via wrangler secret put NAME from the Worker project dir. Stored
encrypted; not visible after setting.
For otq-checkin Worker:
- INTERVALS_ICU_KEY
- TELEGRAM_BOT_TOKEN
- ANTHROPIC_API_KEY
Inspect via dashboard (Workers → otq-checkin → Settings → Variables) — names visible, values not.
App-specific config¶
| File | Contents |
|---|---|
~/.GarminDb/GarminConnectConfig.json |
Garmin SSO username/password + garth_session token (auto-rotated) |
~/.cloudflared/cert.pem |
OAuth-flow cert for cloudflared tunnel login |
~/.cloudflared/<TUNNEL_ID>.json |
Tunnel-specific credentials |
~/.config/rclone/rclone.conf |
rclone [r2] remote config (uses R2_* env vars by default; see file for layout) |
~/.modal/config.toml |
Modal API token |
Cloudflare Tunnel¶
Tunnel auth is via cert.pem from cloudflared tunnel login (OAuth),
NOT API token. Two pieces:
~/.cloudflared/cert.pem— global account cert~/.cloudflared/<TUNNEL_ID>.json— per-tunnel JWT
config.yml references the tunnel by ID; cloudflared finds the JSON
by ID.
Where things commonly leak¶
- Don't paste secrets into Claude Code conversation. Even debug
output. Worker secrets can't be re-shown after
wrangler secret put, but ENV-loaded ones can leak from shell traces. ~/.zshrcgets backed up by Time Machine. Encrypt the backup destination if this matters.- Log files:
daily_sync.logand the Worker's CF dashboard logs may capture errors that include URL params with secrets. The?secret=gate in the Worker uses the last 12 chars of the TG token — avoid pasting that URL anywhere public.
When to rotate¶
| Signal | Action |
|---|---|
wrangler deploy 400 on auth |
Re-run wrangler login |
| GarminDB CLI: "garth session expired" | Re-run garmindb_cli.py with --download (prompts for creds, rewrites session) |
| intervals.icu 401 | Regenerate API key in settings, update ~/.zshrc |
| R2 token compromised | Dashboard → R2 → Manage R2 API Tokens → revoke + reissue |
| Telegram bot token leaked | @BotFather → revoke → reissue → update Worker secret + ~/.zshrc |
Related¶
runbooks/daily-sync-failures.md— has runbook for each kind of credential failure