generated from coulomb/repo-seed
221 lines
6.9 KiB
Markdown
221 lines
6.9 KiB
Markdown
# Ops Hub Activity-Core Event Payloads
|
|
|
|
Date: 2026-06-15
|
|
|
|
Workplan: `IHUB-WP-0022`
|
|
|
|
## Inter-Hub Request Shape
|
|
|
|
Activity-core should submit ops evidence through:
|
|
|
|
```text
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"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`.
|
|
|
|
## 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`.
|
|
- `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 Observed
|
|
|
|
```json
|
|
{
|
|
"widgetId": "<service-widget-uuid>",
|
|
"eventType": "ops-service-observed",
|
|
"viewContext": "ops-inventory-probe/services",
|
|
"metadata": {
|
|
"type": "ops-service-observed",
|
|
"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-observed",
|
|
"service_id": "state-hub",
|
|
"service_name": "State Hub",
|
|
"environment": "local",
|
|
"lifecycle_state": "observed",
|
|
"observed_status": "ok",
|
|
"observed_at": "2026-06-05T10:15:01Z"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Example: Endpoint Verified
|
|
|
|
```json
|
|
{
|
|
"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"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Example: Access Path Checked
|
|
|
|
```json
|
|
{
|
|
"widgetId": "<access-path-widget-uuid>",
|
|
"eventType": "ops-access-path-checked",
|
|
"viewContext": "ops-inventory-probe/access-paths",
|
|
"metadata": {
|
|
"type": "ops-access-path-checked",
|
|
"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-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
|
|
|
|
```json
|
|
{
|
|
"widgetId": "<backup-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"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Example: Inventory Drift
|
|
|
|
```json
|
|
{
|
|
"widgetId": "<drift-widget-uuid>",
|
|
"eventType": "ops-inventory-drift",
|
|
"viewContext": "ops-inventory-probe/drift",
|
|
"metadata": {
|
|
"type": "ops-inventory-drift",
|
|
"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-inventory-drift",
|
|
"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"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 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 | Activate the ops-hub manifest vocabulary. |
|
|
| `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 `unregistered_policy_scope` during widget seed | Policy scope is absent | Declare and activate `ops-evidence`. |
|
|
|
|
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.
|