/** * Revocation — signed events that nullify previously-issued credentials. * * Revocation is the counterpart to lineage/delegation: when a heart issues a * lineage certificate or delegation, it retains the right to revoke it. A * revocation event is a signed declaration that a specific credential should * no longer be honored, with an optional reason code. * * Revocations are signed by the ORIGINAL ISSUER, not the holder. Only the * party that created a credential can revoke it — this preserves the trust * chain (if A delegated to B, only A can revoke; B cannot "revoke A's grant"). * * Propagation is out of band: producers publish revocation events to a feed, * consumers subscribe and check each use. In the simplest deployment, a heart * keeps a local registry and consults it before accepting any credential. * * A revocation is irreversible — once published, the target is dead forever. * If you want temporary disablement, use short TTLs instead. */ import { type CryptoProvider } from '../core/crypto-provider.js'; import { type DidMethodRegistry } from '../core/did-method.js'; import { type HistoricalKeyLookup } from './historical-key-lookup.js'; export type RevocationReason = 'compromised' | 'rotated' | 'completed' | 'expired-early' | 'abuse' | 'unknown'; /** Kind of credential being revoked. */ export type RevocationTarget = 'lineage' | 'delegation' | 'heart'; /** A signed revocation — the issuer declares a credential dead. */ export interface RevocationEvent { /** Opaque ID for this revocation event itself. */ id: string; /** The ID of the thing being revoked. */ targetId: string; /** What kind of credential is being revoked. */ targetKind: RevocationTarget; /** DID of the issuer (who has authority to revoke). */ issuerDid: string; /** Reason for revocation. */ reason: RevocationReason; /** Optional free-form detail (e.g. incident ID). */ detail?: string; /** When this revocation was issued. */ issuedAt: number; /** Random nonce to prevent replay. */ nonce: string; /** Issuer's Ed25519 signature over the payload. */ signature: string; /** Issuer's public key (base64). */ issuerPublicKey: string; } export declare function createRevocation(opts: { targetId: string; targetKind: RevocationTarget; issuerDid: string; issuerPublicKey: string; issuerSigningKey: Uint8Array; reason?: RevocationReason; detail?: string; provider?: CryptoProvider; }): RevocationEvent; export type RevocationVerification = { valid: true; } | { valid: false; reason: string; }; /** * Verify a revocation event's signature + integrity. * * When `lookup` is provided, additionally confirms that the issuer's * public key was effective at the revocation's `issuedAt` timestamp * via the rotation subsystem. Fail-closed if the key is not found or * was not effective. When omitted, existing behavior is preserved. */ export declare function verifyRevocation(rev: RevocationEvent, provider?: CryptoProvider, registry?: DidMethodRegistry, lookup?: HistoricalKeyLookup): RevocationVerification; /** * Resolves the DID that has legitimate authority to revoke a given target. * Typically backed by the delegation/lineage store that originally issued * the target. Return `undefined` if the target is unknown to the resolver; * fail-closed semantics apply (unknown-authority revocations are rejected). */ export type AuthorityResolver = (targetId: string, targetKind: RevocationTarget) => string | undefined; /** * In-memory registry of revocations. Subscribers feed events in, consumers * ask `isRevoked()` before honoring any credential. Persistence is the * caller's job — the registry exposes import/export for that. * * **Authority enforcement.** `add()` verifies that the revocation's * `issuerDid` matches the expected issuer for the target. The expected * issuer is supplied either as an explicit argument to `add()` or via an * `authority` resolver configured at construction time. Unknown-authority * revocations are rejected by default (fail-closed). This closes the * "any fresh key can revoke any delegation" hole. */ export declare class RevocationRegistry { private readonly byTarget; private readonly provider; private readonly authority?; private readonly internalAuthority; constructor(opts?: { provider?: CryptoProvider; /** * Resolver consulted when `add()` is called without an explicit * expected issuer DID. If omitted and no internal authority is * registered via {@link registerAuthority}, such calls fail closed. */ authority?: AuthorityResolver; }); /** * Register the legitimate issuer for a target. Called by the code that * creates the delegation/lineage/heart credential being tracked, so that * when a revocation arrives the registry can cross-check authority. */ registerAuthority(targetId: string, issuerDid: string): void; /** * Add a revocation event. The revocation is accepted only if: * 1. its signature verifies against the declared issuer public key * 2. its `issuerDid` equals the expected authority for the target * 3. the target has not already been revoked in this registry * * The expected issuer is resolved in this order: * - `expectedIssuerDid` argument (if provided) * - internal authority map (populated via `registerAuthority`) * - `authority` resolver supplied at construction time * If none resolves a value, the revocation is rejected (fail-closed). * * Returns `{ accepted: true }` on success, `{ accepted: false, reason }` * otherwise. The reason is a short tag suitable for logging. */ add(event: RevocationEvent, expectedIssuerDid?: string): { accepted: boolean; reason?: string; }; /** Is this target ID revoked? */ isRevoked(targetId: string): boolean; /** Look up the revocation for a target, if any. */ get(targetId: string): RevocationEvent | undefined; /** Number of revocations held. */ get size(): number; /** Export all events (for persistence / sync). */ export(): RevocationEvent[]; /** * Bulk import events (e.g. from a feed). Returns count accepted. Each * event is resolved via the registry's configured authority (internal * map or constructor resolver). Events whose authority is unknown are * rejected. */ import(events: RevocationEvent[]): number; /** Clear the registry (testing only — revocations are forever in production). */ clear(): void; } //# sourceMappingURL=revocation.d.ts.map