From ca76fa23a31e811891ad4912b0b3c85f32e87734 Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 5 Aug 2026 17:56:38 +0200 Subject: [PATCH] WP-0011 T05: live deploy revenue.coulomb.social on railiance01 Image 0.1.1 (schema path fix), OpenBao ESO store, CNPG cluster, TLS, pilot Phase re-registered on public API. Founding token local-only. --- Dockerfile | 5 +- README.md | 2 +- WORK-RECORDS.md | 8 +-- docs/deployment.md | 42 ++++++------- history/260805-T05-GoLive-info-tech-canon.md | 18 +++--- k8s/railiance/bootstrap-job.yaml | 2 +- k8s/railiance/deployment.yaml | 6 +- k8s/railiance/externalsecret.yaml | 3 +- k8s/railiance/migrate-job.yaml | 2 +- scripts/openbao-eso-token-apply.sh | 60 +++++++++++++++++++ src/target_revenue/service/reference_docs.py | 21 ++++++- src/target_revenue/validation.py | 23 ++++++- .../TREV-WP-0011-railiance-reef-deployment.md | 30 ++++++---- 13 files changed, 168 insertions(+), 54 deletions(-) create mode 100755 scripts/openbao-eso-token-apply.sh diff --git a/Dockerfile b/Dockerfile index 61c14da..7c54efe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,10 @@ FROM python:3.12-slim AS runtime ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ - HOME=/home/app + HOME=/home/app \ + TRF_SCHEMAS_DIR=/src/schemas \ + TRF_SPECS_DIR=/src/specs \ + TRF_MIGRATIONS_DIR=/src/migrations RUN useradd --create-home --home-dir /home/app --uid 10001 app diff --git a/README.md b/README.md index 3fd714c..574c7ca 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ The concept's §13 now defines a **Global Contingency Share Determination Rule** | [TREV-WP-0008](workplans/TREV-WP-0008-governance-and-pilot-rollout.md) | Governance formalization + pilot rollout — **finished**, all 5 tasks done. **T05 accepted 2026-08-05**: first real Phase is `trsl:phase:info-tech-canon-service-surface` (`history/260805-T05-GoLive-info-tech-canon.md`). Org-wide TRSL LICENSE adoption (2026-07-30) was a prior, separate act | | [TREV-WP-0009](workplans/TREV-WP-0009-target-revenue-control-plane.md) | Target Revenue Control Plane — interactive UI for the `binky` tenant, incl. interactive Development Credit entry creation (`specs/TargetRevenueControlPlaneConcept.md`) — **finished**, all 4 tasks done. **T04 (interactive UI, `src/target_revenue/service/control_plane_app.py`) built on vendored `whynot-design` web components** rather than from scratch, per an explicit feasibility check | | [TREV-WP-0010](workplans/TREV-WP-0010-development-effort-calculator.md) | Development Effort Calculator — **finished**, all 3 tasks done. Applied to the three real pilot candidates (`history/260730-EffortCalculator-CandidateApplication.md`) — every calculator-derived Initial Target came out materially lower than the earlier hand-picked placeholders, two of three carrying explicit warnings recommending manual review | -| [TREV-WP-0011](workplans/TREV-WP-0011-railiance-reef-deployment.md) | Deploy Trust Service + Control Plane to Railiance (`revenue.coulomb.social`) — **active**; T01–T04 done (Dockerfile, `k8s/railiance/`, ArgoCD Application). **T05** remains: operator DNS/OpenBao/image-push/sync + pilot data migration. Guide: [`docs/deployment.md`](docs/deployment.md) | +| [TREV-WP-0011](workplans/TREV-WP-0011-railiance-reef-deployment.md) | Deploy Trust Service + Control Plane — **finished**. Live at [`https://revenue.coulomb.social`](https://revenue.coulomb.social) (UI `/ui/`). Pilot Phase registered on public API. Guide: [`docs/deployment.md`](docs/deployment.md) | | [TREV-WP-0012](workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md) | Phase provenance, ledger reference, and degeneration-policy modeling — **finished**, all 5 tasks done. Decisions (T02–T04) synthesized into [`specs/PhaseProvenanceSpecAddendum.md`](specs/PhaseProvenanceSpecAddendum.md) (T05) — **not yet accepted for implementation**; that's the document to discuss before any schema/UI work is filed as its own workplan | | [TREV-WP-0013](workplans/TREV-WP-0013-remission-credit-automation.md) | Remission Credit automation (degeneration policy execution) — **finished**, all 3 tasks done. Pure `plan_remission` + hosted apply with `system:policy-engine` actor; monthly UTC schedule; Control Plane forecast + apply button | | [TREV-WP-0014](workplans/TREV-WP-0014-control-plane-extensions-breach-attestation-ui.md) | Control Plane UI: Extension Registry, Breach Records, Conversion Attestation — **finished**, all 4 tasks done. Operator+ register extension / Admin canonicalize; breach form with anonymized-by-default; attestation on phase detail | diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md index 22a51f3..e1034b8 100644 --- a/WORK-RECORDS.md +++ b/WORK-RECORDS.md @@ -86,10 +86,10 @@ | task | TREV-WP-0010-T01 | done | — | workplans/TREV-WP-0010-development-effort-calculator.md | | task | TREV-WP-0010-T02 | done | — | workplans/TREV-WP-0010-development-effort-calculator.md | | task | TREV-WP-0010-T03 | done | — | workplans/TREV-WP-0010-development-effort-calculator.md | -| task | TREV-WP-0011-T01 | todo | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | -| task | TREV-WP-0011-T02 | todo | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | -| task | TREV-WP-0011-T03 | todo | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | -| task | TREV-WP-0011-T04 | todo | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | +| task | TREV-WP-0011-T01 | done | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | +| task | TREV-WP-0011-T02 | done | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | +| task | TREV-WP-0011-T03 | done | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | +| task | TREV-WP-0011-T04 | done | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | | task | TREV-WP-0011-T05 | todo | — | workplans/TREV-WP-0011-railiance-reef-deployment.md | | task | TREV-WP-0012-T01 | done | — | workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md | | task | TREV-WP-0012-T02 | done | — | workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md | diff --git a/docs/deployment.md b/docs/deployment.md index b822eea..0f40c18 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,9 +1,11 @@ # Deploying Target Revenue Trust Service + Control Plane **Workplan:** `workplans/TREV-WP-0011-railiance-reef-deployment.md` -**Public host (target):** `https://revenue.coulomb.social` +**Public host (live 2026-08-05):** `https://revenue.coulomb.social` **Control Plane UI:** `https://revenue.coulomb.social/ui/` -**Trust Service API:** `https://revenue.coulomb.social/` (e.g. `/phases/...`, `/public-key`) +**Trust Service API:** `https://revenue.coulomb.social/` (e.g. `/phases/...`, `/public-key`) +**Cluster:** railiance01 (`KUBECONFIG=~/.kube/config-hosteurope`) +**Image:** `forgejo.coulomb.social/coulomb/target-revenue:0.1.1` This deployment does not by itself authorize new Phases beyond what WP-0008-T05 already recorded. The first pilot Phase is @@ -25,25 +27,25 @@ WP-0008-T05 already recorded. The first pilot Phase is --- -## Operator checklist (first bring-up) +## Operator checklist (first bring-up) — completed 2026-08-05 -1. **DNS:** point `revenue.coulomb.social` at the Railiance Traefik/ingress (same path as other `*.coulomb.social` hosts on railiance01). -2. **OpenBao:** create KV path `workloads/target-revenue/runtime-secrets` with: - - `TRF_SIGNING_KEY_HEX` (64 hex chars) - - `TRF_CONTROL_PLANE_SECRET_KEY` - - `TRF_DATABASE_URL` (trf_app DSN to `target-revenue-pg-rw`) - - `TRF_MIGRATE_DATABASE_URL` (trf_owner DSN for migrate/bootstrap Jobs) -3. **CNPG bootstrap secrets** in namespace `target-revenue` (before Cluster reconciles): - - `target-revenue-pg-credentials` — keys as required by CNPG for owner `trf_owner` - - `target-revenue-trf-app-credentials` — password for managed role `trf_app` -4. **Image:** build and push - `forgejo.coulomb.social/coulomb/target-revenue:0.1.0` - (see Dockerfile; bump tag in Deployment + Jobs when releasing). -5. **ArgoCD:** apply - `railiance-platform/argocd/applications/target-revenue.application.yaml` - (once `main` contains `k8s/railiance/`). -6. **After migrate + bootstrap Jobs succeed:** pull founding token from bootstrap Job logs (`TOKEN=...`), store offline; sign in at `/ui/login`. -7. **Migrate pilot Phase data** from the local go-live instance (`trf-pilot-pg` / `:8765`) if that instance still holds the authoritative ledger — export Manifest + ledger JSON and re-register/append on the public instance (or promote the pilot DB dump deliberately). Do not leave two competing ledgers. +Deployed with **kubectl on railiance01** (forgejo/reuse interim model). +Coulombcore ArgoCD Application exists for future multi-cluster sync but was +**not** the live path for this bring-up. + +1. **DNS:** `revenue.coulomb.social` A-record → railiance01 — done. +2. **OpenBao policy + ESO token:** + `OPENBAO_TOKEN_FILE=~/.local/openbao/platform-admin.token ./scripts/openbao-eso-token-apply.sh` + + apply `openbao-target-revenue.clustersecretstore.yaml`. +3. **CNPG secrets + Cluster**, then write + `platform/workloads/target-revenue/runtime-secrets` (signing key, session secret, DSNs). +4. **Image:** `docker build/push …:0.1.1` (0.1.0 had schema-path bug; fixed). +5. **`kubectl apply -k k8s/railiance/`** on railiance01. +6. **Founding token:** bootstrap Job log → + `~/.config/target-revenue/revenue-coulomb-binky.token` (chmod 600; never commit). +7. **Phase POR:** re-registered + `trsl:phase:info-tech-canon-service-surface` on the public API; local + `trf-pilot-pg` is superseded (ledger was empty, so no entry migration). --- diff --git a/history/260805-T05-GoLive-info-tech-canon.md b/history/260805-T05-GoLive-info-tech-canon.md index 7b71fe8..68d58d2 100644 --- a/history/260805-T05-GoLive-info-tech-canon.md +++ b/history/260805-T05-GoLive-info-tech-canon.md @@ -82,18 +82,18 @@ from day one, not merely a git file: | Item | Value | | --- | --- | -| Trust Service API | `http://127.0.0.1:8765` (local pilot; not public) | -| Postgres | Docker container `trf-pilot-pg`, port `127.0.0.1:55432`, volume `trf-pilot-pgdata` | -| Migrations | 0001–0007 applied | -| Licensor credential | `binky` / label `pilot-founder` (token in `~/.config/target-revenue/pilot-binky.token`, never committed) | -| Registration time (t0) | 2026-08-05T13:57:18Z (host `registered_at`) | +| Trust Service API (initial) | `http://127.0.0.1:8765` (local pilot; used only until public deploy) | +| Trust Service API (**POR, 2026-08-05 later same day**) | `https://revenue.coulomb.social` (railiance01; WP-0011 T05) | +| Control Plane | `https://revenue.coulomb.social/ui/` | +| Postgres (POR) | CNPG `target-revenue-pg` in namespace `target-revenue` | +| Licensor credential | `binky` / label `founding-admin` (token in `~/.config/target-revenue/revenue-coulomb-binky.token`, never committed) | +| Registration time (t0) on POR | 2026-08-05T15:55:52Z (public re-registration; local pilot ledger was empty) | | Extension registered | `trsl:extension:development-license@1.0` | | Ledger at registration | empty; Outstanding Target = 141,800 EUR | -When WP-0011 lands a public instance, this Phase's Manifest and ledger -export must be migrated or re-anchored deliberately — the git-committed -Manifest remains the durable declaration either way -(`specs/TrustServiceOnboarding.md` §1). +Git-committed Manifest remains the durable declaration +(`specs/TrustServiceOnboarding.md` §1). Public Trust Service is the live +ledger authority after WP-0011 T05. --- diff --git a/k8s/railiance/bootstrap-job.yaml b/k8s/railiance/bootstrap-job.yaml index 73b91f2..4f76169 100644 --- a/k8s/railiance/bootstrap-job.yaml +++ b/k8s/railiance/bootstrap-job.yaml @@ -30,7 +30,7 @@ spec: fsGroup: 10001 containers: - name: bootstrap - image: forgejo.coulomb.social/coulomb/target-revenue:0.1.0 + image: forgejo.coulomb.social/coulomb/target-revenue:0.1.1 imagePullPolicy: IfNotPresent env: - name: TRF_BOOTSTRAP_BINKY diff --git a/k8s/railiance/deployment.yaml b/k8s/railiance/deployment.yaml index d6796c3..2ab4302 100644 --- a/k8s/railiance/deployment.yaml +++ b/k8s/railiance/deployment.yaml @@ -25,12 +25,16 @@ spec: fsGroup: 10001 containers: - name: target-revenue - image: forgejo.coulomb.social/coulomb/target-revenue:0.1.0 + image: forgejo.coulomb.social/coulomb/target-revenue:0.1.1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 8000 env: + - name: TRF_SCHEMAS_DIR + value: /src/schemas + - name: TRF_SPECS_DIR + value: /src/specs - name: TRF_DATABASE_URL valueFrom: secretKeyRef: diff --git a/k8s/railiance/externalsecret.yaml b/k8s/railiance/externalsecret.yaml index f471265..3eef170 100644 --- a/k8s/railiance/externalsecret.yaml +++ b/k8s/railiance/externalsecret.yaml @@ -22,7 +22,8 @@ metadata: spec: refreshInterval: 1h secretStoreRef: - name: openbao + # Railiance01 interim store (token auth), not coulombcore's openbao store. + name: openbao-target-revenue kind: ClusterSecretStore target: name: target-revenue-runtime diff --git a/k8s/railiance/migrate-job.yaml b/k8s/railiance/migrate-job.yaml index 7daf9b3..80b38e2 100644 --- a/k8s/railiance/migrate-job.yaml +++ b/k8s/railiance/migrate-job.yaml @@ -28,7 +28,7 @@ spec: fsGroup: 10001 containers: - name: migrate - image: forgejo.coulomb.social/coulomb/target-revenue:0.1.0 + image: forgejo.coulomb.social/coulomb/target-revenue:0.1.1 imagePullPolicy: IfNotPresent env: - name: TRF_RUN_MIGRATIONS diff --git a/scripts/openbao-eso-token-apply.sh b/scripts/openbao-eso-token-apply.sh new file mode 100755 index 0000000..1895280 --- /dev/null +++ b/scripts/openbao-eso-token-apply.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Mint a read-limited OpenBao token and store it on railiance01 for +# ClusterSecretStore openbao-target-revenue (WP-0011). +set -euo pipefail + +POLICY_NAME="${OPENBAO_TARGET_REVENUE_POLICY:-workload-kv-read-target-revenue-runtime}" +POLICY_FILE="${OPENBAO_TARGET_REVENUE_POLICY_FILE:-$HOME/railiance-platform/openbao/policies/workload-kv-read-target-revenue-runtime.hcl}" +BAO_ADDR="${BAO_ADDR:-https://bao.coulomb.social}" +RAILIANCE01_KUBECONFIG="${RAILIANCE01_KUBECONFIG:-$HOME/.kube/config-hosteurope}" +SECRET_NAME="${OPENBAO_TARGET_REVENUE_ESO_SECRET:-openbao-target-revenue-eso-token}" +SECRET_NS="${OPENBAO_TARGET_REVENUE_ESO_NAMESPACE:-external-secrets}" + +if ! command -v bao >/dev/null 2>&1; then + echo "ERROR: bao CLI not found" >&2 + exit 1 +fi +if [[ ! -f "$POLICY_FILE" ]]; then + echo "ERROR: policy file missing: $POLICY_FILE" >&2 + exit 1 +fi + +echo "OpenBao addr: $BAO_ADDR" +echo "Policy: $POLICY_NAME" + +if [[ -n "${BAO_TOKEN:-}" ]]; then + : +elif [[ -n "${OPENBAO_TOKEN_FILE:-}" && -f "${OPENBAO_TOKEN_FILE}" ]]; then + BAO_TOKEN="$(head -n 1 "${OPENBAO_TOKEN_FILE}")" +else + read -r -s -p "OpenBao operator token: " BAO_TOKEN + echo >&2 +fi +if [[ -z "${BAO_TOKEN:-}" ]]; then + echo "ERROR: empty OpenBao token" >&2 + exit 1 +fi + +export BAO_ADDR BAO_TOKEN + +health="$(curl -fsS "$BAO_ADDR/v1/sys/health")" +if echo "$health" | grep -q '"sealed":true'; then + echo "ERROR: OpenBao at $BAO_ADDR reports sealed" >&2 + exit 1 +fi + +bao policy write "$POLICY_NAME" "$POLICY_FILE" +token_json="$(bao token create -policy="$POLICY_NAME" \ + -display-name="eso-target-revenue-runtime" -period=720h -format=json)" +token="$(python3 -c "import json,sys; print(json.load(sys.stdin)['auth']['client_token'])" <<<"$token_json")" + +KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl create namespace "$SECRET_NS" --dry-run=client -o yaml \ + | KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl apply -f - +KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl create secret generic "$SECRET_NAME" \ + --namespace "$SECRET_NS" \ + --from-literal=token="$token" \ + --dry-run=client -o yaml \ + | KUBECONFIG="$RAILIANCE01_KUBECONFIG" kubectl apply -f - + +unset BAO_TOKEN token token_json +echo "ok: applied $SECRET_NS/$SECRET_NAME on railiance01 (token not printed)" diff --git a/src/target_revenue/service/reference_docs.py b/src/target_revenue/service/reference_docs.py index d8feeb4..61cbcd6 100644 --- a/src/target_revenue/service/reference_docs.py +++ b/src/target_revenue/service/reference_docs.py @@ -10,13 +10,32 @@ small markdown library for six profile pages and one policy page. from __future__ import annotations +import os from pathlib import Path from typing import Any import markdown import yaml -_SPECS_DIR = Path(__file__).resolve().parents[3] / "specs" + +def _resolve_specs_dir() -> Path: + if env := os.environ.get("TRF_SPECS_DIR"): + return Path(env) + here = Path(__file__).resolve() + candidates = [ + here.parents[3] / "specs", # monorepo: .../target-revenue/specs + Path("/src/specs"), # container image layout + ] + for candidate in candidates: + if candidate.is_dir(): + return candidate + raise FileNotFoundError( + "cannot find TRF specs directory; set TRF_SPECS_DIR or install " + "specs at /src/specs" + ) + + +_SPECS_DIR = _resolve_specs_dir() # kind -> subdirectory name under specs/. Only these two exist today # (WP-0015-T02); a third kind (e.g. "calculators") can be added here if diff --git a/src/target_revenue/validation.py b/src/target_revenue/validation.py index b9cd4c6..af066a4 100644 --- a/src/target_revenue/validation.py +++ b/src/target_revenue/validation.py @@ -14,12 +14,33 @@ at import/call time (TSD §6.1 determinism). from __future__ import annotations import json +import os from pathlib import Path from typing import Any import jsonschema -_SCHEMAS_DIR = Path(__file__).resolve().parents[2] / "schemas" + +def _resolve_schemas_dir() -> Path: + """Locate `schemas/` in monorepo, container, or packaged installs.""" + if env := os.environ.get("TRF_SCHEMAS_DIR"): + return Path(env) + here = Path(__file__).resolve() + candidates = [ + here.parent / "schemas", # if schemas are package data + here.parents[2] / "schemas", # repo: src/target_revenue/../.. + Path("/src/schemas"), # container image layout (WP-0011 Dockerfile) + ] + for candidate in candidates: + if candidate.is_dir(): + return candidate + raise FileNotFoundError( + "cannot find TRF schemas directory; set TRF_SCHEMAS_DIR or install " + "schemas next to the package / at /src/schemas" + ) + + +_SCHEMAS_DIR = _resolve_schemas_dir() # TSD §6.2/§3.3: an extension's allocation.rule must not redefine these. CORE_TERMS = [ diff --git a/workplans/TREV-WP-0011-railiance-reef-deployment.md b/workplans/TREV-WP-0011-railiance-reef-deployment.md index 5cc9195..eda7390 100644 --- a/workplans/TREV-WP-0011-railiance-reef-deployment.md +++ b/workplans/TREV-WP-0011-railiance-reef-deployment.md @@ -4,7 +4,7 @@ type: workplan title: "Deploy Trust Service and Control Plane to the Railiance reef" domain: infotech repo: target-revenue -status: active +status: finished owner: claude topic_slug: infotech created: "2026-07-30" @@ -100,23 +100,27 @@ bootstrap Jobs. ArgoCD Application: ```task id: TREV-WP-0011-T05 -status: todo +status: done priority: medium state_hub_task_id: "37847415-19c4-43b5-8b0d-de7a4211bfdd" ``` **Bootstrap and smoke-test the live deployment.** -**Remaining operator work** (not fully automatable from this workstation -without cluster credentials + OpenBao write + DNS + image push): +**Result (2026-08-05):** Live on **railiance01** (not coulombcore ArgoCD — +same interim model as forgejo/reuse; DNS points to Railiance01): -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 +| Check | Result | +| --- | --- | +| DNS | `revenue.coulomb.social` → railiance01 (operator) | +| Image | `forgejo.coulomb.social/coulomb/target-revenue:0.1.1` | +| OpenBao ESO | `ClusterSecretStore/openbao-target-revenue` Ready; runtime SecretSynced | +| CNPG | `target-revenue-pg` healthy | +| Migrations + bootstrap Jobs | complete; founding token local-only at `~/.config/target-revenue/revenue-coulomb-binky.token` | +| TLS | cert-manager Ready (`target-revenue-tls`) | +| `GET /healthz` | 200 | +| `GET /ui/login` | 200 | +| Pilot Phase re-registered | `trsl:phase:info-tech-canon-service-surface` on public API (empty ledger; public instance is now POR — local `trf-pilot-pg` is superseded) | -Docs ready: `docs/deployment.md`. No production smoke Phase will be -registered (append-only; pilot Phase already exists). +Schema-path fix in 0.1.1 (`TRF_SCHEMAS_DIR` / validation resolve) after +first image failed package-relative schema loads. \ No newline at end of file