Record ops-hub bootstrap progress and add API bootstrap helper

Document the 2026-06-15 attended Inter-Hub bootstrap: hub row, active manifest,
widget seeding, and runtime API key creation. Add scripts/ops-hub-bootstrap-api.py,
extend the SQL fallback for widget versions and the first Gitea event, refresh
OpsHubBootstrapRunbook, and inline credential-routing guidance for agents.
This commit is contained in:
2026-06-19 19:09:21 +02:00
parent 3638ee14ad
commit 215e62a221
7 changed files with 1005 additions and 43 deletions

View File

@@ -8,8 +8,9 @@ This runbook gives the operator-ready bootstrap path for `ops-hub`, the VSM
Operations / System 1 extension of Inter-Hub.
Use this when an authenticated Inter-Hub admin session or deployment migration
is available. The current public v2 API is not sufficient to create the hub,
manifest, API consumer, API key, or seed widgets by itself.
is available. The current public v2 API now exposes the supported bootstrap
surface; creation and mutation calls require an Inter-Hub operator/admin API
key.
As of 2026-06-06, implementation work for `ops-hub` belongs in the dedicated
repo at `/home/worsch/ops-hub` with remote `gitea-remote:coulomb/ops-hub.git`.
@@ -25,31 +26,23 @@ implementation repo ports or supersedes it.
## Current Bootstrap Decision
Prefer the supported Inter-Hub bootstrap API once production exposes the
current API surface. Do not proceed with manual DB seeding unless the operator
explicitly chooses that fallback. Until the production API gate passes, the
authenticated Inter-Hub admin UI and SQL migration remain fallback paths for
attended operator use only.
Prefer the supported Inter-Hub bootstrap API for hub, manifest, API consumer,
and runtime key creation. Use the idempotent SQL fallback for seed widgets and
initial evidence only when the live API hits a known server-side bootstrap bug
and the operator explicitly approves that fallback.
Production API gate:
Latest check, 2026-06-14 after Inter-Hub deployment:
- `https://hub.coulomb.social/api/v2/hubs` returns `401` unauthenticated, not
`404`.
- `https://hub.coulomb.social/api/v2/hubs` returns `200` with a public,
paginated hub list.
- OpenAPI lists `/hubs`, `/hub-capability-manifests`, `/api-consumers`, and
`/policy-scopes`.
- After the gate passes, run the supported bootstrap/smoke path from the
relevant `inter-hub` or `ops-hub` tooling with `IHUB_BASE` and an operator
key.
Latest check, 2026-06-14:
- `ops-hub/scripts/interhub-gate-probe.py` still reports the production gate
closed.
- `GET /api/v2/hubs` returns `404`.
- Live OpenAPI still omits `/hubs`, `/hub-capability-manifests`,
`/api-consumers`, and `/policy-scopes`.
- Do not run the preferred API bootstrap path until the current Inter-Hub API
is deployed, unless the operator explicitly chooses the SQL fallback.
- Hub and policy-scope list reads are public. Hub creation, manifest
creation/activation, API consumer/key creation, widget creation, and event
submission require `Authorization: Bearer <key>`.
- The old `ops-hub/scripts/interhub-gate-probe.py` expectation that
unauthenticated `GET /api/v2/hubs` must return `401` is stale. The deployed
contract is public-read/authenticated-write.
VSM classification is stored in the manifest capability description for now:
@@ -61,6 +54,120 @@ Newer Inter-Hub schemas have first-class hub metadata columns for these values.
The SQL fallback sets those columns when present and still carries the same
classification in the manifest description for older deployments.
Latest bootstrap result, 2026-06-15:
- `scripts/ops-hub-bootstrap-api.py` created/reused the `ops-hub` hub row,
active manifest, runtime API consumer, and display-once runtime API key.
- `POST /api/v2/widgets` failed with an Inter-Hub `COUNT(*)` decode error in
type-registry validation.
- The operator-approved SQL fallback seeded 14 widgets, 14 initial widget
versions, and the first Gitea registry readiness event.
- The runtime key can be exchanged through `POST /api/v2/token`, can read all
14 widgets through `GET /api/v2/widgets`, and can read the first event
through `GET /api/v2/interaction-events`.
- `GET /api/v2/hub-registry` currently returns HTTP 500 because Inter-Hub
decodes `COUNT(*)` from `api_request_log` as `Int` while PostgreSQL returns
`bigint`.
## Inter-Hub Operator Key
`IHUB_OPERATOR_KEY` is an existing Inter-Hub API key or short-lived access
token accepted by v2 endpoints as:
```text
Authorization: Bearer <key>
```
It is needed only for privileged bootstrap writes:
- creating or reusing the `ops-hub` hub row;
- creating and activating the capability manifest;
- creating the `ops-hub` runtime `ApiConsumer`;
- minting the display-once runtime API key;
- creating widgets and submitting the first event.
The operator key is not the long-term `ops-hub` runtime credential. It should
be used for the attended bootstrap only, then normal `ops-hub` traffic should
use the narrower runtime key created during bootstrap.
Allowed sources:
1. Retrieve an existing Inter-Hub operator/admin key from the approved secret
store, if one has already been created. After `HF-WP-0002` is deployed,
the intended browser path is `https://bao.coulomb.social` with KeyCape auth
path `netkingdom` and role `platform-admin`. The older `keycape` auth path
may remain available as a compatibility alias.
2. If no such key exists, log in to the Inter-Hub admin UI and create an
operator/bootstrap `ApiConsumer` and API key. The full key is display-once;
store it immediately in the approved secret store.
3. NetKingdom/OpenBao may provide the key only as the secret-custody path once
the key exists and an appropriate policy/path is defined. The
`net-kingdom` Git repository must not contain the key.
`net-kingdom-pg-1` is only the PostgreSQL pod used by the SQL fallback. It is
not by itself the source of an Inter-Hub operator key.
When using the OpenBao browser UI, inspect metadata/path presence first, for
example under:
```text
platform/operators/
platform/operators/inter-hub/
```
Do not copy secret values into Git, State Hub, chat, shell history, or this
runbook. Store display-once Inter-Hub and `ops-hub` runtime keys directly in
the approved secret path.
For an attended local run, enter the key in a trusted shell without echoing it:
```bash
export IHUB_BASE="https://hub.coulomb.social"
read -rsp "Inter-Hub operator key: " IHUB_OPERATOR_KEY
echo
export IHUB_OPERATOR_KEY
```
The preferred HelixForge helper uses the prepared manifest and widget seed
artifacts instead of the smaller one-widget smoke fixture:
```bash
umask 077
IHUB_KEY_FILE=$(mktemp)
# Paste the OpenBao value into this file without printing it to the terminal.
read -rsp "Inter-Hub operator key: " IHUB_OPERATOR_KEY
printf '%s' "$IHUB_OPERATOR_KEY" > "$IHUB_KEY_FILE"
unset IHUB_OPERATOR_KEY
echo
IHUB_BASE="https://hub.coulomb.social" \
IHUB_OPERATOR_KEY_FILE="$IHUB_KEY_FILE" \
python3 scripts/ops-hub-bootstrap-api.py
```
The helper creates/reuses the `ops-hub` hub row, activates the full
HelixForge manifest, creates the `ops-hub` API consumer, creates the runtime
API key if needed, creates any missing seed widgets, and submits the first
Gitea registry readiness event. If it creates a new `ops-hub` runtime API key,
it writes the full key to a 0600 temp file and prints only the path and key
prefix. Store that runtime key immediately in OpenBao, for example:
```text
platform/operators/ops-hub/runtime
field: OPS_HUB_KEY
```
After storing both keys in OpenBao, remove local temp files:
```bash
rm -f "$IHUB_KEY_FILE" "<runtime-key-file-printed-by-helper>"
unset IHUB_KEY_FILE
```
`/home/worsch/inter-hub/scripts/ops-hub-bootstrap-smoke.py` remains useful as
a narrow source-side smoke proof, but it creates a one-widget fixture rather
than the full HF-WP-0001 Operations vocabulary.
As of the 2026-05-19 access check, the workstation kubeconfig only points at
CoulombCore (`92.205.130.254`) and does not include the Railiance01
(`92.205.62.239`) cluster where `hub.coulomb.social` resolves. SSH key access
@@ -118,9 +225,10 @@ kubectl exec -i -n databases net-kingdom-pg-1 -- \
```
The SQL fallback creates the hub, active manifest, registry entries, API
consumer row, and seed widgets. It does not create the one-time visible static
API key; generate that in the authenticated Inter-Hub UI and store it outside
Git.
consumer row, seed widgets, initial widget version rows, and the first Gitea
registry readiness event. It does not create the one-time visible static API
key; generate that through the authenticated Inter-Hub API/UI helper and store
it outside Git.
## Validation
@@ -150,20 +258,24 @@ Expected: a short-lived access token is returned.
After widget seeding:
```bash
curl -s https://hub.coulomb.social/api/v2/hub-registry
curl -s https://hub.coulomb.social/api/v2/widgets
curl -s https://hub.coulomb.social/api/v2/interaction-events
```
Expected: `ops-hub` is visible, and the operator can see the seeded widgets in
the authenticated UI.
Expected: the 14 seeded `ops-hub` widgets are readable, and the Gitea registry
readiness event is visible. `GET /api/v2/hub-registry` is the desired final
registry validation, but it currently returns HTTP 500 on production because
of the `api_request_log` `COUNT(*)` decode issue.
## Known Blockers
- The live public v2 API has no `POST /api/v2/hubs`.
- The live public v2 API has no `POST /api/v2/widgets`.
- There are no v2 endpoints for manifest creation/activation.
- There are no v2 endpoints for API consumer or key creation.
- There is no `/api/v2/policy-scopes`.
- Interaction event create currently does not persist submitted metadata.
- Webhook dispatch currently uses the hard-coded `"clicked"` event type.
- `POST /api/v2/widgets` currently fails on production because Inter-Hub
decodes `COUNT(*)` from `widget_type_registry` as `Int` while PostgreSQL
returns `bigint`.
- `GET /api/v2/hub-registry` currently fails on production for the same bug
class in the `api_request_log` rate-limit query.
- The generated `ops-hub` runtime key must be moved from the local temp file
into OpenBao at `platform/operators/ops-hub/runtime`, field `OPS_HUB_KEY`,
then the temp file must be removed.
These are tracked by HF-WP-0001 T10 for Inter-Hub hardening.