feat(phase-3): workstream 5 — Webhooks & Event Streaming

- DB migrations 016/017: webhook_subscriptions and webhook_deliveries tables
- WebhookService: CRUD for subscriptions, Vault-backed secret storage, delivery history
- WebhookDeliveryWorker: Bull queue, HMAC-SHA256 signatures, exponential backoff,
  SSRF protection (RFC 1918 + loopback + link-local rejection), dead-letter handling
- EventPublisher: publishes 10 event types (agent/credential/token lifecycle);
  optional Kafka adapter activated via KAFKA_BROKERS env var
- AgentService, CredentialService, OAuth2Service: wired to EventPublisher
- WebhookController + routes: 6 endpoints with webhooks:read / webhooks:write scope guards
- KafkaAdapter: optional Kafka producer (kafkajs), no-op when KAFKA_BROKERS unset
- OAuthScope extended: webhooks:read, webhooks:write
- AuditAction extended: webhook.created, webhook.updated, webhook.deleted
- Metrics: agentidp_webhook_dead_letters_total counter added to registry
- 523 unit tests passing; TypeScript strict throughout, zero `any`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SentryAgent.ai Developer
2026-03-31 00:07:41 +00:00
parent 03b5de300c
commit 272b69f18d
20 changed files with 1994 additions and 25 deletions

View File

@@ -28,7 +28,14 @@ export type DeploymentEnv = 'development' | 'staging' | 'production';
export type CredentialStatus = 'active' | 'revoked';
/** OAuth 2.0 scope values supported by this IdP. */
export type OAuthScope = 'agents:read' | 'agents:write' | 'tokens:read' | 'audit:read' | 'admin:orgs';
export type OAuthScope =
| 'agents:read'
| 'agents:write'
| 'tokens:read'
| 'audit:read'
| 'admin:orgs'
| 'webhooks:read'
| 'webhooks:write';
/** Audit action identifiers for all significant platform events. */
export type AuditAction =
@@ -47,7 +54,10 @@ export type AuditAction =
| 'org.created'
| 'org.updated'
| 'org.deleted'
| 'org.member_added';
| 'org.member_added'
| 'webhook.created'
| 'webhook.updated'
| 'webhook.deleted';
/** Outcome of an audited action. */
export type AuditOutcome = 'success' | 'failure';

94
src/types/webhook.ts Normal file
View File

@@ -0,0 +1,94 @@
/**
* Shared TypeScript interfaces and types for the Webhook / Event Streaming subsystem.
* Imported by WebhookService, WebhookDeliveryWorker, EventPublisher, and their controllers.
*/
// ============================================================================
// Event Types
// ============================================================================
/** All event type identifiers that can be published and subscribed to. */
export type WebhookEventType =
| 'agent.created'
| 'agent.updated'
| 'agent.suspended'
| 'agent.reactivated'
| 'agent.decommissioned'
| 'credential.generated'
| 'credential.rotated'
| 'credential.revoked'
| 'token.issued'
| 'token.revoked';
// ============================================================================
// Webhook Subscription
// ============================================================================
/** A registered webhook subscription (secret fields are never included). */
export interface IWebhookSubscription {
id: string;
organization_id: string;
name: string;
url: string;
events: WebhookEventType[];
active: boolean;
failure_count: number;
created_at: Date;
updated_at: Date;
}
/** Request body for creating a new webhook subscription. */
export interface ICreateWebhookRequest {
name: string;
url: string;
events: WebhookEventType[];
}
/** Request body for partially updating a webhook subscription. */
export interface IUpdateWebhookRequest {
name?: string;
url?: string;
events?: WebhookEventType[];
active?: boolean;
}
// ============================================================================
// Webhook Delivery
// ============================================================================
/** A single webhook delivery attempt record. */
export interface IWebhookDelivery {
id: string;
subscription_id: string;
event_type: WebhookEventType;
payload: Record<string, unknown>;
status: 'pending' | 'delivered' | 'failed' | 'dead_letter';
http_status_code: number | null;
attempt_count: number;
next_retry_at: Date | null;
delivered_at: Date | null;
created_at: Date;
updated_at: Date;
}
/** The envelope sent in every outbound webhook HTTP request body. */
export interface IWebhookPayload {
/** UUID uniquely identifying this event occurrence. */
id: string;
/** The event type identifier. */
event: WebhookEventType;
/** ISO 8601 timestamp of when the event was published. */
timestamp: string;
/** The organization that owns the resource that changed. */
organization_id: string;
/** Event-specific data. Contents vary by event type. */
data: Record<string, unknown>;
}
/** Paginated response for listing webhook deliveries. */
export interface IPaginatedDeliveriesResponse {
deliveries: IWebhookDelivery[];
total: number;
limit: number;
offset: number;
}