import type { ID } from '../common/types.js';
import type { EscalationRecord } from './escalation.js';
/**
 * Validate a webhook URL for SSRF protection
 */
export declare function validateWebhookUrl(url: string): Promise<{
    valid: boolean;
    reason?: string;
}>;
/**
 * Validate URL at connection time (DNS resolution check)
 * This performs actual DNS resolution to catch DNS rebinding attacks
 */
export declare function validateWebhookUrlAtRuntime(url: string): Promise<{
    valid: boolean;
    reason?: string;
    resolvedIP?: string;
}>;
/**
 * Header name for the HMAC signature
 * Format: v1=<hmac-sha256-hex>
 */
export declare const SIGNATURE_HEADER = "X-Vorion-Signature";
/**
 * Header name for the signature timestamp (Unix seconds)
 * Used to prevent replay attacks
 */
export declare const SIGNATURE_TIMESTAMP_HEADER = "X-Vorion-Timestamp";
/**
 * Verify webhook signature for incoming requests.
 *
 * This function is exported for use in client SDKs to verify that webhook
 * requests originated from Vorion and haven't been tampered with.
 *
 * ## Security Features
 *
 * 1. **Timestamp Validation**: Rejects requests with timestamps older than
 *    `toleranceSeconds` (default: 5 minutes) to prevent replay attacks.
 *
 * 2. **Timing-Safe Comparison**: Uses constant-time comparison to prevent
 *    timing attacks that could leak information about the expected signature.
 *
 * 3. **Signed Payload**: The signature covers both timestamp and payload,
 *    ensuring neither can be modified independently.
 *
 * ## Usage in Client SDKs
 *
 * ```typescript
 * import { verifyWebhookSignature, SIGNATURE_HEADER, SIGNATURE_TIMESTAMP_HEADER } from '@vorionsys/sdk';
 *
 * app.post('/webhook', (req, res) => {
 *   const signature = req.headers[SIGNATURE_HEADER.toLowerCase()];
 *   const timestamp = parseInt(req.headers[SIGNATURE_TIMESTAMP_HEADER.toLowerCase()], 10);
 *   const payload = JSON.stringify(req.body);
 *
 *   if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET, timestamp)) {
 *     return res.status(401).send('Invalid signature');
 *   }
 *
 *   // Process webhook...
 * });
 * ```
 *
 * @param payload - The raw JSON payload string from the request body
 * @param signature - The signature from the X-Vorion-Signature header
 * @param secret - The webhook secret configured for this endpoint
 * @param timestamp - The timestamp from the X-Vorion-Timestamp header (Unix seconds)
 * @param toleranceSeconds - Maximum age of the request in seconds (default: 300 = 5 minutes)
 * @returns true if the signature is valid and timestamp is within tolerance
 *
 * @throws Never throws - returns false for any invalid input
 */
export declare function verifyWebhookSignature(payload: string, signature: string, secret: string, timestamp: number, toleranceSeconds?: number): boolean;
/**
 * Generate a secure webhook secret.
 *
 * The secret is in the format: whsec_<base64-encoded-random-bytes>
 * This format is similar to Stripe's webhook secrets and provides:
 * - Clear identification as a webhook secret
 * - 256 bits of entropy for cryptographic security
 *
 * @returns A new webhook secret string
 *
 * @example
 * ```typescript
 * const secret = generateWebhookSecret();
 * // Returns: "whsec_abc123..."
 * ```
 */
export declare function generateWebhookSecret(): string;
/**
 * Compute a SHA-256 hash of a webhook secret for storage.
 *
 * We store the hash rather than the plaintext secret so that:
 * - Secrets are not exposed in database backups
 * - Secrets are not exposed in logs
 * - A database breach doesn't expose secrets
 *
 * @param secret - The webhook secret to hash
 * @returns The SHA-256 hash of the secret
 */
export declare function hashWebhookSecret(secret: string): string;
/**
 * Get the prefix portion of a webhook secret for display purposes.
 *
 * Returns a truncated version like "whsec_abc...xyz" that allows
 * users to identify which secret is configured without exposing it.
 *
 * @param secret - The full webhook secret
 * @returns A truncated display version of the secret
 */
export declare function getWebhookSecretPrefix(secret: string): string;
/**
 * Verify a webhook secret against a stored hash.
 *
 * Uses timing-safe comparison to prevent timing attacks.
 *
 * @param secret - The secret to verify
 * @param storedHash - The stored SHA-256 hash
 * @returns true if the secret matches the hash
 */
export declare function verifyWebhookSecretHash(secret: string, storedHash: string): boolean;
export type WebhookEventType = 'escalation.created' | 'escalation.approved' | 'escalation.rejected' | 'escalation.timeout' | 'intent.approved' | 'intent.denied' | 'intent.completed';
export interface WebhookPayload {
    id: string;
    eventType: WebhookEventType;
    timestamp: string;
    tenantId: ID;
    data: Record<string, unknown>;
}
export interface WebhookConfig {
    url: string;
    secret?: string;
    enabled: boolean;
    events: WebhookEventType[];
    retryAttempts?: number;
    retryDelayMs?: number;
    /**
     * Resolved IP address stored at registration time for DNS pinning.
     * Used to prevent DNS rebinding attacks where an attacker changes DNS
     * after validation to point to internal IPs (e.g., 169.254.169.254).
     */
    resolvedIp?: string;
    /**
     * SHA-256 hash of the webhook secret for verification.
     * Only present when the webhook has a configured secret.
     */
    secretHash?: string;
    /**
     * Truncated secret prefix for display (e.g., "whsec_abc...xyz").
     * Allows users to identify which secret is configured without exposing it.
     */
    secretPrefix?: string;
    /**
     * Timestamp when the secret was last rotated.
     * ISO 8601 format.
     */
    lastRotatedAt?: string;
}
export interface WebhookDeliveryResult {
    success: boolean;
    statusCode?: number;
    error?: string;
    attempts: number;
    deliveredAt?: string;
    /** True if delivery was skipped due to open circuit breaker */
    skippedByCircuitBreaker?: boolean;
}
/**
 * Circuit breaker states for webhook delivery
 * - closed: Normal operation, deliveries proceed
 * - open: Circuit is tripped, deliveries are skipped
 * - half_open: Testing if webhook is healthy again
 */
export type CircuitBreakerState = 'closed' | 'open' | 'half_open';
/**
 * Circuit breaker state stored in Redis
 */
export interface CircuitBreakerData {
    /** Number of consecutive failures */
    failures: number;
    /** Timestamp when circuit was opened (milliseconds since epoch) */
    openedAt: number | null;
    /** Current state of the circuit */
    state: CircuitBreakerState;
}
/**
 * DNS Rebinding Attack Detection Result
 */
export interface DnsConsistencyResult {
    valid: boolean;
    reason?: string;
    currentIp?: string;
    storedIp?: string;
}
/**
 * Validate webhook IP consistency (DNS pinning).
 * Compares the currently resolved IP with the IP stored at registration time.
 * This prevents DNS rebinding attacks where an attacker:
 * 1. Registers webhook with attacker.com -> resolves to public IP (passes validation)
 * 2. Changes DNS: attacker.com -> 169.254.169.254 (AWS metadata)
 * 3. Webhook delivery resolves new DNS -> blocked because IP changed
 *
 * @param url The webhook URL to validate
 * @param storedIp The IP address stored at registration time
 * @returns Validation result with current and stored IPs for logging
 */
export declare function validateWebhookIpConsistency(url: string, storedIp: string | undefined): Promise<DnsConsistencyResult>;
/**
 * Webhook Service
 *
 * Manages webhook registration, delivery, and persistence.
 * Includes circuit breaker pattern for failing webhooks and
 * persistent delivery records for auditing and replay.
 */
export declare class WebhookService {
    private redis;
    private readonly keyPrefix;
    private readonly circuitKeyPrefix;
    private deliveryRepository;
    /**
     * Get the delivery repository instance (lazy initialization).
     * This allows the repository to be created only when persistence is needed.
     */
    private getDeliveryRepository;
    /**
     * Get circuit breaker state for a webhook from Redis
     */
    private getCircuitState;
    /**
     * Set circuit breaker state for a webhook in Redis
     */
    private setCircuitState;
    /**
     * Transition circuit breaker to a new state with logging and metrics
     */
    private transitionCircuitState;
    /**
     * Check if circuit breaker allows delivery attempt
     * Returns true if delivery should proceed, false if it should be skipped
     */
    private shouldAttemptDelivery;
    /**
     * Record delivery success and potentially close the circuit
     */
    private recordDeliverySuccess;
    /**
     * Record delivery failure and potentially open the circuit
     */
    private recordDeliveryFailure;
    /**
     * Get current circuit breaker status for a webhook (for monitoring/debugging)
     */
    getCircuitBreakerStatus(tenantId: ID, webhookId: string): Promise<CircuitBreakerData & {
        timeUntilResetMs?: number;
    }>;
    /**
     * Manually reset circuit breaker for a webhook (for administrative use)
     */
    resetCircuitBreaker(tenantId: ID, webhookId: string): Promise<void>;
    /**
     * Register a webhook for a tenant
     *
     * Performs SSRF validation and DNS resolution to store the resolved IP
     * for DNS pinning protection against rebinding attacks.
     *
     * If no secret is provided, one is auto-generated for security.
     *
     * @returns Object with webhookId and the plaintext secret (only returned once)
     */
    registerWebhook(tenantId: ID, config: WebhookConfig): Promise<string>;
    registerWebhook(tenantId: ID, config: WebhookConfig, options: {
        returnSecret: true;
    }): Promise<{
        webhookId: string;
        secret: string;
    }>;
    /**
     * Rotate the webhook secret.
     *
     * Generates a new secret and updates the webhook configuration.
     * The new secret is only returned once and should be stored securely by the caller.
     *
     * @param tenantId - Tenant ID
     * @param webhookId - Webhook ID
     * @param rotatedBy - User ID who performed the rotation (for audit)
     * @param reason - Reason for rotation (for audit)
     * @returns The new secret (returned only once, not stored in plaintext)
     * @throws NotFoundError if webhook not found
     */
    rotateSecret(tenantId: ID, webhookId: string, rotatedBy?: string, reason?: string): Promise<{
        secret: string;
        secretPrefix: string;
        previousSecretPrefix?: string;
    }>;
    /**
     * Get a single webhook by ID.
     *
     * @param tenantId - Tenant ID
     * @param webhookId - Webhook ID
     * @returns Webhook config or null if not found
     */
    getWebhook(tenantId: ID, webhookId: string): Promise<WebhookConfig | null>;
    /**
     * Unregister a webhook
     */
    unregisterWebhook(tenantId: ID, webhookId: string): Promise<boolean>;
    /**
     * Get all webhooks for a tenant
     *
     * Decrypts stored webhook secrets on retrieval. Handles both new encrypted
     * format and legacy plaintext secrets for backward compatibility.
     */
    getWebhooks(tenantId: ID): Promise<Array<{
        id: string;
        config: WebhookConfig;
    }>>;
    /**
     * Send webhook notification for an escalation event
     */
    notifyEscalation(eventType: 'escalation.created' | 'escalation.approved' | 'escalation.rejected' | 'escalation.timeout', escalation: EscalationRecord): Promise<WebhookDeliveryResult[]>;
    /**
     * Send webhook notification for an intent event
     */
    notifyIntent(eventType: 'intent.approved' | 'intent.denied' | 'intent.completed', intentId: ID, tenantId: ID, additionalData?: Record<string, unknown>): Promise<WebhookDeliveryResult[]>;
    /**
     * Deliver webhooks to all registered endpoints for a tenant.
     *
     * Creates persistent delivery records before attempting delivery,
     * updating them with success/failure status after each attempt.
     * Includes circuit breaker check to skip consistently failing webhooks.
     * Processes webhooks in parallel batches with configurable concurrency.
     */
    private deliverToTenant;
    /**
     * Deliver webhook with retry logic
     *
     * Uses per-webhook config if provided, otherwise falls back to global config.
     * Global defaults are configurable via environment variables.
     * Updates circuit breaker state and persistent delivery record based on success/failure.
     */
    private deliverWithRetry;
    /**
     * Send HTTP request to webhook URL
     *
     * Performs DNS pinning check to detect DNS rebinding attacks.
     * Timeout is configurable via VORION_WEBHOOK_TIMEOUT_MS environment variable.
     * Default: 10000ms, Min: 1000ms, Max: 60000ms
     */
    private sendWebhook;
    /**
     * Store delivery result for auditing
     *
     * Uses a Redis SET index to track delivery IDs per webhook, avoiding the need
     * for expensive KEYS operations when retrieving deliveries.
     */
    private storeDeliveryResult;
    /**
     * Get recent deliveries for a webhook
     *
     * Uses a Redis sorted set index instead of KEYS command.
     * KEYS is O(n) and blocks Redis during execution, causing latency spikes.
     * ZREVRANGE on the index is O(log(n) + m) where m is the limit, much more efficient.
     */
    getDeliveries(tenantId: ID, webhookId: string, limit?: number): Promise<Array<{
        id: string;
        result: WebhookDeliveryResult;
    }>>;
    /**
     * Clean up stale index entries for a webhook
     *
     * This removes index entries pointing to expired delivery records.
     * Called periodically or on-demand to maintain index hygiene.
     */
    cleanupDeliveryIndex(tenantId: ID, webhookId: string): Promise<number>;
    /**
     * Get persistent delivery history for a webhook.
     *
     * Returns deliveries from the database (not Redis), ordered by creation time.
     *
     * @param webhookId - Webhook ID
     * @param limit - Maximum number of records (default: 50, max: 100)
     * @returns Array of delivery records
     */
    getPersistentDeliveryHistory(webhookId: ID, limit?: number): Promise<WebhookDelivery[]>;
    /**
     * Get a single persistent delivery record by ID.
     *
     * @param deliveryId - Delivery ID
     * @returns Delivery record or null if not found
     */
    getPersistentDeliveryById(deliveryId: ID): Promise<WebhookDelivery | null>;
    /**
     * Replay a failed webhook delivery.
     *
     * Marks the delivery for immediate retry and processes it.
     * Only failed deliveries can be replayed.
     *
     * @param deliveryId - Delivery ID to replay
     * @param tenantId - Tenant ID (for authorization)
     * @returns Updated delivery record
     * @throws NotFoundError if delivery not found
     * @throws ValidationError if delivery is not in failed status
     */
    replayDelivery(deliveryId: ID, tenantId: ID): Promise<WebhookDelivery>;
    /**
     * Process pending retries.
     *
     * This method is intended to be called by a background worker/scheduler
     * to process deliveries that are in 'retrying' status and have passed
     * their nextRetryAt time.
     *
     * @param limit - Maximum number of deliveries to process (default: 100)
     * @returns Array of processing results
     */
    processPendingRetries(limit?: number): Promise<Array<{
        deliveryId: ID;
        success: boolean;
        error?: string;
    }>>;
    /**
     * Get failed deliveries for a tenant.
     *
     * @param tenantId - Tenant ID
     * @param limit - Maximum number of records (default: 50, max: 100)
     * @returns Array of failed delivery records
     */
    getFailedDeliveries(tenantId: ID, limit?: number): Promise<WebhookDelivery[]>;
}
/**
 * Create webhook service instance
 */
export declare function createWebhookService(): WebhookService;
/**
 * Webhook delivery status
 */
export type WebhookDeliveryStatus = 'pending' | 'delivered' | 'failed' | 'retrying';
/**
 * Webhook delivery record - represents a persistent delivery attempt
 */
export interface WebhookDelivery {
    id: ID;
    webhookId: ID;
    tenantId: ID;
    eventType: string;
    payload: Record<string, unknown>;
    status: WebhookDeliveryStatus;
    attempts: number;
    lastAttemptAt: string | null;
    lastError: string | null;
    nextRetryAt: string | null;
    deliveredAt: string | null;
    responseStatus: number | null;
    responseBody: string | null;
    createdAt: string;
}
/**
 * Options for creating a new delivery record
 */
export interface CreateDeliveryOptions {
    webhookId: ID;
    tenantId: ID;
    eventType: string;
    payload: Record<string, unknown>;
}
/**
 * Options for updating delivery status
 */
export interface UpdateDeliveryStatusOptions {
    status: WebhookDeliveryStatus;
    attempts?: number;
    lastAttemptAt?: Date;
    lastError?: string | null;
    nextRetryAt?: Date | null;
    deliveredAt?: Date | null;
    responseStatus?: number | null;
    responseBody?: string | null;
}
/**
 * Calculate next retry time with exponential backoff.
 *
 * Uses the formula: baseDelay * 2^(attempt - 1)
 * With a maximum delay cap to prevent extremely long waits.
 *
 * @param attempt - Current attempt number (1-based)
 * @param baseDelayMs - Base delay in milliseconds (default: 1000)
 * @param maxDelayMs - Maximum delay cap in milliseconds (default: 1 hour)
 * @returns Date when next retry should occur
 */
export declare function calculateNextRetryTime(attempt: number, baseDelayMs?: number, maxDelayMs?: number): Date;
/**
 * Repository for managing webhook delivery persistence.
 *
 * Provides CRUD operations for webhook delivery records, supporting:
 * - Creation of delivery records before attempting delivery
 * - Status updates on success/failure
 * - Retrieval of delivery history for auditing
 * - Fetching pending retries for background processing
 * - Fetching failed deliveries for monitoring/debugging
 */
export declare class WebhookDeliveryRepository {
    private _db;
    /**
     * Get database instance (lazy initialization).
     */
    private get db();
    /**
     * Create a new delivery record.
     *
     * Call this before attempting webhook delivery to ensure
     * the delivery is tracked even if the process crashes.
     *
     * @param data - Delivery creation options
     * @returns Created delivery record
     */
    createDelivery(data: CreateDeliveryOptions): Promise<WebhookDelivery>;
    /**
     * Update delivery status with details.
     *
     * @param id - Delivery ID
     * @param options - Status update options
     * @returns Updated delivery record
     * @throws NotFoundError if delivery not found
     */
    updateDeliveryStatus(id: ID, options: UpdateDeliveryStatusOptions): Promise<WebhookDelivery>;
    /**
     * Get delivery by ID.
     *
     * @param id - Delivery ID
     * @returns Delivery record or null if not found
     */
    getDeliveryById(id: ID): Promise<WebhookDelivery | null>;
    /**
     * Get delivery history for a webhook.
     *
     * Returns deliveries ordered by creation time (most recent first).
     *
     * @param webhookId - Webhook ID
     * @param limit - Maximum number of records to return (default: 50, max: 100)
     * @returns Array of delivery records
     */
    getDeliveryHistory(webhookId: ID, limit?: number): Promise<WebhookDelivery[]>;
    /**
     * Get pending retries that are due for processing.
     *
     * Returns deliveries with status='retrying' and nextRetryAt <= now.
     *
     * @param limit - Maximum number of records to return (default: 100)
     * @returns Array of delivery records ready for retry
     */
    getPendingRetries(limit?: number): Promise<WebhookDelivery[]>;
    /**
     * Get failed deliveries for a tenant.
     *
     * Returns deliveries with status='failed' for monitoring and debugging.
     *
     * @param tenantId - Tenant ID
     * @param limit - Maximum number of records to return (default: 50, max: 100)
     * @returns Array of failed delivery records
     */
    getFailedDeliveries(tenantId: ID, limit?: number): Promise<WebhookDelivery[]>;
    /**
     * Get deliveries by tenant with pagination.
     *
     * @param tenantId - Tenant ID
     * @param options - Pagination options
     * @returns Paginated array of delivery records
     */
    getDeliveriesByTenant(tenantId: ID, options?: {
        limit?: number;
        offset?: number;
        status?: WebhookDeliveryStatus;
    }): Promise<{
        items: WebhookDelivery[];
        hasMore: boolean;
    }>;
    /**
     * Mark a delivery for immediate replay.
     *
     * Resets the delivery status to 'retrying' with nextRetryAt set to now.
     * Does not reset attempt count to preserve audit history.
     *
     * @param id - Delivery ID
     * @returns Updated delivery record
     * @throws NotFoundError if delivery not found
     */
    markForReplay(id: ID): Promise<WebhookDelivery>;
    /**
     * Clean up old delivery records.
     *
     * Deletes deliveries older than the specified retention period.
     * Typically called by a scheduled cleanup job.
     *
     * @param retentionDays - Number of days to retain records
     * @returns Number of records deleted
     */
    cleanupOldDeliveries(retentionDays: number): Promise<number>;
}
/**
 * Create webhook delivery repository instance
 */
export declare function createWebhookDeliveryRepository(): WebhookDeliveryRepository;
//# sourceMappingURL=webhooks.d.ts.map