/** * cr.posture.receipt.v1 verifier (roadmap 1167). * * Verifies the signed posture receipt produced by the data plane * (capability-demo demo/src/posture.js): a catalog read-back asserting the * credential boundary is still wired, signed out-of-DB by the executor key. * * WHY THIS EXISTS. ENFORCING_ATOMIC_V1's credential_boundary invariant accepts * `credentialBoundary: true` — a bare assertion, verified by nothing * (atomic-profile.ts:108-109). An external audit called that a P0, correctly. * _V2 replaces the assertion with this verification. _V1 is frozen and still * accepts the bare form; the break is opt-in. * * No new crypto: `createPublicKey` + `verify` from node:crypto, the same two * primitives deploy-receipt-token.ts uses, and the same registry/pinned-PEM * key resolution shape. * * VERIFY OVER RAW BYTES, PARSE ONLY TO READ. The producer signs * `canonicalJson(body)` (posture.js:436-437), and that canonicalJson is a local * implementation (posture.js:179-183), NOT RFC 8785 JCS. Re-serialising the * parsed object to re-derive the signed bytes would be a bit-exactness trap * that fails on the first key-order or escaping difference. So: the signature is * checked against the DECODED PREIMAGE BYTES exactly as they arrived, and * JSON.parse is used only to read fields out of an already-verified message. * * FRESHNESS, NOT EXPIRY — and the distinction is load-bearing. The receipt * carries `measured_at` (posture.js:431) and NO `expires_at`; measured against * the producer, the field simply does not exist. So this verifier cannot check * an expiry the receipt never made. It checks AGE against a caller-supplied * `maxAgeMs`, and it deliberately has NO default: a default here would present * the guard's policy as though the receipt had stated it. * * No fetch. Same inputs → same output. */ import type { ExecutorKeyRegistry } from '@coderifts/sdk'; export declare const POSTURE_RECEIPT_V: "cr.posture.receipt.v1"; export type PostureVerifyStatus = 'POSTURE_PASS' | 'POSTURE_FAIL' | 'POSTURE_MALFORMED' | 'POSTURE_UNKNOWN_KEY' | 'POSTURE_REVOKED_KEY' | 'POSTURE_INVALID_SIGNATURE' | 'POSTURE_UNBOUND' | 'POSTURE_STALE'; /** * Allowed future skew on measured_at, in ms. A receipt dated further in the * future than this is not fresh — it is clock-skewed / malformed. */ export declare const POSTURE_CLOCK_SKEW_TOLERANCE_MS = 5000; export type PostureReceiptPayload = { v?: string; executor_kid?: string; deployment_id?: string; measured_at?: string; verdict?: string; facts?: unknown; drift?: unknown; /** * The credential-boundary tuple (AUDIT P1 / RES-3). * * MEASURED: the issuer (capability-demo demo/src/posture.js) emits these as * OPTIONAL reserved body fields — only non-empty strings are spread in, so a * receipt may legitimately carry none of them. They are part of the signed * body when present, which is what makes binding them here meaningful rather * than decorative. */ executor_id?: string; adapter_id?: string; target_uri?: string; policy_hash?: string; }; export type PostureVerifyInput = { /** Well-known-shaped registry `{ keys: [{ kid, public_key_pem, status, … }] }`. */ registry?: ExecutorKeyRegistry; /** Air-gap single PEM (kid-agnostic). */ pinnedKeyPem?: string; /** The receipt must be bound to THIS deployment. */ expectedDeploymentId?: string; /** * The credential-boundary tuple this receipt must bind (AUDIT P1 / RES-3). * * Each is INDEPENDENTLY opt-in, and each behaves like expectedDeploymentId: * supply it and the receipt must carry a matching value or the verify fails * closed with POSTURE_UNBOUND. * * A field you do NOT supply is NOT silently ignored — the result's * `bound` / `not_bound` lists say exactly which parts of the tuple this * verification actually proved. A receipt that binds nothing still verifies * as a signature; it just does not get to claim a boundary it never named. */ expectedExecutorId?: string; expectedAdapterId?: string; expectedTargetUri?: string; expectedPolicyHash?: string; /** Caller-supplied freshness window in ms. NO DEFAULT — see the header. */ maxAgeMs?: number; /** Injectable clock for tests. Defaults to Date.now. */ now?: () => number; }; export type PostureVerifyResult = { valid: boolean; status: PostureVerifyStatus; reason: string | null; payload?: PostureReceiptPayload; /** Age in ms at verification time, when it could be computed. */ age_ms?: number; /** * Which credential-boundary fields this verification actually BOUND, and * which it did not. Named rather than implied: a caller reading `valid: true` * must be able to see that a tuple it did not ask about was not checked. */ bound?: string[]; not_bound?: Array<{ field: string; reason: string; }>; }; /** * Verify a posture receipt token. * * @param token `cr.posture.receipt.v1|||` * (envelope shape measured at posture.js:370-377) */ export declare function verifyPostureReceipt(token: unknown, input?: PostureVerifyInput): PostureVerifyResult; //# sourceMappingURL=posture-receipt.d.ts.map