import { Schema } from 'effect'; import { WorkflowRunHandle } from '@voltro/protocol'; declare interface CustomSignatureScheme { readonly _tag: 'custom'; readonly header: string; readonly sign: (rawBody: Uint8Array, secret: string) => string; /** Verify against the request's FULL header map (lowercased keys) — a scheme * whose timestamp lives in a second header can read it here instead of * relying on a caller to splice the two values together. */ readonly verify: (rawBody: Uint8Array, secret: string, headers: Readonly>) => boolean; } export declare const genericProvider: (options?: { readonly signatureHeader?: string; readonly replayWindowSeconds?: number; }) => WebhookProviderDescriptor; export declare const githubProvider: () => WebhookProviderDescriptor; declare type HmacAlgorithm = 'hmacSha256' | 'hmacSha1'; declare interface HmacSignatureScheme { readonly _tag: 'hmac'; readonly algorithm: HmacAlgorithm; /** Header the signature is written to / read from. Stripe uses * `Stripe-Signature`, GitHub uses `X-Hub-Signature-256`, generic * apps use `X-Webhook-Signature`. */ readonly header: string; /** When `true`, the signed payload is `.` and * the header carries both (`t=...,v1=...`). Recipients reject * signatures whose `t` is older than `replayWindowSeconds`. */ readonly includeTimestamp: boolean; /** Window during which a signed payload is replay-safe. Older * signatures get rejected. Default 5 minutes. Only relevant when * `includeTimestamp: true`. */ readonly replayWindowSeconds?: number; /** Encoding the signature is rendered in. `hex` is most common; * Slack uses `hex` prefixed with `v0=`. */ readonly encoding?: 'hex' | 'base64'; /** Prefix written before the hex/base64 signature in the header * value. Slack: `v0=`. GitHub: `sha256=`. Default empty. */ readonly versionPrefix?: string; /** Optional secret-rotation hint: a SECOND secret accepted during * verification but never used for outgoing signing. Lets you * rotate the primary secret without breaking in-flight inbound * deliveries. */ readonly previousSecret?: string; } declare interface IncomingWebhookContext { /** The fully-decoded body, validated against `payload`. */ readonly body: Body; /** Raw bytes — needed to recompute signatures. Already used by * the framework's middleware to verify the inbound signature * before this handler runs; passed along in case the user wants * to compute additional MAC's for downstream relays. */ readonly rawBody: Uint8Array; /** Request headers (lowercased keys). The middleware has already * consumed signature / idempotency headers. */ readonly headers: Readonly>; /** Idempotency key extracted by the middleware (provider-specific * extraction — Stripe uses `Stripe-Signature`'s `t=`, GitHub uses * `X-GitHub-Delivery`, generic uses `Idempotency-Key`). */ readonly idempotencyKey: string; /** Workflow facade supplied by the framework runtime. Use this for * verified external incoming calls that should start or signal * durable workflows after signature + idempotency checks pass. */ readonly workflows?: IncomingWorkflowFacade; } declare interface IncomingWebhookDescriptor { readonly _tag: 'incomingWebhook'; readonly id: WebhookId; /** URL path the framework mounts the route on. Defaults to * `/webhooks/` if absent. Use a custom path for legacy * integrations (`/integrations/stripe/v1`). */ readonly path?: `/${string}`; /** Signature scheme used to authenticate inbound requests. Reject * on mismatch with a 401. Usually filled in by `provider`. */ readonly signature?: SignatureScheme; /** * How this endpoint authenticates its caller. An incoming webhook is a * PUBLIC POST that runs your application code, so the framework will not * mount one that has made no decision here: `mountIncomingWebhook` throws * at boot when there is no effective `signature` (from this descriptor or * from `provider`) AND no explicit value below. * * - omitted → derived. `signature` / `provider` present → `'signature'`; * nothing present → boot refuses. * - `'provider'` → the handler verifies with the provider's own SDK * (Stripe's `constructEvent`, etc.). The framework's generic HMAC layer * is not the authority and does not require a framework-side secret. * - `'none'` → deliberately unverified, because a gateway + IP allow-list * owns the trust boundary. Logged as a warning at every boot, on purpose. */ readonly verification?: 'signature' | 'provider' | 'none'; /** Idempotency key extraction. Default `'Idempotency-Key'` header. * Provider-templates override this to match the provider's wire * format. */ readonly idempotency?: { /** Header name OR a function that reads from headers/body. */ readonly from: string | ((headers: Readonly>, rawBody: Uint8Array) => string | undefined); /** TTL the framework retains the key for de-dup. Default 7 days. */ readonly ttl?: '5m' | '1h' | '6h' | '1d' | '7d' | '30d'; }; /** Validated body schema. The framework decodes the request body * AFTER signature verification, BEFORE the handler runs. */ readonly payload: Schema.Schema; /** Body parser — JSON by default. Stripe / GitHub send `application/json` * but other providers use `application/x-www-form-urlencoded`. */ readonly bodyType?: 'json' | 'form' | 'raw'; /** Provider preset — when set, the framework fills `signature` + * `idempotency` + `bodyType` from the provider's known shape. * Explicit fields above always win. */ readonly provider?: WebhookProviderDescriptor; /** Typed handler. Returns void on success, throws to reject. The * HTTP status is 2xx for success, 4xx for typed validation * errors, 5xx for handler exceptions. The framework retries * 5xx-classified failures via the provider's expected behavior * (most providers retry their own POST on 5xx). */ readonly handler: (context: IncomingWebhookContext) => Promise | void; } declare interface IncomingWorkflowFacade { start(workflowName: string, payload: Payload): Promise; signal(target: { readonly id?: string; readonly executionId?: string; readonly workflowName?: string; }, signalName: string, payload?: unknown): Promise<{ readonly eventId: string; }>; update(target: { readonly id?: string; readonly executionId?: string; readonly workflowName?: string; }, updateName: string, payload?: unknown, options?: { readonly timeoutMs?: number; readonly pollIntervalMs?: number; }): Promise<{ readonly eventId: string; readonly updateId: string; readonly completedEventId: string; readonly result: unknown; }>; } declare type SignatureScheme = HmacSignatureScheme | CustomSignatureScheme | StandardWebhooksSignatureScheme; export declare const slackProvider: () => WebhookProviderDescriptor; /** * The Standard Webhooks (v1.0.0) scheme — three `webhook-*` headers, a * `msg_id.timestamp.payload` signed content, base64 `v1,` signatures, a * `whsec_`-prefixed base64 key. Implemented in `./standardWebhooks`, which * carries the spec quotations and the interop vector it is verified against. */ declare interface StandardWebhooksSignatureScheme { readonly _tag: 'standardWebhooks'; /** Replay tolerance. The spec REQUIRES a tolerance and names no number; 300s * is ours. */ readonly toleranceSeconds?: number; /** A second key accepted on verify AND signed alongside on send — the spec's * zero-downtime rotation, which is what the space-delimited signature list * exists for. */ readonly previousSecret?: string; } export declare const stripeProvider: (options?: { /** Override the replay window. Stripe's default is 5 min; * callers running tests sometimes want a wider window. */ readonly replayWindowSeconds?: number; }) => WebhookProviderDescriptor; /** Stable identifier for an event or webhook. Drives the database * primary key, the inspect endpoint URL, the dashboard listing. Use * dotted-camelCase (`order.completed`, `user.signedUp`). */ declare type WebhookId = string; declare interface WebhookProviderDescriptor { readonly _tag: 'webhookProvider'; readonly id: string; /** Display name for the dashboard. */ readonly name: string; readonly signature: SignatureScheme; readonly idempotency: NonNullable['idempotency']>; readonly bodyType: 'json' | 'form' | 'raw'; /** Optional discriminator: provider-specific event-type extraction * (e.g. Stripe's top-level `type` field). Used by typed handlers * to narrow on payload variant. */ readonly eventTypeFrom?: (body: unknown) => string | undefined; } export { }