/**
 * Express middleware — JSON parsing, CORS, auth, error handling.
 */
import type { Request, Response, NextFunction } from 'express';
/**
 * Prevent a late duplicate Express response from throwing
 * ERR_HTTP_HEADERS_SENT after a handler has already committed a response.
 *
 * This is a last-resort process guard; route handlers should still return
 * immediately after early response branches.
 */
export declare function duplicateResponseGuard(req: Request, res: Response, next: NextFunction): void;
export declare function corsMiddleware(req: Request, res: Response, next: NextFunction): void;
/** Test hook — clear the deprecation log cache. */
export declare function _resetDeprecationLogCache(): void;
/**
 * Auth middleware — enforces Bearer token on API endpoints.
 * Health endpoint is exempt (used for external monitoring).
 *
 * @param authToken  The configured server bearer token, or a getter that
 *   returns it. A getter is resolved on EVERY request so the token can be
 *   rotated at runtime (tunnel credential rotation, Part 6 of the
 *   tunnel-failure-resilience spec) and take effect immediately — old
 *   bearer tokens and old HMAC-signed view URLs are rejected the moment
 *   rotation completes, without a server restart. If omitted (or the
 *   getter returns undefined), all requests pass through (used in tests
 *   and unauthenticated dev runs).
 * @param agentId    The configured server agent identity (e.g. projectName).
 *   When set, the middleware additionally validates the
 *   `X-Instar-AgentId` request header BEFORE comparing the bearer
 *   token. A mismatch returns a structured 403 — the goal is to make
 *   tokens sent to the wrong agent's server structurally inert before
 *   token bytes are even compared. Missing header is accepted during
 *   the backward-compatibility deprecation window with a deduped log
 *   line. See spec docs/specs/telegram-delivery-robustness.md § Layer 1b.
 */
export declare function authMiddleware(authToken?: string | (() => string | undefined), agentId?: string): (req: Request, res: Response, next: NextFunction) => void;
/**
 * Simple in-memory rate limiter using a sliding window.
 * No external dependencies. Suitable for a local management API.
 */
export declare function rateLimiter(windowMs?: number, maxRequests?: number): (req: Request, res: Response, next: NextFunction) => void;
/**
 * Request timeout middleware — prevents slow requests from hanging.
 * Returns 408 if the request takes longer than the timeout.
 *
 * `perPathOverrides` lets specific path prefixes use a longer (or shorter)
 * budget. This exists because some routes are intentionally LLM-backed and
 * need headroom the default 30s doesn't provide — the outbound messaging
 * routes (tone gate review + Telegram Bot API roundtrip) commonly take
 * 30–60s under normal load, and a 408 while the actual send is in flight
 * triggers duplicate-send cascades on the client side.
 *
 * Override matching is by longest-prefix, so a more specific prefix beats a
 * shorter parent if both are registered.
 */
/**
 * Extended request-timeout budget (ms) for outbound-messaging routes. They are
 * LLM-backed (tone-gate review) and hit third-party APIs (Telegram/Slack/WhatsApp)
 * whose latency we don't control; the 30s default 408s mid-send and triggers
 * duplicate-send cascades on the client. 120s covers this path's realistic p99.
 */
export declare const OUTBOUND_MESSAGING_TIMEOUT_MS = 120000;
/**
 * Hard budget (ms) the outbound tone/relevance gate is allowed to spend BEFORE
 * the route fails it open and delivers the message un-reviewed.
 *
 * This MUST stay comfortably below OUTBOUND_MESSAGING_TIMEOUT_MS. The reason is
 * a real production failure (2026-06-08): the tone gate is FAIL-OPEN by design,
 * but `MessagingToneGate.review` will wait up to RATE_LIMIT_WAIT_MS (120s) for a
 * rate-limit window PLUS the call itself — and that whole wait sat inside an
 * un-raced `await` in `checkOutboundMessage`. Under rate-limit pressure the gate
 * routinely finished at 121s–185s (observed in the tone-gate decision log, all
 * failedOpen), blowing past the 120s route budget. The route then 408s, which is
 * the WORST outcome: the message both bypasses the gate AND, because the send
 * "failed", the calling session dumps the note into whatever topic it is active
 * in (the "patch notes landing in the Invoices topic" bug). Capping the gate at
 * this budget — and failing OPEN past it (same contract as the ArcCheck 200ms
 * race) — guarantees the route always returns a verdict in budget. The invariant
 * `OUTBOUND_GATE_REVIEW_BUDGET_MS < OUTBOUND_MESSAGING_TIMEOUT_MS` is asserted in
 * the wiring test so the two budgets can never drift into conflict again.
 */
export declare const OUTBOUND_GATE_REVIEW_BUDGET_MS = 20000;
/**
 * Extended budget for the standards-conformance gate route (`/spec/conformance-check`).
 * It makes a single heavy top-tier review call over a full spec; the 30s default
 * 408s on any real spec. Set ABOVE the reviewer's inner CONFORMANCE_REVIEW_TIMEOUT_MS
 * (150s) so the provider's own clean kill fires first — a genuinely-too-slow spec
 * degrades fail-open (advisory empty report) instead of erroring at the client.
 */
export declare const SPEC_REVIEW_TIMEOUT_MS = 180000;
/**
 * Extended budget for the cutover-readiness parity-pass trigger
 * (`/cutover-readiness/parity-pass`). One pass fetches the FULL live Portal
 * cluster set (paginated) and compares server-side — measured at ~3.5 minutes
 * against the real endpoint under load (2026-06-05). Under the 30s default the
 * client always got a 408 while the handler kept running, and a late failure's
 * 409 then crashed into ERR_HTTP_HEADERS_SENT with no trace of the outcome.
 */
export declare const PARITY_PASS_TIMEOUT_MS = 360000;
/**
 * Extended budget for the deterministic topic transfer (`/pool/transfer`,
 * WS1.2). When the topic's current owner can drain, the handler awaits the
 * owner-side SessionDrainRunner SYNCHRONOUSLY: it waits up to `drainBoundMs`
 * (30s default) for the in-flight turn to reach a boundary, then closes the
 * session and lands the claim — so a CLEAN drain routinely lands at or past
 * 30s, and the remote `_sendDrain` mesh call is itself capped at 50s. Under
 * the 30s default the client would get a 408 mid-drain while the handler kept
 * running to completion (landing the claim + setting the pin) — the exact
 * "408 while the handler keeps running" failure class the outbound-messaging
 * and parity-pass overrides already exist to prevent (2026-06-12 second-pass
 * review concern #1). 75s clears the 50s remote cap + slack with margin.
 */
export declare const POOL_TRANSFER_TIMEOUT_MS = 75000;
/**
 * Slack added on top of a configured parity-source TOTAL fetch budget when
 * deriving the parity-pass/import-dryrun route budgets: the route does the full
 * live fetch PLUS server-side compare/import work after it.
 */
export declare const PARITY_ROUTE_SLACK_MS = 60000;
/**
 * The production per-path request-timeout overrides. Exported as the single
 * source of truth so wiring-integrity tests assert against the SAME map the
 * server actually wires — never a hand-rolled copy that could pass while the
 * server is misconfigured (the PR-#334 dead-code lesson).
 *
 * `paritySourceTotalTimeoutMs` (config `feedbackMigration.paritySource.totalTimeoutMs`):
 * when an operator widens the live-source fetch budget for a degraded source, the
 * parity-pass/import-dryrun ROUTE budgets must widen with it — otherwise every
 * trigger 408s at the constant while the handler keeps running, and a caller that
 * retries on the 408 piles a concurrent fetch onto the degraded source (observed
 * live 2026-06-05: 600s/page configured, 360s route → four concurrent passes).
 * The constant stays the floor; the derived budget never shrinks below it.
 */
export declare function buildRequestTimeoutOverrides(opts?: {
    paritySourceTotalTimeoutMs?: number;
}): Record<string, number>;
/**
 * Resolve the timeout budget for a path against the overrides, by longest-prefix
 * match. Exported and used by BOTH the middleware and its tests so the matching
 * logic can never drift between what is tested and what runs. Match is exact
 * prefix OR prefix followed by '/' so '/foo' never spuriously matches '/foo-bar'
 * — and a sibling like '/spec/conformance-metrics' is NOT matched by the
 * '/spec/conformance-check' prefix. req.path never carries the query string.
 */
export declare function resolveRequestTimeout(reqPath: string, defaultMs: number, perPathOverrides: Record<string, number>): number;
export declare function requestTimeout(defaultMs?: number, perPathOverrides?: Record<string, number>): (req: Request, res: Response, next: NextFunction) => void;
/**
 * HMAC-sign a view path so the URL can be opened in a browser without exposing the auth token.
 * The signature is path-specific — sharing a signed URL only grants access to that one view.
 */
export declare function signViewPath(viewPath: string, authToken: string): string;
/**
 * Security headers for dashboard paths — prevents clickjacking, MIME-sniffing,
 * and restricts resource loading to trusted sources.
 */
export declare function dashboardSecurityHeaders(req: Request, res: Response, next: NextFunction): void;
export declare function errorHandler(err: unknown, req: Request, res: Response, _next: NextFunction): void;
//# sourceMappingURL=middleware.d.ts.map