Files
member-console/docs/hosting.md
T
cgalo5758 94334c0e64 Document CSRF removal in favor of cross-origin checks
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.
2026-09-21 14:35:35 -05:00

90 lines
3.6 KiB
Markdown

---
title: "Hosting member-console"
audience: [admin]
summary: "Build the container image and generate the secrets needed to run a member-console instance."
---
# Hosting member-console
Operational steps for running your own member-console instance. For how the
console fits alongside a public site and identity provider, see
[deployment-architecture.md](deployment-architecture.md); for identity-provider
configuration, see [identity-provider-setup.md](identity-provider-setup.md).
## Building the image
The container image is built with Docker Buildx for both ARM64 and AMD64. Tag it
for your own registry:
```bash
docker buildx build \
--platform linux/arm64,linux/amd64 \
-t your-registry.example/your-org/member-console:latest \
-t your-registry.example/your-org/member-console:$(date +%Y-%m-%d) \
--push \
.
```
> The canonical image is published to `git.coopcloud.tech/wiki-cafe/member-console`.
> Substitute your own registry when self-hosting.
## Secrets
Four configuration keys are secrets, each with a `-file` variant that reads
the value from a file at startup (`docs/environment-reference.md`):
- `db-dsn` — the PostgreSQL connection string, password included.
- `oidc-sp-client-secret` — the OIDC client's secret, issued by the identity
provider.
- `valkey-password` — the session store's password. Required; the console
refuses to start without one.
- `temporal-oauth-client-secret` — only when Temporal is behind OAuth.
None of them is generated by or for the console: each is a credential for
something external, obtained from that system. Sessions are stored server-side
in Valkey and carry no signing secret of their own, and cross-origin request
protection issues no token, so there is no signing key for it either; rotating
any of the four means updating the deployment's configuration and restarting.
## Session store limits
Every key member-console writes to the session store carries a TTL: a
signed-in session lives for the deployment's configured lifetime, and a
pre-sign-in session, the one `/login` or `/register` creates to hold the
OIDC state, nonce, and PKCE verifier, lives fifteen minutes. A bare,
unauthenticated request no longer writes anything to the store. A flood of
`/login` or `/register` requests still writes one short-lived key per
request, so the store's memory is still a resource a flood can spend.
Set `maxmemory` on the Valkey instance to a bound sized for the deployment's
expected number of concurrent sessions, and set `maxmemory-policy` to
`volatile-lru`. Under that policy, once the bound is reached, Valkey evicts
the least recently used session first; that session can belong to a
signed-in person, and losing it signs them out, so they sign in again.
`noeviction`, Valkey's default, is worse: once `maxmemory` is reached it
rejects new writes instead, and a write rejected during `/login` or
`/callback` turns the flood into failed sign-ins for everyone hitting the
store, not just whichever session would have been evicted.
Rate limit `/login` and `/register` at the reverse proxy, since the memory
bound above caps damage but does not stop a flood from reaching the store in
the first place. With Caddy and the caddy-ratelimit plugin's `rate_limit`
directive, one zone covers both entry points:
```caddyfile
@signin path /login* /register*
handle @signin {
rate_limit {
zone signin {
key {remote_host}
events 20
window 1m
}
}
reverse_proxy member-console:8080
}
```
Any reverse proxy that can key a limit by client address and scope it to a
set of paths serves the same purpose.