/** * Decode a JWT's payload without verifying the signature and return its `exp` * claim (seconds since epoch), or null if the token is malformed / has no exp. * * Signature verification is intentionally skipped — the Go API is the trust * boundary. This is only used to decide whether to prompt the host to refresh. */ export declare function decodeJwtExp(token: string): number | null; /** * Returns true when the JWT is expired (or expires within `skewSeconds`). * A token we cannot decode (or that carries no exp) is treated as NOT expired * so we never block a token the Go API might still accept — the downstream * validator remains the source of truth. */ export declare function isJwtExpired(token: string, skewSeconds?: number, nowMs?: number): boolean; /** The current Fly machine id, or "" when running locally / outside Fly. */ export declare function currentMachineId(): string; /** * Wrap a raw session ID with the current machine id when running on Fly. * Local/dev (no FLY_MACHINE_ID) returns the raw id unchanged so behavior and * existing session-id shapes are preserved off-platform. */ export declare function encodeSessionId(rawId: string, machineId?: string): string; /** * Extract the machine id embedded in a session ID, or "" if none is present * (locally-minted id, or a legacy id from before this scheme). */ export declare function machineIdFromSessionId(sessionId: string): string; /** * Decide whether an incoming request for `sessionId` should be replayed to a * different Fly machine. * * Returns the target machine id to replay to, or null to handle locally. * Guards against replay loops: if the request was already replayed * (fly-replay-src header present) we never replay again — the caller falls * through to its normal 404 so the client re-initializes per the MCP spec. */ export declare function replayTargetMachine(sessionId: string, opts: { selfMachineId?: string; alreadyReplayed: boolean; }): string | null; /** * Decode a JWT's `sub` (subject) claim without verifying the signature. * The subject is the stable Supabase user id — it survives token refresh, * whereas the raw JWT string rotates roughly hourly. Returns null when the * token is malformed or carries no usable string `sub`. */ export declare function decodeJwtSub(token: string): string | null; /** * Compute the STABLE binding token for a presented credential — the value a * session is bound to at init and re-checked on every resume: * * - API key → the raw key. A given key is re-presented verbatim on every * request, so binding to it is exact. * - JWT → the `sub` claim (Supabase user id), NOT the raw token. claude.ai * rotates the Bearer JWT ~hourly via /oauth/token; binding to the raw token * would 403 every legitimate refresh. The subject is constant across * refreshes for the same user, so binding survives rotation while still * rejecting a different user's token. * - JWT with no decodable `sub` → fall back to the raw token (best effort; * Supabase access tokens always carry `sub`, so this is a degenerate case). * * Returns null when neither credential is present. */ export declare function credentialBindingToken(auth: { apiKey?: string; jwt?: string; }): string | null; /** SHA-256 hex digest of a binding token — what we store on the session record. */ export declare function hashBindingToken(token: string): string; /** * Compute the stored/compared binding hash for a request's auth, or null when * the request carries no credential. */ export declare function credentialBindingHash(auth: { apiKey?: string; jwt?: string; }): string | null; /** * Constant-time comparison of two binding hashes. Both are fixed-length hex * digests; a length mismatch (or a null presented hash) is a non-match. */ export declare function bindingHashMatches(stored: string, presented: string | null): boolean;