/** * surface-config.ts, driving `EmailService` from the daemon's own mailbox keys. * * `EmailService` reads a flat `email.*` namespace (`email.imapHost`, * `email.passwordRef`, …). The daemon's own mailbox is configured under * `surfaces.email.*`, which is a different shape for a good reason: it sits in * the `surfaces.` domain so it inherits that domain's daemon-ownership rule, * the daemon is the process that acts on the mailbox, so the daemon tier is the * only home for its settings and its password. * * This module is the adapter between the two, and nothing else. It is a * translation of key names and precedence; no setting is invented here, and * `EmailService` is not modified to know about `surfaces.`. * * Both spellings are live and both are supported * ────────────────────────────────────────────── * Two spellings of the same mailbox reached the daemon by different routes: a * nested one (`surfaces.email.imap.host`) written by the settings surface, and * a flat one (`surfaces.email.imapHost`) read by the inbound mail poller. * Config in the field uses each. Refusing either would break a working setup, * so both resolve, in a fixed order: * * imap host imap.host → host → imapHost * imap port imap.port → imapPort → 993 * smtp host smtp.host → host * smtp port smtp.port → 465 * account user → username → imapUser * from from → the account name * mailbox imap.mailbox → INBOX * drafts imap.draftsMailbox → the server's own `\Drafts` folder * * The nested spelling and the shared `host` come first because that is the * order the mail handlers already resolved in: a machine that worked before * resolves to exactly the same host afterwards. The flat keys are a last * resort, which only ever turns a setup that previously could not send mail * into one that can. * * Passwords never come from config * ──────────────────────────────── * Every password is fetched from the secret store under the name the config key * derives (`daemonSecretKeyFor`), never read out of a settings file. The * fallbacks are the ones the mail handlers used: * * IMAP/shared surfaces.email.password → surfaces.email.imap.password * (which is also where surfaces.email.imapPassword lands) * SMTP surfaces.email.smtp.password → the shared chain above * * so a provider that issues one app password works with one setting, and a * provider that issues separate SMTP credentials works without forcing the * shared one to be wrong. */ import type { EmailServiceDeps } from './email-service.js'; export declare const SURFACE_EMAIL_PREFIX = "surfaces.email"; /** The `email.passwordRef` value that points at the daemon's shared mail password. */ export declare const SURFACE_EMAIL_PASSWORD_REF: string; /** The `email.smtpPasswordRef` value that points at the daemon's SMTP password. */ export declare const SURFACE_EMAIL_SMTP_PASSWORD_REF: string; export type ConfigReader = (key: string) => unknown; export interface SecretReader { get(key: string): Promise; } /** The resolved, non-secret half of the daemon's mailbox settings. */ export interface SurfaceEmailSettings { readonly imapHost: string | undefined; readonly imapPort: number; /** * `surfaces.email.imap.secure`. True, the default, and every hosted provider *, means implicit TLS on the IMAP port. False means a plain connection, which * is what a mail server on localhost or a fake in a test offers. */ readonly imapSecure: boolean; readonly smtpHost: string | undefined; readonly smtpPort: number; readonly smtpSecure: boolean; readonly username: string | undefined; readonly fromAddress: string | undefined; /** Mailbox to read. Absent means INBOX. */ readonly mailbox: string | undefined; /** Drafts folder. Absent means let the server's `\Drafts` flag decide. */ readonly draftsMailbox: string | undefined; } /** Resolve every non-secret mailbox setting, in the precedence documented above. */ export declare function readSurfaceEmailSettings(getConfig: ConfigReader): SurfaceEmailSettings; /** * A config reader that answers `email.*` from `surfaces.email.*`. * * Anything outside the `email.` namespace is passed through untouched, so the * same reader can back a service that also reads something else. * * `email.enabled` reports whether the mailbox is CONFIGURED. The daemon's own * mailbox has no separate enable switch, an operator who filled in a host, an * account and a password has enabled it, and asking them to also set a boolean * would only produce a mailbox that silently does nothing. */ export declare function createSurfaceEmailConfigReader(getConfig: ConfigReader): ConfigReader; /** * A secret reader that resolves the daemon's mail passwords through their * fallback chains, and passes every other key straight through. * * The chain lives here rather than in the ref itself because a reference names * ONE secret: expressing "the SMTP password, or the shared one" as a string * would mean inventing a ref syntax, and every reader of that string would have * to learn it. */ export declare function createSurfaceEmailSecretReader(secrets: SecretReader): SecretReader; /** What is missing before the daemon's mailbox can be used, in the operator's terms. */ export interface SurfaceEmailConfigProblem { readonly message: string; readonly code: string; } /** * Why the daemon's mailbox is not usable yet, or `null` when it is. * * Named separately from `validateEmailConfig` because the two speak about * different config: that one reports `email.imapHost is required`, which on a * daemon-configured machine names a key the operator does not have and cannot * set. These messages name the keys that are actually theirs, and are the same * sentences the mail handlers have always answered with. */ export declare function describeSurfaceEmailConfigProblem(getConfig: ConfigReader, secrets: SecretReader): Promise; /** * The same `EmailService` deps, reading the daemon's mailbox instead of the * `email.*` namespace. Everything else, transport, sender-claim describer, * ingest recorder, socket overrides, is passed through unchanged. */ export declare function withSurfaceEmailConfig(deps: EmailServiceDeps): EmailServiceDeps; //# sourceMappingURL=surface-config.d.ts.map