Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Cloudflare Zero Trust + Pi (recommended)

Production deployment: Cloudflare Zero Trust + Pi

Real-world IBKR API deploys hit a wall: api.ibkr.com (fronted by Akamai) rejects connections from cloud datacenter IPs. So your bezant-server can’t run on Railway / Fly / Render / Heroku and reach the upstream successfully — IBKR responds 401 to the SSO→CPAPI bridge call, and every typed API call cascades into 401s.

The pattern that works in 2026:

Your bot (Railway/cloud)
   │   HTTPS + Service Token
   ▼
Cloudflare Zero Trust (Tunnel + Access)
   │   Cloudflare Tunnel
   ▼
Raspberry Pi at home
   │   ┌───────────────────────────────┐
   │   │  bezant-server (port 8080)   │
   │   │  CPGateway (port 5000)       │
   │   └───────────────────────────────┘
   │   Cloudflare WARP egress
   ▼
api.ibkr.com (Akamai)

Why each piece:

  • Pi at home — residential ISP IP would also be flagged by Akamai if it weren’t for WARP. (Don’t skip WARP.)
  • Cloudflare WARP on the Pi — routes outbound to api.ibkr.com through Cloudflare’s edge IPs, which are reputationally clean.
  • Cloudflare Tunnel — exposes the Pi to your bot without opening a port on your router or having a public IP.
  • Cloudflare Zero Trust Access — gates the public hostname. Browsers (you) hit an SSO challenge; service-to-service calls (your bot) carry a Service Token in two headers.

One-shot setup

# 1. On the Pi (Raspberry Pi 4/5 with 4GB+ RAM, Pi OS Lite arm64):
sudo apt update && curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER

# 2. Install Cloudflare WARP (residential→clean-IP egress):
curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | \
  sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] \
  https://pkg.cloudflareclient.com/ bookworm main" | \
  sudo tee /etc/apt/sources.list.d/cloudflare-client.list
sudo apt update && sudo apt install -y cloudflare-warp
warp-cli --accept-tos registration new
warp-cli --accept-tos connect

# 3. Install Cloudflare Tunnel (cloudflared) — get a token from the
#    Zero Trust dashboard → Networks → Tunnels → Create:
sudo cloudflared service install <YOUR_TUNNEL_TOKEN>

# 4. Run bezant-combined (CPGateway + bezant-server in one container):
docker run -d --name bezant --restart unless-stopped \
  -p 127.0.0.1:8080:8080 \
  -e BEZANT_DEBUG_TOKEN="$(openssl rand -hex 32)" \
  ghcr.io/isaacrowntree/bezant-combined:latest

Cloudflare dashboard configuration

  1. Tunnel → add Public Hostname: bezant.yourdomain.com → service HTTP localhost:8080.
  2. AccessApplications → add Self-hosted for the same hostname. Add two policies:
    • Browser (Allow): “Emails = [email protected]” — for you to do the IBKR login interactively.
    • Service (Service Auth): generated Service Token — for your bot. Save the Client ID + Secret.
  3. Your bot calls https://bezant.yourdomain.com/... with two headers:
    CF-Access-Client-Id: <client-id>.access
    CF-Access-Client-Secret: <secret>
    

Login flow

You’ll need to do an interactive IBKR login periodically — open https://bezant.yourdomain.com/sso/Login in a browser, get challenged by Cloudflare Access SSO, then by IBKR’s own login form, and approve the 2FA push on your phone. Once that’s done, bezant-server’s keepalive keeps the session warm and your bot’s API calls succeed.

How often you need to re-login depends on IBKR — community reports range from ~12h to a few days, and IBKR runs nightly maintenance that typically forces a fresh login once per trading day. Don’t assume a hard SLA; design your bot to handle a 401 by surfacing a “needs login” alert rather than crashing.

Security model

  • Cloudflare Zero Trust is the primary perimeter. With a correctly configured Access policy, only your email-authenticated browser and your token-authenticated bot can reach the Pi.
  • bezant-server’s BEZANT_BIND defaults to 0.0.0.0:8080 — that’s fine behind Cloudflare Tunnel + a 127.0.0.1 Docker port-bind (as in the snippet above). Don’t expose 8080 directly to the internet without Zero Trust in front.
  • Debug endpoints (/debug/jar, /debug/probe) are off by default. Setting BEZANT_DEBUG_TOKEN enables them, gated by an X-Bezant-Debug-Token header (or ?token=… query). With Zero Trust in front, this is defense-in-depth — leave it off until you’ve verified your Access policies are tight.
  • The shared cookie jar holds live IBKR session cookies. Anyone who can read it can resume the IBKR session and trade your account. bezant-server is single-tenant by design — don’t deploy this proxy multi-tenant.