/** * Receipt -- chain validation and envelope construction. * * Salvaged from `@kit/core`. * * @module */ import { Effect } from 'effect'; import { ParseError } from '@czap/error'; import type { HLC } from './brands.js'; import { type TypedRef } from './typed-ref.js'; /** The logical entity a receipt describes: an effect, a run, an artifact, or an intent. */ export interface ReceiptSubject { readonly type: 'effect' | 'run' | 'artifact' | 'intent'; readonly id: string; } /** * Single link in a receipt chain: timestamped, content-addressed, and linked * to its predecessor(s). Merge envelopes carry an array of `previous` hashes; * optionally MAC-signed via `Receipt.macEnvelope`. */ export interface ReceiptEnvelope { readonly kind: string; /** * Causal clock (CUT B2): an {@link HLC}, NOT a wall-clock string. It is * INCLUDED in `hashEnvelope` and monotonic-validated by `validateChain` * (`hlc_not_increasing`) — i.e. identity- and ordering-bearing. Not * interchangeable with a `WallClockTimestamp` (the volatile, identity-irrelevant * ISO stamp on command/CLI receipts). */ readonly timestamp: HLC; readonly subject: ReceiptSubject; readonly payload: TypedRef.Shape; readonly hash: string; readonly previous: string | readonly string[]; readonly signature?: string; } /** Structured failure returned by `Receipt.validateChainDetailed`. */ export type ChainValidationError = { readonly type: 'not_genesis'; readonly index: 0; } | { readonly type: 'hash_mismatch'; readonly index: number; readonly computed: string; readonly stored: string; } | { readonly type: 'chain_break'; readonly index: number; readonly expected: string; readonly actual: string; } | { readonly type: 'hlc_not_increasing'; readonly index: number; } | { readonly type: 'checkpoint_invalid'; readonly reason: string; }; /** * Options that let a chain be validated as a COMPACTED TAIL instead of a full * history (see `DAG.checkpoint`). Optional everywhere — omitting them is the * back-compat genesis-rooted check. * * - `base`: a checkpoint watermark hash. The index-0 genesis predicate widens to * accept `previous === base`, so a retained tail validates without its dropped * prefix. * - `checkpoint`: the genesis-shaped checkpoint attestation that authorizes * `base`. When supplied it is integrity-checked (hash + genesis shape + * `subject.id === "czap/checkpoint:"`); a mismatch fails `checkpoint_invalid`. * - `verifyCheckpoint`: an OPTIONAL provenance verifier for the checkpoint — the * injectable capability that closes the one gap the structural checks cannot. */ export interface ChainValidationOptions { readonly base?: string; readonly checkpoint?: ReceiptEnvelope; /** * Provenance verifier for the checkpoint attestation (injected capability). * * The structural checks prove the checkpoint is WELL-FORMED (hash, `kind`, * `subject.type`, payload schema, genesis shape, `subject.id`, HLC-advance) but * NOT that it was minted by `DAG.checkpoint` over the real dropped set — a * compacted-tail validator does not hold the dropped set, so it cannot recompute * the summary `content_hash`. A forged genesis-shaped `kind:"checkpoint"` envelope * with the right subject id and an older timestamp therefore passes the structural * floor and could authorize an arbitrarily TRUNCATED tail. * * In a TRUSTED setting (single-actor self-compaction — you validate the checkpoint * YOU minted) the structural floor is sufficient and no verifier is needed. In an * ADVERSARIAL setting (an untrusted remote supplies the checkpoint) inject a * verifier that establishes provenance — e.g. checks a signature (a trusted * compactor's `Receipt.macEnvelope` over the attestation), or recomputes the * summary against a locally-held dropped set. It resolves `true` to accept, `false` * to reject (fails the chain `checkpoint_invalid`); any verification failure must * resolve `false`, not raise. Absent, only the structural floor applies. */ readonly verifyCheckpoint?: (checkpoint: ReceiptEnvelope) => Effect.Effect; } /** Sentinel `previous` value marking the root of a receipt chain. */ export declare const GENESIS: string; /** * The schema id `DAG.checkpoint` stamps on its summary payload's `TypedRef` * (`schema_hash`). Compacted-tail validation binds authorization to this minted * shape — single source of truth, imported by `dag.ts` so the mint and the * verifier can never drift. */ export declare const CHECKPOINT_ATTESTATION_SCHEMA = "czap/checkpoint-summary/v1"; /** * Compute the content hash of a receipt envelope. * * Normalizes the `previous` field (sorts array form), canonicalizes the * payload, and hashes with SHA-256 via TypedRef. * * @example * ```ts * import { Effect } from 'effect'; * * const hash = yield* Receipt.hashEnvelope(envelope); * // hash === envelope.hash (if envelope is valid) * ``` */ export declare const hashEnvelope: (envelope: ReceiptEnvelope) => Effect.Effect; /** * Create a new receipt envelope with an auto-computed content hash. * * @example * ```ts * const envelope = yield* Receipt.createEnvelope( * 'state-change', * { type: 'effect', id: 'actor-1' }, * { _tag: 'TypedRef', mediaType: 'application/json', data: { key: 'value' } }, * hlcTimestamp, * Receipt.GENESIS, * ); * // envelope.hash is the computed SHA-256 content address * ``` */ export declare const createEnvelope: (kind: string, subject: ReceiptSubject, payload: TypedRef.Shape, timestamp: HLC, previousHash: string | readonly string[]) => Effect.Effect; /** * Build a linear chain of receipt envelopes from an array of entries. * * Each envelope's `previous` points to the prior envelope's hash, * starting from GENESIS. * * @example * ```ts * const chain = yield* Receipt.buildChain([ * { kind: 'init', subject: { type: 'effect', id: 'a' }, payload, timestamp: ts1 }, * { kind: 'update', subject: { type: 'effect', id: 'a' }, payload, timestamp: ts2 }, * ]); * // chain.length === 2 * // chain[1].previous === chain[0].hash * ``` */ export declare const buildChain: (entries: ReadonlyArray<{ kind: string; subject: ReceiptSubject; payload: TypedRef.Shape; timestamp: HLC; }>) => Effect.Effect; /** * Validate a receipt chain: genesis link, hash integrity, chain continuity, HLC ordering. * * The ergonomic everyday check: resolves only to `true` and signals every * violation through the `Error` channel with a human-readable message. * * @example * ```ts * const chain = yield* Receipt.buildChain(entries); * const valid = yield* Receipt.validateChain(chain); * // valid === true * ``` * * @see validateChainDetailed for typed `ChainValidationError` handling. */ export declare const validateChain: (chain: ReadonlyArray, options?: ChainValidationOptions) => Effect.Effect; /** * Validate a receipt chain with detailed, structured error reporting. * * The typed taxonomy for programmatic handling: returns `true` on success * or fails with a `ChainValidationError` discriminated union * (not_genesis | hash_mismatch | chain_break | hlc_not_increasing). * * @example * ```ts * import { Effect } from 'effect'; * * const result = yield* Effect.either(Receipt.validateChainDetailed(chain)); * // result._tag === 'Right' on success * // result._tag === 'Left' with .left.type on failure * ``` * * @see validateChain for the simple Error-channel form. */ export declare const validateChainDetailed: (chain: ReadonlyArray, options?: ChainValidationOptions) => Effect.Effect; /** * Check whether a receipt envelope is a genesis (root) envelope. * * @example * ```ts * const chain = yield* Receipt.buildChain(entries); * Receipt.isGenesis(chain[0]); // true * Receipt.isGenesis(chain[1]); // false * ``` */ export declare const isGenesis: (receipt: ReceiptEnvelope) => boolean; /** * Get the last (most recent) envelope in a chain. * * @example * ```ts * const latest = Receipt.head(chain); * // latest === chain[chain.length - 1] * ``` */ export declare const head: (chain: ReadonlyArray) => ReceiptEnvelope | undefined; /** * Get the first (genesis) envelope in a chain. * * @example * ```ts * const first = Receipt.tail(chain); * // first === chain[0] * ``` */ export declare const tail: (chain: ReadonlyArray) => ReceiptEnvelope | undefined; /** * Append a new entry to an existing chain, auto-linking to the previous hash. * * Optionally accepts explicit previous hashes for merge envelopes. * * @example * ```ts * const chain = yield* Receipt.buildChain([entry1]); * const extended = yield* Receipt.append(chain, { * kind: 'update', subject: { type: 'effect', id: 'a' }, payload, timestamp: ts2, * }); * // extended.length === 2 * ``` */ export declare const append: (chain: ReadonlyArray, entry: { kind: string; subject: ReceiptSubject; payload: TypedRef.Shape; timestamp: HLC; }, previousHashes?: readonly string[]) => Effect.Effect; /** * Find an envelope in a chain by its content hash. * * @example * ```ts * const found = Receipt.findByHash(chain, targetHash); * // found?.hash === targetHash * ``` */ export declare const findByHash: (chain: ReadonlyArray, hash: string) => ReceiptEnvelope | undefined; /** * Find all envelopes in a chain matching a given kind. * * @example * ```ts * const updates = Receipt.findByKind(chain, 'update'); * // updates contains all envelopes with kind === 'update' * ``` */ export declare const findByKind: (chain: ReadonlyArray, kind: string) => ReceiptEnvelope[]; /** * Generate an HMAC-SHA-256 key for signing receipt envelopes. * * @example * ```ts * const key = yield* Receipt.generateMACKey(); * const signed = yield* Receipt.macEnvelope(envelope, key); * // signed.signature is a hex string * ``` */ export declare const generateMACKey: () => Effect.Effect; /** * Sign a receipt envelope with an HMAC key, adding a `signature` field. * * @example * ```ts * const key = yield* Receipt.generateMACKey(); * const signed = yield* Receipt.macEnvelope(envelope, key); * // signed.signature !== undefined * ``` */ export declare const macEnvelope: (envelope: ReceiptEnvelope, key: CryptoKey) => Effect.Effect; /** * Verify an envelope's HMAC signature against a key. * * Returns false if the envelope has no signature. * * @example * ```ts * const valid = yield* Receipt.verifyMAC(signedEnvelope, key); * // valid === true if signature matches * ``` */ export declare const verifyMAC: (envelope: ReceiptEnvelope, key: CryptoKey) => Effect.Effect; /** * Receipt namespace -- chain validation and envelope construction. * * Build, validate, append, query, and sign linear receipt chains. * Each envelope is content-addressed and linked to its predecessor. * Supports HMAC signing/verification for tamper detection. * * @example * ```ts * import { Effect } from 'effect'; * import { Receipt, HLC } from '@czap/core'; * * const program = Effect.gen(function* () { * const ts = HLC.increment(HLC.create('node-1'), Date.now()); * const chain = yield* Receipt.buildChain([ * { kind: 'init', subject: { type: 'effect', id: 'a' }, payload, timestamp: ts }, * ]); * const valid = yield* Receipt.validateChain(chain); * const latest = Receipt.head(chain); * }); * ``` */ export declare const Receipt: { GENESIS: string; createEnvelope: (kind: string, subject: ReceiptSubject, payload: TypedRef.Shape, timestamp: HLC, previousHash: string | readonly string[]) => Effect.Effect; buildChain: (entries: ReadonlyArray<{ kind: string; subject: ReceiptSubject; payload: TypedRef.Shape; timestamp: HLC; }>) => Effect.Effect; validateChain: (chain: ReadonlyArray, options?: ChainValidationOptions) => Effect.Effect; validateChainDetailed: (chain: ReadonlyArray, options?: ChainValidationOptions) => Effect.Effect; hashEnvelope: (envelope: ReceiptEnvelope) => Effect.Effect; isGenesis: (receipt: ReceiptEnvelope) => boolean; head: (chain: ReadonlyArray) => ReceiptEnvelope | undefined; tail: (chain: ReadonlyArray) => ReceiptEnvelope | undefined; append: (chain: ReadonlyArray, entry: { kind: string; subject: ReceiptSubject; payload: TypedRef.Shape; timestamp: HLC; }, previousHashes?: readonly string[]) => Effect.Effect; findByHash: (chain: ReadonlyArray, hash: string) => ReceiptEnvelope | undefined; findByKind: (chain: ReadonlyArray, kind: string) => ReceiptEnvelope[]; generateMACKey: () => Effect.Effect; macEnvelope: (envelope: ReceiptEnvelope, key: CryptoKey) => Effect.Effect; verifyMAC: (envelope: ReceiptEnvelope, key: CryptoKey) => Effect.Effect; }; export declare namespace Receipt { /** Alias for {@link ReceiptSubject}. */ type Subject = ReceiptSubject; /** Alias for {@link ReceiptEnvelope}. */ type Envelope = ReceiptEnvelope; /** Alias for {@link ChainValidationError}. */ type ChainError = ChainValidationError; } //# sourceMappingURL=receipt.d.ts.map