macOS 14 · arm64 · bash 3.2
Keys that survive reboot — without landing in your rc file.
Generates CYCLAW_API_KEY, prompts for Telegram, Claude, Grok, and GitHub (skip any), then stores them the way CyClaw already expects: Keychain first, ~/.CyClaw/.env at mode 600, rc only sources that file.
From a CyClaw checkout
After the pull request lands, the script lives next to the other macos/ glue. Until then, download it here or pull the feat/macos-setup-cyclaw-keys branch. Run it in Terminal — not here.
bash ./macos/setup-cyclaw-keys.shOr download the file above, then:
chmod +x ./setup-cyclaw-keys.sh && bash ./setup-cyclaw-keys.shThis tab cannot see the keys. After it finishes: source ~/.CyClaw/.env. New terminals inherit via the rc source block.
Where secrets go
macOS Keychain
Canonical. launchd chains cyclaw-keychain-env.sh. Secret never on security argv.
~/.CyClaw/.env
chmod 600. Gitignored. What you asked for — and what new shells source.
Checkout .env
Written only if a CyClaw repo is found. Also gitignored.
Shell rc
Sources the home dotenv. Never inlines a value into zshrc or bash_profile.
What it handles
Names match the code, not older docs.
CYCLAW_API_KEY
openssl rand -hex 20. Bearer for /soul/* and /ops/*. Printed once for the console paste.
TELEGRAM_BOT_TOKEN
Skip allowed. Never stored in config.yaml. launchd poll/health wrap Keychain.
ANTHROPIC_API_KEY
Claude. llm/client.py reads this name only — not CLAUDE_API_KEY.
GROK_API_KEY
xAI. dummy is fine offline and required non-empty for pytest. --grok-dummy.
GH_TOKEN
Also writes GITHUB_TOKEN. CyClaw never puts tokens in argv.
Flags
Same style as install-cyclaw.sh.
- --rotate replace existing CYCLAW_API_KEY
- --skip-prompts autogen only
- --grok-dummy offline / pytest
- --no-profile-edit leave rc alone
- --no-print-key do not echo the generated key
- --repo-path PATH write checkout .env
Privacy posture
Follows cyclaw-advisor and the existing macos/ bash contracts.
Secrets never go in config.yaml, launchd plists, or the cyclaw shim.
security is invoked with a bare -w so the value is not on argv / ps.
Log lines name services and variable names only. The generated key is printed once, on purpose, so you can paste it into the Soul console.
Claude is stored as ANTHROPIC_API_KEY because that is what llm/client.py reads.