`EDGE_JWT_SECRET` was read as `process.env.EDGE_JWT_SECRET ?? "dev-edge-secret-change-me"` in two places. Anything holding that string can mint an edge JWT for any `farm_id` it likes and open `/ws/edge/v1` as that farm's gateway: enough to feed a farm false telemetry and to receive the commands meant for its printers. It is in a git repository, so it is not a secret. The `??` made it worse rather than better. It only catches null and undefined, and the committed `.env.example` ships `EDGE_JWT_SECRET=` *empty* - so a deployment that copied the template and filled in everything else signed its edge JWTs with the empty string and never reached the fallback at all. That is exactly the case the fallback existed to cover. Both reads now come from `src/auth/edge_secret.ts`, which throws at module load if the variable is unset or empty. Same shape as `verifier.ts` and for the same reason: a backend that boots and then fails every gateway handshake looks like a network problem somewhere else. There is no development default on purpose - a secret that works out of the box is a secret that reaches production, and `openssl rand -hex 32` is a one-time cost. Tests: the empty-string case, since that is the one the template produced, and a grep for both shapes that reintroduce the hole - a literal passed to `jwt()`, and a defaulted read of the variable - plus an assertion that the variable is read in exactly one place. 60 pass.
30 lines
1.1 KiB
Bash
30 lines
1.1 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)
|
|
R2_ACCOUNT_ID=
|
|
R2_BUCKET=continuum
|
|
R2_ENDPOINT=
|
|
R2_ACCESS_KEY_ID=
|
|
R2_SECRET_ACCESS_KEY=
|
|
|
|
# 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=
|