import type { SessionId } from '../../types/ids/index.js'; import { type RecordPointer, type SessionRecord } from '../../types/session/records.js'; /** * The hash chain of one session log. * * Every record names its predecessor by `prev`: seq, byte offset, byte length * (newline included) and the SHA-256 of exactly those bytes. Verifying a log * is walking it line by line and checking that each `prev` is the pointer of * the line before it. Moved here from `store/evidence/record-chain.ts`, whose * predecessor and text-link rules it keeps, under the session envelope's * names (`prev`, `prevText`). * * The rules: * * - Seq 1 is `session_started`, at offset 0, with `prev: null`. * - Every later record has `seq = previous seq + 1` and a `prev` equal, field * by field, to the previous line's pointer. A flipped byte in any line * changes that line's hash, so the NEXT record's `prev` no longer matches. * - Every record names the same session as the first. * - `gen` never decreases: a record written under an older lease after one * written under a newer lease is a split writer, not a log. * - `prevText`, when present, is a skip link to an earlier text-bearing * record: `null` only where `prev` is `null`, otherwise strictly before the * record, and equal to `prev` when it names the same seq. * * The last record has no successor to vouch for it. A reader that needs the * tail anchored passes the head it expects (an index row, a checkpoint's * `throughSha256`) and the read refuses a log that does not hold it. */ /** One record of a log, with the pointer to its own bytes. */ export interface SessionLogEntry { readonly record: SessionRecord; readonly pointer: RecordPointer; } export type SessionLogBreakReason = 'not-a-record' | 'bad-start' | 'seq-gap' | 'prev-mismatch' | 'session-mismatch' | 'gen-regressed' | 'bad-text-link' | 'anchor-mismatch'; /** A log that is not an unbroken chain. `seq` and `offset` name where the break is. */ export declare class SessionLogIntegrityError extends Error { readonly reason: SessionLogBreakReason; /** The seq the reader expected at the break. */ readonly seq: number; /** Byte offset of the line that broke the chain. */ readonly offset: number; readonly name = "SessionLogIntegrityError"; constructor(reason: SessionLogBreakReason, /** The seq the reader expected at the break. */ seq: number, /** Byte offset of the line that broke the chain. */ offset: number, message: string, options?: ErrorOptions); } /** Where a chain walk starts: the beginning of the log, or just after a known record. */ export interface ChainStart { /** The last verified record; `null` (the default) starts at offset 0. */ readonly head: RecordPointer | null; /** The session every record must name; learnt from seq 1 when omitted. */ readonly sessionId?: SessionId; /** The `gen` of the record at `head`. */ readonly gen?: number; } /** * Verifies a log one line at a time. Throws {@link SessionLogIntegrityError} * at the first line that breaks the chain; the state is then unchanged, so a * tolerant reader can stop there with everything before it intact. */ export declare class SessionLogChain { #private; constructor(start?: ChainStart); get head(): RecordPointer | null; get sessionId(): SessionId | undefined; get gen(): number; /** The byte offset the next line must start at. */ get end(): number; /** Verify one complete line (newline included) found at `offset`. */ accept(line: Uint8Array, offset: number): SessionLogEntry; } /** * Splits a byte stream into complete lines, carrying a partial line across * chunks. Whatever is left at the end with no newline is the torn tail. */ export declare class LineSplitter { #private; constructor(startOffset?: number); /** Bytes held after the last complete line. */ get pendingBytes(): number; push(chunk: Uint8Array): Generator<{ line: Uint8Array; offset: number; }>; } /** Random access to a log's bytes, as both backends provide it. */ export interface LogBytes { size(): Promise; read(offset: number, length: number): Promise; } /** * The last complete record of a log, read from its tail only: never the whole * log. Salvaged from `transcriptTail`. Returns `undefined` for an empty log, * or when the tail is torn or does not parse — the caller then scans. * * Unverified: the record's own `prev` is not checked against the line before * it. It bootstraps a head cheaply; a writer re-verifies before appending. */ export declare function readSessionLogTail(bytes: LogBytes): Promise<{ entry: SessionLogEntry; size: number; } | undefined>; /** * Check that the bytes at `pointer` are exactly the record it names. Used to * resume a walk from a cursor, and to anchor a tail. */ export declare function verifyPointer(bytes: LogBytes, pointer: RecordPointer): Promise; //# sourceMappingURL=chain.d.ts.map