implement ops hub extension contract
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a053ff-1d6f-7fe2-ac1c-a6eb40a42a0c
This commit is contained in:
parent
9bbbf2032d
commit
75f4944144
14 changed files with 662 additions and 43 deletions
18
Makefile
18
Makefile
|
|
@ -1,17 +1,33 @@
|
|||
IHUB_BASE ?= https://hub.coulomb.social
|
||||
IHUB_OPERATOR_KEY_FILE ?=
|
||||
OPS_HUB_RUNTIME_KEY_OUTPUT ?=
|
||||
HUB_CORE_REPO ?= ../hub-core
|
||||
HUB_CORE_PYTHON ?= $(HUB_CORE_REPO)/.venv/bin/python
|
||||
|
||||
.PHONY: help interhub-gate interhub-bootstrap interhub-bootstrap-dry-run interhub-bootstrap-help test
|
||||
.PHONY: help hub-extension-check hub-core-conformance interhub-gate interhub-bootstrap interhub-bootstrap-dry-run interhub-bootstrap-help test
|
||||
|
||||
help:
|
||||
@echo "Targets:"
|
||||
@echo " hub-extension-check Validate the owner package against hub-core 0.1.0 schemas/catalog"
|
||||
@echo " hub-core-conformance Exercise the owner package in a disposable hub-core runtime"
|
||||
@echo " interhub-gate Probe whether production Inter-Hub exposes the ops-hub bootstrap API surface"
|
||||
@echo " interhub-bootstrap-help Show bootstrap helper options"
|
||||
@echo " interhub-bootstrap-dry-run Plan bootstrap actions with IHUB_OPERATOR_KEY_FILE (no mutations)"
|
||||
@echo " interhub-bootstrap Run attended ops-hub Inter-Hub bootstrap with IHUB_OPERATOR_KEY_FILE"
|
||||
@echo " test Run unit tests"
|
||||
|
||||
hub-extension-check:
|
||||
@test -x "$(HUB_CORE_PYTHON)" || \
|
||||
(echo "hub-core environment missing at $(HUB_CORE_PYTHON)" >&2; exit 2)
|
||||
PYTHONPATH=src "$(HUB_CORE_PYTHON)" scripts/hub-extension-check.py \
|
||||
--hub-core-repo "$(HUB_CORE_REPO)"
|
||||
|
||||
hub-core-conformance:
|
||||
@test -x "$(HUB_CORE_PYTHON)" || \
|
||||
(echo "hub-core environment missing at $(HUB_CORE_PYTHON)" >&2; exit 2)
|
||||
PYTHONPATH=src "$(HUB_CORE_PYTHON)" scripts/hub-core-conformance.py \
|
||||
--hub-core-repo "$(HUB_CORE_REPO)"
|
||||
|
||||
interhub-gate:
|
||||
IHUB_BASE="$(IHUB_BASE)" python3 scripts/interhub-gate-probe.py
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
Operations / System 1 extension for Core Hub.
|
||||
Operations / System 1 extension for Hub Core.
|
||||
|
||||
The versioned `helixforge.hub-extension` descriptor, manifest, validation, and
|
||||
State Hub retirement boundary are documented in
|
||||
[`docs/hub-extension.md`](docs/hub-extension.md).
|
||||
|
||||
`ops-hub` is the operational truth surface for environments, hosts, clusters,
|
||||
services, endpoints, releases, backups, incidents, risks, runbooks, readiness
|
||||
gates, and migration waves. Generic hub framework work stays in `core-hub`;
|
||||
gates, and migration waves. Generic hub framework work stays in `hub-core`;
|
||||
operations-specific extension code belongs here. Historical Haskell Inter-Hub is
|
||||
`inter-hub-haskell` (retired 2026-07-08).
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
| Kind | ID | Status | Lane | Source |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| workplan | OPS-WP-0003 | proposed | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
| task | OPS-WP-0003-T01 | todo | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
| task | OPS-WP-0003-T02 | todo | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
| task | OPS-WP-0003-T03 | todo | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
| workplan | OPS-WP-0003 | finished | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
| task | OPS-WP-0003-T01 | done | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
| task | OPS-WP-0003-T02 | done | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
| task | OPS-WP-0003-T03 | done | — | workplans/OPS-WP-0003-extension-contract-alignment.md |
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
# Ops Hub Bootstrap Runbook
|
||||
|
||||
Date: 2026-06-17
|
||||
Date: 2026-08-31
|
||||
|
||||
## Purpose
|
||||
|
||||
This runbook is the operator-ready path for activating `ops-hub` in production
|
||||
Inter-Hub. It covers the preferred API bootstrap, key custody expectations, the
|
||||
ops-warden remote execution lane, and the explicit SQL fallback.
|
||||
Hub Core's `/api/v2` compatibility surface. It covers the preferred API
|
||||
bootstrap, key custody expectations, the ops-warden remote execution lane, and
|
||||
the explicit SQL fallback.
|
||||
|
||||
Use this when finishing `CUST-WP-0047-T05` or any later ops-hub Inter-Hub
|
||||
activation task.
|
||||
|
||||
## Inputs
|
||||
|
||||
- Hub-extension package: `registry/hub-extension/v0.1.0/ops-hub.extension.json`
|
||||
- Manifest draft: `seeds/ops-hub-manifest.draft.json`
|
||||
- Widget seed: `seeds/ops-hub-widgets.seed.json`
|
||||
- API helper: `scripts/ops-hub-bootstrap-api.py`
|
||||
|
|
@ -167,14 +169,21 @@ curl -s https://hub.coulomb.social/api/v2/hub-registry
|
|||
Expected: `ops-hub` is visible, and the operator can see the seeded widgets in
|
||||
the authenticated UI.
|
||||
|
||||
## Current Live-Execution Blocker
|
||||
## Authenticated rerun boundary
|
||||
|
||||
The repo-side helper, dry-run path, and runbook are ready. The remaining step is
|
||||
an operator-attended production run with:
|
||||
Ops Hub bootstrap and consumer gates passed during the hub-core candidate
|
||||
cutover (`RAPPCOREHUB-WP-0002-T04`), and the 2026-08-31 post-cutover public
|
||||
gate passes against `https://hub.coulomb.social`. There is no pending bootstrap
|
||||
mutation merely to finish `OPS-WP-0003`.
|
||||
|
||||
- an operator-provided `IHUB_OPERATOR_KEY_FILE`,
|
||||
- an OpenBao-materialized key on a trusted host, or
|
||||
- an explicitly approved deployment-side migration/bootstrap path.
|
||||
An authenticated rerun remains a high-risk attended operation because it may
|
||||
create a consumer key or production records. Use it only for a real registration
|
||||
change, start with `warden route show inter-hub-bootstrap-ssh --json`, and
|
||||
record non-secret evidence. For routine validation use:
|
||||
|
||||
Until one of those is available, run `make interhub-gate`, `make test`, and
|
||||
`make interhub-bootstrap-dry-run` for local validation.
|
||||
```bash
|
||||
make test
|
||||
make hub-extension-check
|
||||
make hub-core-conformance
|
||||
make interhub-gate
|
||||
```
|
||||
|
|
|
|||
78
docs/hub-extension.md
Normal file
78
docs/hub-extension.md
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
# Ops Hub extension package
|
||||
|
||||
Ops Hub implements the owner side of `helixforge.hub-extension` 0.1.0. Hub
|
||||
Core owns and runs the framework ports; this repository owns the operations
|
||||
descriptor, capability declarations, event boundary, compatibility bootstrap,
|
||||
and evidence used to move service-catalog responsibility out of State Hub.
|
||||
|
||||
The canonical package is
|
||||
`registry/hub-extension/v0.1.0/ops-hub.extension.json`. Its stable identity is
|
||||
`capability.operations.ops-hub`; it also declares
|
||||
`capability.operations.service-catalog` as an owner capability.
|
||||
|
||||
## Ownership boundary
|
||||
|
||||
| Surface | Owner |
|
||||
| --- | --- |
|
||||
| Descriptor, manifest, operational vocabulary, service-catalog records | `ops-hub` |
|
||||
| `port.*` schemas, event catalog, registry and projection runtime | `hub-core` |
|
||||
| Runtime and operator credentials | OpenBao/platform custody; never this package |
|
||||
| State Hub compatibility routes during retirement | `state-hub` until their evidenced cutover |
|
||||
|
||||
Ops Hub is an aspect hub, not a second hub-core deployment. The API endpoint in
|
||||
the manifest is a discovery reference to the hub-core-hosted compatibility
|
||||
surface at `https://hub.coulomb.social/api/v2`.
|
||||
|
||||
## Events and legacy compatibility
|
||||
|
||||
The 0.1.0 port contract emits `ops.endpoint.verified` and consumes
|
||||
`hub.progress.recorded`. The older `/api/v2` bootstrap seed uses
|
||||
`ops-endpoint-verified`; hub-core's compatibility adapter retains that legacy
|
||||
spelling. New port-native evidence uses the dotted catalog event and must not
|
||||
silently rewrite historical events.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
make test
|
||||
make hub-extension-check
|
||||
make hub-core-conformance
|
||||
make interhub-gate
|
||||
```
|
||||
|
||||
`hub-extension-check` validates the owner package against hub-core's packaged
|
||||
Draft 2020-12 descriptor and manifest schemas, checks every declared event
|
||||
against hub-core's catalog, and emits a non-secret checksum receipt.
|
||||
|
||||
`hub-core-conformance` injects this repository's package into a disposable
|
||||
hub-core reference runtime. Contract version 0.1.0 automates C1, C3, C4, C5,
|
||||
C6, and C8 plus Tier-3 F2 and F3. C2 registry resolution, C7 raw-port policy,
|
||||
C9 dependency-aware readiness, and C10 version negotiation are not automated
|
||||
by the current hub-core harness and are not represented as passing.
|
||||
|
||||
`interhub-gate` is the safe post-cutover production check. It proves that the
|
||||
hub-core-hosted `/api/v2` surface exposes the bootstrap resources without
|
||||
requiring a credential or mutating production.
|
||||
|
||||
## Authenticated bootstrap
|
||||
|
||||
The existing `scripts/ops-hub-bootstrap-api.py` remains the idempotent
|
||||
compatibility bootstrap client. It creates or reuses the hub, manifest, API
|
||||
consumer, widgets, and verification event. Follow `docs/bootstrap-runbook.md`.
|
||||
|
||||
Operator and runtime keys are never arguments, logs, Git content, or extension
|
||||
fields. Credential routing starts with:
|
||||
|
||||
```bash
|
||||
warden route show inter-hub-bootstrap-ssh --json
|
||||
```
|
||||
|
||||
The current route is high-risk and attended. A source change or public gate
|
||||
does not authorize another live bootstrap mutation.
|
||||
|
||||
## State Hub retirement handoff
|
||||
|
||||
This package satisfies the receiving-side contract for `STATE-WP-0079` Wave
|
||||
D1. Removing State Hub's service-catalog routes still requires a separate
|
||||
caller inventory, projection/count comparison, rollback receipt, and explicit
|
||||
production writer-cutover authorization. Owner readiness is not cutover proof.
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
repo: ops-hub
|
||||
reason: >
|
||||
Only a single diagnostic bootstrap-API probe script exists; the described operational-truth surface (hosts/services/incidents/runbooks) isn't implemented yet.
|
||||
reviewed: "2026-07-06"
|
||||
reviewed_by: claude-code
|
||||
revisit: >
|
||||
Revisit once the operational-truth surface has real implementation beyond the probe script.
|
||||
---
|
||||
|
||||
# No reusable capability
|
||||
|
||||
This repo was reviewed for the `reuse-surface` capability registry
|
||||
(REUSE-WP-0017 coverage campaign) and has no capability to register at this
|
||||
time. See the `reason` field above.
|
||||
140
registry/capabilities/capability.operations.ops-hub.md
Normal file
140
registry/capabilities/capability.operations.ops-hub.md
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
---
|
||||
id: capability.operations.ops-hub
|
||||
name: Operations Hub Extension Contract
|
||||
summary: Versioned hub-extension package and bootstrap client for operational truth, service-catalog records, readiness evidence, and migration gates.
|
||||
owner: ops-hub
|
||||
status: reviewed
|
||||
domain: operations
|
||||
tags:
|
||||
- operations
|
||||
- service-catalog
|
||||
- readiness
|
||||
- hub-extension
|
||||
|
||||
maturity:
|
||||
discovery:
|
||||
current: D3
|
||||
target: D4
|
||||
confidence: high
|
||||
rationale: >
|
||||
The owner boundary, versioned descriptor/manifest, hub-core ports, event
|
||||
families, compatibility bootstrap, and State Hub retirement handoff are
|
||||
explicit and tested.
|
||||
availability:
|
||||
current: A1
|
||||
target: A3
|
||||
confidence: high
|
||||
rationale: >
|
||||
The contract package and idempotent bootstrap client are available from
|
||||
source; the framework runtime is hosted by hub-core rather than this repo.
|
||||
|
||||
external_evidence:
|
||||
completeness:
|
||||
level: C1
|
||||
confidence: medium
|
||||
basis: scope_vs_intent_and_consumer_expectations
|
||||
satisfied_expectations:
|
||||
- versioned extension descriptor and manifest
|
||||
- hub-core schema and event-catalog validation
|
||||
- disposable Tier 2/3 conformance profile
|
||||
- production compatibility API public gate
|
||||
broken_expectations: []
|
||||
out_of_scope_expectations:
|
||||
- independent framework runtime
|
||||
- secret custody
|
||||
reliability:
|
||||
level: R1
|
||||
confidence: high
|
||||
basis: consumer_quality_signals
|
||||
known_reliability_risks:
|
||||
- authenticated re-bootstrap requires the attended high-risk access lane
|
||||
|
||||
discovery:
|
||||
intent: >
|
||||
Let hub-core host an Ops Hub extension without transferring ownership of
|
||||
operational records or credentials into the framework.
|
||||
includes:
|
||||
- descriptor and capability manifest
|
||||
- operations service-catalog declaration
|
||||
- readiness evidence event boundary
|
||||
- compatibility bootstrap client and public gate
|
||||
excludes:
|
||||
- hub-core framework implementation
|
||||
- credential storage or vending
|
||||
- State Hub compatibility routes after cutover
|
||||
assumptions:
|
||||
- hub-core remains the surviving framework runtime
|
||||
use_cases:
|
||||
- register Ops Hub against hub-core
|
||||
- publish and project service-catalog records
|
||||
- attach non-secret readiness evidence
|
||||
research_memos: []
|
||||
|
||||
availability:
|
||||
current_level: A1
|
||||
target_level: A3
|
||||
current_artifacts:
|
||||
- registry/hub-extension/v0.1.0/ops-hub.extension.json
|
||||
- scripts/ops-hub-bootstrap-api.py
|
||||
- scripts/hub-extension-check.py
|
||||
- scripts/hub-core-conformance.py
|
||||
target_artifacts:
|
||||
- policy-bound unattended registration lane
|
||||
consumption_modes:
|
||||
- contract package
|
||||
- command-line bootstrap
|
||||
|
||||
relations:
|
||||
depends_on:
|
||||
- capability.infotech.hub-core-library
|
||||
supports:
|
||||
- capability.operations.service-catalog
|
||||
related_to: []
|
||||
|
||||
evidence:
|
||||
documentation:
|
||||
- docs/hub-extension.md
|
||||
- docs/bootstrap-runbook.md
|
||||
tests:
|
||||
- tests/test_extension_contract.py
|
||||
- make hub-core-conformance
|
||||
consumer_feedback:
|
||||
- RAPPCOREHUB-WP-0002-T04
|
||||
bug_reports: []
|
||||
incidents: []
|
||||
|
||||
consumer_guidance:
|
||||
recommended_for:
|
||||
- registering and validating the Ops Hub extension on hub-core
|
||||
- migrating State Hub service-catalog consumers to an owner-backed contract
|
||||
not_recommended_for:
|
||||
- treating ops-hub as another hub-core runtime
|
||||
- retrieving or storing credentials
|
||||
known_limitations:
|
||||
- C2, C7, C9, and C10 are not automated by hub-core contract version 0.1.0
|
||||
- authenticated live bootstrap requires the governed attended access lane
|
||||
|
||||
promotion_history:
|
||||
- date: "2026-08-31"
|
||||
dimension: discovery
|
||||
from: D0
|
||||
to: D3
|
||||
rationale: Published the owner-side 0.1.0 package and conformance evidence.
|
||||
author: codex
|
||||
- date: "2026-08-31"
|
||||
dimension: availability
|
||||
from: A0
|
||||
to: A1
|
||||
rationale: Published source-level validation and bootstrap consumers.
|
||||
author: codex
|
||||
---
|
||||
|
||||
# Operations Hub Extension Contract
|
||||
|
||||
The capability is the owner-side contract package and bootstrap client. Hub
|
||||
Core supplies the framework runtime; Ops Hub retains the operational vocabulary,
|
||||
service-catalog model, readiness evidence, and migration-gate ownership.
|
||||
|
||||
The package contains no credentials. Authenticated bootstrap follows
|
||||
`docs/bootstrap-runbook.md` and emits only non-secret identifiers and key
|
||||
prefixes as evidence.
|
||||
76
registry/hub-extension/v0.1.0/ops-hub.extension.json
Normal file
76
registry/hub-extension/v0.1.0/ops-hub.extension.json
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"descriptor": {
|
||||
"contract_id": "helixforge.hub-extension",
|
||||
"contract_version": "0.1.0",
|
||||
"hub_slug": "ops-hub",
|
||||
"display_name": "Operations Hub",
|
||||
"description": "Operations aspect hub for operational truth, service-catalog records, readiness evidence, and migration gates.",
|
||||
"domain": "operations",
|
||||
"hub_kind": "aspect",
|
||||
"status": "active",
|
||||
"reuse_surface_id": "capability.operations.ops-hub",
|
||||
"contract_version_min": "0.1.0",
|
||||
"contract_version_max": "0.1.0",
|
||||
"vsm_system": "operations",
|
||||
"vsm_function": "coordination"
|
||||
},
|
||||
"manifest": {
|
||||
"manifest_version": "0.1.0",
|
||||
"schema_version": "0.1.0",
|
||||
"reuse_surface_id": "capability.operations.ops-hub",
|
||||
"provides": [
|
||||
"capability.operations.ops-hub",
|
||||
"capability.operations.service-catalog"
|
||||
],
|
||||
"consumes": [
|
||||
"port.registry",
|
||||
"port.messaging",
|
||||
"port.events.interaction",
|
||||
"port.projection.query",
|
||||
"port.policy"
|
||||
],
|
||||
"events_emitted": [
|
||||
"ops.endpoint.verified"
|
||||
],
|
||||
"events_consumed": [
|
||||
"hub.progress.recorded"
|
||||
],
|
||||
"endpoints": [
|
||||
{
|
||||
"id": "api",
|
||||
"discovery_key": "service.ops-hub.http"
|
||||
},
|
||||
{
|
||||
"id": "framework-api",
|
||||
"url": "https://hub.coulomb.social/api/v2"
|
||||
},
|
||||
{
|
||||
"id": "docs",
|
||||
"url": "https://forgejo.coulomb.social/coulomb/ops-hub/src/branch/main/docs/hub-extension.md"
|
||||
}
|
||||
],
|
||||
"policy_scopes": [
|
||||
"ops.catalog.read",
|
||||
"ops.evidence.write"
|
||||
],
|
||||
"widgets": [
|
||||
{
|
||||
"id": "ops-overview",
|
||||
"kind": "widget",
|
||||
"description": "Non-secret operations overview surface."
|
||||
}
|
||||
],
|
||||
"operator_surfaces": [
|
||||
{
|
||||
"id": "ops-console",
|
||||
"kind": "console",
|
||||
"description": "Operator-facing operations console hosted by hub-core projections."
|
||||
},
|
||||
{
|
||||
"id": "ops-bootstrap",
|
||||
"kind": "cli",
|
||||
"description": "Attended, idempotent registration and compatibility bootstrap."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,20 @@
|
|||
version: 1
|
||||
updated: '2026-06-16'
|
||||
updated: '2026-08-31'
|
||||
domain: helix_forge
|
||||
capabilities: []
|
||||
capabilities:
|
||||
- id: capability.operations.ops-hub
|
||||
name: Operations Hub Extension Contract
|
||||
summary: Versioned hub-extension package and bootstrap client for operational truth, service-catalog records, readiness evidence, and migration gates.
|
||||
vector: D3 / A1 / C1 / R1
|
||||
domain: operations
|
||||
status: reviewed
|
||||
owner: ops-hub
|
||||
path: registry/capabilities/capability.operations.ops-hub.md
|
||||
tags:
|
||||
- operations
|
||||
- service-catalog
|
||||
- readiness
|
||||
- hub-extension
|
||||
consumption_modes:
|
||||
- contract package
|
||||
- command-line bootstrap
|
||||
|
|
|
|||
35
scripts/hub-core-conformance.py
Normal file
35
scripts/hub-core-conformance.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Run Ops Hub's package through a disposable hub-core reference runtime."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--hub-core-repo", type=Path, default=Path("../hub-core"))
|
||||
args = parser.parse_args()
|
||||
sys.path.insert(0, str(args.hub_core_repo.resolve()))
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
from hub_core.conformance import ConformanceHarness
|
||||
from hub_core.runtime.app import create_app
|
||||
from hub_core.runtime.config import RuntimeSettings
|
||||
from hub_core.runtime.store import InMemoryPortStore
|
||||
from ops_hub.extension_contract import load_extension_package
|
||||
|
||||
settings = RuntimeSettings(environment="test", backend="memory", allow_ephemeral=True)
|
||||
target = TestClient(create_app(settings=settings, port_store=InMemoryPortStore()))
|
||||
harness = ConformanceHarness(target)
|
||||
harness.package = load_extension_package()
|
||||
report = harness.run()
|
||||
print(json.dumps(report.to_dict(), indent=2, sort_keys=True))
|
||||
return 0 if report.passed else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
50
scripts/hub-extension-check.py
Normal file
50
scripts/hub-extension-check.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Validate the owner package against hub-core's versioned schemas."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from jsonschema import Draft202012Validator, FormatChecker
|
||||
|
||||
from ops_hub.extension_contract import load_extension_package, validation_receipt
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--hub-core-repo", type=Path, default=Path("../hub-core"))
|
||||
args = parser.parse_args()
|
||||
|
||||
root = (
|
||||
args.hub_core_repo
|
||||
/ "hub_core"
|
||||
/ "contracts"
|
||||
/ "helixforge_hub_extension"
|
||||
/ "v0_1_0"
|
||||
)
|
||||
schemas = root / "schemas"
|
||||
package = load_extension_package()
|
||||
for key, schema_name in (
|
||||
("descriptor", "hub-descriptor.schema.json"),
|
||||
("manifest", "hub-manifest.schema.json"),
|
||||
):
|
||||
schema = json.loads((schemas / schema_name).read_text(encoding="utf-8"))
|
||||
Draft202012Validator.check_schema(schema)
|
||||
Draft202012Validator(schema, format_checker=FormatChecker()).validate(package[key])
|
||||
|
||||
catalog = json.loads((root / "catalogs" / "event-types.json").read_text(encoding="utf-8"))
|
||||
known_events = {entry["type"] for entry in catalog["event_types"]}
|
||||
declared_events = set(package["manifest"]["events_emitted"])
|
||||
declared_events.update(package["manifest"]["events_consumed"])
|
||||
unknown = sorted(declared_events - known_events)
|
||||
if unknown:
|
||||
raise SystemExit("events absent from hub-core catalog: " + ", ".join(unknown))
|
||||
|
||||
print(json.dumps(validation_receipt(), indent=2, sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
102
src/ops_hub/extension_contract.py
Normal file
102
src/ops_hub/extension_contract.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
"""Ops Hub's owner-side ``helixforge.hub-extension`` package helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from collections.abc import Mapping, Sequence
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
CONTRACT_ID = "helixforge.hub-extension"
|
||||
CONTRACT_VERSION = "0.1.0"
|
||||
REUSE_SURFACE_ID = "capability.operations.ops-hub"
|
||||
PACKAGE_PATH = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "registry"
|
||||
/ "hub-extension"
|
||||
/ "v0.1.0"
|
||||
/ "ops-hub.extension.json"
|
||||
)
|
||||
|
||||
_SECRET_KEY = re.compile(
|
||||
r"(?:^|_)(?:api_?(?:key|token)|access_?token|auth_?token|client_?secret|"
|
||||
r"credential|passwd|password|private_?key|secret)(?:$|_)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
_SECRET_VALUE = re.compile(
|
||||
r"(?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|redis)://"
|
||||
r"[^\s/:]+:[^\s/@]+@|-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def load_extension_package(path: Path = PACKAGE_PATH) -> dict[str, Any]:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def package_sha256(path: Path = PACKAGE_PATH) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def find_secret_violations(value: Any, path: str = "$") -> list[str]:
|
||||
"""Return paths with secret-shaped keys/values without returning values."""
|
||||
|
||||
violations: list[str] = []
|
||||
if isinstance(value, Mapping):
|
||||
for key, child in value.items():
|
||||
child_path = f"{path}.{key}"
|
||||
if _SECRET_KEY.search(str(key)):
|
||||
violations.append(child_path)
|
||||
violations.extend(find_secret_violations(child, child_path))
|
||||
elif isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
|
||||
for index, child in enumerate(value):
|
||||
violations.extend(find_secret_violations(child, f"{path}[{index}]"))
|
||||
elif isinstance(value, str) and _SECRET_VALUE.search(value):
|
||||
violations.append(path)
|
||||
return violations
|
||||
|
||||
|
||||
def validate_owner_invariants(package: Mapping[str, Any]) -> None:
|
||||
descriptor = package.get("descriptor")
|
||||
manifest = package.get("manifest")
|
||||
if not isinstance(descriptor, Mapping) or not isinstance(manifest, Mapping):
|
||||
raise ValueError("package must contain descriptor and manifest objects")
|
||||
if descriptor.get("contract_id") != CONTRACT_ID:
|
||||
raise ValueError("unexpected extension contract id")
|
||||
if descriptor.get("contract_version") != CONTRACT_VERSION:
|
||||
raise ValueError("unexpected extension contract version")
|
||||
if descriptor.get("hub_slug") != "ops-hub":
|
||||
raise ValueError("descriptor is not owned by ops-hub")
|
||||
if descriptor.get("reuse_surface_id") != REUSE_SURFACE_ID:
|
||||
raise ValueError("descriptor reuse_surface_id is not canonical")
|
||||
if manifest.get("reuse_surface_id") != REUSE_SURFACE_ID:
|
||||
raise ValueError("descriptor and manifest reuse_surface_id differ")
|
||||
if REUSE_SURFACE_ID not in manifest.get("provides", []):
|
||||
raise ValueError("manifest does not provide its canonical capability")
|
||||
if "capability.operations.service-catalog" not in manifest.get("provides", []):
|
||||
raise ValueError("manifest omits the service-catalog retirement handoff")
|
||||
if "ops.endpoint.verified" not in manifest.get("events_emitted", []):
|
||||
raise ValueError("manifest omits its governed verification event")
|
||||
violations = find_secret_violations(package)
|
||||
if violations:
|
||||
raise ValueError("secret-like material at " + ", ".join(violations))
|
||||
|
||||
|
||||
def validation_receipt(path: Path = PACKAGE_PATH) -> dict[str, Any]:
|
||||
package = load_extension_package(path)
|
||||
validate_owner_invariants(package)
|
||||
return {
|
||||
"ok": True,
|
||||
"contract_id": CONTRACT_ID,
|
||||
"contract_version": CONTRACT_VERSION,
|
||||
"package": str(path),
|
||||
"sha256": package_sha256(path),
|
||||
"reuse_surface_id": REUSE_SURFACE_ID,
|
||||
"provides": package["manifest"]["provides"],
|
||||
"events_emitted": package["manifest"]["events_emitted"],
|
||||
"events_consumed": package["manifest"]["events_consumed"],
|
||||
"secret_violations": [],
|
||||
}
|
||||
51
tests/test_extension_contract.py
Normal file
51
tests/test_extension_contract.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from ops_hub.extension_contract import (
|
||||
CONTRACT_ID,
|
||||
CONTRACT_VERSION,
|
||||
PACKAGE_PATH,
|
||||
REUSE_SURFACE_ID,
|
||||
find_secret_violations,
|
||||
load_extension_package,
|
||||
package_sha256,
|
||||
validate_owner_invariants,
|
||||
validation_receipt,
|
||||
)
|
||||
|
||||
|
||||
class ExtensionContractTests(unittest.TestCase):
|
||||
def test_owner_package_is_valid_and_versioned(self) -> None:
|
||||
package = load_extension_package()
|
||||
validate_owner_invariants(package)
|
||||
self.assertEqual(package["descriptor"]["contract_id"], CONTRACT_ID)
|
||||
self.assertEqual(package["descriptor"]["contract_version"], CONTRACT_VERSION)
|
||||
self.assertEqual(package["manifest"]["reuse_surface_id"], REUSE_SURFACE_ID)
|
||||
|
||||
def test_package_is_canonical_json_and_receipt_is_non_secret(self) -> None:
|
||||
package = load_extension_package()
|
||||
self.assertEqual(package, json.loads(PACKAGE_PATH.read_text(encoding="utf-8")))
|
||||
receipt = validation_receipt()
|
||||
self.assertTrue(receipt["ok"])
|
||||
self.assertEqual(len(package_sha256()), 64)
|
||||
self.assertEqual(receipt["secret_violations"], [])
|
||||
|
||||
def test_secret_detection_reports_paths_without_values(self) -> None:
|
||||
value = {
|
||||
"nested": {"api_token": "do-not-return"},
|
||||
"database": "postgresql://runtime:do-not-return@example.invalid/hub",
|
||||
}
|
||||
self.assertEqual(find_secret_violations(value), ["$.nested.api_token", "$.database"])
|
||||
|
||||
def test_service_catalog_handoff_and_event_boundary_are_declared(self) -> None:
|
||||
manifest = load_extension_package()["manifest"]
|
||||
self.assertIn("capability.operations.service-catalog", manifest["provides"])
|
||||
self.assertEqual(manifest["events_emitted"], ["ops.endpoint.verified"])
|
||||
self.assertEqual(manifest["events_consumed"], ["hub.progress.recorded"])
|
||||
self.assertIn("port.events.interaction", manifest["consumes"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
@ -4,11 +4,25 @@ type: workplan
|
|||
title: "Extension contract alignment"
|
||||
domain: infotech
|
||||
repo: ops-hub
|
||||
status: proposed
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: infotech
|
||||
created: "2026-08-09"
|
||||
updated: "2026-08-09"
|
||||
updated: "2026-08-31"
|
||||
reviewed_at: "2026-08-31"
|
||||
reviewed_by: codex
|
||||
reviewed_against_commit: "9bbbf2032daf257d786446217bad79dc051a529c"
|
||||
quality_dor: DoR-Ok
|
||||
quality_dor_at: "2026-08-31"
|
||||
quality_dor_by: codex
|
||||
quality_dod: DoD-Ok
|
||||
quality_dod_at: "2026-08-31"
|
||||
quality_dod_by: codex
|
||||
quality_dod_note: >-
|
||||
The owner-side 0.1.0 package is versioned and federated, passes hub-core's
|
||||
schemas/catalog and all eight implemented conformance checks, and the
|
||||
post-cutover production compatibility gate is green. Unimplemented framework
|
||||
checks are live residual HUB-WP-0009 rather than reported as passing.
|
||||
parent_project: prj-state-hub-retirement
|
||||
parent_workplan: SHR-WP-0001
|
||||
related:
|
||||
|
|
@ -30,7 +44,7 @@ registration without secrets.
|
|||
|
||||
```task
|
||||
id: OPS-WP-0003-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "a938b0e2-912e-5126-aa6f-d63bdeebca5d"
|
||||
```
|
||||
|
|
@ -39,22 +53,50 @@ Produce hub descriptor + capability manifest referencing
|
|||
`capability.infotech.ops-hub` (or current registry id), events emitted/consumed,
|
||||
and non-secret endpoints.
|
||||
|
||||
**Completed 2026-08-31.** Published the owner-side
|
||||
`helixforge.hub-extension` 0.1.0 package at
|
||||
`registry/hub-extension/v0.1.0/ops-hub.extension.json`. It uses the current
|
||||
hub-core fixture identity `capability.operations.ops-hub`, declares the
|
||||
operations service-catalog handoff, named framework ports, emitted
|
||||
`ops.endpoint.verified`, consumed `hub.progress.recorded`, and non-secret
|
||||
discovery/documentation endpoints. The package checksum is
|
||||
`d48bb22c88598af482a3a779b930d1269e5bd8fd9e245681e278f14c4bd064d0`.
|
||||
|
||||
The capability is now a reviewed federation entry (`D3 / A1 / C1 / R1`) rather
|
||||
than an empty index. `docs/hub-extension.md` records that hub-core owns the
|
||||
runtime while ops-hub owns operational vocabulary and records; the legacy
|
||||
dash-separated event remains a compatibility spelling, not a rewrite target.
|
||||
|
||||
## Pass Tier 2 conformance against hub-core
|
||||
|
||||
```task
|
||||
id: OPS-WP-0003-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "1d1c422d-892c-5404-8d7f-a1f78c5cd87a"
|
||||
```
|
||||
|
||||
When HUB-WP-0004 harness exists, run C1–C10 checks; fix gaps in ops-hub.
|
||||
|
||||
**Completed 2026-08-31 with framework residuals.** The owner package validates
|
||||
against hub-core's packaged Draft 2020-12 descriptor/manifest schemas and event
|
||||
catalog with zero secret-shaped fields. Injecting that package into a disposable
|
||||
hub-core runtime passed the complete implemented profile: Tier-2 C1, C3, C4,
|
||||
C5, C6, C8 and Tier-3 F2/F3 (8/8).
|
||||
|
||||
Hub Core contract 0.1.0 explicitly does not automate C2, C7, C9, or C10; those
|
||||
cannot be fixed honestly in an extension repository. They are residual-owned by
|
||||
new proposed owner workplan `HUB-WP-0009`, covering registry resolution,
|
||||
raw-port configuration policy, dependency-aware readiness, version negotiation,
|
||||
and any remaining tenant-isolation split. It is registered as State Hub
|
||||
workplan `0d6e94f3-fd15-5f57-af41-60f0b862da5e`. The report does not count
|
||||
unsupported checks as passing.
|
||||
|
||||
## Bootstrap dual-run on consolidated runtime
|
||||
|
||||
```task
|
||||
id: OPS-WP-0003-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "947306d4-8a00-5cd6-8c6d-1f266d45055c"
|
||||
```
|
||||
|
|
@ -62,8 +104,23 @@ state_hub_task_id: "947306d4-8a00-5cd6-8c6d-1f266d45055c"
|
|||
Verify bootstrap/smokes against hub-core-hosted `/api/v2` during CORE-WP-0010
|
||||
dual-run and after cutover.
|
||||
|
||||
**Completed 2026-08-31.** `RAPPCOREHUB-WP-0002-T04` records the ops-hub
|
||||
consumer gate passing against the public candidate during the S1–S5 dual-run.
|
||||
The production cutover and exercised rollback/forward recovery repeated the
|
||||
consumer gates. A fresh post-cutover `make interhub-gate` now passes against
|
||||
`https://hub.coulomb.social`: `/api/v2/hubs` fails closed with 401 for an
|
||||
anonymous caller, `/api/v2/openapi.json` returns 200, and all four required
|
||||
bootstrap resources are present.
|
||||
|
||||
No new authenticated bootstrap mutation was needed or performed. The routed
|
||||
Inter-Hub operator-key lane is high-risk and attended
|
||||
(`inter-hub-bootstrap-ssh`); repeating a successful bootstrap merely to obtain
|
||||
fresh evidence would create credentials and production records without a
|
||||
functional change. Existing idempotent bootstrap behavior remains covered by
|
||||
the source tests, while the current production check is non-mutating.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Hub package artefacts present and versioned
|
||||
- [ ] Tier 2 conformance green (or residual-owned gaps)
|
||||
- [ ] Bootstrap works on consolidated runtime
|
||||
- [x] Hub package artefacts present and versioned
|
||||
- [x] Tier 2 conformance green (or residual-owned gaps)
|
||||
- [x] Bootstrap works on consolidated runtime
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue