2026-09-24 14:35:05 +03:00
|
|
|
# Render placeholders before use. Apply the migration Job first, wait for completion,
|
|
|
|
|
# then apply this file. See docs/deployment.md for IAM, secrets and ingress.
|
|
|
|
|
apiVersion: v1
|
|
|
|
|
kind: ServiceAccount
|
|
|
|
|
metadata:
|
|
|
|
|
name: meguri
|
|
|
|
|
annotations:
|
|
|
|
|
iam.gke.io/gcp-service-account: MEGURI_GSA@PROJECT_ID.iam.gserviceaccount.com
|
|
|
|
|
---
|
|
|
|
|
apiVersion: v1
|
|
|
|
|
kind: ConfigMap
|
2026-10-07 13:28:43 +01:00
|
|
|
metadata:
|
|
|
|
|
name: meguri-instance-config
|
|
|
|
|
data:
|
|
|
|
|
# Declarative instance configuration (see deploy/instance-config.example.yaml for the commented
|
|
|
|
|
# reference and docs/deployment.md). Replace the placeholders. The migrate Job synchronizes it;
|
|
|
|
|
# replicas only read it. Change it, run the migrate Job for the release, then restart replicas.
|
|
|
|
|
config.yaml: |
|
|
|
|
|
auth:
|
|
|
|
|
# Lets ordinary password accounts (created by an instance administrator) sign in.
|
|
|
|
|
# Keep false in production where everyone signs in through SSO.
|
|
|
|
|
password_login: false
|
|
|
|
|
|
|
|
|
|
# Break-glass instance administrators. Omit this whole block in production; add it only to get
|
|
|
|
|
# back in when SSO is broken, then remove it again. While present, the generated password is
|
|
|
|
|
# written to the log of the process that synchronized the configuration (the migrate Job), and
|
|
|
|
|
# anyone who can read that log can sign in as an instance administrator. The password is derived
|
|
|
|
|
# from a database-held secret, the username and the current UTC hour; it rotates every hour.
|
|
|
|
|
# bootstrap_admin:
|
|
|
|
|
# usernames: [rescue]
|
|
|
|
|
|
|
|
|
|
# OpenID Connect provider. Confidential client, authorization-code flow with PKCE.
|
|
|
|
|
# Redirect URI to register: {MEGURI_PUBLIC_URL}/api/oidc/callback
|
|
|
|
|
oidc:
|
|
|
|
|
issuer: https://sso.example.internal/realms/corp # HTTPS; discovery is read from <issuer>/.well-known/openid-configuration
|
|
|
|
|
client_id: meguri
|
|
|
|
|
scopes: [openid, profile, email] # default; must include openid; email links people planned by email
|
|
|
|
|
# Where the groups are. A dotted path into the token claims (for example realm_access.roles).
|
|
|
|
|
# Keycloak: add a "Group Membership" mapper with this claim name, "Add to ID token" and
|
|
|
|
|
# "Add to userinfo" on. Matching below is by exact string, so "Full group path" decides
|
|
|
|
|
# whether values look like /payments/leads or leads.
|
|
|
|
|
groups_claim: groups
|
|
|
|
|
groups_sources: [id_token, userinfo] # searched in this order
|
|
|
|
|
|
|
|
|
|
# Groups whose members are instance administrators: they see and may act in every enabled
|
|
|
|
|
# workspace, manage password users and delete disabled workspaces. Not tied to any workspace.
|
|
|
|
|
instance_admin_groups:
|
|
|
|
|
- /meguri/admins
|
|
|
|
|
|
|
|
|
|
# Workspaces. `key` is the permanent identifier (3-64 characters: a-z, 0-9, hyphen); never reuse
|
|
|
|
|
# it for something else. `name` is unique and is what people see; changing it renames the
|
|
|
|
|
# workspace. A workspace that exists without a key and has exactly this name is adopted.
|
|
|
|
|
# Removing an entry here DISABLES the workspace (nobody can open it, nothing is published, data is
|
|
|
|
|
# kept); listing it again re-enables it. Deleting it for good is a manual action on the
|
|
|
|
|
# instance administrator's page.
|
|
|
|
|
workspaces:
|
|
|
|
|
- key: payments
|
|
|
|
|
name: Payments
|
|
|
|
|
# Workspace administrators (team leads): every permission in this workspace only. At least one group.
|
|
|
|
|
admin_groups:
|
|
|
|
|
- /payments/leads
|
|
|
|
|
# Employees: the workspace's employee permission set, by default calendar, schedule editing
|
|
|
|
|
# and replacement requests. Administrators change that set in the workspace settings.
|
|
|
|
|
member_groups:
|
|
|
|
|
- /payments/team
|
|
|
|
|
|
|
|
|
|
- key: customer-support
|
|
|
|
|
name: Customer Support
|
|
|
|
|
admin_groups:
|
|
|
|
|
- /support/leads
|
|
|
|
|
member_groups:
|
|
|
|
|
- /support/team
|
|
|
|
|
- /support/contractors
|
|
|
|
|
---
|
|
|
|
|
apiVersion: v1
|
|
|
|
|
kind: ConfigMap
|
2026-09-24 14:35:05 +03:00
|
|
|
metadata:
|
|
|
|
|
name: meguri-config
|
|
|
|
|
data:
|
|
|
|
|
MEGURI_BIND: 0.0.0.0:3000
|
|
|
|
|
MEGURI_PUBLIC_URL: https://meguri.example.internal
|
|
|
|
|
MEGURI_ROLE: all
|
|
|
|
|
MEGURI_SKIP_MIGRATIONS: "true"
|
2026-10-07 13:28:43 +01:00
|
|
|
MEGURI_CONFIG_FILE: /etc/meguri/config.yaml
|
2026-09-24 14:35:05 +03:00
|
|
|
MEGURI_SEED_DEMO: "false"
|
|
|
|
|
RUST_LOG: meguri=info,cot=warn
|
|
|
|
|
---
|
|
|
|
|
apiVersion: apps/v1
|
|
|
|
|
kind: Deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: meguri
|
|
|
|
|
spec:
|
|
|
|
|
replicas: 2
|
|
|
|
|
selector:
|
|
|
|
|
matchLabels: {app: meguri}
|
|
|
|
|
template:
|
|
|
|
|
metadata:
|
|
|
|
|
labels: {app: meguri}
|
|
|
|
|
spec:
|
|
|
|
|
serviceAccountName: meguri
|
|
|
|
|
terminationGracePeriodSeconds: 60
|
|
|
|
|
securityContext:
|
|
|
|
|
runAsNonRoot: true
|
|
|
|
|
seccompProfile: {type: RuntimeDefault}
|
|
|
|
|
initContainers:
|
|
|
|
|
- name: cloud-sql-proxy
|
|
|
|
|
restartPolicy: Always
|
|
|
|
|
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.1
|
|
|
|
|
args: ["--private-ip", "--structured-logs", "--port=5432", "--health-check", "--http-address=0.0.0.0", "--http-port=9090", "PROJECT_ID:REGION:INSTANCE"]
|
|
|
|
|
securityContext:
|
|
|
|
|
allowPrivilegeEscalation: false
|
|
|
|
|
capabilities: {drop: [ALL]}
|
|
|
|
|
resources:
|
|
|
|
|
requests: {cpu: 100m, memory: 128Mi}
|
|
|
|
|
limits: {memory: 256Mi}
|
|
|
|
|
startupProbe:
|
|
|
|
|
httpGet: {path: /startup, port: 9090}
|
|
|
|
|
periodSeconds: 2
|
|
|
|
|
failureThreshold: 30
|
2026-10-07 13:28:43 +01:00
|
|
|
volumes:
|
|
|
|
|
- name: instance-config
|
|
|
|
|
configMap: {name: meguri-instance-config}
|
2026-09-24 14:35:05 +03:00
|
|
|
containers:
|
|
|
|
|
- name: meguri
|
|
|
|
|
image: REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY/meguri:RELEASE
|
|
|
|
|
ports: [{name: http, containerPort: 3000}]
|
|
|
|
|
envFrom:
|
|
|
|
|
- configMapRef: {name: meguri-config}
|
|
|
|
|
- secretRef: {name: meguri-secrets}
|
2026-10-07 13:28:43 +01:00
|
|
|
env:
|
|
|
|
|
- name: MEGURI_OIDC_CLIENT_SECRET
|
|
|
|
|
valueFrom:
|
|
|
|
|
secretKeyRef: {name: meguri-secrets, key: MEGURI_OIDC_CLIENT_SECRET, optional: true}
|
|
|
|
|
volumeMounts:
|
|
|
|
|
- {name: instance-config, mountPath: /etc/meguri, readOnly: true}
|
2026-09-24 14:35:05 +03:00
|
|
|
securityContext:
|
|
|
|
|
readOnlyRootFilesystem: true
|
|
|
|
|
allowPrivilegeEscalation: false
|
|
|
|
|
capabilities: {drop: [ALL]}
|
|
|
|
|
resources:
|
|
|
|
|
requests: {cpu: 250m, memory: 256Mi}
|
|
|
|
|
limits: {memory: 512Mi}
|
|
|
|
|
startupProbe:
|
|
|
|
|
httpGet: {path: /health/ready, port: http}
|
|
|
|
|
periodSeconds: 2
|
|
|
|
|
failureThreshold: 60
|
|
|
|
|
readinessProbe:
|
|
|
|
|
httpGet: {path: /health/ready, port: http}
|
|
|
|
|
livenessProbe:
|
|
|
|
|
httpGet: {path: /health/live, port: http}
|
|
|
|
|
---
|
|
|
|
|
apiVersion: v1
|
|
|
|
|
kind: Service
|
|
|
|
|
metadata:
|
|
|
|
|
name: meguri
|
|
|
|
|
spec:
|
|
|
|
|
type: ClusterIP
|
|
|
|
|
selector: {app: meguri}
|
|
|
|
|
ports: [{port: 80, targetPort: http}]
|
|
|
|
|
---
|
|
|
|
|
apiVersion: policy/v1
|
|
|
|
|
kind: PodDisruptionBudget
|
|
|
|
|
metadata:
|
|
|
|
|
name: meguri
|
|
|
|
|
spec:
|
|
|
|
|
minAvailable: 1
|
|
|
|
|
selector:
|
|
|
|
|
matchLabels: {app: meguri}
|