Your memory hub binds to 127.0.0.1:8080 inside a container. To expose it
publicly over HTTPS, without opening a port on your router, we front it with
Cloudflare Tunnel (the outbound cloudflared client) and gate access with
Cloudflare Access (email-OTP login). Your agent bearer token still protects the
API surface; Access adds a human-identity layer for browsers that hit /docs
or similar.
This document fills in the concrete steps that the architecture QUICKSTART intentionally abstracted.
- A domain on Cloudflare (free plan works).
example.comin the snippets below — replace with yours. - Cloudflare account with Zero Trust enabled (free up to 50 users). Pick a team subdomain at setup; you won't need to look at it again.
- The hub running and reachable at
http://localhost:8080/healthzon the host you're deploying from. cloudflaredinstalled:brew install cloudflaredon macOS,apt install cloudflaredon Debian/Ubuntu, or the binary from GitHub releases.
cloudflared tunnel login
Opens a browser. Pick your domain. The command saves a cert at
~/.cloudflared/cert.pem.
cloudflared tunnel create starshard-hub
Records the tunnel UUID. Credentials file at ~/.cloudflared/<uuid>.json
should be chmod 600.
cloudflared tunnel route dns starshard-hub hub.example.com
This adds a proxied CNAME hub.example.com → <uuid>.cfargotunnel.com.
Create ~/.cloudflared/config.yml:
tunnel: <uuid>
credentials-file: /home/you/.cloudflared/<uuid>.json
ingress:
- hostname: hub.example.com
service: http://localhost:8080
- service: http_status:404
Run it:
cloudflared tunnel run starshard-hub
Or install as a service:
sudo cloudflared service install
In the Zero Trust dashboard → Access → Applications → Add a SaaS / Self-hosted application:
- Name:
Starshard hub - Subdomain:
hub· Domain:example.com - Application session duration: 24 hours
- Identity providers: at minimum, enable "One-time PIN" (email OTP)
Add an Access policy:
- Name:
Allow primary users - Action: Allow
- Rules:
Emails include ["you@example.com"]
This allows browser visitors matching the allowlist. API traffic with a valid
bearer token should bypass Access — add a Bypass policy with rule
HTTP Headers contains "Authorization: Bearer" matching, OR use a Cloudflare
Access service token. The simplest for a single-user Phase 0 is to gate the
interactive endpoints only:
- Protect path
/with the user allowlist (email OTP). - Add a second Application for path
/mcpand/memory*with a service-token policy so agents can authenticate programmatically using theCF-Access-*headers.
This dual-Application approach is the recommended Phase 0 pattern. A single-policy setup is possible but harder to reason about.
curl -I https://hub.example.com/healthz
# Expect: HTTP/2 302 to Cloudflare Access login (browser) or HTTP/2 200 if
# you set up bypass for /healthz.
HUB_TOKEN=...
curl -sH "Authorization: Bearer $HUB_TOKEN" https://hub.example.com/memory
# Expect: JSON array.
- Treat
HUB_API_TOKENlike a password. Rotate on any suspected leak. - Access service-token credentials (
CF-Access-Client-Id/CF-Access-Client-Secret) should live in your agent's secret store, not in git. - Keep
cloudflaredupdated; it's the public-internet-facing component. - Enable Cloudflare Zero Trust audit logs for the Access Application.
- 503 Bad Gateway: tunnel running but hub not reachable on localhost. Check
docker compose ps. - 404 on all routes: ingress
hostname:in config.yml doesn't match the DNS CNAME. - Browser loop: Access policy misconfigured. Use the dashboard's "Test policy" with your email to debug.
- 401 on API calls with correct bearer: you're hitting the Access layer
before the app. Add a service-token bypass for
/memory*+/mcp.