feat: greenfield rapp wrap path and rail-sequence rules
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.
This commit is contained in:
parent
4743435f04
commit
f953b1ebf5
6 changed files with 963 additions and 97 deletions
|
|
@ -12,7 +12,55 @@ schema wins:
|
|||
- [`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 throughout: `user-engine` + the empty stub `rapp-user-engine`.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -25,14 +73,14 @@ the app onto a rail and a reef.
|
|||
|
||||
This guide answers:
|
||||
|
||||
1. What a managed Railiance application **is**.
|
||||
2. How rails, reefs, and existing rapps already do it.
|
||||
3. Which files, declarations, and operator surfaces a new wrapper needs.
|
||||
4. How `user-engine` should be wrapped as `rapp-user-engine`.
|
||||
5. What Repo Manager must later automate, in order.
|
||||
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).
|
||||
|
||||
It is the first deliverable of `RMGR-WP-0006`. Implementation of the
|
||||
scaffolder comes after this shape is accepted.
|
||||
`RMGR-WP-0006` recorded the contract and the first wrap.
|
||||
`RMGR-WP-0007` is the greenfield command path.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -73,14 +121,73 @@ Grouping is legitimate only where members share rollout and rollback fate.
|
|||
| `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. |
|
||||
|
||||
Default bias: use `rail-kubernetes` unless the workload truly needs
|
||||
Knative lifecycle semantics. Do not invent a new rail for an adapter or
|
||||
values profile.
|
||||
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. Durable first-class packaging belongs in `rapp-*`.
|
||||
the compatibility overlay-repo pattern. That overlay is the
|
||||
migration-era wrapper. `app.toml` is **not** required on the first wrap.
|
||||
|
||||
### 3.2 Reefs
|
||||
|
||||
|
|
@ -103,17 +210,17 @@ admit it to production and does not make it public (ADR-0008).
|
|||
| `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` | **no** | — | — | — | stub | Empty git repo. Target of this guide. |
|
||||
| `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 only half-proven (`rapp-policy-nexus` declared; `rapp-qonto`
|
||||
drifted; `rapp-vergabe-teilnahme` never extracted). `rapp-user-engine` is
|
||||
the next application-shaped proof, not a substitute for the missing
|
||||
Vergabe extraction.
|
||||
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
|
||||
|
||||
|
|
@ -777,8 +884,10 @@ Use this when filling a stub or extracting a wrapper from
|
|||
product manifesto.
|
||||
- [ ] Author `declarations/rapp.yaml` to the schema. Start
|
||||
`readiness_state: draft`.
|
||||
- [ ] Add `railiance/app.toml` if the primary rail is `rail-kubernetes`.
|
||||
- [ ] Add the Helm chart or manifest set. Pin images by digest.
|
||||
- [ ] 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.
|
||||
|
|
@ -816,42 +925,36 @@ Use this when filling a stub or extracting a wrapper from
|
|||
|
||||
---
|
||||
|
||||
## 12. What Repo Manager should automate
|
||||
## 12. What Repo Manager automates
|
||||
|
||||
This is the work structure for `RMGR-WP-0006`. Each step is a later
|
||||
command or command flag on the existing governed-mutation path
|
||||
(`RMGR-WP-0004` scaffolding, specialized for the rapp family).
|
||||
Implemented under `RMGR-WP-0006` (P1, P4) and `RMGR-WP-0007` (P2, P3, P5,
|
||||
compose).
|
||||
|
||||
| Phase | Repo Manager does | Still human |
|
||||
| Phase | Command | Still human |
|
||||
| --- | --- | --- |
|
||||
| **P0 — this guide** | Record the shape. | Accept or amend the shape. |
|
||||
| **P1 — bootstrap stub** | Create or complete `rapp-<app>` with §6 baseline files, classification, and a `draft` `rapp.yaml` from a questionnaire (ownership repo, rail, classification, criticality, purpose). | Confirm ownership split and purpose sentence. |
|
||||
| **P2 — package skeleton** | Generate Helm chart + Makefile targets from app facts: port, health paths, Containerfile user, image repository. Generate `railiance/app.toml` skeleton. | Review NetworkPolicy and resource requests. |
|
||||
| **P3 — platform bindings** | Draft `rapp-postgres` consumer and `secret_references` from the app's documented logical secret names. | Approve CCR / consumer apply in the owning packages. |
|
||||
| **P4 — validate** | Run family-declaration validation and `helm lint` / render. Refuse `readiness_state` promotions that skip evidence. | Promote `draft` → `declared`. |
|
||||
| **P5 — place** | Set `bound_reefs: [reef-railiance]` only on an explicit place command. | Production gate and any exposure grant. |
|
||||
|
||||
Command sketch (not implemented):
|
||||
| **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 init --app user-engine --ownership-repo user-engine \
|
||||
--rail rail-kubernetes --classification confidential --criticality high
|
||||
|
||||
rmgr rapp skeleton --path ../rapp-user-engine --from-app ../user-engine
|
||||
|
||||
rmgr rapp validate --path ../rapp-user-engine --family-root ..
|
||||
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 on the scaffolder:
|
||||
Constraints:
|
||||
|
||||
- Files stay authoritative. The hub is not written to except via
|
||||
`fix-consistency` after the files exist.
|
||||
- Files stay authoritative. The hub is only updated via `fix-consistency`.
|
||||
- No secret values, ever.
|
||||
- Do not invent package types, rails, or reefs.
|
||||
- Idempotent: re-running `init` on an already-declared rapp must refuse
|
||||
or update in a reviewed diff, not overwrite a live contract.
|
||||
- The three engine stubs (`user-engine`, `secrets-engine`,
|
||||
`tenant-engine`) are the first cohort; `user-engine` is the pilot.
|
||||
- `init` / `wrap` refuse to overwrite a live declaration.
|
||||
- `tenant-engine` is the `RMGR-WP-0007` pilot. `rapp-secrets-engine` is
|
||||
not a wrap target.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue