11 KiB
Core Hub to hub-core absorption plan
Status: accepted for CORE-WP-0010-T02 / HUB-WP-0004-T06
Reviewed: 2026-08-21
Strategy: route-group migration with Core Hub retained as rollback; no
client dual-write and no big-bang cutover
This Core Hub record is aligned with the canonical joint plan at
hub-core/docs/core-hub-absorption-plan.md and State Hub decision
12514947-6cc0-42a0-98ca-9aacc9d875b0. It turns the inventory in
runtime-absorption-inventory.md into ordered, testable migration slices.
Hub-core owns the surviving runtime selected by its ADR-0001. Core Hub remains
the public /api/v2 authority until each route group passes its own data,
contract, consumer, and rollback gates.
Decisions
- One target runtime. Hub-core ships the importable wheel and primary OCI image. Core Hub is a temporary source and rollback runtime, not a permanent host for hub-core.
- Internal candidate first. A hub-core candidate deployment receives no public traffic until it has a published source revision, immutable image digest, durable PostgreSQL backend, migrations, compatible authentication, and green readiness.
- Route groups, not percentages. Traffic moves by coherent API group. Random percentage canaries would send related bootstrap calls to different authorities and are not permitted.
- One writer per route group. Read-only requests may be mirrored for comparison. Production clients never dual-write. Write cutover uses a short drain, final delta replay, route switch, and one active writer.
- Isolated metadata during dual-run. Core Hub keeps the
core_hubschema; hub-core uses its own migration lineage and schema. Cross-importing either service's SQLAlchemy metadata is prohibited. - Stable identity and safe key migration. Durable IDs are preserved. API-key prefixes, hashes, scopes, and status may move; raw bearer tokens or one-time full keys must never enter migration bundles or evidence.
- Archive last. Core Hub stays deployable until the final stabilization gate, reverse-delta requirement, and residual ownership record are closed.
Runtime and database shape
The candidate uses a distinct hub-core-owned schema, provisionally
hub_runtime, beside retained legacy schema core_hub in the existing
platform PostgreSQL allocation. This does not consume another application
database slot. The target adds its own audit ledger and migration lineage;
Core Hub models and migrations are not imported. Deployment remains owned by
rapp-core-hub during dual-run, with final package naming/ownership recorded
before this repository is archived.
Public route movement gates
S0 may build the missing foundation, but no public route group leaves Core Hub until all of these are evidenced:
- the completed
HUB-WP-0004-T01–T05work is committed and pushed; - a hub-core wheel and OCI image identify the same revision and version;
rapp-core-hubcan deploy a second, cluster-internal candidate without changinghub.coulomb.social;- hub-core has a production PostgreSQL
PortStore/compatibility store, audit ledger, and migrations, and fails readiness when durable storage or its authorization dependency is unavailable; - the existing operator token and copied API-key hashes authenticate through the candidate without exposing credential values;
- Core Hub export, hub-core import, row-count, canonical-digest, and delta replay commands are repeatable and record non-secret migration metadata;
- the hub-core conformance profile and Core Hub compatibility tests pass against the candidate.
The current in-memory hub-core backend is a conformance implementation, not an absorption target. Its deliberate production-readiness failure is a useful gate and must not be bypassed.
Ordered slices
| Slice | Surface | Move condition | Consumer gate |
|---|---|---|---|
| S0 — candidate foundation | image, API/MCP processes, migrations, durable store/audit, auth adapter, group controls, health/readiness | All entry gates above; all seven source tables representable; no public route | hub-core conformance plus Core Hub contract suite |
| S1 — system/discovery | health/readiness; widget/event/annotation/policy catalogs; compatibility OpenAPI/docs and aliases | Response status, headers, and canonical JSON match; dependency-aware readiness | public probes, ops-hub public gate, activity-core resolver |
| S2 — registry/manifests | hubs, capability manifests/activation, hub registry | IDs and legacy serialization preserved; counts/digests match; activation audited | ops-hub can resolve/update its manifest; reverse delta proven |
| S3 — consumers/credentials | API consumers, key issuance, /token |
Hashes/prefixes only; auth deny cases and custody separation pass; rate/quota residual explicit | existing ops-hub and activity-core credentials; reverse key-hash delta proven |
| S4 — widgets/interaction | widgets; interaction-event append/list mapped to port.events.interaction |
Historical IDs/timestamps retained; event families remain separate; projection/reverse replay pass | ops-hub and activity-core correlated append/readback |
| S5 — deferred/console | empty annotation, requirement, decision, deployment and outcome collections; /console |
Exact empty/error behavior; residual disposition per collection; console has no source-schema reads | operator CLI, protected console, visual smoke |
| S6 — whole-host authority | all /api/v2, API/MCP entrypoints and remaining traffic |
Final backup/delta has zero unexplained drift; operator approves; seven consecutive stable days before retirement | hub-core conformance and Core Hub, ops-hub, activity-core gates together |
Related calls within a slice move as one traffic group. In particular, public traffic must not mix old and new authorities inside registry, credential, or widget/interaction workflows whose identifiers cross resource boundaries.
Compatibility adapters
Hub-core's named ports are not themselves the legacy /api/v2 contract. The
target needs an explicit compatibility composition layer:
- hub and manifest resources map to
port.registrywhile preserving legacy IDs, body fields, activation behavior, pagination envelope, and hub-registry projection; - interaction events map to
port.events.interactionwith a stable correlation ID, original event ID, widget reference, view context, event type, metadata, and occurrence/recording timestamps; - widgets and API consumers/keys remain compatibility resources until a named replacement port is accepted; they must not be forced into registry records;
- health/readiness retain the response shape expected by deployed Core Hub smoke tooling until consumers adopt the hub-core-native shape;
/api/v2/openapi.json, YAML/docs discovery, and required unprefixed aliases remain versioned compatibility artifacts through final cutover.
Data movement and comparison
For each durable slice:
- Export source records in dependency order with a schema version, source revision, high-water mark, and bundle SHA-256.
- Validate without writing; reject unknown fields, missing parents, raw secret shapes, duplicate identities, or a lower target schema version.
- Upsert into the target by stable ID. Re-running the same bundle must be idempotent and recorded as such.
- Compare table/resource counts, sorted identity sets, and canonical row digests. Timestamp formatting and JSON key order are normalized before hashing; semantic field differences are not ignored.
- Mirror only safe GET requests and compare status plus canonical response. Exercise writes with isolated migration fixtures, never by duplicating a production client request.
- At route cutover, drain writes for that group, capture the final high-water delta, import/compare it, switch the route group, and enable target writes.
Migration evidence may contain IDs, counts, hashes, versions, timestamps, and pass/fail results. It must not contain raw API keys, bearer tokens, database credentials, Authorization headers, or copied Kubernetes Secrets.
Traffic controls and rollback
rapp-core-hub owns one logical switch per slice with the states legacy,
shadow, and candidate, plus independently observable legacy/candidate
write-group sets. The concrete carrier may use the joint plan's
HUB_CORE_V2_GROUPS, HUB_CORE_V2_WRITE_GROUPS, and
CORE_HUB_V2_WRITE_GROUPS or equivalent Helm values. Configuration must
reject overlapping writer sets, and every rendered release must reveal the
selected state as non-secret evidence.
Rollback remains possible while a slice is target-owned:
- stop new target writes for the affected route group;
- export and verify the target-only delta by stable ID;
- replay that delta to Core Hub when the old source must become writable;
- compare counts/digests and run the consumer gate;
- switch the whole route group back to
source.
If reverse replay is not implemented for a write slice, rollback is limited to the period before target writes are enabled. That limitation blocks public cutover and must be visible in the evidence report.
Required evidence per slice
- hub-core source revision, package version, and OCI digest;
- Core Hub source revision and deployed Helm revision;
- source/target schema versions and migration bundle hash;
- before/after counts, identity-set comparison, and canonical digest result;
- compatibility/OpenAPI diff result;
- auth positive and negative checks without credential disclosure;
- named consumer smoke reports and correlation IDs;
- selected route state, switch time, observer, and rollback result or reason it was not invoked.
Any schema mismatch, unexplained count/digest drift, secret-shaped evidence, auth broadening, consumer failure, or non-green readiness stops the slice and keeps public authority on Core Hub.
Ownership
| Owner | Responsibility |
|---|---|
| hub-core | compatibility modules, durable store/migrations, import/delta/reverse-delta, conformance |
| core-hub | source export, legacy fixtures/OpenAPI, canonical response comparison, rollback service |
| rapp-core-hub | candidate deployment, immutable image pin, route switches, rollout/rollback evidence |
| railiance-platform / rapp-postgres | PostgreSQL, credential delivery, backup/restore readiness |
| ops-hub | bootstrap and registry consumer gate |
| activity-core | interaction resolver, append, and readback gate |
| operator | approval for every production writer change, S6 public authority, and later repository archive |
Next executable work
Hub-core published its completed T01–T06 work as revision 7e1ec03; S0 is now
active. Its first implementation commit adds the durable PostgreSQL boundary
and legacy auth/health compatibility in hub-core, followed by a candidate
deployment mode in rapp-core-hub. It must not change public DNS or ingress.