Workspaces, group mappings and sign-in methods now come from a configuration file synchronized at startup; workspaces dropped from it are disabled, never deleted. Instance administrators (a new role) see and help every workspace and may delete disabled ones; workspace administrators and employees are derived from the user's groups at every SSO sign-in or administrator edit, and the employee permission set belongs to the workspace. Self-registration is gone: users come from SSO (only when their groups map) or from the administrator. Break-glass bootstrap accounts get an HMAC-derived hourly password that all replicas agree on. First SSO sign-in links a matching email contact to the user. The demo runs on the same model with a generated per-start password. Co-Authored-By: Claude <noreply@anthropic.com>
10 KiB
GKE and Cloud SQL
Target: GKE, Cloud SQL for PostgreSQL 17, and Cloud SQL Auth Proxy using Workload Identity. PostgreSQL is the durable queue and state store; no Redis is required. Templates are in deploy/. They have not been applied to a cloud project.
- Provision a private-IP Cloud SQL instance, database and application login; enable backups/PITR. Use a VPC-native GKE cluster with access to the database VPC. Configure Workload Identity and grant its Google service account Cloud SQL Client. Follow Google's GKE connection guide.
- Build
Dockerfile, push your image, and replace project, region, instance, release, service-account, image, and public URL placeholders. Review/pin the proxy image for your rollout; the template uses the version in Google's documented example. Native sidecars are required. Docker/GKE deployment must be validated in your environment. - Write the instance configuration (see below; start from
deploy/instance-config.example.yaml) into themeguri-instance-configConfigMap ingke.yaml. Create the Kubernetes service account and ConfigMaps fromgke.yaml, then themeguri-secretsSecret through your deployment secret manager. It needsDATABASE_URL=postgresql://USER:PERCENT_ENCODED_PASSWORD@127.0.0.1:5432/meguriand, when OIDC is configured,MEGURI_OIDC_CLIENT_SECRET. Proxy-to-database transport is encrypted; loopback stays inside the pod. Add tenant-specific JSM_EMAIL/_TOKENvariables only for configured tenants. Never commit rendered secrets. - Apply only
migrate.yaml, wait for its successful completion, then apply the Deployment, Service and PDB. The Job uses the same image withMEGURI_ROLE=migrateand mounts the same configuration: it runs the Cot migrations and then synchronizes the configuration (creates, adopts, renames, enables and disables workspaces, applies bootstrap accounts, recomputes every user's memberships). Replicas haveMEGURI_SKIP_MIGRATIONS=true; they read the configuration but never synchronize it, so a configuration change takes effect by running the Job for the release first and then restarting replicas. Do not race migrations during rollout. A bootstrap password, if enabled, is in the Job's log. - Add your internal HTTPS ingress.
MEGURI_PUBLIC_URLmust exactly match the browser origin (scheme, host, port); mutations, cookies and the OIDC redirect URI ({MEGURI_PUBLIC_URL}/api/oidc/callback) depend on it. The default Service is ClusterIP. There is no registration or manual account bootstrap: people appear when they sign in with SSO and their groups map to a workspace or instance role. For a first deployment before SSO works, list a bootstrap account (below), sign in with the logged password, fix the setup, then remove the block. Never enable demo seeding outside loopback. - Run health checks, a two-user replacement, a workspace-isolation check (an employee of one workspace cannot see another), a sign-in with a user whose groups map to nothing (expect the explanatory message and no new account), and the provider acceptance checklist in
testing.md. Enable real JSM integration only after preview and acceptance.
Instance configuration
Meguri refuses to start without MEGURI_CONFIG_FILE (every role; only demo mode uses a built-in file). The file is YAML; unknown fields and invalid values fail startup with a message. It is the only place where workspaces and the groups that grant access are defined; the application has no UI or API for creating workspaces or editing group mappings.
auth:
password_login: false # ordinary password accounts may sign in (default false)
bootstrap_admin: # optional break-glass instance administrators; omit in production
usernames: [rescue]
oidc: # optional; the client secret is MEGURI_OIDC_CLIENT_SECRET
issuer: https://sso.example.internal/realms/corp
client_id: meguri
scopes: [openid, profile, email] # default; must include openid
groups_claim: groups # default; dotted path, e.g. realm_access.roles
groups_sources: [id_token, userinfo] # default; searched in this order
instance_admin_groups: [/meguri/admins]
workspaces:
- key: payments # stable identifier, 3-64 chars: a-z 0-9 -
name: Payments # unique, 1-120 characters
admin_groups: [/payments/leads] # at least one; workspace administrators
member_groups: [/payments/team] # employees
At least one sign-in method (oidc, password_login or bootstrap_admin) is required, oidc.issuer must be HTTPS (loopback may use HTTP), and keys and names must be unique. Groups are compared by exact string: with Keycloak's Full group path the values look like /payments/leads, without it leads. A group in several workspaces is allowed; a person with both an administrator and an employee group of one workspace is an administrator.
Synchronization (run by the migrate Job, or by a single process without MEGURI_SKIP_MIGRATIONS) makes the database match the file:
- A configured key without a workspace creates it. A workspace that has no key and exactly the configured name is adopted; if several match, startup fails and nothing is changed. A different name renames the workspace as a normal revision.
- A workspace missing from the file is disabled, never deleted: nobody can open it (including open sessions), it is not published, its memberships and queued publication are removed, and its data stays. Listing it again re-enables it and queues a publication. If the file lists no workspaces while enabled ones exist, startup is refused unless
MEGURI_ALLOW_DISABLE_ALL=true; a mistyped key therefore creates a new empty workspace and disables the old one, and fixing the file restores both. - Disabled workspaces appear on the instance administrator's page. Deleting one is a deliberate, permanent action there (type its key; refused while it is configured or publishing). Remote provider resources it published are not removed.
- Every user's memberships are recomputed from the groups stored at their last sign-in (or set by an administrator), so changed group mappings apply to everyone at the release without waiting for logins. Group membership changes in the identity provider apply at the user's next SSO sign-in (sessions last 12 hours).
- Instance-level events (workspace created/adopted/renamed/enabled/disabled/deleted, bootstrap password set, password user created, groups changed, password reset) are written to
instance_audit.
Single sign-on with Keycloak
Create a confidential OpenID Connect client for Meguri: client authentication on, Standard flow only (no implicit, direct-access or service accounts), valid redirect URI {MEGURI_PUBLIC_URL}/api/oidc/callback, and put the generated secret into MEGURI_OIDC_CLIENT_SECRET. Request the openid, profile and email scopes; the email is used to link people already planned by email. Add a Group Membership mapper to the client with Add to ID token and Add to userinfo enabled (userinfo is the fallback source) and a token claim name equal to oidc.groups_claim (groups by default). Full group path decides whether values are /payments/leads or leads; the configuration must use exactly what the token contains. Keycloak does not send groups unless this mapper exists. Meguri verifies RS256 signatures against the realm's JWKS, so keep RS256 as the client's signature algorithm.
A sign-in whose token carries no groups claim is treated as having no groups; the log shows the identity provider returned no groups claim and the user sees the same explanation as for unmapped groups ("the instance could not map your groups") with a short reference. The log entry SSO sign-in was not completed carries that reference, the code (no_access, failed, unavailable) and the cause, including the groups that were received. Sign-out ends only the Meguri session; the identity provider session is untouched.
Bootstrap administrators
auth.bootstrap_admin.usernames creates the listed accounts as instance administrators at synchronization and logs one line per account: Bootstrap administrator enabled: sign in as NAME with password PASSWORD. The password is derived from a secret stored in the database, the username and the current UTC hour, so every process that synchronizes in the same hour logs the same password; it changes in the next hour and ends the account's sessions. Replicas started across an hour boundary can disagree, which a restart resolves. These accounts may sign in even when password_login is false. To recover from a broken SSO setup, add the block, run the migrate Job, read its log, sign in, repair, and remove the block: removal clears the password, ends sessions and revokes the rights while keeping the user record. Treat log access as administrator access while the block exists. A bootstrap name that belongs to an SSO account stops synchronization.
The default Deployment has two replicas serving both web and workers. Conditional database leases serialize each team's publication. Roles can later be split into web and worker deployments without changing the binary. On interruption, a completed remote step remains checkpointed; an unfinished lease expires after five minutes.
Monitor failed/conflict sync jobs, oldest last_success, and window_end approaching the current time. Meguri publishes a finite horizon and must keep running to extend it. Configure operational monitoring around these values; Meguri does not send incident notifications itself. Readiness checks PostgreSQL; liveness checks the process.
The current polling worker and history/audit reads target team-scale workloads. Benchmark and add query pagination/queue batching before broad multi-tenant SaaS rollout. Configure ingress-level rate limits in addition to per-process login throttling. Password recovery, session administration, history archival, and live provider acceptance are subsequent operational work.
Upgrading a database that used local accounts
Memberships are now derived entirely from user groups. The first configuration sync on an upgraded database recomputes every membership from the stored groups; users created before this change have no groups, so their old workspace memberships are removed until the instance administrator assigns groups (password users) or they sign in through SSO. Workspaces without a configuration key are adopted by exact name or disabled; none is deleted. Review the first sync log and the instance audit before opening the instance to users.