/**
 * @module @arcis/node/middleware/correlation
 *
 * V1.6 / improvements.md §1.3 — Stateful per-IP correlation window.
 *
 * Today's middleware is stateless: each request is judged on its own.
 * That misses three classes of attack:
 *
 *   - Scanner sweep. One IP firing payloads from every category in
 *     quick succession is a scanner, not a real user.
 *   - Credential stuffing. Same login route, same IP, dozens of
 *     distinct usernames in 60 seconds.
 *   - Race-condition probe. POST /a immediately followed by GET /b
 *     from the same IP, within 200 ms. Either is fine alone.
 *
 * This module records a small rolling event log per IP (capped) and
 * exposes detection helpers. Detection is *additive* — Pattern 4
 * applies (fail-open). If something goes wrong here, the existing
 * rate-limit + per-vector defenses still run.
 *
 * Mirrors `arcis-python/arcis/middleware/correlation.py`. Both SDKs
 * must accept the same base corpus per Pattern 7.
 */
export interface CorrelationEvent {
    /** Wall-clock seconds (Date.now() / 1000) or test-injected value. */
    timestamp: number;
    /** Detector or operation that produced this event. */
    vector: string;
    route: string;
    method: string;
    /** Username / email / token bucket if relevant; otherwise undefined. */
    distinctValue?: string;
}
export interface CorrelationDetections {
    scanner: boolean;
    credentialStuffing: boolean;
    raceWindow: boolean;
    distinctVectors: number;
    distinctValues: number;
    requestsInWindow: number;
}
export interface CorrelationWindowOptions {
    windowSeconds?: number;
    maxIps?: number;
    maxEventsPerIp?: number;
    scannerDistinctVectors?: number;
    scannerMinRequests?: number;
    credentialStuffingDistinctValues?: number;
    raceWindowMs?: number;
    /** Pre-registered race-pair routes; ad-hoc detect_race_window also works. */
    racePairs?: Array<[string, string]>;
}
/**
 * Rolling per-IP correlation window. Mirrors the Python
 * `CorrelationWindow` class.
 *
 * All detection helpers are read-only; only `record` mutates state.
 */
export declare class CorrelationWindow {
    private readonly windowSeconds;
    private readonly maxIps;
    private readonly maxEventsPerIp;
    private readonly scannerDistinctVectors;
    private readonly scannerMinRequests;
    private readonly csDistinctValues;
    private readonly raceWindowSeconds;
    private readonly racePairKeys;
    private readonly racePairTuples;
    private readonly buckets;
    constructor(options?: CorrelationWindowOptions);
    record(ip: string, vector: string, route: string, method?: string, distinctValue?: string, now?: number): CorrelationDetections;
    detectScanner(ip: string, now?: number): boolean;
    detectCredentialStuffing(ip: string, route: string, now?: number): boolean;
    detectRaceWindow(ip: string, routePair: [string, string], now?: number): boolean;
    reset(ip?: string): void;
    stats(): {
        trackedIps: number;
        eventsInWindow: number;
    };
    private evictStale;
    private evaluate;
    private isScanner;
    private isCredentialStuffing;
    private racePairInBucket;
    private isRaceAny;
}
//# sourceMappingURL=correlation.d.ts.map