docs: treat live net-kingdom user-engine manifests as the wrap source

user-engine already runs from net-kingdom/sso-mfa/k8s/user-engine.
The rapp absorbs those manifests (manifest-managed), keeps or
cutovers the dedicated CNPG cluster, and becomes the only apply home.
This commit is contained in:
tegwick 2026-08-18 12:21:49 +02:00
parent 56cc9099a3
commit 77452492ec
2 changed files with 103 additions and 58 deletions

View file

@ -456,7 +456,8 @@ to `rapp-<app>` and keep `railiance/app.toml` inside the rapp.
### 9.1 Database
Do not run a sidecar Postgres. Declare a consumer against `rapp-postgres`:
Do not run a sidecar Postgres. The default for a new app is a consumer
against `rapp-postgres`:
- boundary unit: `database-per-consumer`
- credential lane: `railiance-platform-credential-broker`
@ -472,6 +473,11 @@ If the app consumes a single `DATABASE_URL`, URL-encode the password.
Prefer discrete `POSTGRES_*` env vars when the framework allows it
(`railiance-apps/docs/django-on-railiance.md`).
A **dedicated CNPG Cluster** is allowed only when placement policy
already says so (live `user-engine-pg` is this case). Then the Cluster
manifests belong in the rapp, not in `net-kingdom` or the app repo. Do
not be both a `rapp-postgres` consumer and a dedicated Cluster.
### 9.2 Secrets
The rapp never stores values. Standing secrets go through a CCR and
@ -524,7 +530,7 @@ a grantable public surface.
### 10.1 Current facts
`user-engine` is a headless user-domain / identity-domain service with a
production WSGI portal. It already has:
production WSGI portal. The **app repo** already has:
- `Containerfile` — Python 3.12 slim, non-root uid `10001`, port `8080`
- `python -m user_engine.runtime`
@ -532,14 +538,40 @@ production WSGI portal. It already has:
- Postgres store + in-tree migrations
- fail-closed production adapters: OIDC, flex-auth, outbox, provisioning,
optional tenant-management and registration verification
- explicit non-ownership of IdP, authorization PDP, and secret custody
- explicit non-ownership of IdP, authorization PDP, secret custody, **and
deployment** (`docs/release.md`)
`rapp-user-engine` is an empty git repository (2026-08-11). ADR-0007
already lists it as undeclared.
`rapp-user-engine` is an empty Forgejo clone
(`forgejo-remote:coulomb/rapp-user-engine.git`). No README, no
declaration. ADR-0007 already lists it as undeclared.
Sister stubs `rapp-secrets-engine` and `rapp-tenant-engine` should follow
the same guide after user-engine proves the path. Do not batch-scaffold
all three before the first declaration validates.
**Live packaging is not in the rapp.** It lives in
`net-kingdom/sso-mfa/k8s/user-engine/`:
| File | Role |
| --- | --- |
| `runtime.yaml` | Namespace, dedicated CNPG `user-engine-pg`, Deployment, Service, SA, NetworkPolicies |
| `ingress.yaml` | Traefik Ingress `users.92-205-62-239.nip.io` (README still says no public Ingress until KeyCape PKCE is configured — treat exposure as unresolved) |
| `openbao-runtime.yaml` | ExternalSecrets from `platform/workloads/user-engine/runtime` |
| `backup.yaml`, `restore-drill.yaml` | Logical dump / restore |
| `verify-operability.sh` | Live checks |
Image already published:
`forgejo.coulomb.social/coulomb/user-engine@sha256:e3b5f65bafc1c0260dfdf2567a52766e67506ceb878a51759a2e9a307c4b5eb8`.
flex-auth for this consumer is pinned **inside** `flex-auth`
(`values/user-engine.yaml`, `examples/user-engine/`), not in a rapp.
Closest sibling analogue: `tenant-engine/deploy/` (digest-pinned
manifests, but that app *is* a `rapp-postgres` consumer).
The wrapper job is therefore **absorb and declare**, not invent a second
production target. Same rule as
`qonto-assistant/docs/knative-runtime-and-rapp-handoff.md`: once the
rapp exists, `net-kingdom` keeps IAM/provisioner contracts and stops
being the apply home.
Sister stubs `rapp-secrets-engine` and `rapp-tenant-engine` follow after
this path validates. `rapp-secrets-engine` is likely a grouping mistake
against `rapp-openbao` / S3, not a fourth identity member.
### 10.2 Recommended decisions
@ -548,12 +580,12 @@ all three before the first declaration validates.
| Create a rapp? | yes | stable identity, clear app/wrapper split, own rollout fate |
| `ownership_repo` | `user-engine` | application accountability, same pattern as `policy-nexus` / `qonto-assistant` |
| `workload_identity.name` | `user-engine` | schema rule |
| `package_type` | `helm-managed-platform-service` | only Helm-shaped enum available today |
| `package_type` | `manifest-managed-platform-service` | live assets are raw YAML/CNPG, not a Helm chart. Do not invent Helm just to match policy-nexus. |
| `primary_rail` | `rail-kubernetes` | long-running portal + durable store; no Knative need |
| `bound_reefs` | `[]` at `declared`; `[reef-railiance]` when placed | empty is legal and means "not yet placed" |
| `data_classification` | `confidential` | identity and account data |
| `criticality` | `high` | shared identity-domain service; not the secrets/postgres critical path |
| `exposure` | omit (private) | operator tunnel later if needed; public grant only with a hostname and residual-risk owner |
| `exposure` | omit (private) until the KeyCape PKCE edge is decided | an Ingress object already exists; that is **not** a grant (ADR-0008). Decide hostname + residual-risk owner before `public`. |
| Granularity | single-repo rapp **for the pilot** | one deployable; do not group with tenant-engine or secrets-engine unless they share rollback fate. `rapp-first-wave-candidates.md` still flags a future grouped `rapp-netkingdom-identity` (user-engine + tenant-engine + key-cape + flex-auth). That grouping is only legal if those members cannot roll back independently — they almost certainly can. Treat the grouped name as a later composition question, not as a reason to leave `rapp-user-engine` undeclared. |
### 10.3 Draft declaration (index, not authority)
@ -572,9 +604,7 @@ contract_version: 1.0.0
readiness_state: draft
workload_identity:
name: user-engine
package_type: helm-managed-platform-service
chart: rapp-user-engine/user-engine
chart_version: 0.1.0
package_type: manifest-managed-platform-service
app_version: 0.1.0
data_classification: confidential
criticality: high
@ -586,28 +616,28 @@ runtime_dependencies:
- kubernetes-api
- helm-apply-path
- forgejo-oci-registry
- openbao-database-secrets-engine
- pvc-storage
- keycape-oidc
- flex-auth
- openbao
- audit-core
- tenant-engine
secret_references:
- platform/workloads/user-engine/runtime
composition:
purpose: Package and operate the user-engine identity-domain service on Railiance.
member_repos:
- repo: rapp-user-engine
role: Helm package, smoke, and rollback for the user-engine runtime
role: Kubernetes manifests, smoke, and rollback for the user-engine runtime
deployables:
- user-engine
- repo: user-engine
role: application source, image, and schema migrations
deployables:
- user-engine-migrate
upstream_components:
- name: user-engine
source: forgejo.coulomb.social/<org>/user-engine
version: sha256:<pin-at-first-release>
source: forgejo.coulomb.social/coulomb/user-engine
version: sha256:e3b5f65bafc1c0260dfdf2567a52766e67506ceb878a51759a2e9a307c4b5eb8
rollout_contract:
default_mode: helm-upgrade-install
default_mode: kubectl-server-side-apply
commands:
- make render
- make server-dry-run
- make deploy
production_gate: reef-production-approved
@ -617,15 +647,15 @@ smoke_contract:
- readyz-ok-when-store-ready
- metrics-requires-proxy-marker
- local-issuer-rejected-in-production
- postgres-consumer-database-present
- live-image-digest-match
commands:
- make verify-live
rollback_contract:
order:
- previous-helm-revision
- previous-immutable-image-digest
- apply-reviewed-git-revision
commands:
- make rollback REVISION=<previous-helm-revision>
- make rollback DIGEST=<previous-image-digest>
source_documents:
- repo: user-engine
path: INTENT.md
@ -633,22 +663,25 @@ source_documents:
path: docs/configuration.md
- repo: user-engine
path: docs/operability.md
- repo: user-engine
path: docs/postgres-durable-store-consumer-requirements.md
- repo: net-kingdom
path: sso-mfa/k8s/user-engine/README.md
- repo: railiance-master
path: schemas/rapp.schema.json
- repo: repo-manager
path: docs/RailianceAppDeploymentGuide.md
```
Cardinality note: `user-engine` the *Deployment* belongs to this rapp.
`user-engine-migrate` is a Job owned by the same rapp. Do not also list
the Deployment under another rapp.
Cardinality note: the live deployable name is `user-engine` (the
Deployment). Migrations run in-process before serve, so do **not**
invent a second `user-engine-migrate` deployable unless a separate Job
is added. `user-engine-pg` is a Cluster owned by this package only if
the dedicated-cluster decision stands; if it is inventoried as its own
deployable it still belongs to **this** rapp.
If the migrate Job is considered the same deployable as the app, collapse
to a single `user-engine` deployable and treat migration as a rollout
step, not a second live unit. Prefer the single-deployable form unless
the Job must be inventoried separately for the coverage check.
`ownership_repo: user-engine` already names the source repo. Do not also
claim the Deployment from `net-kingdom`. A single-member composition
matches every live rapp; add `user-engine` as a second member only if it
contributes a *different* deployable.
### 10.4 Runtime inputs the chart must accept
@ -681,26 +714,32 @@ Logical secret names the app already documents: `database.url`,
in `railiance/app.toml`; do not invent a second naming scheme in the
chart.
### 10.5 Postgres consumer (to be added in `rapp-postgres`)
### 10.5 Database decision (do not do both)
Sketch only — the owning package is `rapp-postgres`:
Live user-engine uses a **dedicated** CNPG Cluster `user-engine-pg`
(postgresql 17.5, db `user_engine`), not a `rapp-postgres` consumer.
tenant-engine and audit-core *are* platform consumers.
```yaml
apiVersion: rapp-postgres.railiance.io/v1alpha1
kind: PostgresConsumer
metadata:
name: user-engine
spec:
database: user_engine
schema: user_engine
costAttributionKey: platform:user-engine
clientNamespaces: [user-engine]
roles:
owner: user_engine_owner
migration: user_engine_migrate
runtime: user_engine_app
tenantKeyingRequired: true
```
Two legal next states:
1. **Keep dedicated.** Move `user-engine-pg` manifests into the rapp.
Declare `pvc-storage` (and later `s3-backup-target` if backup is
attached). Do not add a postgres consumer.
2. **Become a consumer.** Add `rapp-postgres/consumers/user-engine.yaml`
(same shape as `audit-core.yaml`), delete the dedicated Cluster after
a reviewed cutover. Then drop `pvc-storage` from this rapp.
Pick one in T04. The current live state is (1).
### 10.6 Absorb the NetKingdom apply home
T03 should copy
`net-kingdom/sso-mfa/k8s/user-engine/` into
`rapp-user-engine/manifests/` (or `helm/` only if a real chart is
justified), add Makefile `server-dry-run` / `deploy` / `verify-live` /
`rollback DIGEST=…`, and leave a pointer README in NetKingdom so there
is one apply path. Do not Helm-rewrite the live YAML in the same
change.
### 10.6 What stays in `user-engine`
@ -900,3 +939,6 @@ Worked example:
- `user-engine/docs/postgres-durable-store-consumer-requirements.md`
- `user-engine/Containerfile`
- `user-engine/src/user_engine/runtime.py`
- `net-kingdom/sso-mfa/k8s/user-engine/` (current live apply home)
- `tenant-engine/deploy/` (closest sibling manifest package)
- `qonto-assistant/docs/knative-runtime-and-rapp-handoff.md` (one apply home)

View file

@ -77,12 +77,15 @@ priority: high
state_hub_task_id: "3ba6828e-7514-4f4d-a1d3-54134e039f55"
```
Add the Helm chart, Makefile operator targets, `railiance/app.toml`, and
runtime-contract doc described in the guide, derived from `user-engine`
port, health paths, Containerfile user, and documented env/secret names.
Absorb `net-kingdom/sso-mfa/k8s/user-engine/` into
`rapp-user-engine/manifests/` (or a Helm chart only if a rewrite is
justified separately). Add Makefile `server-dry-run` / `deploy` /
`verify-live` / `rollback DIGEST=…` and a runtime-contract doc. Leave a
pointer in NetKingdom so there is one apply path.
`make render` and `make lint` must pass with dummy digests. No cluster
deploy in this task.
`make server-dry-run` must pass. No production apply in this task.
Package type is `manifest-managed-platform-service` unless the rewrite
to Helm is an explicit later decision.
## Draft platform handoffs