/** * Mailbox-first forwarding and polling support. * * KERIpy correspondence: * - ports the conceptual roles of `Poster`, `StreamPoster`, `ForwardHandler`, * and mailbox polling/query handling from `keri.app.forwarding` and * `keri.app.indirecting` * * Current `keri-ts` difference: * - delivery and polling are synchronous Effection operations instead of HIO * doers * - sender retry durability is optional via `Outboxer` * - HTTP posting honors both KERIpy header mode and the Tufa-only body mode */ import { type Operation } from "effection"; import { SerderKERI } from "../../../cesr/mod.js"; import type { Mailboxer } from "../db/mailboxing.js"; import type { OutboxerLike } from "../db/outboxing.js"; import type { ExchangeAttachment, ExchangeRouteHandler } from "./exchanging.js"; import type { Hab, Habery } from "./habbing.js"; import type { MailboxDirector } from "./mailbox-director.js"; import { type MailboxSseMessage } from "./mailbox-sse.js"; import { Organizer } from "./organizing.js"; import { type RuntimeServices } from "./runtime-services.js"; /** Delivery preference accepted by CLI and runtime exchange send helpers. */ export type ExchangeDeliveryPreference = "auto" | "direct" | "indirect"; /** Result summary for one EXN send attempt across all resolved destinations. */ export interface ExchangeSendResult { serder: SerderKERI; deliveries: string[]; queued: string[]; } /** Result summary for one raw CESR delivery attempt. */ export interface RawCesrSendResult { deliveries: string[]; queued: string[]; } interface PosterOptions { mailboxer?: Mailboxer | null; outboxer?: OutboxerLike; services?: RuntimeServices; } interface MailboxPollerOptions { timeouts?: Partial; services?: RuntimeServices; pollTransport?: MailboxPollTransport; } /** * Mailbox-first postman for outbound EXN transport. * * Responsibilities: * - resolve recipient aliases against organizer/contact state * - route to all currently authorized recipient mailbox endpoints when present * - fall back to direct controller/agent endpoints, then one witness endpoint, * when no mailbox exists * - persist sender-side mailbox retry state per mailbox endpoint */ export declare class Poster { readonly hby: Habery; readonly mailboxer: Mailboxer | null; readonly outboxer: OutboxerLike; readonly organizer: Organizer; readonly services: RuntimeServices; constructor(hby: Habery, options?: PosterOptions); /** * Resolve one CLI or user recipient input into the actual destination prefix. * * Resolution order matches the mailbox CLI mental model: * - exact known prefix wins * - otherwise exact organizer alias lookup is required */ resolveRecipient(recipient: string): string; /** * Send one EXN through the mailbox-first delivery policy. * * Default policy: * - if recipient mailboxes exist, broadcast to all mailbox endpoints * - if no mailbox exists, send directly to controller/agent endpoints * - if no direct endpoints exist, fall back to one witness endpoint * - failed mailbox deliveries are queued durably for retry * * Current `keri-ts` difference: * - KERIpy `Poster` is queue/doer based * - here the caller drives one explicit operation and gets a structured * result back immediately */ sendExchange(hab: Hab, args: { recipient: string; route: string; payload: Record; topic?: string; exchangeRecipient?: string | null; modifiers?: Record; date?: string; dig?: string; embeds?: Record; delivery?: ExchangeDeliveryPreference; }): Operation; /** * Send one raw CESR message through the same mailbox-first transport policy. * * This is used by delegation workflows where correctness depends on the * actual KEL bytes reaching the delegator or delegate, not on an EXN wrapper. */ sendBytes(hab: Hab, args: { recipient: string; message: Uint8Array; topic?: string; delivery?: ExchangeDeliveryPreference; split?: boolean; }): Operation; /** * Retry any pending mailbox-target deliveries using current recipient state. * * Retry behavior is per mailbox endpoint, not per logical message. A removed * mailbox cancels only that endpoint's target row. */ processPending(): Operation; /** Return whether any sender retry work still remains. */ hasPendingWork(): boolean; /** Cancel all pending retry targets for one mailbox AID. */ cancelMailbox(eid: string): void; /** * Deliver one message to one mailbox endpoint. * * Delivery modes: * - if the mailbox AID is local to this habery, store directly in * `Mailboxer` * - otherwise wrap in `/fwd` and post to the remote mailbox URL */ private deliverMailboxTarget; /** * Deliver one message using the recipient's witness as a store-and-forward * fallback when no mailbox or direct endpoint exists. */ private deliverWitnessTarget; /** * Deliver one `/fwd`-wrapped payload to a mailbox or witness endpoint. * * Local hosted targets store directly in `Mailboxer`; remote targets receive * KERIpy-style introduction material ahead of the forwarded payload. */ private deliverForwardedTarget; /** Require provider mailbox storage for local mailbox-target delivery. */ private requireMailboxer; } /** * Accepted `/fwd` route handler that stores embedded payloads in mailbox topics. * * This is the `keri-ts` mailbox-storage analogue to KERIpy's `ForwardHandler`. * * Authorization rule: * - the request-scoped mailbox AID set by `MailboxDirector` must currently be * authorized for the addressed recipient */ export declare class ForwardHandler implements ExchangeRouteHandler { static readonly resource = "/fwd"; readonly resource = "/fwd"; readonly mailboxDirector: MailboxDirector; constructor(mailboxDirector: MailboxDirector); /** * Verify that the embedded exchange has the mailbox modifiers and one * extractable forwarded payload. */ verify(args: { serder: SerderKERI; attachments: ExchangeAttachment[]; }): boolean; /** * Store one forwarded payload when the receiving host is allowed to act as * the recipient's async correspondence store. * * Accepted host shapes: * - explicit mailbox provider authorized in `ends.` * - recipient witness host from the accepted KEL witness set * - recipient agent host authorized in `ends.` * * Controller endpoints stay excluded here because they are direct * correspondence destinations, not store-and-forward hosts. */ handle(args: { serder: SerderKERI; attachments: ExchangeAttachment[]; }): void; } /** * Local/remote mailbox consumer for forwarded EXN topics. * * Current scope: * - replays locally stored mailbox topics for the active habitat prefix * - polls configured remote mailbox or witness endpoints with `mbx` queries * - ingests retrieved payloads back through the shared `Reactor` */ export declare class MailboxPoller { static readonly DefaultTimeoutPolicy: Readonly; static readonly ReadIdleTimeoutMs = 500; readonly hby: Habery; readonly mailboxDirector: MailboxDirector; readonly timeoutPolicy: Readonly; readonly services: RuntimeServices; readonly pollTransport: MailboxPollTransport; private readonly localCursors; constructor(hby: Habery, mailboxDirector: MailboxDirector, options?: MailboxPollerOptions); /** Configure mailbox polling state for future expansion; currently a no-op. */ configure(_args?: { hab?: Hab; }): void; /** Register one mailbox topic that this poller should retrieve. */ registerTopic(topic: string): void; /** * Drain one polling turn: * - replay locally hosted mailbox traffic first * - then query remote mailbox or witness endpoints using `mbx` * * The returned batches preserve per-source boundaries so callers can ingest * one local or remote result set and run follow-on escrow/cue work before * moving to the next source. */ processOnce({ budgetMs, }?: { budgetMs?: number; }): Operation; /** * Poll forever, streaming typed batches into one sink. * * Long-lived runtime polling stays callback-based because concurrent remote * workers do not have a natural finite return value. */ pollDo(onBatch: (batch: MailboxPollBatch) => void): Operation; /** * Read locally stored mailbox traffic from the shared `Mailboxer`. * * Local cursors are in-memory only because they are scoped to the current * runtime process, unlike remote mailbox progress tracked in `tops.`. */ private readLocalMailbox; /** Return the in-memory local mailbox cursor map for one local prefix. */ private localCursor; /** * Poll one remote mailbox or witness endpoint once and return one batch. * * Timeout ownership: * - bounded callers pass their remaining command-local budget * - long-lived workers pass the normal mailbox long-poll duration */ private pollRemoteEndpointOnce; /** Keep one long-lived worker running for each currently configured remote endpoint. */ private syncRemoteWorkers; /** * Poll one remote endpoint continuously in the long-lived runtime shape. * * This keeps KERIpy's "one poller per endpoint" behavior while preserving * `keri-ts`'s split between mailbox transport and runtime parser ownership. */ private remoteEndpointWorker; private remotePollTargets; private pollSignerHab; } /** * Return the KERIpy-style introduction stream for one remote endpoint. * * This bootstrap material is intentionally broader than a bare endpoint reply: * - sender KEL replay * - delegation chain when present * - endpoint/location replies for later correspondence * * It is only sent when local DB state does not already show a receipt from the * remote endpoint for the sender's latest event. */ export declare function introduce(hab: Hab, remote: string): Uint8Array; /** * Public helper for deriving mailbox topic defaults from one EXN route. * * This stays exported because CLI and runtime call sites both need one shared * answer for "which mailbox topic should this route land in by default?" */ export declare function mailboxTopicForRoute(route: string): string; /** Explicit mailbox polling timeout policy for one runtime poller. */ export interface MailboxPollingTimeoutPolicy { /** Abort the HTTP request when no response headers arrive before this deadline. */ requestOpenTimeoutMs: number; /** Stop reading one mailbox SSE response after this long-poll window. */ maxPollDurationMs: number; /** Bound one command-local polling turn across sequential remote endpoints. */ commandLocalBudgetMs: number; } /** One mailbox retrieval batch whose message boundaries should stay together. */ export interface MailboxPollBatch { source: "local" | "remote"; pre: string; eid?: string; messages: Uint8Array[]; } export interface MailboxFetchTimeoutPolicy { requestOpenTimeoutMs: number; maxReadDurationMs: number; readIdleTimeoutMs: number; } export interface MailboxPollTransportRequest { hab: Hab; targetPre: string; endpoint: { eid: string; url: string; }; topics: Record; bodyMode: "header" | "body"; timeouts: MailboxFetchTimeoutPolicy; services: RuntimeServices; } export interface MailboxPollTransport { poll(args: MailboxPollTransportRequest): Operation; } /** * Post mailbox or exchange CESR traffic to one endpoint. * * Header-mode policy: * - split a combined CESR stream into one request per message * - send the message body in the HTTP body * - send that message's attachments in `CESR-ATTACHMENT` * * Body-mode policy: * - send the whole provided payload in the request body */ export declare function postCesrMessage(url: string, body: Uint8Array, bodyMode: "header" | "body", destination?: string, services?: RuntimeServices, options?: { split?: boolean; }): Operation; /** Shared EXN send helper used by CLI commands that want KERIpy-style behavior. */ export declare function sendExchangeMessage(hby: Habery, hab: Hab, args: { recipient: string; route: string; payload: Record; topic?: string; modifiers?: Record; date?: string; dig?: string; embeds?: Record; delivery?: ExchangeDeliveryPreference; }): Operation; export {}; //# sourceMappingURL=forwarding.d.ts.map