/** Authorization scheme token. `Authorization: AVCS-Sig keyId="...", ts="...", ...`. */ export declare const AUTH_SCHEME = "AVCS-Sig"; /** Default freshness window for a request signature (ms). A request whose `ts` is more * than this far from the server clock (either direction) is rejected as stale/replayed. */ export declare const DEFAULT_AUTH_WINDOW_MS = 300000; /** The parsed fields of an AVCS-Sig Authorization header. */ export interface AuthCredential { keyId: string; ts: string; nonce: string; sig: string; /** The repository the client believes it is addressing (issue #49). Absent on an * unscoped credential — which is every credential written before this existed. */ scope?: string; /** Signature over the scope-bearing material. Present iff `scope` is. Separate from * `sig` so a scope-unaware verifier can still check `sig` and accept the request. */ bsig?: string; } /** * The exact byte string both sides sign/verify. Binds the signature to the method, the * request target, a timestamp (freshness) and a nonce (replay), plus a hash of the body * so a captured signature cannot be replayed against different content. * * `scope` (issue #49) additionally binds it to a REPOSITORY. The path a client signs is * the endpoint suffix ("/objects"), because the reference hub sits at the root — so * without a scope nothing in the signed material says which repository the write was for, * and on a multi-tenant hub a credential captured for one repo is structurally valid for * another. Signing the full path instead would couple the signature to the server's mount * layout and break under ordinary path-rewriting proxies. * * Appended rather than inserted, and empty when absent, so an unscoped credential produces * byte-identical material to what this function produced before scope existed. */ export declare function canonicalRequest(method: string, path: string, ts: string, nonce: string, body: string, scope?: string): string; /** Build an `Authorization: AVCS-Sig …` header value, signing the request with the local * actor's private key. `body` is the exact request body the client will send (""for none). */ export declare function buildAuthHeader(args: { keyId: string; privateKey: string; method: string; path: string; body?: string; ts?: string; nonce?: string; /** Repository this credential is for (issue #49). Omit, or pass "", to sign unscoped. */ scope?: string; }): string; /** Parse an AVCS-Sig Authorization header. Returns null on any scheme/field mismatch. */ export declare function parseAuthHeader(header: string | undefined | null): AuthCredential | null; /** * A bounded seen-nonce cache for replay protection. Entries expire after `ttlMs` (the * freshness window — a nonce can only be replayed within it anyway) and the map is * capped so a hostile client cannot grow it without bound. */ export declare class NonceCache { #private; constructor(ttlMs?: number, max?: number); /** Record a nonce; returns false if it was already seen (a replay). */ check(nonce: string, now: number): boolean; } /** Resolve a keyId to the public key(s) registered for it. This is the pluggable hook (D3): * the default server resolver reads `member:`; an embedder (e.g. a hosted hub) injects * its own user-DB lookup. * * Returns a single PEM, an array of PEMs, or null/[] when the key is unknown. The array form * exists because a keyId is an ACTOR, and one actor may hold several signing keys at once (a * per-account key set, key rotation without downtime). The AVCS-Sig header names only the * actor, never which key signed — so `verifyAuth` tries each candidate against the signature. * The freshness/nonce/scope checks run ONCE regardless, so extra candidates never consume the * nonce or widen the replay window. */ export type PublicKeyResolver = (keyId: string) => Promise; export type AuthResult = { ok: true; keyId: string; } | { ok: false; reason: string; }; /** * Verify a request's AVCS-Sig credential. Steps, in order: * 1. parse the header, * 2. reject a stale/future timestamp (outside the freshness window), * 3. reject a replayed nonce, * 4. resolve the keyId to a public key (unknown key → unauthenticated), * 5. verify the signature over the canonical request. * Any failure returns `{ ok: false, reason }` for a 401 body. Success returns the keyId. */ export declare function verifyAuth(args: { header: string | undefined | null; method: string; path: string; body: string; resolvePublicKey: PublicKeyResolver; now: number; windowMs?: number; nonceCache?: NonceCache; /** * The repository this request is actually for (issue #49). When set, the credential must * name the same one — so a signature captured for another repo on a multi-tenant hub is * refused even though key, method, path, body and freshness all still check out. * * Leave unset on a single-repo hub: there is nothing to compare against, and every * existing credential keeps verifying exactly as before. */ expectedScope?: string; }): Promise; //# sourceMappingURL=transportAuth.d.ts.map