From c64fb1d4f1444f2eeb4aee59d079bea676bed589 Mon Sep 17 00:00:00 2001 From: Paul Hitt Date: Wed, 9 Sep 2026 08:25:23 -0400 Subject: [PATCH] Make a fresh self-hosted start actually work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by running `docker compose config` against every file with only what `.env.example` provides, rather than by reading the guide. 🔴 `FN_AT_REST_SECRET` blocked every fresh start. Three compose files required it with `:?`, it is absent from `.env.example`, and the relay reads it zero times: `docs/PLAINTEXT.md` records it as removed when the plaintext store went. So anybody following SELF-HOSTING.md hit required variable FN_AT_REST_SECRET is missing a value on their first `docker compose up -d`, for a variable that does nothing. Removed from all three. ⚠️ `docker-compose.pooled.yml` cannot layer on the base file: it uses the `internal` network, which only `prod` defines, so base + pooled fails with "refers to undefined network internal". `docs/POOLING.md` had the right invocation; the new compose map in SELF-HOSTING.md now says so too. Every FN_* the relay reads is documented: 78 of 78, up from 68. The ten that were in no document at all were the whole APNs block, which is what wakes an iPhone; FN_TRUSTED_PROXIES, which decides whether per-IP limits apply to one client or to everyone behind the proxy at once; FN_IOS_LIVE; FN_DELETE_ON_ACK; FN_DISPOSABLE_EMAIL_DOMAINS; and FN_FEDERATION_SCREEN. The five FN_OBFS4_* read by the prod compose file are documented too. All five compose files now validate: base, and base layered with build, prod, federated, and prod+pooled. 697 tests pass. Co-Authored-By: Claude Opus 5 --- docker-compose.federated.yml | 1 - docker-compose.prod.yml | 1 - docker-compose.yml | 1 - docs/CONFIGURATION.md | 47 ++++++++++++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docker-compose.federated.yml b/docker-compose.federated.yml index 05c014e..fb78c0b 100644 --- a/docker-compose.federated.yml +++ b/docker-compose.federated.yml @@ -76,7 +76,6 @@ services: FN_RELAY_AUDIENCE: "${FN_PUBLIC_HOST:?set FN_PUBLIC_HOST in .env}" FN_ISSUER_PUBLIC_KEY: "${FN_ISSUER_PUBLIC_KEY:?generate an issuer keypair - see .env.example}" FN_ISSUER_PRIVATE_KEY: "${FN_ISSUER_PRIVATE_KEY:?generate an issuer keypair - see .env.example}" - FN_AT_REST_SECRET: "${FN_AT_REST_SECRET:?set FN_AT_REST_SECRET in .env}" # Federation proper, see the Federation section of .env.example. FN_FEDERATION: "${FN_FEDERATION:-allowlist}" FN_FEDERATION_PEERS: "${FN_FEDERATION_PEERS:-}" diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 412380f..da8f53b 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -85,7 +85,6 @@ services: FN_RELAY_AUDIENCE: "relay.flamenet.io" FN_ISSUER_PUBLIC_KEY: "${FN_ISSUER_PUBLIC_KEY:?generate an issuer keypair - see .env.example}" FN_ISSUER_PRIVATE_KEY: "${FN_ISSUER_PRIVATE_KEY:?generate an issuer keypair - see .env.example}" - FN_AT_REST_SECRET: "${FN_AT_REST_SECRET:?set FN_AT_REST_SECRET in .env}" # The shared FlameNet account service's signing key, when this relay # accepts FlameNet sessions. Empty on a self-hosted relay, which is its # own identity provider and registers no such route. diff --git a/docker-compose.yml b/docker-compose.yml index 01b5ed1..0a36d41 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,7 +57,6 @@ services: # every restart. FN_ISSUER_PUBLIC_KEY: "${FN_ISSUER_PUBLIC_KEY:?generate an issuer keypair - see .env.example}" FN_ISSUER_PRIVATE_KEY: "${FN_ISSUER_PRIVATE_KEY:?generate an issuer keypair - see .env.example}" - FN_AT_REST_SECRET: "${FN_AT_REST_SECRET:?set FN_AT_REST_SECRET in .env}" # Donations are off unless both are set. Card details never reach this # service: the form posts an amount and the relay redirects to the # processor's own hosted checkout. diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 60bce91..1fdf38d 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -200,6 +200,53 @@ All optional. Each is absent rather than half-enabled when unconfigured. | `FN_DONATE_ACCOUNT` | The connected account to charge, if `FN_DONATE_SECRET` belongs to a Stripe *platform* rather than to the account meant to receive the money. Sent as `Stripe-Account`, which makes the payment a direct charge there. Blank charges whatever account the key itself belongs to, the right answer when you are self-hosting with your own key, and the wrong one with a platform key, where Stripe answers 200 and the money lands on the platform with nothing on the page looking wrong. With this set, the publishable key stays the **platform's**. | +## iPhone push (APNs) + +Only the iOS app needs this; Android is woken directly. All five must be set +together, and the relay logs that push is disabled if any is missing. + +| Variable | Default | Notes | +|---|---|---| +| `FN_APNS_KEY_P8` | unset | Path to the `.p8` signing key from Apple. Mount it as a secret; it is not a file to bake into an image. | +| `FN_APNS_KEY_ID` | unset | The key's ten-character id. | +| `FN_APNS_TEAM_ID` | unset | Your Apple developer team id. | +| `FN_APNS_TOPIC` | `net.flamenet.messenger` | The app's bundle id. Change it if you ship your own build. | +| `FN_APNS_ENV` | `sandbox` | `sandbox` or `production`. A token from a TestFlight or App Store build is meaningless to the sandbox host and the other way round, and APNs returns the same 400 either way. Getting this wrong means no iPhone is ever woken, so set it to `production` the moment you ship a build that is not from Xcode. | + +## Behind a proxy + +| Variable | Default | Notes | +|---|---|---| +| `FN_TRUSTED_PROXIES` | `0` | How many proxy hops to trust when reading `X-Forwarded-For`. Left at 0 the relay uses the socket address, which is your proxy, so every request looks like it comes from one IP and per-IP limits apply to everyone at once. Set it to the number of proxies in front of the relay, not higher: a client can forge the header for any hop you trust beyond the ones you actually run. | + +## Pluggable transport (obfs4) + +Reachability, not privacy: a second address that does not look like a relay, +for networks that block the first. It is what `docker-compose.prod.yml` runs +under the `obfs4` profile, and the variables are read by that compose file +rather than by the relay. + +| Variable | Default | Notes | +|---|---|---| +| `FN_OBFS4_PORT` | `8443` | Published on every interface, unlike the relay. A bridge only loopback can reach helps nobody; what makes that safe is that the protocol answers nobody who cannot complete a handshake. | +| `FN_OBFS4_ADVERTISE` | unset | The public address to put in the bridge line handed to clients. A container cannot know its own, so without this the line says `` and is not dialable. | +| `FN_OBFS4_IMAGE` | `flamenet-obfs4bridge:prod` | Set it to an image you can pull. | +| `FN_OBFS4_CONTEXT` | `../flamenet-elligator2` | Build context, a sibling checkout. That repository is private, so its registry image cannot be pulled anonymously. | +| `FN_OBFS4_UPSTREAM` | `relay:8080` | Where the bridge forwards to. | + +The bridge's state volume holds its identity key, which clients pin through the +`cert` in the bridge line. Losing it invalidates every bridge line you have +handed out. + +## Site copy + +| Variable | Default | Notes | +|---|---|---| +| `FN_IOS_LIVE` | `false` | Flips clauses on the public pages that describe iPhone availability. Turning it on states general availability, so it belongs on after an App Store release and not during a beta. | +| `FN_DELETE_ON_ACK` | see Retention | Delete a delivered envelope the moment the recipient's app confirms it, rather than after the delivered-retention window. The metadata page reads this and describes whichever is true. | +| `FN_DISPOSABLE_EMAIL_DOMAINS` | unset | Comma-separated domains refused at sign-up. Empty means none are. | +| `FN_FEDERATION_SCREEN` | unset | Advisory IP screening for federated peers. It does not gate federation; the allowlist and the pinned key do. | + ## Watching the key log (relay#32) | Variable | Default | What it does | -- 2.54.0