/** Header name (case-insensitive). */
export declare const SIGNATURE_HEADER = "x-aiwg-signature";
/** Five minutes — RFC 8941 timestamp tolerance. */
export declare const DEFAULT_TIMESTAMP_TOLERANCE_SECONDS = 300;
/** Idempotency cache size. Old entries are evicted FIFO. */
export declare const DEFAULT_IDEMPOTENCY_CAPACITY = 4096;
export interface VerifyResult {
    ok: boolean;
    /** When `ok=false`, a stable machine code suitable for ProblemDetails. */
    code?: 'signature_missing' | 'signature_malformed' | 'signature_mismatch' | 'timestamp_skew' | 'secret_unknown';
    /** Human-readable detail. */
    detail?: string;
    /** Parsed timestamp on success (epoch seconds). */
    timestamp?: number;
}
export interface VerifyOptions {
    /**
     * Function to look up the per-config secret given the `configId` query
     * parameter on the webhook URL. Returning `null` causes a
     * `secret_unknown` failure.
     *
     * Implementations should fetch from a per-mission store populated when
     * createPushNotificationConfig is called.
     */
    lookupSecret: (configId: string) => string | null | Promise<string | null>;
    /** Skew tolerance in seconds. Defaults to 5 minutes. */
    toleranceSeconds?: number;
    /** Clock override for testing. */
    now?: () => number;
}
/**
 * Verify the `X-AIWG-Signature` header against the raw request body.
 *
 * Stripe-style format:
 *   `X-AIWG-Signature: t=1700000000,v1=<hex hmac-sha256>`
 *
 * The HMAC is computed over `t=<timestamp>.<raw body>` (the timestamp and
 * a literal dot, prepended to the body). Multiple `v1=` entries may be
 * present in a single header (during a key rotation); any match accepts.
 */
export declare function verifyWebhookSignature(signature: string | undefined, body: Buffer, configId: string, opts: VerifyOptions): Promise<VerifyResult>;
/** Parse `t=...,v1=...,(v1=...,)*` into structured fields. */
export declare function parseSignatureHeader(header: string): {
    timestamp: number;
    v1: string[];
} | null;
/**
 * Bounded FIFO event-id deduper. The first call with a given id returns
 * true; subsequent calls return false until the id falls out of the
 * window.
 *
 * Subscribers MUST dedupe per the spec — the executor's delivery worker
 * retries on non-2xx responses, so a flaky downstream handler will
 * receive the same event-id multiple times.
 */
export declare class IdempotencyCache {
    private readonly capacity;
    private readonly seen;
    private readonly order;
    constructor(capacity?: number);
    /** Returns true if `id` is new (and stores it); false if it's a duplicate. */
    markFresh(id: string): boolean;
    size(): number;
}
/**
 * Maps `configId` to its symmetric HMAC secret + optional mission
 * routing metadata. Populated when the AIWG client calls
 * createPushNotificationConfig on mission start; torn down on mission
 * complete.
 *
 * Threading model: single-process in-memory map. For multi-replica
 * deployments swap in a shared backing store (Redis, etc.) with the
 * same interface.
 */
export interface PushSecretRegistryEntry {
    configId: string;
    secret: string;
    /** Mission this config belongs to — used for routing webhook payloads. */
    missionId?: string;
    /** Task this config belongs to — used to scope StreamEvent application. */
    taskId?: string;
    /** Free-form metadata returned alongside the entry on lookup. */
    metadata?: Record<string, unknown>;
}
export declare class PushSecretRegistry {
    private readonly entries;
    register(entry: PushSecretRegistryEntry): void;
    lookup(configId: string): PushSecretRegistryEntry | null;
    unregister(configId: string): boolean;
    /** Test/debug helper. */
    size(): number;
}
export interface WebhookHandlerOptions {
    registry: PushSecretRegistry;
    idempotency: IdempotencyCache;
    /**
     * Route the verified, deduplicated StreamEvent into the mission state
     * machine. Implementations typically forward to the same event handler
     * SSE uses.
     */
    route: (entry: PushSecretRegistryEntry, event: unknown) => void | Promise<void>;
    toleranceSeconds?: number;
    now?: () => number;
}
export interface HandleResult {
    status: number;
    /** ProblemDetails-shape body for non-2xx; minimal `{ ok: true }` for 2xx. */
    body: Record<string, unknown>;
}
/**
 * One-shot processor: takes a raw webhook request (configId + body +
 * signature header + event-id header), verifies, dedupes, routes.
 *
 * Returns the status code and body the HTTP layer should emit. The HTTP
 * adapter (serve.ts) is responsible for reading the raw body bytes
 * BEFORE any JSON parsing — the signature is computed over raw bytes
 * (whitespace-sensitive).
 */
export declare function handleWebhook(configId: string, body: Buffer, signature: string | undefined, eventId: string | undefined, opts: WebhookHandlerOptions): Promise<HandleResult>;
//# sourceMappingURL=webhook.d.ts.map