Admin console

The admin console is the control plane: tenants, apps, ingest tokens, policies, and dashboards. The docker/admin stack ships one image carrying the control-plane API with the Ambilobe.Ui SPA bundled into its wwwroot.

Bring it up after the datastores and the ingest receiver. It needs Postgres for config, an OIDC issuer to authenticate operators, and optionally ClickHouse for dashboards and Redis for session replay.

One image, one origin #

Kestrel serves the SPA on / and the API on /v1 from one container on one port, so the SPA and the API share an origin. There is no separate web server to deploy.

Required configuration #

Three values are required, and Compose aborts the build or start if any is missing: the rum_config Postgres connection string, the OIDC authority, and the API audience.

docker/admin/.env bash
# docker/admin/.env
ADMIN_HTTP_PORT=8080   # serves both the SPA (/) and the API (/v1)

# Required: rum_config Postgres. Host `postgres` is the docker/pg
# service name on the shared `ambilobe` network.
ADMIN_CONFIGDB_CONNECTIONSTRING=Host=postgres;Port=5432;Database=rum_config;Username=ambilobe;Password=CHANGEME

# Required: OIDC issuer. Authority is the issuer URL; Audience is the
# API resource name the access-token `aud` claim must carry.
ADMIN_AUTH_AUTHORITY=https://identity.example.com
ADMIN_AUTH_AUDIENCE=https://identity.example.com/resources
ADMIN_AUTH_REQUIRE_HTTPS=true

OIDC issuer and the SPA client #

The API validates access tokens against ADMIN_AUTH_AUTHORITY, and the same authority is baked into the SPA bundle as its OIDC issuer, so the two never drift apart. The SPA's client settings are compiled into the bundle at image-build time, since a SPA has no server to read env vars at runtime. Re-run docker compose build to change them.

docker/admin/.env bash
# docker/admin/.env (SPA OIDC client, compiled into the bundle at build time)
ADMIN_UI_OIDC_CLIENT_ID=ambilobe-front
ADMIN_UI_OIDC_SCOPE=openid profile webapi offline_access
# Empty: the SPA calls /v1/* on its own origin (this same app).
ADMIN_UI_API_BASE_URL=

Schema migrations #

On startup the admin console applies every pending rum_config migration through RumConfigDbMigrator, so a fresh deploy needs no out-of-band dotnet ef database update. The target database must already exist; only its schema is created. Set ADMIN_CONFIGDB_AUTOMIGRATE=false to manage the schema yourself.

docker/admin/.env bash
# docker/admin/.env
# Apply pending rum_config migrations on startup (RumConfigDbMigrator).
# Set false to manage the schema with an out-of-band dotnet ef database update.
ADMIN_CONFIGDB_AUTOMIGRATE=true

Optional backends #

The console reads ClickHouse for dashboards and Redis for session replay. Both connection strings are optional; leave them empty until you need those features. The heartbeat secret lets registered ingest receivers report their live health into the admin UI and must match Ingest__Heartbeat__Secret on every receiver.

docker/admin/.env bash
# docker/admin/.env (optional backends)
# ClickHouse rum database, read by the dashboard endpoints.
ADMIN_CLICKHOUSE_CONNECTIONSTRING=
# Redis store backing session replay. Empty leaves those endpoints at 503.
ADMIN_REPLAY_REDIS_CONNECTIONSTRING=
# Ingest heartbeat shared secret. Must match Ingest__Heartbeat__Secret.
ADMIN_HEARTBEAT_SECRET=

Seed the first tenant #

Tenants are never created over HTTP, so once the stack is up an operator seeds the first tenant and its Owner from the command line. Pass the OIDC sub of the user who will own it.

Shell bash
# Tenants are never created over HTTP. Seed the first tenant and its
# Owner from inside the admin stack directory.
docker compose run --rm admin \
    bootstrap-tenant --slug acme --name "Acme Inc" --owner-sub "<oidc-sub>"

After that, manage tenants and members in the console. See Tenants & members.