Files
inter-hub/docs/new-hub-quickstart.md

12 KiB
Raw Blame History

New Domain Hub — Quickstart Guide

Audience: A developer starting a new domain hub (dev-hub, ops-hub, fin-hub, etc.) that will live in its own repository and use inter-hub as the governance substrate.

Current state: inter-hub v0.2.0-alpha.1 exposes its supported integration surface under /api/v2. The examples below use $IHUB_BASE; point it at the environment you are bootstrapping against.


Two Patterns — Choose One

Pattern A: API Consumer Hub (any language, start today)

Your hub is a standalone application that talks to inter-hub via REST API. No Haskell required. Full framework services available from day one.

Best for: Hubs that already have a tech stack (Node, Python, Go, etc.), prototypes, or teams that want zero build overhead.

Pattern B: IHP Extension Hub (Haskell, shares build infra)

Your hub is a separate IHP project that runs alongside inter-hub, sharing the same Nix/GHC installation on haskelseed and optionally the same PostgreSQL cluster (different schema or database).

Best for: Hubs that need server-rendered UI, deep governance integration, or type-safe access to inter-hub's data model.


Pattern A — API Consumer Hub

1. Start with an operator API key

Every write call below requires Authorization: Bearer <key>. Use an existing operator/admin API key for the first bootstrap call. New hub-specific keys can then be created through the API and should replace the operator key for normal runtime traffic.

export IHUB_BASE="http://127.0.0.1:8000"
export IHUB_OPERATOR_KEY="<existing-operator-api-key>"

2. Register the VSM Operations hub

curl -s -X POST "$IHUB_BASE/api/v2/hubs" \
  -H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Operations Hub",
    "slug": "ops-hub",
    "domain": "operations",
    "hubKind": "domain",
    "hubFamily": "vsm",
    "vsmFunction": "operations",
    "vsmSystem": "1"
  }'

Save the returned id — this is your hubId for all subsequent calls.

3. Register and activate the ops-hub manifest

curl -s -X POST "$IHUB_BASE/api/v2/hub-capability-manifests" \
  -H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "hubId": "<ops-hub-id>",
    "manifestVersion": "1.0",
    "declaredWidgetTypes": ["ops-endpoint-card"],
    "declaredEventTypes": ["ops-endpoint-verified"],
    "declaredAnnotationCategories": ["ops-risk"],
    "declaredPolicyScopes": ["ops-internal"],
    "capabilityDescription": "Operations inventory and endpoint verification",
    "contact": "ops@example.com"
  }'

Then activate the returned manifest:

curl -s -X POST "$IHUB_BASE/api/v2/hub-capability-manifests/<manifest-id>/activate" \
  -H "Authorization: Bearer $IHUB_OPERATOR_KEY"

Activation registers the declared vocabulary. Domain-owned widget types, event types, annotation categories, and policy scopes must be declared here before use.

4. Create an ops-hub API consumer and key

curl -s -X POST "$IHUB_BASE/api/v2/api-consumers" \
  -H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ops-hub",
    "description": "Operations hub runtime client",
    "hubCapabilityManifestId": "<active-manifest-id>",
    "rateLimitPerMinute": 120,
    "quotaPerDay": 50000
  }'

Create the static key for the returned consumer:

curl -s -X POST "$IHUB_BASE/api/v2/api-consumers/<api-consumer-id>/api-keys" \
  -H "Authorization: Bearer $IHUB_OPERATOR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scopes": "ops:write"}'

The response contains fullKey exactly once. Store it in the hub runtime secret store and use it for all following calls:

export OPS_HUB_KEY="<fullKey-from-create-api-key-response>"

5. Register widgets

curl -s -X POST "$IHUB_BASE/api/v2/widgets" \
  -H "Authorization: Bearer $OPS_HUB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CoulombCore Gitea Registry",
    "widgetType": "ops-endpoint-card",
    "hubId": "<ops-hub-id>",
    "viewContext": "operations-inventory",
    "policyScope": "ops-internal"
  }'

6. Record interaction events

curl -s -X POST "$IHUB_BASE/api/v2/interaction-events" \
  -H "Authorization: Bearer $OPS_HUB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "widgetId": "<widget-id>",
    "eventType": "ops-endpoint-verified",
    "viewContext": "registry-readiness",
    "metadata": {
      "service": "gitea",
      "endpoint": "https://gitea.coulomb.social/v2/",
      "result": "auth-challenge-ok"
    }
  }'

7. Verify the bootstrap

curl -s "$IHUB_BASE/api/v2/interaction-events?widgetId=<widget-id>&eventType=ops-endpoint-verified" \
  -H "Authorization: Bearer $OPS_HUB_KEY"

The event should appear with the submitted metadata. If the API returns event_type_not_in_manifest, check that the API consumer is bound to the active ops-hub manifest and that the event type was declared before activation.

8. What you get for free

Once events are flowing, the inter-hub framework automatically provides:

  • Annotation collection on any widget
  • Requirement candidate escalation from annotations
  • Triage queue and governance lifecycle (Requirement → Decision → Deployment)
  • AI-assisted requirement drafting (if AgentRegistration is configured)
  • Outcome signals and regression detection
  • Widget marketplace discovery

Your hub only needs to register its vocabulary, seed meaningful widgets, and POST events. Everything downstream is managed by inter-hub.


Pattern B — IHP Extension Hub (Haskell)

Prerequisites

The same build infrastructure used for inter-hub works directly:

  • haskelseed VM (192.168.178.135) with GHC 9.10.3 in the Nix store
  • devenv for reproducible environments
  • The painful one-time Nix setup is already done — a new IHP project reuses the same Nix store

Bootstrap a new hub repo

# On your workstation (Nix must be installed)
nix profile install nixpkgs#ihp-new
ihp-new dev-hub
cd dev-hub

# Edit devenv.nix to pin to the same IHP version as inter-hub (1.5.0)
# Then:
devenv up

The first devenv up on a fresh machine takes 2040 min to fetch Nix dependencies. On haskelseed, most dependencies are already in the Nix store and the setup takes ~2 minutes.

Connect to inter-hub's API

Add the inter-hub API client to your hub. The simplest approach:

-- Application/Helper/InterHubClient.hs
module Application.Helper.InterHubClient where

import IHP.Prelude
import Network.HTTP.Simple

postEvent :: Text -> Text -> Text -> Value -> IO ()
postEvent apiKey widgetId eventType metadata = do
    let req = setRequestMethod "POST"
            $ setRequestHeader "Authorization" ["Bearer " <> cs apiKey]
            $ setRequestHeader "Content-Type" ["application/json"]
            $ setRequestBodyJSON (object
                [ "widgetId"  .= widgetId
                , "eventType" .= eventType
                , "metadata"  .= metadata
                ])
            $ parseRequest_ "http://127.0.0.1:8000/api/v2/interaction-events"
    void $ httpLBS req

Shared database (optional)

If your hub needs read access to inter-hub's tables (e.g., to join against requirements or decision_records), connect to the same PostgreSQL:

# In your hub's .env:
DATABASE_URL=postgresql://ihp:ihp@192.168.178.135/interhub

Your IHP app can then use query @DecisionRecord directly without going through the API. This is appropriate for tightly-coupled hubs that are part of the same operational boundary.

For loosely-coupled hubs (separate teams, separate deploy cadence), use the API only — do not share the database.

How fast is the Haskell build for a new hub?

A fresh IHP project with 10 controllers and 20 views compiles to ~150 modules (vs inter-hub's 616). With the Nix store already populated on haskelseed:

Stage Time
First devenv up (Nix fetch) ~2 min (store populated)
First GHCi load (150 modules) ~35 min
Incremental reload (1 module changed) ~515 s
Adding a new controller+view pair ~1030 s compile time

This is practical for active development. The painful build experience with inter-hub was caused by its scale (616 modules, 12 phases worth of code) and the Alpine setup being done from scratch. A new hub starts small.


Honest Assessment: Is IHP a Good Framework for Domain Hubs?

Yes, with caveats.

Strengths:

  • Type safety catches integration errors at compile time, not at 2am
  • Server-rendered HSX views are fast to write once you know IHP conventions
  • The query builder and auto-generated types eliminate a whole class of SQL bugs
  • IHP's code generator scaffolds a controller+4 views in seconds
  • Once the Nix environment is set up, it is reproducible — no "works on my machine"

Caveats:

  • The initial Nix setup is still painful on a new machine (~1h)
  • GHC error messages for type inference failures are dense
  • No hot-reload for Haskell (GHCi restart is fast, but not instant)
  • The hub-core shared library is planned but not yet implemented — each new hub currently duplicates boilerplate for API client setup, hub registration, and event posting

Bottom line: If you are already comfortable with Haskell and IHP, building domain hubs in the same stack is efficient and the type safety pays dividends quickly. If your team is not Haskell-native, Pattern A (API consumer) is the pragmatic choice — the API surface is stable and well-documented, and you can add a lightweight web layer in whatever language fits your team.


What hub-core Would Provide

The planned hub-core Haskell library (not yet implemented) would give every domain hub:

  • HubRegistration typeclass — register with inter-hub on startup
  • WidgetEnvelope helpers — consistent widget wrapping across hubs
  • InterHubClient — typed API client with retry and auth built in
  • HubCapabilityManifest bootstrap — auto-activate manifest on startup (planned; use the API recipe above today)
  • Shared defaultLayout with inter-hub navigation integration

Until hub-core exists, copy the client helper above and the 3-step registration pattern into your new hub. It is ~50 lines of boilerplate.


Checklist for a New Hub

  • Start with an existing operator API key
  • Create ApiConsumer + ApiKey through /api/v2/api-consumers
  • Record your hub ID and API key in the new hub's .env
  • Register HubCapabilityManifest with domain type vocabulary through /api/v2/hub-capability-manifests
  • Activate the manifest through /api/v2/hub-capability-manifests/<id>/activate
  • Create at least one Widget per meaningful UI surface
  • Instrument interactions with POST to /api/v2/interaction-events
  • Verify events appear in inter-hub at /InteractionEvents
  • (Optional) Configure AgentRegistration and ModelRoutingPolicy for AI-assisted requirement drafting
  • (Optional) Set up HubRoutingRules to route annotations to your hub's triage queue

Reference

Resource Location
API reference (OpenAPI) $IHUB_BASE/api/v2/openapi.json
Swagger UI $IHUB_BASE/api/v2/docs
Type registry browser $IHUB_BASE/TypeRegistries/WidgetTypes
Domain hub extension guide docs/domain-hub-extension-guide.md
IHP data and queries docs/ihp-data-and-queries.md
IHP controllers and views docs/ihp-controllers-views-forms.md
Functional module maturity docs/functional-modules.md
IHF v0.2 specification specs/InteractionHubFrameworkSpecification_v0.2.md