AuditsControls › Secrets handling

Security · control 15 of 21

Secrets handling: does your trading bot have it?

Do API keys come from environment variables or a secret store, never from the code or versioned files, and do they stay out of the logs?

Why it matters

A key in a config file pushed to GitHub is stolen within minutes.

How to check it in the code

  1. Search for api_key, secret, os.environ, dotenv, .env.example.
  2. Check the repository has no committed config with real-looking keys and that .gitignore covers the secrets file.
  3. Check the logger masks keys and signed URLs.

This is what the audit does for every bot in the list, on public code, without installing or running anything. Verdict values: present, partial, absent, not applicable, not verified. See the method.

Results across audited bots

Across 10 audited bots: 6 present, 1 partial, 1 absent, 0 not applicable, 2 not verified.

BotVerdictEvidenceNote
HKUDS/Vibe-TradingPresentagent/src/trading/onboarding.py to_dict(): "secret_storage": "os_keyring" if self.credential_fields else None; agent/.env.example is a checked-in template of variable names, not real secret valuesCredentials are declared to be stored via the OS keyring, not in code or plain config files.
hummingbot/hummingbotPresenthummingbot/client/config/security.py, class Security (login/decrypt_all/api_keys); README.md: "hbot connect binance # store API keys (encrypted)"API keys are stored encrypted under conf/ using a master password (config_crypt.PASSWORD_VERIFICATION_PATH) and decrypted only in memory.
Drakkar-Software/OctoBotNot verifiedExchangeCredentialsData is an in-memory dataclass only; the on-disk storage/encryption mechanism for saved exchange credentials (profile config) was not located within the time budget.Not confirmed whether keys are stored in plaintext config, an OS keychain, or encrypted at rest.
freqtrade/freqtradeNot verified
jesse-ai/jessePartialjesse/services/auth.py (dashboard password comes from ENV_VALUES['PASSWORD'], i.e. environment — good) BUT jesse/models/ExchangeApiKeys.py stores 'api_key' and 'api_secret' as plain peewee CharField columns in the local database with no visible encryption wrapper, and jesse/models/AiModel.py stores an LLM provider 'api_key' the same wayExchange and AI-provider secrets are kept in the local SQLite/DB file in plaintext columns, not only in env vars; a copy of that DB file exposes all configured exchange keys.
chrisleekr/binance-trading-botAbsentSECURITY.md, verbatim: "Secrets are stored unencrypted. Binance API keys and notifier credentials live in plaintext in Postgres. There is no encryption at rest."This is the project's own documented, deliberate design choice, explicitly marked "out of scope" for security fixes. Infra-level secrets (AUTH_SECRET, Postgres/Redis passwords) are handled well — generated with openssl and delivered via Docker secret files or .env, never hardcoded — but the actual trading credential, the Binance API key, has no encryption at rest: anyone with DB access, a DB backup, or host filesystem access has it.
alsk1992/CloddsBotPresentdocs/SECURITY_AUDIT.md section 4: 'No hardcoded secrets - All from environment'; .env.example centralizes every credential as an env var; section 2.12 documents credentials/escrow keypairs encrypted at rest with AES-256-GCM via CLODDS_CREDENTIAL_KEY / CLODDS_ESCROW_KEY (generated with `openssl rand -hex 32`).Secrets management follows standard practice: env-sourced, encrypted at rest, not committed.
Lumiwealth/lumibotPresent.secrets/lumi_secrets.env.example and .env.local.example (raw.githubusercontent.com, read 2026-09-17) contain only placeholders and the header 'THIS IS AN EXAMPLE FILE, DO NOT ADD YOUR KEYS HERE, THEY WILL BE CHECKED IN TO GIT... This file is not stored in GitHub'. AGENTS.md and SECURITY.md both state the repo uses 'GitHub secret scanning with push protection plus a lightweight public-hygiene CI check for instruction files'.
YizhiSong/FriesTraderPresentFull repository tree (git/trees/main, 15 entries: README.md, LICENSE, PHASE_A_TASK.md, PHASE_B_TASK.md, risk_rules.json, trade_log_template.jsonl, scripts/*.py x7) contains zero API keys, tokens, or .env files; all 7 scripts take only CLI arguments, no credential handling.risk_rules.json's account_number field is an account identifier, not a secret. Auth lives entirely in the user's own Claude Code MCP connector config, never committed here.
c9s/bbgoPresent.env.local.example (BINANCE_API_KEY/SECRET, MAX_API_KEY/SECRET, SLACK_TOKEN, DB_DSN); pkg/bbgo/environment.go (os.LookupEnv for DB credentials)Exchange, database and Slack credentials are read from environment variables, not hardcoded in source.

If your bot does not have it

The watchdog enforces daily loss, drawdown, position size and heartbeat limits from outside the bot, with a read-only key, whatever the bot's own code does.