generated from coulomb/repo-seed
Align ops-hub activity-core contract vocabulary
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
# Ops Hub Activity-Core Event Payloads
|
# Ops Hub Activity-Core Event Payloads
|
||||||
|
|
||||||
Date: 2026-06-15
|
Date: 2026-06-15
|
||||||
|
Updated: 2026-06-27
|
||||||
|
|
||||||
Workplan: `IHUB-WP-0022`
|
Workplan: `IHUB-WP-0022`
|
||||||
|
|
||||||
@@ -33,6 +34,28 @@ Each request body must use the Inter-Hub v2 interaction-event shape:
|
|||||||
Inter-Hub sets `occurredAt` on receipt. Activity-core must send the actual
|
Inter-Hub sets `occurredAt` on receipt. Activity-core must send the actual
|
||||||
probe timestamp as `metadata.attributes.observed_at`.
|
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
|
## Shared Rules
|
||||||
|
|
||||||
- `widgetId` must be a UUID for an existing ops-hub widget.
|
- `widgetId` must be a UUID for an existing ops-hub widget.
|
||||||
@@ -41,7 +64,7 @@ probe timestamp as `metadata.attributes.observed_at`.
|
|||||||
be declared by that manifest.
|
be declared by that manifest.
|
||||||
- `viewContext` should be `ops-inventory-probe` unless a more specific context
|
- `viewContext` should be `ops-inventory-probe` unless a more specific context
|
||||||
is useful, such as `ops-inventory-probe/endpoints`.
|
is useful, such as `ops-inventory-probe/endpoints`.
|
||||||
- `metadata.type` must match the Inter-Hub `eventType`.
|
- `metadata.type` must match the Inter-Hub `eventType` after alias translation.
|
||||||
- `metadata.version` must match the activity-core event definition version.
|
- `metadata.version` must match the activity-core event definition version.
|
||||||
- `metadata.publisher` must be `activity-core`.
|
- `metadata.publisher` must be `activity-core`.
|
||||||
- `metadata.attributes.idempotency_key` is required, even though Inter-Hub does
|
- `metadata.attributes.idempotency_key` is required, even though Inter-Hub does
|
||||||
@@ -69,26 +92,27 @@ Use `reason` for compact machine-readable explanations, for example:
|
|||||||
- `backup_probe_not_implemented`
|
- `backup_probe_not_implemented`
|
||||||
- `missing_endpoint`
|
- `missing_endpoint`
|
||||||
|
|
||||||
## Example: Service Observed
|
## Example: Service Discovered
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"widgetId": "<service-widget-uuid>",
|
"widgetId": "<service-or-catalog-widget-uuid>",
|
||||||
"eventType": "ops-service-observed",
|
"eventType": "ops-service-discovered",
|
||||||
"viewContext": "ops-inventory-probe/services",
|
"viewContext": "ops-inventory-probe/services",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"type": "ops-service-observed",
|
"type": "ops-service-discovered",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"publisher": "activity-core",
|
"publisher": "activity-core",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
|
"activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
|
||||||
"idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:state-hub:ops-service-observed",
|
"idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:state-hub:ops-service-discovered",
|
||||||
"service_id": "state-hub",
|
"service_id": "state-hub",
|
||||||
"service_name": "State Hub",
|
"service_name": "State Hub",
|
||||||
"environment": "local",
|
"environment": "local",
|
||||||
"lifecycle_state": "observed",
|
"lifecycle_state": "discovered",
|
||||||
"observed_status": "ok",
|
"observed_status": "ok",
|
||||||
"observed_at": "2026-06-05T10:15:01Z"
|
"observed_at": "2026-06-05T10:15:01Z",
|
||||||
|
"widget_ref": "ops:service:state-hub"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,37 +148,26 @@ Use `reason` for compact machine-readable explanations, for example:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example: Access Path Checked
|
## Deferred: Access Path Checked
|
||||||
|
|
||||||
```json
|
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
|
||||||
"widgetId": "<access-path-widget-uuid>",
|
continue to include compact access-path detail in State Hub fallback summaries
|
||||||
"eventType": "ops-access-path-checked",
|
and return a non-secret deferred sink result for Inter-Hub.
|
||||||
"viewContext": "ops-inventory-probe/access-paths",
|
|
||||||
"metadata": {
|
If the operator chooses to represent access-path state as readiness or risk,
|
||||||
"type": "ops-access-path-checked",
|
create a separate mapping decision and send the supported event type instead of
|
||||||
"version": "1.0",
|
silently rewriting access-path evidence.
|
||||||
"publisher": "activity-core",
|
|
||||||
"attributes": {
|
|
||||||
"activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
|
|
||||||
"idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-access-1:ops-access-path-checked",
|
|
||||||
"service_id": "gitea",
|
|
||||||
"access_path_id": "gitea-access-1",
|
|
||||||
"access_path_type": "k8s",
|
|
||||||
"declared_status": "unknown",
|
|
||||||
"observed_status": "skipped",
|
|
||||||
"reason": "unsupported_access_path_type",
|
|
||||||
"observed_at": "2026-06-05T10:15:01Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example: Backup Verified
|
## 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.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"widgetId": "<backup-widget-uuid>",
|
"widgetId": "<backup-set-widget-uuid>",
|
||||||
"eventType": "ops-backup-verified",
|
"eventType": "ops-backup-verified",
|
||||||
"viewContext": "ops-inventory-probe/backups",
|
"viewContext": "ops-inventory-probe/backups",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
@@ -170,26 +183,27 @@ Use `reason` for compact machine-readable explanations, for example:
|
|||||||
"restore_verified": false,
|
"restore_verified": false,
|
||||||
"observed_status": "skipped",
|
"observed_status": "skipped",
|
||||||
"reason": "backup_probe_not_implemented",
|
"reason": "backup_probe_not_implemented",
|
||||||
"observed_at": "2026-06-05T10:15:01Z"
|
"observed_at": "2026-06-05T10:15:01Z",
|
||||||
|
"widget_ref": "ops:backup-set:aggregate"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example: Inventory Drift
|
## Example: Drift Detected
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"widgetId": "<drift-widget-uuid>",
|
"widgetId": "<readiness-or-risk-widget-uuid>",
|
||||||
"eventType": "ops-inventory-drift",
|
"eventType": "ops-drift-detected",
|
||||||
"viewContext": "ops-inventory-probe/drift",
|
"viewContext": "ops-inventory-probe/drift",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"type": "ops-inventory-drift",
|
"type": "ops-drift-detected",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"publisher": "activity-core",
|
"publisher": "activity-core",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
|
"activity_core_run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
|
||||||
"idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-oci-registry:ops-inventory-drift",
|
"idempotency_key": "12345678-aaaa-bbbb-cccc-123456789abc:gitea:gitea-oci-registry:ops-drift-detected",
|
||||||
"service_id": "gitea",
|
"service_id": "gitea",
|
||||||
"inventory_object_id": "gitea-oci-registry",
|
"inventory_object_id": "gitea-oci-registry",
|
||||||
"drift_kind": "status_mismatch",
|
"drift_kind": "status_mismatch",
|
||||||
@@ -197,7 +211,8 @@ Use `reason` for compact machine-readable explanations, for example:
|
|||||||
"observed_summary": "status_code=200",
|
"observed_summary": "status_code=200",
|
||||||
"observed_status": "degraded",
|
"observed_status": "degraded",
|
||||||
"reason": "expected_status_mismatch",
|
"reason": "expected_status_mismatch",
|
||||||
"observed_at": "2026-06-05T10:15:01Z"
|
"observed_at": "2026-06-05T10:15:01Z",
|
||||||
|
"widget_ref": "ops:readiness:gitea-registry"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,12 +223,12 @@ Use `reason` for compact machine-readable explanations, for example:
|
|||||||
Activity-core should treat these as configuration or rollout errors:
|
Activity-core should treat these as configuration or rollout errors:
|
||||||
|
|
||||||
| Error | Meaning | Recovery |
|
| Error | Meaning | Recovery |
|
||||||
|---|---|---|
|
| --- | --- | --- |
|
||||||
| `401` | Missing or invalid `OPS_HUB_KEY` | Check Secret provisioning; do not log the key. |
|
| `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 | Activate the ops-hub manifest vocabulary. |
|
| `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 `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`. |
|
| `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 | Declare and activate `ops-evidence`. |
|
| `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
|
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
|
fail the whole probe if State Hub fallback posting succeeds. It should return a
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Ops Hub Activity-Core Widget Mapping
|
# Ops Hub Activity-Core Widget Mapping
|
||||||
|
|
||||||
Date: 2026-06-15
|
Date: 2026-06-15
|
||||||
|
Updated: 2026-06-27
|
||||||
|
|
||||||
Workplan: `IHUB-WP-0022`
|
Workplan: `IHUB-WP-0022`
|
||||||
|
|
||||||
@@ -8,8 +9,21 @@ Workplan: `IHUB-WP-0022`
|
|||||||
|
|
||||||
`OPS_HUB_WIDGET_MAPPING` tells activity-core which Inter-Hub widget receives
|
`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
|
each ops evidence event. The value must be non-secret JSON. It may contain
|
||||||
Inter-Hub widget UUIDs and logical references, but it must never contain
|
Inter-Hub widget UUIDs and stable logical refs, but it must never contain
|
||||||
`OPS_HUB_KEY` or any operator credential.
|
`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
|
Activity-core currently only checks that a mapping value is present before
|
||||||
returning `inter_hub_sink_deferred`. This document defines the contract that
|
returning `inter_hub_sink_deferred`. This document defines the contract that
|
||||||
@@ -17,6 +31,9 @@ the future Inter-Hub submission implementation should parse.
|
|||||||
|
|
||||||
## Versioned Shape
|
## Versioned Shape
|
||||||
|
|
||||||
|
The shape remains version `v1`; this is a compatibility revision of the
|
||||||
|
vocabulary inside the mapping, not a structural breaking change.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": "ops-hub.activity-core.widget-mapping.v1",
|
"version": "ops-hub.activity-core.widget-mapping.v1",
|
||||||
@@ -24,87 +41,109 @@ the future Inter-Hub submission implementation should parse.
|
|||||||
"slug": "ops-hub",
|
"slug": "ops-hub",
|
||||||
"id": "<ops-hub-uuid>"
|
"id": "<ops-hub-uuid>"
|
||||||
},
|
},
|
||||||
"policyScope": "ops-evidence",
|
|
||||||
"defaultViewContext": "ops-inventory-probe",
|
"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": {
|
"events": {
|
||||||
"ops-service-observed": {
|
"ops-service-discovered": {
|
||||||
"family": "services",
|
"family": "services",
|
||||||
"aggregateWidgetRef": "ops:service:aggregate"
|
"selector": "service_id",
|
||||||
|
"defaultWidgetRef": "ops:service-catalog"
|
||||||
},
|
},
|
||||||
"ops-endpoint-verified": {
|
"ops-endpoint-verified": {
|
||||||
"family": "endpoints",
|
"family": "endpoints",
|
||||||
"aggregateWidgetRef": "ops:endpoint:aggregate"
|
"selector": "<service_id>:<endpoint_id>",
|
||||||
},
|
"defaultWidgetRef": "ops:endpoint:gitea-registry"
|
||||||
"ops-access-path-checked": {
|
|
||||||
"family": "accessPaths",
|
|
||||||
"aggregateWidgetRef": "ops:access-path:aggregate"
|
|
||||||
},
|
},
|
||||||
"ops-backup-verified": {
|
"ops-backup-verified": {
|
||||||
"family": "backups",
|
"family": "backups",
|
||||||
"aggregateWidgetRef": "ops:backup:aggregate"
|
"selector": "<service_id>:<backing_store_ref>",
|
||||||
|
"defaultWidgetRef": "ops:backup-set:aggregate",
|
||||||
|
"requiresSeed": true
|
||||||
},
|
},
|
||||||
"ops-inventory-drift": {
|
"ops-drift-detected": {
|
||||||
"family": "drift",
|
"family": "readiness",
|
||||||
"aggregateWidgetRef": "ops:drift:aggregate"
|
"selector": "<service_id>:<inventory_object_id>",
|
||||||
|
"defaultWidgetRef": "ops:readiness:gitea-registry"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgets": {
|
"widgets": {
|
||||||
"aggregate": {
|
"byRef": {
|
||||||
"ops:service:aggregate": {
|
"ops:service-catalog": {
|
||||||
"widgetId": "<uuid>",
|
"widgetId": "<uuid>",
|
||||||
"widgetType": "ops-service-card",
|
"widgetType": "ops-service-catalog",
|
||||||
"name": "Ops Service Evidence Intake"
|
"policyScope": "ops-production",
|
||||||
|
"name": "Operations Service Catalog"
|
||||||
},
|
},
|
||||||
"ops:endpoint:aggregate": {
|
"ops:service:state-hub": {
|
||||||
"widgetId": "<uuid>",
|
"widgetId": "<uuid>",
|
||||||
"widgetType": "ops-endpoint-card",
|
"widgetType": "ops-service",
|
||||||
"name": "Ops Endpoint Evidence Intake"
|
"policyScope": "ops-local",
|
||||||
|
"name": "State Hub Service"
|
||||||
},
|
},
|
||||||
"ops:access-path:aggregate": {
|
"ops:service:gitea": {
|
||||||
"widgetId": "<uuid>",
|
"widgetId": "<uuid>",
|
||||||
"widgetType": "ops-access-path-card",
|
"widgetType": "ops-service",
|
||||||
"name": "Ops Access Path Evidence Intake"
|
"policyScope": "ops-transitional-prod",
|
||||||
|
"name": "Gitea Service"
|
||||||
},
|
},
|
||||||
"ops:backup:aggregate": {
|
"ops:service:inter-hub": {
|
||||||
"widgetId": "<uuid>",
|
"widgetId": "<uuid>",
|
||||||
"widgetType": "ops-backup-card",
|
"widgetType": "ops-service",
|
||||||
"name": "Ops Backup Evidence Intake"
|
"policyScope": "ops-production",
|
||||||
|
"name": "Inter-Hub Service"
|
||||||
},
|
},
|
||||||
"ops:drift:aggregate": {
|
"ops:endpoint:gitea-registry": {
|
||||||
"widgetId": "<uuid>",
|
"widgetId": "<uuid>",
|
||||||
"widgetType": "ops-drift-card",
|
"widgetType": "ops-endpoint",
|
||||||
"name": "Ops Inventory Drift Evidence Intake"
|
"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": {
|
"services": {
|
||||||
"state-hub": {
|
"state-hub": { "widgetRef": "ops:service:state-hub" },
|
||||||
"widgetRef": "ops:service:state-hub",
|
"gitea": { "widgetRef": "ops:service:gitea" },
|
||||||
"widgetId": "<uuid>"
|
"inter-hub": { "widgetRef": "ops:service:inter-hub" }
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"gitea:gitea-oci-registry": {
|
"gitea:gitea-oci-registry": { "widgetRef": "ops:endpoint:gitea-registry" }
|
||||||
"widgetRef": "ops:endpoint:gitea-registry",
|
|
||||||
"widgetId": "<uuid>"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"accessPaths": {
|
|
||||||
"gitea:gitea-access-1": {
|
|
||||||
"widgetRef": "ops:access-path:gitea-access-1",
|
|
||||||
"widgetId": "<uuid>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"backups": {
|
"backups": {
|
||||||
"gitea:database:gitea-db": {
|
"gitea:database:gitea-db": { "widgetRef": "ops:backup-set:aggregate" }
|
||||||
"widgetRef": "ops:backup:gitea-db",
|
|
||||||
"widgetId": "<uuid>"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"drift": {
|
"readiness": {
|
||||||
"gitea:gitea-oci-registry": {
|
"gitea:gitea-oci-registry": { "widgetRef": "ops:readiness:gitea-registry" }
|
||||||
"widgetRef": "ops:drift:gitea-oci-registry",
|
|
||||||
"widgetId": "<uuid>"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,44 +154,60 @@ the future Inter-Hub submission implementation should parse.
|
|||||||
Activity-core should choose a widget in this order:
|
Activity-core should choose a widget in this order:
|
||||||
|
|
||||||
1. If the evidence payload carries a `widget_ref` and that reference exists in
|
1. If the evidence payload carries a `widget_ref` and that reference exists in
|
||||||
the mapping, use it.
|
`widgets.byRef`, use it.
|
||||||
2. For `ops-service-observed`, use `services["<service_id>"]`.
|
2. If the event name is present in `eventAliases`, translate it to the live
|
||||||
3. For `ops-endpoint-verified`, use
|
event name before selector lookup and submission.
|
||||||
`endpoints["<service_id>:<endpoint_id>"]`.
|
3. For `ops-service-discovered`, use `services["<service_id>"]`; otherwise use
|
||||||
4. For `ops-access-path-checked`, use
|
`ops:service-catalog`.
|
||||||
`accessPaths["<service_id>:<access_path_id>"]`.
|
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
|
5. For `ops-backup-verified`, use
|
||||||
`backups["<service_id>:<backing_store_ref>"]`.
|
`backups["<service_id>:<backing_store_ref>"]`; otherwise skip until a
|
||||||
6. For `ops-inventory-drift`, use
|
backup-set widget is seeded.
|
||||||
`drift["<service_id>:<inventory_object_id>"]`.
|
6. For `ops-drift-detected`, use
|
||||||
7. If no entity-specific widget exists, use the event's aggregate widget.
|
`readiness["<service_id>:<inventory_object_id>"]`; otherwise use a seeded
|
||||||
8. If neither an entity-specific nor aggregate widget exists, skip Inter-Hub
|
readiness/risk widget chosen by the operator.
|
||||||
submission with a non-secret result that names the missing selector.
|
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
|
## Bootstrap Widget Names
|
||||||
|
|
||||||
The initial aggregate widgets should be seeded before activity-core is pointed
|
These refs are already present in the current `ops-hub` seed file and should be
|
||||||
at Inter-Hub:
|
looked up in the protected widget registry during the authenticated bootstrap:
|
||||||
|
|
||||||
| Widget ref | Widget type | Suggested name |
|
| Widget ref | Widget type | Name |
|
||||||
|---|---|---|
|
| --- | --- | --- |
|
||||||
| `ops:service:aggregate` | `ops-service-card` | Ops Service Evidence Intake |
|
| `ops:service-catalog` | `ops-service-catalog` | Operations Service Catalog |
|
||||||
| `ops:endpoint:aggregate` | `ops-endpoint-card` | Ops Endpoint Evidence Intake |
|
| `ops:service:gitea` | `ops-service` | Gitea Service |
|
||||||
| `ops:access-path:aggregate` | `ops-access-path-card` | Ops Access Path Evidence Intake |
|
| `ops:service:state-hub` | `ops-service` | State Hub Service |
|
||||||
| `ops:backup:aggregate` | `ops-backup-card` | Ops Backup Evidence Intake |
|
| `ops:service:inter-hub` | `ops-service` | Inter-Hub Service |
|
||||||
| `ops:drift:aggregate` | `ops-drift-card` | Ops Inventory Drift Evidence Intake |
|
| `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.
|
Per-entity widgets may be seeded later without changing the event contract.
|
||||||
When a per-entity widget is added, update the mapping and keep the aggregate
|
When a per-entity widget is added, update the mapping and keep a deliberate
|
||||||
widget as the fallback.
|
fallback only when the fallback widget actually exists in the protected widget
|
||||||
|
registry.
|
||||||
|
|
||||||
## Compatibility Rules
|
## Compatibility Rules
|
||||||
|
|
||||||
- `version` is required. Reject unknown major versions.
|
- `version` is required. Reject unknown major versions.
|
||||||
- Consumers must tolerate additional fields.
|
- Consumers must tolerate additional fields.
|
||||||
- Widget UUIDs may rotate, but `widgetRef` values should remain stable.
|
- Widget UUIDs may rotate, but `widgetRef` values should remain stable.
|
||||||
- Removing a widget mapping is a breaking change for that selector unless the
|
- Event aliases are a transition aid only. New activity-core submissions should
|
||||||
aggregate fallback remains present.
|
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
|
- Mapping updates must be deployed before activity-core starts sending events
|
||||||
that depend on the new selectors.
|
that depend on the new selectors.
|
||||||
- The mapping is non-secret and may be stored in a ConfigMap or environment
|
- The mapping is non-secret and may be stored in a ConfigMap or environment
|
||||||
@@ -160,7 +215,9 @@ widget as the fallback.
|
|||||||
|
|
||||||
## Minimum Valid Mapping
|
## Minimum Valid Mapping
|
||||||
|
|
||||||
For the first live smoke, an aggregate-only mapping is enough:
|
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.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -169,37 +226,30 @@ For the first live smoke, an aggregate-only mapping is enough:
|
|||||||
"slug": "ops-hub",
|
"slug": "ops-hub",
|
||||||
"id": "<ops-hub-uuid>"
|
"id": "<ops-hub-uuid>"
|
||||||
},
|
},
|
||||||
"policyScope": "ops-evidence",
|
|
||||||
"defaultViewContext": "ops-inventory-probe",
|
"defaultViewContext": "ops-inventory-probe",
|
||||||
|
"eventAliases": {
|
||||||
|
"ops-service-observed": "ops-service-discovered",
|
||||||
|
"ops-inventory-drift": "ops-drift-detected"
|
||||||
|
},
|
||||||
"events": {
|
"events": {
|
||||||
"ops-service-observed": {
|
"ops-service-discovered": {
|
||||||
"family": "services",
|
"family": "services",
|
||||||
"aggregateWidgetRef": "ops:service:aggregate"
|
"defaultWidgetRef": "ops:service-catalog"
|
||||||
},
|
},
|
||||||
"ops-endpoint-verified": {
|
"ops-endpoint-verified": {
|
||||||
"family": "endpoints",
|
"family": "endpoints",
|
||||||
"aggregateWidgetRef": "ops:endpoint:aggregate"
|
"defaultWidgetRef": "ops:endpoint:gitea-registry"
|
||||||
},
|
},
|
||||||
"ops-access-path-checked": {
|
"ops-drift-detected": {
|
||||||
"family": "accessPaths",
|
"family": "readiness",
|
||||||
"aggregateWidgetRef": "ops:access-path:aggregate"
|
"defaultWidgetRef": "ops:readiness:gitea-registry"
|
||||||
},
|
|
||||||
"ops-backup-verified": {
|
|
||||||
"family": "backups",
|
|
||||||
"aggregateWidgetRef": "ops:backup:aggregate"
|
|
||||||
},
|
|
||||||
"ops-inventory-drift": {
|
|
||||||
"family": "drift",
|
|
||||||
"aggregateWidgetRef": "ops:drift:aggregate"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"widgets": {
|
"widgets": {
|
||||||
"aggregate": {
|
"byRef": {
|
||||||
"ops:service:aggregate": { "widgetId": "<uuid>" },
|
"ops:service-catalog": { "widgetId": "<uuid>" },
|
||||||
"ops:endpoint:aggregate": { "widgetId": "<uuid>" },
|
"ops:endpoint:gitea-registry": { "widgetId": "<uuid>" },
|
||||||
"ops:access-path:aggregate": { "widgetId": "<uuid>" },
|
"ops:readiness:gitea-registry": { "widgetId": "<uuid>" }
|
||||||
"ops:backup:aggregate": { "widgetId": "<uuid>" },
|
|
||||||
"ops:drift:aggregate": { "widgetId": "<uuid>" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Ops Hub Evidence Intake - Current State
|
# Ops Hub Evidence Intake - Current State
|
||||||
|
|
||||||
Date: 2026-06-15
|
Date: 2026-06-15
|
||||||
|
Updated: 2026-06-27
|
||||||
|
|
||||||
Workplan: `IHUB-WP-0022`
|
Workplan: `IHUB-WP-0022`
|
||||||
|
|
||||||
@@ -17,6 +18,26 @@ slice is therefore contract-first:
|
|||||||
- wait on live ops-hub manifest/widgets, key provisioning, and production
|
- wait on live ops-hub manifest/widgets, key provisioning, and production
|
||||||
smoke before enabling per-entity Inter-Hub submission.
|
smoke before enabling per-entity Inter-Hub submission.
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-06-27 Live Vocabulary Alignment
|
||||||
|
|
||||||
|
Public production probes now show an `ops-hub` row and public registry
|
||||||
|
vocabulary on `https://hub.coulomb.social`. The live ops-hub seed vocabulary
|
||||||
|
supersedes the early activity-core proposal for several names:
|
||||||
|
|
||||||
|
| Early proposal | Live target |
|
||||||
|
| --- | --- |
|
||||||
|
| `ops-service-observed` | `ops-service-discovered` |
|
||||||
|
| `ops-inventory-drift` | `ops-drift-detected` |
|
||||||
|
| `ops-access-path-checked` | Deferred; no live access-path event or widget type |
|
||||||
|
| `ops-evidence` policy scope | Use declared scopes such as `ops-production`, `ops-registry`, or `ops-backup-retention` |
|
||||||
|
| card-style widget types | Use live widget types such as `ops-service`, `ops-endpoint`, `ops-backup-set`, `ops-readiness-gate`, and `ops-risk` |
|
||||||
|
|
||||||
|
The contract docs have been revised to target the live seed vocabulary and to
|
||||||
|
keep access-path evidence on the State Hub fallback path until ops-hub either
|
||||||
|
adds access-path vocabulary or records an explicit readiness/risk mapping
|
||||||
|
decision.
|
||||||
|
|
||||||
## Inter-Hub API Surface
|
## Inter-Hub API Surface
|
||||||
|
|
||||||
The current repo supports the necessary primitives through `/api/v2`.
|
The current repo supports the necessary primitives through `/api/v2`.
|
||||||
@@ -119,30 +140,39 @@ Known gates before per-entity Inter-Hub submission can be treated as live:
|
|||||||
|
|
||||||
## Recommended Manifest Vocabulary
|
## Recommended Manifest Vocabulary
|
||||||
|
|
||||||
Use one policy scope for the first slice:
|
Use the live ops-hub manifest vocabulary rather than the early proposal names.
|
||||||
|
The current target is:
|
||||||
|
|
||||||
- `ops-evidence`
|
Policy scopes:
|
||||||
|
|
||||||
Use one annotation category:
|
- `ops-local`
|
||||||
|
- `ops-transitional-prod`
|
||||||
|
- `ops-production`
|
||||||
|
- `ops-threephoenix`
|
||||||
|
- `ops-registry`
|
||||||
|
- `ops-secrets`
|
||||||
|
- `ops-backup-retention`
|
||||||
|
|
||||||
- `ops-risk`
|
Widget types for activity-core evidence:
|
||||||
|
|
||||||
Use these widget types unless the operator prefers to keep a smaller aggregate
|
- `ops-service-catalog`
|
||||||
surface:
|
- `ops-service`
|
||||||
|
- `ops-endpoint`
|
||||||
|
- `ops-backup-set`
|
||||||
|
- `ops-readiness-gate`
|
||||||
|
- `ops-risk` when risk-specific drift representation is needed
|
||||||
|
|
||||||
- `ops-service-card`
|
Event types for the first Inter-Hub activity-core slice:
|
||||||
- `ops-endpoint-card`
|
|
||||||
- `ops-access-path-card`
|
|
||||||
- `ops-backup-card`
|
|
||||||
- `ops-drift-card`
|
|
||||||
|
|
||||||
Use the activity-core event types exactly as published:
|
- `ops-service-discovered`
|
||||||
|
|
||||||
- `ops-service-observed`
|
|
||||||
- `ops-endpoint-verified`
|
- `ops-endpoint-verified`
|
||||||
- `ops-access-path-checked`
|
- `ops-backup-verified` after a backup-set widget exists
|
||||||
- `ops-backup-verified`
|
- `ops-drift-detected`
|
||||||
- `ops-inventory-drift`
|
|
||||||
|
Keep `ops-access-path-checked` deferred until ops-hub declares access-path
|
||||||
|
vocabulary or a separate decision maps those checks to readiness/risk events.
|
||||||
|
Do not seed the old `ops-evidence` policy scope unless the manifest is
|
||||||
|
explicitly expanded to include it.
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ status: active
|
|||||||
owner: codex
|
owner: codex
|
||||||
topic_slug: inter_hub
|
topic_slug: inter_hub
|
||||||
created: "2026-06-15"
|
created: "2026-06-15"
|
||||||
updated: "2026-06-16"
|
updated: "2026-06-27"
|
||||||
planning_priority: high
|
planning_priority: high
|
||||||
planning_order: 22
|
planning_order: 22
|
||||||
related_repos:
|
related_repos:
|
||||||
@@ -94,20 +94,22 @@ closed.
|
|||||||
|
|
||||||
## Proposed Evidence Vocabulary
|
## Proposed Evidence Vocabulary
|
||||||
|
|
||||||
Activity-core has already declared the event contracts it wants to send:
|
The original activity-core suggestion used five evidence names. Production
|
||||||
|
ops-hub now exposes a live seed vocabulary, so the Inter-Hub activation target
|
||||||
|
is the compatibility mapping below rather than the early names verbatim:
|
||||||
|
|
||||||
| Event type | Suggested widget family | Purpose |
|
| Original evidence intent | Live Inter-Hub target | Widget family | Purpose |
|
||||||
|---|---|---|
|
|---|---|---|---|
|
||||||
| `ops-service-observed` | service inventory | Record that a service exists and was observed. |
|
| `ops-service-observed` | `ops-service-discovered` | `ops-service` / `ops-service-catalog` | Record that a service exists and was discovered. |
|
||||||
| `ops-endpoint-verified` | endpoint inventory | Record endpoint reachability, auth challenge, or health verification. |
|
| `ops-endpoint-verified` | `ops-endpoint-verified` | `ops-endpoint` | Record endpoint reachability, auth challenge, or health verification. |
|
||||||
| `ops-access-path-checked` | access path inventory | Record operator or service access path verification. |
|
| `ops-access-path-checked` | deferred | State Hub fallback until ops-hub adds access-path vocabulary or maps it to readiness/risk | Record operator or service access path verification without inventing unsupported registry names. |
|
||||||
| `ops-backup-verified` | backup inventory | Record backup presence, recency, or restore-drill evidence. |
|
| `ops-backup-verified` | `ops-backup-verified` | `ops-backup-set` | Record backup presence, recency, or restore-drill evidence after a backup-set widget exists. |
|
||||||
| `ops-inventory-drift` | drift inventory | Record drift between expected and observed operations inventory. |
|
| `ops-inventory-drift` | `ops-drift-detected` | `ops-readiness-gate` / `ops-risk` | Record drift between expected and observed operations inventory. |
|
||||||
|
|
||||||
The first implementation should keep one stable widget per entity and evidence
|
The first implementation should use existing seeded widgets where possible and
|
||||||
family where possible. If activity-core cannot know entity identity reliably,
|
seed only the missing backup/risk widgets that are needed for the attended
|
||||||
use one aggregate intake widget per family as a conservative first slice, then
|
smoke. If activity-core cannot know entity identity reliably, keep State Hub
|
||||||
split into per-entity widgets after payload evidence proves stable.
|
fallback evidence rather than submitting to a made-up aggregate widget.
|
||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
@@ -179,6 +181,13 @@ versioned non-secret `OPS_HUB_WIDGET_MAPPING` JSON shape, aggregate-first
|
|||||||
fallback widgets, per-entity selector rules, stable `widgetRef` values, and
|
fallback widgets, per-entity selector rules, stable `widgetRef` values, and
|
||||||
Secret-only handling for `OPS_HUB_KEY`.
|
Secret-only handling for `OPS_HUB_KEY`.
|
||||||
|
|
||||||
|
Compatibility note (2026-06-27): revised the mapping contract to match the
|
||||||
|
live ops-hub seed vocabulary. The mapping now aliases `ops-service-observed`
|
||||||
|
to `ops-service-discovered`, aliases `ops-inventory-drift` to
|
||||||
|
`ops-drift-detected`, uses declared ops policy scopes instead of the old
|
||||||
|
`ops-evidence` proposal, and defers access-path evidence until ops-hub has a
|
||||||
|
supported event/widget target.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### T03 - Prepare manifest vocabulary and seed widgets
|
### T03 - Prepare manifest vocabulary and seed widgets
|
||||||
@@ -218,6 +227,12 @@ environment with the `5101eb5` COUNT decode fix live and an authenticated
|
|||||||
operator/runtime key path. The required vocabulary is documented, but no live
|
operator/runtime key path. The required vocabulary is documented, but no live
|
||||||
manifest or widget seed was performed in this implementation slice.
|
manifest or widget seed was performed in this implementation slice.
|
||||||
|
|
||||||
|
Progress note (2026-06-27): public production probes show an `ops-hub` row and
|
||||||
|
the live seed registry vocabulary, and the contract docs now target that live
|
||||||
|
vocabulary. T03 remains waiting because protected widget lookup, widget ids,
|
||||||
|
any missing backup/risk seed widgets, and authenticated smoke evidence still
|
||||||
|
require the operator/runtime key path.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### T04 - Provision the runtime API key outside Git
|
### T04 - Provision the runtime API key outside Git
|
||||||
@@ -289,6 +304,11 @@ Inter-Hub request envelope, shared validation rules, idempotency expectations,
|
|||||||
forbidden payload material, expected API errors, and one example for each
|
forbidden payload material, expected API errors, and one example for each
|
||||||
activity-core event type.
|
activity-core event type.
|
||||||
|
|
||||||
|
Compatibility note (2026-06-27): revised payload examples to submit only live
|
||||||
|
ops-hub event types. Access-path payloads are documented as deferred fallback
|
||||||
|
evidence, and old event names are treated as aliases that should not be posted
|
||||||
|
to Inter-Hub.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### T06 - Validate fallback-first intake
|
### T06 - Validate fallback-first intake
|
||||||
|
|||||||
Reference in New Issue
Block a user