/** * EVO-13 P1 — ingestion trust boundary for an instance mirror (DEC-125). * * Reuses the EVO-11 verification primitives (`verifyEnvelopeSignature` + * `H2AReplayGuard`). Authority is **possession of a key the operator enrolled**, * never a self-declared instance id and never a shared bearer token (the Opus * review rejected both). Two safeguards, both keyed on the VERIFIED signing key: * 1. the envelope must verify against a registry key for the signer OR an * operator-enrolled key (`enrolledKeys`) — an unknown key is refused (no * trust-on-first-use across the remote boundary); * 2. a registration is applied only if its `publicKeys` contains the verified * key — an agent can mirror ONLY its own identity (no namespace squatting). * * Never throws on rejection; only the caller-supplied `applyRegistration` may. * * ── INGEST BOUNDARY (2026-07-25) ─────────────────────────────────────────── * * Verification says the bytes came from a key we trust. It says nothing about * what is IN them, and a trusted sender running an older CLI sends raw records — * `workspace.path`, `launchContext`, `pid`, `file://` endpoints. So every record * is narrowed by `ingest.ts` (which reuses the SEND plans) before it reaches a * caller's apply callback. * * That narrowing is made structural by the callback TYPES: they take * `H2AMirrored*`, not the local record types, so no caller of this function can * be handed an unnarrowed record even by writing its callback carelessly. Same * instrument as `sanitizeActorForMirror` on the send side — the raw value is not * in scope at the point it would be used, so "it cannot leak here" is a * consequence of the signature rather than a claim in a comment. * * Ordering, since a signed payload forces it: narrowing is strictly AFTER * signature verification (narrowing first would alter the signed bytes and every * push would fail), and strictly after the `publicKeys` authorization filter, so * it can never change an authorization outcome. See `ingest.ts` for the full * argument and for what this consequently does NOT claim. */ import { type H2AReplayGuard } from "@sentropic/h2a"; import { type H2AMirroredRegistration, type H2AMirroredSession, type H2AMirroredSubagentBinding, type MirrorNarrowingReport } from "./ingest.js"; export type H2AMirrorRejection = "malformed" | "not-mirror" | "no-signature" | "unauthorized-key" | "bad-signature" | "invalid-timestamp" | "expired" | "future" | "replayed" | "stale-sequence" | "instance-key-mismatch"; export type H2AMirrorResult = { ok: true; applied: string[]; signer: string; /** * What the ingest boundary removed from this push. `records: 0` means the * sender already sends narrowed records — i.e. it is running a CLI with the * send boundary. Anything higher is an un-upgraded sender, and this is the * signal that makes that visible instead of silent. */ narrowed: MirrorNarrowingReport; } | { ok: false; reason: H2AMirrorRejection; }; export interface AcceptMirrorOptions { /** Active public-key PEMs already registered for the signer (registry + keyring, minus revoked). */ resolvePublicKeys: (signerInstance: string) => string[]; /** Operator-enrolled key PEMs allowed to mirror/bootstrap (out-of-band trust, not a wire token). */ enrolledKeys: readonly string[]; /** Replay guard (DEC-074) — its freshness window also enforces timestamp checks. */ guard: H2AReplayGuard; /** * Apply an authorized registration to the local (remote-side) store. * * Receives the NARROWED row (`H2AMirroredRegistration`), never the raw one — * see the ingest-boundary note in the module header. The type is the boundary. */ applyRegistration: (registration: H2AMirroredRegistration) => void; /** * P2: apply a presence session (the impl RE-STAMPS heartbeatAt with the remote * clock so freshness derives from the beat). Only called for sessions whose * instance is owned by the verified key. Omit to ignore presence (P1 behavior). * * Receives the NARROWED record (`H2AMirroredSession`), never the raw one. */ applyPresence?: (session: H2AMirroredSession) => void; /** * P2 fencing: return true iff `seq` is strictly newer than the last applied * for `signerInstance` (and record it). Return false to reject a stale/replayed * beat. Omit to skip fencing (P1 behavior). */ fenceSequence?: (signerInstance: string, seq: number) => boolean; /** * P3: apply a subagent binding (the impl is idempotent — a known binding is a * no-op). Only called for bindings whose `parentInstance` is owned by the * verified key. Omit to ignore subagents (P1/P2 behavior). * * Receives the NARROWED binding (`H2AMirroredSubagentBinding`). */ applySubagent?: (binding: H2AMirroredSubagentBinding) => void; /** Reference time (ms epoch) for the guard. Defaults to `Date.now()`. */ now?: number; } export declare function acceptMirrorEnvelope(payload: unknown, options: AcceptMirrorOptions): H2AMirrorResult; //# sourceMappingURL=accept.d.ts.map