generated from coulomb/repo-seed
feat(WP-0010): IHF Phase 9 — External API Surface and Consumer SDKs
Some checks failed
Test / test (push) Has been cancelled
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:
@@ -701,3 +701,87 @@ CREATE INDEX hub_capability_manifests_status_idx ON hub_capability_manifests (st
|
||||
-- GAAF: type registries enforced from here (IHUB-WP-0009)
|
||||
-- All new type discriminator columns (widget_type, event_type, category,
|
||||
-- policy_scope) must reference a registry table or carry a CHECK constraint.
|
||||
|
||||
-- IHF Phase 9 — External API Surface and Consumer SDKs (IHUB-WP-0010)
|
||||
|
||||
CREATE TABLE api_consumers (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
hub_capability_manifest_id UUID REFERENCES hub_capability_manifests(id),
|
||||
rate_limit_per_minute INTEGER NOT NULL DEFAULT 60,
|
||||
quota_per_day INTEGER NOT NULL DEFAULT 10000,
|
||||
quota_resets_at TIMESTAMP WITH TIME ZONE NOT NULL
|
||||
DEFAULT (date_trunc('day', NOW() AT TIME ZONE 'UTC') + interval '1 day'),
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX api_consumers_manifest_idx ON api_consumers (hub_capability_manifest_id);
|
||||
|
||||
CREATE TABLE api_keys (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||
api_consumer_id UUID NOT NULL REFERENCES api_consumers(id) ON DELETE CASCADE,
|
||||
key_prefix TEXT NOT NULL,
|
||||
key_hash TEXT NOT NULL,
|
||||
scopes TEXT NOT NULL DEFAULT '',
|
||||
token_type TEXT NOT NULL DEFAULT 'static'
|
||||
CHECK (token_type IN ('static', 'oauth')),
|
||||
expires_at TIMESTAMP WITH TIME ZONE,
|
||||
revoked_at TIMESTAMP WITH TIME ZONE,
|
||||
last_used_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX api_keys_prefix_idx ON api_keys (key_prefix);
|
||||
CREATE INDEX api_keys_consumer_idx ON api_keys (api_consumer_id);
|
||||
CREATE INDEX api_keys_hash_idx ON api_keys (key_hash);
|
||||
|
||||
CREATE TABLE webhook_subscriptions (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||
api_consumer_id UUID NOT NULL REFERENCES api_consumers(id) ON DELETE CASCADE,
|
||||
event_type TEXT NOT NULL CHECK (event_type IN (
|
||||
'interaction_event.created',
|
||||
'annotation.created',
|
||||
'requirement_candidate.created',
|
||||
'decision_record.created',
|
||||
'deployment_record.created',
|
||||
'outcome_signal.created'
|
||||
)),
|
||||
target_url TEXT NOT NULL,
|
||||
secret TEXT NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX webhook_subs_consumer_idx ON webhook_subscriptions (api_consumer_id);
|
||||
CREATE INDEX webhook_subs_event_type_idx ON webhook_subscriptions (event_type);
|
||||
|
||||
CREATE TABLE webhook_deliveries (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||
webhook_subscription_id UUID NOT NULL REFERENCES webhook_subscriptions(id),
|
||||
payload JSONB NOT NULL,
|
||||
attempted_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
status TEXT NOT NULL CHECK (status IN ('pending', 'delivered', 'failed')),
|
||||
response_code INTEGER,
|
||||
latency_ms INTEGER,
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX webhook_deliveries_sub_idx
|
||||
ON webhook_deliveries (webhook_subscription_id, attempted_at DESC);
|
||||
|
||||
CREATE TABLE api_request_log (
|
||||
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL,
|
||||
api_consumer_id UUID REFERENCES api_consumers(id),
|
||||
endpoint TEXT NOT NULL,
|
||||
method TEXT NOT NULL,
|
||||
status_code INTEGER NOT NULL,
|
||||
latency_ms INTEGER,
|
||||
requested_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX api_request_log_consumer_time_idx
|
||||
ON api_request_log (api_consumer_id, requested_at DESC);
|
||||
|
||||
Reference in New Issue
Block a user