/** * routes/email-composition.ts, the daemon's own mailbox. * * Adapts the platform `EmailService` onto the `EmailGatewayService` slice the * `email.*` verb handlers are written against. Assembled here rather than in * the runtime composition root so one property stays visible: the daemon reads * its mail settings and its password from the DAEMON tier and nowhere else, so * a setup performed in any surface is usable here the moment it lands and * stays usable after that surface exits. * * Returns `null` when the composition is too narrow to reach a real store, so * the verbs stay unregistered rather than half-wired and answering 500s. * * Three layers, in order, so each is one idea: * * 1. `createServiceBackedGateway`, the mapping onto `EmailService`, and the * one place its errors become honest statuses. `EmailService` throws * plain-language `Error`s, "Email is not enabled", "Email config is * invalid", an IMAP/SMTP refusal, and collapsing all of them into 500 * would report the operator's own unfinished setup as a server fault. * 2. `instrumentEmailGateway`, unfinished setup reported in the operator's * own key names, and an operational log line per verb. * 3. `createDaemonEmailGatewayService`, which of those a given composition * gets. * * Every address in a log field is a digest. See `email/address-digest.ts` for * why that is not optional. */ import { EmailService, type EmailServiceDeps } from '../../email/index.js'; import { type UntrustedContentLedger } from '../../security/untrusted-content.js'; import type { SurfaceEmailConfigProblem } from '../../email/surface-config.js'; import type { GatewayMethodCatalog } from '../method-catalog.js'; import type { EmailGatewayService } from './email.js'; /** * One operational log line about a mail verb. * * Addresses arrive here already reduced to a digest, see `address-digest.ts` * for why, and note that this signature makes it awkward to pass anything else: * the fields are scalars a log can hold, not a message a log should not. */ export type EmailGatewayLog = (event: string, fields: Readonly>) => void; /** The slice of the verb-group deps this composition needs. */ export interface EmailCompositionDeps { /** Test seam: overrides the whole service, so no real socket is opened. */ readonly emailGateway?: EmailGatewayService | undefined; /** Everything `EmailService` needs. Absent in narrow compositions. */ readonly emailServiceDeps?: EmailServiceDeps | undefined; /** * The ledger a mailbox read is recorded into. * * Defaults to the process-wide one, the SAME ledger the daemon's browser * engine records page reads into (routes/browser-composition.ts). Sharing it * is the point: reading a stranger's page and reading a stranger's mail are * the same kind of exposure, and a send made afterwards discloses both from * one record rather than from two that cannot see each other. */ readonly untrustedContentLedger?: UntrustedContentLedger | undefined; /** * Why the mailbox is not usable yet, in the operator's own key names. * * Supplied when the settings come from the daemon's `surfaces.email.*` keys, * because the service's own validation reports `email.imapHost is required`, * a key that operator does not have and cannot set. Absent for a plain * `email.*` composition, where that wording is the correct wording. */ readonly describeEmailConfigProblem?: (() => Promise) | undefined; /** Operational log sink. Absent means these verbs log nothing at all. */ readonly emailLog?: EmailGatewayLog | undefined; } /** The `EmailGatewayService` slice, served by a platform `EmailService`. */ export declare function createServiceBackedGateway(service: EmailService): EmailGatewayService; export interface EmailGatewayInstrumentation { readonly describeEmailConfigProblem?: (() => Promise) | undefined; readonly emailLog?: EmailGatewayLog | undefined; } /** * Wrap a mail backend with the two things every caller of these verbs should * get regardless of what is behind them: unfinished setup reported in terms the * operator can act on, and a log line that says what happened without saying * who it happened with. */ export declare function instrumentEmailGateway(backend: EmailGatewayService, options: EmailGatewayInstrumentation): EmailGatewayService; export declare function createDaemonEmailGatewayService(deps: EmailCompositionDeps): EmailGatewayService | null; /** * Register the daemon's `email.*` verbs, with the owner identity the taint * exemption needs. * * Lives here rather than at the verb-group root so the whole mail composition *, service, instrumentation, and now the owner's own addresses, is decided * in one file. The addresses are read from the daemon's configuration and from * nowhere a message can reach; empty leaves the taint refusal in force rather * than guessing at an identity. See security/owner-identity.ts. */ export declare function registerDaemonEmailVerbs(catalog: GatewayMethodCatalog, deps: EmailCompositionDeps & { readonly configManager?: { get(key: never): unknown; } | undefined; }): void; //# sourceMappingURL=email-composition.d.ts.map