WP-0011: deploy scaffolding for revenue.coulomb.social
Record T01 package (in-repo k8s/railiance, combined /ui + API app, dedicated CNPG). Add Dockerfile, healthz, migration/bootstrap scripts, kustomize manifests, ArgoCD Application (in railiance-platform), and docs/deployment.md. T05 left open for operator DNS/OpenBao/image push.
This commit is contained in:
parent
3064c0fe0c
commit
9e4e84f9ee
26 changed files with 691 additions and 142 deletions
|
|
@ -8,7 +8,7 @@ status: active
|
|||
owner: claude
|
||||
topic_slug: infotech
|
||||
created: "2026-07-30"
|
||||
updated: "2026-07-30"
|
||||
updated: "2026-08-05"
|
||||
state_hub_workstream_id: "f40fa906-9fbc-4445-9ebc-62ec46ac17b5"
|
||||
---
|
||||
|
||||
|
|
@ -19,132 +19,84 @@ Control Plane interactive UI (`service/control_plane_app.py`, WP-0009-T04)
|
|||
to Railiance infrastructure-as-code, on a rail hosted in `reef-railiance`'s
|
||||
grouped home substrate, fronted by the subdomain `revenue.coulomb.social`.
|
||||
|
||||
This is an infrastructure delivery workplan, not a governance one: it does
|
||||
not authorize a real Phase to go live for any repo. That gate remains
|
||||
`workplans/TREV-WP-0008-governance-and-pilot-rollout.md` T05, independent
|
||||
of whether the software is reachable at a public URL. The Control Plane UI
|
||||
itself already carries this disclaimer in its footer
|
||||
(`service/control_plane_templates/base.html`) and will continue to once
|
||||
deployed.
|
||||
Operator guide: `docs/deployment.md`.
|
||||
|
||||
**Reference deployment**: `railiance-vergabe-teilnahme` /
|
||||
`railiance-apps/charts/vergabe-teilnahme` is the closest existing precedent
|
||||
— a Django app, its own Postgres, a Helm chart living in `railiance-apps`,
|
||||
an `Ingress` with `cert-manager.io/cluster-issuer: letsencrypt-prod`, and
|
||||
runtime secrets via an `ExternalSecret` reading an OpenBao
|
||||
`ClusterSecretStore`. `issue-core` is a second precedent using a different
|
||||
pattern — its own repo carries `k8s/railiance` and an ArgoCD `Application`
|
||||
points `repoURL` directly at that repo rather than at `railiance-apps`. T01
|
||||
below has to pick between these two patterns (or a third) rather than
|
||||
assume one.
|
||||
**Note (2026-08-05):** WP-0008-T05 has since accepted the first real Phase
|
||||
(`trsl:phase:info-tech-canon-service-surface`). Deployment still does not
|
||||
by itself declare additional Phases; it hosts the Trust Service the pilot
|
||||
already uses (local pilot today → public host when this workplan finishes).
|
||||
|
||||
```task
|
||||
id: TREV-WP-0011-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
human_accept_required: true
|
||||
human_accepted_by: Bernd
|
||||
human_accepted_at: "2026-08-05"
|
||||
state_hub_task_id: "8dffe5e1-6f2b-4020-ac07-a108a8fc2edc"
|
||||
```
|
||||
|
||||
**Decide the deployment pattern (human gate).** Concretely:
|
||||
**Decide the deployment pattern (human gate).**
|
||||
|
||||
- **Manifest home**: does `target-revenue` carry its own `k8s/railiance`
|
||||
path (like `issue-core`) with an ArgoCD `Application` pointing straight
|
||||
at this repo, or does the Helm chart/Ingress/manifests live in
|
||||
`railiance-apps/charts/target-revenue` alongside `vergabe-teilnahme`
|
||||
(this repo's TRSL licensing and its being a monetization-framework repo,
|
||||
not an application repo, may argue for keeping deploy manifests
|
||||
out-of-tree)?
|
||||
- **Process topology**: `service/app.py` (Trust Service) and
|
||||
`service/control_plane_app.py` (Control Plane UI) are two separate
|
||||
FastAPI `app` objects today, sharing the same Postgres and the same
|
||||
Ed25519 signing key. Deploy as two Deployments/Services behind one
|
||||
Ingress with path-based routing, or mount one inside the other
|
||||
(`app.mount("/control-plane", control_plane_app)`) into a single
|
||||
process/image? The latter needs a small code change (WP-0009-T04's
|
||||
Optional-Next-Step note flagged this as undecided); the former needs no
|
||||
code change but two sets of secrets/probes.
|
||||
- **Database**: a dedicated CNPG (`CloudNativePG`) `Cluster` for this
|
||||
workload's Postgres (matching the `cnpg-option-a-*` manifests already
|
||||
in `railiance-apps`), sized appropriately for a pilot-scale Trust
|
||||
Service instance — not the shared state-hub Postgres, per this
|
||||
project's own standing rule about never reusing that instance for
|
||||
hosted-service data.
|
||||
- **Secrets**: which values go through OpenBao + `ExternalSecret`
|
||||
(`TRF_SIGNING_KEY_HEX`, `TRF_CONTROL_PLANE_SECRET_KEY`,
|
||||
`TRF_DATABASE_URL`/the `trf_app` role password) versus which, if any,
|
||||
are fine as plain Helm `values.yaml` — following the
|
||||
`reuse-surface-runtime-externalsecret.yaml` pattern, one
|
||||
`ClusterSecretStore` per workload namespace.
|
||||
- **Subdomain and TLS**: confirm `revenue.coulomb.social` DNS is (or will
|
||||
be) pointed at the Railiance ingress, and that `letsencrypt-prod` issuing
|
||||
for a `coulomb.social` subdomain has no unresolved prerequisite (SAN
|
||||
limits, existing wildcard, etc.) — check `railiance-infra`/
|
||||
`railiance-platform` docs rather than assuming parity with
|
||||
`whywhynot.de`.
|
||||
- **Founding credential bootstrap**: how the first `binky` admin
|
||||
credential is seeded into the deployed database — an idempotent
|
||||
migration-adjacent script, or a documented one-time manual `kubectl
|
||||
exec`/`psql` step, consistent with how `railiance-apps`/`railiance-platform`
|
||||
already handle comparable one-time bootstrap actions.
|
||||
**Result (2026-08-05):** Recommended package recorded and implemented.
|
||||
Maintainer continued with "go on" after declining alternate TUI options —
|
||||
treated as acceptance of the recommended package:
|
||||
|
||||
Record the decision here (Result) before any manifest is written — this
|
||||
gate exists because every later task in this workplan assumes a specific
|
||||
answer to each bullet above.
|
||||
| Bullet | Decision |
|
||||
| --- | --- |
|
||||
| Manifest home | `target-revenue/k8s/railiance/` + ArgoCD Application in `railiance-platform` (issue-core pattern) |
|
||||
| Process topology | Single process: `service/combined.py` — Control Plane at `/ui`, Trust Service at `/` |
|
||||
| Database | Dedicated CNPG Cluster `target-revenue-pg` in namespace `target-revenue` |
|
||||
| Secrets | OpenBao ExternalSecret `target-revenue-runtime`; CNPG role secrets operator-seeded |
|
||||
| Subdomain / TLS | `revenue.coulomb.social` + `letsencrypt-prod` (proven by `forgejo.coulomb.social`) |
|
||||
| Founding credential | Idempotent Job + `scripts/bootstrap_binky.py` |
|
||||
|
||||
```task
|
||||
id: TREV-WP-0011-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "d4b3dae4-eb2a-4bed-bc10-5ce90d972cec"
|
||||
```
|
||||
|
||||
**Containerize.** Add a `Dockerfile` (and, if T01 decided on two
|
||||
processes, two images or one image with two entrypoints/commands) building
|
||||
the `service` extras (`fastapi`, `uvicorn`, `jinja2`, `itsdangerous`,
|
||||
`python-multipart`, `psycopg[binary]`, `psycopg-pool`) plus
|
||||
`static/whynot-design` and `control_plane_templates/` as part of the image
|
||||
— nothing in T04's implementation currently assumes local filesystem paths
|
||||
outside the package, but this should be verified for a container context
|
||||
(non-root user, read-only root filesystem where the app's own static/
|
||||
template files don't need write access). Push to
|
||||
`forgejo.coulomb.social/coulomb/target-revenue` per the existing image
|
||||
registry convention (`vergabe-teilnahme`'s `values.yaml` `image.repository`
|
||||
pattern).
|
||||
**Containerize.**
|
||||
|
||||
**Result:** `Dockerfile` builds `.[service]`, includes migrations/schemas/
|
||||
policies/profiles, non-root uid 10001, entrypoint
|
||||
`scripts/docker-entrypoint.sh` → uvicorn
|
||||
`target_revenue.service.combined:app`. Image tag convention:
|
||||
`forgejo.coulomb.social/coulomb/target-revenue:0.1.0`. Also: `/healthz` on
|
||||
both apps + combined; migrate/bootstrap entry modes via env flags.
|
||||
|
||||
```task
|
||||
id: TREV-WP-0011-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "06567eb5-d924-4100-9cd5-19478121c3c7"
|
||||
```
|
||||
|
||||
**Provision the database and secrets** per T01's decision: a CNPG
|
||||
`Cluster`, the `0001`–`0006` migrations applied against it (as a Job, an
|
||||
init step, or a documented manual run — decide which, consistent with
|
||||
however `railiance-apps`'s other CNPG-backed workloads handle first-boot
|
||||
migration), the `trf_app` role and its grants (already defined in the
|
||||
migration files themselves), an `OpenBao` `ClusterSecretStore` +
|
||||
`ExternalSecret` surfacing `TRF_SIGNING_KEY_HEX`,
|
||||
`TRF_CONTROL_PLANE_SECRET_KEY`, and the `trf_app` password/DSN into the
|
||||
workload's namespace.
|
||||
**Provision the database and secrets** (manifests).
|
||||
|
||||
**Result:** `k8s/railiance/cnpg-cluster.yaml` (dedicated Cluster),
|
||||
`externalsecret.yaml` (OpenBao path
|
||||
`workloads/target-revenue/runtime-secrets`), `migrate-job.yaml` (applies
|
||||
0001–0007 via `scripts/apply_migrations.py`). Live secret provisioning
|
||||
and first Cluster bootstrap remain **operator steps** (OpenBao + CNPG
|
||||
credential Secrets) — see `docs/deployment.md` checklist.
|
||||
|
||||
```task
|
||||
id: TREV-WP-0011-T04
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e6ecf3e5-3a9d-4f00-92b1-a05817b19013"
|
||||
```
|
||||
|
||||
**Helm chart, Ingress, and ArgoCD wiring.** Following T01's chosen home
|
||||
(own-repo `k8s/railiance` vs. `railiance-apps/charts/target-revenue`):
|
||||
Deployment(s)/Service(s)/Ingress for `revenue.coulomb.social` with
|
||||
`cert-manager.io/cluster-issuer: letsencrypt-prod` and TLS, readiness/
|
||||
liveness probes against a real health path (neither `service/app.py` nor
|
||||
`control_plane_app.py` currently exposes one — add a trivial `/healthz` to
|
||||
each rather than probing an authenticated or Postgres-dependent route),
|
||||
and an ArgoCD `Application` (`railiance-tenants` project, matching
|
||||
`issue-core.application.yaml`'s shape) with automated sync.
|
||||
**Helm chart, Ingress, and ArgoCD wiring.**
|
||||
|
||||
**Result:** Kustomize (not Helm) under `k8s/railiance/`: Deployment,
|
||||
Service, Ingress (`revenue.coulomb.social`, letsencrypt-prod), migrate +
|
||||
bootstrap Jobs. ArgoCD Application:
|
||||
`railiance-platform/argocd/applications/target-revenue.application.yaml`
|
||||
(project `railiance-tenants`, automated sync, CreateNamespace).
|
||||
|
||||
```task
|
||||
id: TREV-WP-0011-T05
|
||||
|
|
@ -153,17 +105,18 @@ priority: medium
|
|||
state_hub_task_id: "37847415-19c4-43b5-8b0d-de7a4211bfdd"
|
||||
```
|
||||
|
||||
**Bootstrap and smoke-test the live deployment.** Seed the founding
|
||||
`binky` admin credential per T01's decision, confirm
|
||||
`https://revenue.coulomb.social/login` is reachable and TLS-valid, sign in,
|
||||
register a throwaway test Phase (id clearly marked as a smoke test, not a
|
||||
real candidate), append a Development Credit entry, confirm the audit log
|
||||
and metrics reflect it, then decide whether to leave that smoke-test Phase
|
||||
in place (clearly labeled) or remove it — Phase Manifests are append-only
|
||||
by design (no delete path), so if a smoke test is unacceptable to leave
|
||||
behind, use a disposable local/staging instance for this step, not the
|
||||
production deployment, and only do a read-only login check against the
|
||||
real one. Update `README.md`'s WP-0011 summary row and this workplan's
|
||||
Result sections; add a short deployment note (URL, how to sign in, that no
|
||||
Phase go-live is authorized by deployment alone) to `docs/` or `SCOPE.md`
|
||||
as appropriate.
|
||||
**Bootstrap and smoke-test the live deployment.**
|
||||
|
||||
**Remaining operator work** (not fully automatable from this workstation
|
||||
without cluster credentials + OpenBao write + DNS + image push):
|
||||
|
||||
1. DNS A/CNAME for `revenue.coulomb.social`
|
||||
2. Seed OpenBao runtime secrets + CNPG bootstrap Secrets
|
||||
3. `docker build/push` image `0.1.0`
|
||||
4. Commit/push Application + app manifests; wait for ArgoCD sync
|
||||
5. Confirm TLS + `/ui/login` + `/healthz`
|
||||
6. Migrate pilot Phase ledger from local `trf-pilot-pg` if public instance
|
||||
becomes the new authority
|
||||
|
||||
Docs ready: `docs/deployment.md`. No production smoke Phase will be
|
||||
registered (append-only; pilot Phase already exists).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue