/** * Revocation log — an append-only, hash-chained sequence of revocations. * * The `RevocationRegistry` is a flat set: tamper-evident ONLY on individual * revocation signatures. An operator who holds a registry can drop entries * before exporting it, and the consumer has no way to detect the omission. * * `RevocationLog` closes audit limit #2: every appended event links to the * previous one via a hash chain, and the operator's current head can be * committed to via a signed `LogHead`. Once two peers exchange signed heads, * either party can later prove the other omitted or reordered events by * showing the original signed head alongside their own chain. * * Three properties guaranteed: * 1. No silent drops within a chain — gaps break the hash linkage. * 2. No silent reordering — sequence numbers are monotonic & checked. * 3. Accountable commitment — operators sign their heads; divergence is * provable with the old signature in hand. * * This module does NOT handle distribution (gossip / pub-sub). That is a * transport concern and is addressed in the design doc for limit #1. */ import { type CryptoProvider } from '../core/crypto-provider.js'; import { type AuthorityResolver, type RevocationEvent } from './revocation.js'; /** One link in the revocation log. */ export interface RevocationLogEntry { sequence: number; previousHash: string; revocation: RevocationEvent; hash: string; } /** Signed commitment to the current head of a log. */ export interface LogHead { /** Sequence number of the most recent entry (length - 1). Empty logs = -1. */ sequence: number; /** Hash of the most recent entry, or genesis hash if empty. */ hash: string; /** When the operator signed this head. */ signedAt: number; /** DID of the operator (signer). */ operatorDid: string; /** Operator's base64 public key. */ operatorPublicKey: string; /** Ed25519 signature over the canonical payload. */ signature: string; } export type LogVerification = { valid: true; } | { valid: false; reason: string; }; /** * Append-only, hash-chained revocation log. * * **Threat model.** The log is a relay / audit layer. It verifies that each * appended revocation is *cryptographically valid* (signature, DID binding, * no duplicates) but does not by default enforce *authority* — that is, it * does not check "was this signer entitled to revoke this target?". Authority * is enforced at the {@link RevocationRegistry} layer, which is what actually * gates verification decisions. This split exists because gossip peers relay * events for delegations whose original issuer they may not know locally. * * Callers that do want authority enforcement at the log layer (e.g. the * operator's own private log, where authority is always the operator) can * pass an `authority` resolver at construction or use * {@link registerAuthority}. When authority information is present for a * target, the log enforces it; when absent, the log relays without checking. */ export declare class RevocationLog { private readonly entries; private readonly seenTargets; private readonly provider; private readonly authority?; private readonly internalAuthority; /** Genesis hash (depends on active hash algorithm). */ readonly genesisHash: string; constructor(opts?: { provider?: CryptoProvider; /** * Optional authority resolver. When provided (or when authorities * are added via {@link registerAuthority}), {@link append} enforces * authority for targets with known authority records. Targets with * no known authority still append (relay semantics). */ authority?: AuthorityResolver; }); /** * Register the legitimate issuer for a target so that later appends for * that target are authority-checked. Targets with no registered authority * still relay unchecked. */ registerAuthority(targetId: string, issuerDid: string): void; /** * Append a revocation to the log. Throws if: * - the revocation signature is invalid * - the target has a known authority and the revocation's issuerDid * does not match it * - the same target was already revoked in this log */ append(revocation: RevocationEvent): RevocationLogEntry; /** Current head hash (genesis hash for empty logs). */ get head(): string; /** Number of entries in the log. */ get length(): number; /** Is this target revoked? */ isRevoked(targetId: string): boolean; /** Get a read-only snapshot of all entries. */ getEntries(): readonly RevocationLogEntry[]; /** Produce a signed commitment to the current head. */ signHead(operatorSigningKey: Uint8Array, operatorPublicKey: Uint8Array): LogHead; /** Verify chain integrity of this log. */ verify(): LogVerification; /** * Replace this log's contents with an imported set of entries. Rejects * (and leaves log untouched) if the imported chain does not verify. * Use this for bootstrapping from a trusted peer. In steady state, prefer * `merge()` which preserves existing entries. */ replaceWith(entries: RevocationLogEntry[]): LogVerification; /** * Verify a standalone sequence of log entries. Checks: * 1. Each entry's revocation signature * 2. Monotonic sequence numbers starting at 0 * 3. Hash chain linkage (previousHash matches prior entry) * 4. Each entry's hash is correctly computed * 5. No duplicate targetIds */ static verifyEntries(entries: RevocationLogEntry[], provider?: CryptoProvider): LogVerification; /** Verify a signed log head — signature + DID consistency. */ static verifyHead(head: LogHead, provider?: CryptoProvider): LogVerification; } //# sourceMappingURL=revocation-log.d.ts.map