ipx-web: the page in its own container, behind Caddy (#203)

web/build.mjs builds a static site, web/dist, with the icons and typeface
beside the pages; Dockerfile.web serves it with Caddy, which passes /api
and /media to ipx on the same origin, so cookies, live updates and
Cloudflare Access work as before. ipx serves no page any more: build.rs
and every include_str! of the web files are gone, and its image is built
without node.

What ipx did to the page as it served it is kept another way. Caddy asks
ipx before serving a page (forward_auth): /api/page-check sends a browser
not signed in to /login and a non-admin away from /admin, and refuses them
/admin.js; /api/login-check sends someone Access vouches for past the
form; a page asked for with ?token= goes to /api/signin, which sets the
cookie. The theme and the admin link, once written into the page per
person, are set by web/boot.js in <head> from the theme cookie and the
last visit, before the first frame, and /api/me brings the version and
the account's theme from before.

Caddy strips the Cf-Access-* headers from every peer but TUNNEL_PEER, the
address cloudflared's requests come from, which is the boundary
trusted_proxies drew; ipx then trusts only Caddy's address.

The browser suite runs Caddy in front of its daemon, so it tests what
production would run. Not deployed: production's cutover is on #203.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 05:06:53 +00:00
parent ed82166745
commit de6b0a4ca5
21 changed files with 276 additions and 278 deletions

View File

@@ -1,6 +1,7 @@
# A sample: ipx on its own, keeping everything in SQLite. Build the image first
# (`docker build -t ipx .`), then `docker compose up -d`, and sign in at http://<host>:8099
# as admin / ipodderx. docs/configuration.md has the rest, Postgres included.
# A sample: ipx with its database in SQLite, and ipx-web, the page, in front of it. Build both
# images first (`docker build -t ipx .` and `docker build -f Dockerfile.web -t ipx-web .`), then
# `docker compose up -d`, and sign in at http://<host>:8099 as admin / ipodderx.
# docs/configuration.md has the rest, Postgres included.
services:
ipx:
image: ipx
@@ -13,8 +14,8 @@ services:
TZ: "UTC"
# For Postgres instead of SQLite:
# IPX_DATABASE_URL: "postgres://ipx:password@db-host:5432/ipx"
# No web port of its own: ipx-web is the way in, and passes the API on to it.
ports:
- "8099:8099" # web UI
- "6881:6881/tcp" # BitTorrent peers
- "6881:6881/udp" # DHT
volumes:
@@ -27,3 +28,22 @@ services:
timeout: 5s
retries: 3
start_period: 10s
ipx-web:
image: ipx-web
container_name: ipx-web
restart: unless-stopped
environment:
IPX_UPSTREAM: "ipx:8099"
# Behind Cloudflare Access only: where cloudflared's requests come from, the one address
# whose Cf-Access-* headers are passed on. docs/sso.md says how to find it.
# TUNNEL_PEER: "192.168.64.1"
ports:
- "8099:8080" # the web UI
depends_on:
- ipx
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8080/favicon.png"]
interval: 30s
timeout: 5s
retries: 3