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.
172 lines
8.9 KiB
Markdown
172 lines
8.9 KiB
Markdown
---
|
|
title: "Production Deployment"
|
|
audience: [admin]
|
|
summary: "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](../README.md)). 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](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](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](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 configured` and 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 `Host` header unchanged, or otherwise ensures the console
|
|
sees a request whose host matches `base-url`'s host exactly.
|
|
- Passes the `Origin` and `Referer` headers through unmodified (do not strip
|
|
them — the CSRF check below depends on `Origin`).
|
|
|
|
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](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`](../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](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](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](stripe.md) for the
|
|
full configuration, subscribed events, and catalog-to-Stripe sync.
|
|
- **Discourse** — [discourse-setup.md](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](temporal-authorization-setup.md) for the
|
|
IdP-side claim configuration.
|
|
|
|
Every key any installed integration reads is documented in
|
|
[environment-reference.md](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.md#webhook-endpoint). 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](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.
|