Secrets management for trading bots
Keep private keys out of git with secrets.json, how to use env vars vs files, and what to do if a key leaks.
The risk is real
A private key exposed on GitHub can be drained within seconds. Automated bots scrape public repositories for private keys and seed phrases. If you have ever committed a key, assume it is compromised, rotate immediately, and check your transaction history.
Trading bots are especially sensitive because:
- The private key signs on-chain transactions (redeemer, polycopy, all bots placing orders).
- The API keys control the CLOB -- an attacker can cancel all your orders or place adversarial ones.
- USDC on Polygon has real value and can be swept in one transaction.
The secrets.json pattern
All our bots load credentials from a secrets.json file that is explicitly git-ignored:
{
"privateKey": "0xabc123...",
"polyApiKey": "your-clob-api-key",
"polyApiSecret": "your-clob-api-secret",
"polyApiPassphrase": "your-passphrase",
"telegramBotToken": "123456:ABC...",
"telegramChatId": "-100123456"
}Load it at startup:
const secrets = JSON.parse(fs.readFileSync('./secrets.json', 'utf8'))
const wallet = new ethers.Wallet(secrets.privateKey, provider)The .gitignore entry
Your .gitignore must contain:
secrets.json
*.secret
.env.local
Verify it is actually ignored:
git check-ignore -v secrets.json
# Should output: .gitignore:1:secrets.json secrets.jsonIf it outputs nothing, the file is not ignored. Running git status showing secrets.json as an untracked file is normal and safe. Seeing it as modified or added means it is tracked -- fix that immediately.
Removing a secret that was accidentally committed
If you committed secrets.json:
# Remove from tracking but keep the file locally
git rm --cached secrets.json
git commit -m "Remove secrets.json from tracking"
# Rewrite history to purge it from all commits (requires force push)
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch secrets.json' \
--prune-empty --tag-name-filter cat -- --all
git push origin --force --allThen immediately rotate every credential in the file -- treat all of them as compromised even if no one cloned the repo.
Environment variables vs file
Environment variables are the other common pattern: no file on disk, credentials live in the shell environment or a process manager config.
const privateKey = process.env.PRIVATE_KEY
const apiKey = process.env.POLY_API_KEYSet them in your shell for local dev:
export PRIVATE_KEY="0xabc123..."
export POLY_API_KEY="your-key"Or in a .env file loaded via dotenv (also git-ignored):
require('dotenv').config()
const privateKey = process.env.PRIVATE_KEYThe trade-off: env vars are easier to rotate on a server (change and restart, no file to manage), but secrets.json is simpler for local dev and more portable. We use secrets.json because it supports multiple bots from the same file without environment variable naming conflicts.
If a key leaks -- immediate response
- Rotate the private key immediately: generate a new wallet, transfer all USDC to it, blacklist the old wallet address in any API configurations.
- Revoke CLOB API keys: log into Polymarket and regenerate your API key pair. Old keys become invalid.
- Rotate Telegram token: message
@BotFather, use/revoke, generate a new token. - Check transaction history: look at the old wallet on Polygonscan for any unauthorized transactions.
- Search GitHub for exposure: use
git log --all -S"<key_fragment>"to find every commit that contained the key.
Using a dedicated trading wallet
Never use your main wallet for bot trading. Create a dedicated wallet with only the USDC and MATIC you are willing to lose. This limits the blast radius if a key leaks.
// Generate a fresh wallet
const { ethers } = require('ethers')
const wallet = ethers.Wallet.createRandom()
console.log('Address:', wallet.address)
console.log('Private key:', wallet.privateKey)
// Save the private key to secrets.json, fund the address, doneKeep the minimum required on the trading wallet:
- USDC: only what the bots actively need (e.g. total across all bot caps).
- MATIC: 0.5-1 MATIC for gas (top up when it drops below 0.1).
Backup and recovery
Store a backup of secrets.json in a password manager (1Password, Bitwarden), not in cloud storage or another git repo. Test the restoration procedure by loading the file on a second machine before you need it.
Summary
- Put all credentials in
secrets.json, verify it is in.gitignore. - Never use your main wallet -- use a dedicated trading wallet.
- If a key leaks: rotate immediately, check Polygonscan, revoke all associated API keys.
- Keep only the minimum USDC balance the bots need on the trading wallet.
- Back up
secrets.jsonin a password manager, not a cloud drive or git repo.
Related bots
redeemer — On-chain position redeemer
Claims resolved Polymarket positions on-chain via the Safe relayer. Supports standard CTF and neg-risk adapter. Dry-run by default, --live to execute, batches of 20. Free in every bundle.
polycopy — Whale copy-trading bot
Mirrors trades from profitable Polymarket wallets within seconds. Per-whale WR gate ≥55% after 10 trades, 24h stale exit, $0.10 best-bid collapse exit. Adaptive 3s polling after whale activity.