import type { InboundLinkVerdict, InboundMailOutcome, InboundMailRecord, InboundNoticeStatus } from './record-store.js'; /** What `validateInboundMailRecord` accepts for `subject`; redaction must not exceed it. */ export declare const MAX_SUBJECT_CHARS = 998; /** What `validateInboundMailRecord` accepts for `senderDisplay`; same rule as the subject. */ export declare const MAX_SENDER_DISPLAY_CHARS = 998; /** What `validateInboundMailRecord` accepts for `deliveredToAddress`. */ export declare const MAX_DELIVERED_TO_CHARS = 320; /** * What `validateInboundMailRecord` accepts for `noticeFailureReason`. * * This field had NO bound, and it is the one field on the record whose text a * REMOTE SERVER writes: `intake.ts` sets it from `delivery.error`, which is * whatever the notice transport handed back, a Telegram or Slack error body, * verbatim. A push service answering a refusal with a megabyte of HTML put a * megabyte on disk, per message, inside a store that believed `maxRecords` * bounded it. 512 characters is longer than any refusal worth reading and * shorter than anything worth storing. */ export declare const MAX_NOTICE_FAILURE_REASON_CHARS = 512; /** * What `isValidLinkVerdict` accepts for a verdict's `reason`. * * Bounded for the same reason and with less excuse: `links` was capped at 64 * entries with `registrableDomain` capped at 253, so the array LOOKED bounded *, and then each entry carried an unbounded `reason`. Sixty-four unbounded * strings is an unbounded record. No production path fills `links` yet * (`intake.ts` passes `[]` until the body-fetch round), so this is closed * before it is reachable rather than after. */ export declare const MAX_LINK_REASON_CHARS = 256; /** How many link verdicts one record may carry. */ export declare const MAX_LINK_VERDICTS = 64; /** * What `validateInboundMailRecord` accepts for `account` and `mailbox`. * * These two were bounded on the LOAD path and clamped nowhere on the write * path, which is the worst of the two arrangements: a megabyte `mailbox` was * written to disk in full and then failed its own validation on the very next * load, taking the whole record with it. Measured, a record built from * megabyte fields was two megabytes on disk, and every other field was already * clamped, so this pair was the entire remainder. * * Not attacker-written today (both come from the daemon's own configuration), * which is exactly why it survived review: the bound that is never exercised * is the bound nobody notices is missing on one of its two paths. */ export declare const MAX_ACCOUNT_CHARS = 256; export declare const MAX_MAILBOX_CHARS = 512; /** * Clamp the account/mailbox pair a record is scoped by, at WRITE time and at * LOOKUP time, so both sides of a comparison get the same treatment. * * Applied in two places on purpose. `record()` clamps what it stores; * `findByMessage()` clamps the key it is asked about. Clamping only the write * would make a long account store as one string and look up as another, and * dedup, which is what `findByMessage` exists for, would miss and announce * the same message twice. */ export declare function clampRecordScope(scope: { readonly account: string; readonly mailbox: string; }): { readonly account: string; readonly mailbox: string; }; /** * Clamp a redacted delivery address back inside its bound WITHOUT losing the * `@` the loader insists on. * * The plain `.slice(0, max)` the subject uses is wrong for this one field. * `validateInboundMailRecord` requires a `deliveredToAddress` to contain an * `@`, and a redaction marker is longer than the digits it replaces * (`[redacted:security-code]` is twenty-four characters for three), so a long * address carrying card shapes in its local part can grow past the bound and a * head-slice would cut the `@` off. The record would then fail its own * validation on the very next load and be discarded WHOLE, the mail would * vanish from the store entirely, which is a worse outcome than the exposure * this redaction exists to close. * * So the domain is kept intact and the local part gives up the characters. The * part that is truncated is the part that had already been overwritten with * markers, and the part that identifies where the message landed survives. */ export declare function clampDeliveryAddress(value: string): string; /** * Clamp a link verdict list to what the loader accepts, at WRITE time. * * Both bounds, not one: the entry count and each entry's `reason`. A write * that exceeded either produced a record the very next load would discard * whole, losing the message rather than the oversized field, which is the * failure mode `clampDeliveryAddress` exists to avoid on its own field. */ export declare function clampLinkVerdicts(links: readonly InboundLinkVerdict[]): readonly InboundLinkVerdict[]; export declare const INBOUND_MAIL_OUTCOMES: readonly InboundMailOutcome[]; export declare const INBOUND_NOTICE_STATUSES: readonly InboundNoticeStatus[]; /** * Validate a record by its parsed content, not by its presence in the file. * Returns `null` for anything torn, oversized, or out of range. Never throws, * never repairs, a body excerpt that somehow exceeds the hard cap is a * reason to discard the whole record, not to truncate it again on read. */ export declare function validateInboundMailRecord(value: unknown): InboundMailRecord | null; //# sourceMappingURL=record-validation.d.ts.map