import type { IncomingMessage } from 'http'; export declare const PENDING_TTL_MS: number; /** Approved pairings idle this long expire (Ben, 2026-09-25). */ export declare const IDLE_EXPIRY_MS: number; export declare const NONCE_TTL_MS = 60000; /** Signed requests must be this fresh, in either direction. */ export declare const IAT_SKEW_MS = 60000; export interface PublicJwk { kty: 'EC'; crv: 'P-256'; x: string; y: string; } export interface Pairing { id: string; origin: string; label: string; public_key_jwk: PublicJwk; status: 'pending' | 'approved'; /** pending only: what HQ shows and the human types. */ code?: string; requested_at: string; approved_at?: string; last_used?: string; } export declare function pairingsPath(): string; export declare function listPairings(): Pairing[]; export declare function isPairingActive(id: string): boolean; /** * A P-256 PUBLIC key and nothing else. A JWK carrying `d` is a private key: the * page is supposed to hold a non-extractable key, so receiving one means * something is badly wrong, and it is refused rather than stored. */ export declare function validatePublicJwk(v: unknown): PublicJwk; /** RFC 7638 thumbprint. */ export declare function thumbprint(jwk: PublicJwk): string; /** * The code both sides show. Derived from the key AND a daemon nonce: from the * key alone, an attacker could grind keys offline until one lands on a chosen * code (cheap at 10^6); with the daemon's nonce mixed in, there is nothing to * grind against before asking. */ export declare function pairingCode(jwk: PublicJwk, daemonNonce: string): string; export declare function requestPairing(origin: string, jwkIn: unknown, labelIn: unknown): Promise<{ pairing_id: string; code: string; expires_at: string; }>; export declare function pairingStatus(id: string, origin: string): { status: 'pending' | 'approved' | 'unknown'; }; /** CLI only. Never reachable over HTTP. */ export declare function approveByCode(code: string): Promise; /** * The native macOS dialog's Allow (pairDialog.ts). Never reachable over HTTP: * it is called only from the daemon after a local click. It approves the exact * request the dialog showed, by id AND code, so a request that was replaced * while the dialog was open is not approved by it. */ export declare function approvePendingLocally(id: string, code: string): Promise; /** CLI, or a signed request from the pairing itself. */ export declare function revokePairing(id: string): Promise; /** Test seam. */ export declare function _setPairingClock(fn: (() => number) | null): void; export declare function issueNonces(pairingId: unknown, origin: string, countIn: unknown): { nonces: string[]; expires_at: string; }; /** * Exactly what the browser signs. The body is not included: replay is stopped * by the single-use nonce, and anything able to rewrite a loopback request in * flight is already past every boundary this could add. */ export declare function signingPayload(method: string, pathAndQuery: string, origin: string, pairingId: string, nonce: string, iat: number): string; /** The pairing id a request was verified under, if it was. */ export declare function pairingOf(req: IncomingMessage): string | undefined; export declare function hasPairingHeaders(req: IncomingMessage): boolean; /** * Verify a signed request. Order is the ADR's: loopback, origin allowlist, * pairing approved and bound to this origin, freshness, nonce (burned before * the signature check, so a bad signature still spends it), signature. */ export declare function verifyPairedRequest(req: IncomingMessage, pathAndQuery: string, allowedOrigins: string[]): string; /** Test seam. */ export declare function _resetPairing(): void; //# sourceMappingURL=pairing.d.ts.map