From a26abe8334e0a2fb3d1e1c083476d56b9a429c27 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 2 Jul 2026 19:21:38 +0200 Subject: [PATCH 01/10] chore(consistency): sync task status from DB [auto] Updated by fix-consistency on 2026-07-02: - update .custodian-brief.md for core-hub --- .custodian-brief.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.custodian-brief.md b/.custodian-brief.md index dd1967f..7806364 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,7 +2,7 @@ # Custodian Brief — core-hub **Domain:** infotech -**Last synced:** 2026-07-02 16:13 UTC +**Last synced:** 2026-07-02 17:21 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams @@ -17,10 +17,9 @@ Progress: 0/4 done | workstream_id: `a8d66822-e435-4b1e-ad81-37a298d1795e` - · Retire Probe Repo `d3a494a3` ### Migrate Inter-Hub data and cut over production -Progress: 1/4 done | workstream_id: `a36efc08-5906-40c7-b1de-f3a553ffea02` +Progress: 2/4 done | workstream_id: `a36efc08-5906-40c7-b1de-f3a553ffea02` **Open tasks:** -- ! Run Staging Import `e0ea0928` - ! Dual-Run Smokes `9166afe8` - ! Production Cutover `fa68858a` From 1f1288ad945d08d4e586aff417500a1536dd4936 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 2 Jul 2026 19:24:09 +0200 Subject: [PATCH 02/10] CORE-WP-0005-T03 done: dual-run compat deltas; found ops-* widget-type catalog gap for cutover Co-Authored-By: Claude Fable 5 --- ...CORE-WP-0005-data-migration-and-cutover.md | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/workplans/CORE-WP-0005-data-migration-and-cutover.md b/workplans/CORE-WP-0005-data-migration-and-cutover.md index 4d8d61d..c33992f 100644 --- a/workplans/CORE-WP-0005-data-migration-and-cutover.md +++ b/workplans/CORE-WP-0005-data-migration-and-cutover.md @@ -47,7 +47,7 @@ actual import can run. ```task id: CORE-WP-0005-T03 -status: wait +status: done priority: high state_hub_task_id: "9166afe8-27ef-4345-a7b1-c33f15c90673" ``` @@ -84,3 +84,40 @@ second run proved idempotency (created=0, updated=28, no duplicates). Server-side confirmed: the imported `ops-hub` hub and its 15 widgets are visible via `/api/v2/*`. T02 done; the staging DB now holds real Inter-Hub data for the T03 dual-run smokes. + + +## Progress 2026-07-02 — dual-run smokes and compatibility deltas + +Ran unauthenticated + authenticated probes against both live surfaces via +ops-bridge tunnels (Inter-Hub `inter-hub-coulombcore`:18020, Core Hub staging +`core-hub-staging-coulombcore`:18010). + +Status parity table (unauthenticated GET): + +| Endpoint | Inter-Hub | Core Hub | Note | +| --- | --- | --- | --- | +| `/healthz`, `/readyz` | 404 | 200 | Core Hub adds liveness/readiness (new capability, not a regression). | +| `/api/v2/hubs` | 200 | 401 | **Auth delta**: hub listing is public on Inter-Hub, protected on Core Hub. Anonymous consumers must authenticate against Core Hub. | +| `/api/v2/hub-registry` | 401 | 401 | Parity. | +| `/api/v2/widget-types`, `event-types`, `annotation-categories`, `policy-scopes` | 200 | 200 | Public-catalog status parity. | +| `/api/v2/widgets`, `interaction-events` | 401 | 401 | Parity. | + +Content/shape deltas: + +- Catalog record shape differs: Core Hub uses `{name, slug, description}`; + Inter-Hub uses `{name, label, description, ownerHubId, status}`. Consumers + parsing catalog records need updates for the `slug`-based shape. +- **Cutover blocker found**: the migrated Inter-Hub widgets reference 7 + widget-type names (`ops-endpoint`, `ops-environment`, `ops-host`, + `ops-migration-wave`, `ops-readiness-gate`, `ops-service`, + `ops-service-catalog`) that are **absent** from Core Hub's seeded + widget-type registry (which has only `Event Stream`, `Status Summary`, + `Workplan Board`). The `core-hub.migration.v1` bundle schema (COLLECTIONS) + carries widgets but not the `widget_type_registry` / `event_type_registry`, + so migrated widgets land referentially incomplete. **Production cutover + (T04) must first migrate or seed the ops-* widget-type and event-type + vocabularies into Core Hub.** Filed as the top T04 prerequisite. + +T03 done: compatibility surface is characterized; the auth-hardening delta and +the missing-catalog-vocabulary blocker are the two items the cutover decision +must account for. From a43b9fe640cb230a1bd39c863c82b333812c4443 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 2 Jul 2026 19:24:39 +0200 Subject: [PATCH 03/10] chore(consistency): sync task status from DB [auto] Updated by fix-consistency on 2026-07-02: - update .custodian-brief.md for core-hub --- .custodian-brief.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.custodian-brief.md b/.custodian-brief.md index 7806364..45a83b2 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,7 +2,7 @@ # Custodian Brief — core-hub **Domain:** infotech -**Last synced:** 2026-07-02 17:21 UTC +**Last synced:** 2026-07-02 17:24 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams @@ -17,10 +17,9 @@ Progress: 0/4 done | workstream_id: `a8d66822-e435-4b1e-ad81-37a298d1795e` - · Retire Probe Repo `d3a494a3` ### Migrate Inter-Hub data and cut over production -Progress: 2/4 done | workstream_id: `a36efc08-5906-40c7-b1de-f3a553ffea02` +Progress: 3/4 done | workstream_id: `a36efc08-5906-40c7-b1de-f3a553ffea02` **Open tasks:** -- ! Dual-Run Smokes `9166afe8` - ! Production Cutover `fa68858a` --- From 0a23c9a9b445e5df7f69a8b6a5d37f0a7809e2d0 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 2 Jul 2026 19:25:24 +0200 Subject: [PATCH 04/10] CORE-WP-0005-T04: record cutover prerequisites from T03 (catalog vocab + auth posture) Co-Authored-By: Claude Fable 5 --- .../CORE-WP-0005-data-migration-and-cutover.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/workplans/CORE-WP-0005-data-migration-and-cutover.md b/workplans/CORE-WP-0005-data-migration-and-cutover.md index c33992f..28889db 100644 --- a/workplans/CORE-WP-0005-data-migration-and-cutover.md +++ b/workplans/CORE-WP-0005-data-migration-and-cutover.md @@ -67,6 +67,21 @@ state_hub_task_id: "fa68858a-b18e-4f09-a477-6b11e54099d4" With operator approval, switch production traffic such as `hub.coulomb.social` to Core Hub. Keep rollback available until stabilization passes. +**Prerequisites (blocking, found in T03 dual-run 2026-07-02):** + +1. **Migrate/seed the ops-* type vocabularies.** Migrated Inter-Hub widgets + reference 7 widget types (`ops-endpoint`, `ops-environment`, `ops-host`, + `ops-migration-wave`, `ops-readiness-gate`, `ops-service`, + `ops-service-catalog`) plus their event types, none of which exist in Core + Hub's seeded registries. Either extend the `core-hub.migration.v1` bundle + with `widgetTypeRegistry` / `eventTypeRegistry` collections, or seed the + ops-* vocabularies into Core Hub before cutover, or migrated widgets are + referentially incomplete. +2. **Auth-posture change is a consumer break.** `/api/v2/hubs` is public on + Inter-Hub but protected on Core Hub — any anonymous consumer of the hub + list must gain a token before cutover. +3. Operator approval + rollback plan (existing gate). + ## Progress 2026-07-02 — staging import completed From a33191ab3a0e7b2d1e889f62fdd75b5eaaaf643b Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 3 Jul 2026 01:39:45 +0200 Subject: [PATCH 05/10] CORE-WP-0005-T04 prerequisites resolved: seed full Inter-Hub vocabulary; keep /hubs protected (ops-hub gate expects 401) Co-Authored-By: Claude Fable 5 --- src/core_hub/seeds.py | 57 +++++++++++++++++++ ...CORE-WP-0005-data-migration-and-cutover.md | 18 ++++++ 2 files changed, 75 insertions(+) diff --git a/src/core_hub/seeds.py b/src/core_hub/seeds.py index bf78096..b94bd6a 100644 --- a/src/core_hub/seeds.py +++ b/src/core_hub/seeds.py @@ -35,12 +35,69 @@ WIDGET_TYPES = [ CatalogItem(slug="status-summary", name="Status Summary"), CatalogItem(slug="workplan-board", name="Workplan Board"), CatalogItem(slug="event-stream", name="Event Stream"), + # Inter-Hub compatibility vocabulary (CORE-WP-0005-T04 prerequisite 1): + # the full legacy widget_type_registry so migrated widgets stay + # referentially complete and legacy catalog consumers see a superset. + CatalogItem(slug="action", name="Action Control", description="Button, link, or trigger widget"), + CatalogItem(slug="chart", name="Chart", description="Data visualisation chart widget"), + CatalogItem(slug="chat", name="Chat Region", description="Conversational interaction region"), + CatalogItem(slug="diff", name="Diff / Review", description="Code diff or change review element"), + CatalogItem(slug="form", name="Form", description="Data entry form widget"), + CatalogItem(slug="ops-backup-set", name="ops-backup-set"), + CatalogItem(slug="ops-cluster", name="ops-cluster"), + CatalogItem(slug="ops-endpoint", name="ops-endpoint"), + CatalogItem(slug="ops-environment", name="ops-environment"), + CatalogItem(slug="ops-host", name="ops-host"), + CatalogItem(slug="ops-incident", name="ops-incident"), + CatalogItem(slug="ops-migration-wave", name="ops-migration-wave"), + CatalogItem(slug="ops-readiness-gate", name="ops-readiness-gate"), + CatalogItem(slug="ops-release", name="ops-release"), + CatalogItem(slug="ops-risk", name="ops-risk"), + CatalogItem(slug="ops-runbook", name="ops-runbook"), + CatalogItem(slug="ops-secret-set", name="ops-secret-set"), + CatalogItem(slug="ops-service", name="ops-service"), + CatalogItem(slug="ops-service-catalog", name="ops-service-catalog"), + CatalogItem(slug="panel", name="Status Panel", description="Summary or status information panel"), + CatalogItem(slug="recommendation", name="Recommendation", description="AI or system recommendation block"), + CatalogItem(slug="table", name="Table", description="Tabular data display widget"), + CatalogItem(slug="workflow-step", name="Workflow Step", description="Single step in a multi-step workflow"), ] EVENT_TYPES = [ CatalogItem(slug="interaction.event", name="Interaction Event"), CatalogItem(slug="workplan.progress", name="Workplan Progress"), CatalogItem(slug="evidence.recorded", name="Evidence Recorded"), + # Inter-Hub compatibility vocabulary (see WIDGET_TYPES note). + CatalogItem(slug="abandoned", name="Abandoned"), + CatalogItem(slug="accepted_recommendation", name="Accepted Recommendation"), + CatalogItem(slug="blocked_by_policy", name="Blocked by Policy"), + CatalogItem(slug="clicked", name="Clicked"), + CatalogItem(slug="commented", name="Commented"), + CatalogItem(slug="escalated", name="Escalated"), + CatalogItem(slug="failed", name="Failed"), + CatalogItem(slug="flagged_confusing", name="Flagged Confusing"), + CatalogItem(slug="flagged_helpful", name="Flagged Helpful"), + CatalogItem(slug="focused", name="Focused"), + CatalogItem(slug="ops-backup-verified", name="ops-backup-verified"), + CatalogItem(slug="ops-drift-detected", name="ops-drift-detected"), + CatalogItem(slug="ops-endpoint-verified", name="ops-endpoint-verified"), + CatalogItem(slug="ops-health-checked", name="ops-health-checked"), + CatalogItem(slug="ops-inventory-registered", name="ops-inventory-registered"), + CatalogItem(slug="ops-inventory-updated", name="ops-inventory-updated"), + CatalogItem(slug="ops-migration-gate-failed", name="ops-migration-gate-failed"), + CatalogItem(slug="ops-migration-gate-passed", name="ops-migration-gate-passed"), + CatalogItem(slug="ops-readiness-gate-updated", name="ops-readiness-gate-updated"), + CatalogItem(slug="ops-release-observed", name="ops-release-observed"), + CatalogItem(slug="ops-restore-tested", name="ops-restore-tested"), + CatalogItem(slug="ops-risk-accepted", name="ops-risk-accepted"), + CatalogItem(slug="ops-risk-raised", name="ops-risk-raised"), + CatalogItem(slug="ops-runbook-executed", name="ops-runbook-executed"), + CatalogItem(slug="ops-service-discovered", name="ops-service-discovered"), + CatalogItem(slug="rejected_recommendation", name="Rejected Recommendation"), + CatalogItem(slug="retracted", name="Retracted"), + CatalogItem(slug="retried", name="Retried"), + CatalogItem(slug="submitted", name="Submitted"), + CatalogItem(slug="viewed", name="Viewed"), ] ANNOTATION_CATEGORIES = [ diff --git a/workplans/CORE-WP-0005-data-migration-and-cutover.md b/workplans/CORE-WP-0005-data-migration-and-cutover.md index 28889db..21d8a9b 100644 --- a/workplans/CORE-WP-0005-data-migration-and-cutover.md +++ b/workplans/CORE-WP-0005-data-migration-and-cutover.md @@ -82,6 +82,24 @@ With operator approval, switch production traffic such as `hub.coulomb.social` t list must gain a token before cutover. 3. Operator approval + rollback plan (existing gate). +**Prerequisite resolutions 2026-07-03:** + +1. **Catalog gap — resolved by seeding.** Core Hub's catalogs are static + in-code seeds (`seeds.py`), not DB registries, so extending the migration + bundle schema was the wrong shape. Seeded the complete Inter-Hub + vocabulary instead (23 widget types incl. all 7 orphaned `ops-*` types, + 30 event types) as an explicit Inter-Hub-compatibility block; existing + Core Hub seeds retained. 22 tests pass. Migrated widgets are + referentially complete once the new image ships. +2. **Auth posture — resolved by keeping Core Hub protected.** Consumer + audit found only two `/api/v2/hubs` status consumers: + `ops-hub/interhub_gate_probe.py` **asserts 401** (it encodes the desired + hardened contract and currently fails against Inter-Hub production, + which serves 200), and `railiance-forge/tools/gitea-runner-status.sh` + is informational. No anonymous consumer depends on a 200. Core Hub's + protected listing is therefore an intentional hardening that satisfies + the ops-hub gate at cutover; no code change made. + ## Progress 2026-07-02 — staging import completed From defe4a6af8d1c3fff2d063083d91884c0f2d978f Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 3 Jul 2026 01:41:38 +0200 Subject: [PATCH 06/10] Production deployment profile + cutover runbook for hub.coulomb.social (CORE-WP-0005-T04) Co-Authored-By: Claude Fable 5 --- k8s/railiance-production/00-namespace.yaml | 9 ++ k8s/railiance-production/20-runtime.yaml | 113 ++++++++++++++++++ k8s/railiance-production/30-ingress.yaml | 30 +++++ k8s/railiance-production/README.md | 47 ++++++++ .../rollback-inter-hub-ingress.yaml | 39 ++++++ 5 files changed, 238 insertions(+) create mode 100644 k8s/railiance-production/00-namespace.yaml create mode 100644 k8s/railiance-production/20-runtime.yaml create mode 100644 k8s/railiance-production/30-ingress.yaml create mode 100644 k8s/railiance-production/README.md create mode 100644 k8s/railiance-production/rollback-inter-hub-ingress.yaml diff --git a/k8s/railiance-production/00-namespace.yaml b/k8s/railiance-production/00-namespace.yaml new file mode 100644 index 0000000..aa823a7 --- /dev/null +++ b/k8s/railiance-production/00-namespace.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: core-hub + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/part-of: core-hub + environment: production + railiance.io/postgres-client: apps-pg diff --git a/k8s/railiance-production/20-runtime.yaml b/k8s/railiance-production/20-runtime.yaml new file mode 100644 index 0000000..f19a822 --- /dev/null +++ b/k8s/railiance-production/20-runtime.yaml @@ -0,0 +1,113 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: core-hub-prod-runtime + namespace: core-hub + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/part-of: core-hub + environment: production +data: + CORE_HUB_ENV: production + CORE_HUB_AUTO_CREATE_TABLES: "0" +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: core-hub-prod-migrate + namespace: core-hub + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/component: migration + app.kubernetes.io/part-of: core-hub + environment: production +spec: + backoffLimit: 1 + template: + metadata: + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/component: migration + app.kubernetes.io/part-of: core-hub + environment: production + spec: + restartPolicy: Never + containers: + - name: migrate + image: gitea.coulomb.social/coulomb/core-hub:production-placeholder + imagePullPolicy: IfNotPresent + command: ["alembic", "upgrade", "head"] + envFrom: + - configMapRef: + name: core-hub-prod-runtime + - secretRef: + name: core-hub-prod-env +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: core-hub-api + namespace: core-hub + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/component: api + app.kubernetes.io/part-of: core-hub + environment: production +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/component: api + template: + metadata: + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/component: api + app.kubernetes.io/part-of: core-hub + environment: production + spec: + containers: + - name: api + image: gitea.coulomb.social/coulomb/core-hub:production-placeholder + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 8010 + envFrom: + - configMapRef: + name: core-hub-prod-runtime + - secretRef: + name: core-hub-prod-env + readinessProbe: + httpGet: + path: /readyz + port: http + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 15 + periodSeconds: 20 +--- +apiVersion: v1 +kind: Service +metadata: + name: core-hub-api + namespace: core-hub + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/component: api + app.kubernetes.io/part-of: core-hub + environment: production +spec: + type: ClusterIP + selector: + app.kubernetes.io/name: core-hub + app.kubernetes.io/component: api + ports: + - name: http + port: 8010 + targetPort: http diff --git a/k8s/railiance-production/30-ingress.yaml b/k8s/railiance-production/30-ingress.yaml new file mode 100644 index 0000000..454e299 --- /dev/null +++ b/k8s/railiance-production/30-ingress.yaml @@ -0,0 +1,30 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: core-hub + namespace: core-hub + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + labels: + app.kubernetes.io/name: core-hub + app.kubernetes.io/part-of: core-hub + environment: production +spec: + ingressClassName: traefik + rules: + - host: hub.coulomb.social + http: + paths: + - backend: + service: + name: core-hub-api + port: + number: 8010 + path: / + pathType: Prefix + tls: + - hosts: + - hub.coulomb.social + secretName: core-hub-tls diff --git a/k8s/railiance-production/README.md b/k8s/railiance-production/README.md new file mode 100644 index 0000000..8e51392 --- /dev/null +++ b/k8s/railiance-production/README.md @@ -0,0 +1,47 @@ +# Core Hub production deployment (hub.coulomb.social cutover) + +`30-ingress.yaml` is the cutover flip — do NOT apply it while the inter-hub +ingress still claims `hub.coulomb.social`. + +## Sequence + +```bash +# 1. Image (operator: registry push) +IMAGE_REPOSITORY=gitea.coulomb.social/coulomb/core-hub IMAGE_TAG= make container-build +docker push gitea.coulomb.social/coulomb/core-hub: + +# 2. Prod DB + secret (operator: shared-DB DDL) +# role core_hub / db core_hub on apps-pg; secret core-hub-prod-env with +# CORE_HUB_DATABASE_URL (postgresql+asyncpg://...apps-pg-rw.databases...:5432/core_hub) +# and CORE_HUB_API_TOKEN (generated) + +# 3. Deploy (no traffic yet) +kubectl apply -f k8s/railiance-production/00-namespace.yaml +kubectl apply -f k8s/railiance-production/20-runtime.yaml +kubectl -n core-hub set image job/core-hub-prod-migrate migrate=gitea.coulomb.social/coulomb/core-hub: +kubectl -n core-hub set image deploy/core-hub-api api=gitea.coulomb.social/coulomb/core-hub: +kubectl -n core-hub wait --for=condition=complete job/core-hub-prod-migrate --timeout=180s +kubectl -n core-hub rollout status deploy/core-hub-api --timeout=180s + +# 4. Final data import (fresh Inter-Hub export at cutover time) +kubectl exec -i -n databases net-kingdom-pg-1 -- psql -U postgres -d interhub -tA \ + < scripts/migration/inter_hub_export.sql > final-bundle.json +# validate, then import in-cluster (see scripts/migration/README.md) + +# 5. THE FLIP (operator approval point) +kubectl -n inter-hub delete ingress inter-hub +kubectl apply -f k8s/railiance-production/30-ingress.yaml +# cert-manager issues core-hub-tls for hub.coulomb.social (~1 min) + +# 6. Verify +curl -s https://hub.coulomb.social/healthz # 200 +curl -s https://hub.coulomb.social/api/v2/widget-types | head -c 200 +curl -s -o /dev/null -w '%{http_code}' https://hub.coulomb.social/api/v2/hubs # 401 = hardened contract + +## Rollback (any time; Inter-Hub deployment stays running) +kubectl delete -f k8s/railiance-production/30-ingress.yaml +kubectl -n inter-hub apply -f # or helm upgrade inter-hub +``` + +Keep the Inter-Hub deployment/service running untouched until stabilization +passes (CORE-WP-0005-T04); only the ingress moves. diff --git a/k8s/railiance-production/rollback-inter-hub-ingress.yaml b/k8s/railiance-production/rollback-inter-hub-ingress.yaml new file mode 100644 index 0000000..b1d7628 --- /dev/null +++ b/k8s/railiance-production/rollback-inter-hub-ingress.yaml @@ -0,0 +1,39 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + meta.helm.sh/release-name: inter-hub + meta.helm.sh/release-namespace: inter-hub + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + creationTimestamp: "2026-06-14T12:49:01Z" + generation: 1 + labels: + app: inter-hub + app.kubernetes.io/managed-by: Helm + name: inter-hub + namespace: inter-hub + resourceVersion: "14479609" + uid: 2f1d1557-76be-445c-b77b-e03e7b0c3935 +spec: + ingressClassName: traefik + rules: + - host: hub.coulomb.social + http: + paths: + - backend: + service: + name: inter-hub + port: + number: 8000 + path: / + pathType: Prefix + tls: + - hosts: + - hub.coulomb.social + secretName: inter-hub-tls +status: + loadBalancer: + ingress: + - ip: 92.205.130.254 From 173e8d9cfc9f226604e0156262e7b9f5adeb67b2 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 3 Jul 2026 11:25:01 +0200 Subject: [PATCH 07/10] CORE-WP-0005-T04 done: hub.coulomb.social cut over to Core Hub (verified, rollback retained) Co-Authored-By: Claude Fable 5 --- ...CORE-WP-0005-data-migration-and-cutover.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/workplans/CORE-WP-0005-data-migration-and-cutover.md b/workplans/CORE-WP-0005-data-migration-and-cutover.md index 21d8a9b..3f27817 100644 --- a/workplans/CORE-WP-0005-data-migration-and-cutover.md +++ b/workplans/CORE-WP-0005-data-migration-and-cutover.md @@ -60,7 +60,7 @@ Run public and protected smokes against both Inter-Hub and Core Hub. Record comp ```task id: CORE-WP-0005-T04 -status: wait +status: done priority: high state_hub_task_id: "fa68858a-b18e-4f09-a477-6b11e54099d4" ``` @@ -154,3 +154,20 @@ Content/shape deltas: T03 done: compatibility surface is characterized; the auth-hardening delta and the missing-catalog-vocabulary blocker are the two items the cutover decision must account for. + + +## Completion 2026-07-03 — production cutover executed + +Operator-approved and operator-executed flip: `hub.coulomb.social` ingress +moved from Inter-Hub to Core Hub (`core-hub` namespace, image `a33191a`). +Verified post-flip: cert-manager issued `core-hub-tls`; `/healthz` and +`/readyz` 200; public catalogs 200 serving 26 widget types (all formerly +orphaned `ops-*` types present); `/api/v2/hubs` 401 — the hardened contract +the ops-hub gate probe asserts. Post-flip idempotent reconcile import: 0 +created / 28 updated / 0 errors (no Inter-Hub writes were missed). + +Rollback remains one command (`30-ingress.yaml` delete + +`rollback-inter-hub-ingress.yaml` apply) and the Inter-Hub deployment stays +running untouched through the stabilization window. `CORE-WP-0007` Haskell +retirement stays gated until stabilization passes and the operator approves +retiring the fallback. From c77706c94cf33d7fa74edcfe483f8dfb876ce269 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 3 Jul 2026 11:25:59 +0200 Subject: [PATCH 08/10] chore(consistency): sync task status from DB [auto] Updated by fix-consistency on 2026-07-03: - update .custodian-brief.md for core-hub --- .custodian-brief.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.custodian-brief.md b/.custodian-brief.md index 45a83b2..58efeec 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,7 +2,7 @@ # Custodian Brief — core-hub **Domain:** infotech -**Last synced:** 2026-07-02 17:24 UTC +**Last synced:** 2026-07-03 09:25 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams @@ -17,10 +17,7 @@ Progress: 0/4 done | workstream_id: `a8d66822-e435-4b1e-ad81-37a298d1795e` - · Retire Probe Repo `d3a494a3` ### Migrate Inter-Hub data and cut over production -Progress: 3/4 done | workstream_id: `a36efc08-5906-40c7-b1de-f3a553ffea02` - -**Open tasks:** -- ! Production Cutover `fa68858a` +Progress: 4/4 done | workstream_id: `a36efc08-5906-40c7-b1de-f3a553ffea02` --- ## MCP Orientation (when available) From 5371eaf13960039616628b808c4052c9c8c6dfaa Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 3 Jul 2026 18:36:27 +0200 Subject: [PATCH 09/10] chore(consistency): sync task status from DB [auto] Updated by fix-consistency on 2026-07-03: - update .custodian-brief.md for core-hub --- .custodian-brief.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.custodian-brief.md b/.custodian-brief.md index 58efeec..33e5176 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,7 +2,7 @@ # Custodian Brief — core-hub **Domain:** infotech -**Last synced:** 2026-07-03 09:25 UTC +**Last synced:** 2026-07-03 16:36 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams @@ -16,9 +16,6 @@ Progress: 0/4 done | workstream_id: `a8d66822-e435-4b1e-ad81-37a298d1795e` - ! Update Cross-Repo References `ed7f8e01` - · Retire Probe Repo `d3a494a3` -### Migrate Inter-Hub data and cut over production -Progress: 4/4 done | workstream_id: `a36efc08-5906-40c7-b1de-f3a553ffea02` - --- ## MCP Orientation (when available) From df6ed8a520fbacb0ab23a55b59e7eae43bb9c922 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 6 Jul 2026 15:54:32 +0200 Subject: [PATCH 10/10] Add Forgejo CI smoke and image workflow (tier 3 T04) --- .forgejo/workflows/ci-smoke.yaml | 29 ++++++++++++++++++++ .forgejo/workflows/image.yaml | 47 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 .forgejo/workflows/ci-smoke.yaml create mode 100644 .forgejo/workflows/image.yaml diff --git a/.forgejo/workflows/ci-smoke.yaml b/.forgejo/workflows/ci-smoke.yaml new file mode 100644 index 0000000..bd44c56 --- /dev/null +++ b/.forgejo/workflows/ci-smoke.yaml @@ -0,0 +1,29 @@ +# Canonical CI smoke template (tier 1 routing drill). +# Copy to: .forgejo/workflows/ci-smoke.yaml in consumer repos. +name: CI Smoke + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + host-smoke: + runs-on: self-hosted + steps: + - name: Routing probe (host runner) + run: | + set -eu + echo "repository=${GITHUB_REPOSITORY:-unknown}" + echo "sha=${GITHUB_SHA:-unknown}" + echo "runner=${RUNNER_NAME:-unknown}" + uname -a + + container-smoke: + runs-on: ubuntu-latest + steps: + - name: Routing probe (container label) + run: | + set -eu + echo "container-smoke ok for ${GITHUB_REPOSITORY:-unknown}" \ No newline at end of file diff --git a/.forgejo/workflows/image.yaml b/.forgejo/workflows/image.yaml new file mode 100644 index 0000000..fb26fba --- /dev/null +++ b/.forgejo/workflows/image.yaml @@ -0,0 +1,47 @@ +name: Build and Publish Container Image + +on: + push: + branches: + - main + paths: + - ".forgejo/workflows/image.yaml" + - "Dockerfile" + - "src/**" + - "migrations/**" + - "scripts/**" + - "pyproject.toml" + - "uv.lock" + - "alembic.ini" + workflow_dispatch: + +env: + REGISTRY: forgejo.coulomb.social + IMAGE_NAME: coulomb/core-hub + DOCKER_HOST: tcp://127.0.0.1:2375 + +jobs: + build-and-push: + runs-on: container-build + steps: + - name: Build and push image + env: + REGISTRY_USER: ${{ secrets.REGISTRY_USER }} + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + set -eu + REF="${GITHUB_SHA:-main}" + SHORT="${REF:0:7}" + mkdir -p buildctx "${HOME}/bin" + wget -qO /tmp/repo.tar.gz \ + "https://forgejo.coulomb.social/${GITHUB_REPOSITORY}/archive/${SHORT}.tar.gz" + tar xzf /tmp/repo.tar.gz -C buildctx --strip-components=1 + wget -qO- https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz \ + | tar xz --strip-components=1 -C "${HOME}/bin" docker/docker + export PATH="${HOME}/bin:${PATH}" + echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" -u "${REGISTRY_USER}" --password-stdin + IMAGE="${REGISTRY}/${IMAGE_NAME}" + docker build -t "${IMAGE}:latest" -t "${IMAGE}:main-${SHORT}" buildctx + docker push "${IMAGE}:latest" + docker push "${IMAGE}:main-${SHORT}" + echo "pushed ${IMAGE}:latest and ${IMAGE}:main-${SHORT}" \ No newline at end of file