diff --git a/scripts/migration/README.md b/scripts/migration/README.md new file mode 100644 index 0000000..fe03993 --- /dev/null +++ b/scripts/migration/README.md @@ -0,0 +1,27 @@ +# Inter-Hub → Core Hub migration export + +`inter_hub_export.sql` is a read-only query against the legacy Inter-Hub +Postgres database (`interhub` on net-kingdom-pg) that emits a +`core-hub.migration.v1` bundle covering hubs, hubCapabilityManifests, +apiConsumers, apiKeys (prefix + hash only, no plaintext), widgets, and +interactionEvents. Field names are aliased from Inter-Hub's snake_case columns +to the bundle's camelCase contract. + +## Run + +```bash +kubectl exec -i -n databases net-kingdom-pg-1 -- \ + psql -U postgres -d interhub -tA < scripts/migration/inter_hub_export.sql \ + > interhub-bundle.json + +# validate (no DB needed) +PYTHONPATH=src uv run python scripts/core_hub_cli.py migration validate interhub-bundle.json + +# import into a target (CORE_HUB_DATABASE_URL); run in-cluster for staging +python scripts/core_hub_cli.py migration import --dry-run interhub-bundle.json +python scripts/core_hub_cli.py migration import interhub-bundle.json +``` + +The importer is idempotent (upsert by id): re-running updates in place, never +duplicates. `api_keys` carry only `keyPrefix` + `keyHash`, so no secret material +transits the bundle. diff --git a/scripts/migration/inter_hub_export.sql b/scripts/migration/inter_hub_export.sql new file mode 100644 index 0000000..57f382a --- /dev/null +++ b/scripts/migration/inter_hub_export.sql @@ -0,0 +1,13 @@ +SELECT json_build_object( + 'schemaVersion','core-hub.migration.v1', + 'source','inter-hub-production', + 'exportedAt', now(), + 'records', json_build_object( + 'hubs', COALESCE((SELECT json_agg(json_build_object('id',id,'slug',slug,'name',name,'domain',domain,'hubKind',hub_kind,'hubFamily',hub_family,'vsmFunction',vsm_function,'vsmSystem',vsm_system,'status','active')) FROM hubs), '[]'::json), + 'hubCapabilityManifests', COALESCE((SELECT json_agg(json_build_object('id',id,'hubId',hub_id,'manifestVersion',manifest_version,'declaredWidgetTypes',declared_widget_types,'declaredEventTypes',declared_event_types,'declaredAnnotationCategories',declared_annotation_categories,'declaredPolicyScopes',declared_policy_scopes,'status',status,'capabilityDescription',capability_description,'contact',contact,'activatedAt',activated_at)) FROM hub_capability_manifests), '[]'::json), + 'apiConsumers', COALESCE((SELECT json_agg(json_build_object('id',id,'name',name,'description',description,'hubCapabilityManifestId',hub_capability_manifest_id,'rateLimitPerMinute',rate_limit_per_minute,'quotaPerDay',quota_per_day,'isActive',is_active)) FROM api_consumers), '[]'::json), + 'apiKeys', COALESCE((SELECT json_agg(json_build_object('id',id,'apiConsumerId',api_consumer_id,'keyPrefix',key_prefix,'keyHash',key_hash,'scopes',scopes,'tokenType',token_type,'expiresAt',expires_at,'revokedAt',revoked_at)) FROM api_keys), '[]'::json), + 'widgets', COALESCE((SELECT json_agg(json_build_object('id',id,'hubId',hub_id,'name',name,'widgetType',widget_type,'capabilityRef',capability_ref,'viewContext',view_context,'policyScope',policy_scope,'status',status,'version',version,'isArchived',is_archived)) FROM widgets), '[]'::json), + 'interactionEvents', COALESCE((SELECT json_agg(json_build_object('id',id,'widgetId',widget_id,'eventType',event_type,'viewContext',view_context_ref,'actorId',actor_id,'actorType',actor_type,'metadata',metadata,'occurredAt',occurred_at)) FROM interaction_events), '[]'::json) + ) +); diff --git a/workplans/CORE-WP-0005-data-migration-and-cutover.md b/workplans/CORE-WP-0005-data-migration-and-cutover.md index 73388bc..4d8d61d 100644 --- a/workplans/CORE-WP-0005-data-migration-and-cutover.md +++ b/workplans/CORE-WP-0005-data-migration-and-cutover.md @@ -31,7 +31,7 @@ Result 2026-06-27: Defined and implemented the `core-hub.migration.v1` bundle pa ```task id: CORE-WP-0005-T02 -status: wait +status: done priority: high state_hub_task_id: "e0ea0928-a3ea-4ece-bbb7-ee08f8a3279b" ``` @@ -66,3 +66,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. + + +## Progress 2026-07-02 — staging import completed + +Built the Inter-Hub exporter (`scripts/migration/inter_hub_export.sql`, read-only +against the `interhub` DB on net-kingdom-pg) emitting a validated +`core-hub.migration.v1` bundle (sha256 `e1b53b7f2d67ef7a…`): 1 hub (`ops-hub`), +1 manifest, 2 api consumers, 8 api keys (prefix+hash only), 15 widgets, 1 +interaction event. `migration validate` passed with zero errors/warnings and no +secret material. + +Imported into Core Hub staging via an in-cluster Job (staging DB URL from +`core-hub-staging-env` secretKeyRef, bundle via ConfigMap): dry-run `ok=true` +(28 would-create), real import created all 28 records with zero errors, and a +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.