Add the §0 playbook and kubernetes-then-knative gate to the guide. Implement rmgr rapp skeleton/wrap/place, draft postgres consumers, and copy the fleet image workflow when missing.
1047 lines
44 KiB
Markdown
1047 lines
44 KiB
Markdown
# Railiance App Deployment Guide
|
|
|
|
Best-practice guide for wrapping an ordinary application as a Railiance
|
|
managed workload (`rapp-*`) so Repo Manager can later scaffold the same
|
|
shape for `rapp-some-app`.
|
|
|
|
This document is a practice and work-structure guide. It does **not**
|
|
restate the family schemas. Where a field list and a schema disagree, the
|
|
schema wins:
|
|
|
|
- [`railiance-master/schemas/rapp.schema.json`](../../railiance-master/schemas/rapp.schema.json)
|
|
- [`railiance-master/docs/adr/ADR-0007-rapp-declaration-contract.md`](../../railiance-master/docs/adr/ADR-0007-rapp-declaration-contract.md)
|
|
- [`railiance-master/docs/repo-family-bootstrap-contract.md`](../../railiance-master/docs/repo-family-bootstrap-contract.md)
|
|
|
|
Worked example (absorb path): `user-engine` → `rapp-user-engine` (§10).
|
|
Greenfield default: `rmgr rapp wrap` (§0). Rail choice: §3.1.
|
|
|
|
`RMGR-WP-0006` proved the contract. `RMGR-WP-0007` is the wrap path.
|
|
|
|
---
|
|
|
|
## 0. Greenfield playbook
|
|
|
|
Use this section to wrap an app that has **no rapp yet**. Do not start in
|
|
§10 unless you are absorbing an existing apply home.
|
|
|
|
```text
|
|
decide (§4, §3.1) → rmgr rapp wrap → human reviews drafts
|
|
→ CI publishes a digest → rmgr rapp pin-image
|
|
→ rmgr rapp place → operator deploy
|
|
```
|
|
|
|
```bash
|
|
rmgr rapp wrap \
|
|
--path ../rapp-<app> \
|
|
--app <app> \
|
|
--ownership-repo <app> \
|
|
--from-app ../<app>
|
|
|
|
rmgr rapp validate --path ../rapp-<app> --family-root ..
|
|
# after CI prints a digest:
|
|
rmgr rapp pin-image --path ../rapp-<app> --digest sha256:<64 hex>
|
|
# only when you intend to bind compute:
|
|
rmgr rapp place --path ../rapp-<app> --reef reef-railiance
|
|
```
|
|
|
|
`wrap` runs init → skeleton (or absorb `deploy/`) → app `image.yaml` if
|
|
missing → postgres consumer **draft** → validate. It stops. It does not
|
|
set `bound_reefs`, apply to a cluster, or grant public exposure.
|
|
|
|
Defaults:
|
|
|
|
| Decision | Default |
|
|
| --- | --- |
|
|
| Rail | `rail-kubernetes` (§3.1). Not Knative. |
|
|
| Package type | `manifest-managed-platform-service`. Helm only if a chart already exists. |
|
|
| Database | `rapp-postgres` consumer draft. No dedicated Cluster. |
|
|
| Image | fleet `.forgejo/workflows/image.yaml` in the **app** repo. Pin by digest in the rapp. |
|
|
| `railiance/app.toml` | optional; not part of the first wrap |
|
|
| Exposure | omitted (private) |
|
|
|
|
Still human: the purpose sentence, classification/criticality, NetworkPolicy
|
|
review, CCR / consumer **apply**, production gate, any public grant.
|
|
|
|
---
|
|
|
|
## 1. Purpose
|
|
|
|
Repo Manager should make it easy to set up a managed Railiance application
|
|
wrapper. The product of that setup is a `rapp-<app>` repository: a
|
|
packaging and operations home that can deploy, verify, roll back, and bind
|
|
the app onto a rail and a reef.
|
|
|
|
This guide answers:
|
|
|
|
1. How to wrap a new app from scratch (§0).
|
|
2. Which rail to start on, and when Knative is justified (§3.1).
|
|
3. What a managed Railiance application **is**.
|
|
4. Which files, declarations, and operator surfaces a wrapper needs.
|
|
5. How `user-engine` was absorbed as `rapp-user-engine` (§10).
|
|
|
|
`RMGR-WP-0006` recorded the contract and the first wrap.
|
|
`RMGR-WP-0007` is the greenfield command path.
|
|
|
|
---
|
|
|
|
## 2. Vocabulary
|
|
|
|
Railiance classifies repositories on four complementary axes
|
|
(`railiance-master/docs/repository-axes.md`, ADR-0001):
|
|
|
|
| Prefix | Axis | Question it answers |
|
|
| --- | --- | --- |
|
|
| `railiance-*` | ownership | Who owns this capability or layer? |
|
|
| `rail-*` | execution contract | How does a workload run here? |
|
|
| `rapp-*` | managed workload package | What is packaged and operated? |
|
|
| `reef-*` | substrate | Where does it run, and what is bound there? |
|
|
|
|
A `rapp` is **managed packaging, not ownership**. The application repo
|
|
keeps domain logic, tests, image build, and schema migrations. The rapp
|
|
owns Helm/manifests, rail compatibility, smoke and rollback, secret
|
|
*references*, and reef binding. The architecture term is *managed
|
|
workload package*; "service wrapper" is informal.
|
|
|
|
Do not derive a rapp from a Forgejo organization or a State Hub domain.
|
|
Rapp context is its own dimension (ADR-0007):
|
|
|
|
- repos ↔ rapps is **many-to-many**
|
|
- deployables ↔ rapps is **one-to-one**
|
|
|
|
Grouping is legitimate only where members share rollout and rollback fate.
|
|
|
|
---
|
|
|
|
## 3. Current inventory (2026-08-18)
|
|
|
|
### 3.1 Rails
|
|
|
|
| Rail | Kind | Architecture | Readiness | Use when |
|
|
| --- | --- | --- | --- | --- |
|
|
| `rail-kubernetes` | base | Kubernetes | verified | Default for platform services **and** managed applications |
|
|
| `rail-knative` | derived from `rail-kubernetes` | Knative Serving on Kubernetes | rail.yaml `verified`; reef binding not production-approved | Scale-to-zero, revision traffic, request activation (today: Qonto). Serving 1.22.0 is installed on Railiance01; single-node control plane still blocks production approval. |
|
|
|
|
This is a **default plus a specialization gate**, not a maturity ladder.
|
|
An app does not graduate from Kubernetes to Knative.
|
|
|
|
```text
|
|
rail-kubernetes ← start here, stay here unless proven otherwise
|
|
│
|
|
└── rail-knative ← only if the execution model itself must change
|
|
```
|
|
|
|
`rail-kubernetes` is the base rail. `rail-knative` is derived from it:
|
|
same identity, smoke, promotion vocabulary, and recovery; it only
|
|
overrides activation, scale-to-zero, concurrency, revision traffic,
|
|
cold-start, and revision rollback
|
|
(`railiance-master/docs/rail-composition-contract.md`).
|
|
|
|
There is no next rail after Knative. Names such as `rail-keda`,
|
|
`rail-fission`, and `rail-nuclio` are examples of *when a new rail would
|
|
be justified*, not a planned path. Do not invent a rail for an adapter
|
|
or a values profile.
|
|
|
|
Do not mix rail choice with the other progressions:
|
|
|
|
| Sequence | What it is |
|
|
| --- | --- |
|
|
| `draft` → `declared` → `deployed` → `verified` | package readiness |
|
|
| omitted / `private` → `operator` → `public` | who may reach the listener |
|
|
| kubernetes → knative | **how the process is executed** |
|
|
|
|
**Stay on Kubernetes when** the app is a long-running portal or API; it
|
|
has a durable store, in-process migrations, or startup that cannot die
|
|
between requests; ordinary Deployment rolling updates and digest
|
|
rollback are enough; “scale down at night” can be an HPA / replica
|
|
count; or no cold-start SLO has been measured.
|
|
|
|
**Move to Knative only if all of these hold:**
|
|
|
|
1. The semantics are intrinsic: request activation, scale-to-zero,
|
|
revision traffic splits, or previous-revision rollback — not just
|
|
fewer replicas.
|
|
2. A Kubernetes profile cannot say it safely. If a Deployment + Service
|
|
+ HPA is enough, it is not a new rail.
|
|
3. The workload can die between requests. No migration-on-boot that must
|
|
finish before the first probe; no sticky in-memory session that
|
|
cannot cold-start.
|
|
4. Callers tolerate cold-start. Measure activator/buffering timeouts,
|
|
concurrency, and retry ownership before those numbers are
|
|
load-bearing (`docs/qonto-knative-runtime-contract.md`).
|
|
5. Egress is restricted. Unrestricted HTTPS egress is not
|
|
production-approved for critical Knative workloads.
|
|
6. The reef will admit it. Knative Serving is installed and *verified*
|
|
on Railiance01; **production approval is still blocked** (single-node
|
|
control plane). Today only `rapp-qonto` uses this rail.
|
|
|
|
Qonto is the worked example: internet-reachable, scales to zero, holds a
|
|
bank credential, needs revision canaries. user-engine stays on
|
|
Kubernetes — long-running portal plus Postgres.
|
|
|
|
New wraps set `primary_rail: rail-kubernetes`. Change it only when the
|
|
app owner can write: *this process must not exist until a request
|
|
arrives, and rollback is a previous Knative revision, not a previous
|
|
image digest.* If that sentence needs “and also it has a database that
|
|
migrates on start,” keep Kubernetes.
|
|
|
|
`rail-kubernetes` also owns the generic staged-promotion contract
|
|
`railiance/app.toml` (Stage 1 local, Stage 2 canary, Stage 3 promote) and
|
|
the compatibility overlay-repo pattern. That overlay is the
|
|
migration-era wrapper. `app.toml` is **not** required on the first wrap.
|
|
|
|
### 3.2 Reefs
|
|
|
|
| Reef | Kind | State | What it is |
|
|
| --- | --- | --- | --- |
|
|
| `reef-railiance` | `server-group` | active | Home compute reef (`Railiance01`). Hosts `rail-kubernetes` and `rail-knative`. Public host ports 80/443 (and 2224 for the provider agent) are granted. |
|
|
| `reef-storage` | `object-storage` | planned | Provider-delegated S3 (Scaleway). Hosts no rail. Backup target, not a compute placement. |
|
|
|
|
A rapp names its placement in `bound_reefs`. The reef's `bound_rapps` is a
|
|
**derived projection** of that field, not a hand list.
|
|
|
|
Topology is not readiness (ADR-0006). Binding a rapp to a reef does not
|
|
admit it to production and does not make it public (ADR-0008).
|
|
|
|
### 3.3 Live and stub rapps
|
|
|
|
| Repo | Declaration | Package type | Rail | Ownership repo | Readiness | Notes |
|
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
| `rapp-openbao` | yes | helm-managed-platform-service | kubernetes | `railiance-platform` | verified | Third-party platform service. Proven S3 pattern. |
|
|
| `rapp-postgres` | yes | helm-managed-platform-service | kubernetes | `railiance-platform` | verified | Shared CNPG + per-consumer surface. Apps consume this; they do not run their own Postgres. |
|
|
| `rapp-qonto` | yes, pre-schema drift | knative-managed-service (implied) | knative | `qonto-assistant` | verified | First-party Knative app. Missing `composition`, `bound_reefs`, `package_type`; `workload_identity.name` is still `rapp-qonto`. |
|
|
| `rapp-policy-nexus` | yes | helm-managed-platform-service | kubernetes | `policy-nexus` | declared | Closest first-party Helm app wrapper. Public grant at `policy.coulomb.social`. |
|
|
| `rapp-user-engine` | yes | manifest-managed-platform-service | kubernetes | `user-engine` | verified | First-party absorb wrap. Apply home for the portal. |
|
|
| `rapp-secrets-engine` | **no** | — | — | — | stub | README only. Same family as user-engine. |
|
|
| `rapp-tenant-engine` | **no** | — | — | — | stub | README only. Same family. |
|
|
| `rapp-vergabe-teilnahme` | never built | — | — | — | — | Planned user-facing proof; workload still lives in `railiance-apps`. |
|
|
| `rapp-forgejo` | not built | — | — | — | — | Deferred; forge still mixed into `railiance-forge` / `railiance-apps`. |
|
|
|
|
The platform-service wrapper is proven twice. The first-party application
|
|
wrapper is proven once as an absorb (`rapp-user-engine`) and once as a
|
|
static Helm site (`rapp-policy-nexus`). `rapp-qonto` is drifted.
|
|
`rapp-vergabe-teilnahme` was never extracted. `rapp-tenant-engine` is
|
|
the greenfield/absorb pilot for `RMGR-WP-0007`.
|
|
|
|
### 3.4 Ownership repos that still hold wrappers
|
|
|
|
`railiance-apps` is still the S5 release surface for workloads that have
|
|
not been extracted (`vergabe-teilnahme`, `coulomb-social`, `core-hub`,
|
|
`reuse-surface`, `inter-hub`, Forgejo values). Its
|
|
`docs/s5-app-onboarding-checklist.md` remains the operator recipe for
|
|
Helm, probes, landing pages, and image pins. New first-class wrappers
|
|
should **copy those recipes into a `rapp-*` repo**, not add another chart
|
|
to `railiance-apps`.
|
|
|
|
When a rapp already exists, S5 becomes a thin delegate rather than a
|
|
second chart. `railiance-apps` `make policy-nexus-deploy` calls
|
|
`make -C ../rapp-policy-nexus deploy`. Production *approval* may still
|
|
be recorded in `railiance-apps` bindings; the package and apply path
|
|
belong to the rapp.
|
|
|
|
`railiance-platform` retains cross-workload policy, credential custody,
|
|
lane approval, and the postgres credential broker.
|
|
|
|
`railiance-fabric` projects `supports_rail` / `hosts_rail` / `binds_rapp`
|
|
from family declarations. That is discovery, not a deploy engine. Do not
|
|
treat "register in Fabric" as wrapping the app.
|
|
|
|
### 3.5 Three wrap paths (only one is the target)
|
|
|
|
| Path | Where it lives | Use |
|
|
| --- | --- | --- |
|
|
| **A. `rapp-*` repo** | `declarations/rapp.yaml` + package Makefile | Canonical destination for a managed app or platform service. This guide. |
|
|
| **B. Overlay + `railiance/app.toml`** | `bin/railiance create-overlay` in `rail-kubernetes` | Migration-era third-party wrap. Produces `<app>-railiance-overlay/`. Graduate it to Path A; do not start new overlays. |
|
|
| **C. S5 chart in `railiance-apps`** | `charts/<app>/` + `helm/<app>-values.yaml` | How user-facing apps actually ship today. Stay here only for an already-extracted-or-not decision; new managed apps should leave as Path A. |
|
|
|
|
`flex-auth` is a live Path B example (`flex-auth/railiance/app.toml`)
|
|
with no `rapp-*` repo. `vergabe-teilnahme` is a live Path C example.
|
|
`rapp-policy-nexus` is the only live A←C bridge.
|
|
|
|
There is no `railiance register-rapp`. A rapp exists when
|
|
`declarations/rapp.yaml` is committed and
|
|
`validate-family-declarations.py` accepts it.
|
|
|
|
---
|
|
|
|
## 4. Decide before creating a rapp
|
|
|
|
Ask these in order. Stop at the first "no".
|
|
|
|
1. **Does the workload have a stable identity?** A name that is not the
|
|
repo prefix, a package surface already visible in Git, and a reason to
|
|
version and roll back on its own.
|
|
2. **Is wrapping distinct from ownership?** If the only thing you would
|
|
put in the rapp already belongs in `railiance-platform` or
|
|
`railiance-apps` as shared policy, do not create a rapp.
|
|
3. **Do members share fate?** One rapp per bounded context that deploys
|
|
and rolls back together. Independent rollback → separate rapp.
|
|
4. **Which rail?** Default `rail-kubernetes`. Choose `rail-knative` only
|
|
for request-activation / scale-to-zero / revision-traffic semantics.
|
|
5. **Which reef?** New compute workloads bind `reef-railiance`. Object
|
|
storage is `reef-storage` and is consumed as a capability, not as a
|
|
placement.
|
|
6. **What package type?** One of the schema enums (see §6).
|
|
7. **What exposure?** Omit the field (private) until a grant exists.
|
|
|
|
A service that fails (1) or (2) can still be operated from its ownership
|
|
repo. It is not blocked; it is just not ready to be a rapp. Keep it on
|
|
Path C (S5) or in the ownership repo until those answers are yes.
|
|
|
|
If the workload is already an S5 chart and is not yet ready for a rapp,
|
|
do not generate an overlay (Path B) on top of it. Path B is only a
|
|
third-party compatibility wrap.
|
|
|
|
---
|
|
|
|
## 5. Ownership split
|
|
|
|
Use the S3 ownership test from
|
|
`railiance-platform/docs/rapp-platform-service-pattern.md`, generalized
|
|
to applications:
|
|
|
|
1. Would this asset survive replacing the product? → ownership / platform
|
|
repo.
|
|
2. Does any other workload depend on it? → platform or rail.
|
|
3. Does it encode *who may approve*, not *how to apply*? → platform /
|
|
reef admission.
|
|
4. Otherwise it is the package's.
|
|
|
|
### Applied to a first-party app
|
|
|
|
| Asset | Home |
|
|
| --- | --- |
|
|
| Domain code, tests, migrations, Containerfile, OpenAPI | app repo (`user-engine`) |
|
|
| Helm chart, values, NetworkPolicy, Ingress, ServiceAccount | rapp |
|
|
| `declarations/rapp.yaml`, `railiance/app.toml` | rapp |
|
|
| Deploy / dry-run / status / verify / rollback Makefile | rapp |
|
|
| Workload-specific smoke outcomes | rapp |
|
|
| Image **build** and digest publication | app repo + forge |
|
|
| Image **pin** in the release | rapp (digest, never `latest`) |
|
|
| Postgres cluster, backup, credential broker | `rapp-postgres` + `railiance-platform` |
|
|
| Consumer declaration (`database-per-consumer`) | `rapp-postgres/consumers/<app>.yaml` requested by the rapp |
|
|
| Schema migrations | app repo, invoked by the rapp at rollout |
|
|
| OIDC / flex-auth / tenant policy | those ownership repos |
|
|
| Secret **values** | OpenBao via the platform broker |
|
|
| Secret **references** | rapp `secret_references` + `railiance/app.toml` |
|
|
| Public hostname grant | rapp `exposure.grant` + reef public surface |
|
|
| DNS / TLS / Traefik | reef / `railiance-cluster` |
|
|
| Shared S5 recipes (probe Host header, landing pages) | copied into the rapp; source of the lesson stays in `railiance-apps` |
|
|
|
|
The rapp repo must never become a shadow ownership repo. If a paragraph
|
|
in `INTENT.md` starts explaining *why the product exists*, it belongs in
|
|
the app repo.
|
|
|
|
---
|
|
|
|
## 6. Required repo baseline
|
|
|
|
Every `rapp-*` starts with the first-wave bootstrap contract:
|
|
|
|
```text
|
|
rapp-<app>/
|
|
.repo-classification.yaml
|
|
README.md
|
|
INTENT.md
|
|
SCOPE.md
|
|
AGENTS.md
|
|
CLAUDE.md
|
|
LICENSE
|
|
Makefile
|
|
declarations/rapp.yaml # family contract (normative schema)
|
|
workplans/
|
|
workplans/archived/
|
|
```
|
|
|
|
Then add the package surface. For a `rail-kubernetes` first-party app
|
|
the proven layout is the union of `rapp-policy-nexus` (Helm + digest
|
|
pins + `make render|deploy|verify-live|rollback`) and the
|
|
`rail-kubernetes` overlay contract:
|
|
|
|
```text
|
|
railiance/app.toml # staged promotion (rail contract)
|
|
helm/<app>/ # chart owned by the rapp
|
|
Chart.yaml
|
|
values.yaml
|
|
templates/
|
|
deployment.yaml
|
|
service.yaml
|
|
serviceaccount.yaml
|
|
networkpolicy.yaml
|
|
ingress.yaml # only if exposure will become public/operator
|
|
consumers/ # only if this rapp provisions units
|
|
docs/
|
|
runtime-contract.md
|
|
retained-handoff.md # if extracting from railiance-apps / platform
|
|
tests/ # render / contract tests, no cluster required
|
|
tools/ # verify_live.sh, smoke helpers
|
|
bindings/ # production binding records when needed
|
|
```
|
|
|
|
Implementation files come after the baseline. An empty `rapp-*` git
|
|
directory is not a wrapper; `railiance-master` already treats
|
|
`rapp-user-engine` as undeclared.
|
|
|
|
### Classification
|
|
|
|
Copy the shape used by live rapps. Domain follows the **workload**, not
|
|
the wrapper:
|
|
|
|
```yaml
|
|
repo_classification:
|
|
standard: Repo Classification Standard
|
|
version: "1.0"
|
|
classified_at: "YYYY-MM-DD"
|
|
classified_by: agent
|
|
category: project
|
|
domain: <workload domain>
|
|
secondary_domains:
|
|
- infotech
|
|
capability_tags:
|
|
- platform
|
|
- operations
|
|
- configuration
|
|
business_stake:
|
|
- technology
|
|
- operations
|
|
business_mechanics:
|
|
- coordination
|
|
- operation
|
|
```
|
|
|
|
---
|
|
|
|
## 7. `declarations/rapp.yaml`
|
|
|
|
Normative shape: `railiance-master/schemas/rapp.schema.json`.
|
|
Validate with:
|
|
|
|
```bash
|
|
# from railiance-master, with sibling family repos checked out
|
|
tools/validate-family-declarations.py --root ..
|
|
```
|
|
|
|
### Required meaning, not a second schema
|
|
|
|
| Field | Practice |
|
|
| --- | --- |
|
|
| `kind` | always `managed-workload-package` |
|
|
| `repo_family` | always `rapp` |
|
|
| `rapp_id` / `repo` | `rapp-<app>` for a single-repo rapp |
|
|
| `ownership_repo` | the accountable app or layer repo, **never** the rapp itself. Application: `user-engine`. Platform service: `railiance-platform`. |
|
|
| `contract_version` | start at `1.0.0` |
|
|
| `readiness_state` | `draft` while scaffolding → `declared` once valid → `deployed` / `verified` only with evidence. There is no `production-approved` on a rapp. |
|
|
| `workload_identity.name` | the workload (`user-engine`), **not** `rapp-user-engine`. The schema forbids the `rapp-` prefix. |
|
|
| `workload_identity.package_type` | one of `helm-managed-platform-service`, `manifest-managed-platform-service`, `knative-managed-service`, `grouped-composition` |
|
|
| `data_classification` | highest class the workload handles |
|
|
| `criticality` | `low` \| `medium` \| `high` \| `critical` |
|
|
| `primary_rail` | must also appear in `supported_rails` |
|
|
| `bound_reefs` | `[]` until placed; then `[reef-railiance]` |
|
|
| `runtime_dependencies` | capability names, not the resource that satisfies them |
|
|
| `composition.purpose` | one sentence; if it needs "and also", split the rapp |
|
|
| `composition.member_repos` | one member for a single-repo rapp; each lists `deployables` |
|
|
| `composition.upstream_components` | pinned third-party charts/images. Exact version or digest. Never `latest`. |
|
|
| `rollout_contract.default_mode` | `helm-upgrade-install` or `kubectl-server-side-apply` (or `revision-canary` on Knative) |
|
|
| `smoke_contract.required` | **outcomes** that must be true, not a command diary |
|
|
| `rollback_contract.order` | preferred strategies first |
|
|
| `source_documents` | repo + path of the docs this declaration was derived from |
|
|
|
|
Optional, use when true:
|
|
|
|
- `secret_references` — OpenBao **paths**, never values
|
|
- `consumer_contract` / `consumers` — only if this rapp provisions isolated units to others (`rapp-postgres` is the model)
|
|
- `exposure` — omit for private. `public` requires a grant, `binding_admission: production-approved`, and a reef that has granted a public surface.
|
|
|
|
### Package-type choice
|
|
|
|
The schema has no `helm-managed-application` enum. Until that is added in
|
|
`railiance-master`, first-party Helm apps use
|
|
`helm-managed-platform-service` (as `rapp-policy-nexus` does). Do not
|
|
invent a local type. Record the naming mismatch in `source_documents` /
|
|
workplan notes if you want the schema extended later.
|
|
|
|
### Capability names in use today
|
|
|
|
There is not yet a controlled vocabulary shared with
|
|
`rail.required_substrate_capabilities`. Prefer names already live in
|
|
declarations rather than minting near-duplicates:
|
|
|
|
- `kubernetes-api`
|
|
- `helm-apply-path` / `helm-repo-access`
|
|
- `traefik-ingress`
|
|
- `cert-manager`
|
|
- `forgejo-oci-registry`
|
|
- `pvc-storage`
|
|
- `operator-kubeconfig-access`
|
|
- `knative-serving`
|
|
- `keycape-oidc` / `key-cape-oidc` (spellings have already drifted — pick one and stay consistent inside a rapp)
|
|
- `flex-auth`
|
|
- `openbao`
|
|
- `openbao-database-secrets-engine`
|
|
- `s3-backup-target`
|
|
|
|
Who provisions the capability is **outside** the rapp:
|
|
`resource-control` procures, the reef publishes non-secret attributes,
|
|
`railiance-platform` vends the credential.
|
|
|
|
---
|
|
|
|
## 8. Service wrapper patterns
|
|
|
|
Three live patterns. Copy the one that matches the rail and package type.
|
|
|
|
### 8.1 Helm platform / app service (`rail-kubernetes`)
|
|
|
|
References: `rapp-openbao`, `rapp-policy-nexus`,
|
|
`railiance-apps/docs/s5-app-onboarding-checklist.md`.
|
|
|
|
Operator surface (minimum Makefile targets):
|
|
|
|
| Target | Meaning |
|
|
| --- | --- |
|
|
| `make check` / `make test` / `make lint` | contract and `helm lint` with dummy digests |
|
|
| `make render` | write rendered manifests locally |
|
|
| `make server-dry-run` | `kubectl apply --dry-run=server` against a representative API |
|
|
| `make deploy` | `helm upgrade --install --atomic --wait` |
|
|
| `make status` | helm/kubectl status |
|
|
| `make verify-live` | named smoke outcomes from `smoke_contract.required` |
|
|
| `make rollback REVISION=…` | previous Helm revision |
|
|
|
|
Pins:
|
|
|
|
- image by **digest**, not tag (`rapp-policy-nexus` is the model)
|
|
- chart version and app version in `workload_identity`
|
|
- forge-owned registry path (`forgejo.coulomb.social/...`)
|
|
|
|
Runtime hardening that should be the default in new charts:
|
|
|
|
- non-root numeric user
|
|
- read-only root filesystem where the app allows it
|
|
- dropped capabilities
|
|
- NetworkPolicy default-deny plus explicit ingress/egress
|
|
- unauthenticated `/healthz` and `/readyz`
|
|
- `/metrics` cluster-internal and authorized
|
|
- probe `Host` header set when the app validates Host (Django lesson)
|
|
|
|
### 8.2 Manifest-managed platform service
|
|
|
|
Reference: `rapp-postgres` (`kubectl-server-side-apply` of CNPG Cluster
|
|
and NetworkPolicies; Helm only for the *operator*, which is cluster-owned).
|
|
|
|
Use this when the workload is a set of reviewed manifests rather than a
|
|
chart you upgrade as a release.
|
|
|
|
### 8.3 Knative managed service
|
|
|
|
Reference: `rapp-qonto` (`runtime/knative/`, revision-canary,
|
|
previous-revision rollback). Only when `rail-knative` is the primary rail.
|
|
|
|
### 8.4 Overlay compatibility path
|
|
|
|
`rail-kubernetes/docs/overlay-repo-pattern.md` still describes
|
|
`<app>-railiance-overlay/` with `railiance/app.toml`. The existing
|
|
scaffold is:
|
|
|
|
```bash
|
|
# from rail-kubernetes; cluster/bootstrap only keep shims
|
|
bin/railiance create-overlay --app-id <id> --upstream-url <url> …
|
|
bin/railiance run <overlay-dir> # Stage 1
|
|
bin/railiance deploy --stage 2 <overlay-dir> # Stage 2 canary
|
|
bin/railiance promote <overlay-dir> # Stage 3
|
|
bin/railiance rollback <overlay-dir>
|
|
```
|
|
|
|
`create-overlay` writes local files only. It does not create
|
|
`declarations/rapp.yaml`, a Forgejo remote, or a family classification.
|
|
Repo Manager's later `rmgr rapp init` replaces this for first-party apps.
|
|
|
|
New work should not create overlay repos. If you find one, graduate it
|
|
to `rapp-<app>` and keep `railiance/app.toml` inside the rapp.
|
|
|
|
---
|
|
|
|
## 9. Platform handoffs every app wrapper needs
|
|
|
|
### 9.1 Database
|
|
|
|
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`
|
|
- roles split: owner / migration / runtime
|
|
- tenant-keying required even for a single current tenant
|
|
|
|
The consumer file lives in `rapp-postgres/consumers/<app>.yaml`. The rapp
|
|
records the capability (`openbao-database-secrets-engine` or equivalent)
|
|
and the OpenBao path in `secret_references`. The app repo keeps
|
|
migrations and applies them with the migration role.
|
|
|
|
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
|
|
`secret_references`. Short-lived leases go through the grant catalog and
|
|
`rapp_id`. See `railiance-platform/docs/rapp-credential-lane-binding.md`.
|
|
|
|
In `railiance/app.toml`, declare `secrets.references` with route ids
|
|
(`openbao-api-key`, `key-cape-oidc-login`, …) and Kubernetes Secret /
|
|
ExternalSecret **names**.
|
|
|
|
### 9.3 Identity and authorization
|
|
|
|
Apps that speak OIDC / flex-auth declare those as runtime capabilities
|
|
and receive issuer, audience, client id, and token-file paths through
|
|
the broker. The rapp does not mint tenants or tokens.
|
|
`workload_identity.tenant` is an opaque NetKingdom id when one is
|
|
needed; the rapp never parses or authorizes on it.
|
|
|
|
### 9.4 Images
|
|
|
|
Build stays in the app repo. The rapp consumes a forge-published digest.
|
|
Verify the cluster can pull the image before the first non-smoke deploy
|
|
(`railiance-apps/tools/check-oci-image.sh` is the existing helper).
|
|
|
|
### 9.5 Ingress, TLS, landing pages
|
|
|
|
Private by default. When a public hostname is later granted:
|
|
|
|
- name host, namespace, release, ingress, TLS secret in the runbook
|
|
- cert-manager owns certificates
|
|
- every public `/` has an intentional browser response (S5 landing-page
|
|
rule)
|
|
- do not break `/healthz`, `/readyz`, API prefixes, or OIDC callbacks
|
|
|
|
### 9.6 Exposure (ADR-0008)
|
|
|
|
| Posture | Who reaches the listener |
|
|
| --- | --- |
|
|
| omitted / `private` | in-cluster DNS, same-reef services |
|
|
| `operator` | same, plus a named ops-bridge / SSH tunnel |
|
|
| `public` | internet, only with grant + production-approved binding + reef public surface |
|
|
|
|
`data_classification: public` is **not** exposure. Port `6443` is never
|
|
a grantable public surface.
|
|
|
|
---
|
|
|
|
## 10. Worked example: `user-engine` → `rapp-user-engine`
|
|
|
|
### 10.1 Current facts
|
|
|
|
`user-engine` is a headless user-domain / identity-domain service with a
|
|
production WSGI portal. The **app repo** already has:
|
|
|
|
- `Containerfile` — Python 3.12 slim, non-root uid `10001`, port `8080`
|
|
- `python -m user_engine.runtime`
|
|
- `/healthz`, `/readyz`, `/metrics` (metrics require the trusted proxy marker)
|
|
- 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, secret custody, **and
|
|
deployment** (`docs/release.md`)
|
|
|
|
`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.
|
|
|
|
**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
|
|
|
|
| Decision | Value | Why |
|
|
| --- | --- | --- |
|
|
| 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` | `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) 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)
|
|
|
|
The live file will be `rapp-user-engine/declarations/rapp.yaml`. This is
|
|
the intended fill of the schema, to be validated before it is treated as
|
|
declared.
|
|
|
|
```yaml
|
|
kind: managed-workload-package
|
|
repo_family: rapp
|
|
rapp_id: rapp-user-engine
|
|
repo: rapp-user-engine
|
|
ownership_repo: user-engine
|
|
contract_version: 1.0.0
|
|
readiness_state: draft
|
|
workload_identity:
|
|
name: user-engine
|
|
package_type: manifest-managed-platform-service
|
|
app_version: 0.1.0
|
|
data_classification: confidential
|
|
criticality: high
|
|
primary_rail: rail-kubernetes
|
|
supported_rails:
|
|
- rail-kubernetes
|
|
bound_reefs: []
|
|
runtime_dependencies:
|
|
- kubernetes-api
|
|
- helm-apply-path
|
|
- forgejo-oci-registry
|
|
- 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: Kubernetes manifests, smoke, and rollback for the user-engine runtime
|
|
deployables:
|
|
- user-engine
|
|
upstream_components:
|
|
- name: user-engine
|
|
source: forgejo.coulomb.social/coulomb/user-engine
|
|
version: sha256:e3b5f65bafc1c0260dfdf2567a52766e67506ceb878a51759a2e9a307c4b5eb8
|
|
rollout_contract:
|
|
default_mode: kubectl-server-side-apply
|
|
commands:
|
|
- make server-dry-run
|
|
- make deploy
|
|
production_gate: reef-production-approved
|
|
smoke_contract:
|
|
required:
|
|
- healthz-ok
|
|
- readyz-ok-when-store-ready
|
|
- metrics-requires-proxy-marker
|
|
- local-issuer-rejected-in-production
|
|
- live-image-digest-match
|
|
commands:
|
|
- make verify-live
|
|
rollback_contract:
|
|
order:
|
|
- previous-immutable-image-digest
|
|
- apply-reviewed-git-revision
|
|
commands:
|
|
- make rollback DIGEST=<previous-image-digest>
|
|
source_documents:
|
|
- repo: user-engine
|
|
path: INTENT.md
|
|
- repo: user-engine
|
|
path: docs/configuration.md
|
|
- repo: user-engine
|
|
path: docs/operability.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: 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.
|
|
|
|
`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
|
|
|
|
From `user-engine` `runtime.py` and `docs/configuration.md`. All values
|
|
arrive as secret-backed env or files. The chart names the keys; OpenBao
|
|
fills them.
|
|
|
|
Required for production assembly:
|
|
|
|
- `USER_ENGINE_DATABASE_URL` (or discrete Postgres vars + a rendered DSN)
|
|
- `USER_ENGINE_OIDC_ISSUER`, `USER_ENGINE_OIDC_AUDIENCE`,
|
|
`USER_ENGINE_OIDC_CLIENT_ID`, `USER_ENGINE_OIDC_REDIRECT_URI`
|
|
- `USER_ENGINE_FLEX_AUTH_URL`, `USER_ENGINE_FLEX_AUTH_TOKEN_FILE`
|
|
- `USER_ENGINE_PROXY_SECRET`
|
|
- `USER_ENGINE_LOGIN_URL`
|
|
- `USER_ENGINE_EVENT_URL`, `USER_ENGINE_EVENT_TOKEN`
|
|
- `USER_ENGINE_PROVISIONING_URL`, `USER_ENGINE_PROVISIONING_TOKEN`
|
|
|
|
Optional / fail-closed when absent:
|
|
|
|
- `USER_ENGINE_TENANT_MANAGEMENT_URL` + `USER_ENGINE_TENANT_MANAGEMENT_TOKEN`
|
|
- `USER_ENGINE_MAIL_URL` + `USER_ENGINE_MAIL_TOKEN`
|
|
- public-registration adapter settings
|
|
|
|
Port: `USER_ENGINE_HTTP_PORT` (default `8080`). Do not use
|
|
`USER_ENGINE_PORT` — Kubernetes service-link injection occupies that name.
|
|
|
|
Logical secret names the app already documents: `database.url`,
|
|
`event.signing_key`, `webhook.shared_secret`. Map those to broker routes
|
|
in `railiance/app.toml`; do not invent a second naming scheme in the
|
|
chart.
|
|
|
|
### 10.5 Database decision (do not do both)
|
|
|
|
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.
|
|
|
|
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`
|
|
|
|
- domain model, adapters, tests, conformance harness
|
|
- `Containerfile` and image publish instructions
|
|
- SQL migrations
|
|
- portal/OIDC behavior
|
|
- `docs/operability.md` diagnostics
|
|
|
|
The app repo should grow a short "consumed by `rapp-user-engine`" note
|
|
pointing here. It should not grow Helm charts.
|
|
|
|
### 10.7 First-wave `railiance/app.toml` for this rapp
|
|
|
|
Minimum: `schema_version = "railiance.app.v1"`, `app.id = "user-engine"`,
|
|
source digest policy `required`, postgres + oidc + flex-auth + openbao
|
|
dependencies, secret references by route, `/healthz` and `/readyz`
|
|
checks, Stage 1 (`make test` in the app repo + `helm template`), Stage 2
|
|
canary requiring approval, Stage 3 promote with a recorded rollback
|
|
target. Copy structure from
|
|
`rail-kubernetes/examples/railiance/app.toml`.
|
|
|
|
---
|
|
|
|
## 11. Operator checklist (new rapp)
|
|
|
|
Use this when filling a stub or extracting a wrapper from
|
|
`railiance-apps`.
|
|
|
|
### Shape
|
|
|
|
- [ ] Confirm the workload passes §4.
|
|
- [ ] Create or fill `rapp-<app>` with the §6 baseline.
|
|
- [ ] Write `INTENT.md` / `SCOPE.md` as a **package** boundary, not a
|
|
product manifesto.
|
|
- [ ] Author `declarations/rapp.yaml` to the schema. Start
|
|
`readiness_state: draft`.
|
|
- [ ] Add `railiance/app.toml` only if you need Stage 1/2/3 rail
|
|
promotion. It is not part of the first wrap.
|
|
- [ ] Add the Helm chart or manifest set. Pin images by digest. Prefer
|
|
`rmgr rapp wrap` / `skeleton` over hand-copying.
|
|
- [ ] Add Makefile targets from §8.1.
|
|
- [ ] Name smoke **outcomes** and rollback **order**.
|
|
- [ ] Leave `exposure` unset.
|
|
|
|
### Handoffs
|
|
|
|
- [ ] Request a `rapp-postgres` consumer when a durable store is needed.
|
|
- [ ] Request credential lanes / CCRs in `railiance-platform`. Commit
|
|
paths only.
|
|
- [ ] Confirm forge image publish path in the **app** repo.
|
|
- [ ] Confirm cluster pull before the first deploy.
|
|
- [ ] Record `source_documents`.
|
|
|
|
### Evidence before `declared` → `deployed`
|
|
|
|
- [ ] `tools/validate-family-declarations.py --root ..` passes.
|
|
- [ ] `make render` and `make server-dry-run` pass.
|
|
- [ ] `make deploy` on `reef-railiance` only after the reef binding is
|
|
intended.
|
|
- [ ] `make verify-live` proves every `smoke_contract.required` item.
|
|
- [ ] Rollback is rehearsed at least once on a non-destructive revision.
|
|
- [ ] Deployable names match the live inventory
|
|
(`railiance-platform/docs/evidence/reef-railiance-deployables.json`)
|
|
when `--inventory` is run.
|
|
|
|
### Do not
|
|
|
|
- [ ] Commit credentials, kubeconfigs, or tokenized registry URLs.
|
|
- [ ] Add the workload to `reef.yaml` `bound_rapps` by hand.
|
|
- [ ] Put Helm charts in the app repo "just for now".
|
|
- [ ] Create a new rail.
|
|
- [ ] Create a reef because a hostname exists.
|
|
- [ ] Group unrelated engines into one rapp.
|
|
- [ ] Set `exposure.posture: public` to make a smoke test easier.
|
|
|
|
---
|
|
|
|
## 12. What Repo Manager automates
|
|
|
|
Implemented under `RMGR-WP-0006` (P1, P4) and `RMGR-WP-0007` (P2, P3, P5,
|
|
compose).
|
|
|
|
| Phase | Command | Still human |
|
|
| --- | --- | --- |
|
|
| **P0 — this guide** | — | Accept or amend the shape. |
|
|
| **P1 — bootstrap** | `rmgr rapp init` | Purpose sentence, classification. |
|
|
| **P2 — package skeleton** | `rmgr rapp skeleton --from-app` | NetworkPolicy and resource review. |
|
|
| **P3 — platform drafts** | consumer draft + app `image.yaml` | CCR / consumer **apply**. |
|
|
| **P4 — validate** | `rmgr rapp validate` | Promote `draft` → `declared`. |
|
|
| **P5 — place** | `rmgr rapp place --reef reef-railiance` | Production gate, exposure grant. |
|
|
|
|
```text
|
|
rmgr rapp wrap --path ../rapp-<app> --app <app> \
|
|
--ownership-repo <app> --from-app ../<app>
|
|
rmgr rapp validate --path ../rapp-<app> --family-root ..
|
|
rmgr rapp pin-image --path ../rapp-<app> --digest sha256:<64 hex>
|
|
rmgr rapp place --path ../rapp-<app> --reef reef-railiance
|
|
```
|
|
|
|
Constraints:
|
|
|
|
- Files stay authoritative. The hub is only updated via `fix-consistency`.
|
|
- No secret values, ever.
|
|
- Do not invent package types, rails, or reefs.
|
|
- `init` / `wrap` refuse to overwrite a live declaration.
|
|
- `tenant-engine` is the `RMGR-WP-0007` pilot. `rapp-secrets-engine` is
|
|
not a wrap target.
|
|
|
|
---
|
|
|
|
## 13. Open questions and known drift
|
|
|
|
Recorded so the scaffolder does not silently pick a side.
|
|
|
|
1. **`ownership_repo` tightness.** Schema currently allows a layer repo
|
|
(`railiance-platform`) or an application repo. For application rapps,
|
|
prefer the application repo. (`railiance-master/schemas/README.md`)
|
|
2. **No application package_type.** First-party Helm apps reuse
|
|
`helm-managed-platform-service`. A future `railiance-master` schema
|
|
change may add `helm-managed-application`.
|
|
3. **`rapp-qonto` is not a copy target.** It is verified in production
|
|
but does not yet conform to ADR-0007 (`composition`, `bound_reefs`,
|
|
`package_type`, workload name). Copy `rapp-policy-nexus` +
|
|
`rapp-openbao` instead.
|
|
4. **Capability vocabulary is free-form.** Spellings already drift
|
|
(`keycape-oidc` vs `key-cape-oidc`). The scaffolder should emit a
|
|
small allow-list and refuse new names without a note.
|
|
5. **`rapp-vergabe-teilnahme` was the planned user-facing proof and was
|
|
never built.** Extracting it remains a `railiance-apps` residual. Do
|
|
not pretend `rapp-user-engine` closes that ADR-0003 item. Other
|
|
planned-but-unbuilt names (`rapp-forgejo`, `rapp-coulomb-social`,
|
|
`rapp-netkingdom-identity`) are not this workplan.
|
|
6. **Reef `bound_rapps` on `reef-railiance` has been stale.** Never
|
|
teach the scaffolder to edit it.
|
|
7. **Metrics and Host-validated apps** need extra chart care. The
|
|
scaffolder should read probe paths from the app if present
|
|
(`/healthz`, `/readyz` for user-engine) and not assume `/health`.
|
|
8. **Grouped identity vs single-engine stubs.** Architecture still
|
|
mentions folding `rapp-user-engine` into `rapp-netkingdom-identity`.
|
|
ADR-0007 only allows that if members share rollout and rollback fate.
|
|
`rapp-secrets-engine` is also a suspect duplicate of `rapp-openbao` /
|
|
S3 custody, not a fourth identity member. The pilot declares
|
|
`rapp-user-engine` alone; a later composition change is a new
|
|
declaration, not a rename of the stub.
|
|
|
|
---
|
|
|
|
## 14. References
|
|
|
|
Architecture and contracts:
|
|
|
|
- `railiance-master/docs/repository-axes.md`
|
|
- `railiance-master/docs/adr/ADR-0001-repository-prefix-architecture.md`
|
|
- `railiance-master/docs/adr/ADR-0003-rapp-first-wave-selection.md`
|
|
- `railiance-master/docs/adr/ADR-0005-derived-rail-composition.md`
|
|
- `railiance-master/docs/adr/ADR-0006-reef-production-admission.md`
|
|
- `railiance-master/docs/adr/ADR-0007-rapp-declaration-contract.md`
|
|
- `railiance-master/docs/adr/ADR-0008-private-by-default-exposure.md`
|
|
- `railiance-master/docs/repo-family-bootstrap-contract.md`
|
|
- `railiance-master/docs/rapp-first-wave-candidates.md`
|
|
- `railiance-master/schemas/README.md`
|
|
|
|
Rails and reefs:
|
|
|
|
- `rail-kubernetes/declarations/rail.yaml`
|
|
- `rail-kubernetes/docs/deployment-lifecycle.md`
|
|
- `rail-kubernetes/docs/app-toml-contract.md`
|
|
- `rail-kubernetes/docs/overlay-repo-pattern.md`
|
|
- `rail-knative/declarations/rail.yaml`
|
|
- `reef-railiance/declarations/reef.yaml`
|
|
- `reef-storage/declarations/reef.yaml`
|
|
|
|
Proven wrappers and operator recipes:
|
|
|
|
- `railiance-platform/docs/rapp-platform-service-pattern.md`
|
|
- `rapp-openbao/declarations/rapp.yaml`
|
|
- `rapp-postgres/declarations/rapp.yaml`
|
|
- `rapp-policy-nexus/declarations/rapp.yaml`
|
|
- `rapp-policy-nexus/docs/runtime-contract.md`
|
|
- `railiance-apps/docs/s5-app-onboarding-checklist.md`
|
|
- `railiance-apps/docs/django-on-railiance.md`
|
|
- `railiance-apps/docs/operator-recipes.md`
|
|
- `railiance-apps/Makefile` (`policy-nexus-deploy` delegate)
|
|
- `rail-kubernetes/docs/create-overlay-command.md`
|
|
- `railiance-fabric/docs/rail-rapp-reef-adaptation.md` (discovery only)
|
|
|
|
Worked example:
|
|
|
|
- `user-engine/INTENT.md`
|
|
- `user-engine/docs/configuration.md`
|
|
- `user-engine/docs/operability.md`
|
|
- `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)
|