# 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 `. 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. ```bash export IHUB_BASE="http://127.0.0.1:8000" export IHUB_OPERATOR_KEY="" ``` ### 2. Register the VSM Operations hub ```bash 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 ```bash curl -s -X POST "$IHUB_BASE/api/v2/hub-capability-manifests" \ -H "Authorization: Bearer $IHUB_OPERATOR_KEY" \ -H "Content-Type: application/json" \ -d '{ "hubId": "", "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: ```bash curl -s -X POST "$IHUB_BASE/api/v2/hub-capability-manifests//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 ```bash 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": "", "rateLimitPerMinute": 120, "quotaPerDay": 50000 }' ``` Create the static key for the returned consumer: ```bash curl -s -X POST "$IHUB_BASE/api/v2/api-consumers//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: ```bash export OPS_HUB_KEY="" ``` ### 5. Register widgets ```bash 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": "", "viewContext": "operations-inventory", "policyScope": "ops-internal" }' ``` ### 6. Record interaction events ```bash curl -s -X POST "$IHUB_BASE/api/v2/interaction-events" \ -H "Authorization: Bearer $OPS_HUB_KEY" \ -H "Content-Type: application/json" \ -d '{ "widgetId": "", "eventType": "ops-endpoint-verified", "viewContext": "registry-readiness", "metadata": { "service": "gitea", "endpoint": "https://gitea.coulomb.social/v2/", "result": "auth-challenge-ok" } }' ``` ### 7. Verify the bootstrap ```bash curl -s "$IHUB_BASE/api/v2/interaction-events?widgetId=&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. The same path is available as a smoke script: ```bash IHUB_BASE="$IHUB_BASE" IHUB_OPERATOR_KEY="$IHUB_OPERATOR_KEY" \ scripts/ops-hub-bootstrap-smoke.py ``` ### 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`) as the CI/Nix build runner 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 when built on the runner ### Bootstrap a new hub repo ```bash # 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 20–40 min to fetch Nix dependencies. On haskelseed, most dependencies are already in the Nix store, which is why it is useful as a build runner. It is not the production runtime host for inter-hub. ### Connect to inter-hub's API Add the inter-hub API client to your hub. The simplest approach: ```haskell -- 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) Production inter-hub runs on Railiance01 K3s and uses PostgreSQL inside the Railiance cluster. Do not connect new hubs to a haskelseed database. Prefer the API boundary for extension hubs; request a governed read model or dedicated service account if a hub truly needs database-level integration. ### 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) | ~3–5 min | | Incremental reload (1 module changed) | ~5–15 s | | Adding a new controller+view pair | ~10–30 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//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` - [ ] Run `scripts/ops-hub-bootstrap-smoke.py` against a disposable or staging environment before adapting the recipe for another VSM hub - [ ] (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` |