The server no longer uses CSRF tokens, relying instead on the origin check in `internal/middleware/csrf.go`. Update all documentation, code comments, error handling, and specs to reflect the new protection model and remove the `csrf-secret` configuration key.
199 lines
17 KiB
Markdown
199 lines
17 KiB
Markdown
---
|
|
title: "Environment Variable Reference"
|
|
audience: [admin, developer]
|
|
summary: "Every configuration key member-console reads: purpose, default, required/optional, and its derived MC_ environment variable override."
|
|
---
|
|
|
|
# Environment Variable Reference
|
|
|
|
member-console reads configuration through [Viper](https://github.com/spf13/viper),
|
|
layered in this precedence order (highest wins): command-line flag, `MC_*`
|
|
environment variable, `mc-config.yaml` (or the file passed to `--config`),
|
|
built-in default. `internal/config/config_reference_test.go` asserts every
|
|
configuration key the source code reads is documented on this page.
|
|
|
|
**Derivation rule:** every key below is written in `mc-config.yaml` as its
|
|
kebab-case name (e.g. `db-dsn`). Its environment variable override is
|
|
`MC_` followed by that name upper-cased with every `-` replaced by `_` (e.g.
|
|
`MC_DB_DSN`). This is a single mechanical rule (`cmd/root.go`:
|
|
`viper.SetEnvPrefix("MC")` + `viper.SetEnvKeyReplacer(strings.NewReplacer("-",
|
|
"_"))`) applied uniformly; the table below states each key once and omits the
|
|
env column's value where it is a pure derivation, but always names the env
|
|
form so it can be found by search.
|
|
|
|
**Secret keys and `-file` variants.** A key marked *secret* also accepts a
|
|
`<key>-file` variant (env `MC_<KEY>_FILE`) whose value is a filesystem path;
|
|
at boot, member-console reads the file's contents into the key. Setting both
|
|
the direct value and the `-file` variant for the same key is a fatal
|
|
misconfiguration (the process exits before starting any service). This is
|
|
how Docker/Podman secrets and Kubernetes-mounted secrets are wired in without
|
|
putting secret material in the environment or the config file; see
|
|
[production-deployment.md](production-deployment.md).
|
|
|
|
**Type.** Every key's value is one of a closed set: `string`, `url`
|
|
(absolute, `http` or `https`, with a host), `duration` (a Go duration
|
|
string, e.g. `1h30m`; never negative), `int`, `bool` (`true`/`false`), `list`
|
|
(comma-separated), or an enum's members joined by `|`. `duration, greater
|
|
than zero` marks a duration key whose own consumer replaces a saved zero
|
|
with its default silently, so zero is refused rather than accepted and
|
|
ignored (design D6, `typed-config-keys`). Every declared integration key's
|
|
Type column is checked against its declaration by
|
|
`internal/config/config_reference_test.go`; core keys are filled by hand
|
|
from `internal/config/validate.go`'s (`ValidateStart`) checks.
|
|
|
|
## Core
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `port` | string | `MC_PORT` | Port the HTTP server listens on. | `8080` | Optional |
|
|
| `base-url` | url | `MC_BASE_URL` | Public URL of this console, e.g. `https://console.example.com`; `http` or `https`. Its origin is the console's sole cross-origin trusted origin, and its scheme decides the session cookie's `Secure` flag, the CSP `upgrade-insecure-requests` directive, and the `Strict-Transport-Security` header (all on for `https`, off for `http`); see [production-deployment.md](production-deployment.md). | none | Required |
|
|
| `env` | string | `MC_ENV` | Environment label. `development` selects text log formatting; any other value selects JSON. Nothing security-relevant hangs off it. | `development` | Optional |
|
|
| `deployment-name` | string | `MC_DEPLOYMENT_NAME` | Name this deployment presents for itself on the member and operator mastheads, page titles, and OpenGraph tags. | `Member Console` | Optional (rejected if set to blank/whitespace-only) |
|
|
| `support-url` | url | `MC_SUPPORT_URL` | URL shown in error messages for users to get support. | none | Optional |
|
|
|
|
## Database and session store
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `db-dsn` | url (postgres) | `MC_DB_DSN` | PostgreSQL connection string, e.g. `postgres://user:pass@host:5432/dbname?sslmode=disable`. Use this or `db-dsn-file`, never both. Treated as a secret: never logged; the boot log names host, port, database and user only. | none | One of this or `db-dsn-file` |
|
|
| `db-dsn-file` | path | `MC_DB_DSN_FILE` | File holding the PostgreSQL DSN, read at startup. | none | One of this or `db-dsn` |
|
|
| `valkey-addr` | string | `MC_VALKEY_ADDR` | Valkey/Redis address (`host:port`) for server-side session storage. | `localhost:6379` | Required (satisfied by the default unless explicitly cleared) |
|
|
| `valkey-username` | string | `MC_VALKEY_USERNAME` | ACL username for the session store, when it uses one. | none | Optional |
|
|
| `valkey-password` | string | `MC_VALKEY_PASSWORD` | Password for the session store. Use this or `valkey-password-file`, never both. | none | **Required** (the store must not be reachable unauthenticated) |
|
|
| `valkey-password-file` | path | `MC_VALKEY_PASSWORD_FILE` | File holding the session-store password, read at startup. | none | One of this or `valkey-password` |
|
|
| `valkey-tls` | bool | `MC_VALKEY_TLS` | Connect to the session store over TLS. | `false` | Optional |
|
|
| `valkey-tls-skip-verify` | bool | `MC_VALKEY_TLS_SKIP_VERIFY` | Do not verify the session store's TLS certificate. Self-signed stores only; logs a warning at every boot. | `false` | Optional |
|
|
|
|
## Identity (OIDC)
|
|
|
|
Cross-origin request protection has no configuration key: it is the server's
|
|
origin check, trusting `base-url`'s origin (`internal/middleware/csrf.go`). A
|
|
configuration that still carries `csrf-secret` boots; the key is not read.
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `oidc-idp-issuer-url` | url | `MC_OIDC_IDP_ISSUER_URL` | OIDC issuer URL, e.g. `https://idp.example.com/realms/main`. | none | Required |
|
|
| `oidc-idp-account-url` | url | `MC_OIDC_IDP_ACCOUNT_URL` | URL of the identity provider's self-service account console, opened by the shell's Identity and Access control. OpenID discovery defines no such URL, so providers other than Keycloak set it here. | `{oidc-idp-issuer-url}/account` (Keycloak's shape) | Optional |
|
|
| `oidc-sp-client-id` | string | `MC_OIDC_SP_CLIENT_ID` | OIDC client ID registered for this console. | none | Required |
|
|
| `oidc-sp-client-secret` (secret) | string | `MC_OIDC_SP_CLIENT_SECRET` | OIDC client secret. | none | Not enforced by boot validation; set it (or `-file`) for a confidential OIDC client |
|
|
| `oidc-sp-client-secret-file` | string | `MC_OIDC_SP_CLIENT_SECRET_FILE` | File path holding `oidc-sp-client-secret`. | none | Optional |
|
|
|
|
See [identity-provider-setup.md](identity-provider-setup.md) for configuring the IdP side of this exchange.
|
|
|
|
## Temporal
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `temporal-host` | string | `MC_TEMPORAL_HOST` | Temporal server `host:port`, e.g. `localhost:7233`. Leaving it unset disables Temporal, the worker, integration workflows, and every scheduled sweep below. | none | Optional (functionally required for provisioning and scheduled jobs) |
|
|
| `temporal-namespace` | string | `MC_TEMPORAL_NAMESPACE` | Temporal namespace. | `default` | Required once `temporal-host` is set |
|
|
| `temporal-oauth-token-url` | url | `MC_TEMPORAL_OAUTH_TOKEN_URL` | OAuth2 token endpoint for Temporal client authentication. | none | Optional; if any `temporal-oauth-*` key below is set, all three must be |
|
|
| `temporal-oauth-client-id` | string | `MC_TEMPORAL_OAUTH_CLIENT_ID` | OAuth2 client ID for Temporal authentication. | none | Optional; see above |
|
|
| `temporal-oauth-client-secret` (secret) | string | `MC_TEMPORAL_OAUTH_CLIENT_SECRET` | OAuth2 client secret for Temporal authentication. | none | Optional; see above |
|
|
| `temporal-oauth-client-secret-file` | string | `MC_TEMPORAL_OAUTH_CLIENT_SECRET_FILE` | File path holding `temporal-oauth-client-secret`. | none | Optional |
|
|
| `temporal-oauth-scopes` | list | `MC_TEMPORAL_OAUTH_SCOPES` | OAuth2 scopes for Temporal authentication (comma-separated). | none | Optional |
|
|
| `temporal-connect-timeout` | duration | `MC_TEMPORAL_CONNECT_TIMEOUT` | Max time to retry connecting to Temporal at boot before failing. | `90s` | Optional |
|
|
|
|
See [temporal-authorization-setup.md](temporal-authorization-setup.md) for configuring the IdP side of Temporal's own JWT authorization.
|
|
|
|
## Custom domains (external domain claims)
|
|
|
|
Members with the capability can point their own domain at a FedWiki site or
|
|
other resource; these keys tune that subsystem. All are optional; leaving
|
|
`domains-connect-target` unset disables external domain claims
|
|
deployment-wide.
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `domains-connect-target` | string | `MC_DOMAINS_CONNECT_TARGET` | DNS target members point external custom domains at (CNAME host or A-record IP). Empty disables external domain claims deployment-wide. | none | Optional |
|
|
| `domains-ask-fallback-url` | url | `MC_DOMAINS_ASK_FALLBACK_URL` | Legacy on-demand-TLS answerer consulted for names unknown to the domains registry. Empty refuses unknown names. | none | Optional |
|
|
| `domains-claim-window` | duration | `MC_DOMAINS_CLAIM_WINDOW` | How long a member has to publish the TXT challenge before a pending claim expires. | `24h` | Optional |
|
|
| `domains-pending-cap` | int | `MC_DOMAINS_PENDING_CAP` | Concurrent pending external domain verifications allowed per workspace. | `5` | Optional |
|
|
| `domains-abandon-budget` | int | `MC_DOMAINS_ABANDON_BUDGET` | Verifications a workspace may start and abandon under one domain scope, within the abandonment window, before further claims are refused. `0` disables the ledger. | `3` | Optional |
|
|
| `domains-abandon-window` | duration | `MC_DOMAINS_ABANDON_WINDOW` | Rolling window abandoned verifications are counted over. | `168h` | Optional |
|
|
| `domains-scope-labels` | int | `MC_DOMAINS_SCOPE_LABELS` | Trailing DNS labels forming the scope abandonments are counted under, so sibling names share one scope. | `2` | Optional |
|
|
| `domains-initiation-budget` | int | `MC_DOMAINS_INITIATION_BUDGET` | New external domain claims allowed per workspace per rolling 24h. `0` disables the cap. | `10` | Optional |
|
|
| `domains-expiry-sweep-interval` | duration | `MC_DOMAINS_EXPIRY_SWEEP_INTERVAL` | Interval between the Temporal-backed domain-claim expiry sweep (backstop for claims whose verification workflow is gone). | `15m` | Optional |
|
|
|
|
## Billing and maintenance sweeps
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `billing-sweep-interval` | duration | `MC_BILLING_SWEEP_INTERVAL` | Interval between billing scheduled-change sweeps (backstop firing path for due subscription changes). | `1h` | Optional |
|
|
| `webhook-partition-ensure-interval` | duration | `MC_WEBHOOK_PARTITION_ENSURE_INTERVAL` | Interval between `core.webhook_events` partition-ensure runs, keeping monthly partitions provisioned ahead of need. | `24h` | Optional |
|
|
|
|
## Stripe (optional; only for paid products)
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `stripe-api-key` (secret) | string | `MC_STRIPE_API_KEY` | Stripe secret API key. | none | Required together with `stripe-webhook-secret` to enable billing; both empty leaves it disabled |
|
|
| `stripe-api-key-file` | string | `MC_STRIPE_API_KEY_FILE` | File path holding `stripe-api-key`. | none | Optional |
|
|
| `stripe-webhook-secret` (secret) | string | `MC_STRIPE_WEBHOOK_SECRET` | Stripe webhook endpoint signing secret. | none | Required together with `stripe-api-key` |
|
|
| `stripe-webhook-secret-file` | string | `MC_STRIPE_WEBHOOK_SECRET_FILE` | File path holding `stripe-webhook-secret`. | none | Optional |
|
|
|
|
See [stripe.md](stripe.md) for the webhook endpoint, subscribed events, and catalog sync.
|
|
|
|
## FedWiki (optional; the FedWiki farm connection)
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `fedwiki-farm-api-url` | url | `MC_FEDWIKI_FARM_API_URL` | URL of the FedWiki farm's FarmManager API endpoint, e.g. `https://admin.wiki.example.com`. | none | Required together with `fedwiki-admin-token`; both empty leaves the farm connection disabled |
|
|
| `fedwiki-admin-token` (secret) | string | `MC_FEDWIKI_ADMIN_TOKEN` | FarmManager API admin access token. | none | Required together with `fedwiki-farm-api-url` |
|
|
| `fedwiki-admin-token-file` | string | `MC_FEDWIKI_ADMIN_TOKEN_FILE` | File path holding `fedwiki-admin-token`. | none | Optional |
|
|
| `fedwiki-allowed-domains` | list | `MC_FEDWIKI_ALLOWED_DOMAINS` | Domains where users can create FedWiki sites (comma-separated), e.g. `wiki.example.com,wiki2.example.com`. | none | Optional |
|
|
| `fedwiki-site-scheme` | http\|https | `MC_FEDWIKI_SITE_SCHEME` | URL scheme for generated FedWiki site links: `http` or `https`. | `https` | Optional |
|
|
| `fedwiki-sync-enabled` | bool | `MC_FEDWIKI_SYNC_ENABLED` | Enable periodic sync of sites from the FedWiki farm. | `false` | Optional |
|
|
| `fedwiki-sync-interval` | duration, greater than zero | `MC_FEDWIKI_SYNC_INTERVAL` | Interval between FedWiki site syncs. Inert while sync is disabled. | `1h` | Optional |
|
|
| `fedwiki-sync-trigger-immediately` | bool | `MC_FEDWIKI_SYNC_TRIGGER_IMMEDIATELY` | Run a sync immediately at startup, in addition to the interval. | `true` | Optional |
|
|
| `fedwiki-swap-cooldown` | duration | `MC_FEDWIKI_SWAP_COOLDOWN` | Cooldown window between FedWiki active-site rotations (swap or archive-then-restore). `0` disables it. | `720h` (30 days) | Optional |
|
|
|
|
See [fedwiki-setup.md](fedwiki-setup.md) for the FarmManager security stack and local dev setup.
|
|
|
|
## Discourse (optional)
|
|
|
|
| Key | Type | Env override | Purpose | Default | Required |
|
|
|---|---|---|---|---|---|
|
|
| `discourse-base-url` | url | `MC_DISCOURSE_BASE_URL` | Base URL of the Discourse instance, e.g. `https://forum.example.com`. | none | Required together with `discourse-api-key`; both empty leaves the integration dormant |
|
|
| `discourse-api-key` (secret) | string | `MC_DISCOURSE_API_KEY` | Discourse admin API key (global key, used with `discourse-api-username`). | none | Required together with `discourse-base-url` |
|
|
| `discourse-api-key-file` | string | `MC_DISCOURSE_API_KEY_FILE` | File path holding `discourse-api-key`. | none | Optional |
|
|
| `discourse-api-username` | string | `MC_DISCOURSE_API_USERNAME` | `Api-Username` header value for Discourse admin API calls. | `system` | Optional |
|
|
| `discourse-linkage-mode` | email\|oidc\|discourseconnect | `MC_DISCOURSE_LINKAGE_MODE` | How persons are matched to Discourse users: `email`, `oidc`, or `discourseconnect`. | `oidc` | Optional |
|
|
| `discourse-auto-create-users` | false\|true | `MC_DISCOURSE_AUTO_CREATE_USERS` | Create Discourse accounts for entitled persons with no forum user (`true`/`false`). The default only finds and links existing users. | `false` | Optional |
|
|
| `discourse-webhook-secret` (secret) | string | `MC_DISCOURSE_WEBHOOK_SECRET` | Shared secret verifying Discourse webhook signatures. Leave empty to disable the webhook endpoint. | none | Optional |
|
|
| `discourse-webhook-secret-file` | string | `MC_DISCOURSE_WEBHOOK_SECRET_FILE` | File path holding `discourse-webhook-secret`. | none | Optional |
|
|
| `discourse-sync-interval` | duration, greater than zero | `MC_DISCOURSE_SYNC_INTERVAL` | Interval between Discourse group-sync sweeps. | `15m` | Optional |
|
|
| `discourse-sync-trigger-immediately` | bool | `MC_DISCOURSE_SYNC_TRIGGER_IMMEDIATELY` | Run a group-sync sweep immediately at startup. | `false` | Optional |
|
|
|
|
See [discourse-setup.md](discourse-setup.md) for forum-side prerequisites and linkage-mode selection.
|
|
|
|
## Integration settings overlay
|
|
|
|
Every key above that is part of an installed integration's declared spec
|
|
(FedWiki, Stripe, Discourse) can also be reviewed and overridden after boot
|
|
from the operator Integration Settings UI, without touching `mc-config.yaml`
|
|
or the environment. An override an operator saves there is stored in
|
|
`core.integration_config_overrides` and layered over the environment's value
|
|
at the *next* boot (`internal/config.ApplyOverlay`); it is not read live, so
|
|
a saved override takes effect on restart. The overlay is read once, during
|
|
single-threaded boot, so an override that no longer parses has no running
|
|
value to fall back on and boot fails, naming the key. PostgreSQL's split,
|
|
fatal at start and ignored with a log entry at reload, is the rule to copy
|
|
if the overlay ever gains a reload path. Secret-marked keys are never
|
|
overridable from this UI; only their presence is shown, never their value
|
|
(`internal/config/validate.go:180`, `internal/config.RequiredKeysUnresolved`).
|
|
This page does not restate each integration's key list a second time for
|
|
the UI; the keys are the same ones documented above.
|
|
|
|
## Direct environment reads outside Viper
|
|
|
|
| Variable | Type | Purpose | Default |
|
|
|---|---|---|---|
|
|
| `LOG_LEVEL` | debug\|info\|warn\|error | Log verbosity, read directly via `os.Getenv` in `internal/logging/logging.go` (not a Viper/`MC_*` key). Accepts any value `slog.Level.UnmarshalText` parses (case-insensitive). An unparseable value panics at boot. | `info` |
|
|
|
|
## Test-only environment variables
|
|
|
|
`TEST_DATABASE_URL` is read directly (`os.Getenv`) by the Go test suite's
|
|
DB-backed tests; it is not a member-console application configuration key and
|
|
has no `MC_*` form. It, `E2E_DATABASE_URL`, and `POSTGRES_ADMIN_DSN` are part
|
|
of the test-stack contract documented in [`test/AGENTS.md`](../test/AGENTS.md)
|
|
and [`testing.md`](testing.md), not this reference.
|