Skip to content

05 - Security Architecture

Trust Boundaries

Tradeboard is a self-hosted trading application. The application enforces authentication, request validation, rate limits, IP bans, browser protections, and encrypted token persistence. The operator owns host hardening, TLS termination, reverse-proxy trust, firewall policy, secrets, backups, and broker-account controls.

text
client
  -> TLS/reverse proxy and host firewall
  -> Flask security and IP-ban middleware
  -> session, API-key, webhook, or OAuth authentication
  -> schema/CSRF/rate-limit policy
  -> service and broker/sandbox boundary
  -> isolated local stores

The internal ZeroMQ market-data endpoint is unauthenticated and must remain private. The public market-data WebSocket has its own API-key authentication handshake.

Application Authentication

Application passwords are Argon2-hashed with API_KEY_PEPPER. TOTP can be required independently for login, Remote MCP write authorization, and password reset.

Device sessions and the installation-wide broker session are separate:

  • at most five active application sessions are retained per user;
  • same-user/same-IP login replaces the existing device row;
  • session-status polling refreshes last_seen no more than once every 30 seconds;
  • broker-token expiry preserves a valid app session and reports reconnect state;
  • password changes revoke active device sessions.

The default daily session boundary is 03:00 IST. DISABLE_SESSION_EXPIRY=true supports deliberately continuous deployments such as crypto, but it expands session lifetime and should be an explicit decision.

API Keys And Tokens

Tradeboard generates one current 64-character hexadecimal API key per user. database/auth_db.py stores:

  • an Argon2 hash for verification;
  • an encrypted copy for authenticated UI and integration retrieval;
  • hashed cache keys for positive and negative verification caches.

Regeneration invalidates the prior key. The key has no per-operation scopes; public REST operations remain subject to schemas, Analyzer Mode, Action Center policy, services, and rate limits.

Broker auth/feed tokens and other supported token fields use Fernet. The key is derived from API_KEY_PEPPER and the per-install FERNET_SALT. A legacy static-salt fallback exists only for migration compatibility and must not be documented as the normal installation design.

Static broker application credentials remain in .env; masking them in Profile responses does not encrypt that file.

Browser And REST Policy

  • HTTPS deployments use Secure, HTTP-only, SameSite session cookies and the __Secure- prefix where configured.
  • Session-authenticated state changes use CSRF protection unless a route has a specific callback/webhook exemption.
  • /api/v1 is CSRF-exempt because it uses API-key request authentication.
  • CORS, CSP, referrer, permissions, framing, and related response headers are configured centrally.
  • Debug mode is rejected on externally bound hosts unless the operator explicitly overrides the guard.

Most REST schemas require apikey in the JSON body. Only endpoints that explicitly implement another location should accept a header or query parameter.

IP Security

SecurityMiddleware checks the resolved client IP against logs.db before Flask handles the request. TRUST_PROXY_HEADERS is false by default; forwarded headers are accepted only when it is enabled. Enabling it without an enforced reverse-proxy boundary permits IP spoofing.

Automatic banning is persisted in application settings and is off by default:

SettingDefault
404 threshold in 24 hours100
404 ban duration0 hours (permanent)
Invalid API-key threshold in 24 hours100
Invalid API-key ban duration0 hours (permanent)
Repeat-offender limit2 bans

Localhost addresses are excluded from automatic bans. There is no general CIDR allowlist in the application middleware. See 23 IP Security.

Traffic And Audit Boundaries

Traffic logging stores timestamp, client IP, method, path, status, duration, host, optional middleware error, and user ID when available. It does not store request/response bodies, headers, user agents, or a processing timeline.

Order, analyzer, login, MCP, latency, health, and notification events use separate persistence or logging paths. logs.db traffic rows are therefore not a complete audit trail of every state change.

WebSocket And MCP Security

The public market-data proxy requires an API-key authentication message within WS_AUTH_GRACE_SECONDS. Per-client queue size, ping interval, timeout, and subscription capacity are bounded by configuration. Private order, position, and margin topics are not fanned out as public market data.

Remote MCP is opt-in. Its OAuth boundary implements exact redirect-URI checks, S256-only PKCE, signed access tokens, refresh-token rotation, and token-family revocation after refresh-token reuse. The sample keeps HTTP off but has approval off and write scope enabled if only the master switch is changed; the Docker enable helper applies approval on and write scope off. Write authorization can require fresh TOTP.

Data Protection

Five SQLite databases and one DuckDB store isolate operational domains; this is not database-wide encryption. Backups must protect all stores together with .env, Fernet material, strategy secrets, and MCP signing keys.

StoreSecurity-relevant contents
tradeboard.dbUsers, hashes, encrypted tokens/API key copy, settings, sessions, OAuth and application state
logs.dbTraffic metadata, login/security trackers, IP bans
latency.dbTiming telemetry
health.dbRuntime health telemetry
sandbox.dbSimulated account and order state
historify.duckdbHistorical market data and Historify metadata

Key Files

FileResponsibility
app.pySecurity initialization, CSRF exemptions, headers, teardown
database/user_db.pyPassword/TOTP user model
database/auth_db.pyAPI-key verification, encrypted tokens, sessions
database/traffic_db.pyTraffic and IP security persistence
database/settings_db.pyPersisted automatic-ban settings
utils/security_middleware.pyPre-Flask ban enforcement
utils/ip_helper.pyTrusted client-IP resolution
utils/session.pySession-expiry guards
blueprints/security.pySecurity dashboard controls
blueprints/mcp_oauth.pyRemote MCP OAuth boundary