Security
Anyone who can send requests to this API can trade the brokerage account the gateway is logged into. Treat the API token like the account password.
What the stack can do
- Read balances, positions, orders and executions for every account on the IBKR login.
- Place, cancel and exercise for real through
POST /v1/orders,POST /v1/options/combo,POST /v1/options/exercise,DELETE /v1/orders/{orderId}andDELETE /v1/orders. The last one cancels every open order on the account, including orders placed in other tools. - Do all of the above through MCP at
/mcpwith the same token.
The only built-in brake is IBKR’s own: READ_ONLY_API=yes in .env.ibkr makes the gateway refuse orders. A paper login (TRADING_MODE=paper) keeps real money out of reach.
Authentication
- One shared bearer token,
api_tokeninconfig.yamlorAPI_TOKENin the environment. The middleware inibkrapi/server.pychecks it on every path:/v1/*,/mcp,/docsand/openapi.json. It compares withhmac.compare_digest. - An empty token turns auth off for everything, with one warning line from
run.shat startup. - There are no users, scopes or read-only tokens. Whoever has the token has full access.
- Generate a long random token and keep
config/config.yamlout of Git (it is gitignored):
openssl rand -hex 32
Network exposure
- nginx publishes the API on
127.0.0.1:8889by default (API_HOST_BIND,API_HOST_PORT). nginx itself speaks plain HTTP; there is no TLS in the stack. - The
apicontainer has no published port. The gateway’s TWS socket is only on the internalbackendnetwork. VNC (5900) and port8001are published on127.0.0.1only. - wickworks sits on an
internal: truenetwork with no route out. - The MCP endpoint does not check the
Hostheader (the SDK’s DNS rebinding protection is turned off), so a browser page could reach a loopback API without a token if you run it without one. Always set a token, even on localhost.
Before you bind to anything but loopback:
- Set a strong
api_token. - Put TLS in front: the commented
cloudflaredservice tunnels tohttp://nginx:80without a host port, and thetailscaleservice puts it on a tailnet. Add an access layer of your own (Cloudflare Access, tailnet ACLs) in front of the token. - Test the whole chain against a paper account from another network before you switch to live.
- Rotate the token after testing.
What is stored where
| Data | Location | Notes |
|---|---|---|
| IBKR username and password | .env.ibkr, passed only to the ibgateway container | Gitignored. The API process never sees them. |
| API token | config/config.yaml, and .env (written by run.sh) | Both gitignored. The startup log records only whether a token is set and its length. |
| Gateway settings | .data/ibgateway/ | |
| Request logs | logs/full.log and container stdout | Method, path, status, duration, client IP and user agent. No bodies, no tokens. |
| Market data and trading history | data/history/ | Executions and completed orders include account numbers, prices and quantities. Protect backups accordingly. |
Container hardening
api, nginx and wickworks run read-only with all capabilities dropped and no-new-privileges; the api image runs as a non-root user with hash-checked dependencies. The gateway container cannot be locked down the same way. Details on the deployment page.
Supply chain
Python dependencies are hash-locked and pass a 7-day age gate before they can enter the lock, base images are pinned by digest, and make audit, make audit-go and make audit-compose scan the Python deps, the Go client and the compose files. See Development.