Makes the printer cameras playable from a hosted console, which they were not: GET /printers/:id/stream handed out the gateway's own LAN address, and a browser cannot use it. go2rtc sends no CORS headers and checks its API password before CORS, so a browser's unauthenticated preflight gets a 401 (measured against 1.9.14), and a gateway is behind a workshop NAT anyway. That endpoint could only ever have worked for an operator standing on the same network, and offered everyone else a player that silently never loaded. So /:id/stream now returns a camera name and short-lived ICE servers, and POST /:id/webrtc relays the offer to the gateway over the socket it already holds open and returns go2rtc's answer. Media travels browser-to-gateway through the TURN relay, DTLS-SRTP encrypted, so neither this process nor Cloudflare can read it - which is why this is signalling rather than proxying fMP4 through the API. That alternative also needed a credential in the URL, because <video> cannot send an Authorization header. Only the browser gets TURN credentials. It is pinned to relay-only, so it allocates the relay and go2rtc sends to it; one side is enough. That keeps rotation on a per-viewer request instead of in go2rtc's config, which is read once at startup - rotating there would restart it and drop every viewer on a timer. Unconfigured TURN is supported rather than fatal, unlike R2: a viewer on the gateway's LAN connects on host candidates. turnConfigured is returned alongside, because empty iceServers means "not set up" in one case and "minting failed" in the other, and those need different answers. Camera negotiations are failed immediately when a gateway disconnects, rather than waiting out the 12s timeout - a browser is holding a request open on each one. getStreamBaseUrl is kept but no longer served to anyone: gateways still report the field and it is the only record of where one thinks it is reachable, which is useful when diagnosing one. e2e-edge.sh asserts the new shape, that no streamUrl comes back, and drives a real offer through the relay - asserting the refusal came from go2rtc rather than from a timeout, since that is what proves the round trip happened.
56 lines
2.5 KiB
Bash
56 lines
2.5 KiB
Bash
PORT=3000
|
|
|
|
# Postgres, routed through pgCat/PgBouncer
|
|
DATABASE_URL=postgres://continuum:continuum@localhost:6432/continuum
|
|
DATABASE_POOL_MAX=10
|
|
|
|
# Valkey (state + streams), NOT Redis - Redis 7.4+ is RSALv2/SSPL and 8.0+
|
|
# adds AGPLv3, none of which we can ship commercially. Valkey is BSD-3-Clause
|
|
# and wire-compatible. The URL scheme stays `redis://` because that names the
|
|
# RESP wire protocol, which is what the client libraries parse - no client
|
|
# understands a `valkey://` scheme.
|
|
VALKEY_URL=redis://localhost:6379
|
|
|
|
# Cloudflare R2 (S3-compatible). The two credentials are required and the
|
|
# process will not start without them: an empty key signs presigned URLs that
|
|
# R2 answers with a 403, which surfaced as a gateway downloading an XML error
|
|
# document in place of a print rather than as "R2 is not configured".
|
|
#
|
|
# Either R2_ENDPOINT or R2_ACCOUNT_ID is needed too - the endpoint is derived
|
|
# from the account id the way Cloudflare's own URLs are formed. R2_BUCKET is
|
|
# not a credential and defaults to "continuum".
|
|
R2_ACCOUNT_ID=
|
|
R2_BUCKET=continuum
|
|
R2_ENDPOINT=
|
|
R2_ACCESS_KEY_ID=
|
|
R2_SECRET_ACCESS_KEY=
|
|
|
|
# Cloudflare Realtime TURN, for relaying printer camera video.
|
|
#
|
|
# Optional, and unset is a supported state rather than a broken one: with no
|
|
# TURN the console can still play a camera for a viewer on the gateway's own
|
|
# network, over host candidates. Remote viewers need this.
|
|
#
|
|
# Why a relay at all: a browser cannot reach a gateway directly. go2rtc sends
|
|
# no CORS headers and checks its API password before CORS, so a browser's
|
|
# unauthenticated preflight gets a 401 - and a gateway is behind a workshop
|
|
# NAT besides. Only the SDP exchange is relayed by this backend; the media
|
|
# goes browser-to-gateway through TURN, DTLS-SRTP encrypted, so neither this
|
|
# process nor Cloudflare can read it.
|
|
#
|
|
# The browser is pinned to relay-only when these are set, so video never goes
|
|
# peer-to-peer and behaves the same on every customer network. Credentials
|
|
# are minted per request with a 1 hour TTL (src/camera/ice.ts).
|
|
# CLOUDFLARE_TURN_KEY_ID=
|
|
# CLOUDFLARE_TURN_API_TOKEN=
|
|
|
|
# Supabase Auth. Access tokens are verified against this project's published
|
|
# JWKS (RS256), so no signing secret is needed - or wanted - here.
|
|
SUPABASE_URL=
|
|
|
|
# Signs the edge JWTs minted for continuum-proxy gateways via
|
|
# POST /auth/edge-token and verified by /ws/edge/v1. Required - the process
|
|
# refuses to start without it, because anything that knows this secret can
|
|
# connect as any farm's gateway. Generate one with: openssl rand -hex 32
|
|
EDGE_JWT_SECRET=
|