/** * ADR-111 Phase 5 — Witness attestation chain for WG mesh mutations. * * Every coordination change (peer added/suspended/evicted/reactivated, * keypair rotated) appends a signed entry to an append-only log * `.claude-flow/federation/wg-changes.log`. The signature is over a * canonical JSON encoding of the entry's content fields, using the * operator's federation Ed25519 key — the same identity that signs * federation manifests, so anyone who already trusts the manifest chain * automatically trusts the WG change chain. * * Append-only via O_APPEND open + entries chained by prevHash (sha256 of * the previous entry's canonical bytes). Tampering with history requires * forging a chain of signatures with the operator's key. * * The periodic ruflo witness regen (plugins/ruflo-core/scripts/witness/) * includes wg-changes.log as one of its watched artifacts (operator wires * this via witness-fixes.json). * * Pure service: emits the canonical bytes and the signature to write. * Caller does the I/O so this stays unit-testable without fs mocks. */ import type { WgCommand } from './wg-mesh-service.js'; export type WgWitnessEventType = 'peer-added' | 'peer-removed-suspended' | 'peer-restored' | 'peer-evicted' | 'key-rotated' | 'interface-config-applied'; /** * Canonical fields of a witness entry. The hash + signature are computed * over these in a stable JSON encoding (sorted keys, no whitespace). */ export interface WgWitnessContent { readonly version: '1'; readonly type: WgWitnessEventType; readonly timestamp: string; readonly nodeId: string; /** Public key of the affected peer, if any. */ readonly peerPublicKey?: string; /** Mesh IP affected, if any. */ readonly meshIP?: string; /** wg command that was emitted, if any. */ readonly wgCommand?: string; /** Human-readable rationale for the audit log. */ readonly rationale: string; /** Hash of the previous entry in the chain (sha256 hex), or empty for the genesis entry. */ readonly prevHash: string; } /** * Full witness entry as it appears on disk — content + the operator's * Ed25519 signature over the canonical encoding of content. */ export interface WgWitnessEntry { readonly content: WgWitnessContent; readonly hash: string; readonly signature: string; } export interface WgWitnessSigner { /** Returns an Ed25519 signature (base64) over the given canonical bytes. */ sign(bytes: Buffer): Promise; } /** * Stable JSON serialization for hashing/signing. Keys are sorted; no * whitespace; undefined fields omitted. Same shape on every host so * cross-host verifiers produce identical hashes. */ export declare function canonicalizeContent(content: WgWitnessContent): Buffer; export declare function hashContent(content: WgWitnessContent): string; export declare class WgWitnessService { private readonly nodeId; private readonly signer; private lastHash; constructor(nodeId: string, signer: WgWitnessSigner); /** * Override the previous-hash pointer (used when resuming from an existing * log on disk). Caller reads the last entry from `wg-changes.log` and * passes its `hash` here before appending more. */ setLastHash(hash: string): void; /** Build + sign an entry. Caller is responsible for the actual append-write. */ build(type: WgWitnessEventType, fields: Omit): Promise; /** * Convenience: take a WgCommand emitted by WgMeshService Phase 2/3 and * produce a witness entry of the right type. Maps verb → eventType. */ attestWgCommand(cmd: WgCommand, meshIP?: string): Promise; } /** * Verify a single entry's signature + hash. Caller verifies the chain by * iterating entries and confirming each entry's `prevHash` equals the * preceding entry's `hash`. */ export declare function verifyWitnessEntry(entry: WgWitnessEntry, verify: (bytes: Buffer, signature: string) => Promise): Promise; /** * Verify a full chain — every entry valid + chain link unbroken. */ export declare function verifyWitnessChain(entries: readonly WgWitnessEntry[], verify: (bytes: Buffer, signature: string) => Promise): Promise<{ ok: boolean; failedAt?: number; reason?: string; }>; //# sourceMappingURL=wg-witness-service.d.ts.map