import type { ScopeOfClaim } from '../../accountability/types/base.js'; import type { CyclesEvidenceRef } from './types.js'; /** The signed CyclesEvidence envelope as fetched from the URL. The two * named members are the ones the content-hash recipe empties; all * other members ride through canonicalization unchanged. */ export interface FetchedCyclesEvidenceEnvelope { /** The envelope's own content-addressed id. Emptied before hashing. */ evidence_id?: string; /** The envelope's signature. Emptied before hashing. */ signature?: string; /** Remaining envelope body, preserved verbatim through JCS. */ [key: string]: unknown; } /** * - 'matched' : envelope fetched and its recomputed content hash * equals the claimed cycles_evidence_id_sha256. * - 'hash_mismatch' : envelope fetched but recomputed hash differs from * the claimed hash. Fail-closed always (a body that * loads but hashes wrong is NOT transient). * - 'malformed' : envelope fetched but was not a structurally usable * object. Fail-closed always. * - 'not_found' : the resolver loaded the endpoint but no envelope * was present at the reference. Fail-closed always. * - 'unreachable' : network error, timeout, non-200, or non-JSON body. * The ONLY status a fail-open policy may relax to a * degraded (still non-matching) outcome. * - 'no_resolver' : no resolver was supplied; the receipt's claimed * hash was never tested against a fetched envelope. */ export type EvidenceResolutionStatus = 'matched' | 'hash_mismatch' | 'malformed' | 'not_found' | 'unreachable' | 'no_resolver'; /** Failure posture, mirrors M3 FailurePolicy. Default 'closed'. */ export type EvidenceFailurePolicy = 'closed' | 'open'; /** * What the caller's resolver returns. When `ok` is true the resolver * fetched an envelope and `envelope` is the parsed object; this module * then recomputes and compares the hash. When `ok` is false the * resolver reports a non-fetch outcome via `status` and the envelope is * absent. The resolver MUST NOT itself decide `matched`: matching is * this module's job, so the resolver never returns 'matched' or * 'hash_mismatch'. */ export interface EvidenceFetchResult { ok: boolean; /** A resolver-side non-fetch status. Only 'not_found' | 'unreachable' * | 'malformed' are meaningful here; the match verdict is computed by * resolveEvidenceRef, never by the resolver. */ status?: 'not_found' | 'unreachable' | 'malformed'; /** The fetched envelope, present iff ok. */ envelope?: FetchedCyclesEvidenceEnvelope; /** Human-readable, non-sensitive explanation. */ reason?: string; } /** * Caller-supplied envelope resolver (the M3 caller-supplied-resolver * pattern). The SDK hardcodes no network egress: the caller owns the * transport, HTTPS enforcement, timeout, and caching. `canResolve` * lets a caller decline a URL scheme it does not handle. `resolve` * MUST always resolve its promise; transport failure is reported via * the result's `ok`/`status`, never by rejecting. */ export interface EvidenceResolver { /** True iff this resolver handles the given evidence URL. */ canResolve(url: string): boolean; /** Fetch the envelope at the URL. Never rejects on transport failure; * reports it via the returned EvidenceFetchResult. */ resolve(url: string): Promise; } /** * The outcome of resolving (or declining to resolve) a CyclesEvidenceRef. * * This is the SHARP claimed-vs-resolved distinction the verifier * surfaces. `claimed_sha256` is always the hash the receipt signed over. * `resolved` is true ONLY when an envelope was fetched and its recomputed * hash matched. `resolved_sha256` is the hash this module actually * recomputed from the fetched envelope (present whenever an envelope was * fetched, whether or not it matched), so a mismatch is auditable. * * `degraded` is true only when a fail-open policy turned an 'unreachable' * condition into a non-rejecting outcome. A degraded result is NOT * `resolved`; it MUST NOT be read as a positive match. */ export interface EvidenceResolutionResult { /** The receipt's claimed content hash (signature-covered). */ claimed_sha256: string; /** True iff an envelope was fetched AND recomputed-hash === claimed. */ resolved: boolean; status: EvidenceResolutionStatus; /** The hash recomputed from the fetched envelope, when one was * fetched. Present on 'matched' and 'hash_mismatch'. */ resolved_sha256?: string; /** True iff a fail-open policy produced a non-rejecting degraded * outcome from an unreachable endpoint. Never true with resolved. */ degraded?: boolean; /** Human-readable, non-sensitive explanation. */ reason?: string; /** Scope-of-claim for the resolution, dogfooding the descriptor. */ scope_of_claim?: ScopeOfClaim; /** The fetched envelope, present whenever one was retrieved (on 'matched' * AND 'hash_mismatch'). Surfaced so a downstream signer-AUTHORITY check * (#43) can verify the envelope's own signature — independent of this * receipt↔envelope hash join. */ envelope?: FetchedCyclesEvidenceEnvelope; } /** Resolution config. `failurePolicy` defaults to 'closed'. */ export interface ResolveEvidenceConfig { failurePolicy?: EvidenceFailurePolicy; } /** * Recompute the Cycles content-addressed id of a fetched envelope: * JCS-canonical bytes of the envelope with both `evidence_id` and * `signature` set to empty string, then sha256 hex. Reproduces the * recipe documented on CyclesEvidenceRef.cycles_evidence_id_sha256 and * matched by the Cycles server, so the recomputed value is comparable * byte-for-byte against the claimed hash. */ export declare function recomputeEvidenceContentHash(envelope: FetchedCyclesEvidenceEnvelope): string; /** * Resolve a CyclesEvidenceRef against a caller-supplied resolver and * report the claimed-vs-resolved verdict. * * Records failure, never throws. A missing resolver yields a * 'no_resolver' result (claimed-not-resolved), NOT an error. A resolver * that reports unreachable yields 'unreachable' (degraded under * fail-open, still non-matching). Only a fetched envelope whose * recomputed hash equals the claimed hash yields resolved=true. * * @param ref the receipt's cycles_evidence join shape * @param resolver caller-supplied envelope resolver, or undefined * @param config failure policy (default fail-closed) */ export declare function resolveEvidenceRef(ref: CyclesEvidenceRef, resolver: EvidenceResolver | undefined, config?: ResolveEvidenceConfig): Promise; /** * The external-evidence observation basis, a mechanical fact for the * W2-A1 descriptor. It distinguishes the two evidentiary states the * brief names: * - 'signature_only' : the receipt signature covers a pointer to a * hash; no envelope was fetched and matched. * - 'counterparty_resolved': an envelope was fetched at the URL and its * recomputed content hash matched the claim. */ export type EvidenceObservationBasis = 'signature_only' | 'counterparty_resolved'; /** * The mechanical descriptor input the W2-A1 verifier descriptor consumes * for a cycles receipt's external-evidence axis. Mechanical facts only; * no assurance scalar, no ladder. A1 derives any policy output on top. */ export interface EvidenceDescriptorInput { /** The receipt's claimed content hash (always present; signed-over). */ claimed_sha256: string; /** signature_only vs counterparty_resolved, the sharp distinction. */ observation_basis: EvidenceObservationBasis; /** True iff observation_basis === 'counterparty_resolved'. */ envelope_matched: boolean; /** The recomputed hash, when an envelope was fetched (matched or not). */ resolved_sha256?: string; /** The underlying resolution status, preserved for the A1 lattice. */ resolution_status: EvidenceResolutionStatus; /** True iff a fail-open degraded outcome produced this input. */ degraded?: boolean; } /** * Project a resolution result into the W2-A1 descriptor input. Pure, * mechanical, total. `counterparty_resolved` iff the envelope was * fetched and matched; every other status is `signature_only` (the * receipt signature still stands on its own; it simply was not * corroborated by a fetched envelope). */ export declare function toEvidenceDescriptorInput(result: EvidenceResolutionResult): EvidenceDescriptorInput; //# sourceMappingURL=evidence-resolution.d.ts.map