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

7.8 KiB

Ops Hub Activity-Core Event Payloads

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

Workplan: IHUB-WP-0022

Inter-Hub Request Shape

Activity-core should submit ops evidence through:

POST /api/v2/interaction-events
Authorization: Bearer ${OPS_HUB_KEY}
Content-Type: application/json

Each request body must use the Inter-Hub v2 interaction-event shape:

{
  "widgetId": "<widget-uuid-from-OPS_HUB_WIDGET_MAPPING>",
  "eventType": "ops-endpoint-verified",
  "viewContext": "ops-inventory-probe",
  "metadata": {
    "type": "ops-endpoint-verified",
    "version": "1.0",
    "publisher": "activity-core",
    "attributes": {}
  }
}

Inter-Hub sets occurredAt on receipt. Activity-core must send the actual probe timestamp as metadata.attributes.observed_at.

Live Vocabulary Alignment

Use event names declared by the live ops-hub manifest:

  • ops-service-discovered
  • ops-endpoint-verified
  • ops-backup-verified
  • ops-drift-detected

Transitional aliases may be accepted in local mapping logic but should not be submitted to Inter-Hub:

Earlier activity-core name Live event name
ops-service-observed ops-service-discovered
ops-inventory-drift ops-drift-detected

ops-access-path-checked is deferred because the live manifest has no access-path event type or access-path widget type. Keep State Hub fallback posting for access-path evidence until ops-hub adds that vocabulary or an operator decision maps it to ops-readiness-gate-updated or ops-risk-raised.

Shared Rules

  • widgetId must be a UUID for an existing ops-hub widget.
  • eventType must exist in Inter-Hub's event type registry.
  • If the API consumer is bound to an active ops-hub manifest, eventType must be declared by that manifest.
  • viewContext should be ops-inventory-probe unless a more specific context is useful, such as ops-inventory-probe/endpoints.
  • metadata.type must match the Inter-Hub eventType after alias translation.
  • metadata.version must match the activity-core event definition version.
  • metadata.publisher must be activity-core.
  • metadata.attributes.idempotency_key is required, even though Inter-Hub does not currently enforce idempotency.
  • Duplicate tolerance is required on the reader side until Inter-Hub provides a unique idempotency constraint.
  • Payloads must not include secrets, authorization headers, cookies, token-like values, private key material, raw response bodies, command output, or unredacted URL query strings.

Status Vocabulary

Use the activity-core status vocabulary:

  • ok
  • degraded
  • down
  • skipped

Use reason for compact machine-readable explanations, for example:

  • expected_status_mismatch
  • expected_signal_missing
  • unsupported_access_path_type
  • backup_probe_not_implemented
  • missing_endpoint

Example: Service Discovered

{
  "widgetId": "<service-or-catalog-widget-uuid>",
  "eventType": "ops-service-discovered",
  "viewContext": "ops-inventory-probe/services",
  "metadata": {
    "type": "ops-service-discovered",
    "version": "1.0",
    "publisher": "activity-core",
    "attributes": {
      "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
      "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:state-hub:ops-service-discovered",
      "service_id": "state-hub",
      "service_name": "State Hub",
      "environment": "local",
      "lifecycle_state": "discovered",
      "observed_status": "ok",
      "observed_at": "2026-06-05T10:15:01Z",
      "widget_ref": "ops:service:state-hub"
    }
  }
}

Example: Endpoint Verified

{
  "widgetId": "<endpoint-widget-uuid>",
  "eventType": "ops-endpoint-verified",
  "viewContext": "ops-inventory-probe/endpoints",
  "metadata": {
    "type": "ops-endpoint-verified",
    "version": "1.0",
    "publisher": "activity-core",
    "attributes": {
      "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
      "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-oci-registry:ops-endpoint-verified",
      "service_id": "gitea",
      "endpoint_id": "gitea-oci-registry",
      "endpoint_type": "https",
      "endpoint_url": "https://gitea.coulomb.social/v2/",
      "expected_status": 401,
      "status_code": 401,
      "matched_expected_status": true,
      "matched_expected_signal": true,
      "observed_status": "ok",
      "observed_at": "2026-06-05T10:15:01Z",
      "widget_ref": "ops:endpoint:gitea-registry"
    }
  }
}

Deferred: Access Path Checked

Do not submit ops-access-path-checked to Inter-Hub while the live ops-hub manifest lacks that event type and a matching widget type. Activity-core should continue to include compact access-path detail in State Hub fallback summaries and return a non-secret deferred sink result for Inter-Hub.

If the operator chooses to represent access-path state as readiness or risk, create a separate mapping decision and send the supported event type instead of silently rewriting access-path evidence.

Example: Backup Verified

This event is declared by the live manifest, but it needs a target ops-backup-set widget such as ops:backup-set:aggregate before the smoke can submit it.

{
  "widgetId": "<backup-set-widget-uuid>",
  "eventType": "ops-backup-verified",
  "viewContext": "ops-inventory-probe/backups",
  "metadata": {
    "type": "ops-backup-verified",
    "version": "1.0",
    "publisher": "activity-core",
    "attributes": {
      "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
      "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:database:gitea-db:ops-backup-verified",
      "service_id": "gitea",
      "backing_store_ref": "database:gitea-db",
      "backup_evidence_ref": "state-hub:progress:<progress-id>",
      "restore_verified": false,
      "observed_status": "skipped",
      "reason": "backup_probe_not_implemented",
      "observed_at": "2026-06-05T10:15:01Z",
      "widget_ref": "ops:backup-set:aggregate"
    }
  }
}

Example: Drift Detected

{
  "widgetId": "<readiness-or-risk-widget-uuid>",
  "eventType": "ops-drift-detected",
  "viewContext": "ops-inventory-probe/drift",
  "metadata": {
    "type": "ops-drift-detected",
    "version": "1.0",
    "publisher": "activity-core",
    "attributes": {
      "activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
      "idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-oci-registry:ops-drift-detected",
      "service_id": "gitea",
      "inventory_object_id": "gitea-oci-registry",
      "drift_kind": "status_mismatch",
      "declared_summary": "expected_status=401",
      "observed_summary": "status_code=200",
      "observed_status": "degraded",
      "reason": "expected_status_mismatch",
      "observed_at": "2026-06-05T10:15:01Z",
      "widget_ref": "ops:readiness:gitea-registry"
    }
  }
}

Expected API Errors

Activity-core should treat these as configuration or rollout errors:

Error Meaning Recovery
401 Missing or invalid OPS_HUB_KEY Check Secret provisioning; do not log the key.
422 with unregistered_event_type Event type not in Inter-Hub registry Use a live manifest event or activate a corrected ops-hub manifest.
422 with event_type_not_in_manifest Runtime consumer manifest does not declare the event Bind the consumer to the active manifest or activate a corrected manifest.
422 with Widget not found Mapping points at a missing widget Refresh OPS_HUB_WIDGET_MAPPING from authenticated widget lookup.
422 with unregistered_policy_scope during widget seed Policy scope is absent Use one of the declared ops scopes, not the old ops-evidence proposal.

For the first activity-core slice, a failed Inter-Hub submission should not fail the whole probe if State Hub fallback posting succeeds. It should return a compact sink result naming the non-secret failure class.