feat(WP-0010): IHF Phase 9 — External API Surface and Consumer SDKs
Some checks failed
Test / test (push) Has been cancelled

Delivers the full Phase 9 external API layer:

- Versioned REST API (/api/v2/) with OpenAPI 3.1 spec; enum arrays for
  widget_type, event_type, annotation category drawn live from registry tables
- OAuth 2.0 client credentials flow (/api/v2/token); hub:*:write scopes
  gated on active HubCapabilityManifest FK
- API key management: SHA256-hashed tokens, key_prefix for display,
  one-time reveal on creation, revocation support
- TypeScript and Python consumer SDKs generated from registry tables
  (/api/v2/sdk/ihf-client.ts, /api/v2/sdk/ihf-client.py)
- Webhook delivery: HMAC-SHA256 signing, append-only webhook_deliveries,
  fire-and-forget dispatch via forkIO, 3-retry logic
- Admin API dashboard with 24h stats (request count, error rate, last seen)
- Rate limiting (per-minute) and daily quota enforcement via api_request_log
- Schema migration: api_consumers, api_keys, webhook_subscriptions (CHECK
  constraint on 6 framework lifecycle topics), webhook_deliveries
  (append-only trigger), api_request_log
- ARCHITECTURE-LAYERS.md scorecard: 3.34 → 3.41 (approaching Strong)
- contracts/functional/interaction-reporting-v1.md extended with Phase 9
  endpoint catalogue and 422 validation error format

GAAF: no bare TEXT discriminators; webhook event_type uses CHECK constraint
over 6 allowed framework lifecycle topic strings (not widget event types).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-01 19:52:20 +00:00
parent 286d33923a
commit 3cac021213
38 changed files with 3581 additions and 17 deletions

View File

@@ -32,6 +32,22 @@ import Web.Controller.ArchiveRecords ()
import Web.Controller.FederatedGovernance ()
import Web.Controller.TypeRegistries ()
import Web.Controller.HubCapabilityManifests ()
-- Phase 9 — External API Surface (IHUB-WP-0010)
import Web.Controller.ApiConsumers ()
import Web.Controller.ApiKeys ()
import Web.Controller.WebhookSubscriptions ()
import Web.Controller.ApiDashboard ()
import Web.Controller.Api.V2.Widgets ()
import Web.Controller.Api.V2.InteractionEvents ()
import Web.Controller.Api.V2.Annotations ()
import Web.Controller.Api.V2.RequirementCandidates ()
import Web.Controller.Api.V2.DecisionRecords ()
import Web.Controller.Api.V2.DeploymentRecords ()
import Web.Controller.Api.V2.OutcomeSignals ()
import Web.Controller.Api.V2.Registries ()
import Web.Controller.Api.V2.OpenApi ()
import Web.Controller.Api.V2.Token ()
import Web.Controller.Api.V2.Sdk ()
import Web.Controller.Sessions ()
instance FrontController WebApplication where
@@ -60,6 +76,23 @@ instance FrontController WebApplication where
, parseRoute @FederatedGovernanceController
, parseRoute @TypeRegistriesController
, parseRoute @HubCapabilityManifestsController
-- Phase 9 — External API Surface (IHUB-WP-0010)
, parseRoute @ApiConsumersController
, parseRoute @ApiKeysController
, parseRoute @WebhookSubscriptionsController
, parseRoute @ApiDashboardController
-- /api/v2/ REST endpoints (registered before /api/v1/ to avoid prefix clash)
, parseRoute @ApiV2WidgetsController
, parseRoute @ApiV2InteractionEventsController
, parseRoute @ApiV2AnnotationsController
, parseRoute @ApiV2RequirementCandidatesController
, parseRoute @ApiV2DecisionRecordsController
, parseRoute @ApiV2DeploymentRecordsController
, parseRoute @ApiV2OutcomeSignalsController
, parseRoute @ApiV2RegistriesController
, parseRoute @ApiV2OpenApiController
, parseRoute @ApiV2TokenController
, parseRoute @ApiV2SdkController
]
instance InitControllerContext WebApplication where
@@ -106,6 +139,8 @@ defaultLayout inner = [hsx|
<a href={ArchiveRecordsAction} class="text-sm text-gray-600 hover:text-gray-900">Archive</a>
<a href={WidgetTypeRegistryAction} class="text-sm text-gray-600 hover:text-gray-900">Registries</a>
<a href={HubCapabilityManifestsAction} class="text-sm text-gray-600 hover:text-gray-900">Extensions</a>
<a href={ApiConsumersAction} class="text-sm text-gray-600 hover:text-gray-900">API</a>
<a href={ShowApiDashboardAction} class="text-sm text-gray-600 hover:text-gray-900">API Dashboard</a>
<div class="ml-auto">
<a href={DeleteSessionAction} class="text-sm text-gray-500 hover:text-gray-700">Sign out</a>
</div>