Files
inter-hub/docs/contracts/ops-hub-activity-core-mapping.md

8.9 KiB

Ops Hub Activity-Core Widget Mapping

Date: 2026-06-15 Updated: 2026-06-27

Workplan: IHUB-WP-0022

Purpose

OPS_HUB_WIDGET_MAPPING tells activity-core which Inter-Hub widget receives each ops evidence event. The value must be non-secret JSON. It may contain Inter-Hub widget UUIDs and stable logical refs, but it must never contain OPS_HUB_KEY, bearer tokens, cookies, or any operator credential.

This revision aligns the contract to the live ops-hub manifest vocabulary observed on 2026-06-27 and to the source seed files in ops-hub:

  • event types use the live registry names such as ops-service-discovered and ops-drift-detected;
  • widget types use live seed types such as ops-service, ops-endpoint, ops-backup-set, ops-readiness-gate, and ops-risk;
  • policy scopes use the declared ops scopes; there is no ops-evidence scope in the live manifest;
  • access-path evidence remains deferred until ops-hub explicitly adds an access-path event and widget type or activity-core maps it to a supported readiness/risk event by decision.

Activity-core currently only checks that a mapping value is present before returning inter_hub_sink_deferred. This document defines the contract that the future Inter-Hub submission implementation should parse.

Versioned Shape

The shape remains version v1; this is a compatibility revision of the vocabulary inside the mapping, not a structural breaking change.

{
  "version": "ops-hub.activity-core.widget-mapping.v1",
  "hub": {
    "slug": "ops-hub",
    "id": "<ops-hub-uuid>"
  },
  "defaultViewContext": "ops-inventory-probe",
  "allowedPolicyScopes": [
    "ops-local",
    "ops-transitional-prod",
    "ops-production",
    "ops-threephoenix",
    "ops-registry",
    "ops-secrets",
    "ops-backup-retention"
  ],
  "eventAliases": {
    "ops-service-observed": "ops-service-discovered",
    "ops-inventory-drift": "ops-drift-detected"
  },
  "deferredEvents": {
    "ops-access-path-checked": {
      "reason": "not_declared_by_live_ops_hub_manifest",
      "fallback": "state-hub-progress",
      "decisionNeeded": "add ops-access-path vocabulary or map access-path evidence to ops-readiness-gate-updated/ops-risk-raised"
    }
  },
  "events": {
    "ops-service-discovered": {
      "family": "services",
      "selector": "service_id",
      "defaultWidgetRef": "ops:service-catalog"
    },
    "ops-endpoint-verified": {
      "family": "endpoints",
      "selector": "<service_id>:<endpoint_id>",
      "defaultWidgetRef": "ops:endpoint:gitea-registry"
    },
    "ops-backup-verified": {
      "family": "backups",
      "selector": "<service_id>:<backing_store_ref>",
      "defaultWidgetRef": "ops:backup-set:aggregate",
      "requiresSeed": true
    },
    "ops-drift-detected": {
      "family": "readiness",
      "selector": "<service_id>:<inventory_object_id>",
      "defaultWidgetRef": "ops:readiness:gitea-registry"
    }
  },
  "widgets": {
    "byRef": {
      "ops:service-catalog": {
        "widgetId": "<uuid>",
        "widgetType": "ops-service-catalog",
        "policyScope": "ops-production",
        "name": "Operations Service Catalog"
      },
      "ops:service:state-hub": {
        "widgetId": "<uuid>",
        "widgetType": "ops-service",
        "policyScope": "ops-local",
        "name": "State Hub Service"
      },
      "ops:service:gitea": {
        "widgetId": "<uuid>",
        "widgetType": "ops-service",
        "policyScope": "ops-transitional-prod",
        "name": "Gitea Service"
      },
      "ops:service:inter-hub": {
        "widgetId": "<uuid>",
        "widgetType": "ops-service",
        "policyScope": "ops-production",
        "name": "Inter-Hub Service"
      },
      "ops:endpoint:gitea-registry": {
        "widgetId": "<uuid>",
        "widgetType": "ops-endpoint",
        "policyScope": "ops-registry",
        "name": "Gitea Registry Endpoint"
      },
      "ops:readiness:gitea-registry": {
        "widgetId": "<uuid>",
        "widgetType": "ops-readiness-gate",
        "policyScope": "ops-registry",
        "name": "Gitea Registry Readiness"
      },
      "ops:backup-set:aggregate": {
        "widgetId": "<uuid>",
        "widgetType": "ops-backup-set",
        "policyScope": "ops-backup-retention",
        "name": "Ops Backup Evidence Intake",
        "requiresSeed": true
      }
    },
    "services": {
      "state-hub": { "widgetRef": "ops:service:state-hub" },
      "gitea": { "widgetRef": "ops:service:gitea" },
      "inter-hub": { "widgetRef": "ops:service:inter-hub" }
    },
    "endpoints": {
      "gitea:gitea-oci-registry": { "widgetRef": "ops:endpoint:gitea-registry" }
    },
    "backups": {
      "gitea:database:gitea-db": { "widgetRef": "ops:backup-set:aggregate" }
    },
    "readiness": {
      "gitea:gitea-oci-registry": { "widgetRef": "ops:readiness:gitea-registry" }
    }
  }
}

Selector Rules

Activity-core should choose a widget in this order:

  1. If the evidence payload carries a widget_ref and that reference exists in widgets.byRef, use it.
  2. If the event name is present in eventAliases, translate it to the live event name before selector lookup and submission.
  3. For ops-service-discovered, use services["<service_id>"]; otherwise use ops:service-catalog.
  4. For ops-endpoint-verified, use endpoints["<service_id>:<endpoint_id>"]; otherwise skip or use a deliberately seeded endpoint aggregate.
  5. For ops-backup-verified, use backups["<service_id>:<backing_store_ref>"]; otherwise skip until a backup-set widget is seeded.
  6. For ops-drift-detected, use readiness["<service_id>:<inventory_object_id>"]; otherwise use a seeded readiness/risk widget chosen by the operator.
  7. For ops-access-path-checked, do not submit to Inter-Hub yet. Keep State Hub fallback evidence unless ops-hub adds access-path vocabulary or an explicit decision maps access-path checks to readiness/risk events.
  8. If no selector can resolve to a widget id, skip Inter-Hub submission with a non-secret result naming the missing selector.

Bootstrap Widget Names

These refs are already present in the current ops-hub seed file and should be looked up in the protected widget registry during the authenticated bootstrap:

Widget ref Widget type Name
ops:service-catalog ops-service-catalog Operations Service Catalog
ops:service:gitea ops-service Gitea Service
ops:service:state-hub ops-service State Hub Service
ops:service:inter-hub ops-service Inter-Hub Service
ops:endpoint:gitea-registry ops-endpoint Gitea Registry Endpoint
ops:readiness:gitea-registry ops-readiness-gate Gitea Registry Readiness

Additional seed required before backup evidence can be smoke-tested:

Widget ref Widget type Suggested name Policy scope
ops:backup-set:aggregate ops-backup-set Ops Backup Evidence Intake ops-backup-retention

Per-entity widgets may be seeded later without changing the event contract. When a per-entity widget is added, update the mapping and keep a deliberate fallback only when the fallback widget actually exists in the protected widget registry.

Compatibility Rules

  • version is required. Reject unknown major versions.
  • Consumers must tolerate additional fields.
  • Widget UUIDs may rotate, but widgetRef values should remain stable.
  • Event aliases are a transition aid only. New activity-core submissions should use live registry event names directly.
  • Removing a widget mapping is a breaking change for that selector unless a verified fallback widget remains present.
  • Mapping updates must be deployed before activity-core starts sending events that depend on the new selectors.
  • The mapping is non-secret and may be stored in a ConfigMap or environment variable. OPS_HUB_KEY must remain Secret-only.

Minimum Valid Mapping

For the first live smoke, use only live registry events and widgets that are known from the seed contract and can be confirmed through an authenticated widget lookup. Backup and access-path evidence can remain deferred.

{
  "version": "ops-hub.activity-core.widget-mapping.v1",
  "hub": {
    "slug": "ops-hub",
    "id": "<ops-hub-uuid>"
  },
  "defaultViewContext": "ops-inventory-probe",
  "eventAliases": {
    "ops-service-observed": "ops-service-discovered",
    "ops-inventory-drift": "ops-drift-detected"
  },
  "events": {
    "ops-service-discovered": {
      "family": "services",
      "defaultWidgetRef": "ops:service-catalog"
    },
    "ops-endpoint-verified": {
      "family": "endpoints",
      "defaultWidgetRef": "ops:endpoint:gitea-registry"
    },
    "ops-drift-detected": {
      "family": "readiness",
      "defaultWidgetRef": "ops:readiness:gitea-registry"
    }
  },
  "widgets": {
    "byRef": {
      "ops:service-catalog": { "widgetId": "<uuid>" },
      "ops:endpoint:gitea-registry": { "widgetId": "<uuid>" },
      "ops:readiness:gitea-registry": { "widgetId": "<uuid>" }
    }
  }
}