Security & Threat Model
A trading bot platform holds the keys that move your money. We take that seriously. This page describes what we encrypt, what we don't see, what attacks we defend against, and how to disclose a vulnerability.
Sections
1. Core security principles
Never take custody of funds.
We never hold, route, or have visibility into your trading capital. Your API keys talk directly to the exchange you choose; there is no WatchDog Bot wallet or escrow layer in the path. If we got fully compromised tomorrow, an attacker cannot steal a single dollar of your money — only your keys.
Minimize what we store.
We collect the smallest amount of data needed to run the service. API keys are encrypted. Code and logs are stored only for users who opted into cloud sync. Telemetry is opt-out. No third-party tracking.
Keys never appear in bot source.
Bots access credentials via wd.connection(name) at runtime — the SDK retrieves keys from the encrypted local store. Source code never has the secret in it, which means it can't accidentally end up in logs, in version control, or in a screenshot you share with us for debugging.
Reduce blast radius per bot.
Each bot runs in its own isolated Python venv. A malicious package in one bot's dependencies can't corrupt other bots or escape into the host shell. Each bot also has its own UUID; logs and state are scoped to that UUID end-to-end.
2. How we handle API keys
When you add an exchange API key in Settings → API Connections:
- The key + secret are encrypted in your local SQLite database using AES-256-GCM with a per-account key derived from your password (Argon2id KDF).
- If you've opted into cloud sync, an encrypted copy syncs to Supabase. Supabase sees only the ciphertext; the decryption key never leaves your local app instance.
- At runtime, your bot calls
wd.connection("Kalshi"). The platform decrypts the key in-memory, passes it into the bot's process environment, and the bot uses it to authenticate with the exchange. - The key is never logged. The platform's logging layer pattern-matches on common API key formats and redacts them before any log line is written.
- If you call
repr(conn)by mistake, theConnectionobject returns a masked representation (e.g.,Connection(name='Kalshi', api_key='ABC***...', ...)) — secrets are not exposed even by accident.
What we recommend you do at the exchange side:
- Always disable withdrawal permissions on any API key used by a bot. The bot needs trade permission; it does not need to move funds. See the exchange setup guide for the exact toggle on each venue.
- Use IP-restricted keys where the exchange supports it. Pin the key to your home/VPS IP — a stolen key from an unfamiliar IP gets rejected.
- Rotate keys every 90 days. Most exchanges auto-expire keys; treat the expiry as a reminder to rotate proactively.
- Use a sub-account or dedicated trading account. Don't use the same API key for manual trades and bot trades — separate audit trails make incident response much easier.
3. Encryption
| Asset | In transit | At rest |
|---|---|---|
| Account credentials | TLS 1.3 | Argon2id password hash; never plaintext |
| API keys | TLS 1.3 | AES-256-GCM, per-account derived key |
| Bot source code | TLS 1.3 | Encrypted at rest in Supabase (cloud sync only) |
| Bot logs | TLS 1.3 | Encrypted at rest in Supabase |
| Stripe billing data | TLS 1.3 (Stripe) | PCI-DSS compliant at Stripe; we never touch full card numbers |
| Backups | — | Encrypted database snapshots, 30-day retention |
4. Threat model
The threats we explicitly defend against:
| Threat | Defense |
|---|---|
| Attacker gains access to our database | API keys are AES-encrypted with a key derived from your password. Without your password, the ciphertext is useless. |
| Attacker intercepts API traffic | TLS 1.3 with certificate pinning on the desktop client. |
| Malicious bot tries to exfiltrate other bots' data | Each bot runs in an isolated venv with restricted filesystem scope (cannot read other bots' state directories). |
| Malicious bot tries to escape the venv | The runtime doesn't run user code with elevated privileges. The OS sandbox (Windows AppContainer / macOS sandbox) is the final boundary. |
| Brute force on user password | Argon2id with high cost parameters; rate-limited login attempts; mandatory 2FA on accounts that opt in. |
| SQL injection / XSS in dashboard | Parameterized queries throughout; React's automatic JSX escaping; CSP headers on dashboard pages. |
| Supply-chain attack via auto-installed deps | Auto-install denylist for high-risk packages (setuptools, pip itself, etc.); package names sanity-checked before uv pip install; user-visible log when any new package is installed. |
| API key leaked via log files | Logger pattern-matches and redacts common key formats before write. |
Threats we explicitly do not defend against (out of scope):
- Compromise of your local machine. If an attacker has root access to your laptop, they can read the decrypted keys in memory. Defense: keep your machine secure (FileVault/BitLocker, OS updates, no random binaries).
- Compromise of the exchange. If Binance gets hacked, we can't help. Defense: don't keep more on any exchange than you're willing to lose.
- Phishing. If you give your password to a fake "WatchDog Bot login" page, that's outside our scope. Defense: always verify the URL is exactly
watchdogbot.cloud. - Social engineering of our support. We do not change account passwords via email or chat. Account recovery is automated and email-loop-based.
- Malicious bot code you wrote yourself. If you write a bot that exfiltrates your own API keys to an attacker-controlled URL, we can't stop that. Defense: don't paste untrusted code without reading it.
5. AI Fix — what gets sent to Claude
When you click "Fix with AI", the platform sends the following to Anthropic's Claude API:
- Your bot's source code (the file you wrote)
- The Python traceback that triggered the error
- The last ~50 lines of bot logs
- Bot metadata: bot ID, bot name (if set), connection name (if relevant)
What is explicitly not sent:
- Your API keys. Bots read keys via
wd.connection()at runtime — they're never visible in source code, so they can't be included in what we send. - Your account email, IP, or user identifier. Only the bot ID is sent.
- Other bots' code, logs, or state.
- Anthropic does not train on API inputs under their standard API terms. We don't retain the request or response server-side beyond the request lifetime (~30 seconds).
- An audit row is written to our database recording that the call happened, which bot it was for, and whether it succeeded — but never the content sent.
Full details: AI Fix Guide.
6. Infrastructure & vendors
The third-party services we use:
| Vendor | Purpose | What they see |
|---|---|---|
| Supabase | Encrypted database | Encrypted ciphertext only; never decryption keys |
| Railway | Web backend host | HTTP request metadata; no persistent storage of user data |
| Stripe | Payment processing | Card data (PCI compliant); never seen by us |
| Anthropic | AI Fix LLM provider | Bot code + traceback per request; no training, no retention |
| Google Workspace | Email infrastructure | Standard Gmail processing for support correspondence |
| GitHub | Code hosting | Our source code; no user data |
All vendors are SOC 2 compliant. Geographic location: primarily US East (Supabase, Railway US West regions). EU/UK users: data may transfer to US under Standard Contractual Clauses (see Privacy Policy section 9).
7. What you should do
- Strong, unique password on your WatchDog Bot account. Not reused from any other service.
- Enable 2FA on your account (Settings → Security → Add Authenticator).
- Disable withdrawal permissions on every exchange API key you give us.
- IP-restrict your API keys where the exchange supports it.
- Rotate API keys every 90 days, or whenever you've shared them with anyone (debugging, demos, etc.).
- Keep your OS up to date. Most successful "trading bot hacks" are local machine compromises, not platform compromises.
- Don't paste random bot code from Discord. Untrusted code can do anything the bot venv allows.
- Verify the URL before logging in:
watchdogbot.cloudonly. There is nowatchdog-bot.ioor similar — those are phishing.
8. Responsible disclosure
Found a vulnerability? Tell us.
We don't currently operate a paid bug bounty, but we deeply appreciate responsible disclosure and credit reporters in our public changelog with their permission.
Response time: < 24 hours for initial acknowledgment, any day of the week, including weekends.
PGP key available on request. We accept reports in plain text or encrypted.
What we ask:
- Give us reasonable time (typically 90 days, faster for critical issues) to fix the vulnerability before public disclosure.
- Don't exploit the vulnerability beyond what's needed to demonstrate it.
- Don't access, modify, or download user data beyond your own.
- Don't run automated scans that generate significant traffic.
- Tell us if you accidentally accessed someone else's data; we will not pursue legal action against good-faith researchers.
In scope:
- Anything on
*.watchdogbot.cloud - The WatchDog Bot desktop application (Windows + macOS)
- Authentication flows, authorization checks, RLS policies
- Encryption implementation and key derivation
- The
wdPython SDK - Auto-install / self-heal logic in the runtime
Out of scope (please don't submit these unless they have unusual real-world impact):
- Self-XSS or social engineering
- Issues only reproducible on outdated browsers
- Rate-limit configurations that aren't actually exploitable
- Reports generated by automated scanners with no manual validation
- "Disclosure of public information" findings
Questions, audit requests, compliance docs?
For SOC 2 questions, DPA requests, vendor security questionnaires, or any other compliance need, email security@watchdogbot.cloud. We respond within 2 business days.
WatchDog Bot