Self-hosting the tunnel
The tunnel (codeflower-tunnel) relays end-to-end encrypted traffic between paired browsers and your computers, and keeps the accounts that own them. Run your own to keep even that metadata in-house, or to restrict who may use it.
With Docker (any server)
Section titled “With Docker (any server)”git clone https://github.com/lutrarutra/codeflower-tunnel && cd codeflower-tunneldocker build -t codeflower-tunnel .docker run -d --name codeflower-tunnel -p 8787:8787 -v tunnel:/data \ -e PUBLIC_URL=https://tunnel.example.org \ -e COOKIE_SECRET="$(openssl rand -base64 48)" \ -e GITHUB_CLIENT_ID=… -e GITHUB_CLIENT_SECRET=… \ codeflower-tunnelPut it behind a reverse proxy with HTTPS at PUBLIC_URL, passing WebSockets through. It keeps its data in an
SQLite file (TUNNEL_DB, /data/tunnel.sqlite in the image). Without Docker: npm ci && npm start with the same
variables (Node 22).
| Variable | |
|---|---|
PUBLIC_URL |
required: the tunnel’s address |
COOKIE_SECRET |
required: at least 32 random characters. Changing it signs everyone out |
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
sign-in with GitHub (an OAuth app with callback PUBLIC_URL/auth/github/callback) |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
sign-in with Google (callback PUBLIC_URL/auth/google/callback) |
REQUIRE_GITHUB_2FA |
GitHub accounts without two-factor authentication are refused unless this is false |
WEBAUTHN_RP_ID |
the passkeys’ domain; default: PUBLIC_URL’s host |
ALLOWED_IPS |
comma-separated CIDR ranges allowed to open pages, e.g. your institute’s networks |
CLIENT_IP_HEADER |
behind a proxy: the header with the browser’s address, e.g. X-Forwarded-For |
PORT, HOST |
where it listens; default 8787 on all interfaces |
Passkeys work without any OAuth provider. Passkeys are bound to the domain: changing it means everyone registers theirs again.
On Cloudflare
Section titled “On Cloudflare”The hosted tunnel runs as a Cloudflare Worker with a D1 database and a Durable Object per connected computer. Adapt
wrangler.toml (your domain, your D1 database), set the secrets with wrangler secret put (COOKIE_SECRET and the
OAuth pairs), then:
npm cinpm run deploy # applies the database migrations, then deploysUse it
Section titled “Use it”Sign each computer in to your tunnel:
codeflower tunnel login --url https://tunnel.example.orgThis saves tunnel.url in config.yaml. Then pair browsers as usual.