Implement the ux-first-run change: a state-derived setup checklist on /operator/setup with a landing region that recedes once required steps are done, and empty states that distinguish blocked from empty across operator and member surfaces. Also add production deployment and environment reference docs, plus a config-key completeness test.
8.9 KiB
title, audience, summary
| title | audience | summary | |
|---|---|---|---|
| Production Deployment |
|
The sequenced path from a built image to a running production instance: what order to configure things in, and the glue between the per-service guides that no single one of them covers. |
Production Deployment
This guide sequences the existing per-service guides into one path and adds the connective pieces none of them owns on their own: the reverse proxy and trusted-origin contract, matching the identity provider's branding to the console's configured name, the Stripe webhook flow, and what happens the first time the process boots. It links out to each per-service guide instead of repeating it — read this first to know what order to do things in and where each step's real instructions live.
Status. member-console is pre-production software (see the root README). This guide gets a self-hoster to a working instance; it does not claim a hardened, one-command production rollout. Expect to adapt each step to your own infrastructure.
1. Build the image and prepare secrets
Follow hosting.md to build the container image for your
registry and generate the csrf-secret. Every other secret this instance
needs — the OIDC client secret, the Stripe API key and webhook secret, the
FedWiki admin token, the Discourse API key — is generated or obtained in the
later steps below and injected the same way: either as an MC_* environment
variable or as a file referenced by that key's -file variant (MC_*_FILE),
per environment-reference.md. Prefer the -file
form with your platform's secret-mounting mechanism (Docker/Podman secrets,
a Kubernetes Secret volume) over putting secret values directly in the
environment.
2. Decide the deployment shape
Read deployment-architecture.md for how member-console fits alongside a public homepage and an identity provider — it is infrastructure behind your own front door, not the front door itself. Decide now which services member-console needs for your offering:
- PostgreSQL and Valkey — always required.
- An OIDC identity provider — always required (any OIDC-compliant provider; see step 4).
- Temporal — required for provisioning, integration sync, and billing
sweeps to actually run; the console starts without it but logs
Temporal not configuredand every scheduled job stays off. - A FedWiki farm — required only if you offer FedWiki sites, the product's first conforming integration; see step 6.
- Stripe — required only for paid products; a deployment offering only free/unmetered products can skip it entirely.
- Discourse — optional, for forum-membership sync.
The test stack's test/compose.yaml is a useful reference for how these
services relate to each other and to member-console (ports, environment
wiring, health-check ordering) — read it as a shape example, not a
supported production compose file. It runs Keycloak, not-yet-hardened
secrets, and sslmode=disable, all of which are deliberately wrong for
production.
3. Reverse proxy, TLS, and trusted origins
member-console does not terminate TLS itself; put a reverse proxy in front of it (Caddy, nginx, Traefik, your platform's ingress) that:
- Terminates TLS and forwards plain HTTP to the console.
- Forwards the
Hostheader unchanged, or otherwise ensures the console sees a request whose host matchesbase-url's host exactly. - Passes the
OriginandRefererheaders through unmodified (do not strip them — the CSRF check below depends onOrigin).
Set base-url (MC_BASE_URL) to the console's public URL, e.g.
https://console.example.com. At boot, the console parses base-url and
sets its sole CSRF trusted origin to that URL's host
(internal/server/server.go, internal/middleware/csrf.go); every
state-changing request's Origin must match it or the request is rejected
with a CSRF error. There is no separate "trusted origins" list to configure
— it is derived from base-url alone, so a reverse proxy that rewrites the
Host header to something other than base-url's host, or that fronts the
console at more than one public hostname, breaks CSRF validation.
Set env (MC_ENV) to production. Among other effects
(internal/middleware/security.go), this marks the CSRF and session cookies
Secure, so browsers refuse to send them over a plain-HTTP connection —
another reason TLS at the proxy is not optional.
4. Identity provider
Follow identity-provider-setup.md to configure
your OIDC provider: the client, the claim shape, and the operator-member
role that gates the operator panel.
Match the realm's display branding to deployment-name. The sign-in
page a member sees is rendered by the identity provider, not by
member-console — the console only ever redirects to it and receives a token
back. If the console is configured with deployment-name (MC_DEPLOYMENT_NAME,
see internal/config/deployment.go) set
to your organization's name, but the IdP's realm still shows Keycloak's
default branding or a different name, a member sees two different
identities across one login flow. Set the realm's display name (Keycloak:
Realm Settings → General → Display name / HTML Display name, and
its logo/theme if you use one) to the same name as deployment-name so the
sign-in page and the app introduce themselves consistently.
5. Database
Follow database-management.md for the schema
layout and migration conventions. There is nothing extra to run before
first boot: migrations run automatically at startup (member-console start calls db.ConnectAndMigrate before serving any request —
cmd/start.go). A fresh database becomes fully migrated on the first
successful boot; there is no separate "run migrations" step required for a
new deployment, though member-console migrate up|down|status is available
for out-of-band migration control (e.g. running the pending migrations before
a rolling restart).
member-console seed-demo is not for production. It inserts a fixed,
idempotent reference catalog (demo products, a plan ladder, sample
grants) so an operator panel has something to click through. Never run it
against a production database.
6. Integrations
Configure whichever of these your deployment actually offers. Each is independently optional; an unconfigured one simply stays dormant (no routes, no scheduled jobs), it does not block boot.
- FedWiki — fedwiki-setup.md covers the FarmManager
security stack, the farm-side setup, and the
fedwiki-*keys. - Stripe — see the webhook flow below and stripe.md for the full configuration, subscribed events, and catalog-to-Stripe sync.
- Discourse — discourse-setup.md covers forum-side prerequisites and linkage-mode selection.
- Temporal authorization — if you run Temporal behind its own JWT authorizer (recommended), see temporal-authorization-setup.md for the IdP-side claim configuration.
Every key any installed integration reads is documented in environment-reference.md, including which keys are required together and which default values apply when unset.
Stripe webhook endpoint and signing secret
Once stripe-api-key is set, register a webhook endpoint in the Stripe
Dashboard pointing at https://<base-url>/webhooks/stripe and subscribe it
to the event types listed in stripe.md. Stripe
generates a signing secret (whsec_...) for that endpoint when you create
it; set that value as stripe-webhook-secret (or point
stripe-webhook-secret-file at it). stripe-api-key and
stripe-webhook-secret are a required pair — the console rejects a boot
where only one is set, and treats both left empty as billing intentionally
disabled.
7. First boot
With the database reachable, base-url/env set, the CSRF secret in place,
and the OIDC keys pointed at your realm, start the process (member-console start, or your image's equivalent entrypoint). At startup it, in order:
runs pending migrations, validates the aggregated configuration (failing
fast with every problem named at once if something required is missing —
see environment-reference.md for what's
required), connects to Temporal if configured, and starts serving.
Sign in as an operator (the account holding the operator-member role you
configured in step 4) and go to /operator/setup. It is a checklist of
everything the instance still needs before it has a deliverable product:
entitlement sets, a published product, plan ladders, and — if you configured
Stripe — a resolved billing configuration. Work through it in order; each
step links to the operator page that completes it and reflects as done once
the underlying data exists.