@maildock/mailctl 0.1.0-rc.2 → 0.1.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/commands/install.ts", "../../../packages/core/src/primitives.ts", "../../../packages/core/src/entities.ts", "../../../packages/core/src/auth.ts", "../../../packages/core/src/server-config.ts", "../../../packages/core/src/setup.ts", "../../../packages/core/src/dns.ts", "../../../packages/core/src/sieve.ts", "../../../packages/core/src/deliverability.ts", "../../../packages/core/src/dmarc.ts", "../../../packages/core/src/delivery-log.ts", "../../../packages/core/src/webmail.ts", "../src/lib/api.ts", "../src/lib/compose.ts", "../src/lib/host.ts", "../src/lib/install-dir.ts", "../src/lib/preflight.ts", "../src/commands/doctor.ts", "../src/commands/backup.ts", "../src/commands/restore.ts", "../src/commands/upgrade.ts", "../src/index.ts"],
4
- "sourcesContent": ["import { Command, Flags } from '@oclif/core';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { SetupAnswers } from '@mailserver/core';\nimport { ApiClient, formatDns } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { publicIp } from '../lib/host.js';\nimport { DEFAULT_DIR, defaultEnv, materialise, readEnv, writeEnv, type EnvFile } from '../lib/install-dir.js';\nimport { DEFAULT_PORTS, blocking, formatChecks, preflight } from '../lib/preflight.js';\n\nexport default class Install extends Command {\n static override description =\n 'Install the mail server on this host: preflight, secrets, compose up, then hand off to setup (wizard URL or --answers).';\n static override examples = [\n '<%= config.bin %> install --hostname mail.example.com',\n '<%= config.bin %> install --answers setup.yaml',\n ];\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n hostname: Flags.string({ description: 'mail hostname (FQDN); read from --answers when given' }),\n answers: Flags.string({\n description: 'answers file (YAML/JSON) for a headless install \u2014 completes setup without the wizard',\n }),\n tag: Flags.string({\n description: 'image tag to install',\n default: process.env['MAILSERVER_VERSION'] ?? 'latest',\n }),\n registry: Flags.string({\n description: 'image registry/namespace',\n default: process.env['MAILSERVER_REGISTRY'] ?? 'ghcr.io/mail-dock/mailserver',\n }),\n 'skip-preflight': Flags.boolean({\n description: 'continue despite failed preflight checks',\n default: false,\n }),\n 'no-pull': Flags.boolean({ description: 'do not pull images (use local builds)', default: false }),\n };\n\n async run() {\n const { flags } = await this.parse(Install);\n let answers: SetupAnswers | undefined;\n if (flags.answers) {\n const raw = await fs.readFile(flags.answers, 'utf8');\n const parsed = SetupAnswers.safeParse(parseYaml(raw));\n if (!parsed.success)\n this.error(\n `invalid answers file:\\n${parsed.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n')}`,\n );\n answers = parsed.data;\n }\n const hostname = answers?.hostname ?? flags.hostname;\n if (!hostname) this.error('--hostname is required (or provide --answers)');\n\n const dir = flags.dir;\n await fs.mkdir(dir, { recursive: true });\n const existing = await readEnv(dir);\n if (existing['JWT_SECRET']) this.log(`Existing install found in ${dir}; keeping its secrets.`);\n\n this.log('Preflight\u2026');\n const checks = await preflight({\n hostname,\n dataDir: dir,\n ports: DEFAULT_PORTS,\n skipPorts: !!existing['JWT_SECRET'],\n });\n this.log(formatChecks(checks));\n const blockers = blocking(checks);\n if (blockers.length && !flags['skip-preflight'])\n this.error(`${blockers.length} blocking check(s) failed. Fix them or re-run with --skip-preflight.`);\n\n const ip = await publicIp();\n const env: EnvFile = {\n ...defaultEnv({ hostname, tag: flags.tag, registry: flags.registry, publicIp: ip }),\n ...existing,\n MAIL_HOSTNAME: hostname,\n IMAGE_TAG: flags.tag,\n IMAGE_REGISTRY: flags.registry,\n };\n await writeEnv(dir, env);\n await materialise(dir);\n this.log(`Wrote ${path.join(dir, '.env')} and compose bundle.`);\n\n const c = compose(dir);\n if (!flags['no-pull']) {\n this.log('Pulling images\u2026');\n await c.pull();\n }\n this.log('Starting services\u2026');\n await c.up();\n\n const api = new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`);\n const health = await api.waitHealthy(\n 180_000,\n (t) => t % 10_000 < 2000 && this.log(` waiting for API (${Math.round(t / 1000)}s)\u2026`),\n );\n this.log(`API ${health.version} healthy; setup ${health.setup}.`);\n\n if (answers) {\n if (health.setup === 'complete')\n this.error(\n 'setup is already complete on this server; --answers ignored. Use the admin UI to change settings.',\n );\n this.log('Applying answers file\u2026');\n const result = await api.applyAnswers(answers);\n this.log('\\nSetup complete. Publish these DNS records:\\n');\n this.log(formatDns(result.dns));\n this.log(`\\nAdmin: https://${hostname}/ \u00B7 API docs: https://${hostname}/api/v1/docs`);\n } else if (health.setup === 'pending') {\n this.log(\n `\\nOpen the Setup Wizard to finish: https://${hostname}/ (or http://${ip ?? '<server-ip>'}/ until DNS resolves)`,\n );\n } else {\n this.log(`\\nInstalled. Admin: https://${hostname}/`);\n }\n }\n}\n", "import { z } from 'zod';\n\n/** RFC 1035 hostname label; we additionally require at least two labels for a domain. */\nconst LABEL = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/;\n\nexport function normalizeDomain(input: string): string {\n return input.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nexport const DomainName = z\n .string()\n .overwrite(normalizeDomain)\n .refine(\n (v) => v.length <= 253 && v.split('.').length >= 2 && v.split('.').every((l) => LABEL.test(l)),\n 'must be a fully-qualified domain name',\n );\nexport type DomainName = z.infer<typeof DomainName>;\n\nexport const Hostname = DomainName;\n\n/** Local part: conservative subset (dot-atom without quoting) that Postfix/Dovecot handle without escaping. */\nexport const LocalPart = z\n .string()\n .trim()\n .toLowerCase()\n .refine(\n (v) => /^[a-z0-9](?:[a-z0-9._+-]{0,62}[a-z0-9])?$/.test(v) && !v.includes('..'),\n 'invalid local part',\n );\nexport type LocalPart = z.infer<typeof LocalPart>;\n\nexport const EmailAddress = z\n .string()\n .trim()\n .toLowerCase()\n .refine((v) => {\n const at = v.lastIndexOf('@');\n if (at <= 0) return false;\n return LocalPart.safeParse(v.slice(0, at)).success && DomainName.safeParse(v.slice(at + 1)).success;\n }, 'invalid email address');\nexport type EmailAddress = z.infer<typeof EmailAddress>;\n\nexport function splitAddress(address: string): { localPart: string; domain: string } {\n const at = address.lastIndexOf('@');\n return { localPart: address.slice(0, at), domain: address.slice(at + 1) };\n}\n\nexport const Uuid = z.uuid();\nexport const Password = z.string().min(10, 'at least 10 characters').max(256);\n/** Bytes; 0 means unlimited. */\nexport const QuotaBytes = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);\n\nexport const Paginated = <T extends z.ZodTypeAny>(item: T) =>\n z.object({ items: z.array(item), total: z.number().int() });\n\nexport const PageQuery = z.object({\n limit: z.coerce.number().int().min(1).max(500).default(100),\n offset: z.coerce.number().int().min(0).default(0),\n});\nexport type PageQuery = z.infer<typeof PageQuery>;\n\nexport const ApiError = z.object({\n statusCode: z.number(),\n error: z.string(),\n message: z.string(),\n code: z.string().optional(),\n});\nexport type ApiError = z.infer<typeof ApiError>;\n", "import { z } from 'zod';\nimport { DomainName, EmailAddress, LocalPart, Password, QuotaBytes, Uuid } from './primitives.js';\n\nconst timestamps = { createdAt: z.iso.datetime(), updatedAt: z.iso.datetime() };\n\nexport const Domain = z.object({\n id: Uuid,\n name: DomainName,\n active: z.boolean(),\n defaultQuotaBytes: QuotaBytes,\n ...timestamps,\n});\nexport type Domain = z.infer<typeof Domain>;\nexport const DomainCreate = z.object({ name: DomainName, defaultQuotaBytes: QuotaBytes.default(0) });\nexport const DomainUpdate = z.object({\n active: z.boolean().optional(),\n defaultQuotaBytes: QuotaBytes.optional(),\n});\n\nexport const Mailbox = z.object({\n id: Uuid,\n domainId: Uuid,\n localPart: LocalPart,\n address: EmailAddress,\n displayName: z.string().nullable(),\n quotaBytes: QuotaBytes,\n active: z.boolean(),\n suspended: z.boolean(),\n ...timestamps,\n});\nexport type Mailbox = z.infer<typeof Mailbox>;\n/** Live storage usage as mirrored by Dovecot `quota_clone`; `updatedAt` is null until Dovecot has reported once. */\nexport const MailboxUsage = z.object({\n mailboxId: Uuid,\n address: EmailAddress,\n quotaBytes: QuotaBytes,\n usedBytes: z.number().int().nonnegative(),\n messages: z.number().int().nonnegative(),\n /** 0\u2013100+ (may exceed 100 when the quota was lowered); null when the quota is unlimited. */\n percent: z.number().nullable(),\n updatedAt: z.iso.datetime().nullable(),\n});\nexport type MailboxUsage = z.infer<typeof MailboxUsage>;\nexport const MailboxCreate = z.object({\n localPart: LocalPart,\n password: Password,\n displayName: z.string().max(200).optional(),\n /** Omit to inherit the domain default. */\n quotaBytes: QuotaBytes.optional(),\n});\nexport const MailboxUpdate = z.object({\n displayName: z.string().max(200).nullable().optional(),\n quotaBytes: QuotaBytes.optional(),\n suspended: z.boolean().optional(),\n active: z.boolean().optional(),\n});\nexport const MailboxPasswordReset = z.object({ password: Password });\n\nexport const Alias = z.object({\n id: Uuid,\n domainId: Uuid,\n address: EmailAddress,\n destinations: z.array(EmailAddress).min(1),\n active: z.boolean(),\n ...timestamps,\n});\nexport type Alias = z.infer<typeof Alias>;\nexport const AliasCreate = z.object({\n localPart: LocalPart,\n destinations: z.array(EmailAddress).min(1).max(50),\n});\nexport const AliasUpdate = z.object({\n destinations: z.array(EmailAddress).min(1).max(50).optional(),\n active: z.boolean().optional(),\n});\n\nexport const Forwarder = z.object({\n id: Uuid,\n mailboxId: Uuid,\n destination: EmailAddress,\n keepCopy: z.boolean(),\n active: z.boolean(),\n ...timestamps,\n});\nexport type Forwarder = z.infer<typeof Forwarder>;\nexport const ForwarderCreate = z.object({ destination: EmailAddress, keepCopy: z.boolean().default(true) });\nexport const ForwarderUpdate = z.object({ keepCopy: z.boolean().optional(), active: z.boolean().optional() });\n\n/**\n * Catch-all / default address: where mail for a non-existent local part in the domain goes.\n * Without one, unknown recipients are rejected at SMTP time (the safest default).\n */\nexport const Catchall = z.object({\n id: Uuid,\n domainId: Uuid,\n destination: EmailAddress,\n active: z.boolean(),\n ...timestamps,\n});\nexport type Catchall = z.infer<typeof Catchall>;\nexport const CatchallSet = z.object({ destination: EmailAddress, active: z.boolean().default(true) });\n\nexport const AuditEntry = z.object({\n id: z.number().int(),\n actorUserId: Uuid.nullable(),\n actor: z.string().nullable(),\n action: z.string(),\n entity: z.string(),\n entityId: z.string().nullable(),\n before: z.unknown().nullable(),\n after: z.unknown().nullable(),\n ip: z.string().nullable(),\n at: z.iso.datetime(),\n});\nexport type AuditEntry = z.infer<typeof AuditEntry>;\n", "import { z } from 'zod';\nimport { EmailAddress, Password, Uuid } from './primitives.js';\n\nexport const UserRole = z.enum(['owner', 'admin', 'domain_admin', 'mailbox_user']);\nexport type UserRole = z.infer<typeof UserRole>;\n/** Roles that may administer the server; TOTP is required for them once enrolled. */\nexport const ADMIN_ROLES: readonly UserRole[] = ['owner', 'admin'];\n\nexport const User = z.object({\n id: Uuid,\n email: EmailAddress,\n role: UserRole,\n totpEnabled: z.boolean(),\n active: z.boolean(),\n createdAt: z.iso.datetime(),\n});\nexport type User = z.infer<typeof User>;\n\nexport const LoginRequest = z.object({\n email: EmailAddress,\n password: z.string().min(1),\n /** Required once the user has enabled TOTP. */\n totp: z\n .string()\n .regex(/^\\d{6}$/)\n .optional(),\n});\nexport const LoginResponse = z.object({\n accessToken: z.string(),\n expiresIn: z.number().int(),\n user: User,\n});\nexport const TotpSetupResponse = z.object({ secret: z.string(), otpauthUrl: z.string() });\nexport const TotpEnableRequest = z.object({ code: z.string().regex(/^\\d{6}$/) });\n\nexport const ApiToken = z.object({\n id: Uuid,\n name: z.string(),\n role: UserRole,\n lastUsedAt: z.iso.datetime().nullable(),\n expiresAt: z.iso.datetime().nullable(),\n createdAt: z.iso.datetime(),\n});\nexport type ApiToken = z.infer<typeof ApiToken>;\nexport const ApiTokenCreate = z.object({\n name: z.string().min(1).max(100),\n role: UserRole.default('admin'),\n expiresAt: z.iso.datetime().optional(),\n});\nexport const ApiTokenCreated = ApiToken.extend({ token: z.string() });\n\nexport const UserCreate = z.object({ email: EmailAddress, password: Password, role: UserRole });\n\n/** Principal attached to every authenticated request. */\nexport const Principal = z.object({\n /** `mailbox` = a mailbox owner signed in to webmail (role `mailbox_user`); `id` is then the mailbox id. */\n kind: z.enum(['user', 'token', 'mailbox']),\n id: Uuid,\n tenantId: Uuid,\n role: UserRole,\n label: z.string(),\n});\nexport type Principal = z.infer<typeof Principal>;\n", "import { z } from 'zod';\nimport { EmailAddress, Hostname } from './primitives.js';\n\nexport const TlsMode = z.enum(['acme', 'byo', 'none']);\nexport type TlsMode = z.infer<typeof TlsMode>;\n\n/**\n * Server-level settings rendered into Postfix / Dovecot / Rspamd / Caddy configuration (ADR 0005, ADR 0006).\n * Per-mailbox data is NOT here \u2014 daemons read it live from the Postgres views.\n */\nexport const ServerConfig = z.object({\n hostname: Hostname,\n tls: z.object({\n mode: TlsMode,\n /** ACME contact / expiry notices. Required for `acme`. */\n acmeEmail: EmailAddress.optional(),\n }),\n limits: z.object({\n messageSizeBytes: z.number().int().min(1_048_576).max(1_073_741_824).default(52_428_800),\n /** Outbound messages per hour per authenticated sender (Rspamd ratelimit, soft-reject above); 0 = unlimited. */\n perUserMessagesPerHour: z.number().int().min(0).default(500),\n /** Outbound messages per hour per sender domain; 0 = unlimited. */\n perDomainMessagesPerHour: z.number().int().min(0).default(2000),\n smtpdClientConnectionRateLimit: z.number().int().min(0).default(60),\n }),\n relay: z\n .object({\n host: Hostname,\n port: z.number().int().min(1).max(65535).default(587),\n username: z.string().min(1),\n password: z.string().min(1),\n })\n .nullable()\n .default(null),\n spam: z.object({\n rejectScore: z.number().default(15),\n addHeaderScore: z.number().default(6),\n greylistScore: z.number().default(4),\n }),\n /**\n * MTA-STS policy served at https://mta-sts.<domain>/.well-known/mta-sts.txt for every domain (needs\n * `tls.mode = acme`: Caddy issues the mta-sts.<domain> certificates on demand). `none` publishes no policy.\n */\n mtaSts: z\n .object({\n mode: z.enum(['none', 'testing', 'enforce']).default('testing'),\n maxAgeSeconds: z.number().int().min(86_400).max(31_557_600).default(604_800),\n })\n .default({ mode: 'testing', maxAgeSeconds: 604_800 }),\n /**\n * IP warm-up: a server-wide outbound cap that grows from 50/h on day 1 by 25 %/day until it reaches\n * `targetPerHour` (\u2248 3 weeks for 2000/h). Rendered as an rspamd ratelimit bucket and re-applied hourly\n * by the API while the cap is still below the target.\n */\n warmup: z\n .object({\n enabled: z.boolean().default(false),\n /** Day 1 of the schedule (ISO date); set when enabling. */\n startedAt: z.iso.datetime().optional(),\n targetPerHour: z.number().int().min(10).default(2000),\n })\n .default({ enabled: false, targetPerHour: 2000 }),\n /** Trusted networks that may relay without auth (docker network is always included by the adapter). */\n trustedNetworks: z.array(z.string().regex(/^[0-9a-f.:]+\\/\\d{1,3}$/i)).default([]),\n});\nexport type ServerConfig = z.infer<typeof ServerConfig>;\n\nexport const ServerConfigInput = ServerConfig.partial({\n limits: true,\n spam: true,\n relay: true,\n trustedNetworks: true,\n mtaSts: true,\n warmup: true,\n});\n\nexport const ServerConfigVersion = z.object({\n version: z.number().int(),\n config: ServerConfig,\n status: z.enum(['pending', 'applied', 'failed', 'rolled_back']),\n message: z.string().nullable(),\n createdBy: z.string().nullable(),\n createdAt: z.iso.datetime(),\n});\nexport type ServerConfigVersion = z.infer<typeof ServerConfigVersion>;\n\nexport const WARMUP_DAY1_PER_HOUR = 50;\nexport const WARMUP_GROWTH_PER_DAY = 1.25;\n\n/**\n * Today's outbound cap (messages/hour) for the warm-up schedule, or null when no cap applies (disabled,\n * not started, or the target has been reached). Pure: pass `now`.\n */\nexport function warmupCap(config: Pick<ServerConfig, 'warmup'>, now: Date): number | null {\n const w = config.warmup;\n if (!w.enabled || !w.startedAt) return null;\n const day = Math.floor((now.getTime() - new Date(w.startedAt).getTime()) / 86_400_000) + 1;\n if (day < 1) return WARMUP_DAY1_PER_HOUR;\n const cap = Math.round(WARMUP_DAY1_PER_HOUR * Math.pow(WARMUP_GROWTH_PER_DAY, day - 1));\n return cap >= w.targetPerHour ? null : cap;\n}\n", "import { z } from 'zod';\nimport { DomainName, EmailAddress, Hostname, Password, QuotaBytes } from './primitives.js';\nimport { TlsMode } from './server-config.js';\n\nexport const SetupState = z.enum(['pending', 'complete']);\nexport type SetupState = z.infer<typeof SetupState>;\n\n/** Steps of the setup state machine; each PUT/POST persists its slice, `complete` renders + applies. */\nexport const SetupStatus = z.object({\n state: SetupState,\n version: z.string(),\n steps: z.object({\n hostname: z.boolean(),\n tls: z.boolean(),\n domain: z.boolean(),\n owner: z.boolean(),\n }),\n hostname: Hostname.nullable(),\n tlsMode: TlsMode.nullable(),\n domain: DomainName.nullable(),\n ownerEmail: EmailAddress.nullable(),\n});\nexport type SetupStatus = z.infer<typeof SetupStatus>;\n\nexport const SetupHostname = z.object({ hostname: Hostname });\nexport const SetupTls = z.discriminatedUnion('mode', [\n z.object({ mode: z.literal('acme'), acmeEmail: EmailAddress }),\n z.object({ mode: z.literal('byo'), certificatePem: z.string().min(1), privateKeyPem: z.string().min(1) }),\n z.object({ mode: z.literal('none') }),\n]);\nexport const SetupDomain = z.object({ name: DomainName, defaultQuotaBytes: QuotaBytes.default(0) });\nexport const SetupOwner = z.object({\n email: EmailAddress,\n password: Password,\n /** Also create a mailbox for the owner address when it belongs to the first domain. */\n createMailbox: z.boolean().default(true),\n});\n\n/**\n * Answers file for headless install (`mailctl install --answers setup.yaml`, ADR 0005).\n * Versioned public contract: bump `version` on breaking changes.\n */\nexport const SetupAnswers = z.object({\n version: z.literal(1),\n hostname: Hostname,\n tls: SetupTls,\n domain: SetupDomain,\n owner: SetupOwner,\n});\nexport type SetupAnswers = z.infer<typeof SetupAnswers>;\n", "import { z } from 'zod';\n\nexport const DnsRecord = z.object({\n type: z.enum(['A', 'AAAA', 'MX', 'TXT', 'CNAME', 'SRV', 'PTR']),\n name: z.string(),\n value: z.string(),\n priority: z.number().int().optional(),\n ttl: z.number().int().default(3600),\n purpose: z.string(),\n /** Stable key for matching records across list / verification results. */\n key: z.string(),\n /** Records the domain needs before it is considered ready to send and receive (MX, SPF, DKIM). */\n required: z.boolean().default(true),\n});\nexport type DnsRecord = z.infer<typeof DnsRecord>;\n\n/** A DKIM signing key as exposed to admins; the private key never leaves the server. */\nexport const DkimKey = z.object({\n id: z.string(),\n domainId: z.string(),\n selector: z.string(),\n algorithm: z.literal('rsa'),\n bits: z.number().int(),\n status: z.enum(['active', 'retired']),\n /** Base64 SPKI public key (the `p=` value). */\n publicKey: z.string(),\n dnsName: z.string(),\n dnsValue: z.string(),\n createdAt: z.string(),\n retiredAt: z.string().nullable(),\n});\nexport type DkimKey = z.infer<typeof DkimKey>;\n\n/** What `requiredDnsRecords` needs to know about a domain's DKIM keys. */\nexport interface DkimDnsInput {\n selector: string;\n publicKey: string;\n status: 'active' | 'retired';\n retiredAt?: string | null;\n}\n\n/** MTA-STS policy text (RFC 8461 \u00A73.2) for this server; `id` in the DNS record is derived from it. */\nexport function mtaStsPolicy(hostname: string, mode: 'testing' | 'enforce', maxAgeSeconds: number) {\n return `version: STSv1\\nmode: ${mode}\\nmx: ${hostname}\\nmax_age: ${maxAgeSeconds}\\n`;\n}\n/** Deterministic policy id: changes exactly when the policy text changes (receivers re-fetch on id change). */\nexport function mtaStsId(policy: string) {\n let h = 0x811c9dc5;\n for (const c of policy) {\n h ^= c.charCodeAt(0);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h.toString(16).padStart(8, '0');\n}\n\nexport interface DnsRecordOptions {\n /** Present when MTA-STS is enabled and servable (ACME TLS): the policy id to publish. */\n mtaStsId?: string | undefined;\n /** True when the server can serve https://autoconfig.<domain> / autodiscover.<domain> (ACME TLS). */\n autoconfig?: boolean | undefined;\n}\n\n/** Days a retired selector's DNS record should stay published so in-flight mail still verifies. */\nexport const DKIM_RETIRED_OVERLAP_DAYS = 30;\n\nexport function dkimDnsName(domain: string, selector: string) {\n return `${selector}._domainkey.${domain}`;\n}\nexport function dkimDnsValue(publicKey: string) {\n return `v=DKIM1; k=rsa; p=${publicKey}`;\n}\n\n/**\n * Records a customer must publish for a domain to send and receive mail through `hostname`.\n * Every DKIM key (active, and retired ones inside the overlap window) contributes one TXT record.\n */\nexport function requiredDnsRecords(\n domain: string,\n hostname: string,\n serverIp?: string,\n dkim: DkimDnsInput[] = [],\n opts: DnsRecordOptions = {},\n): DnsRecord[] {\n const records: DnsRecord[] = [\n {\n type: 'MX',\n name: domain,\n value: `${hostname}.`,\n priority: 10,\n ttl: 3600,\n purpose: 'inbound mail',\n key: 'mx',\n required: true,\n },\n {\n type: 'TXT',\n name: domain,\n value: `v=spf1 mx -all`,\n ttl: 3600,\n purpose: 'SPF',\n key: 'spf',\n required: true,\n },\n ];\n for (const k of dkim) {\n const retired = k.status === 'retired';\n records.push({\n type: 'TXT',\n name: dkimDnsName(domain, k.selector),\n value: dkimDnsValue(k.publicKey),\n ttl: 3600,\n purpose: retired\n ? `DKIM (retired selector \u2014 keep until ${overlapEnd(k.retiredAt)})`\n : 'DKIM signing key',\n key: `dkim:${k.selector}`,\n required: !retired,\n });\n }\n records.push(\n {\n type: 'TXT',\n name: `_dmarc.${domain}`,\n value: `v=DMARC1; p=none; rua=mailto:dmarc-reports@${domain}`,\n ttl: 3600,\n purpose:\n 'DMARC (monitor mode; aggregate reports are ingested from dmarc-reports@ \u2014 tighten to quarantine/reject once they look clean)',\n key: 'dmarc',\n required: false,\n },\n ...(opts.mtaStsId\n ? [\n {\n type: 'TXT' as const,\n name: `_mta-sts.${domain}`,\n value: `v=STSv1; id=${opts.mtaStsId}`,\n ttl: 3600,\n purpose: 'MTA-STS (policy served at https://mta-sts.' + domain + '/.well-known/mta-sts.txt)',\n key: 'mta-sts',\n required: false,\n },\n {\n type: 'CNAME' as const,\n name: `mta-sts.${domain}`,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'MTA-STS policy host (certificate issued on first request)',\n key: 'mta-sts-host',\n required: false,\n },\n {\n type: 'TXT' as const,\n name: `_smtp._tls.${domain}`,\n value: `v=TLSRPTv1; rua=mailto:dmarc-reports@${domain}`,\n ttl: 3600,\n purpose: 'TLS-RPT (receivers report TLS failures to the reports mailbox)',\n key: 'tls-rpt',\n required: false,\n },\n ]\n : []),\n ...(opts.autoconfig\n ? [\n {\n type: 'CNAME' as const,\n name: `autoconfig.${domain}`,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'Thunderbird / mobile autoconfig (https://autoconfig.<domain>/mail/config-v1.1.xml)',\n key: 'autoconfig',\n required: false,\n },\n {\n type: 'CNAME' as const,\n name: `autodiscover.${domain}`,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'Outlook autodiscover (https://autodiscover.<domain>/autodiscover/autodiscover.xml)',\n key: 'autodiscover',\n required: false,\n },\n ]\n : []),\n {\n type: 'SRV',\n name: `_imaps._tcp.${domain}`,\n value: `0 1 993 ${hostname}.`,\n ttl: 3600,\n purpose: 'IMAP autodiscover',\n key: 'srv:imaps',\n required: false,\n },\n {\n type: 'SRV',\n name: `_submission._tcp.${domain}`,\n value: `0 1 587 ${hostname}.`,\n ttl: 3600,\n purpose: 'SMTP autodiscover',\n key: 'srv:submission',\n required: false,\n },\n );\n records.push({\n type: 'TXT',\n name: hostname,\n value: 'v=spf1 a -all',\n ttl: 3600,\n purpose: 'SPF for the mail host (forwarded mail is sent as SRS0=\u2026@' + hostname + ')',\n key: 'spf-host',\n required: false,\n });\n if (serverIp) {\n records.unshift({\n type: 'A',\n name: hostname,\n value: serverIp,\n ttl: 3600,\n purpose: 'mail host',\n key: 'a',\n required: true,\n });\n records.push({\n type: 'PTR',\n name: serverIp,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'reverse DNS (set at your VPS provider)',\n key: 'ptr',\n required: false,\n });\n }\n return records;\n}\n\nfunction overlapEnd(retiredAt?: string | null) {\n const d = retiredAt ? new Date(retiredAt) : new Date();\n d.setUTCDate(d.getUTCDate() + DKIM_RETIRED_OVERLAP_DAYS);\n return d.toISOString().slice(0, 10);\n}\n\n// ---- verification\n\nexport const DnsRecordStatus = z.enum(['ok', 'propagating', 'mismatch', 'missing', 'error']);\nexport type DnsRecordStatus = z.infer<typeof DnsRecordStatus>;\n\nexport const DnsResolverResult = z.object({\n resolver: z.string(),\n status: DnsRecordStatus,\n observed: z.array(z.string()),\n});\nexport type DnsResolverResult = z.infer<typeof DnsResolverResult>;\nexport const DnsRecordCheck = z.object({\n record: DnsRecord,\n /** `ok` when every resolver agrees, `propagating` when at least one does. */\n status: DnsRecordStatus,\n /** Union of what the resolvers returned for this name/type (for showing the admin what is actually published). */\n observed: z.array(z.string()),\n resolvers: z.array(DnsResolverResult),\n});\nexport type DnsRecordCheck = z.infer<typeof DnsRecordCheck>;\n\nexport const DnsVerification = z.object({\n domainId: z.string(),\n checkedAt: z.string(),\n /** Every `required` record is `ok` or `propagating`. */\n ready: z.boolean(),\n records: z.array(DnsRecordCheck),\n});\nexport type DnsVerification = z.infer<typeof DnsVerification>;\n\n// ---- export formats\n\n/** Split a long TXT value into \u2264255-byte quoted strings (DKIM keys); other values are quoted whole. */\nexport function txtChunks(value: string): string {\n const parts: string[] = [];\n for (let i = 0; i < value.length; i += 255) parts.push(`\"${value.slice(i, i + 255).replace(/\"/g, '\\\\\"')}\"`);\n return parts.join(' ');\n}\n\n/** BIND-style zone snippet (relative names against `$ORIGIN <domain>.`). PTR rows are skipped (set at the VPS). */\nexport function toZoneFile(domain: string, records: DnsRecord[]): string {\n const rel = (name: string) =>\n name === domain ? '@' : name.endsWith('.' + domain) ? name.slice(0, -(domain.length + 1)) : name + '.';\n const lines = [`$ORIGIN ${domain}.`, '$TTL 3600'];\n for (const r of records) {\n if (r.type === 'PTR') continue;\n const name = rel(r.name).padEnd(28);\n switch (r.type) {\n case 'MX':\n lines.push(`${name} IN MX ${r.priority ?? 10} ${r.value}`);\n break;\n case 'TXT':\n lines.push(`${name} IN TXT ${txtChunks(r.value)}`);\n break;\n case 'SRV':\n lines.push(`${name} IN SRV ${r.value}`);\n break;\n default:\n lines.push(`${name} IN ${r.type.padEnd(5)} ${r.value}`);\n }\n }\n return lines.join('\\n') + '\\n';\n}\n\n/** Provider notes shown next to the export (what trips people up at each provider). */\nexport const DNS_PROVIDER_NOTES: Record<'cloudflare' | 'route53' | 'generic', string[]> = {\n cloudflare: [\n 'Set the proxy status to \"DNS only\" (grey cloud) for every record here \u2014 proxied A/CNAME records break mail, MTA-STS and autoconfig.',\n 'Paste TXT values without surrounding quotes; Cloudflare splits long DKIM values itself.',\n 'Cloudflare enforces DNSSEC-safe CNAME flattening at the apex only; the mta-sts/autoconfig/autodiscover CNAMEs are fine as subdomains.',\n ],\n route53: [\n 'TXT values must be quoted, and any string longer than 255 characters split into several quoted strings \u2014 the zone-file export below is already split.',\n 'Use \"Simple routing\"; set TTL 3600.',\n 'The PTR record is set in the EC2/Lightsail console (reverse DNS request), not in Route 53.',\n ],\n generic: [\n 'Names are relative to the domain: \"@\" is the domain itself, \"_dmarc\" means _dmarc.<domain>.',\n 'If the panel rejects a long TXT value, split it into pieces of at most 255 characters (the DKIM record).',\n 'Set the PTR (reverse DNS) at your hosting provider to the mail hostname.',\n ],\n};\n\n// ---- client autoconfiguration documents\n\nexport interface ClientEndpoints {\n hostname: string;\n domain: string;\n displayName?: string | undefined;\n}\n\n/** Thunderbird / K-9 autoconfig (https://autoconfig.<domain>/mail/config-v1.1.xml). */\nexport function autoconfigXml({ hostname, domain, displayName }: ClientEndpoints): string {\n const name = displayName ?? domain;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<clientConfig version=\"1.1\">\n <emailProvider id=\"${domain}\">\n <domain>${domain}</domain>\n <displayName>${name}</displayName>\n <displayShortName>${name}</displayShortName>\n <incomingServer type=\"imap\">\n <hostname>${hostname}</hostname>\n <port>993</port>\n <socketType>SSL</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </incomingServer>\n <incomingServer type=\"imap\">\n <hostname>${hostname}</hostname>\n <port>143</port>\n <socketType>STARTTLS</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </incomingServer>\n <outgoingServer type=\"smtp\">\n <hostname>${hostname}</hostname>\n <port>587</port>\n <socketType>STARTTLS</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </outgoingServer>\n <outgoingServer type=\"smtp\">\n <hostname>${hostname}</hostname>\n <port>465</port>\n <socketType>SSL</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </outgoingServer>\n </emailProvider>\n</clientConfig>\n`;\n}\n\n/** Outlook autodiscover (POX) response for `email`. */\nexport function autodiscoverXml({ hostname }: ClientEndpoints, email: string): string {\n const server = (\n type: 'IMAP' | 'SMTP',\n port: number,\n ssl: 'on' | 'off',\n encryption?: 'TLS',\n ) => ` <Protocol>\n <Type>${type}</Type>\n <Server>${hostname}</Server>\n <Port>${port}</Port>\n <DomainRequired>off</DomainRequired>\n <LoginName>${email}</LoginName>\n <SPA>off</SPA>\n <SSL>${ssl}</SSL>${encryption ? `\\n <Encryption>${encryption}</Encryption>` : ''}\n <AuthRequired>on</AuthRequired>\n </Protocol>`;\n return `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">\n <Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">\n <Account>\n <AccountType>email</AccountType>\n <Action>settings</Action>\n${server('IMAP', 993, 'on')}\n${server('SMTP', 587, 'on', 'TLS')}\n </Account>\n </Response>\n</Autodiscover>\n`;\n}\n", "// Mailbox filters + autoresponder: structured rules (what the UI edits) and their compilation to Sieve\n// (what Dovecot runs). The compiler is pure so it is unit-tested without a daemon; the API pushes the\n// output through ManageSieve as the mailbox's single active script.\nimport { z } from 'zod';\nimport { EmailAddress } from './primitives.js';\n\nconst HeaderName = z\n .string()\n .trim()\n .regex(/^[!-9;-~]{1,64}$/, 'invalid header name');\nconst FolderName = z.string().trim().min(1).max(255);\n\nexport const FilterCondition = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('header'),\n /** e.g. Subject, From, To, X-Spam-Flag. */\n header: HeaderName,\n operator: z.enum([\n 'contains',\n 'not_contains',\n 'is',\n 'not_is',\n 'matches',\n 'not_matches',\n 'exists',\n 'not_exists',\n ]),\n value: z.string().max(998).default(''),\n }),\n z.object({\n type: z.literal('address'),\n header: z.enum(['From', 'To', 'Cc', 'Sender', 'Reply-To']),\n part: z.enum(['all', 'localpart', 'domain']).default('all'),\n operator: z.enum(['contains', 'not_contains', 'is', 'not_is', 'matches', 'not_matches']),\n value: z.string().max(998),\n }),\n z.object({\n type: z.literal('body'),\n operator: z.enum(['contains', 'not_contains']),\n value: z.string().min(1).max(998),\n }),\n z.object({\n type: z.literal('size'),\n operator: z.enum(['over', 'under']),\n /** Bytes. */\n value: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),\n }),\n]);\nexport type FilterCondition = z.infer<typeof FilterCondition>;\n\nexport const FilterAction = z.discriminatedUnion('type', [\n z.object({ type: z.literal('fileinto'), folder: FolderName }),\n z.object({ type: z.literal('copy'), folder: FolderName }),\n z.object({ type: z.literal('redirect'), address: EmailAddress }),\n z.object({ type: z.literal('redirect_copy'), address: EmailAddress }),\n z.object({ type: z.literal('flag'), flag: z.enum(['\\\\Seen', '\\\\Flagged', '\\\\Answered', '\\\\Deleted']) }),\n z.object({ type: z.literal('discard') }),\n z.object({ type: z.literal('keep') }),\n]);\nexport type FilterAction = z.infer<typeof FilterAction>;\n\nexport const FilterRule = z.object({\n name: z.string().trim().min(1).max(100),\n enabled: z.boolean().default(true),\n /** `all` = every condition must hold (allof); `any` = at least one (anyof). */\n match: z.enum(['all', 'any']).default('all'),\n conditions: z.array(FilterCondition).min(1).max(20),\n actions: z.array(FilterAction).min(1).max(10),\n /** Stop processing later rules when this one matched. */\n stop: z.boolean().default(true),\n});\nexport type FilterRule = z.infer<typeof FilterRule>;\n\nexport const Autoresponder = z.object({\n enabled: z.boolean().default(false),\n subject: z.string().trim().max(200).default(''),\n body: z.string().max(10_000).default(''),\n /** Days before the same sender gets another reply (Sieve `:days`, 1\u201330). */\n intervalDays: z.number().int().min(1).max(30).default(1),\n /** ISO dates; outside the window the responder is silent. Null = open-ended. */\n startsAt: z.iso.datetime().nullable().default(null),\n endsAt: z.iso.datetime().nullable().default(null),\n});\nexport type Autoresponder = z.infer<typeof Autoresponder>;\n\n/** What the UI edits. `raw` mode pushes `raw` verbatim and ignores rules + autoresponder. */\nexport const MailboxFiltersSet = z.object({\n mode: z.enum(['rules', 'raw']).default('rules'),\n rules: z.array(FilterRule).max(100).default([]),\n raw: z.string().max(64_000).default(''),\n});\nexport type MailboxFiltersSet = z.infer<typeof MailboxFiltersSet>;\n\nexport const MailboxFilters = z.object({\n mailboxId: z.uuid(),\n mode: z.enum(['rules', 'raw']),\n rules: z.array(FilterRule),\n raw: z.string(),\n autoresponder: Autoresponder,\n /** The Sieve script currently active in Dovecot (compiled from rules, or `raw`). */\n script: z.string(),\n /** When the script was last accepted by Dovecot; null if never pushed. */\n pushedAt: z.iso.datetime().nullable(),\n updatedAt: z.iso.datetime().nullable(),\n});\nexport type MailboxFilters = z.infer<typeof MailboxFilters>;\n\nexport const SieveCheck = z.object({ script: z.string().max(64_000) });\nexport const SieveCheckResult = z.object({ ok: z.boolean(), message: z.string() });\n\n// ---- compiler\n\nconst q = (s: string) => `\"${s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\r?\\n/g, ' ')}\"`;\n/** Sieve multi-line text literal: the body ends at a line holding a single dot (dot-stuffed), then the command's `;`. */\nconst text = (s: string) =>\n 'text:\\r\\n' +\n s\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')\n .map((l) => (l.startsWith('.') ? '.' + l : l))\n .join('\\r\\n') +\n '\\r\\n.\\r\\n;';\n\nconst MATCH: Record<string, { neg: boolean; type: string }> = {\n contains: { neg: false, type: ':contains' },\n not_contains: { neg: true, type: ':contains' },\n is: { neg: false, type: ':is' },\n not_is: { neg: true, type: ':is' },\n matches: { neg: false, type: ':matches' },\n not_matches: { neg: true, type: ':matches' },\n};\n\nfunction condition(c: FilterCondition, req: Set<string>): string {\n const wrap = (neg: boolean, test: string) => (neg ? `not ${test}` : test);\n switch (c.type) {\n case 'header': {\n if (c.operator === 'exists') return `exists ${q(c.header)}`;\n if (c.operator === 'not_exists') return `not exists ${q(c.header)}`;\n const m = MATCH[c.operator]!;\n return wrap(m.neg, `header ${m.type} ${q(c.header)} ${q(c.value)}`);\n }\n case 'address': {\n const m = MATCH[c.operator]!;\n const part = c.part === 'all' ? '' : `:${c.part} `;\n return wrap(m.neg, `address ${part}${m.type} ${q(c.header)} ${q(c.value)}`);\n }\n case 'body': {\n req.add('body');\n const m = MATCH[c.operator]!;\n return wrap(m.neg, `body :text :contains ${q(c.value)}`);\n }\n case 'size':\n return `size :${c.operator} ${c.value}`;\n }\n}\n\nfunction action(a: FilterAction, req: Set<string>): string {\n switch (a.type) {\n case 'fileinto':\n req.add('fileinto');\n return `fileinto ${q(a.folder)};`;\n case 'copy':\n req.add('fileinto');\n req.add('copy');\n return `fileinto :copy ${q(a.folder)};`;\n case 'redirect':\n return `redirect ${q(a.address)};`;\n case 'redirect_copy':\n req.add('copy');\n return `redirect :copy ${q(a.address)};`;\n case 'flag':\n req.add('imap4flags');\n return `addflag ${q(a.flag)};`;\n case 'discard':\n return 'discard;';\n case 'keep':\n return 'keep;';\n }\n}\n\nfunction sieveDate(iso: string): string {\n // Sieve `date`/`currentdate` compare ISO 8601 \"iso8601\" part; the `date` extension gives currentdate.\n return iso.slice(0, 19) + 'Z';\n}\n\n/**\n * Compile structured rules + autoresponder to a Sieve script. Output is deterministic and only uses\n * extensions Pigeonhole ships by default (fileinto, copy, body, imap4flags, vacation, date, relational).\n */\nexport function compileSieve(input: {\n rules: FilterRule[];\n autoresponder?: Autoresponder | undefined;\n}): string {\n const req = new Set<string>();\n const out: string[] = [];\n const ar = input.autoresponder;\n if (ar?.enabled) {\n req.add('vacation');\n const guards: string[] = [];\n if (ar.startsAt) {\n req.add('date');\n req.add('relational');\n guards.push(`currentdate :value \"ge\" \"iso8601\" ${q(sieveDate(ar.startsAt))}`);\n }\n if (ar.endsAt) {\n req.add('date');\n req.add('relational');\n guards.push(`currentdate :value \"le\" \"iso8601\" ${q(sieveDate(ar.endsAt))}`);\n }\n const vac = `vacation :days ${ar.intervalDays}${ar.subject ? ` :subject ${q(ar.subject)}` : ''} ${text(ar.body)}`;\n out.push('# Autoresponder');\n // The text: literal is not indented \u2014 its terminating line must be exactly \".\".\n if (guards.length) out.push(`if allof(${guards.join(', ')}) {`, vac, '}');\n else out.push(vac);\n out.push('');\n }\n for (const r of input.rules) {\n if (!r.enabled) continue;\n const tests = r.conditions.map((c) => condition(c, req));\n const test =\n tests.length === 1 ? tests[0]! : `${r.match === 'all' ? 'allof' : 'anyof'}(${tests.join(', ')})`;\n // imap4flags: addflag only affects fileinto/keep/redirect that run *after* it, so flags go first.\n const ordered = [\n ...r.actions.filter((a) => a.type === 'flag'),\n ...r.actions.filter((a) => a.type !== 'flag'),\n ];\n const body = ordered.map((a) => action(a, req));\n if (r.stop) body.push('stop;');\n out.push(`# rule: ${r.name.replace(/[\\r\\n]/g, ' ')}`, `if ${test} {`, ...body.map(indent), '}', '');\n }\n const header = req.size ? `require [${[...req].sort().map(q).join(', ')}];\\n\\n` : '';\n return (\n `# Generated by mailserver \u2014 edit in the admin UI (Mailbox \u2192 Filters). Manual edits are overwritten.\\n${header}${out.join('\\n')}`.trimEnd() +\n '\\n'\n );\n}\n\nconst indent = (s: string) =>\n s\n .split('\\n')\n .map((l) => (l ? ' ' + l : l))\n .join('\\n');\n", "import { z } from 'zod';\n\n// ---- alerts (raised by the worker's scheduled checks; listed on the dashboard)\n\nexport const AlertSeverity = z.enum(['warning', 'critical']);\nexport type AlertSeverity = z.infer<typeof AlertSeverity>;\n\nexport const Alert = z.object({\n id: z.string(),\n /** Stable identity of the condition (`dns:<domainId>:<recordKey>`, `dnsbl:<zone>`, `rdns`, `tls_expiry`). */\n key: z.string(),\n kind: z.enum(['dns_drift', 'dnsbl', 'rdns', 'tls_expiry']),\n severity: AlertSeverity,\n title: z.string(),\n detail: z.record(z.string(), z.unknown()),\n firstSeen: z.string(),\n lastSeen: z.string(),\n resolvedAt: z.string().nullable(),\n});\nexport type Alert = z.infer<typeof Alert>;\n\n// ---- reputation checks of the server itself\n\nexport const DnsblResult = z.object({\n zone: z.string(),\n status: z.enum(['clean', 'listed', 'error']),\n detail: z.string().nullable(),\n delistUrl: z.string(),\n});\nexport type DnsblResult = z.infer<typeof DnsblResult>;\n\nexport const RdnsResult = z.object({\n ip: z.string(),\n hostname: z.string(),\n ptr: z.array(z.string()),\n ptrMatches: z.boolean(),\n forwardConfirmed: z.boolean(),\n heloResolves: z.boolean(),\n ok: z.boolean(),\n error: z.string().nullable(),\n});\nexport type RdnsResult = z.infer<typeof RdnsResult>;\n\nexport const TlsExpiryResult = z.object({\n subject: z.string(),\n issuer: z.string(),\n notAfter: z.string(),\n daysLeft: z.number().int(),\n});\nexport type TlsExpiryResult = z.infer<typeof TlsExpiryResult>;\n\n/** Stored by the worker's hourly reputation job; `null` fields mean the check could not run (no PUBLIC_IP, no cert). */\nexport const ReputationReport = z.object({\n checkedAt: z.string(),\n ip: z.string().nullable(),\n dnsbl: z.array(DnsblResult),\n rdns: RdnsResult.nullable(),\n tls: TlsExpiryResult.nullable(),\n});\nexport type ReputationReport = z.infer<typeof ReputationReport>;\n", "import { z } from 'zod';\n\n/** One `<record>` of an aggregate report (RFC 7489 \u00A77.2). */\nexport const DmarcRecord = z.object({\n sourceIp: z.string(),\n count: z.number().int(),\n disposition: z.enum(['none', 'quarantine', 'reject']),\n dkim: z.enum(['pass', 'fail']),\n spf: z.enum(['pass', 'fail']),\n headerFrom: z.string(),\n /** Per-mechanism auth results: `dkim:selector:result`, `spf:domain:result`. */\n authResults: z.array(z.string()),\n});\nexport type DmarcRecord = z.infer<typeof DmarcRecord>;\n\nexport const DmarcReport = z.object({\n id: z.string(),\n domainId: z.string(),\n reportId: z.string(),\n orgName: z.string(),\n orgEmail: z.string().nullable(),\n /** Reporting window (ISO). */\n begin: z.string(),\n end: z.string(),\n policy: z.object({\n domain: z.string(),\n p: z.string().nullable(),\n sp: z.string().nullable(),\n adkim: z.string().nullable(),\n aspf: z.string().nullable(),\n pct: z.number().int().nullable(),\n }),\n records: z.array(DmarcRecord),\n receivedAt: z.string(),\n});\nexport type DmarcReport = z.infer<typeof DmarcReport>;\n\n/** What the dashboard shows: totals + per-source breakdown over a window. */\nexport const DmarcSummary = z.object({\n domainId: z.string(),\n days: z.number().int(),\n reports: z.number().int(),\n messages: z.number().int(),\n /** Messages where DKIM or SPF passed with alignment (DMARC pass). */\n aligned: z.number().int(),\n dkimPass: z.number().int(),\n spfPass: z.number().int(),\n byDisposition: z.object({ none: z.number().int(), quarantine: z.number().int(), reject: z.number().int() }),\n sources: z.array(\n z.object({\n sourceIp: z.string(),\n messages: z.number().int(),\n aligned: z.number().int(),\n dkimPass: z.number().int(),\n spfPass: z.number().int(),\n reporters: z.array(z.string()),\n }),\n ),\n latest: z.array(DmarcReport),\n});\nexport type DmarcSummary = z.infer<typeof DmarcSummary>;\n", "import { z } from 'zod';\n\nexport const DeliveryStatus = z.enum(['sent', 'bounced', 'deferred', 'expired']);\nexport type DeliveryStatus = z.infer<typeof DeliveryStatus>;\n\n/** Why a delivery failed (or `delivered`): what the admin needs to know to act. */\nexport const DeliveryClass = z.enum(['delivered', 'hard', 'soft', 'policy', 'reputation']);\nexport type DeliveryClass = z.infer<typeof DeliveryClass>;\n\nexport const DeliveryDirection = z.enum(['inbound', 'outbound', 'internal']);\nexport type DeliveryDirection = z.infer<typeof DeliveryDirection>;\n\nexport const DeliveryEvent = z.object({\n id: z.string(),\n queueId: z.string(),\n at: z.string(),\n /** Envelope sender as logged by qmgr (SRS-rewritten for forwards); null until the from= line was seen. */\n sender: z.string().nullable(),\n messageId: z.string().nullable(),\n recipient: z.string(),\n origTo: z.string().nullable(),\n relay: z.string(),\n status: DeliveryStatus,\n class: DeliveryClass,\n dsn: z.string().nullable(),\n /** The remote server's reply or Postfix's reason, verbatim. */\n detail: z.string(),\n direction: DeliveryDirection,\n});\nexport type DeliveryEvent = z.infer<typeof DeliveryEvent>;\n\nexport const DeliveryLogQuery = z.object({\n /** Matches sender, recipient, original recipient, message-id or queue id (substring, case-insensitive). */\n q: z.string().trim().max(200).optional(),\n status: DeliveryStatus.optional(),\n class: DeliveryClass.optional(),\n direction: DeliveryDirection.optional(),\n /** Restrict to a domain (sender or recipient side). */\n domain: z.string().trim().toLowerCase().max(253).optional(),\n days: z.coerce.number().int().min(1).max(90).default(7),\n limit: z.coerce.number().int().min(1).max(500).default(100),\n offset: z.coerce.number().int().min(0).default(0),\n});\nexport type DeliveryLogQuery = z.infer<typeof DeliveryLogQuery>;\n\nexport const DeliveryLogPage = z.object({\n items: z.array(DeliveryEvent),\n total: z.number().int(),\n /** Counts by class over the same filter (for the stat tiles). */\n byClass: z.object({\n delivered: z.number().int(),\n hard: z.number().int(),\n soft: z.number().int(),\n policy: z.number().int(),\n reputation: z.number().int(),\n }),\n});\nexport type DeliveryLogPage = z.infer<typeof DeliveryLogPage>;\n", "import { z } from 'zod';\n\n// ---- webmail (Phase 4): everything the SPA sees; IMAP details stay behind the API bridge (ADR 0010)\n\nexport const FolderRole = z.enum(['inbox', 'sent', 'drafts', 'trash', 'junk', 'archive', 'other']);\nexport type FolderRole = z.infer<typeof FolderRole>;\n\nexport const Folder = z.object({\n /** IMAP path (also the id used in every other endpoint), e.g. `INBOX`, `Sent`, `Projects/2026`. */\n path: z.string(),\n name: z.string(),\n role: FolderRole,\n delimiter: z.string().nullable(),\n subscribed: z.boolean(),\n messages: z.number().int(),\n unseen: z.number().int(),\n});\nexport type Folder = z.infer<typeof Folder>;\n\nexport const Address = z.object({ name: z.string().nullable(), address: z.string() });\nexport type Address = z.infer<typeof Address>;\n\nexport const MessageSummary = z.object({\n uid: z.number().int(),\n folder: z.string(),\n messageId: z.string().nullable(),\n /** `References`/`In-Reply-To` heads used for conversation grouping on the client. */\n inReplyTo: z.string().nullable(),\n subject: z.string(),\n from: z.array(Address),\n to: z.array(Address),\n date: z.string().nullable(),\n size: z.number().int(),\n flags: z.array(z.string()),\n seen: z.boolean(),\n flagged: z.boolean(),\n answered: z.boolean(),\n hasAttachments: z.boolean(),\n});\nexport type MessageSummary = z.infer<typeof MessageSummary>;\n\nexport const MessageListQuery = z.object({\n folder: z.string().default('INBOX'),\n /** Newest first; `before` = uid to continue from (exclusive) for infinite scroll. */\n before: z.coerce.number().int().positive().optional(),\n limit: z.coerce.number().int().min(1).max(200).default(50),\n /** Free-text search (from/to/subject/body via IMAP SEARCH, Dovecot FTS when enabled). */\n q: z.string().trim().max(200).optional(),\n unseen: z\n .enum(['true', 'false'])\n .optional()\n .transform((v) => v === 'true'),\n flagged: z\n .enum(['true', 'false'])\n .optional()\n .transform((v) => v === 'true'),\n attachments: z\n .enum(['true', 'false'])\n .optional()\n .transform((v) => v === 'true'),\n since: z.iso.date().optional(),\n until: z.iso.date().optional(),\n});\nexport type MessageListQuery = z.infer<typeof MessageListQuery>;\n\nexport const MessageList = z.object({\n folder: z.string(),\n items: z.array(MessageSummary),\n /** Total messages matching (folder size when unfiltered). */\n total: z.number().int(),\n /** Pass as `before` to fetch the next (older) page; null when exhausted. */\n next: z.number().int().nullable(),\n});\nexport type MessageList = z.infer<typeof MessageList>;\n\nexport const Attachment = z.object({\n /** IMAP body part id (`2`, `1.2`) \u2014 download via `/mail/messages/{uid}/parts/{part}`. */\n part: z.string(),\n filename: z.string(),\n contentType: z.string(),\n size: z.number().int(),\n /** `cid:` reference for inline images, without the angle brackets. */\n contentId: z.string().nullable(),\n inline: z.boolean(),\n});\nexport type Attachment = z.infer<typeof Attachment>;\n\nexport const Message = MessageSummary.extend({\n cc: z.array(Address),\n bcc: z.array(Address),\n replyTo: z.array(Address),\n references: z.array(z.string()),\n /** Plain-text body (derived from HTML when the message has none). */\n text: z.string(),\n /** Raw HTML body; the client sanitises before rendering (DOMPurify + sandboxed iframe). */\n html: z.string().nullable(),\n attachments: z.array(Attachment),\n headers: z.record(z.string(), z.string()),\n});\nexport type Message = z.infer<typeof Message>;\n\nexport const FlagsUpdate = z.object({\n folder: z.string(),\n uids: z.array(z.number().int().positive()).min(1).max(1000),\n add: z.array(z.string()).default([]),\n remove: z.array(z.string()).default([]),\n});\nexport const MoveRequest = z.object({\n folder: z.string(),\n uids: z.array(z.number().int().positive()).min(1).max(1000),\n to: z.string(),\n});\nexport const DeleteRequest = z.object({\n folder: z.string(),\n uids: z.array(z.number().int().positive()).min(1).max(1000),\n /** Skip the Trash and expunge immediately (what \"delete\" does inside Trash/Junk). */\n permanent: z.boolean().default(false),\n});\nexport const FolderCreate = z.object({ path: z.string().min(1).max(255) });\nexport const FolderRename = z.object({ path: z.string().min(1), to: z.string().min(1).max(255) });\n\nexport const SendRequest = z.object({\n to: z.array(z.string().min(3)).min(1),\n cc: z.array(z.string()).default([]),\n bcc: z.array(z.string()).default([]),\n subject: z.string().max(998).default(''),\n text: z.string().default(''),\n html: z.string().optional(),\n /** Message-ID being replied to / forwarded (sets In-Reply-To/References and the \\Answered flag). */\n inReplyTo: z.string().optional(),\n replyFolder: z.string().optional(),\n replyUid: z.number().int().positive().optional(),\n /** Draft to delete after a successful send. */\n draftUid: z.number().int().positive().optional(),\n /** Attachments already uploaded with POST /mail/uploads (ids), plus optional forwarded parts. */\n uploads: z.array(z.string()).default([]),\n forwardParts: z\n .array(z.object({ folder: z.string(), uid: z.number().int().positive(), part: z.string() }))\n .default([]),\n});\nexport type SendRequest = z.infer<typeof SendRequest>;\n\nexport const DraftSave = SendRequest.omit({ draftUid: true, replyFolder: true, replyUid: true }).extend({\n /** Existing draft uid to replace. */\n uid: z.number().int().positive().optional(),\n});\nexport const DraftSaved = z.object({ uid: z.number().int(), folder: z.string() });\nexport const Upload = z.object({\n id: z.string(),\n filename: z.string(),\n size: z.number().int(),\n contentType: z.string(),\n});\n\nexport const MailEvent = z.object({\n type: z.enum(['exists', 'expunge', 'flags', 'connected', 'error']),\n folder: z.string().nullable(),\n uid: z.number().int().nullable(),\n count: z.number().int().nullable(),\n});\nexport type MailEvent = z.infer<typeof MailEvent>;\n\nexport const MailboxSettings = z.object({\n signature: z.string().max(4000).default(''),\n signatureHtml: z.boolean().default(false),\n /** Reply-quoting and display preferences. */\n replyQuote: z.boolean().default(true),\n messagesPerPage: z.number().int().min(10).max(200).default(50),\n theme: z.enum(['system', 'light', 'dark']).default('system'),\n /** Delay before a queued send actually goes out (undo window), seconds. */\n undoSendSeconds: z.number().int().min(0).max(30).default(5),\n});\nexport type MailboxSettings = z.infer<typeof MailboxSettings>;\nexport const MailboxSettingsUpdate = MailboxSettings.partial();\n\nexport const MailAccount = z.object({\n mailboxId: z.string(),\n address: z.string(),\n displayName: z.string().nullable(),\n quotaBytes: z.number().int(),\n usedBytes: z.number().int(),\n settings: MailboxSettings,\n});\nexport type MailAccount = z.infer<typeof MailAccount>;\nexport const PasswordChange = z.object({ current: z.string().min(1), password: z.string().min(10).max(200) });\n", "import type { SetupAnswers } from '@mailserver/core';\n\n/** Thin client for the setup + health endpoints `mailctl` needs; everything else is the admin UI's job. */\nexport class ApiClient {\n constructor(readonly baseUrl: string) {}\n\n async health(): Promise<{ ok: boolean; version: string; setup: 'pending' | 'complete' } | null> {\n try {\n const res = await fetch(`${this.baseUrl}/api/v1/health`, { signal: AbortSignal.timeout(3000) });\n return res.ok\n ? ((await res.json()) as { ok: boolean; version: string; setup: 'pending' | 'complete' })\n : null;\n } catch {\n return null;\n }\n }\n\n async waitHealthy(timeoutMs: number, onTick?: (elapsed: number) => void) {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const h = await this.health();\n if (h?.ok) return h;\n onTick?.(Date.now() - start);\n await new Promise((r) => setTimeout(r, 2000));\n }\n throw new Error(`API at ${this.baseUrl} did not become healthy within ${timeoutMs / 1000}s`);\n }\n\n async applyAnswers(answers: SetupAnswers) {\n const res = await fetch(`${this.baseUrl}/api/v1/setup/answers`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(answers),\n signal: AbortSignal.timeout(120_000),\n });\n const body = (await res.json()) as {\n message?: string;\n dns?: { type: string; name: string; value: string; priority?: number; purpose: string }[];\n };\n if (!res.ok) throw new Error(`setup failed (${res.status}): ${body.message ?? JSON.stringify(body)}`);\n return body as {\n dns: { type: string; name: string; value: string; priority?: number; purpose: string }[];\n };\n }\n}\n\nexport function formatDns(\n records: { type: string; name: string; value: string; priority?: number; purpose: string }[],\n): string {\n const rows = records.map((r) => [\n r.type,\n r.name,\n r.priority !== undefined ? `${r.priority} ${r.value}` : r.value,\n r.purpose,\n ]);\n const w = [0, 1, 2].map((i) => Math.max(...rows.map((r) => r[i]!.length)));\n return rows\n .map((r) => `${r[0]!.padEnd(w[0]!)} ${r[1]!.padEnd(w[1]!)} ${r[2]!.padEnd(w[2]!)} # ${r[3]}`)\n .join('\\n');\n}\n", "import path from 'node:path';\nimport { run } from './host.js';\nimport { composeFile } from './install-dir.js';\n\n/** `docker compose` bound to the install directory's compose file and `.env`. */\nexport function compose(dir: string) {\n const base = [\n 'compose',\n '--project-name',\n 'mailserver',\n '--env-file',\n path.join(dir, '.env'),\n '-f',\n composeFile(dir),\n ];\n return {\n exec: (args: string[], stdio: 'inherit' | 'pipe' = 'inherit') =>\n run('docker', [...base, ...args], { cwd: dir, stdio }),\n pull: () => run('docker', [...base, 'pull', '--quiet'], { cwd: dir, stdio: 'inherit' }),\n up: () =>\n run('docker', [...base, 'up', '-d', '--remove-orphans', '--wait'], { cwd: dir, stdio: 'inherit' }),\n down: () => run('docker', [...base, 'down'], { cwd: dir, stdio: 'inherit' }),\n ps: async () =>\n (await run('docker', [...base, 'ps', '--format', 'json'], { cwd: dir, stdio: 'pipe' })).stdout,\n /** Run a one-off command inside a service container, capturing stdout. */\n execIn: (service: string, cmd: string[], input?: string | Buffer) =>\n execInService(base, dir, service, cmd, input),\n };\n}\n\nasync function execInService(\n base: string[],\n dir: string,\n service: string,\n cmd: string[],\n input?: string | Buffer,\n) {\n const { spawn } = await import('node:child_process');\n return new Promise<Buffer>((resolve, reject) => {\n const child = spawn('docker', [...base, 'exec', '-T', service, ...cmd], {\n cwd: dir,\n stdio: ['pipe', 'pipe', 'inherit'],\n });\n const chunks: Buffer[] = [];\n child.stdout.on('data', (c: Buffer) => chunks.push(c));\n child.on('error', reject);\n child.on('exit', (code) =>\n code === 0\n ? resolve(Buffer.concat(chunks))\n : reject(new Error(`docker compose exec ${service} ${cmd[0]} exited with ${code}`)),\n );\n if (input !== undefined) child.stdin.end(input);\n else child.stdin.end();\n });\n}\n", "// Host-side helpers for mailctl: shell, ports, sizing, DNS. No mail configuration lives here (ADR 0005).\nimport { execFile } from 'node:child_process';\nimport dns from 'node:dns/promises';\nimport net from 'node:net';\nimport os from 'node:os';\nimport { promisify } from 'node:util';\n\nexport const execFileP = promisify(execFile);\n\nexport async function run(\n cmd: string,\n args: string[],\n opts: { cwd?: string; env?: NodeJS.ProcessEnv; stdio?: 'inherit' | 'pipe' } = {},\n) {\n if (opts.stdio === 'inherit') {\n const { spawn } = await import('node:child_process');\n return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd: opts.cwd,\n env: { ...process.env, ...opts.env },\n stdio: 'inherit',\n });\n child.on('exit', (code) =>\n code === 0\n ? resolve({ stdout: '', stderr: '' })\n : reject(new Error(`${cmd} ${args.join(' ')} exited with ${code}`)),\n );\n child.on('error', reject);\n });\n }\n return execFileP(cmd, args, {\n cwd: opts.cwd,\n env: { ...process.env, ...opts.env },\n maxBuffer: 64 * 1024 * 1024,\n });\n}\n\nexport async function commandExists(cmd: string): Promise<boolean> {\n try {\n await execFileP(process.platform === 'win32' ? 'where' : 'which', [cmd]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function dockerVersion(): Promise<{ docker: string | null; compose: string | null }> {\n const out = { docker: null as string | null, compose: null as string | null };\n try {\n out.docker = (await execFileP('docker', ['version', '--format', '{{.Server.Version}}'])).stdout.trim();\n } catch {\n /* not installed or daemon down */\n }\n try {\n out.compose = (await execFileP('docker', ['compose', 'version', '--short'])).stdout.trim();\n } catch {\n /* no compose plugin */\n }\n return out;\n}\n\n/** True when nothing on this host is listening on `port` (all interfaces). */\nexport function portFree(port: number, host = '0.0.0.0'): Promise<boolean> {\n return new Promise((resolve) => {\n const srv = net.createServer();\n srv.once('error', () => resolve(false));\n srv.listen({ port, host, exclusive: true }, () => srv.close(() => resolve(true)));\n });\n}\n\nexport function memoryGiB(): number {\n return os.totalmem() / 1024 ** 3;\n}\n\nexport async function diskFreeGiB(path: string): Promise<number | null> {\n try {\n const { stdout } = await execFileP('df', ['-Pk', path]);\n const line = stdout.trim().split('\\n').at(-1) ?? '';\n const avail = Number(line.split(/\\s+/)[3]);\n return Number.isFinite(avail) ? avail / 1024 ** 2 : null;\n } catch {\n return null;\n }\n}\n\nexport async function publicIp(): Promise<string | null> {\n for (const url of ['https://api.ipify.org', 'https://ifconfig.me/ip']) {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(4000) });\n const ip = (await res.text()).trim();\n if (net.isIP(ip)) return ip;\n } catch {\n /* try next */\n }\n }\n return null;\n}\n\nexport async function resolveA(hostname: string): Promise<string[]> {\n try {\n return await dns.resolve4(hostname);\n } catch {\n return [];\n }\n}\n\nexport async function reverseDns(ip: string): Promise<string[]> {\n try {\n return await dns.reverse(ip);\n } catch {\n return [];\n }\n}\n\nexport function randomSecret(bytes = 32): string {\n return Buffer.from(Array.from({ length: bytes }, () => Math.floor(Math.random() * 256))).toString(\n 'base64url',\n );\n}\n", "import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { randomSecret } from './host.js';\n\nexport const DEFAULT_DIR = process.env['MAILSERVER_DIR'] ?? '/opt/mailserver';\n\n/** The compose bundle is shipped inside the CLI package (copied from `compose/` at build time). */\nexport function bundledComposeFile(): string {\n return path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'assets', 'compose.yaml');\n}\nexport function bundledCaddyfile(): string {\n return path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'assets', 'Caddyfile');\n}\n\nexport interface EnvFile {\n [key: string]: string;\n}\n\nexport async function readEnv(dir: string): Promise<EnvFile> {\n const text = await fs.readFile(path.join(dir, '.env'), 'utf8').catch(() => '');\n const env: EnvFile = {};\n for (const line of text.split('\\n')) {\n const m = /^\\s*([A-Z0-9_]+)\\s*=\\s*(.*)\\s*$/.exec(line);\n if (m) env[m[1]!] = m[2]!;\n }\n return env;\n}\n\nexport async function writeEnv(dir: string, env: EnvFile): Promise<void> {\n const body = [\n '# Written by mailctl. Secrets, ports, paths and image tags only (ADR 0005). Configuration lives in the database.',\n ...Object.entries(env).map(([k, v]) => `${k}=${v}`),\n '',\n ].join('\\n');\n await fs.writeFile(path.join(dir, '.env'), body, { mode: 0o600 });\n}\n\n/** Minimal `.env` for a customer install: generated secrets, standard ports, the requested image tag. */\nexport function defaultEnv(opts: {\n hostname: string;\n tag: string;\n registry: string;\n publicIp?: string | null | undefined;\n}): EnvFile {\n return {\n IMAGE_REGISTRY: opts.registry,\n IMAGE_TAG: opts.tag,\n MAIL_HOSTNAME: opts.hostname,\n DB_ADMIN_USER: 'mail',\n DB_ADMIN_PASSWORD: randomSecret(24),\n DB_NAME: 'mail',\n DB_DAEMON_USER: 'mail_daemon',\n DB_DAEMON_PASSWORD: randomSecret(24),\n JWT_SECRET: randomSecret(48),\n DOVECOT_MASTER_PASSWORD: randomSecret(32),\n SRS_SECRET: randomSecret(32),\n PUBLIC_IP: opts.publicIp ?? '',\n COOKIE_SECURE: 'true',\n SMTP_PORT: '25',\n SMTPS_PORT: '465',\n SUBMISSION_PORT: '587',\n IMAP_PORT: '143',\n IMAPS_PORT: '993',\n POP3_PORT: '110',\n POP3S_PORT: '995',\n SIEVE_PORT: '4190',\n HTTP_PORT: '80',\n HTTPS_PORT: '443',\n API_PORT: '3000',\n DB_PORT: '5432',\n };\n}\n\n/** Copy the bundled compose file + Caddyfile into the install directory (idempotent, overwrites on upgrade). */\nexport async function materialise(dir: string): Promise<void> {\n await fs.mkdir(path.join(dir, 'docker', 'caddy'), { recursive: true });\n await fs.mkdir(path.join(dir, 'compose'), { recursive: true });\n await fs.copyFile(bundledComposeFile(), path.join(dir, 'compose', 'compose.yaml'));\n await fs.copyFile(bundledCaddyfile(), path.join(dir, 'docker', 'caddy', 'Caddyfile'));\n}\n\nexport const composeFile = (dir: string) => path.join(dir, 'compose', 'compose.yaml');\n", "import {\n commandExists,\n diskFreeGiB,\n dockerVersion,\n memoryGiB,\n portFree,\n publicIp,\n resolveA,\n reverseDns,\n} from './host.js';\n\nexport interface Check {\n name: string;\n ok: boolean;\n /** Warnings do not block the install. */\n level: 'error' | 'warn' | 'info';\n detail: string;\n}\n\nexport interface PreflightOptions {\n hostname?: string | undefined;\n dataDir: string;\n ports: number[];\n minMemoryGiB?: number;\n minDiskGiB?: number;\n /** Skip port checks (upgrade/doctor on a running stack). */\n skipPorts?: boolean;\n}\n\nexport const DEFAULT_PORTS = [25, 80, 443, 465, 587, 993, 995];\n\n/** Host readiness checks run by `mailctl install` and `mailctl doctor`. */\nexport async function preflight(o: PreflightOptions): Promise<Check[]> {\n const checks: Check[] = [];\n const dv = await dockerVersion();\n checks.push({\n name: 'docker',\n ok: !!dv.docker,\n level: 'error',\n detail: dv.docker\n ? `engine ${dv.docker}`\n : 'docker engine not reachable (install Docker or run as a user in the docker group)',\n });\n checks.push({\n name: 'docker compose',\n ok: !!dv.compose,\n level: 'error',\n detail: dv.compose ? `v${dv.compose}` : 'compose plugin missing',\n });\n checks.push({\n name: 'curl/tar',\n ok: (await commandExists('tar')) && (await commandExists('curl')),\n level: 'warn',\n detail: 'needed for backup/restore',\n });\n\n const mem = memoryGiB();\n const minMem = o.minMemoryGiB ?? 2;\n checks.push({\n name: 'memory',\n ok: mem >= minMem,\n level: 'error',\n detail: `${mem.toFixed(1)} GiB (min ${minMem})`,\n });\n const disk = await diskFreeGiB(o.dataDir);\n const minDisk = o.minDiskGiB ?? 10;\n checks.push({\n name: 'disk',\n ok: disk === null || disk >= minDisk,\n level: 'warn',\n detail:\n disk === null\n ? `cannot stat ${o.dataDir}`\n : `${disk.toFixed(1)} GiB free at ${o.dataDir} (min ${minDisk})`,\n });\n\n if (!o.skipPorts) {\n for (const p of o.ports) {\n const free = await portFree(p);\n checks.push({\n name: `port ${p}`,\n ok: free,\n level: 'error',\n detail: free ? 'free' : 'in use \u2014 stop the service using it (e.g. an existing MTA, nginx, apache)',\n });\n }\n }\n\n const ip = await publicIp();\n checks.push({\n name: 'public ip',\n ok: !!ip,\n level: 'warn',\n detail: ip ?? 'could not determine (no outbound HTTPS?)',\n });\n if (o.hostname) {\n const a = await resolveA(o.hostname);\n const matches = !!ip && a.includes(ip);\n checks.push({\n name: 'hostname A record',\n ok: a.length > 0,\n level: 'warn',\n detail: a.length\n ? `${o.hostname} \u2192 ${a.join(', ')}${matches ? '' : \" (does not match this host's public IP)\"}`\n : `${o.hostname} does not resolve \u2014 ACME will fail until it does`,\n });\n if (ip) {\n const ptr = await reverseDns(ip);\n checks.push({\n name: 'reverse DNS (PTR)',\n ok: ptr.includes(o.hostname),\n level: 'warn',\n detail: ptr.length\n ? `${ip} \u2192 ${ptr.join(', ')}${ptr.includes(o.hostname) ? '' : ` (expected ${o.hostname}; set at your VPS provider)`}`\n : `no PTR for ${ip} \u2014 many receivers will reject or spam-folder your mail`,\n });\n }\n }\n return checks;\n}\n\nexport const blocking = (checks: Check[]) => checks.filter((c) => !c.ok && c.level === 'error');\n\nexport function formatChecks(checks: Check[]): string {\n return checks\n .map((c) => `${c.ok ? '\u2714' : c.level === 'error' ? '\u2716' : '\u26A0'} ${c.name.padEnd(20)} ${c.detail}`)\n .join('\\n');\n}\n", "import { Command, Flags } from '@oclif/core';\nimport { ApiClient } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { DEFAULT_DIR, readEnv } from '../lib/install-dir.js';\nimport { formatChecks, preflight, type Check } from '../lib/preflight.js';\n\nexport default class Doctor extends Command {\n static override description =\n 'Re-run host preflight, check every service and the API, and verify DNS for the mail hostname.';\n static override flags = { dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }) };\n\n async run() {\n const { flags } = await this.parse(Doctor);\n const env = await readEnv(flags.dir);\n if (!env['MAIL_HOSTNAME']) this.error(`no install found in ${flags.dir} (missing .env)`);\n const checks = await preflight({\n hostname: env['MAIL_HOSTNAME'],\n dataDir: flags.dir,\n ports: [],\n skipPorts: true,\n });\n\n let services: { Service: string; State: string; Health?: string }[] = [];\n try {\n services = (await compose(flags.dir).ps())\n .split('\\n')\n .filter(Boolean)\n .map((l) => JSON.parse(l) as { Service: string; State: string; Health?: string });\n } catch (e) {\n checks.push({ name: 'compose', ok: false, level: 'error', detail: String(e) });\n }\n for (const s of services) {\n const ok = s.State === 'running' && (!s.Health || s.Health === 'healthy');\n checks.push({\n name: `service ${s.Service}`,\n ok,\n level: 'error',\n detail: `${s.State}${s.Health ? ` (${s.Health})` : ''}`,\n });\n }\n const expected = [\n 'postgres',\n 'redis',\n 'rspamd',\n 'dovecot',\n 'postfix',\n 'api',\n 'worker',\n 'web',\n 'caddy',\n 'cert-sync',\n ];\n for (const name of expected.filter((n) => !services.some((s) => s.Service === n))) {\n checks.push({ name: `service ${name}`, ok: false, level: 'error', detail: 'not running' });\n }\n\n const api = new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`);\n const h = await api.health();\n checks.push({\n name: 'api',\n ok: !!h?.ok,\n level: 'error',\n detail: h ? `version ${h.version}, setup ${h.setup}` : 'unreachable',\n });\n\n this.log(formatChecks(checks));\n const bad = checks.filter((c: Check) => !c.ok && c.level === 'error');\n if (bad.length) this.error(`${bad.length} problem(s) found`, { exit: 1 });\n this.log('\\nAll checks passed.');\n }\n}\n", "import { Command, Flags } from '@oclif/core';\nimport { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { compose } from '../lib/compose.js';\nimport { run } from '../lib/host.js';\nimport { DEFAULT_DIR, readEnv } from '../lib/install-dir.js';\n\n/** Backup = database dump + maildir tarball + .env, plus a manifest with SHA-256 of each part. */\nexport default class Backup extends Command {\n static override description =\n 'Back up the database (pg_dump), all mail (maildir tar) and .env to a timestamped directory with checksums.';\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n out: Flags.string({ description: 'backup root directory', default: path.join(DEFAULT_DIR, 'backups') }),\n };\n\n async run() {\n const { flags } = await this.parse(Backup);\n const target = await backup(flags.dir, flags.out, (m) => this.log(m));\n this.log(`Backup written to ${target}`);\n }\n}\n\nconst sha256 = async (file: string) =>\n createHash('sha256')\n .update(await fs.readFile(file))\n .digest('hex');\n\nexport async function backup(dir: string, outRoot: string, log: (m: string) => void): Promise<string> {\n const env = await readEnv(dir);\n if (!env['DB_NAME']) throw new Error(`no install found in ${dir}`);\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n const out = path.join(outRoot, stamp);\n await fs.mkdir(out, { recursive: true });\n const c = compose(dir);\n\n log('Dumping database\u2026');\n const dump = await c.execIn('postgres', [\n 'pg_dump',\n '-U',\n env['DB_ADMIN_USER'] ?? 'mail',\n '-Fc',\n env['DB_NAME'],\n ]);\n await fs.writeFile(path.join(out, 'database.dump'), dump);\n\n log('Archiving mail\u2026');\n await run(\n 'docker',\n [\n 'run',\n '--rm',\n '-v',\n 'mailserver_vmail:/var/vmail:ro',\n '-v',\n `${out}:/backup`,\n 'alpine:3.20',\n 'tar',\n 'czf',\n '/backup/vmail.tar.gz',\n '-C',\n '/var',\n 'vmail',\n ],\n { stdio: 'inherit' },\n );\n\n await fs.copyFile(path.join(dir, '.env'), path.join(out, 'env'));\n await fs.chmod(path.join(out, 'env'), 0o600);\n\n const files = ['database.dump', 'vmail.tar.gz', 'env'];\n const manifest = {\n createdAt: new Date().toISOString(),\n version: env['IMAGE_TAG'] ?? 'unknown',\n hostname: env['MAIL_HOSTNAME'],\n files: Object.fromEntries(\n await Promise.all(\n files.map(async (f) => [\n f,\n { sha256: await sha256(path.join(out, f)), bytes: (await fs.stat(path.join(out, f))).size },\n ]),\n ),\n ),\n };\n await fs.writeFile(path.join(out, 'manifest.json'), JSON.stringify(manifest, null, 2));\n return out;\n}\n\nexport async function verifyBackup(backupDir: string): Promise<{ version: string; hostname: string }> {\n const manifest = JSON.parse(await fs.readFile(path.join(backupDir, 'manifest.json'), 'utf8')) as {\n version: string;\n hostname: string;\n files: Record<string, { sha256: string }>;\n };\n for (const [f, meta] of Object.entries(manifest.files)) {\n const actual = await sha256(path.join(backupDir, f));\n if (actual !== meta.sha256)\n throw new Error(`integrity check failed for ${f} (expected ${meta.sha256}, got ${actual})`);\n }\n return manifest;\n}\n", "import { Command, Flags } from '@oclif/core';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { ApiClient } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { run } from '../lib/host.js';\nimport { DEFAULT_DIR, materialise, readEnv } from '../lib/install-dir.js';\nimport { verifyBackup } from './backup.js';\n\nexport default class Restore extends Command {\n static override description =\n 'Restore a backup made by `mailctl backup` (verifies checksums, restores .env, database and mail, restarts the stack).';\n static override args = {};\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n from: Flags.string({ description: 'backup directory (contains manifest.json)', required: true }),\n yes: Flags.boolean({ description: 'do not ask for confirmation', default: false }),\n };\n\n async run() {\n const { flags } = await this.parse(Restore);\n const manifest = await verifyBackup(flags.from);\n this.log(`Backup verified: ${manifest.hostname} @ ${manifest.version}`);\n if (!flags.yes) {\n const { confirm } = await import('@inquirer/prompts').catch(() => ({ confirm: null }));\n if (\n confirm &&\n !(await confirm({\n message: `This REPLACES the database and all mail in ${flags.dir}. Continue?`,\n default: false,\n }))\n )\n this.exit(1);\n }\n await fs.mkdir(flags.dir, { recursive: true });\n await fs.copyFile(path.join(flags.from, 'env'), path.join(flags.dir, '.env'));\n await fs.chmod(path.join(flags.dir, '.env'), 0o600);\n await materialise(flags.dir);\n const env = await readEnv(flags.dir);\n const c = compose(flags.dir);\n\n this.log('Stopping mail services\u2026');\n await c.exec(['stop', 'postfix', 'dovecot', 'api', 'cert-sync']);\n await c.exec(['up', '-d', '--wait', 'postgres']);\n\n this.log('Restoring database\u2026');\n const user = env['DB_ADMIN_USER'] ?? 'mail';\n const db = env['DB_NAME'] ?? 'mail';\n await c.execIn('postgres', [\n 'psql',\n '-U',\n user,\n '-d',\n 'postgres',\n '-c',\n `DROP DATABASE IF EXISTS ${db} WITH (FORCE)`,\n ]);\n await c.execIn('postgres', ['psql', '-U', user, '-d', 'postgres', '-c', `CREATE DATABASE ${db}`]);\n await c.execIn(\n 'postgres',\n ['pg_restore', '-U', user, '-d', db, '--no-owner'],\n await fs.readFile(path.join(flags.from, 'database.dump')),\n );\n\n this.log('Restoring mail\u2026');\n await run(\n 'docker',\n [\n 'run',\n '--rm',\n '-v',\n 'mailserver_vmail:/var/vmail',\n '-v',\n `${path.resolve(flags.from)}:/backup:ro`,\n 'alpine:3.20',\n 'sh',\n '-c',\n 'rm -rf /var/vmail/* && tar xzf /backup/vmail.tar.gz -C /var && chown -R 5000:5000 /var/vmail',\n ],\n { stdio: 'inherit' },\n );\n\n this.log('Starting services\u2026');\n await c.up();\n await new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`).waitHealthy(120_000);\n this.log('Restore complete.');\n }\n}\n", "import { Command, Flags } from '@oclif/core';\nimport path from 'node:path';\nimport { ApiClient } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { DEFAULT_DIR, defaultEnv, materialise, readEnv, writeEnv } from '../lib/install-dir.js';\nimport { backup } from './backup.js';\n\n/**\n * Upgrade = backup \u2192 set IMAGE_TAG (+ back-fill any .env key a newer release introduced, with a fresh\n * secret where one is generated) \u2192 pull \u2192 up (API runs migrations on boot) \u2192 health check.\n */\nexport default class Upgrade extends Command {\n static override description =\n 'Upgrade to a new version: pre-upgrade backup, pull images, restart, run migrations, verify health.';\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n tag: Flags.string({ description: 'target image tag (default: latest)', default: 'latest' }),\n 'skip-backup': Flags.boolean({\n default: false,\n description: 'skip the pre-upgrade backup (not recommended)',\n }),\n };\n\n async run() {\n const { flags } = await this.parse(Upgrade);\n const env = await readEnv(flags.dir);\n if (!env['IMAGE_TAG']) this.error(`no install found in ${flags.dir}`);\n this.log(`Upgrading ${env['MAIL_HOSTNAME']} from ${env['IMAGE_TAG']} to ${flags.tag}`);\n if (!flags['skip-backup']) {\n const out = await backup(flags.dir, path.join(flags.dir, 'backups'), (m) => this.log(m));\n this.log(`Pre-upgrade backup: ${out}`);\n }\n const defaults = defaultEnv({\n hostname: env['MAIL_HOSTNAME'] ?? '',\n tag: flags.tag,\n registry: env['IMAGE_REGISTRY'] ?? '',\n publicIp: env['PUBLIC_IP'],\n });\n const added = Object.keys(defaults).filter((k) => !(k in env));\n if (added.length) this.log(`Adding new .env keys: ${added.join(', ')}`);\n await writeEnv(flags.dir, { ...defaults, ...env, IMAGE_TAG: flags.tag });\n await materialise(flags.dir);\n const c = compose(flags.dir);\n this.log('Pulling images\u2026');\n await c.pull();\n this.log('Restarting services (migrations run on API start)\u2026');\n await c.up();\n const h = await new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`).waitHealthy(180_000);\n this.log(`Upgrade complete: API reports version ${h.version}.`);\n this.log(`If something is wrong: mailctl restore --from <backup dir> restores the pre-upgrade state.`);\n }\n}\n", "// @maildock/mailctl \u2014 mailctl: install, upgrade, backup, restore, doctor (host lifecycle only \u2014 ADR 0005)\nexport const PACKAGE_NAME = '@maildock/mailctl' as const;\nexport { default as Install } from './commands/install.js';\nexport { default as Doctor } from './commands/doctor.js';\nexport { default as Backup } from './commands/backup.js';\nexport { default as Restore } from './commands/restore.js';\nexport { default as Upgrade } from './commands/upgrade.js';\nexport { preflight, blocking, formatChecks } from './lib/preflight.js';\nexport { defaultEnv, readEnv, writeEnv } from './lib/install-dir.js';\n\nimport Install from './commands/install.js';\nimport Doctor from './commands/doctor.js';\nimport Backup from './commands/backup.js';\nimport Restore from './commands/restore.js';\nimport Upgrade from './commands/upgrade.js';\n/** oclif explicit command map (package.json \u2192 oclif.commands). */\nexport const COMMANDS = {\n install: Install,\n doctor: Doctor,\n backup: Backup,\n restore: Restore,\n upgrade: Upgrade,\n};\n"],
5
- "mappings": "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",
6
- "names": ["fs", "path", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "path", "path", "path", "fs", "path", "Command", "Flags", "Command", "Flags", "Command", "Flags", "fs", "path", "Command", "Flags", "path", "fs", "Command", "Flags", "fs", "path", "Command", "Flags", "fs", "path", "Command", "Flags", "path", "Command", "Flags", "path"]
3
+ "sources": ["../src/commands/install.ts", "../../../packages/core/src/primitives.ts", "../../../packages/core/src/entities.ts", "../../../packages/core/src/auth.ts", "../../../packages/core/src/server-config.ts", "../../../packages/core/src/setup.ts", "../../../packages/core/src/dns.ts", "../../../packages/core/src/sieve.ts", "../../../packages/core/src/deliverability.ts", "../../../packages/core/src/dmarc.ts", "../../../packages/core/src/delivery-log.ts", "../../../packages/core/src/license.ts", "../../../packages/core/src/webmail.ts", "../../../packages/core/src/ai.ts", "../../../packages/core/src/migration.ts", "../../../packages/license/src/index.ts", "../src/lib/api.ts", "../src/lib/compose.ts", "../src/lib/host.ts", "../src/lib/install-dir.ts", "../src/lib/registry.ts", "../src/lib/preflight.ts", "../src/commands/doctor.ts", "../src/commands/backup.ts", "../src/commands/restore.ts", "../src/commands/upgrade.ts", "../src/lib/upgrade.ts", "../src/lib/rollback.ts", "../src/commands/license.ts", "../src/lib/license.ts", "../src/index.ts"],
4
+ "sourcesContent": ["import { Command, Flags } from '@oclif/core';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { SetupAnswers } from '@mailserver/core';\nimport { fingerprint } from '@mailserver/license';\nimport { ApiClient, formatDns } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { registryLogin } from '../lib/registry.js';\nimport { publicIp } from '../lib/host.js';\nimport {\n DEFAULT_DIR,\n IMAGE_REGISTRY,\n defaultEnv,\n materialise,\n readEnv,\n writeEnv,\n type EnvFile,\n} from '../lib/install-dir.js';\nimport { DEFAULT_PORTS, blocking, formatChecks, preflight } from '../lib/preflight.js';\n\nexport default class Install extends Command {\n static override description =\n 'Install the mail server on this host: preflight, secrets, compose up, then hand off to setup (wizard URL or --answers).';\n static override examples = [\n '<%= config.bin %> install --hostname mail.example.com',\n '<%= config.bin %> install --answers setup.yaml',\n ];\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n hostname: Flags.string({ description: 'mail hostname (FQDN); read from --answers when given' }),\n answers: Flags.string({\n description: 'answers file (YAML/JSON) for a headless install \u2014 completes setup without the wizard',\n }),\n tag: Flags.string({\n description: 'image tag to install',\n default: process.env['MAILSERVER_VERSION'] ?? 'latest',\n }),\n registry: Flags.string({\n // ECR, not GHCR (ADR 0011). The namespace is part of the value: compose composes images as\n // `${IMAGE_REGISTRY}/<service>:${IMAGE_TAG}`, and the repositories are `maildock/<service>`.\n description: 'image registry/namespace',\n default: process.env['MAILSERVER_REGISTRY'] ?? IMAGE_REGISTRY,\n }),\n 'skip-preflight': Flags.boolean({\n description: 'continue despite failed preflight checks',\n default: false,\n }),\n 'no-pull': Flags.boolean({ description: 'do not pull images (use local builds)', default: false }),\n 'license-id': Flags.string({\n description: 'subscription id, used to authenticate to the private image registry',\n default: process.env['MAILDOCK_LICENSE_ID'] ?? '',\n }),\n 'license-server': Flags.string({\n description: 'licence server base URL (override for self-hosted or testing)',\n default: process.env['MAILDOCK_LICENSE_SERVER'] ?? '',\n }),\n };\n\n async run() {\n const { flags } = await this.parse(Install);\n let answers: SetupAnswers | undefined;\n if (flags.answers) {\n const raw = await fs.readFile(flags.answers, 'utf8');\n const parsed = SetupAnswers.safeParse(parseYaml(raw));\n if (!parsed.success)\n this.error(\n `invalid answers file:\\n${parsed.error.issues.map((i) => ` ${i.path.join('.')}: ${i.message}`).join('\\n')}`,\n );\n answers = parsed.data;\n }\n const hostname = answers?.hostname ?? flags.hostname;\n if (!hostname) this.error('--hostname is required (or provide --answers)');\n\n const dir = flags.dir;\n await fs.mkdir(dir, { recursive: true });\n const existing = await readEnv(dir);\n if (existing['JWT_SECRET']) this.log(`Existing install found in ${dir}; keeping its secrets.`);\n\n this.log('Preflight\u2026');\n const checks = await preflight({\n hostname,\n dataDir: dir,\n ports: DEFAULT_PORTS,\n skipPorts: !!existing['JWT_SECRET'],\n });\n this.log(formatChecks(checks));\n const blockers = blocking(checks);\n if (blockers.length && !flags['skip-preflight'])\n this.error(`${blockers.length} blocking check(s) failed. Fix them or re-run with --skip-preflight.`);\n\n const ip = await publicIp();\n const env: EnvFile = {\n ...defaultEnv({\n hostname,\n tag: flags.tag,\n registry: flags.registry,\n publicIp: ip,\n licenseId: flags['license-id'],\n }),\n ...existing,\n MAIL_HOSTNAME: hostname,\n IMAGE_TAG: flags.tag,\n IMAGE_REGISTRY: flags.registry,\n // flags win over an existing .env: re-running install with a new licence id should adopt it\n ...(flags['license-id'] ? { LICENSE_ID: flags['license-id'] } : {}),\n ...(flags['license-server'] ? { LICENSE_SERVER_URL: flags['license-server'] } : {}),\n };\n await writeEnv(dir, env);\n await materialise(dir);\n this.log(`Wrote ${path.join(dir, '.env')} and compose bundle.`);\n\n const c = compose(dir);\n if (!flags['no-pull']) {\n await this.registryLogin(env);\n this.log('Pulling images\u2026');\n await c.pull();\n }\n this.log('Starting services\u2026');\n await c.up();\n\n const api = new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`);\n const health = await api.waitHealthy(\n 180_000,\n (t) => t % 10_000 < 2000 && this.log(` waiting for API (${Math.round(t / 1000)}s)\u2026`),\n );\n this.log(`API ${health.version} healthy; setup ${health.setup}.`);\n\n if (answers) {\n if (health.setup === 'complete')\n this.error(\n 'setup is already complete on this server; --answers ignored. Use the admin UI to change settings.',\n );\n this.log('Applying answers file\u2026');\n const result = await api.applyAnswers(answers);\n this.log('\\nSetup complete. Publish these DNS records:\\n');\n this.log(formatDns(result.dns));\n this.log(`\\nAdmin: https://${hostname}/ \u00B7 API docs: https://${hostname}/api/v1/docs`);\n } else if (health.setup === 'pending') {\n this.log(\n `\\nOpen the Setup Wizard to finish: https://${hostname}/ (or http://${ip ?? '<server-ip>'}/ until DNS resolves)`,\n );\n } else {\n this.log(`\\nInstalled. Admin: https://${hostname}/`);\n }\n }\n /**\n * Authenticate to the private registry before pulling. An install without a licence id is allowed\n * to continue \u2014 self-built images need no credentials \u2014 but a *failure* to authenticate is\n * reported and stops here, because the alternative is a confusing \"image not found\" from docker.\n */\n private async registryLogin(env: Record<string, string>) {\n const r = await registryLogin(env, fingerprint());\n if (r.status === 'failed') this.error(r.message, { exit: 3 });\n this.log(r.message);\n }\n}\n", "import { z } from 'zod';\n\n/** RFC 1035 hostname label; we additionally require at least two labels for a domain. */\nconst LABEL = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/;\n\nexport function normalizeDomain(input: string): string {\n return input.trim().toLowerCase().replace(/\\.$/, '');\n}\n\nexport const DomainName = z\n .string()\n .overwrite(normalizeDomain)\n .refine(\n (v) => v.length <= 253 && v.split('.').length >= 2 && v.split('.').every((l) => LABEL.test(l)),\n 'must be a fully-qualified domain name',\n );\nexport type DomainName = z.infer<typeof DomainName>;\n\nexport const Hostname = DomainName;\n\n/** Local part: conservative subset (dot-atom without quoting) that Postfix/Dovecot handle without escaping. */\nexport const LocalPart = z\n .string()\n .trim()\n .toLowerCase()\n .refine(\n (v) => /^[a-z0-9](?:[a-z0-9._+-]{0,62}[a-z0-9])?$/.test(v) && !v.includes('..'),\n 'invalid local part',\n );\nexport type LocalPart = z.infer<typeof LocalPart>;\n\nexport const EmailAddress = z\n .string()\n .trim()\n .toLowerCase()\n .refine((v) => {\n const at = v.lastIndexOf('@');\n if (at <= 0) return false;\n return LocalPart.safeParse(v.slice(0, at)).success && DomainName.safeParse(v.slice(at + 1)).success;\n }, 'invalid email address');\nexport type EmailAddress = z.infer<typeof EmailAddress>;\n\nexport function splitAddress(address: string): { localPart: string; domain: string } {\n const at = address.lastIndexOf('@');\n return { localPart: address.slice(0, at), domain: address.slice(at + 1) };\n}\n\nexport const Uuid = z.uuid();\nexport const Password = z.string().min(10, 'at least 10 characters').max(256);\n/** Bytes; 0 means unlimited. */\nexport const QuotaBytes = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);\n\nexport const Paginated = <T extends z.ZodTypeAny>(item: T) =>\n z.object({ items: z.array(item), total: z.number().int() });\n\nexport const PageQuery = z.object({\n limit: z.coerce.number().int().min(1).max(500).default(100),\n offset: z.coerce.number().int().min(0).default(0),\n});\nexport type PageQuery = z.infer<typeof PageQuery>;\n\nexport const ApiError = z.object({\n statusCode: z.number(),\n error: z.string(),\n message: z.string(),\n code: z.string().optional(),\n});\nexport type ApiError = z.infer<typeof ApiError>;\n", "import { z } from 'zod';\nimport { DomainName, EmailAddress, LocalPart, Password, QuotaBytes, Uuid } from './primitives.js';\n\nconst timestamps = { createdAt: z.iso.datetime(), updatedAt: z.iso.datetime() };\n\nexport const Domain = z.object({\n id: Uuid,\n name: DomainName,\n active: z.boolean(),\n defaultQuotaBytes: QuotaBytes,\n ...timestamps,\n});\nexport type Domain = z.infer<typeof Domain>;\nexport const DomainCreate = z.object({ name: DomainName, defaultQuotaBytes: QuotaBytes.default(0) });\nexport const DomainUpdate = z.object({\n active: z.boolean().optional(),\n defaultQuotaBytes: QuotaBytes.optional(),\n});\n\nexport const Mailbox = z.object({\n id: Uuid,\n domainId: Uuid,\n localPart: LocalPart,\n address: EmailAddress,\n displayName: z.string().nullable(),\n quotaBytes: QuotaBytes,\n active: z.boolean(),\n suspended: z.boolean(),\n /**\n * Submission refused because the mailbox looks compromised (mailserver ADR 0016). Distinct from\n * `suspended`: the owner can still sign in and read their mail. Only an admin clears it.\n */\n sendingBlocked: z.boolean(),\n sendingBlockedReason: z.string().nullable(),\n sendingBlockedAt: z.iso.datetime().nullable(),\n ...timestamps,\n});\nexport type Mailbox = z.infer<typeof Mailbox>;\n/** Live storage usage as mirrored by Dovecot `quota_clone`; `updatedAt` is null until Dovecot has reported once. */\nexport const MailboxUsage = z.object({\n mailboxId: Uuid,\n address: EmailAddress,\n quotaBytes: QuotaBytes,\n usedBytes: z.number().int().nonnegative(),\n messages: z.number().int().nonnegative(),\n /** 0\u2013100+ (may exceed 100 when the quota was lowered); null when the quota is unlimited. */\n percent: z.number().nullable(),\n updatedAt: z.iso.datetime().nullable(),\n});\nexport type MailboxUsage = z.infer<typeof MailboxUsage>;\nexport const MailboxCreate = z.object({\n localPart: LocalPart,\n password: Password,\n displayName: z.string().max(200).optional(),\n /** Omit to inherit the domain default. */\n quotaBytes: QuotaBytes.optional(),\n});\nexport const MailboxUpdate = z.object({\n displayName: z.string().max(200).nullable().optional(),\n quotaBytes: QuotaBytes.optional(),\n suspended: z.boolean().optional(),\n active: z.boolean().optional(),\n /** Only ever set to `false` \u2014 an admin restoring a mailbox the worker blocked. */\n sendingBlocked: z.literal(false).optional(),\n});\nexport const MailboxPasswordReset = z.object({ password: Password });\n\nexport const Alias = z.object({\n id: Uuid,\n domainId: Uuid,\n address: EmailAddress,\n destinations: z.array(EmailAddress).min(1),\n active: z.boolean(),\n ...timestamps,\n});\nexport type Alias = z.infer<typeof Alias>;\nexport const AliasCreate = z.object({\n localPart: LocalPart,\n destinations: z.array(EmailAddress).min(1).max(50),\n});\nexport const AliasUpdate = z.object({\n destinations: z.array(EmailAddress).min(1).max(50).optional(),\n active: z.boolean().optional(),\n});\n\nexport const Forwarder = z.object({\n id: Uuid,\n mailboxId: Uuid,\n destination: EmailAddress,\n keepCopy: z.boolean(),\n active: z.boolean(),\n ...timestamps,\n});\nexport type Forwarder = z.infer<typeof Forwarder>;\nexport const ForwarderCreate = z.object({ destination: EmailAddress, keepCopy: z.boolean().default(true) });\nexport const ForwarderUpdate = z.object({ keepCopy: z.boolean().optional(), active: z.boolean().optional() });\n\n/**\n * Catch-all / default address: where mail for a non-existent local part in the domain goes.\n * Without one, unknown recipients are rejected at SMTP time (the safest default).\n */\nexport const Catchall = z.object({\n id: Uuid,\n domainId: Uuid,\n destination: EmailAddress,\n active: z.boolean(),\n ...timestamps,\n});\nexport type Catchall = z.infer<typeof Catchall>;\nexport const CatchallSet = z.object({ destination: EmailAddress, active: z.boolean().default(true) });\n\nexport const AuditEntry = z.object({\n id: z.number().int(),\n actorUserId: Uuid.nullable(),\n actor: z.string().nullable(),\n action: z.string(),\n entity: z.string(),\n entityId: z.string().nullable(),\n before: z.unknown().nullable(),\n after: z.unknown().nullable(),\n ip: z.string().nullable(),\n at: z.iso.datetime(),\n});\nexport type AuditEntry = z.infer<typeof AuditEntry>;\n", "import { z } from 'zod';\nimport { EmailAddress, Password, Uuid } from './primitives.js';\n\nexport const UserRole = z.enum(['owner', 'admin', 'domain_admin', 'mailbox_user']);\nexport type UserRole = z.infer<typeof UserRole>;\n/** Roles that may administer the server; TOTP is required for them once enrolled. */\nexport const ADMIN_ROLES: readonly UserRole[] = ['owner', 'admin'];\n\nexport const User = z.object({\n id: Uuid,\n email: EmailAddress,\n role: UserRole,\n totpEnabled: z.boolean(),\n active: z.boolean(),\n createdAt: z.iso.datetime(),\n});\nexport type User = z.infer<typeof User>;\n\nexport const LoginRequest = z.object({\n email: EmailAddress,\n password: z.string().min(1),\n /** Required once the user has enabled TOTP. */\n totp: z\n .string()\n .regex(/^\\d{6}$/)\n .optional(),\n});\nexport const LoginResponse = z.object({\n accessToken: z.string(),\n expiresIn: z.number().int(),\n user: User,\n});\nexport const TotpSetupResponse = z.object({ secret: z.string(), otpauthUrl: z.string() });\nexport const TotpEnableRequest = z.object({ code: z.string().regex(/^\\d{6}$/) });\n\nexport const ApiToken = z.object({\n id: Uuid,\n name: z.string(),\n role: UserRole,\n lastUsedAt: z.iso.datetime().nullable(),\n expiresAt: z.iso.datetime().nullable(),\n createdAt: z.iso.datetime(),\n});\nexport type ApiToken = z.infer<typeof ApiToken>;\nexport const ApiTokenCreate = z.object({\n name: z.string().min(1).max(100),\n role: UserRole.default('admin'),\n expiresAt: z.iso.datetime().optional(),\n});\nexport const ApiTokenCreated = ApiToken.extend({ token: z.string() });\n\nexport const UserCreate = z.object({ email: EmailAddress, password: Password, role: UserRole });\n\n/** Principal attached to every authenticated request. */\nexport const Principal = z.object({\n /** `mailbox` = a mailbox owner signed in to webmail (role `mailbox_user`); `id` is then the mailbox id. */\n kind: z.enum(['user', 'token', 'mailbox']),\n id: Uuid,\n tenantId: Uuid,\n role: UserRole,\n label: z.string(),\n});\nexport type Principal = z.infer<typeof Principal>;\n", "import { z } from 'zod';\nimport { EmailAddress, Hostname } from './primitives.js';\n\nexport const TlsMode = z.enum(['acme', 'byo', 'none']);\nexport type TlsMode = z.infer<typeof TlsMode>;\n\n/**\n * Server-level settings rendered into Postfix / Dovecot / Rspamd / Caddy configuration (ADR 0005, ADR 0006).\n * Per-mailbox data is NOT here \u2014 daemons read it live from the Postgres views.\n */\nexport const ServerConfig = z.object({\n hostname: Hostname,\n tls: z.object({\n mode: TlsMode,\n /** ACME contact / expiry notices. Required for `acme`. */\n acmeEmail: EmailAddress.optional(),\n }),\n limits: z.object({\n messageSizeBytes: z.number().int().min(1_048_576).max(1_073_741_824).default(52_428_800),\n /** Outbound messages per hour per authenticated sender (Rspamd ratelimit, soft-reject above); 0 = unlimited. */\n perUserMessagesPerHour: z.number().int().min(0).default(500),\n /** Outbound messages per hour per sender domain; 0 = unlimited. */\n perDomainMessagesPerHour: z.number().int().min(0).default(2000),\n smtpdClientConnectionRateLimit: z.number().int().min(0).default(60),\n }),\n relay: z\n .object({\n host: Hostname,\n port: z.number().int().min(1).max(65535).default(587),\n username: z.string().min(1),\n password: z.string().min(1),\n })\n .nullable()\n .default(null),\n spam: z.object({\n rejectScore: z.number().default(15),\n addHeaderScore: z.number().default(6),\n greylistScore: z.number().default(4),\n }),\n /**\n * MTA-STS policy served at https://mta-sts.<domain>/.well-known/mta-sts.txt for every domain (needs\n * `tls.mode = acme`: Caddy issues the mta-sts.<domain> certificates on demand). `none` publishes no policy.\n */\n mtaSts: z\n .object({\n mode: z.enum(['none', 'testing', 'enforce']).default('testing'),\n maxAgeSeconds: z.number().int().min(86_400).max(31_557_600).default(604_800),\n })\n .default({ mode: 'testing', maxAgeSeconds: 604_800 }),\n /**\n * IP warm-up: a server-wide outbound cap that grows from 50/h on day 1 by 25 %/day until it reaches\n * `targetPerHour` (\u2248 3 weeks for 2000/h). Rendered as an rspamd ratelimit bucket and re-applied hourly\n * by the API while the cap is still below the target.\n */\n warmup: z\n .object({\n enabled: z.boolean().default(false),\n /** Day 1 of the schedule (ISO date); set when enabling. */\n startedAt: z.iso.datetime().optional(),\n targetPerHour: z.number().int().min(10).default(2000),\n })\n .default({ enabled: false, targetPerHour: 2000 }),\n /**\n * Compromised-mailbox containment. The worker watches outbound delivery per sender and, when a\n * mailbox's mail is being rejected for reputation or hard-bounce reasons at a rate no legitimate\n * sender produces, blocks its **submission** and raises a critical alert.\n *\n * It never touches `suspended`: the owner keeps IMAP/webmail and can still read their mail. Only\n * sending stops, and only an admin can restore it (mailserver ADR 0016).\n */\n abuse: z\n .object({\n enabled: z.boolean().default(true),\n /** Rolling window the outbound statistics are measured over. */\n windowMinutes: z.number().int().min(5).max(1440).default(60),\n /** A mailbox must have sent at least this many outbound messages in the window to be judged at all. */\n minMessages: z.number().int().min(5).default(20),\n /** Share of a sender's outbound deliveries rejected for reputation reasons that trips containment. */\n reputationRatio: z.number().min(0.01).max(1).default(0.3),\n /** Share of a sender's outbound deliveries that hard-bounced that trips containment. */\n hardBounceRatio: z.number().min(0.01).max(1).default(0.6),\n /**\n * Block sending automatically on a verdict. **Off by default**: containment raises the critical\n * alert and leaves the mailbox sending until an operator has seen the thresholds fire against\n * their own traffic and chosen to arm it (ADR 0016).\n */\n autoBlock: z.boolean().default(false),\n })\n .default({\n enabled: true,\n windowMinutes: 60,\n minMessages: 20,\n reputationRatio: 0.3,\n hardBounceRatio: 0.6,\n autoBlock: false,\n }),\n /** Trusted networks that may relay without auth (docker network is always included by the adapter). */\n trustedNetworks: z.array(z.string().regex(/^[0-9a-f.:]+\\/\\d{1,3}$/i)).default([]),\n});\nexport type ServerConfig = z.infer<typeof ServerConfig>;\n\nexport const ServerConfigInput = ServerConfig.partial({\n limits: true,\n spam: true,\n relay: true,\n trustedNetworks: true,\n mtaSts: true,\n warmup: true,\n abuse: true,\n});\n\nexport const ServerConfigVersion = z.object({\n version: z.number().int(),\n config: ServerConfig,\n status: z.enum(['pending', 'applied', 'failed', 'rolled_back']),\n message: z.string().nullable(),\n createdBy: z.string().nullable(),\n createdAt: z.iso.datetime(),\n});\nexport type ServerConfigVersion = z.infer<typeof ServerConfigVersion>;\n\nexport const WARMUP_DAY1_PER_HOUR = 50;\nexport const WARMUP_GROWTH_PER_DAY = 1.25;\n\n/**\n * Today's outbound cap (messages/hour) for the warm-up schedule, or null when no cap applies (disabled,\n * not started, or the target has been reached). Pure: pass `now`.\n */\nexport function warmupCap(config: Pick<ServerConfig, 'warmup'>, now: Date): number | null {\n const w = config.warmup;\n if (!w.enabled || !w.startedAt) return null;\n const day = Math.floor((now.getTime() - new Date(w.startedAt).getTime()) / 86_400_000) + 1;\n if (day < 1) return WARMUP_DAY1_PER_HOUR;\n const cap = Math.round(WARMUP_DAY1_PER_HOUR * Math.pow(WARMUP_GROWTH_PER_DAY, day - 1));\n return cap >= w.targetPerHour ? null : cap;\n}\n", "import { z } from 'zod';\nimport { DomainName, EmailAddress, Hostname, Password, QuotaBytes } from './primitives.js';\nimport { TlsMode } from './server-config.js';\n\nexport const SetupState = z.enum(['pending', 'complete']);\nexport type SetupState = z.infer<typeof SetupState>;\n\n/** Steps of the setup state machine; each PUT/POST persists its slice, `complete` renders + applies. */\nexport const SetupStatus = z.object({\n state: SetupState,\n version: z.string(),\n steps: z.object({\n hostname: z.boolean(),\n tls: z.boolean(),\n domain: z.boolean(),\n owner: z.boolean(),\n }),\n hostname: Hostname.nullable(),\n tlsMode: TlsMode.nullable(),\n domain: DomainName.nullable(),\n ownerEmail: EmailAddress.nullable(),\n});\nexport type SetupStatus = z.infer<typeof SetupStatus>;\n\nexport const SetupHostname = z.object({ hostname: Hostname });\nexport const SetupTls = z.discriminatedUnion('mode', [\n z.object({ mode: z.literal('acme'), acmeEmail: EmailAddress }),\n z.object({ mode: z.literal('byo'), certificatePem: z.string().min(1), privateKeyPem: z.string().min(1) }),\n z.object({ mode: z.literal('none') }),\n]);\nexport const SetupDomain = z.object({ name: DomainName, defaultQuotaBytes: QuotaBytes.default(0) });\nexport const SetupOwner = z.object({\n email: EmailAddress,\n password: Password,\n /** Also create a mailbox for the owner address when it belongs to the first domain. */\n createMailbox: z.boolean().default(true),\n});\n\n/**\n * Answers file for headless install (`mailctl install --answers setup.yaml`, ADR 0005).\n * Versioned public contract: bump `version` on breaking changes.\n */\nexport const SetupAnswers = z.object({\n version: z.literal(1),\n hostname: Hostname,\n tls: SetupTls,\n domain: SetupDomain,\n owner: SetupOwner,\n});\nexport type SetupAnswers = z.infer<typeof SetupAnswers>;\n", "import { z } from 'zod';\n\nexport const DnsRecord = z.object({\n type: z.enum(['A', 'AAAA', 'MX', 'TXT', 'CNAME', 'SRV', 'PTR']),\n name: z.string(),\n value: z.string(),\n priority: z.number().int().optional(),\n ttl: z.number().int().default(3600),\n purpose: z.string(),\n /** Stable key for matching records across list / verification results. */\n key: z.string(),\n /** Records the domain needs before it is considered ready to send and receive (MX, SPF, DKIM). */\n required: z.boolean().default(true),\n});\nexport type DnsRecord = z.infer<typeof DnsRecord>;\n\n/** A DKIM signing key as exposed to admins; the private key never leaves the server. */\nexport const DkimKey = z.object({\n id: z.string(),\n domainId: z.string(),\n selector: z.string(),\n algorithm: z.literal('rsa'),\n bits: z.number().int(),\n status: z.enum(['active', 'retired']),\n /** Base64 SPKI public key (the `p=` value). */\n publicKey: z.string(),\n dnsName: z.string(),\n dnsValue: z.string(),\n createdAt: z.string(),\n retiredAt: z.string().nullable(),\n});\nexport type DkimKey = z.infer<typeof DkimKey>;\n\n/** What `requiredDnsRecords` needs to know about a domain's DKIM keys. */\nexport interface DkimDnsInput {\n selector: string;\n publicKey: string;\n status: 'active' | 'retired';\n retiredAt?: string | null;\n}\n\n/** MTA-STS policy text (RFC 8461 \u00A73.2) for this server; `id` in the DNS record is derived from it. */\nexport function mtaStsPolicy(hostname: string, mode: 'testing' | 'enforce', maxAgeSeconds: number) {\n return `version: STSv1\\nmode: ${mode}\\nmx: ${hostname}\\nmax_age: ${maxAgeSeconds}\\n`;\n}\n/** Deterministic policy id: changes exactly when the policy text changes (receivers re-fetch on id change). */\nexport function mtaStsId(policy: string) {\n let h = 0x811c9dc5;\n for (const c of policy) {\n h ^= c.charCodeAt(0);\n h = Math.imul(h, 0x01000193) >>> 0;\n }\n return h.toString(16).padStart(8, '0');\n}\n\nexport interface DnsRecordOptions {\n /** Present when MTA-STS is enabled and servable (ACME TLS): the policy id to publish. */\n mtaStsId?: string | undefined;\n /** True when the server can serve https://autoconfig.<domain> / autodiscover.<domain> (ACME TLS). */\n autoconfig?: boolean | undefined;\n}\n\n/** Days a retired selector's DNS record should stay published so in-flight mail still verifies. */\nexport const DKIM_RETIRED_OVERLAP_DAYS = 30;\n\nexport function dkimDnsName(domain: string, selector: string) {\n return `${selector}._domainkey.${domain}`;\n}\nexport function dkimDnsValue(publicKey: string) {\n return `v=DKIM1; k=rsa; p=${publicKey}`;\n}\n\n/**\n * Records a customer must publish for a domain to send and receive mail through `hostname`.\n * Every DKIM key (active, and retired ones inside the overlap window) contributes one TXT record.\n */\nexport function requiredDnsRecords(\n domain: string,\n hostname: string,\n serverIp?: string,\n dkim: DkimDnsInput[] = [],\n opts: DnsRecordOptions = {},\n): DnsRecord[] {\n const records: DnsRecord[] = [\n {\n type: 'MX',\n name: domain,\n value: `${hostname}.`,\n priority: 10,\n ttl: 3600,\n purpose: 'inbound mail',\n key: 'mx',\n required: true,\n },\n {\n type: 'TXT',\n name: domain,\n value: `v=spf1 mx -all`,\n ttl: 3600,\n purpose: 'SPF',\n key: 'spf',\n required: true,\n },\n ];\n for (const k of dkim) {\n const retired = k.status === 'retired';\n records.push({\n type: 'TXT',\n name: dkimDnsName(domain, k.selector),\n value: dkimDnsValue(k.publicKey),\n ttl: 3600,\n purpose: retired\n ? `DKIM (retired selector \u2014 keep until ${overlapEnd(k.retiredAt)})`\n : 'DKIM signing key',\n key: `dkim:${k.selector}`,\n required: !retired,\n });\n }\n records.push(\n {\n type: 'TXT',\n name: `_dmarc.${domain}`,\n value: `v=DMARC1; p=none; rua=mailto:dmarc-reports@${domain}`,\n ttl: 3600,\n purpose:\n 'DMARC (monitor mode; aggregate reports are ingested from dmarc-reports@ \u2014 tighten to quarantine/reject once they look clean)',\n key: 'dmarc',\n required: false,\n },\n ...(opts.mtaStsId\n ? [\n {\n type: 'TXT' as const,\n name: `_mta-sts.${domain}`,\n value: `v=STSv1; id=${opts.mtaStsId}`,\n ttl: 3600,\n purpose: 'MTA-STS (policy served at https://mta-sts.' + domain + '/.well-known/mta-sts.txt)',\n key: 'mta-sts',\n required: false,\n },\n {\n type: 'CNAME' as const,\n name: `mta-sts.${domain}`,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'MTA-STS policy host (certificate issued on first request)',\n key: 'mta-sts-host',\n required: false,\n },\n {\n type: 'TXT' as const,\n name: `_smtp._tls.${domain}`,\n value: `v=TLSRPTv1; rua=mailto:dmarc-reports@${domain}`,\n ttl: 3600,\n purpose: 'TLS-RPT (receivers report TLS failures to the reports mailbox)',\n key: 'tls-rpt',\n required: false,\n },\n ]\n : []),\n ...(opts.autoconfig\n ? [\n {\n type: 'CNAME' as const,\n name: `autoconfig.${domain}`,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'Thunderbird / mobile autoconfig (https://autoconfig.<domain>/mail/config-v1.1.xml)',\n key: 'autoconfig',\n required: false,\n },\n {\n type: 'CNAME' as const,\n name: `autodiscover.${domain}`,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'Outlook autodiscover (https://autodiscover.<domain>/autodiscover/autodiscover.xml)',\n key: 'autodiscover',\n required: false,\n },\n ]\n : []),\n {\n type: 'SRV',\n name: `_imaps._tcp.${domain}`,\n value: `0 1 993 ${hostname}.`,\n ttl: 3600,\n purpose: 'IMAP autodiscover',\n key: 'srv:imaps',\n required: false,\n },\n {\n type: 'SRV',\n name: `_submission._tcp.${domain}`,\n value: `0 1 587 ${hostname}.`,\n ttl: 3600,\n purpose: 'SMTP autodiscover',\n key: 'srv:submission',\n required: false,\n },\n );\n records.push({\n type: 'TXT',\n name: hostname,\n value: 'v=spf1 a -all',\n ttl: 3600,\n purpose: 'SPF for the mail host (forwarded mail is sent as SRS0=\u2026@' + hostname + ')',\n key: 'spf-host',\n required: false,\n });\n if (serverIp) {\n records.unshift({\n type: 'A',\n name: hostname,\n value: serverIp,\n ttl: 3600,\n purpose: 'mail host',\n key: 'a',\n required: true,\n });\n records.push({\n type: 'PTR',\n name: serverIp,\n value: `${hostname}.`,\n ttl: 3600,\n purpose: 'reverse DNS (set at your VPS provider)',\n key: 'ptr',\n required: false,\n });\n }\n return records;\n}\n\nfunction overlapEnd(retiredAt?: string | null) {\n const d = retiredAt ? new Date(retiredAt) : new Date();\n d.setUTCDate(d.getUTCDate() + DKIM_RETIRED_OVERLAP_DAYS);\n return d.toISOString().slice(0, 10);\n}\n\n// ---- verification\n\nexport const DnsRecordStatus = z.enum(['ok', 'propagating', 'mismatch', 'missing', 'error']);\nexport type DnsRecordStatus = z.infer<typeof DnsRecordStatus>;\n\nexport const DnsResolverResult = z.object({\n resolver: z.string(),\n status: DnsRecordStatus,\n observed: z.array(z.string()),\n});\nexport type DnsResolverResult = z.infer<typeof DnsResolverResult>;\nexport const DnsRecordCheck = z.object({\n record: DnsRecord,\n /** `ok` when every resolver agrees, `propagating` when at least one does. */\n status: DnsRecordStatus,\n /** Union of what the resolvers returned for this name/type (for showing the admin what is actually published). */\n observed: z.array(z.string()),\n resolvers: z.array(DnsResolverResult),\n});\nexport type DnsRecordCheck = z.infer<typeof DnsRecordCheck>;\n\nexport const DnsVerification = z.object({\n domainId: z.string(),\n checkedAt: z.string(),\n /** Every `required` record is `ok` or `propagating`. */\n ready: z.boolean(),\n records: z.array(DnsRecordCheck),\n});\nexport type DnsVerification = z.infer<typeof DnsVerification>;\n\n// ---- export formats\n\n/** Split a long TXT value into \u2264255-byte quoted strings (DKIM keys); other values are quoted whole. */\nexport function txtChunks(value: string): string {\n const parts: string[] = [];\n for (let i = 0; i < value.length; i += 255) parts.push(`\"${value.slice(i, i + 255).replace(/\"/g, '\\\\\"')}\"`);\n return parts.join(' ');\n}\n\n/** BIND-style zone snippet (relative names against `$ORIGIN <domain>.`). PTR rows are skipped (set at the VPS). */\nexport function toZoneFile(domain: string, records: DnsRecord[]): string {\n const rel = (name: string) =>\n name === domain ? '@' : name.endsWith('.' + domain) ? name.slice(0, -(domain.length + 1)) : name + '.';\n const lines = [`$ORIGIN ${domain}.`, '$TTL 3600'];\n for (const r of records) {\n if (r.type === 'PTR') continue;\n const name = rel(r.name).padEnd(28);\n switch (r.type) {\n case 'MX':\n lines.push(`${name} IN MX ${r.priority ?? 10} ${r.value}`);\n break;\n case 'TXT':\n lines.push(`${name} IN TXT ${txtChunks(r.value)}`);\n break;\n case 'SRV':\n lines.push(`${name} IN SRV ${r.value}`);\n break;\n default:\n lines.push(`${name} IN ${r.type.padEnd(5)} ${r.value}`);\n }\n }\n return lines.join('\\n') + '\\n';\n}\n\n/** Provider notes shown next to the export (what trips people up at each provider). */\nexport const DNS_PROVIDER_NOTES: Record<'cloudflare' | 'route53' | 'generic', string[]> = {\n cloudflare: [\n 'Set the proxy status to \"DNS only\" (grey cloud) for every record here \u2014 proxied A/CNAME records break mail, MTA-STS and autoconfig.',\n 'Paste TXT values without surrounding quotes; Cloudflare splits long DKIM values itself.',\n 'Cloudflare enforces DNSSEC-safe CNAME flattening at the apex only; the mta-sts/autoconfig/autodiscover CNAMEs are fine as subdomains.',\n ],\n route53: [\n 'TXT values must be quoted, and any string longer than 255 characters split into several quoted strings \u2014 the zone-file export below is already split.',\n 'Use \"Simple routing\"; set TTL 3600.',\n 'The PTR record is set in the EC2/Lightsail console (reverse DNS request), not in Route 53.',\n ],\n generic: [\n 'Names are relative to the domain: \"@\" is the domain itself, \"_dmarc\" means _dmarc.<domain>.',\n 'If the panel rejects a long TXT value, split it into pieces of at most 255 characters (the DKIM record).',\n 'Set the PTR (reverse DNS) at your hosting provider to the mail hostname.',\n ],\n};\n\n// ---- client autoconfiguration documents\n\nexport interface ClientEndpoints {\n hostname: string;\n domain: string;\n displayName?: string | undefined;\n}\n\n/** Thunderbird / K-9 autoconfig (https://autoconfig.<domain>/mail/config-v1.1.xml). */\nexport function autoconfigXml({ hostname, domain, displayName }: ClientEndpoints): string {\n const name = displayName ?? domain;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<clientConfig version=\"1.1\">\n <emailProvider id=\"${domain}\">\n <domain>${domain}</domain>\n <displayName>${name}</displayName>\n <displayShortName>${name}</displayShortName>\n <incomingServer type=\"imap\">\n <hostname>${hostname}</hostname>\n <port>993</port>\n <socketType>SSL</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </incomingServer>\n <incomingServer type=\"imap\">\n <hostname>${hostname}</hostname>\n <port>143</port>\n <socketType>STARTTLS</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </incomingServer>\n <outgoingServer type=\"smtp\">\n <hostname>${hostname}</hostname>\n <port>587</port>\n <socketType>STARTTLS</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </outgoingServer>\n <outgoingServer type=\"smtp\">\n <hostname>${hostname}</hostname>\n <port>465</port>\n <socketType>SSL</socketType>\n <authentication>password-cleartext</authentication>\n <username>%EMAILADDRESS%</username>\n </outgoingServer>\n </emailProvider>\n</clientConfig>\n`;\n}\n\n/** Outlook autodiscover (POX) response for `email`. */\nexport function autodiscoverXml({ hostname }: ClientEndpoints, email: string): string {\n const server = (\n type: 'IMAP' | 'SMTP',\n port: number,\n ssl: 'on' | 'off',\n encryption?: 'TLS',\n ) => ` <Protocol>\n <Type>${type}</Type>\n <Server>${hostname}</Server>\n <Port>${port}</Port>\n <DomainRequired>off</DomainRequired>\n <LoginName>${email}</LoginName>\n <SPA>off</SPA>\n <SSL>${ssl}</SSL>${encryption ? `\\n <Encryption>${encryption}</Encryption>` : ''}\n <AuthRequired>on</AuthRequired>\n </Protocol>`;\n return `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">\n <Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">\n <Account>\n <AccountType>email</AccountType>\n <Action>settings</Action>\n${server('IMAP', 993, 'on')}\n${server('SMTP', 587, 'on', 'TLS')}\n </Account>\n </Response>\n</Autodiscover>\n`;\n}\n", "// Mailbox filters + autoresponder: structured rules (what the UI edits) and their compilation to Sieve\n// (what Dovecot runs). The compiler is pure so it is unit-tested without a daemon; the API pushes the\n// output through ManageSieve as the mailbox's single active script.\nimport { z } from 'zod';\nimport { EmailAddress } from './primitives.js';\n\nconst HeaderName = z\n .string()\n .trim()\n .regex(/^[!-9;-~]{1,64}$/, 'invalid header name');\nconst FolderName = z.string().trim().min(1).max(255);\n\nexport const FilterCondition = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('header'),\n /** e.g. Subject, From, To, X-Spam-Flag. */\n header: HeaderName,\n operator: z.enum([\n 'contains',\n 'not_contains',\n 'is',\n 'not_is',\n 'matches',\n 'not_matches',\n 'exists',\n 'not_exists',\n ]),\n value: z.string().max(998).default(''),\n }),\n z.object({\n type: z.literal('address'),\n header: z.enum(['From', 'To', 'Cc', 'Sender', 'Reply-To']),\n part: z.enum(['all', 'localpart', 'domain']).default('all'),\n operator: z.enum(['contains', 'not_contains', 'is', 'not_is', 'matches', 'not_matches']),\n value: z.string().max(998),\n }),\n z.object({\n type: z.literal('body'),\n operator: z.enum(['contains', 'not_contains']),\n value: z.string().min(1).max(998),\n }),\n z.object({\n type: z.literal('size'),\n operator: z.enum(['over', 'under']),\n /** Bytes. */\n value: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),\n }),\n]);\nexport type FilterCondition = z.infer<typeof FilterCondition>;\n\nexport const FilterAction = z.discriminatedUnion('type', [\n z.object({ type: z.literal('fileinto'), folder: FolderName }),\n z.object({ type: z.literal('copy'), folder: FolderName }),\n z.object({ type: z.literal('redirect'), address: EmailAddress }),\n z.object({ type: z.literal('redirect_copy'), address: EmailAddress }),\n z.object({ type: z.literal('flag'), flag: z.enum(['\\\\Seen', '\\\\Flagged', '\\\\Answered', '\\\\Deleted']) }),\n z.object({ type: z.literal('discard') }),\n z.object({ type: z.literal('keep') }),\n]);\nexport type FilterAction = z.infer<typeof FilterAction>;\n\nexport const FilterRule = z.object({\n name: z.string().trim().min(1).max(100),\n enabled: z.boolean().default(true),\n /** `all` = every condition must hold (allof); `any` = at least one (anyof). */\n match: z.enum(['all', 'any']).default('all'),\n conditions: z.array(FilterCondition).min(1).max(20),\n actions: z.array(FilterAction).min(1).max(10),\n /** Stop processing later rules when this one matched. */\n stop: z.boolean().default(true),\n});\nexport type FilterRule = z.infer<typeof FilterRule>;\n\nexport const Autoresponder = z.object({\n enabled: z.boolean().default(false),\n subject: z.string().trim().max(200).default(''),\n body: z.string().max(10_000).default(''),\n /** Days before the same sender gets another reply (Sieve `:days`, 1\u201330). */\n intervalDays: z.number().int().min(1).max(30).default(1),\n /** ISO dates; outside the window the responder is silent. Null = open-ended. */\n startsAt: z.iso.datetime().nullable().default(null),\n endsAt: z.iso.datetime().nullable().default(null),\n});\nexport type Autoresponder = z.infer<typeof Autoresponder>;\n\n/** What the UI edits. `raw` mode pushes `raw` verbatim and ignores rules + autoresponder. */\nexport const MailboxFiltersSet = z.object({\n mode: z.enum(['rules', 'raw']).default('rules'),\n rules: z.array(FilterRule).max(100).default([]),\n raw: z.string().max(64_000).default(''),\n});\nexport type MailboxFiltersSet = z.infer<typeof MailboxFiltersSet>;\n\nexport const MailboxFilters = z.object({\n mailboxId: z.uuid(),\n mode: z.enum(['rules', 'raw']),\n rules: z.array(FilterRule),\n raw: z.string(),\n autoresponder: Autoresponder,\n /** The Sieve script currently active in Dovecot (compiled from rules, or `raw`). */\n script: z.string(),\n /** When the script was last accepted by Dovecot; null if never pushed. */\n pushedAt: z.iso.datetime().nullable(),\n updatedAt: z.iso.datetime().nullable(),\n});\nexport type MailboxFilters = z.infer<typeof MailboxFilters>;\n\nexport const SieveCheck = z.object({ script: z.string().max(64_000) });\nexport const SieveCheckResult = z.object({ ok: z.boolean(), message: z.string() });\n\n// ---- compiler\n\nconst q = (s: string) => `\"${s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\r?\\n/g, ' ')}\"`;\n/** Sieve multi-line text literal: the body ends at a line holding a single dot (dot-stuffed), then the command's `;`. */\nconst text = (s: string) =>\n 'text:\\r\\n' +\n s\n .replace(/\\r\\n/g, '\\n')\n .split('\\n')\n .map((l) => (l.startsWith('.') ? '.' + l : l))\n .join('\\r\\n') +\n '\\r\\n.\\r\\n;';\n\nconst MATCH: Record<string, { neg: boolean; type: string }> = {\n contains: { neg: false, type: ':contains' },\n not_contains: { neg: true, type: ':contains' },\n is: { neg: false, type: ':is' },\n not_is: { neg: true, type: ':is' },\n matches: { neg: false, type: ':matches' },\n not_matches: { neg: true, type: ':matches' },\n};\n\nfunction condition(c: FilterCondition, req: Set<string>): string {\n const wrap = (neg: boolean, test: string) => (neg ? `not ${test}` : test);\n switch (c.type) {\n case 'header': {\n if (c.operator === 'exists') return `exists ${q(c.header)}`;\n if (c.operator === 'not_exists') return `not exists ${q(c.header)}`;\n const m = MATCH[c.operator]!;\n return wrap(m.neg, `header ${m.type} ${q(c.header)} ${q(c.value)}`);\n }\n case 'address': {\n const m = MATCH[c.operator]!;\n const part = c.part === 'all' ? '' : `:${c.part} `;\n return wrap(m.neg, `address ${part}${m.type} ${q(c.header)} ${q(c.value)}`);\n }\n case 'body': {\n req.add('body');\n const m = MATCH[c.operator]!;\n return wrap(m.neg, `body :text :contains ${q(c.value)}`);\n }\n case 'size':\n return `size :${c.operator} ${c.value}`;\n }\n}\n\nfunction action(a: FilterAction, req: Set<string>): string {\n switch (a.type) {\n case 'fileinto':\n req.add('fileinto');\n return `fileinto ${q(a.folder)};`;\n case 'copy':\n req.add('fileinto');\n req.add('copy');\n return `fileinto :copy ${q(a.folder)};`;\n case 'redirect':\n return `redirect ${q(a.address)};`;\n case 'redirect_copy':\n req.add('copy');\n return `redirect :copy ${q(a.address)};`;\n case 'flag':\n req.add('imap4flags');\n return `addflag ${q(a.flag)};`;\n case 'discard':\n return 'discard;';\n case 'keep':\n return 'keep;';\n }\n}\n\nfunction sieveDate(iso: string): string {\n // Sieve `date`/`currentdate` compare ISO 8601 \"iso8601\" part; the `date` extension gives currentdate.\n return iso.slice(0, 19) + 'Z';\n}\n\n/**\n * Compile structured rules + autoresponder to a Sieve script. Output is deterministic and only uses\n * extensions Pigeonhole ships by default (fileinto, copy, body, imap4flags, vacation, date, relational).\n */\nexport function compileSieve(input: {\n rules: FilterRule[];\n autoresponder?: Autoresponder | undefined;\n}): string {\n const req = new Set<string>();\n const out: string[] = [];\n const ar = input.autoresponder;\n if (ar?.enabled) {\n req.add('vacation');\n const guards: string[] = [];\n if (ar.startsAt) {\n req.add('date');\n req.add('relational');\n guards.push(`currentdate :value \"ge\" \"iso8601\" ${q(sieveDate(ar.startsAt))}`);\n }\n if (ar.endsAt) {\n req.add('date');\n req.add('relational');\n guards.push(`currentdate :value \"le\" \"iso8601\" ${q(sieveDate(ar.endsAt))}`);\n }\n const vac = `vacation :days ${ar.intervalDays}${ar.subject ? ` :subject ${q(ar.subject)}` : ''} ${text(ar.body)}`;\n out.push('# Autoresponder');\n // The text: literal is not indented \u2014 its terminating line must be exactly \".\".\n if (guards.length) out.push(`if allof(${guards.join(', ')}) {`, vac, '}');\n else out.push(vac);\n out.push('');\n }\n for (const r of input.rules) {\n if (!r.enabled) continue;\n const tests = r.conditions.map((c) => condition(c, req));\n const test =\n tests.length === 1 ? tests[0]! : `${r.match === 'all' ? 'allof' : 'anyof'}(${tests.join(', ')})`;\n // imap4flags: addflag only affects fileinto/keep/redirect that run *after* it, so flags go first.\n const ordered = [\n ...r.actions.filter((a) => a.type === 'flag'),\n ...r.actions.filter((a) => a.type !== 'flag'),\n ];\n const body = ordered.map((a) => action(a, req));\n if (r.stop) body.push('stop;');\n out.push(`# rule: ${r.name.replace(/[\\r\\n]/g, ' ')}`, `if ${test} {`, ...body.map(indent), '}', '');\n }\n const header = req.size ? `require [${[...req].sort().map(q).join(', ')}];\\n\\n` : '';\n return (\n `# Generated by mailserver \u2014 edit in the admin UI (Mailbox \u2192 Filters). Manual edits are overwritten.\\n${header}${out.join('\\n')}`.trimEnd() +\n '\\n'\n );\n}\n\nconst indent = (s: string) =>\n s\n .split('\\n')\n .map((l) => (l ? ' ' + l : l))\n .join('\\n');\n", "import { z } from 'zod';\n\n// ---- alerts (raised by the worker's scheduled checks; listed on the dashboard)\n\nexport const AlertSeverity = z.enum(['warning', 'critical']);\nexport type AlertSeverity = z.infer<typeof AlertSeverity>;\n\nexport const Alert = z.object({\n id: z.string(),\n /**\n * Stable identity of the condition (`dns:<domainId>:<recordKey>`, `dnsbl:<zone>`, `rdns`,\n * `tls_expiry`, `outbound_abuse:<mailboxId>`).\n */\n key: z.string(),\n kind: z.enum(['dns_drift', 'dnsbl', 'rdns', 'tls_expiry', 'outbound_abuse']),\n severity: AlertSeverity,\n title: z.string(),\n detail: z.record(z.string(), z.unknown()),\n firstSeen: z.string(),\n lastSeen: z.string(),\n resolvedAt: z.string().nullable(),\n});\nexport type Alert = z.infer<typeof Alert>;\n\n// ---- reputation checks of the server itself\n\nexport const DnsblResult = z.object({\n zone: z.string(),\n status: z.enum(['clean', 'listed', 'error']),\n detail: z.string().nullable(),\n delistUrl: z.string(),\n});\nexport type DnsblResult = z.infer<typeof DnsblResult>;\n\nexport const RdnsResult = z.object({\n ip: z.string(),\n hostname: z.string(),\n ptr: z.array(z.string()),\n ptrMatches: z.boolean(),\n forwardConfirmed: z.boolean(),\n heloResolves: z.boolean(),\n ok: z.boolean(),\n error: z.string().nullable(),\n});\nexport type RdnsResult = z.infer<typeof RdnsResult>;\n\nexport const TlsExpiryResult = z.object({\n subject: z.string(),\n issuer: z.string(),\n notAfter: z.string(),\n daysLeft: z.number().int(),\n});\nexport type TlsExpiryResult = z.infer<typeof TlsExpiryResult>;\n\n/** Stored by the worker's hourly reputation job; `null` fields mean the check could not run (no PUBLIC_IP, no cert). */\nexport const ReputationReport = z.object({\n checkedAt: z.string(),\n ip: z.string().nullable(),\n dnsbl: z.array(DnsblResult),\n rdns: RdnsResult.nullable(),\n tls: TlsExpiryResult.nullable(),\n});\nexport type ReputationReport = z.infer<typeof ReputationReport>;\n", "import { z } from 'zod';\n\n/** One `<record>` of an aggregate report (RFC 7489 \u00A77.2). */\nexport const DmarcRecord = z.object({\n sourceIp: z.string(),\n count: z.number().int(),\n disposition: z.enum(['none', 'quarantine', 'reject']),\n dkim: z.enum(['pass', 'fail']),\n spf: z.enum(['pass', 'fail']),\n headerFrom: z.string(),\n /** Per-mechanism auth results: `dkim:selector:result`, `spf:domain:result`. */\n authResults: z.array(z.string()),\n});\nexport type DmarcRecord = z.infer<typeof DmarcRecord>;\n\nexport const DmarcReport = z.object({\n id: z.string(),\n domainId: z.string(),\n reportId: z.string(),\n orgName: z.string(),\n orgEmail: z.string().nullable(),\n /** Reporting window (ISO). */\n begin: z.string(),\n end: z.string(),\n policy: z.object({\n domain: z.string(),\n p: z.string().nullable(),\n sp: z.string().nullable(),\n adkim: z.string().nullable(),\n aspf: z.string().nullable(),\n pct: z.number().int().nullable(),\n }),\n records: z.array(DmarcRecord),\n receivedAt: z.string(),\n});\nexport type DmarcReport = z.infer<typeof DmarcReport>;\n\n/** What the dashboard shows: totals + per-source breakdown over a window. */\nexport const DmarcSummary = z.object({\n domainId: z.string(),\n days: z.number().int(),\n reports: z.number().int(),\n messages: z.number().int(),\n /** Messages where DKIM or SPF passed with alignment (DMARC pass). */\n aligned: z.number().int(),\n dkimPass: z.number().int(),\n spfPass: z.number().int(),\n byDisposition: z.object({ none: z.number().int(), quarantine: z.number().int(), reject: z.number().int() }),\n sources: z.array(\n z.object({\n sourceIp: z.string(),\n messages: z.number().int(),\n aligned: z.number().int(),\n dkimPass: z.number().int(),\n spfPass: z.number().int(),\n reporters: z.array(z.string()),\n }),\n ),\n latest: z.array(DmarcReport),\n});\nexport type DmarcSummary = z.infer<typeof DmarcSummary>;\n", "import { z } from 'zod';\n\nexport const DeliveryStatus = z.enum(['sent', 'bounced', 'deferred', 'expired']);\nexport type DeliveryStatus = z.infer<typeof DeliveryStatus>;\n\n/** Why a delivery failed (or `delivered`): what the admin needs to know to act. */\nexport const DeliveryClass = z.enum(['delivered', 'hard', 'soft', 'policy', 'reputation']);\nexport type DeliveryClass = z.infer<typeof DeliveryClass>;\n\nexport const DeliveryDirection = z.enum(['inbound', 'outbound', 'internal']);\nexport type DeliveryDirection = z.infer<typeof DeliveryDirection>;\n\nexport const DeliveryEvent = z.object({\n id: z.string(),\n queueId: z.string(),\n at: z.string(),\n /** Envelope sender as logged by qmgr (SRS-rewritten for forwards); null until the from= line was seen. */\n sender: z.string().nullable(),\n messageId: z.string().nullable(),\n recipient: z.string(),\n origTo: z.string().nullable(),\n relay: z.string(),\n status: DeliveryStatus,\n class: DeliveryClass,\n dsn: z.string().nullable(),\n /** The remote server's reply or Postfix's reason, verbatim. */\n detail: z.string(),\n direction: DeliveryDirection,\n});\nexport type DeliveryEvent = z.infer<typeof DeliveryEvent>;\n\nexport const DeliveryLogQuery = z.object({\n /** Matches sender, recipient, original recipient, message-id or queue id (substring, case-insensitive). */\n q: z.string().trim().max(200).optional(),\n status: DeliveryStatus.optional(),\n class: DeliveryClass.optional(),\n direction: DeliveryDirection.optional(),\n /** Restrict to a domain (sender or recipient side). */\n domain: z.string().trim().toLowerCase().max(253).optional(),\n days: z.coerce.number().int().min(1).max(90).default(7),\n limit: z.coerce.number().int().min(1).max(500).default(100),\n offset: z.coerce.number().int().min(0).default(0),\n});\nexport type DeliveryLogQuery = z.infer<typeof DeliveryLogQuery>;\n\nexport const DeliveryLogPage = z.object({\n items: z.array(DeliveryEvent),\n total: z.number().int(),\n /** Counts by class over the same filter (for the stat tiles). */\n byClass: z.object({\n delivered: z.number().int(),\n hard: z.number().int(),\n soft: z.number().int(),\n policy: z.number().int(),\n reputation: z.number().int(),\n }),\n});\nexport type DeliveryLogPage = z.infer<typeof DeliveryLogPage>;\n", "import { z } from 'zod';\n\n// ---- licensing (Phase 5b): what the admin UI and CLI see.\n//\n// The *verification* of a key lives in `@mailserver/license`, which is node-only. These are the\n// shapes that cross the API boundary, so they stay here in browser-safe `core` \u2014 the SPA renders a\n// licence page and must never import crypto to do it.\n\nexport const LicenseStateName = z.enum(['unlicensed', 'active', 'grace', 'degraded']);\nexport type LicenseStateName = z.infer<typeof LicenseStateName>;\n\n/** The decoded key, minus anything the customer has no use for. Null while unlicensed. */\nexport const LicenseDetails = z.object({\n licenseId: z.string(),\n tier: z.string(),\n /** 0 means unlimited. */\n maxMailboxes: z.number().int(),\n maxDomains: z.number().int(),\n issuedAt: z.string(),\n expiresAt: z.string(),\n features: z.array(z.string()),\n issuedTo: z.string().nullable(),\n /**\n * The machine fingerprint the key is pinned to, or null for a portable key. Shown rather than\n * reduced to a boolean because a transfer is a conversation about two fingerprints \u2014 the one in\n * the key and the one this server reports \u2014 and the admin has to be able to quote both.\n */\n boundTo: z.string().nullable(),\n});\nexport type LicenseDetails = z.infer<typeof LicenseDetails>;\n\nexport const LicenseStatus = z.object({\n state: LicenseStateName,\n /** Plain-English explanation for anything other than `active`; shown verbatim in a banner. */\n reason: z.string().nullable(),\n /** Days until the next step down (grace \u2192 degraded); null when nothing is counting down. */\n daysRemaining: z.number().int().nullable(),\n license: LicenseDetails.nullable(),\n /** Current usage, so the UI can show \"18 of 25 mailboxes\" without a second call. */\n usage: z.object({ mailboxes: z.number().int(), domains: z.number().int() }),\n lastHeartbeatAt: z.string().nullable(),\n /** Why the last heartbeat failed, if it did. */\n lastError: z.string().nullable(),\n /** This server's fingerprint, so support can match it against what was issued. */\n machine: z.string(),\n});\nexport type LicenseStatus = z.infer<typeof LicenseStatus>;\n\nexport const LicenseActivate = z.object({\n /** The `mdl1.\u2026` key, as pasted. Whitespace is trimmed before verification. */\n key: z.string().min(16).max(8000),\n});\nexport type LicenseActivate = z.infer<typeof LicenseActivate>;\n", "import { z } from 'zod';\n\n// ---- webmail (Phase 4): everything the SPA sees; IMAP details stay behind the API bridge (ADR 0010)\n\nexport const FolderRole = z.enum(['inbox', 'sent', 'drafts', 'trash', 'junk', 'archive', 'other']);\nexport type FolderRole = z.infer<typeof FolderRole>;\n\nexport const Folder = z.object({\n /** IMAP path (also the id used in every other endpoint), e.g. `INBOX`, `Sent`, `Projects/2026`. */\n path: z.string(),\n name: z.string(),\n role: FolderRole,\n delimiter: z.string().nullable(),\n subscribed: z.boolean(),\n messages: z.number().int(),\n unseen: z.number().int(),\n});\nexport type Folder = z.infer<typeof Folder>;\n\nexport const Address = z.object({ name: z.string().nullable(), address: z.string() });\nexport type Address = z.infer<typeof Address>;\n\nexport const MessageSummary = z.object({\n uid: z.number().int(),\n folder: z.string(),\n messageId: z.string().nullable(),\n /** `In-Reply-To` head used for conversation grouping on the client. */\n inReplyTo: z.string().nullable(),\n /** `References` chain (oldest first, angle brackets stripped) \u2014 the other half of the grouping. */\n references: z.array(z.string()).default([]),\n subject: z.string(),\n from: z.array(Address),\n to: z.array(Address),\n date: z.string().nullable(),\n size: z.number().int(),\n flags: z.array(z.string()),\n seen: z.boolean(),\n flagged: z.boolean(),\n answered: z.boolean(),\n hasAttachments: z.boolean(),\n});\nexport type MessageSummary = z.infer<typeof MessageSummary>;\n\nexport const MessageListQuery = z.object({\n folder: z.string().default('INBOX'),\n /** Newest first; `before` = uid to continue from (exclusive) for infinite scroll. */\n before: z.coerce.number().int().positive().optional(),\n limit: z.coerce.number().int().min(1).max(200).default(50),\n /** Free-text search (from/to/subject/body via IMAP SEARCH, Dovecot FTS when enabled). */\n q: z.string().trim().max(200).optional(),\n unseen: z\n .enum(['true', 'false'])\n .optional()\n .transform((v) => v === 'true'),\n flagged: z\n .enum(['true', 'false'])\n .optional()\n .transform((v) => v === 'true'),\n attachments: z\n .enum(['true', 'false'])\n .optional()\n .transform((v) => v === 'true'),\n since: z.iso.date().optional(),\n until: z.iso.date().optional(),\n});\nexport type MessageListQuery = z.infer<typeof MessageListQuery>;\n\nexport const MessageList = z.object({\n folder: z.string(),\n items: z.array(MessageSummary),\n /** Total messages matching (folder size when unfiltered). */\n total: z.number().int(),\n /** Pass as `before` to fetch the next (older) page; null when exhausted. */\n next: z.number().int().nullable(),\n});\nexport type MessageList = z.infer<typeof MessageList>;\n\nexport const Attachment = z.object({\n /** IMAP body part id (`2`, `1.2`) \u2014 download via `/mail/messages/{uid}/parts/{part}`. */\n part: z.string(),\n filename: z.string(),\n contentType: z.string(),\n size: z.number().int(),\n /** `cid:` reference for inline images, without the angle brackets. */\n contentId: z.string().nullable(),\n inline: z.boolean(),\n});\nexport type Attachment = z.infer<typeof Attachment>;\n\nexport const Message = MessageSummary.extend({\n cc: z.array(Address),\n bcc: z.array(Address),\n replyTo: z.array(Address),\n /** Plain-text body (derived from HTML when the message has none). */\n text: z.string(),\n /** Raw HTML body; the client sanitises before rendering (DOMPurify + sandboxed iframe). */\n html: z.string().nullable(),\n attachments: z.array(Attachment),\n headers: z.record(z.string(), z.string()),\n});\nexport type Message = z.infer<typeof Message>;\n\nexport const FlagsUpdate = z.object({\n folder: z.string(),\n uids: z.array(z.number().int().positive()).min(1).max(1000),\n add: z.array(z.string()).default([]),\n remove: z.array(z.string()).default([]),\n});\nexport const MoveRequest = z.object({\n folder: z.string(),\n uids: z.array(z.number().int().positive()).min(1).max(1000),\n to: z.string(),\n});\nexport const DeleteRequest = z.object({\n folder: z.string(),\n uids: z.array(z.number().int().positive()).min(1).max(1000),\n /** Skip the Trash and expunge immediately (what \"delete\" does inside Trash/Junk). */\n permanent: z.boolean().default(false),\n});\nexport const FolderCreate = z.object({ path: z.string().min(1).max(255) });\nexport const FolderRename = z.object({ path: z.string().min(1), to: z.string().min(1).max(255) });\n\nexport const SendRequest = z.object({\n to: z.array(z.string().min(3)).min(1),\n cc: z.array(z.string()).default([]),\n bcc: z.array(z.string()).default([]),\n subject: z.string().max(998).default(''),\n text: z.string().default(''),\n html: z.string().optional(),\n /** Message-ID being replied to / forwarded (sets In-Reply-To/References and the \\Answered flag). */\n inReplyTo: z.string().optional(),\n replyFolder: z.string().optional(),\n replyUid: z.number().int().positive().optional(),\n /** Draft to delete after a successful send. */\n draftUid: z.number().int().positive().optional(),\n /** Attachments already uploaded with POST /mail/uploads (ids), plus optional forwarded parts. */\n uploads: z.array(z.string()).default([]),\n forwardParts: z\n .array(z.object({ folder: z.string(), uid: z.number().int().positive(), part: z.string() }))\n .default([]),\n});\nexport type SendRequest = z.infer<typeof SendRequest>;\n\nexport const DraftSave = SendRequest.omit({ draftUid: true, replyFolder: true, replyUid: true }).extend({\n /** Existing draft uid to replace. */\n uid: z.number().int().positive().optional(),\n});\nexport const DraftSaved = z.object({ uid: z.number().int(), folder: z.string() });\nexport const Upload = z.object({\n id: z.string(),\n filename: z.string(),\n size: z.number().int(),\n contentType: z.string(),\n});\n\n/** A recipient the mailbox has written to before, for composer autocomplete. */\nexport const Contact = z.object({\n address: z.string(),\n /** Best display name seen for the address (the most recent non-empty one), else null. */\n name: z.string().nullable(),\n /** How many sent messages went to this address \u2014 the primary ranking. */\n count: z.number().int(),\n /** ISO date of the most recent message to this address; ties are broken on it. */\n lastUsed: z.string().nullable(),\n});\nexport type Contact = z.infer<typeof Contact>;\nexport const ContactQuery = z.object({\n /** Prefix/substring match over the address and display name; empty returns the most-used. */\n q: z.string().trim().max(100).optional(),\n limit: z.coerce.number().int().min(1).max(50).default(10),\n});\nexport type ContactQuery = z.infer<typeof ContactQuery>;\n\nexport const MailEvent = z.object({\n type: z.enum(['exists', 'expunge', 'flags', 'connected', 'error']),\n folder: z.string().nullable(),\n uid: z.number().int().nullable(),\n count: z.number().int().nullable(),\n});\nexport type MailEvent = z.infer<typeof MailEvent>;\n\nexport const MailboxSettings = z.object({\n signature: z.string().max(4000).default(''),\n signatureHtml: z.boolean().default(false),\n /** Reply-quoting and display preferences. */\n replyQuote: z.boolean().default(true),\n messagesPerPage: z.number().int().min(10).max(200).default(50),\n /** Group the message list into conversations (References/In-Reply-To, subject as a fallback). */\n threaded: z.boolean().default(true),\n theme: z.enum(['system', 'light', 'dark']).default('system'),\n /** Delay before a queued send actually goes out (undo window), seconds. */\n undoSendSeconds: z.number().int().min(0).max(30).default(5),\n});\nexport type MailboxSettings = z.infer<typeof MailboxSettings>;\nexport const MailboxSettingsUpdate = MailboxSettings.partial();\n\nexport const MailAccount = z.object({\n mailboxId: z.string(),\n address: z.string(),\n displayName: z.string().nullable(),\n quotaBytes: z.number().int(),\n usedBytes: z.number().int(),\n settings: MailboxSettings,\n});\nexport type MailAccount = z.infer<typeof MailAccount>;\nexport const PasswordChange = z.object({ current: z.string().min(1), password: z.string().min(10).max(200) });\n", "import { z } from 'zod';\n\n// ---- AI providers (Phase 7): what the admin UI, the API and the run-book see.\n//\n// The *adapters* live in `@mailserver/ai`, which is node-only (DNS resolution for local-only\n// enforcement, streaming HTTP, the Anthropic SDK). These are the shapes that cross the API boundary\n// and the catalogue the SPA renders, so they stay here in browser-safe `core` \u2014 same split as\n// licensing.\n//\n// AI is off until an admin configures it, and the customer supplies the inference (ADR 0004).\n\n/**\n * The wire protocol an adapter speaks. Only three exist; most vendors share the middle one, which is\n * why a customer picks a `ProviderId` below rather than one of these.\n */\nexport const AiTransport = z.enum(['anthropic', 'openai', 'ollama']);\nexport type AiTransport = z.infer<typeof AiTransport>;\n\n/** Every provider a customer can choose. Described one by one in `AI_PROVIDER_CATALOGUE`. */\nexport const AiProviderId = z.enum([\n 'anthropic',\n 'openai',\n 'gemini',\n 'grok',\n 'llama',\n 'groq',\n 'vllm',\n 'ollama',\n 'openai-compatible',\n]);\nexport type AiProviderId = z.infer<typeof AiProviderId>;\n\nexport const AiCapabilities = z.object({\n streaming: z.boolean(),\n tools: z.boolean(),\n promptCaching: z.boolean(),\n embeddings: z.boolean(),\n});\nexport type AiCapabilities = z.infer<typeof AiCapabilities>;\n\n/**\n * Everything the admin UI needs to describe one provider.\n *\n * This exists so that \"which providers are supported\" has exactly one answer, written down. Without\n * it, support is an emergent property of a protocol matching \u2014 an admin would have to already know\n * that Gemini works if you paste the right base URL. The picker, the run-book and the config\n * validator all read this list.\n */\nexport const AiProviderCatalogueEntry = z.object({\n id: AiProviderId,\n /** Shown in the provider picker. */\n label: z.string(),\n /** Who the customer gets a key from. */\n vendor: z.string(),\n transport: AiTransport,\n /** Pre-filled base URL; `null` means the admin must supply one (self-hosted). */\n defaultEndpoint: z.string().nullable(),\n auth: z.enum(['bearer', 'x-api-key', 'none']),\n requiresKey: z.boolean(),\n /** Whether this endpoint can sit on a private network, i.e. is usable with `localOnly`. */\n canBeLocal: z.boolean(),\n capabilities: AiCapabilities,\n /**\n * `verified` means every Phase 7 feature has been run against it and the phase gate covers it.\n * `untested` means it speaks a protocol we implement and is expected to work, but we have not\n * proven it \u2014 shown as such in the UI so nobody reads compatibility as a guarantee. Flipped to\n * `verified` by an actual gate run, never optimistically.\n */\n verification: z.enum(['verified', 'untested']),\n /** Where the customer gets a key and finds the model names. */\n docsUrl: z.string(),\n /**\n * Model ids move faster than our release cycle, so the authoritative list is whatever the endpoint\n * itself returns when the admin tests the connection. These are a starting suggestion only, and\n * are deliberately empty where we would otherwise be guessing.\n */\n suggestedModels: z.array(z.string()),\n notes: z.string(),\n});\nexport type AiProviderCatalogueEntry = z.infer<typeof AiProviderCatalogueEntry>;\n\nconst HOSTED: AiCapabilities = { streaming: true, tools: true, promptCaching: false, embeddings: true };\nconst HOSTED_NO_EMBEDDINGS: AiCapabilities = { ...HOSTED, embeddings: false };\n\nexport const AI_PROVIDER_CATALOGUE: readonly AiProviderCatalogueEntry[] = [\n {\n id: 'anthropic',\n label: 'Anthropic (Claude)',\n vendor: 'Anthropic',\n transport: 'anthropic',\n defaultEndpoint: 'https://api.anthropic.com',\n auth: 'x-api-key',\n requiresKey: true,\n canBeLocal: false,\n capabilities: { streaming: true, tools: true, promptCaching: true, embeddings: false },\n // Verified 2026-08-31 against `claude-haiku-4-5`: the provider contract (5/5), the prompt\n // evaluation (11/11) and **every Phase 7 feature** \u2014 thread summary, explain, smart replies,\n // all four compose actions, and the copilot including a stored proposal and its approval.\n verification: 'verified',\n docsUrl: 'https://platform.claude.com/docs',\n suggestedModels: ['claude-opus-5', 'claude-sonnet-5', 'claude-haiku-4-5'],\n notes:\n 'The only provider here with prompt caching, which makes re-sending a long thread on every follow-up cheap. No embeddings endpoint.',\n },\n {\n id: 'openai',\n label: 'OpenAI',\n vendor: 'OpenAI',\n transport: 'openai',\n defaultEndpoint: 'https://api.openai.com/v1',\n auth: 'bearer',\n requiresKey: true,\n canBeLocal: false,\n capabilities: HOSTED,\n // Verified 2026-08-31 against `gpt-4o-mini`: the provider contract (5/5), the prompt evaluation\n // (12/12) and every Phase 7 feature, including the copilot's tool use and a stored proposal.\n // The contract run found the cancel defect the other two transports were hiding.\n verification: 'verified',\n docsUrl: 'https://platform.openai.com/docs/api-reference/chat',\n suggestedModels: ['gpt-4o-mini'],\n notes: 'The reference implementation of the chat-completions protocol every entry below shares.',\n },\n {\n id: 'gemini',\n label: 'Google Gemini',\n vendor: 'Google AI Studio',\n transport: 'openai',\n defaultEndpoint: 'https://generativelanguage.googleapis.com/v1beta/openai/',\n auth: 'bearer',\n requiresKey: true,\n canBeLocal: false,\n capabilities: HOSTED,\n verification: 'untested',\n docsUrl: 'https://ai.google.dev/gemini-api/docs/openai',\n suggestedModels: [],\n notes:\n \"Google's OpenAI-compatible route. The trailing /openai/ is required and the model must be a Gemini id. Gemini's native API has extras (context caching, safety settings) this route does not expose.\",\n },\n {\n id: 'grok',\n label: 'xAI (Grok)',\n vendor: 'xAI',\n transport: 'openai',\n defaultEndpoint: 'https://api.x.ai/v1',\n auth: 'bearer',\n requiresKey: true,\n canBeLocal: false,\n capabilities: HOSTED_NO_EMBEDDINGS,\n verification: 'untested',\n docsUrl: 'https://docs.x.ai/docs/api-reference',\n suggestedModels: [],\n notes: 'Chat-completions compatible. Embeddings are not exposed on this route.',\n },\n {\n id: 'llama',\n label: 'Meta Llama API',\n vendor: 'Meta',\n transport: 'openai',\n defaultEndpoint: 'https://api.llama.com/compat/v1/',\n auth: 'bearer',\n requiresKey: true,\n canBeLocal: false,\n capabilities: HOSTED_NO_EMBEDDINGS,\n verification: 'untested',\n docsUrl: 'https://llama.developer.meta.com/docs/features/compatibility/',\n suggestedModels: [],\n notes:\n \"Meta's hosted Llama models over their compatibility route; access is waitlisted and regionally limited. To run Llama without that, use Ollama or a self-hosted vLLM below.\",\n },\n {\n id: 'groq',\n label: 'Groq',\n vendor: 'Groq',\n transport: 'openai',\n defaultEndpoint: 'https://api.groq.com/openai/v1',\n auth: 'bearer',\n requiresKey: true,\n canBeLocal: false,\n capabilities: HOSTED_NO_EMBEDDINGS,\n verification: 'untested',\n docsUrl: 'https://console.groq.com/docs/openai',\n suggestedModels: [],\n notes: 'Hosted open-weight models; the quickest of the hosted options for short inbox features.',\n },\n {\n id: 'vllm',\n label: 'vLLM (self-hosted)',\n vendor: 'self-hosted',\n transport: 'openai',\n defaultEndpoint: null,\n auth: 'bearer',\n requiresKey: false,\n canBeLocal: true,\n capabilities: HOSTED,\n verification: 'untested',\n docsUrl: 'https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html',\n suggestedModels: [],\n notes:\n 'Your own GPU box serving the chat-completions protocol. Usable with local-only mode when it sits on a private network.',\n },\n {\n id: 'ollama',\n label: 'Ollama (local)',\n vendor: 'self-hosted',\n transport: 'ollama',\n defaultEndpoint: 'http://localhost:11434',\n auth: 'none',\n requiresKey: false,\n canBeLocal: true,\n capabilities: { streaming: true, tools: true, promptCaching: false, embeddings: true },\n verification: 'untested',\n docsUrl: 'https://docs.ollama.com/api',\n suggestedModels: [],\n notes:\n 'Runs the model on the mail server itself, so no message content leaves the machine. Tool use depends on the model you pull; a small model makes a poor admin copilot.',\n },\n {\n id: 'openai-compatible',\n label: 'Other OpenAI-compatible endpoint',\n vendor: 'other',\n transport: 'openai',\n defaultEndpoint: null,\n auth: 'bearer',\n requiresKey: false,\n canBeLocal: true,\n capabilities: HOSTED,\n verification: 'untested',\n docsUrl: 'https://platform.openai.com/docs/api-reference/chat',\n suggestedModels: [],\n notes:\n 'Anything else speaking POST /chat/completions \u2014 llama.cpp, LM Studio, OpenRouter, Together, a corporate gateway. Untested by us by definition.',\n },\n];\n\nconst CATALOGUE_BY_ID = new Map(AI_PROVIDER_CATALOGUE.map((e) => [e.id, e]));\n\nexport function aiProviderEntry(id: AiProviderId): AiProviderCatalogueEntry {\n const entry = CATALOGUE_BY_ID.get(id);\n if (!entry) throw new Error(`unknown AI provider: ${id}`);\n return entry;\n}\n\n/** The providers an admin may choose while `localOnly` is in force. */\nexport const LOCAL_AI_PROVIDER_IDS: AiProviderId[] = AI_PROVIDER_CATALOGUE.filter((e) => e.canBeLocal).map(\n (e) => e.id,\n);\n\n/** What an admin submits when configuring AI. The key is write-only and never comes back out. */\nexport const AiProviderConfigInput = z\n .object({\n provider: AiProviderId,\n model: z.string().trim().min(1).max(200),\n /** Overrides the catalogue default; required where that entry has none. */\n endpoint: z.string().trim().url().max(2000).optional(),\n /** Omit to keep the stored key unchanged; empty string clears it. */\n apiKey: z.string().max(8000).optional(),\n /** Refuse any endpoint that is not on a private network. */\n localOnly: z.boolean().default(false),\n })\n .superRefine((cfg, ctx) => {\n const entry = CATALOGUE_BY_ID.get(cfg.provider);\n if (!entry) return;\n if (!entry.defaultEndpoint && !cfg.endpoint)\n ctx.addIssue({\n code: 'custom',\n path: ['endpoint'],\n message: `${entry.label} is self-hosted, so it needs an endpoint URL`,\n });\n // A hosted vendor can never satisfy local-only, so say so at the point of choosing rather than\n // letting the DNS check reject it later with a vaguer message.\n if (cfg.localOnly && !entry.canBeLocal)\n ctx.addIssue({\n code: 'custom',\n path: ['provider'],\n message: `${entry.label} is a hosted service and cannot be used with local-only mode; choose ${LOCAL_AI_PROVIDER_IDS.join(', ')}`,\n });\n });\nexport type AiProviderConfigInput = z.infer<typeof AiProviderConfigInput>;\n\n/**\n * What the API returns. Never contains the key \u2014 only whether one is stored, and a hint at which.\n * `configured: false` is the shipped state: AI is off until an admin turns it on.\n */\nexport const AiProviderConfigView = z.discriminatedUnion('configured', [\n z.object({ configured: z.literal(false) }),\n z.object({\n configured: z.literal(true),\n provider: AiProviderId,\n model: z.string(),\n endpoint: z.string(),\n localOnly: z.boolean(),\n hasKey: z.boolean(),\n /** Last four characters of the stored key, so an admin can tell which one is in place. */\n keyHint: z.string().nullable(),\n /**\n * Set when the stored key cannot be decrypted \u2014 `SECRET_ENCRYPTION_KEY` was lost or replaced.\n * The UI shows \"re-enter your key\" rather than a failure, because that is the actual remedy.\n */\n keyUnreadable: z.boolean(),\n updatedAt: z.string(),\n }),\n]);\nexport type AiProviderConfigView = z.infer<typeof AiProviderConfigView>;\n\n/** Result of testing a saved or proposed configuration against the live endpoint. */\nexport const AiConnectionTest = z.object({\n ok: z.boolean(),\n /** Models the endpoint advertises; empty when it does not expose a listing. */\n models: z.array(z.string()),\n /** Whether the configured model is among them; null when the endpoint does not say. */\n modelAvailable: z.boolean().nullable(),\n /** `AiErrorKind` when `ok` is false \u2014 `auth`, `network`, `local_only_refused`\u2026 */\n errorKind: z.string().nullable(),\n /** Plain-English failure, safe to show an admin verbatim. */\n message: z.string().nullable(),\n});\nexport type AiConnectionTest = z.infer<typeof AiConnectionTest>;\n\n/**\n * One row of the AI audit log. Metadata only, by construction: there is no field here that could\n * carry message content, which is what makes \"we log every call but never what was in it\" a property\n * of the schema rather than a promise (ADR 0004).\n */\nexport const AiCallRecord = z.object({\n provider: AiProviderId,\n model: z.string(),\n /** Which feature made the call \u2014 `thread-summary`, `smart-reply`, `admin-copilot`\u2026 */\n feature: z.string(),\n /** Version of the prompt template used, so a regression can be traced to a template change. */\n promptVersion: z.string(),\n outcome: z.enum(['ok', 'error', 'cancelled']),\n /** `AiErrorKind` when the outcome is `error`. */\n errorKind: z.string().nullable(),\n inputTokens: z.number().int(),\n outputTokens: z.number().int(),\n cacheReadTokens: z.number().int(),\n cacheWriteTokens: z.number().int(),\n latencyMs: z.number().int(),\n /** Digest of the prompt, so repeated calls can be correlated without storing the prompt. */\n promptDigest: z.string(),\n});\nexport type AiCallRecord = z.infer<typeof AiCallRecord>;\n\n// ---- privacy controls (Phase 7, ADR 0004)\n//\n// Three gates stand between a configured provider and a message being sent to it, and they answer\n// different questions:\n//\n// 1. Is a provider configured? An admin's deliberate act. No configuration, no AI.\n// 2. Does the domain allow it? An admin veto, per domain. Allowed unless switched off.\n// 3. Has the user opted in? Consent, recorded with a timestamp. **Off by default.**\n//\n// The third is the one that matters: no mailbox's contents reach a provider because someone else\n// enabled a feature. The second exists because a multi-domain server hosts tenants who do not all\n// answer to the same policy.\n\n/** Per-domain admin policy. No row means the defaults below. */\nexport const AiDomainPolicy = z.object({\n domainId: z.string(),\n domain: z.string(),\n /** Admin veto for this domain. Default true: configuring a provider is already deliberate. */\n enabled: z.boolean(),\n /**\n * Whether extracted attachment text may be sent along with a message. **Default false**: an\n * attachment is far more likely than a message body to hold something the sender never meant to\n * share with a third party.\n */\n allowAttachments: z.boolean(),\n /** Null when no admin has ever set a policy for this domain. */\n updatedAt: z.string().nullable(),\n});\nexport type AiDomainPolicy = z.infer<typeof AiDomainPolicy>;\n\nexport const AiDomainPolicyUpdate = z.object({\n enabled: z.boolean().optional(),\n allowAttachments: z.boolean().optional(),\n});\nexport type AiDomainPolicyUpdate = z.infer<typeof AiDomainPolicyUpdate>;\n\n/** One mailbox user's consent. Off until they turn it on themselves. */\nexport const AiOptIn = z.object({\n optedIn: z.boolean(),\n /** When consent was given, kept so it can be shown back and audited. */\n optedInAt: z.string().nullable(),\n});\nexport type AiOptIn = z.infer<typeof AiOptIn>;\n\nexport const AiOptInUpdate = z.object({ optedIn: z.boolean() });\nexport type AiOptInUpdate = z.infer<typeof AiOptInUpdate>;\n\nexport const AiUnavailableReason = z.enum([\n /** No provider configured on this server. */\n 'not_configured',\n /** An admin has switched AI off for this domain. */\n 'domain_disabled',\n /** The user has not opted in. */\n 'not_opted_in',\n]);\nexport type AiUnavailableReason = z.infer<typeof AiUnavailableReason>;\n\n/**\n * The resolved answer for one mailbox, so the webmail client can decide whether to render AI at all\n * \u2014 and, when it cannot, say which of the three gates is closed rather than hiding the feature with\n * no explanation.\n */\nexport const AiAvailability = z.object({\n available: z.boolean(),\n reason: AiUnavailableReason.nullable(),\n provider: AiProviderId.nullable(),\n model: z.string().nullable(),\n /** Whether this mailbox's domain permits attachment text to be sent. */\n attachmentsAllowed: z.boolean(),\n optedIn: z.boolean(),\n /** Whether the user could opt in \u2014 false when a gate above them is closed. */\n canOptIn: z.boolean(),\n});\nexport type AiAvailability = z.infer<typeof AiAvailability>;\n\n/** One row of the AI call log as the admin UI reads it. Metadata only, like `AiCallRecord`. */\nexport const AiCallLogEntry = AiCallRecord.extend({\n id: z.string(),\n /** Mailbox the call was made for; null for admin-console calls, which read telemetry, not mail. */\n mailbox: z.string().nullable(),\n createdAt: z.string(),\n});\nexport type AiCallLogEntry = z.infer<typeof AiCallLogEntry>;\n\nexport const AiCallLogQuery = z.object({\n limit: z.coerce.number().int().min(1).max(200).default(50),\n offset: z.coerce.number().int().min(0).default(0),\n});\nexport type AiCallLogQuery = z.infer<typeof AiCallLogQuery>;\n\nexport const AiCallLogPage = z.object({\n items: z.array(AiCallLogEntry),\n total: z.number().int(),\n});\nexport type AiCallLogPage = z.infer<typeof AiCallLogPage>;\n\n// ---- inbox features (Phase 7)\n\n/**\n * What the client asks to have summarised: a folder and the uids of the messages it grouped into a\n * conversation.\n *\n * The client sends **identifiers, not content**. Threading is a client-side concern (`threads.ts`\n * groups by `In-Reply-To`/`References`), but the bodies are read server-side from the caller's own\n * mailbox, so a client cannot put words in the model's mouth, inflate the token bill with text that\n * was never in a message, or ask about mail that is not its own. The uid cap is what stops one\n * request from turning into an unbounded read.\n */\nexport const AiSummariseRequest = z.object({\n folder: z.string().min(1).max(500),\n uids: z.array(z.number().int().positive()).min(1).max(50),\n});\nexport type AiSummariseRequest = z.infer<typeof AiSummariseRequest>;\n\n/** One message to explain. Unlike a summary this is about a single message, not a conversation. */\nexport const AiExplainRequest = z.object({\n folder: z.string().min(1).max(500),\n uid: z.number().int().positive(),\n});\nexport type AiExplainRequest = z.infer<typeof AiExplainRequest>;\n\n/**\n * Why a message looks trustworthy or not.\n *\n * `unclear` is a real answer rather than a failure, and it is what an unusable model response\n * becomes \u2014 the one thing this must never do is call something legitimate because it could not\n * decide. The verdict is advice: the spam filter still decides what reaches the inbox.\n */\nexport const AiEmailExplanation = z.object({\n verdict: z.enum(['legitimate', 'suspicious', 'dangerous', 'unclear']),\n summary: z.string(),\n /** Concrete things in the message that support the verdict. */\n signals: z.array(z.string()),\n advice: z.string(),\n /** Whether the mail server's own spam analysis was available to the model. */\n usedSpamSignals: z.boolean(),\n});\nexport type AiEmailExplanation = z.infer<typeof AiEmailExplanation>;\n\n/** Suggested replies for a conversation, as the client asks for them. */\nexport const AiRepliesRequest = z.object({\n folder: z.string().min(1).max(500),\n uids: z.array(z.number().int().positive()).min(1).max(50),\n});\nexport type AiRepliesRequest = z.infer<typeof AiRepliesRequest>;\n\n/**\n * Reply suggestions.\n *\n * Possibly empty, and that is a normal answer: a model may return something unparseable, and a\n * feature that invented a chip rather than offering none would be worse. The client puts a chosen\n * one into the compose box for the user to read \u2014 never sends it \u2014 which is the mitigation that\n * does not depend on how well the configured model resists an instruction hidden in an email.\n */\nexport const AiReplies = z.object({ replies: z.array(z.string()) });\nexport type AiReplies = z.infer<typeof AiReplies>;\n\n/**\n * Compose assist: help with the message the user is writing.\n *\n * A discriminated union rather than four routes, because they are one feature from the writer's\n * point of view and share every gate, every log field and the same streamed response. The draft the\n * user has written is theirs and is sent as text \u2014 unlike the read-side features, there is nothing\n * to look up server-side, because it does not exist anywhere yet.\n */\nexport const AiComposeRequest = z.discriminatedUnion('action', [\n z.object({\n action: z.literal('draft'),\n instruction: z.string().trim().min(1).max(2000),\n /** The thread being replied to, so a draft can match what it answers. */\n folder: z.string().min(1).max(500).optional(),\n uid: z.number().int().positive().optional(),\n }),\n z.object({\n action: z.literal('rewrite'),\n draft: z.string().min(1).max(20000),\n tone: z.enum(['formal', 'friendly', 'direct', 'apologetic']).optional(),\n length: z.enum(['shorter', 'longer', 'same']).optional(),\n }),\n z.object({\n action: z.literal('translate'),\n draft: z.string().min(1).max(20000),\n language: z.string().trim().min(1).max(60),\n }),\n z.object({ action: z.literal('grammar'), draft: z.string().min(1).max(20000) }),\n]);\nexport type AiComposeRequest = z.infer<typeof AiComposeRequest>;\nexport type AiComposeAction = AiComposeRequest['action'];\n\n/**\n * One event from a streaming AI response, as the browser receives it.\n *\n * Deliberately *not* `EventSource`: that cannot POST a body and reconnects on its own, which is\n * exactly wrong for a one-shot generation the customer is billed for. The client reads the response\n * body of a normal POST and aborts it to cancel. See the `ai-inbox-features` feature-doc.\n */\nexport const AiStreamEvent = z.discriminatedUnion('type', [\n z.object({ type: z.literal('text'), text: z.string() }),\n z.object({ type: z.literal('done') }),\n z.object({ type: z.literal('error'), kind: z.string(), message: z.string() }),\n]);\nexport type AiStreamEvent = z.infer<typeof AiStreamEvent>;\n\n// ---- admin copilot (Phase 7)\n\n/**\n * The tools the copilot may call.\n *\n * Read tools are tenant-scoped and return **metadata only** \u2014 never message bodies, and\n * `getLicenseState` never the licence key. The exposure that is real and not mitigated is\n * `getDeliveryLog` and `explainBounce`, whose rows carry recipient addresses and bounce text to\n * whichever provider the customer configured; redacting them would leave the copilot unable to do\n * the one job it exists for, so the feature-doc says so plainly instead.\n *\n * Write tools are named `propose*` because that is all they do: each computes a change, stores it,\n * and returns an id. Nothing here applies anything.\n */\nexport const AiCopilotTool = z.enum([\n 'listDomains',\n 'getDeliveryLog',\n 'explainBounce',\n 'checkDns',\n 'getReputation',\n 'getAlerts',\n 'getLicenseState',\n 'getMailbox',\n 'proposeRestoreSending',\n 'proposeRotateDkimKey',\n 'proposeResolveAlert',\n]);\nexport type AiCopilotTool = z.infer<typeof AiCopilotTool>;\n\nexport const AiProposalState = z.enum(['pending', 'applied', 'rejected', 'expired', 'failed']);\nexport type AiProposalState = z.infer<typeof AiProposalState>;\n\n/**\n * A change the copilot computed, waiting for an administrator.\n *\n * `change` is what the server will apply, and the client never sends it back \u2014 approval is by id.\n * `precondition` is what was true when it was computed, re-checked at apply time so a stale approval\n * fails closed. Both are rendered to the admin, because a confirm gate over something nobody can\n * read is not a confirmation.\n */\nexport const AiProposal = z.object({\n id: z.string(),\n conversationId: z.string(),\n tool: AiCopilotTool,\n title: z.string(),\n summary: z.string(),\n targetKind: z.enum(['mailbox', 'domain', 'alert']),\n targetId: z.string(),\n targetLabel: z.string(),\n change: z.record(z.string(), z.unknown()),\n precondition: z.record(z.string(), z.unknown()),\n state: AiProposalState,\n expiresAt: z.string(),\n appliedAt: z.string().nullable(),\n /** Why an apply failed or was refused; null while pending and on success. */\n detail: z.string().nullable(),\n createdAt: z.string(),\n});\nexport type AiProposal = z.infer<typeof AiProposal>;\n\nexport const AiProposalQuery = z.object({\n state: AiProposalState.optional(),\n limit: z.coerce.number().int().min(1).max(200).default(50),\n});\nexport type AiProposalQuery = z.infer<typeof AiProposalQuery>;\n\n/** One turn of the conversation, as the browser holds it and sends it back. */\nexport const AiCopilotTurn = z.object({\n role: z.enum(['user', 'assistant']),\n text: z.string().max(20000),\n});\nexport type AiCopilotTurn = z.infer<typeof AiCopilotTurn>;\n\n/**\n * Ask the copilot something.\n *\n * The conversation is **held by the client** and posted back each turn, which is worth stating\n * because it looks like a trust problem and is not: every consequential act is a proposal, and a\n * proposal is computed server-side by running the tool for real at the moment it is made. A client\n * that fabricated a tool result could at most mislead the model into proposing something, and that\n * proposal is still built from live data, still carries a precondition, and still has to be approved\n * by a human who can read it. The caller is already an owner or admin who could call every one of\n * these endpoints directly.\n *\n * `conversationId` correlates the tool calls and proposals of one conversation in the log. It comes\n * from the client and is treated as opaque.\n */\nexport const AiCopilotRequest = z.object({\n conversationId: z.string().uuid(),\n /** Prior turns, oldest first. Only the text is carried across; tool results are not replayed. */\n history: z.array(AiCopilotTurn).max(20).default([]),\n message: z.string().trim().min(1).max(4000),\n});\nexport type AiCopilotRequest = z.infer<typeof AiCopilotRequest>;\n\n/**\n * One event from the copilot's stream.\n *\n * A superset of `AiStreamEvent`, kept separate rather than bolted onto it: the inbox rail's three\n * events are what an inbox client knows how to render, and widening that union would make every\n * inbox consumer handle events it can never receive.\n */\nexport const AiCopilotEvent = z.discriminatedUnion('type', [\n z.object({ type: z.literal('text'), text: z.string() }),\n /** A tool the server ran, as it ran it \u2014 the transcript the admin watches. */\n z.object({\n type: z.literal('tool'),\n tool: AiCopilotTool,\n args: z.record(z.string(), z.unknown()),\n ok: z.boolean(),\n detail: z.string(),\n }),\n /** A change awaiting approval. Rendered as a card, never applied by arriving. */\n z.object({ type: z.literal('proposal'), proposal: AiProposal }),\n z.object({ type: z.literal('done') }),\n z.object({ type: z.literal('error'), kind: z.string(), message: z.string() }),\n]);\nexport type AiCopilotEvent = z.infer<typeof AiCopilotEvent>;\n\n/**\n * What the copilot can do on this install, so the page can say why a control is missing rather than\n * hiding it. `toolsSupported` is false when the configured model has no tool support \u2014 which for a\n * local runtime is a property of the weights, not the server, and is exactly the case that would\n * otherwise present as an empty answer.\n */\nexport const AiCopilotReadiness = z.object({\n available: z.boolean(),\n reason: z.string().nullable(),\n provider: AiProviderId.nullable(),\n model: z.string().nullable(),\n toolsSupported: z.boolean(),\n});\nexport type AiCopilotReadiness = z.infer<typeof AiCopilotReadiness>;\n\n/**\n * One tool the copilot actually ran.\n *\n * Deliberately not folded into the AI call log, which is metadata-only by the shape of its columns\n * and has to stay that way. A tool call carries arguments \u2014 a domain name, a recipient searched for\n * \u2014 so it is content-bearing and lives in its own table with its own statement about that.\n */\nexport const AiToolCallEntry = z.object({\n id: z.string(),\n conversationId: z.string(),\n tool: z.string(),\n args: z.record(z.string(), z.unknown()),\n ok: z.boolean(),\n resultCount: z.number().int().nullable(),\n detail: z.string().nullable(),\n proposalId: z.string().nullable(),\n latencyMs: z.number().int(),\n createdAt: z.string(),\n});\nexport type AiToolCallEntry = z.infer<typeof AiToolCallEntry>;\n", "import { z } from 'zod';\n\n/**\n * Migration (Phase 6). Schemas only \u2014 this module is imported by `apps/web`, so nothing here may\n * reach for `node:` builtins. The process wrapper, the connectors and credential sealing live in\n * `@mailserver/migration`, which is Node-only.\n */\n\n/**\n * Where the mail is coming from.\n *\n * Every one of them is generic IMAP plus a directory to ask \"who exists\" and a different way of\n * getting a per-mailbox credential \u2014 which is why the IMAP connector was built first and the rest are\n * variations on it. The hosted two differ in one respect that matters: they have no password to hand\n * over at all, so the credential is an app-only grant the customer's administrator can withdraw.\n * Every one of them is generic IMAP plus a discovery API and a different way of getting a per-mailbox\n * credential \u2014 which is why the IMAP connector was built first and the rest are variations on it.\n */\nexport const MigrationSourceKind = z.enum(['imap', 'cpanel', 'plesk', 'google', 'm365']);\nexport type MigrationSourceKind = z.infer<typeof MigrationSourceKind>;\n\nexport const ImapSecurity = z.enum(['tls', 'starttls', 'none']);\nexport type ImapSecurity = z.infer<typeof ImapSecurity>;\n\n/** Non-secret half of a source definition. Stored as plain jsonb and safe to return to the UI. */\nexport const ImapSourceConfig = z.object({\n kind: z.literal('imap'),\n host: z.string().trim().toLowerCase().min(1).max(253),\n port: z.number().int().min(1).max(65535).default(993),\n security: ImapSecurity.default('tls'),\n /**\n * Skip certificate verification. Off by default and surfaced as a deliberate choice in the wizard:\n * plenty of cPanel boxes being migrated *away from* still present an expired or self-signed\n * certificate, and refusing to connect helps nobody once the admin has seen the hostname.\n */\n allowInvalidCert: z.boolean().default(false),\n});\nexport type ImapSourceConfig = z.infer<typeof ImapSourceConfig>;\n\n/**\n * cPanel/WHM source: generic IMAP plus a control panel that can answer \"who exists here\".\n *\n * There are two token kinds because the credential an admin can actually obtain depends on who they\n * are. A customer holds a **cPanel account token** (port 2083, scoped to their one account, made in\n * cPanel -> Manage API Tokens). A host or reseller holds a **WHM token** (port 2087, every account on\n * the box), and then `account` says which one to read.\n *\n * `host` is the control panel. The mail itself is fetched over IMAP, usually from the same machine \u2014\n * but not always, so the IMAP endpoint is separately addressable rather than assumed.\n */\nexport const CpanelSourceConfig = z.object({\n kind: z.literal('cpanel'),\n host: z.string().trim().toLowerCase().min(1).max(253),\n port: z.number().int().min(1).max(65535).default(2083),\n api: z.enum(['cpanel', 'whm']).default('cpanel'),\n /** The cPanel account to read. Required with a WHM token; a cPanel token is already scoped to one. */\n account: z.string().trim().min(1).max(64).optional(),\n /** Defaults to `host` when unset. */\n imapHost: z.string().trim().toLowerCase().min(1).max(253).optional(),\n imapPort: z.number().int().min(1).max(65535).default(993),\n imapSecurity: ImapSecurity.default('tls'),\n allowInvalidCert: z.boolean().default(false),\n});\nexport type CpanelSourceConfig = z.infer<typeof CpanelSourceConfig>;\n\n/**\n * Plesk source: generic IMAP plus Plesk's XML API for everything else.\n *\n * Plesk's newer REST API (`/api/v2`) has no mail endpoints at all \u2014 mailboxes, forwarding, aliases\n * and auto-responders live only in the long-standing XML-RPC API at `/enterprise/control/agent.php`,\n * which is why this connector speaks XML where the cPanel one speaks JSON. Port 8443 either way.\n *\n * `domain` scopes the migration to one mail domain. Left unset, the connector reads every domain the\n * login can see \u2014 right for a customer login, and usually far too much for `admin` on a shared box.\n */\nexport const PleskSourceConfig = z.object({\n kind: z.literal('plesk'),\n host: z.string().trim().toLowerCase().min(1).max(253),\n port: z.number().int().min(1).max(65535).default(8443),\n /** One mail domain to read. Unset means every domain this login can see. */\n domain: z.string().trim().toLowerCase().min(1).max(253).optional(),\n /** Defaults to `host` when unset. */\n imapHost: z.string().trim().toLowerCase().min(1).max(253).optional(),\n imapPort: z.number().int().min(1).max(65535).default(993),\n imapSecurity: ImapSecurity.default('tls'),\n allowInvalidCert: z.boolean().default(false),\n});\nexport type PleskSourceConfig = z.infer<typeof PleskSourceConfig>;\n\n/**\n * Google Workspace source: Gmail over IMAP, the Admin SDK for the directory, and one service account\n * with **domain-wide delegation** standing in for every password.\n *\n * There is no master credential to ask for and no per-mailbox password to collect \u2014 which makes this\n * the only source where a two-hundred-mailbox migration needs exactly one credential. The price is\n * that the customer's own super-admin has to authorise the service account's scopes in the Admin\n * console, and `adminEmail` is the administrator the service account impersonates to read the\n * directory (Google refuses directory calls made as the service account itself).\n */\nexport const GoogleSourceConfig = z.object({\n kind: z.literal('google'),\n /** The Workspace domain whose users are being migrated. */\n domain: z.string().trim().toLowerCase().min(1).max(253),\n /** A super-admin in that domain, impersonated for directory reads only. */\n adminEmail: z.string().trim().toLowerCase().min(3).max(320),\n imapHost: z.string().trim().toLowerCase().min(1).max(253).default('imap.gmail.com'),\n imapPort: z.number().int().min(1).max(65535).default(993),\n /**\n * Read each user's Gmail settings \u2014 vacation responder, filters, auto-forwarding. Three extra API\n * calls per mailbox and a wider scope grant, so it is a choice rather than an assumption.\n */\n readGmailSettings: z.boolean().default(true),\n /** Import Google Groups as aliases. Their archives and moderation do not come across. */\n includeGroups: z.boolean().default(true),\n});\nexport type GoogleSourceConfig = z.infer<typeof GoogleSourceConfig>;\n\n/**\n * Microsoft 365 source: Exchange Online over IMAP, Microsoft Graph for the directory, and one Entra\n * application with `IMAP.AccessAsApp` standing in for every password.\n *\n * The same bargain as Google and for the same reasons (see `GoogleSourceConfig`), with one structural\n * difference: Microsoft's app-only grant is tenant-wide rather than per-user, so a single token opens\n * every mailbox instead of one being minted per person.\n *\n * `domain` narrows a tenant that holds several \u2014 common after an acquisition, and not something to\n * migrate wholesale by accident.\n */\nexport const M365SourceConfig = z.object({\n kind: z.literal('m365'),\n /** The Entra tenant: its GUID, or any domain it owns. */\n tenantId: z.string().trim().min(1).max(253),\n /** Only migrate mailboxes on this domain. Unset means every mailbox in the tenant. */\n domain: z.string().trim().toLowerCase().min(1).max(253).optional(),\n imapHost: z.string().trim().toLowerCase().min(1).max(253).default('outlook.office365.com'),\n imapPort: z.number().int().min(1).max(65535).default(993),\n /**\n * Read each mailbox's automatic replies and inbox rules. Two extra Graph calls per mailbox and the\n * `MailboxSettings.Read` permission, so it is a choice rather than an assumption.\n */\n readMailboxSettings: z.boolean().default(true),\n /** Import mail-enabled groups and distribution lists as aliases. */\n includeGroups: z.boolean().default(true),\n});\nexport type M365SourceConfig = z.infer<typeof M365SourceConfig>;\n\nexport const MigrationSourceConfig = z.discriminatedUnion('kind', [\n ImapSourceConfig,\n CpanelSourceConfig,\n PleskSourceConfig,\n GoogleSourceConfig,\n M365SourceConfig,\n]);\nexport type MigrationSourceConfig = z.infer<typeof MigrationSourceConfig>;\n\n/** The IMAP half of any source, however its config spells it. */\nexport function imapEndpointOf(source: MigrationSourceConfig): {\n host: string;\n port: number;\n security: ImapSecurity;\n allowInvalidCert: boolean;\n} {\n if (source.kind === 'imap')\n return {\n host: source.host,\n port: source.port,\n security: source.security,\n allowInvalidCert: source.allowInvalidCert,\n };\n // A hosted source has no panel host and no certificate worth waiving: Google and Microsoft are\n // reached at fixed names over TLS that verifies, and an option to skip that check would only ever\n // be a way to get a migration pointed somewhere it should not be.\n if (source.kind === 'google' || source.kind === 'm365')\n return { host: source.imapHost, port: source.imapPort, security: 'tls', allowInvalidCert: false };\n // Every control-panel source addresses the panel and the IMAP server separately, because they are\n // not always the same machine \u2014 a Plesk box with a separate mail node is a supported layout.\n return {\n host: source.imapHost ?? source.host,\n port: source.imapPort,\n security: source.imapSecurity,\n allowInvalidCert: source.allowInvalidCert,\n };\n}\n\n/**\n * Secret half of a source definition. Never stored in the clear, never returned by the API, never\n * written to the job log (ADR 0018). An admin credential is one login that can read every mailbox \u2014\n * a Dovecot master user, a cPanel API token. Without one, each mailbox carries its own password.\n */\nexport const ImapSourceSecret = z.object({\n kind: z.literal('imap'),\n /**\n * Dovecot/Courier master-user login, when the source has one. `adminSeparator` is the character\n * between the mailbox address and the master user (`user@example.com*master`); Dovecot's is `*`.\n */\n adminUser: z.string().min(1).max(320).optional(),\n adminPassword: z.string().min(1).max(1024).optional(),\n adminSeparator: z.string().length(1).default('*'),\n});\nexport type ImapSourceSecret = z.infer<typeof ImapSourceSecret>;\n\n/**\n * cPanel credentials: an API token for the control panel, and separately whatever opens IMAP.\n *\n * These are two different things and it matters. The API token lists accounts, forwarders, quotas and\n * filters \u2014 it cannot read a single message, and it cannot recover a mailbox password, because cPanel\n * stores those hashed. So IMAP still needs either a Dovecot master user (which cPanel does not ship;\n * root has to add one) or each mailbox's own password. See the `migration-cpanel` feature doc.\n */\nexport const CpanelSourceSecret = z.object({\n kind: z.literal('cpanel'),\n /** The user the token belongs to: the cPanel account, or the WHM/reseller user. */\n username: z.string().trim().min(1).max(64),\n /** An API token, never a password: cPanel's own tokens are revocable and scoped. */\n apiToken: z.string().min(1).max(1024),\n adminUser: z.string().min(1).max(320).optional(),\n adminPassword: z.string().min(1).max(1024).optional(),\n adminSeparator: z.string().length(1).default('*'),\n});\nexport type CpanelSourceSecret = z.infer<typeof CpanelSourceSecret>;\n\n/**\n * Plesk credentials. Two forms, because which one an admin can get depends on their Plesk version and\n * on whether they are willing to hand over the panel password:\n *\n * - an **API key** (`KEY` header), created by the admin and revocable \u2014 the right answer, and the\n * only one that can be scoped and withdrawn afterwards;\n * - the panel **login and password**, which is what Plesk's own documentation still shows.\n *\n * Exactly one is required; a missing pair is reported by the connector rather than by the schema,\n * because a cross-field rule inside a discriminated union is fragile and the message matters more\n * than where it is enforced.\n *\n * As with cPanel, none of this opens a mailbox: Plesk stores mail passwords hashed, so IMAP still\n * needs a Dovecot master user or a password per mailbox.\n */\nexport const PleskSourceSecret = z.object({\n kind: z.literal('plesk'),\n /** Plesk API secret key. Preferred over the password: revocable, and can be bound to our address. */\n apiKey: z.string().min(1).max(1024).optional(),\n /** Panel login, used only with `password`. */\n username: z.string().trim().min(1).max(64).optional(),\n password: z.string().min(1).max(1024).optional(),\n adminUser: z.string().min(1).max(320).optional(),\n adminPassword: z.string().min(1).max(1024).optional(),\n adminSeparator: z.string().length(1).default('*'),\n});\nexport type PleskSourceSecret = z.infer<typeof PleskSourceSecret>;\n\n/**\n * Google Workspace credentials: one service-account key, and nothing else.\n *\n * Not a password, and deliberately not one: the customer's admin creates the service account, grants\n * it named scopes in their own Admin console, sees every call it makes in their own audit log, and\n * revokes it in one click when the migration is over. That is a strictly better bargain than being\n * handed somebody's password, and it is the reason app-only auth was chosen over per-user OAuth \u2014\n * which would also have needed a redirect URI we cannot register, since every install is on the\n * customer's own hostname.\n */\nexport const GoogleSourceSecret = z.object({\n kind: z.literal('google'),\n /** The service account's own address, `\u2026@\u2026.iam.gserviceaccount.com`. */\n clientEmail: z.string().trim().min(3).max(320),\n /** The PEM private key from the service account's JSON key file. */\n privateKey: z.string().min(1).max(8192),\n});\nexport type GoogleSourceSecret = z.infer<typeof GoogleSourceSecret>;\n\n/**\n * Microsoft 365 credentials: one Entra application registration.\n *\n * As with Google, deliberately not a password. The customer's own administrator registers the\n * application, grants it the named application permissions with an explicit consent step, watches it\n * in their own sign-in logs, and deletes it when the migration is done.\n */\nexport const M365SourceSecret = z.object({\n kind: z.literal('m365'),\n /** The application (client) ID from the Entra app registration. */\n clientId: z.string().trim().min(1).max(200),\n /** A client secret from that registration. Not a certificate \u2014 Entra allows either; this is simpler. */\n clientSecret: z.string().min(1).max(2048),\n});\nexport type M365SourceSecret = z.infer<typeof M365SourceSecret>;\n\nexport const MigrationSourceSecret = z.discriminatedUnion('kind', [\n ImapSourceSecret,\n CpanelSourceSecret,\n PleskSourceSecret,\n GoogleSourceSecret,\n M365SourceSecret,\n]);\nexport type MigrationSourceSecret = z.infer<typeof MigrationSourceSecret>;\n\n/**\n * Job lifecycle. `ready` means mailboxes are discovered and mapped but nothing has been copied;\n * `paused` is an operator stop that keeps every count, so resuming is a delta run rather than a\n * restart. There is no `deleted` \u2014 cancelling wipes the sealed credentials and keeps the record.\n */\nexport const MigrationJobState = z.enum([\n 'draft',\n 'discovering',\n 'ready',\n 'running',\n 'paused',\n 'done',\n 'failed',\n 'cancelled',\n]);\nexport type MigrationJobState = z.infer<typeof MigrationJobState>;\n\nexport const MigrationMailboxState = z.enum(['pending', 'running', 'done', 'failed', 'skipped']);\nexport type MigrationMailboxState = z.infer<typeof MigrationMailboxState>;\n\n/**\n * Error taxonomy for triage. The point is that an admin looking at 40 failed mailboxes can tell at a\n * glance whether they typed a password wrong (`auth`, fix and re-run), hit the destination quota\n * (`quota`, raise it), or are being throttled by the source (`rate_limited`, run it again later).\n */\nexport const MigrationErrorCode = z.enum([\n 'auth',\n 'connection',\n 'tls',\n 'quota',\n 'rate_limited',\n 'folder',\n 'source_missing',\n 'target_missing',\n 'cancelled',\n 'unknown',\n]);\nexport type MigrationErrorCode = z.infer<typeof MigrationErrorCode>;\n\n/** Per-mailbox counters. Bytes are what imapsync reports transferred, not mailbox size on disk. */\nexport const MigrationProgress = z.object({\n foldersTotal: z.number().int().nonnegative(),\n foldersDone: z.number().int().nonnegative(),\n messagesTotal: z.number().int().nonnegative(),\n messagesDone: z.number().int().nonnegative(),\n /** Messages the source had that the target already held \u2014 the delta-run \"did nothing\" number. */\n messagesSkipped: z.number().int().nonnegative(),\n bytesDone: z.number().int().nonnegative(),\n});\nexport type MigrationProgress = z.infer<typeof MigrationProgress>;\n\nexport const EMPTY_PROGRESS: MigrationProgress = {\n foldersTotal: 0,\n foldersDone: 0,\n messagesTotal: 0,\n messagesDone: 0,\n messagesSkipped: 0,\n bytesDone: 0,\n};\n\n/**\n * Cutover checklist. The steps are fixed and ordered; the state is just how far the admin has got.\n * It is deliberately advisory \u2014 nothing here blocks a run \u2014 because the one thing we cannot do for\n * them is change their MX record, and a checklist that lied about having done so would be worse than\n * no checklist at all.\n */\nexport const CutoverStep = z.enum(['lower_ttl', 'initial_sync', 'verify_counts', 'switch_mx', 'final_delta']);\nexport type CutoverStep = z.infer<typeof CutoverStep>;\n\nexport const CUTOVER_STEPS = CutoverStep.options;\n\nexport const MigrationMailbox = z.object({\n id: z.string(),\n jobId: z.string(),\n /** Address on the source server. */\n sourceAddress: z.string(),\n /** Local mailbox this copies into; null until the admin maps it (or discovery matched one). */\n targetMailboxId: z.string().nullable(),\n targetAddress: z.string().nullable(),\n state: MigrationMailboxState,\n progress: MigrationProgress,\n errorCode: MigrationErrorCode.nullable(),\n errorDetail: z.string().nullable(),\n /** Set when this mailbox last completed a run; the delta run copies only what appeared since. */\n lastRunAt: z.string().nullable(),\n /** True when a source password is sealed for this mailbox specifically. Never the password itself. */\n hasSecret: z.boolean(),\n});\nexport type MigrationMailbox = z.infer<typeof MigrationMailbox>;\n\nexport const MigrationJob = z.object({\n id: z.string(),\n name: z.string(),\n kind: MigrationSourceKind,\n /** Non-secret source configuration. */\n source: MigrationSourceConfig,\n state: MigrationJobState,\n /** Aggregate of the mailbox rows, computed on read. */\n progress: MigrationProgress,\n mailboxCount: z.number().int().nonnegative(),\n mailboxesDone: z.number().int().nonnegative(),\n mailboxesFailed: z.number().int().nonnegative(),\n errorCode: MigrationErrorCode.nullable(),\n errorDetail: z.string().nullable(),\n /** True while an admin credential is sealed; false once the job finished and it was wiped. */\n hasSecret: z.boolean(),\n /** Cutover steps the admin has ticked off, in no particular order. */\n cutover: z.array(CutoverStep),\n startedAt: z.string().nullable(),\n finishedAt: z.string().nullable(),\n createdAt: z.string(),\n updatedAt: z.string(),\n});\nexport type MigrationJob = z.infer<typeof MigrationJob>;\n\nexport const MigrationJobCreate = z.object({\n name: z.string().trim().min(1).max(120),\n source: MigrationSourceConfig,\n secret: MigrationSourceSecret,\n});\nexport type MigrationJobCreate = z.infer<typeof MigrationJobCreate>;\n\n/** One discovered mailbox on the source, before the admin maps it. */\nexport const DiscoveredMailbox = z.object({\n address: z.string(),\n /** Bytes on the source, where the source will say; null when it will not. */\n sizeBytes: z.number().int().nonnegative().nullable(),\n /** Quota on the source, so a pre-created mailbox starts with the same allowance. Null = unlimited. */\n quotaBytes: z.number().int().nonnegative().nullable(),\n /** Suspended on the source. Worth copying, and worth not silently re-enabling here. */\n suspended: z.boolean(),\n /** A local mailbox with the same address, when one already exists. */\n suggestedTargetMailboxId: z.string().nullable(),\n suggestedTargetAddress: z.string().nullable(),\n});\nexport type DiscoveredMailbox = z.infer<typeof DiscoveredMailbox>;\n\n/**\n * A forwarder on the source: `source` receives, `destination` gets a copy. Whether that becomes an\n * alias or a forwarder here depends on whether `source` is also a real mailbox \u2014 see the import.\n */\nexport const DiscoveredForwarder = z.object({\n source: z.string(),\n destination: z.string(),\n});\nexport type DiscoveredForwarder = z.infer<typeof DiscoveredForwarder>;\n\nexport const DiscoveredAutoresponder = z.object({\n address: z.string(),\n subject: z.string(),\n /** ISO timestamps; null means open-ended in that direction. */\n startsAt: z.string().nullable(),\n endsAt: z.string().nullable(),\n /** False when the window has already closed \u2014 imported, but left switched off. */\n active: z.boolean(),\n});\nexport type DiscoveredAutoresponder = z.infer<typeof DiscoveredAutoresponder>;\n\n/**\n * A filter on the source, and whether we can express it. The translated rules stay on the server \u2014\n * only this summary crosses the wire, so the client can never post a Sieve script of its own choosing.\n */\nexport const DiscoveredFilter = z.object({\n address: z.string(),\n name: z.string(),\n /** False when the source rule uses something with no equivalent here; `note` says what. */\n supported: z.boolean(),\n note: z.string().nullable(),\n});\nexport type DiscoveredFilter = z.infer<typeof DiscoveredFilter>;\n\nexport const MigrationDiscovery = z.object({\n mailboxes: z.array(DiscoveredMailbox),\n forwarders: z.array(DiscoveredForwarder),\n autoresponders: z.array(DiscoveredAutoresponder),\n filters: z.array(DiscoveredFilter),\n /** Domains the source account holds, so the admin can see what is missing here. */\n domains: z.array(z.string()),\n /** Things worth telling the admin that are not failures: skipped constructs, partial answers. */\n warnings: z.array(z.string()),\n /**\n * True when the source was reached with a single admin credential, so per-mailbox passwords are not\n * needed. False for a plain IMAP source where each mailbox must be entered by hand \u2014 and, notably,\n * false for cPanel without a Dovecot master user: the API token lists mailboxes but cannot open one.\n */\n usedAdminCredential: z.boolean(),\n});\nexport type MigrationDiscovery = z.infer<typeof MigrationDiscovery>;\n\nexport const EMPTY_DISCOVERY: MigrationDiscovery = {\n mailboxes: [],\n forwarders: [],\n autoresponders: [],\n filters: [],\n domains: [],\n warnings: [],\n usedAdminCredential: false,\n};\n\n/** Adding mailboxes to a job: the mapping step of the wizard. */\nexport const MigrationMailboxAdd = z.object({\n sourceAddress: z.string().trim().toLowerCase().min(1).max(320),\n targetMailboxId: z.string().uuid().nullable().default(null),\n /** Required when the job has no admin credential. Sealed on arrival, never returned. */\n sourcePassword: z.string().min(1).max(1024).optional(),\n});\nexport type MigrationMailboxAdd = z.infer<typeof MigrationMailboxAdd>;\n\nexport const MigrationMailboxesAdd = z.object({\n mailboxes: z.array(MigrationMailboxAdd).min(1).max(1000),\n});\nexport type MigrationMailboxesAdd = z.infer<typeof MigrationMailboxesAdd>;\n\n/**\n * Starting a run. A `delta` run copies only what the source gained since `lastRunAt` and is what the\n * cutover step uses; `full` re-examines every message (duplicate suppression still means nothing is\n * copied twice). `dryRun` asks imapsync what it would do without writing anything.\n */\nexport const MigrationRunStart = z.object({\n mode: z.enum(['full', 'delta']).default('full'),\n dryRun: z.boolean().default(false),\n /** Restrict the run to these mailbox rows; empty means every mailbox on the job. */\n mailboxIds: z.array(z.string().uuid()).default([]),\n});\nexport type MigrationRunStart = z.infer<typeof MigrationRunStart>;\n\nexport const MigrationLogLevel = z.enum(['info', 'warn', 'error']);\nexport type MigrationLogLevel = z.infer<typeof MigrationLogLevel>;\n\nexport const MigrationLogEntry = z.object({\n id: z.string(),\n jobId: z.string(),\n mailboxId: z.string().nullable(),\n at: z.string(),\n level: MigrationLogLevel,\n message: z.string(),\n});\nexport type MigrationLogEntry = z.infer<typeof MigrationLogEntry>;\n\nexport const MigrationLogQuery = z.object({\n mailboxId: z.string().uuid().optional(),\n level: MigrationLogLevel.optional(),\n limit: z.coerce.number().int().min(1).max(1000).default(200),\n offset: z.coerce.number().int().min(0).default(0),\n});\nexport type MigrationLogQuery = z.infer<typeof MigrationLogQuery>;\n\nexport const MigrationLogPage = z.object({\n items: z.array(MigrationLogEntry),\n total: z.number().int(),\n});\nexport type MigrationLogPage = z.infer<typeof MigrationLogPage>;\n\nexport const CutoverUpdate = z.object({\n step: CutoverStep,\n done: z.boolean(),\n});\nexport type CutoverUpdate = z.infer<typeof CutoverUpdate>;\n\n/**\n * Pre-create: make local mailboxes matching the ones discovered on the source, so the copy has\n * somewhere to go. A migration copies *into* mailboxes and never creates them implicitly, which makes\n * this the one step between \"we found 40 accounts\" and \"start\".\n *\n * Domains are deliberately not created here. Adding a domain rotates a DKIM key, publishes DNS\n * records and consumes licence entitlement; that belongs on the Domains page, done once and\n * deliberately, not as a side effect of a migration.\n */\nexport const MigrationPrecreate = z.object({\n addresses: z.array(z.string().trim().toLowerCase().min(3).max(320)).min(1).max(1000),\n /**\n * Copy each source mailbox's quota onto the one created here. Off means the domain's default is\n * used \u2014 which is usually what you want when moving off a host with wildly generous quotas.\n */\n copyQuotas: z.boolean().default(false),\n /** Add every created mailbox to this job's mapping, so the copy can start straight afterwards. */\n map: z.boolean().default(true),\n});\nexport type MigrationPrecreate = z.infer<typeof MigrationPrecreate>;\n\n/**\n * What pre-create did. The generated passwords are returned **once** and never stored in readable\n * form: a cPanel mailbox password is a hash on the source and cannot be recovered, so a migrated\n * mailbox necessarily gets a new one and somebody has to hand it to the user.\n */\nexport const MigrationPrecreateResult = z.object({\n created: z.array(z.object({ address: z.string(), password: z.string() })),\n skipped: z.array(z.object({ address: z.string(), reason: z.string() })),\n mapped: z.number().int().nonnegative(),\n});\nexport type MigrationPrecreateResult = z.infer<typeof MigrationPrecreateResult>;\n\n/**\n * Import the things that are not mail: forwarders, autoresponders and filters. Re-discovered\n * server-side from the stored credential rather than posted by the client, so nothing here lets a\n * caller choose the Sieve script that ends up on a mailbox.\n */\nexport const MigrationImport = z.object({\n forwarders: z.boolean().default(true),\n autoresponders: z.boolean().default(true),\n filters: z.boolean().default(true),\n /** Replace what is already there. Off means an address that already has rules is left alone. */\n overwrite: z.boolean().default(false),\n});\nexport type MigrationImport = z.infer<typeof MigrationImport>;\n\nexport const MigrationImportResult = z.object({\n aliases: z.array(z.string()),\n forwarders: z.array(z.string()),\n autoresponders: z.array(z.string()),\n filters: z.array(z.string()),\n skipped: z.array(z.object({ what: z.string(), reason: z.string() })),\n});\nexport type MigrationImportResult = z.infer<typeof MigrationImportResult>;\n\n/**\n * Sum per-mailbox counters into the job-level figure the dashboard shows. Kept here rather than in\n * the API so the wizard can recompute optimistically between polls without a round trip.\n */\nexport function sumProgress(parts: readonly MigrationProgress[]): MigrationProgress {\n return parts.reduce<MigrationProgress>(\n (a, p) => ({\n foldersTotal: a.foldersTotal + p.foldersTotal,\n foldersDone: a.foldersDone + p.foldersDone,\n messagesTotal: a.messagesTotal + p.messagesTotal,\n messagesDone: a.messagesDone + p.messagesDone,\n messagesSkipped: a.messagesSkipped + p.messagesSkipped,\n bytesDone: a.bytesDone + p.bytesDone,\n }),\n { ...EMPTY_PROGRESS },\n );\n}\n", "/**\n * @mailserver/license \u2014 the licence contract.\n *\n * Both sides of the business depend on this module agreeing with itself: the licence server signs\n * keys with it, and the product verifies them with it, offline, with no network in the path. That is\n * why signing lives here beside verification rather than in the server \u2014 one implementation, one set\n * of tests, no chance of the two drifting into a format only one of them can read.\n *\n * Node-only (`node:crypto`, `node:os`, `node:fs`): the API, worker and CLI may import it; `apps/web`\n * must not.\n */\nimport {\n createHash,\n createPrivateKey,\n createPublicKey,\n generateKeyPairSync,\n sign,\n verify,\n} from 'node:crypto';\nimport { networkInterfaces, hostname } from 'node:os';\nimport { readFileSync } from 'node:fs';\nimport { z } from 'zod';\n\n// ---- key payload\n\nexport const LicenseTier = z.enum(['starter', 'standard', 'business', 'enterprise']);\nexport type LicenseTier = z.infer<typeof LicenseTier>;\n\n/** Optional capabilities a tier unlocks; unknown values are ignored rather than rejected, so the server can add one without every deployed product refusing the key. */\nexport const LicenseFeature = z.string().min(1).max(40);\n\nexport const LicensePayload = z.object({\n /** Format version, so a future change can be detected rather than mis-parsed. */\n v: z.literal(1),\n licenseId: z.string().min(8).max(64),\n tier: LicenseTier,\n /** 0 means unlimited. Counted excluding `system` mailboxes (DMARC ingestion and friends). */\n maxMailboxes: z.number().int().min(0),\n maxDomains: z.number().int().min(0),\n issuedAt: z.iso.datetime(),\n expiresAt: z.iso.datetime(),\n features: z.array(LicenseFeature).default([]),\n /** Machine fingerprint this key is bound to; absent means the key is portable. */\n fingerprint: z.string().min(16).max(64).nullable().default(null),\n /** Free-text label shown in the admin UI (usually the customer's company). */\n issuedTo: z.string().max(200).nullable().default(null),\n});\nexport type LicensePayload = z.infer<typeof LicensePayload>;\n\n// ---- token codec\n//\n// `mdl1.<base64url payload>.<base64url signature>` \u2014 one line, URL-safe, and pasteable into a form\n// without a customer having to preserve whitespace.\n\nconst PREFIX = 'mdl1';\nconst b64u = (b: Buffer) => b.toString('base64url');\nconst unb64u = (s: string) => Buffer.from(s, 'base64url');\n\nexport type LicenseErrorCode =\n 'malformed' | 'bad_signature' | 'unsupported_version' | 'invalid_payload' | 'fingerprint_mismatch';\n\n/**\n * Note the explicit field rather than a TypeScript parameter property: this module is shared verbatim\n * with the licence server, which runs `.ts` directly under Node's type stripping \u2014 and stripping\n * rejects parameter properties. Portability is the point of this file.\n */\nexport class LicenseError extends Error {\n readonly code: LicenseErrorCode;\n constructor(code: LicenseErrorCode, message: string) {\n super(message);\n this.code = code;\n this.name = 'LicenseError';\n }\n}\n\n/** Base64 DER keys: SPKI for the public half, PKCS#8 for the private. Text, so they survive a secrets store and an env var. */\nexport interface Keypair {\n publicKey: string;\n privateKey: string;\n}\n\nexport function generateKeypair(): Keypair {\n const { publicKey, privateKey } = generateKeyPairSync('ed25519');\n return {\n publicKey: publicKey.export({ format: 'der', type: 'spki' }).toString('base64'),\n privateKey: privateKey.export({ format: 'der', type: 'pkcs8' }).toString('base64'),\n };\n}\n\nconst publicKeyFrom = (base64: string) =>\n createPublicKey({ key: Buffer.from(base64, 'base64'), format: 'der', type: 'spki' });\nconst privateKeyFrom = (base64: string) =>\n createPrivateKey({ key: Buffer.from(base64, 'base64'), format: 'der', type: 'pkcs8' });\n\n/** Canonical bytes that get signed: the payload exactly as it will be transmitted, never a re-serialisation. */\nconst signedBytes = (payloadB64: string) => Buffer.from(`${PREFIX}.${payloadB64}`, 'utf8');\n\n/** Issue a key. The licence server owns the private half; nothing in this repo should ever hold it. */\nexport function signLicense(payload: LicensePayload, privateKeyBase64: string): string {\n const parsed = LicensePayload.parse(payload);\n const body = b64u(Buffer.from(JSON.stringify(parsed), 'utf8'));\n const signature = sign(null, signedBytes(body), privateKeyFrom(privateKeyBase64));\n return `${PREFIX}.${body}.${b64u(signature)}`;\n}\n\n/**\n * Verify a key offline and return its payload. Throws `LicenseError` and never returns a partly\n * trusted result \u2014 a caller cannot accidentally read the payload of a key that failed to verify.\n */\nexport function verifyLicense(token: string, publicKeyBase64: string): LicensePayload {\n const parts = token.trim().split('.');\n if (parts.length !== 3) throw new LicenseError('malformed', 'expected three dot-separated segments');\n const [prefix, body, signature] = parts as [string, string, string];\n if (prefix !== PREFIX)\n throw new LicenseError('unsupported_version', `unknown licence format ${JSON.stringify(prefix)}`);\n\n let ok: boolean;\n try {\n ok = verify(null, signedBytes(body), publicKeyFrom(publicKeyBase64), unb64u(signature));\n } catch {\n // a malformed signature or key is a failed verification, not a crash\n throw new LicenseError('bad_signature', 'signature could not be checked');\n }\n if (!ok) throw new LicenseError('bad_signature', 'signature does not match this licence');\n\n let json: unknown;\n try {\n json = JSON.parse(unb64u(body).toString('utf8'));\n } catch {\n throw new LicenseError('invalid_payload', 'licence body is not JSON');\n }\n const parsed = LicensePayload.safeParse(json);\n // The signature already proved we issued this, so a payload we cannot read means a newer server\n // wrote a shape this build predates \u2014 worth saying plainly rather than \"invalid licence\".\n if (!parsed.success)\n throw new LicenseError('invalid_payload', `licence payload not understood: ${parsed.error.message}`);\n return parsed.data;\n}\n\n// ---- machine fingerprint\n\nexport interface FingerprintInputs {\n machineId: string | null;\n mac: string | null;\n host: string;\n}\n\n/**\n * Reads what identifies this machine. Split from the hashing so tests do not depend on the box they\n * run on, and so the API can log which parts were missing when a fingerprint changes unexpectedly.\n */\nexport function fingerprintInputs(): FingerprintInputs {\n let machineId: string | null = null;\n for (const p of ['/etc/machine-id', '/var/lib/dbus/machine-id']) {\n try {\n const v = readFileSync(p, 'utf8').trim();\n if (v) {\n machineId = v;\n break;\n }\n } catch {\n // absent on macOS and in some containers; the MAC and hostname still identify the host\n }\n }\n const macs = Object.values(networkInterfaces())\n .flatMap((i) => i ?? [])\n .filter((i) => !i.internal && i.mac && i.mac !== '00:00:00:00:00:00')\n .map((i) => i.mac)\n .sort();\n return { machineId, mac: macs[0] ?? null, host: hostname() };\n}\n\n/**\n * A stable id for this server. All three inputs contribute, so losing any one of them \u2014 a rebuilt\n * host, a renamed NIC, a container without `/etc/machine-id` \u2014 produces a different fingerprint and\n * the licence stops matching. That is why binding is **optional** on the key and why\n * `mailctl license transfer` has to exist: hardware changes, and a customer must not need support to\n * survive it.\n */\nexport function fingerprint(inputs: FingerprintInputs = fingerprintInputs()): string {\n const material = [inputs.machineId ?? '', inputs.mac ?? '', inputs.host].join('|');\n return createHash('sha256').update(material).digest('hex').slice(0, 32);\n}\n\n// ---- state\n\nexport const LicenseState = z.enum(['unlicensed', 'active', 'grace', 'degraded']);\nexport type LicenseState = z.infer<typeof LicenseState>;\n\n/** Days a server keeps running normally after its licence expires or its heartbeat stops (ADR 0001). */\nexport const GRACE_DAYS = 30;\n/** A missed daily heartbeat is not worth a banner; a week of them is. */\nexport const HEARTBEAT_WARN_DAYS = 7;\nconst DAY_MS = 86_400_000;\n\nexport interface EvaluateInput {\n /** A verified payload, or null when no key is installed. */\n payload: LicensePayload | null;\n /** When the licence server was last reached. Null means never \u2014 a fresh activation counts. */\n lastHeartbeatAt: Date | null;\n now?: Date;\n graceDays?: number;\n /** This machine, when the key is bound to one. */\n machine?: string;\n}\n\nexport interface Evaluation {\n state: LicenseState;\n /** Days left before the next step down; null when nothing is counting down. */\n daysRemaining: number | null;\n /** Why the licence is not `active`, in words the admin UI can show verbatim. */\n reason: string | null;\n}\n\n/**\n * The state machine from ADR 0001: `unlicensed \u2192 active \u2192 grace \u2192 degraded`.\n *\n * Expiry and heartbeat share one grace window rather than each having their own, because a customer\n * whose card fails experiences both at once and two overlapping counters would be impossible to\n * explain in a banner.\n *\n * A fingerprint mismatch degrades **immediately** and without grace: it means the key was copied to\n * another machine, which is the one case that is not an accident of connectivity.\n */\nexport function evaluate({\n payload,\n lastHeartbeatAt,\n now = new Date(),\n graceDays = GRACE_DAYS,\n machine,\n}: EvaluateInput): Evaluation {\n if (!payload) return { state: 'unlicensed', daysRemaining: null, reason: 'No licence key installed.' };\n\n if (payload.fingerprint && machine && payload.fingerprint !== machine)\n return {\n state: 'degraded',\n daysRemaining: null,\n reason: 'This licence key is registered to a different machine.',\n };\n\n const expiry = new Date(payload.expiresAt).getTime();\n // The heartbeat clock only starts once we have actually reached the server, and a missed day is\n // not news \u2014 a home connection drops. Warn from a week, act at the grace limit.\n const staleFrom = lastHeartbeatAt ? lastHeartbeatAt.getTime() : null;\n const deadlines: { at: number; reason: string }[] = [];\n if (expiry <= now.getTime())\n deadlines.push({ at: expiry, reason: 'The subscription expired; renew to restore full access.' });\n if (staleFrom !== null && now.getTime() - staleFrom > HEARTBEAT_WARN_DAYS * DAY_MS)\n deadlines.push({ at: staleFrom, reason: 'The licence server has not been reachable.' });\n\n if (deadlines.length === 0) return { state: 'active', daysRemaining: null, reason: null };\n\n // whichever clock started first is the one that runs out first\n const earliest = deadlines.reduce((a, b) => (a.at <= b.at ? a : b));\n const elapsedDays = (now.getTime() - earliest.at) / DAY_MS;\n if (elapsedDays > graceDays) return { state: 'degraded', daysRemaining: 0, reason: earliest.reason };\n return {\n state: 'grace',\n daysRemaining: Math.max(0, Math.ceil(graceDays - elapsedDays)),\n reason: earliest.reason,\n };\n}\n\n// ---- enforcement\n\n/** Things a licence can stop. Mail flow is deliberately absent: delivery, IMAP and webmail never stop (ADR 0001). */\nexport const GatedAction = z.enum(['create_mailbox', 'create_domain', 'ai_features']);\nexport type GatedAction = z.infer<typeof GatedAction>;\n\n/**\n * Exactly what a degraded licence refuses. Written as a set rather than folded into the condition so\n * that widening it is a visible, reviewable edit \u2014 ADR 0014 makes adding a third gated action an\n * ADR-level change, and this is where that promise is kept.\n */\nconst BLOCKED_WHEN_DEGRADED: ReadonlySet<GatedAction> = new Set<GatedAction>([\n 'create_mailbox',\n 'create_domain',\n 'ai_features',\n]);\n\n/** Grace is fully functional on purpose \u2014 it is a warning, not a punishment. Only `degraded` refuses. */\nexport const allows = (state: LicenseState, action: GatedAction): boolean =>\n state !== 'degraded' || !BLOCKED_WHEN_DEGRADED.has(action);\n\nexport interface Usage {\n mailboxes: number;\n domains: number;\n}\n\n/**\n * Whether adding one more of something would exceed the tier. `0` means unlimited, and an\n * `unlicensed` server is *not* capped here \u2014 an unactivated install has to be usable enough to\n * evaluate, and it is `degraded` that stops growth.\n */\nexport function withinLimits(\n payload: LicensePayload | null,\n usage: Usage,\n action: GatedAction,\n): { ok: true } | { ok: false; reason: string } {\n if (!payload) return { ok: true };\n if (action === 'create_mailbox' && payload.maxMailboxes > 0 && usage.mailboxes >= payload.maxMailboxes)\n return {\n ok: false,\n reason: `This licence covers ${payload.maxMailboxes} mailboxes; upgrade the plan to add more.`,\n };\n if (action === 'create_domain' && payload.maxDomains > 0 && usage.domains >= payload.maxDomains)\n return {\n ok: false,\n reason: `This licence covers ${payload.maxDomains} domains; upgrade the plan to add more.`,\n };\n return { ok: true };\n}\n", "import type { LicenseStatus, SetupAnswers } from '@mailserver/core';\n\n/** The control plane's error body (`apps/api/src/app.ts`), narrowed to what an operator needs read out. */\nexport class ApiError extends Error {\n readonly status: number;\n readonly code: string;\n constructor(status: number, code: string, message: string) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.code = code;\n }\n}\n\n/**\n * Thin client for the setup, health and licence endpoints `mailctl` needs; everything else is the\n * admin UI's job (ADR 0005).\n */\nexport class ApiClient {\n constructor(readonly baseUrl: string) {}\n\n /**\n * One request, JSON in and out, errors turned into `ApiError` so a command can print the API's own\n * sentence rather than an HTTP status. The bearer token is whatever `resolveToken` produced \u2014 an\n * `msk_\u2026` API token or an access token from a sign-in; the API accepts both in the same header.\n */\n private async json<T>(\n method: string,\n path: string,\n opts: { token?: string | undefined; body?: unknown; timeoutMs?: number } = {},\n ): Promise<T> {\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/api/v1${path}`, {\n method,\n headers: {\n ...(opts.body === undefined ? {} : { 'content-type': 'application/json' }),\n ...(opts.token ? { authorization: `Bearer ${opts.token}` } : {}),\n },\n ...(opts.body === undefined ? {} : { body: JSON.stringify(opts.body) }),\n signal: AbortSignal.timeout(opts.timeoutMs ?? 15_000),\n });\n } catch (e) {\n throw new ApiError(\n 0,\n 'unreachable',\n `could not reach the API at ${this.baseUrl}: ${(e as Error).message}`,\n );\n }\n const body: unknown = await res.json().catch(() => undefined);\n if (!res.ok) {\n const b = (body ?? {}) as { code?: string; error?: string; message?: string };\n throw new ApiError(\n res.status,\n b.code ?? b.error ?? 'error',\n b.message ?? `request failed (${res.status})`,\n );\n }\n return body as T;\n }\n\n /**\n * Sign in as an admin user. TOTP is asked for only when the server says it is needed, so an\n * account without it is never prompted for a code it cannot produce.\n */\n async login(creds: { email: string; password: string; totp?: string | undefined }): Promise<string> {\n const { accessToken } = await this.json<{ accessToken: string }>('POST', '/auth/login', {\n body: {\n email: creds.email,\n password: creds.password,\n ...(creds.totp ? { totp: creds.totp } : {}),\n },\n });\n return accessToken;\n }\n\n licenseStatus(token: string): Promise<LicenseStatus> {\n return this.json<LicenseStatus>('GET', '/license', { token });\n }\n\n /** Install a key. Owner-only, and verified server-side before it is stored. */\n activateLicense(token: string, key: string): Promise<LicenseStatus> {\n return this.json<LicenseStatus>('POST', '/license/activate', { token, body: { key: key.trim() } });\n }\n\n async health(): Promise<{ ok: boolean; version: string; setup: 'pending' | 'complete' } | null> {\n try {\n const res = await fetch(`${this.baseUrl}/api/v1/health`, { signal: AbortSignal.timeout(3000) });\n return res.ok\n ? ((await res.json()) as { ok: boolean; version: string; setup: 'pending' | 'complete' })\n : null;\n } catch {\n return null;\n }\n }\n\n async waitHealthy(timeoutMs: number, onTick?: (elapsed: number) => void) {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const h = await this.health();\n if (h?.ok) return h;\n onTick?.(Date.now() - start);\n await new Promise((r) => setTimeout(r, 2000));\n }\n throw new Error(`API at ${this.baseUrl} did not become healthy within ${timeoutMs / 1000}s`);\n }\n\n async applyAnswers(answers: SetupAnswers) {\n const res = await fetch(`${this.baseUrl}/api/v1/setup/answers`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(answers),\n signal: AbortSignal.timeout(120_000),\n });\n const body = (await res.json()) as {\n message?: string;\n dns?: { type: string; name: string; value: string; priority?: number; purpose: string }[];\n };\n if (!res.ok) throw new Error(`setup failed (${res.status}): ${body.message ?? JSON.stringify(body)}`);\n return body as {\n dns: { type: string; name: string; value: string; priority?: number; purpose: string }[];\n };\n }\n}\n\nexport function formatDns(\n records: { type: string; name: string; value: string; priority?: number; purpose: string }[],\n): string {\n const rows = records.map((r) => [\n r.type,\n r.name,\n r.priority !== undefined ? `${r.priority} ${r.value}` : r.value,\n r.purpose,\n ]);\n const w = [0, 1, 2].map((i) => Math.max(...rows.map((r) => r[i]!.length)));\n return rows\n .map((r) => `${r[0]!.padEnd(w[0]!)} ${r[1]!.padEnd(w[1]!)} ${r[2]!.padEnd(w[2]!)} # ${r[3]}`)\n .join('\\n');\n}\n", "import path from 'node:path';\nimport { run } from './host.js';\nimport { composeFile } from './install-dir.js';\n\n/** `docker compose` bound to the install directory's compose file and `.env`. */\nexport function compose(dir: string) {\n const base = [\n 'compose',\n '--project-name',\n 'mailserver',\n '--env-file',\n path.join(dir, '.env'),\n '-f',\n composeFile(dir),\n ];\n return {\n exec: (args: string[], stdio: 'inherit' | 'pipe' = 'inherit') =>\n run('docker', [...base, ...args], { cwd: dir, stdio }),\n pull: () => run('docker', [...base, 'pull', '--quiet'], { cwd: dir, stdio: 'inherit' }),\n up: () =>\n run('docker', [...base, 'up', '-d', '--remove-orphans', '--wait'], { cwd: dir, stdio: 'inherit' }),\n down: () => run('docker', [...base, 'down'], { cwd: dir, stdio: 'inherit' }),\n ps: async () =>\n (await run('docker', [...base, 'ps', '--format', 'json'], { cwd: dir, stdio: 'pipe' })).stdout,\n /** Run a one-off command inside a service container, capturing stdout. */\n execIn: (service: string, cmd: string[], input?: string | Buffer) =>\n execInService(base, dir, service, cmd, input),\n };\n}\n\nasync function execInService(\n base: string[],\n dir: string,\n service: string,\n cmd: string[],\n input?: string | Buffer,\n) {\n const { spawn } = await import('node:child_process');\n return new Promise<Buffer>((resolve, reject) => {\n const child = spawn('docker', [...base, 'exec', '-T', service, ...cmd], {\n cwd: dir,\n stdio: ['pipe', 'pipe', 'inherit'],\n });\n const chunks: Buffer[] = [];\n child.stdout.on('data', (c: Buffer) => chunks.push(c));\n child.on('error', reject);\n child.on('exit', (code) =>\n code === 0\n ? resolve(Buffer.concat(chunks))\n : reject(new Error(`docker compose exec ${service} ${cmd[0]} exited with ${code}`)),\n );\n if (input !== undefined) child.stdin.end(input);\n else child.stdin.end();\n });\n}\n", "// Host-side helpers for mailctl: shell, ports, sizing, DNS. No mail configuration lives here (ADR 0005).\nimport { execFile } from 'node:child_process';\nimport dns from 'node:dns/promises';\nimport net from 'node:net';\nimport os from 'node:os';\nimport { promisify } from 'node:util';\n\nexport const execFileP = promisify(execFile);\n\nexport async function run(\n cmd: string,\n args: string[],\n opts: {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n stdio?: 'inherit' | 'pipe';\n /**\n * Written to the child's stdin and closed. Used for secrets \u2014 `docker login --password-stdin`\n * keeps the credential out of argv, where any other user on the host could read it from `ps`.\n */\n input?: string;\n } = {},\n) {\n if (opts.stdio === 'inherit' || opts.input !== undefined) {\n const { spawn } = await import('node:child_process');\n return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd: opts.cwd,\n env: { ...process.env, ...opts.env },\n stdio: opts.input === undefined ? 'inherit' : ['pipe', 'inherit', 'inherit'],\n });\n child.on('exit', (code) =>\n code === 0\n ? resolve({ stdout: '', stderr: '' })\n : reject(new Error(`${cmd} ${args.join(' ')} exited with ${code}`)),\n );\n child.on('error', reject);\n if (opts.input !== undefined) child.stdin?.end(opts.input);\n });\n }\n return execFileP(cmd, args, {\n cwd: opts.cwd,\n env: { ...process.env, ...opts.env },\n maxBuffer: 64 * 1024 * 1024,\n });\n}\n\nexport async function commandExists(cmd: string): Promise<boolean> {\n try {\n await execFileP(process.platform === 'win32' ? 'where' : 'which', [cmd]);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function dockerVersion(): Promise<{ docker: string | null; compose: string | null }> {\n const out = { docker: null as string | null, compose: null as string | null };\n try {\n out.docker = (await execFileP('docker', ['version', '--format', '{{.Server.Version}}'])).stdout.trim();\n } catch {\n /* not installed or daemon down */\n }\n try {\n out.compose = (await execFileP('docker', ['compose', 'version', '--short'])).stdout.trim();\n } catch {\n /* no compose plugin */\n }\n return out;\n}\n\n/** True when nothing on this host is listening on `port` (all interfaces). */\nexport function portFree(port: number, host = '0.0.0.0'): Promise<boolean> {\n return new Promise((resolve) => {\n const srv = net.createServer();\n srv.once('error', () => resolve(false));\n srv.listen({ port, host, exclusive: true }, () => srv.close(() => resolve(true)));\n });\n}\n\nexport function memoryGiB(): number {\n return os.totalmem() / 1024 ** 3;\n}\n\nexport async function diskFreeGiB(path: string): Promise<number | null> {\n try {\n const { stdout } = await execFileP('df', ['-Pk', path]);\n const line = stdout.trim().split('\\n').at(-1) ?? '';\n const avail = Number(line.split(/\\s+/)[3]);\n return Number.isFinite(avail) ? avail / 1024 ** 2 : null;\n } catch {\n return null;\n }\n}\n\nexport async function publicIp(): Promise<string | null> {\n for (const url of ['https://api.ipify.org', 'https://ifconfig.me/ip']) {\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(4000) });\n const ip = (await res.text()).trim();\n if (net.isIP(ip)) return ip;\n } catch {\n /* try next */\n }\n }\n return null;\n}\n\nexport async function resolveA(hostname: string): Promise<string[]> {\n try {\n return await dns.resolve4(hostname);\n } catch {\n return [];\n }\n}\n\nexport async function reverseDns(ip: string): Promise<string[]> {\n try {\n return await dns.reverse(ip);\n } catch {\n return [];\n }\n}\n\nexport function randomSecret(bytes = 32): string {\n return Buffer.from(Array.from({ length: bytes }, () => Math.floor(Math.random() * 256))).toString(\n 'base64url',\n );\n}\n", "import fs from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { randomSecret } from './host.js';\n\nexport const DEFAULT_DIR = process.env['MAILSERVER_DIR'] ?? '/opt/mailserver';\n\n/**\n * Where product images come from (ADR 0011). Private ECR, not GHCR \u2014 `ghcr.io/mail-dock/mailserver`\n * was an interim default that never had an image pushed to it.\n *\n * The `/maildock` namespace is part of the value: compose composes references as\n * `${IMAGE_REGISTRY}/<service>:${IMAGE_TAG}` and the repositories are named `maildock/<service>`,\n * which is what keeps compose.yaml portable across registries.\n */\nexport const IMAGE_REGISTRY = '603283648754.dkr.ecr.us-east-1.amazonaws.com/maildock';\n\n/**\n * The compose bundle is shipped inside the CLI package (copied from `compose/` at build time).\n *\n * Two layouts have to work, and they are different depths: this file at `src/lib/install-dir.ts`\n * when `bin/dev.js` runs the sources through tsx, and *inlined into* `dist/index.js` in the\n * published package, because esbuild bundles the whole CLI into one file. A fixed `../../assets`\n * is correct for the first and points above the package root for the second \u2014 which is how\n * `0.1.0-rc.2` shipped a `mailctl install` that could not find its own compose file. Probing is\n * what keeps that from depending on which build you happen to be running.\n */\nexport function assetsDir(from = path.dirname(fileURLToPath(import.meta.url))): string {\n const tried = ['../../assets', '../assets'].map((rel) => path.resolve(from, rel));\n const found = tried.find((p) => existsSync(path.join(p, 'compose.yaml')));\n if (!found)\n throw new Error(\n `mailctl is missing its bundled assets (looked in ${tried.join(' and ')}). Reinstall it: npm i -g ${'@maildock/mailctl'}`,\n );\n return found;\n}\n\nexport function bundledComposeFile(): string {\n return path.join(assetsDir(), 'compose.yaml');\n}\nexport function bundledCaddyfile(): string {\n return path.join(assetsDir(), 'Caddyfile');\n}\n\nexport interface EnvFile {\n [key: string]: string;\n}\n\nexport async function readEnv(dir: string): Promise<EnvFile> {\n const text = await fs.readFile(path.join(dir, '.env'), 'utf8').catch(() => '');\n const env: EnvFile = {};\n for (const line of text.split('\\n')) {\n const m = /^\\s*([A-Z0-9_]+)\\s*=\\s*(.*)\\s*$/.exec(line);\n if (m) env[m[1]!] = m[2]!;\n }\n return env;\n}\n\nexport async function writeEnv(dir: string, env: EnvFile): Promise<void> {\n const body = [\n '# Written by mailctl. Secrets, ports, paths and image tags only (ADR 0005). Configuration lives in the database.',\n ...Object.entries(env).map(([k, v]) => `${k}=${v}`),\n '',\n ].join('\\n');\n await fs.writeFile(path.join(dir, '.env'), body, { mode: 0o600 });\n}\n\n/** Minimal `.env` for a customer install: generated secrets, standard ports, the requested image tag. */\nexport function defaultEnv(opts: {\n hostname: string;\n tag: string;\n registry: string;\n publicIp?: string | null | undefined;\n licenseId?: string | null | undefined;\n}): EnvFile {\n return {\n IMAGE_REGISTRY: opts.registry,\n IMAGE_TAG: opts.tag,\n /**\n * The subscription this server runs under. Empty is legitimate \u2014 a self-built image needs no\n * credentials \u2014 but a customer pulling from the private registry needs it, and `mailctl upgrade`\n * back-fills the key so an older install gains the field without hand-editing.\n */\n LICENSE_ID: opts.licenseId ?? '',\n LICENSE_SERVER_URL: 'https://license.maildock.io',\n MAIL_HOSTNAME: opts.hostname,\n DB_ADMIN_USER: 'mail',\n DB_ADMIN_PASSWORD: randomSecret(24),\n DB_NAME: 'mail',\n DB_DAEMON_USER: 'mail_daemon',\n DB_DAEMON_PASSWORD: randomSecret(24),\n JWT_SECRET: randomSecret(48),\n SECRET_ENCRYPTION_KEY: randomSecret(48),\n DOVECOT_MASTER_PASSWORD: randomSecret(32),\n SRS_SECRET: randomSecret(32),\n /**\n * Seals the source-server credentials a migration holds (ADR 0018). Rotating it makes every\n * in-flight migration's stored credential unopenable \u2014 the admin re-enters them \u2014 but touches\n * nothing else, because no other subsystem uses this key.\n */\n MIGRATION_SECRET_KEY: randomSecret(48),\n PUBLIC_IP: opts.publicIp ?? '',\n COOKIE_SECURE: 'true',\n SMTP_PORT: '25',\n SMTPS_PORT: '465',\n SUBMISSION_PORT: '587',\n IMAP_PORT: '143',\n IMAPS_PORT: '993',\n POP3_PORT: '110',\n POP3S_PORT: '995',\n SIEVE_PORT: '4190',\n HTTP_PORT: '80',\n HTTPS_PORT: '443',\n API_PORT: '3000',\n DB_PORT: '5432',\n };\n}\n\n/** Copy the bundled compose file + Caddyfile into the install directory (idempotent, overwrites on upgrade). */\nexport async function materialise(dir: string): Promise<void> {\n await fs.mkdir(path.join(dir, 'docker', 'caddy'), { recursive: true });\n await fs.mkdir(path.join(dir, 'compose'), { recursive: true });\n await fs.copyFile(bundledComposeFile(), path.join(dir, 'compose', 'compose.yaml'));\n await fs.copyFile(bundledCaddyfile(), path.join(dir, 'docker', 'caddy', 'Caddyfile'));\n}\n\nexport const composeFile = (dir: string) => path.join(dir, 'compose', 'compose.yaml');\n", "import { run } from './host.js';\n\n/**\n * Authenticating to the private image registry (licence-server ADR 0004).\n *\n * This is the point where a subscription actually matters: the licence server hands out short-lived\n * pull credentials, and refuses them for a revoked or lapsed licence. Everything else about\n * licensing degrades politely \u2014 this is the part that stops.\n *\n * Deliberately forgiving in one direction: an install with no `LICENSE_ID` does **not** fail. Someone\n * running images they built themselves, or pulling from a registry that needs no credentials, must\n * not be blocked by a licence check that has nothing to check.\n */\nexport interface RegistryLoginResult {\n status: 'logged-in' | 'skipped' | 'failed';\n message: string;\n}\n\nexport interface LoginEnv {\n LICENSE_ID?: string | undefined;\n LICENSE_SERVER_URL?: string | undefined;\n IMAGE_REGISTRY?: string | undefined;\n}\n\n/** The registry host, without the `/maildock` path suffix `IMAGE_REGISTRY` carries for compose. */\nexport const registryHost = (imageRegistry: string): string => imageRegistry.split('/')[0] ?? imageRegistry;\n\ninterface TokenResponse {\n registry: string;\n username: string;\n password: string;\n expiresAt: string;\n}\n\n/**\n * Exchange the licence id for registry credentials and `docker login`.\n *\n * The password is written to docker's stdin rather than passed as an argument, so it never appears\n * in the process list of a shared host.\n */\n/** `docker login`, injectable so the success path is testable without a docker daemon. */\nexport type DockerLogin = (c: { registry: string; username: string; password: string }) => Promise<void>;\n\nconst dockerLogin: DockerLogin = async (c) => {\n // --password-stdin, never argv: on a shared host anyone can read another process's command line\n await run('docker', ['login', '--username', c.username, '--password-stdin', c.registry], {\n input: c.password,\n });\n};\n\nexport async function registryLogin(\n env: LoginEnv,\n fingerprint: string,\n fetchImpl: typeof fetch = fetch,\n login: DockerLogin = dockerLogin,\n): Promise<RegistryLoginResult> {\n const licenseId = env.LICENSE_ID?.trim();\n const server = env.LICENSE_SERVER_URL?.trim();\n if (!licenseId)\n return { status: 'skipped', message: 'No LICENSE_ID in .env \u2014 pulling without registry credentials.' };\n if (!server)\n return {\n status: 'skipped',\n message: 'No LICENSE_SERVER_URL in .env \u2014 pulling without registry credentials.',\n };\n\n let token: TokenResponse;\n try {\n const res = await fetchImpl(`${server.replace(/\\/$/, '')}/v1/registry/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ licenseId, fingerprint }),\n });\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: string; message?: string };\n // These are the answers a human needs to act on, so they are spelled out rather than passed\n // through as an HTTP status.\n const why =\n body.error === 'expired'\n ? 'This subscription has lapsed. Renew it to pull new images; the running server is unaffected.'\n : body.error === 'revoked'\n ? 'This licence has been revoked. Contact support.'\n : body.error === 'unknown_license'\n ? `The licence server does not recognise ${licenseId}.`\n : body.error === 'fingerprint_mismatch'\n ? 'This licence is registered to a different machine. Transfer it before upgrading here.'\n : (body.message ?? `The licence server returned ${res.status}.`);\n return { status: 'failed', message: why };\n }\n token = (await res.json()) as TokenResponse;\n } catch (e) {\n return {\n status: 'failed',\n message: `Could not reach the licence server at ${server}: ${(e as Error).message}`,\n };\n }\n\n await login({ registry: token.registry, username: token.username, password: token.password });\n return { status: 'logged-in', message: `Authenticated to ${token.registry}.` };\n}\n", "import {\n commandExists,\n diskFreeGiB,\n dockerVersion,\n memoryGiB,\n portFree,\n publicIp,\n resolveA,\n reverseDns,\n} from './host.js';\n\nexport interface Check {\n name: string;\n ok: boolean;\n /** Warnings do not block the install. */\n level: 'error' | 'warn' | 'info';\n detail: string;\n}\n\nexport interface PreflightOptions {\n hostname?: string | undefined;\n dataDir: string;\n ports: number[];\n minMemoryGiB?: number;\n minDiskGiB?: number;\n /** Skip port checks (upgrade/doctor on a running stack). */\n skipPorts?: boolean;\n}\n\nexport const DEFAULT_PORTS = [25, 80, 443, 465, 587, 993, 995];\n\n/** Host readiness checks run by `mailctl install` and `mailctl doctor`. */\nexport async function preflight(o: PreflightOptions): Promise<Check[]> {\n const checks: Check[] = [];\n const dv = await dockerVersion();\n checks.push({\n name: 'docker',\n ok: !!dv.docker,\n level: 'error',\n detail: dv.docker\n ? `engine ${dv.docker}`\n : 'docker engine not reachable (install Docker or run as a user in the docker group)',\n });\n checks.push({\n name: 'docker compose',\n ok: !!dv.compose,\n level: 'error',\n detail: dv.compose ? `v${dv.compose}` : 'compose plugin missing',\n });\n checks.push({\n name: 'curl/tar',\n ok: (await commandExists('tar')) && (await commandExists('curl')),\n level: 'warn',\n detail: 'needed for backup/restore',\n });\n\n const mem = memoryGiB();\n const minMem = o.minMemoryGiB ?? 2;\n checks.push({\n name: 'memory',\n ok: mem >= minMem,\n level: 'error',\n detail: `${mem.toFixed(1)} GiB (min ${minMem})`,\n });\n const disk = await diskFreeGiB(o.dataDir);\n const minDisk = o.minDiskGiB ?? 10;\n checks.push({\n name: 'disk',\n ok: disk === null || disk >= minDisk,\n level: 'warn',\n detail:\n disk === null\n ? `cannot stat ${o.dataDir}`\n : `${disk.toFixed(1)} GiB free at ${o.dataDir} (min ${minDisk})`,\n });\n\n if (!o.skipPorts) {\n for (const p of o.ports) {\n const free = await portFree(p);\n checks.push({\n name: `port ${p}`,\n ok: free,\n level: 'error',\n detail: free ? 'free' : 'in use \u2014 stop the service using it (e.g. an existing MTA, nginx, apache)',\n });\n }\n }\n\n const ip = await publicIp();\n checks.push({\n name: 'public ip',\n ok: !!ip,\n level: 'warn',\n detail: ip ?? 'could not determine (no outbound HTTPS?)',\n });\n if (o.hostname) {\n const a = await resolveA(o.hostname);\n const matches = !!ip && a.includes(ip);\n checks.push({\n name: 'hostname A record',\n ok: a.length > 0,\n level: 'warn',\n detail: a.length\n ? `${o.hostname} \u2192 ${a.join(', ')}${matches ? '' : \" (does not match this host's public IP)\"}`\n : `${o.hostname} does not resolve \u2014 ACME will fail until it does`,\n });\n if (ip) {\n const ptr = await reverseDns(ip);\n checks.push({\n name: 'reverse DNS (PTR)',\n ok: ptr.includes(o.hostname),\n level: 'warn',\n detail: ptr.length\n ? `${ip} \u2192 ${ptr.join(', ')}${ptr.includes(o.hostname) ? '' : ` (expected ${o.hostname}; set at your VPS provider)`}`\n : `no PTR for ${ip} \u2014 many receivers will reject or spam-folder your mail`,\n });\n }\n }\n return checks;\n}\n\nexport const blocking = (checks: Check[]) => checks.filter((c) => !c.ok && c.level === 'error');\n\nexport function formatChecks(checks: Check[]): string {\n return checks\n .map((c) => `${c.ok ? '\u2714' : c.level === 'error' ? '\u2716' : '\u26A0'} ${c.name.padEnd(20)} ${c.detail}`)\n .join('\\n');\n}\n", "import { Command, Flags } from '@oclif/core';\nimport { ApiClient } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { DEFAULT_DIR, readEnv } from '../lib/install-dir.js';\nimport { formatChecks, preflight, type Check } from '../lib/preflight.js';\n\nexport default class Doctor extends Command {\n static override description =\n 'Re-run host preflight, check every service and the API, and verify DNS for the mail hostname.';\n static override flags = { dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }) };\n\n async run() {\n const { flags } = await this.parse(Doctor);\n const env = await readEnv(flags.dir);\n if (!env['MAIL_HOSTNAME']) this.error(`no install found in ${flags.dir} (missing .env)`);\n const checks = await preflight({\n hostname: env['MAIL_HOSTNAME'],\n dataDir: flags.dir,\n ports: [],\n skipPorts: true,\n });\n\n let services: { Service: string; State: string; Health?: string }[] = [];\n try {\n services = (await compose(flags.dir).ps())\n .split('\\n')\n .filter(Boolean)\n .map((l) => JSON.parse(l) as { Service: string; State: string; Health?: string });\n } catch (e) {\n checks.push({ name: 'compose', ok: false, level: 'error', detail: String(e) });\n }\n for (const s of services) {\n const ok = s.State === 'running' && (!s.Health || s.Health === 'healthy');\n checks.push({\n name: `service ${s.Service}`,\n ok,\n level: 'error',\n detail: `${s.State}${s.Health ? ` (${s.Health})` : ''}`,\n });\n }\n const expected = [\n 'postgres',\n 'redis',\n 'rspamd',\n 'dovecot',\n 'postfix',\n 'api',\n 'worker',\n 'web',\n 'caddy',\n 'cert-sync',\n ];\n for (const name of expected.filter((n) => !services.some((s) => s.Service === n))) {\n checks.push({ name: `service ${name}`, ok: false, level: 'error', detail: 'not running' });\n }\n\n const api = new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`);\n const h = await api.health();\n checks.push({\n name: 'api',\n ok: !!h?.ok,\n level: 'error',\n detail: h ? `version ${h.version}, setup ${h.setup}` : 'unreachable',\n });\n\n this.log(formatChecks(checks));\n const bad = checks.filter((c: Check) => !c.ok && c.level === 'error');\n if (bad.length) this.error(`${bad.length} problem(s) found`, { exit: 1 });\n this.log('\\nAll checks passed.');\n }\n}\n", "import { Command, Flags } from '@oclif/core';\nimport { createHash } from 'node:crypto';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { compose } from '../lib/compose.js';\nimport { run } from '../lib/host.js';\nimport { DEFAULT_DIR, readEnv } from '../lib/install-dir.js';\n\n/** Backup = database dump + maildir tarball + .env, plus a manifest with SHA-256 of each part. */\nexport default class Backup extends Command {\n static override description =\n 'Back up the database (pg_dump), all mail (maildir tar) and .env to a timestamped directory with checksums.';\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n out: Flags.string({ description: 'backup root directory', default: path.join(DEFAULT_DIR, 'backups') }),\n };\n\n async run() {\n const { flags } = await this.parse(Backup);\n const target = await backup(flags.dir, flags.out, (m) => this.log(m));\n this.log(`Backup written to ${target}`);\n }\n}\n\nconst sha256 = async (file: string) =>\n createHash('sha256')\n .update(await fs.readFile(file))\n .digest('hex');\n\nexport async function backup(dir: string, outRoot: string, log: (m: string) => void): Promise<string> {\n const env = await readEnv(dir);\n if (!env['DB_NAME']) throw new Error(`no install found in ${dir}`);\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n const out = path.join(outRoot, stamp);\n await fs.mkdir(out, { recursive: true });\n const c = compose(dir);\n\n log('Dumping database\u2026');\n const dump = await c.execIn('postgres', [\n 'pg_dump',\n '-U',\n env['DB_ADMIN_USER'] ?? 'mail',\n '-Fc',\n env['DB_NAME'],\n ]);\n await fs.writeFile(path.join(out, 'database.dump'), dump);\n\n log('Archiving mail\u2026');\n await run(\n 'docker',\n [\n 'run',\n '--rm',\n '-v',\n 'mailserver_vmail:/var/vmail:ro',\n '-v',\n `${out}:/backup`,\n 'alpine:3.20',\n 'tar',\n 'czf',\n '/backup/vmail.tar.gz',\n // The Xapian full-text index (ADR 0013) sits beside each mailbox and is derived data: it would\n // add a large fraction of the mail size to every backup for nothing. `mailctl restore` rebuilds\n // it in the background on the first search, or `doveadm index -A '*'` forces it up front.\n '--exclude=fts-flatcurve',\n '-C',\n '/var',\n 'vmail',\n ],\n { stdio: 'inherit' },\n );\n\n await fs.copyFile(path.join(dir, '.env'), path.join(out, 'env'));\n await fs.chmod(path.join(out, 'env'), 0o600);\n\n const files = ['database.dump', 'vmail.tar.gz', 'env'];\n const manifest = {\n createdAt: new Date().toISOString(),\n version: env['IMAGE_TAG'] ?? 'unknown',\n hostname: env['MAIL_HOSTNAME'],\n files: Object.fromEntries(\n await Promise.all(\n files.map(async (f) => [\n f,\n { sha256: await sha256(path.join(out, f)), bytes: (await fs.stat(path.join(out, f))).size },\n ]),\n ),\n ),\n };\n await fs.writeFile(path.join(out, 'manifest.json'), JSON.stringify(manifest, null, 2));\n return out;\n}\n\nexport async function verifyBackup(backupDir: string): Promise<{ version: string; hostname: string }> {\n const manifest = JSON.parse(await fs.readFile(path.join(backupDir, 'manifest.json'), 'utf8')) as {\n version: string;\n hostname: string;\n files: Record<string, { sha256: string }>;\n };\n for (const [f, meta] of Object.entries(manifest.files)) {\n const actual = await sha256(path.join(backupDir, f));\n if (actual !== meta.sha256)\n throw new Error(`integrity check failed for ${f} (expected ${meta.sha256}, got ${actual})`);\n }\n return manifest;\n}\n", "import { Command, Flags } from '@oclif/core';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { ApiClient } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { run } from '../lib/host.js';\nimport { DEFAULT_DIR, materialise, readEnv } from '../lib/install-dir.js';\nimport { verifyBackup } from './backup.js';\n\nexport default class Restore extends Command {\n static override description =\n 'Restore a backup made by `mailctl backup` (verifies checksums, restores .env, database and mail, restarts the stack).';\n static override args = {};\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n from: Flags.string({ description: 'backup directory (contains manifest.json)', required: true }),\n yes: Flags.boolean({ description: 'do not ask for confirmation', default: false }),\n };\n\n async run() {\n const { flags } = await this.parse(Restore);\n const manifest = await verifyBackup(flags.from);\n this.log(`Backup verified: ${manifest.hostname} @ ${manifest.version}`);\n if (!flags.yes) {\n const { confirm } = await import('@inquirer/prompts').catch(() => ({ confirm: null }));\n if (\n confirm &&\n !(await confirm({\n message: `This REPLACES the database and all mail in ${flags.dir}. Continue?`,\n default: false,\n }))\n )\n this.exit(1);\n }\n await fs.mkdir(flags.dir, { recursive: true });\n await fs.copyFile(path.join(flags.from, 'env'), path.join(flags.dir, '.env'));\n await fs.chmod(path.join(flags.dir, '.env'), 0o600);\n await materialise(flags.dir);\n const env = await readEnv(flags.dir);\n const c = compose(flags.dir);\n\n this.log('Stopping mail services\u2026');\n await c.exec(['stop', 'postfix', 'dovecot', 'api', 'cert-sync']);\n await c.exec(['up', '-d', '--wait', 'postgres']);\n\n this.log('Restoring database\u2026');\n const user = env['DB_ADMIN_USER'] ?? 'mail';\n const db = env['DB_NAME'] ?? 'mail';\n await c.execIn('postgres', [\n 'psql',\n '-U',\n user,\n '-d',\n 'postgres',\n '-c',\n `DROP DATABASE IF EXISTS ${db} WITH (FORCE)`,\n ]);\n await c.execIn('postgres', ['psql', '-U', user, '-d', 'postgres', '-c', `CREATE DATABASE ${db}`]);\n await c.execIn(\n 'postgres',\n ['pg_restore', '-U', user, '-d', db, '--no-owner'],\n await fs.readFile(path.join(flags.from, 'database.dump')),\n );\n\n this.log('Restoring mail\u2026');\n await run(\n 'docker',\n [\n 'run',\n '--rm',\n '-v',\n 'mailserver_vmail:/var/vmail',\n '-v',\n `${path.resolve(flags.from)}:/backup:ro`,\n 'alpine:3.20',\n 'sh',\n '-c',\n 'rm -rf /var/vmail/* && tar xzf /backup/vmail.tar.gz -C /var && chown -R 5000:5000 /var/vmail',\n ],\n { stdio: 'inherit' },\n );\n\n this.log('Starting services\u2026');\n await c.up();\n await new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`).waitHealthy(120_000);\n this.log('Restore complete.');\n }\n}\n", "import { Command, Flags } from '@oclif/core';\nimport { ApiClient } from '../lib/api.js';\nimport { compose } from '../lib/compose.js';\nimport { registryLogin } from '../lib/registry.js';\nimport { DEFAULT_DIR, materialise, readEnv } from '../lib/install-dir.js';\nimport { runUpgrade } from '../lib/upgrade.js';\nimport { backup } from './backup.js';\nimport { fingerprint } from '@mailserver/license';\n\n/**\n * Upgrade = snapshot the host state \u2192 backup \u2192 set IMAGE_TAG (+ back-fill any .env key a newer\n * release introduced) \u2192 pull \u2192 up (the API runs migrations on boot) \u2192 health check \u2192 **roll back if\n * that health check never passes**.\n *\n * The rollback is conditional on evidence rather than unconditional, because the API migrates on\n * boot: putting the previous release back is only safe while the schema has not moved. When it has,\n * or when the database cannot be read to find out, the upgrade stops and hands the operator the\n * backup it took \u2014 a wrong guess there costs a customer their mail, and there is no undo for that.\n * The decisions live in `lib/upgrade.ts` behind an interface so they can be tested; this command is\n * only the wiring and the exit code.\n */\nexport default class Upgrade extends Command {\n static override description =\n 'Upgrade to a new version: pre-upgrade backup, pull images, restart, run migrations, verify health, and roll back automatically if the health check fails.';\n static override flags = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n tag: Flags.string({ description: 'target image tag (default: latest)', default: 'latest' }),\n 'skip-backup': Flags.boolean({\n default: false,\n description: 'skip the pre-upgrade backup (not recommended)',\n }),\n 'no-rollback': Flags.boolean({\n default: false,\n description: 'leave a failed upgrade in place instead of restoring the previous release',\n }),\n 'health-timeout': Flags.integer({\n description: 'seconds to wait for the API to report healthy',\n default: 180,\n }),\n };\n\n async run() {\n const { flags } = await this.parse(Upgrade);\n const env = await readEnv(flags.dir);\n if (!env['IMAGE_TAG']) this.error(`no install found in ${flags.dir}`);\n\n const r = await runUpgrade(\n {\n compose: compose(flags.dir),\n api: new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`),\n backup,\n materialise,\n registryLogin,\n log: (m) => this.log(m),\n },\n {\n dir: flags.dir,\n tag: flags.tag,\n fingerprint: fingerprint(),\n skipBackup: flags['skip-backup'],\n noRollback: flags['no-rollback'],\n healthTimeoutMs: flags['health-timeout'] * 1000,\n },\n );\n\n // A successful rollback is not an error to print \u2014 but the upgrade still failed, and a script\n // (or `doctor` in a loop) has to be able to tell. Hence a message and a non-zero code, not both.\n if (r.outcome === 'upgraded' || r.outcome === 'rolled_back') {\n this.log(r.message);\n if (r.exitCode !== 0) this.exit(r.exitCode);\n return;\n }\n this.error(r.message, { exit: r.exitCode });\n }\n}\n", "import path from 'node:path';\nimport type { EnvFile } from './install-dir.js';\nimport { defaultEnv, readEnv, writeEnv } from './install-dir.js';\nimport type { RegistryLoginResult } from './registry.js';\nimport {\n SCHEMA_STATE_SQL,\n clearSnapshot,\n parseSchemaState,\n restoreSnapshot,\n rollbackDecision,\n snapshot,\n type SchemaState,\n} from './rollback.js';\n\n/**\n * The upgrade flow, with everything that touches docker, the network or the clock injected.\n *\n * `mailctl upgrade` is the one command that can leave a customer without mail, and it cannot be\n * driven for real on a developer's machine \u2014 `lib/compose.ts` pins `--project-name mailserver`, so\n * running it would replace the dev stack. Keeping the decisions here, behind an interface, is what\n * makes \"it rolls back\" a thing the tests assert rather than a thing the docs claim.\n */\nexport interface UpgradeDeps {\n compose: {\n pull(): Promise<unknown>;\n up(): Promise<unknown>;\n execIn(service: string, cmd: string[]): Promise<Buffer>;\n };\n api: { waitHealthy(ms: number): Promise<{ version: string }> };\n backup(dir: string, outRoot: string, log: (m: string) => void): Promise<string>;\n /**\n * Write the compose bundle shipped inside this `mailctl` into the install directory. Injected like\n * the rest: it reads `assets/`, which is generated at build time and gitignored, so a test that\n * called the real one would depend on whether someone had run a build first.\n */\n materialise(dir: string): Promise<void>;\n registryLogin(env: EnvFile, fingerprint: string): Promise<RegistryLoginResult>;\n log(m: string): void;\n}\n\nexport interface UpgradeOptions {\n dir: string;\n tag: string;\n fingerprint: string;\n skipBackup: boolean;\n noRollback: boolean;\n healthTimeoutMs: number;\n}\n\nexport type UpgradeOutcome =\n /** The new release came up healthy. */\n | 'upgraded'\n /** It did not, and the previous release is running again. */\n | 'rolled_back'\n /** It did not, and rolling back would have been unsafe or unprovable \u2014 restore from the backup. */\n | 'restore_required'\n /** It did not, and `--no-rollback` said to leave it alone. */\n | 'left_in_place'\n /** The licence was refused at the registry, so nothing was pulled or restarted. */\n | 'registry_refused';\n\nexport interface UpgradeResult {\n outcome: UpgradeOutcome;\n /** 0 upgraded \u00B7 3 registry refused \u00B7 4 rolled back \u00B7 5 needs a person. */\n exitCode: number;\n message: string;\n}\n\nconst EXIT: Record<UpgradeOutcome, number> = {\n upgraded: 0,\n registry_refused: 3,\n rolled_back: 4,\n restore_required: 5,\n left_in_place: 5,\n};\nconst result = (outcome: UpgradeOutcome, message: string): UpgradeResult => ({\n outcome,\n exitCode: EXIT[outcome],\n message,\n});\n\n/**\n * Read Drizzle's migration bookkeeping through the database container. Null on any failure \u2014\n * postgres not up, the table absent on a very old install, psql missing \u2014 and that null is what\n * makes `rollbackDecision` refuse rather than assume.\n */\nasync function schemaState(deps: UpgradeDeps, env: EnvFile): Promise<SchemaState | null> {\n try {\n const out = await deps.compose.execIn('postgres', [\n 'psql',\n '-U',\n env['DB_ADMIN_USER'] ?? 'mail',\n '-d',\n env['DB_NAME'] ?? 'mail',\n '-tAc',\n SCHEMA_STATE_SQL,\n ]);\n return parseSchemaState(out.toString('utf8'));\n } catch {\n return null;\n }\n}\n\nexport async function runUpgrade(deps: UpgradeDeps, opts: UpgradeOptions): Promise<UpgradeResult> {\n const env = await readEnv(opts.dir);\n if (!env['IMAGE_TAG']) throw new Error(`no install found in ${opts.dir}`);\n const from = env['IMAGE_TAG'];\n deps.log(`Upgrading ${env['MAIL_HOSTNAME']} from ${from} to ${opts.tag}`);\n\n // Read the schema *before* anything is touched: this is the only moment it is knowably the old\n // release's, and without it a rollback later has nothing to compare against.\n const before = await schemaState(deps, env);\n\n await snapshot(opts.dir);\n let backupDir: string | null = null;\n if (!opts.skipBackup) {\n backupDir = await deps.backup(opts.dir, path.join(opts.dir, 'backups'), deps.log);\n deps.log(`Pre-upgrade backup: ${backupDir}`);\n }\n const hint = backupDir\n ? `mailctl restore --from ${backupDir}`\n : 'mailctl restore --from <backup dir> (this run used --skip-backup, so it made none)';\n\n const defaults = defaultEnv({\n hostname: env['MAIL_HOSTNAME'] ?? '',\n tag: opts.tag,\n registry: env['IMAGE_REGISTRY'] ?? '',\n publicIp: env['PUBLIC_IP'],\n });\n const added = Object.keys(defaults).filter((k) => !(k in env));\n if (added.length) deps.log(`Adding new .env keys: ${added.join(', ')}`);\n await writeEnv(opts.dir, { ...defaults, ...env, IMAGE_TAG: opts.tag });\n await deps.materialise(opts.dir);\n\n const login = await deps.registryLogin(env, opts.fingerprint);\n // A refusal here is fatal for an upgrade in a way it is not for an install: continuing would pull\n // nothing, restart the stack on the images it already has, and report success. Nothing has been\n // started yet, so the snapshot simply goes back.\n if (login.status === 'failed') {\n await restoreSnapshot(opts.dir);\n // The host is byte-for-byte where it started, so the snapshot has no further job. Leaving it\n // would make \"a snapshot is still there\" mean two different things.\n await clearSnapshot(opts.dir);\n return result('registry_refused', login.message);\n }\n deps.log(login.message);\n\n try {\n deps.log('Pulling images\u2026');\n await deps.compose.pull();\n deps.log('Restarting services (migrations run on API start)\u2026');\n await deps.compose.up();\n const h = await deps.api.waitHealthy(opts.healthTimeoutMs);\n await clearSnapshot(opts.dir);\n return result(\n 'upgraded',\n `Upgrade complete: API reports version ${h.version}.\\nIf something is wrong later: ${hint}`,\n );\n } catch (e) {\n return recover(deps, opts, { from, before, hint, cause: e as Error });\n }\n}\n\nasync function recover(\n deps: UpgradeDeps,\n opts: UpgradeOptions,\n ctx: { from: string; before: SchemaState | null; hint: string; cause: Error },\n): Promise<UpgradeResult> {\n deps.log('');\n deps.log(`Upgrade failed: ${ctx.cause.message}`);\n\n if (opts.noRollback)\n return result('left_in_place', `Left in place as asked (--no-rollback). To undo it: ${ctx.hint}`);\n\n const decision = rollbackDecision(ctx.before, await schemaState(deps, await readEnv(opts.dir)));\n if (decision.kind !== 'roll_back') {\n deps.log(`Not rolling back automatically: ${decision.reason}.`);\n return result(\n 'restore_required',\n `Starting ${ctx.from} against this database could turn a recoverable failure into a permanent one, so nothing was changed back. Restore instead: ${ctx.hint}`,\n );\n }\n\n deps.log(`Rolling back to ${ctx.from} \u2014 ${decision.reason}.`);\n const restored = await restoreSnapshot(opts.dir);\n deps.log(`Restored ${restored.join(', ')}.`);\n try {\n await deps.compose.pull();\n await deps.compose.up();\n const h = await deps.api.waitHealthy(opts.healthTimeoutMs);\n await clearSnapshot(opts.dir);\n return result(\n 'rolled_back',\n `Rolled back to ${ctx.from}: the API is healthy again and reports version ${h.version}.\\nNothing was lost. The upgrade that failed is described above.`,\n );\n } catch (e) {\n return result(\n 'restore_required',\n `Rollback to ${ctx.from} also failed to come up healthy (${(e as Error).message}). Restore from the backup: ${ctx.hint}`,\n );\n }\n}\n", "import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { composeFile } from './install-dir.js';\n\n/**\n * Rolling an upgrade back (roadmap Phase 5b).\n *\n * The hard part is not putting the old image tag back \u2014 it is knowing whether that is *safe*. The\n * API runs pending migrations on boot, so a failed upgrade may already have moved the schema\n * forward, and starting the previous release against a newer schema is how a recoverable failure\n * becomes an unrecoverable one. So the decision is made from evidence, and when the evidence is\n * missing this refuses to act rather than guessing.\n */\n\n/** Where the pre-upgrade host state is parked. Deleted once an upgrade succeeds. */\nexport const SNAPSHOT_DIR = '.upgrade-rollback';\n\n/** Drizzle's bookkeeping, as a fingerprint: how many migrations have run and when the newest ran. */\nexport interface SchemaState {\n migrations: number;\n /** `created_at` of the newest applied migration (epoch ms, as text \u2014 it is only ever compared). */\n latest: string;\n}\n\n/** One line of `count|max(created_at)` from `drizzle.__drizzle_migrations`. */\nexport const SCHEMA_STATE_SQL =\n \"select count(*)::text || '|' || coalesce(max(created_at)::text, '') from drizzle.__drizzle_migrations\";\n\nexport function parseSchemaState(out: string): SchemaState | null {\n const [count, latest] = out.trim().split('|');\n const n = Number(count);\n if (!Number.isInteger(n) || latest === undefined) return null;\n return { migrations: n, latest };\n}\n\nexport type RollbackDecision =\n | { kind: 'roll_back'; reason: string }\n | { kind: 'restore_required'; reason: string }\n | { kind: 'unknown'; reason: string };\n\n/**\n * Whether putting the previous release back is safe.\n *\n * Unchanged schema is the common failure \u2014 an image that will not start, a health check that never\n * passes, a pull that half-completed \u2014 and the one case where the old release finds exactly the\n * database it left. Anything else, including \"the database could not be read\", is handed to a\n * person with the backup we took: a wrong guess here costs a customer their mail.\n */\nexport function rollbackDecision(before: SchemaState | null, after: SchemaState | null): RollbackDecision {\n if (!before || !after)\n return {\n kind: 'unknown',\n reason: !before\n ? 'the database schema could not be read before the upgrade, so there is nothing to compare against'\n : 'the database could not be read after the upgrade, so whether it was migrated is unknown',\n };\n if (before.migrations === after.migrations && before.latest === after.latest)\n return {\n kind: 'roll_back',\n reason: `the database was not migrated (${before.migrations} migrations, unchanged)`,\n };\n return {\n kind: 'restore_required',\n reason: `the database was migrated during the upgrade (${before.migrations} \u2192 ${after.migrations} migrations)`,\n };\n}\n\n/** The files an upgrade overwrites, relative to the install directory. */\nconst SNAPSHOT_FILES = ['.env', 'compose/compose.yaml', 'docker/caddy/Caddyfile'] as const;\n\n/**\n * Copy the host state an upgrade is about to overwrite.\n *\n * `.env` alone is not enough: `materialise` also replaces the compose file and the Caddyfile with\n * the ones bundled in *this* CLI, so an image-only rollback would leave the previous release running\n * under a newer release's compose file.\n */\nexport async function snapshot(dir: string): Promise<void> {\n const out = path.join(dir, SNAPSHOT_DIR);\n await fs.rm(out, { recursive: true, force: true });\n await fs.mkdir(path.join(out, 'compose'), { recursive: true });\n await fs.mkdir(path.join(out, 'docker', 'caddy'), { recursive: true });\n for (const f of SNAPSHOT_FILES) {\n // A file that is not there yet (an install predating one of them) is not an error: it simply has\n // nothing to restore, and `restoreSnapshot` skips it for the same reason.\n await fs.copyFile(path.join(dir, f), path.join(out, f)).catch(() => undefined);\n }\n await fs.chmod(path.join(out, '.env'), 0o600).catch(() => undefined);\n}\n\n/** Put the snapshot back. Returns the files actually restored, so the caller can say what it did. */\nexport async function restoreSnapshot(dir: string): Promise<string[]> {\n const out = path.join(dir, SNAPSHOT_DIR);\n const restored: string[] = [];\n for (const f of SNAPSHOT_FILES) {\n const from = path.join(out, f);\n if (!(await fs.stat(from).catch(() => null))) continue;\n await fs.mkdir(path.dirname(path.join(dir, f)), { recursive: true });\n await fs.copyFile(from, path.join(dir, f));\n restored.push(f);\n }\n await fs.chmod(path.join(dir, '.env'), 0o600).catch(() => undefined);\n return restored;\n}\n\n/** Drop the snapshot once the upgrade has proved itself, so a stale one cannot mislead later. */\nexport const clearSnapshot = (dir: string) =>\n fs.rm(path.join(dir, SNAPSHOT_DIR), { recursive: true, force: true });\n\n/** Sanity check used by the command: the compose file it is about to snapshot actually exists. */\nexport const installLooksComplete = (dir: string) =>\n fs\n .stat(composeFile(dir))\n .then(() => true)\n .catch(() => false);\n", "import { Args, Command, Flags } from '@oclif/core';\nimport type { LicenseStatus } from '@mailserver/core';\nimport { ApiClient, ApiError } from '../lib/api.js';\nimport { DEFAULT_DIR, readEnv, writeEnv } from '../lib/install-dir.js';\nimport { formatStatus, readKey, resolveToken, transferPlan } from '../lib/license.js';\n\n/**\n * `mailctl license status|activate|transfer` (ADR 0005, ADR 0014).\n *\n * These talk to the control plane's own `/license` endpoints rather than to the licence server: the\n * product's licence state is offline by design, and a command that needed the internet to answer\n * \"what is wrong with my licence\" would fail exactly when it is most needed.\n */\nconst common = {\n dir: Flags.string({ description: 'install directory', default: DEFAULT_DIR }),\n 'api-url': Flags.string({ description: 'control plane base URL (default: the local install)' }),\n token: Flags.string({ description: 'API token with the owner role; otherwise you are asked to sign in' }),\n email: Flags.string({ description: 'admin email, to skip one prompt' }),\n};\n\n/** The local API, addressed the way `doctor` addresses it: loopback on the port `.env` records. */\nasync function apiFor(dir: string, override: string | undefined, fail: (m: string) => never) {\n if (override) return new ApiClient(override.replace(/\\/$/, ''));\n const env = await readEnv(dir);\n if (!env['API_PORT'] && !env['MAIL_HOSTNAME'])\n fail(`no install found in ${dir} (missing .env) \u2014 pass --api-url to reach a server elsewhere`);\n return new ApiClient(`http://127.0.0.1:${env['API_PORT'] ?? '3000'}`);\n}\n\n/**\n * Exit non-zero only when the licence is actually stopping something, so this command can be a\n * monitoring check. `grace` is a warning and exits 0 on purpose \u2014 it blocks nothing (ADR 0014).\n */\nconst failing = (s: LicenseStatus) => s.state === 'degraded';\n\nexport class LicenseStatusCommand extends Command {\n static override id = 'license:status';\n static override description =\n 'Show the licence: state, plan, usage against its limits, this machine\u2019s ID and the last check-in. Exits 1 when degraded.';\n static override examples = ['<%= config.bin %> license status'];\n static override flags = common;\n\n async run() {\n const { flags } = await this.parse(LicenseStatusCommand);\n const api = await apiFor(flags.dir, flags['api-url'], (m) => this.error(m));\n const token = await resolveToken(api, { token: flags.token, email: flags.email });\n const status = await api.licenseStatus(token);\n this.log(formatStatus(status));\n if (failing(status)) this.exit(1);\n }\n}\n\nexport class LicenseActivateCommand extends Command {\n static override id = 'license:activate';\n static override description =\n 'Install a licence key. The key is verified by the server before it is stored, so a bad one is refused here rather than silently ignored.';\n static override examples = [\n '<%= config.bin %> license activate mdl1.\u2026',\n '<%= config.bin %> license activate',\n ];\n static override args = { key: Args.string({ description: 'the mdl1.\u2026 key; prompted for when omitted' }) };\n static override flags = common;\n\n async run() {\n const { args, flags } = await this.parse(LicenseActivateCommand);\n const api = await apiFor(flags.dir, flags['api-url'], (m) => this.error(m));\n const token = await resolveToken(api, { token: flags.token, email: flags.email });\n const key = await readKey(args.key);\n if (!key) this.error('no licence key given');\n\n let status: LicenseStatus;\n try {\n status = await api.activateLicense(token, key);\n } catch (e) {\n if (e instanceof ApiError && e.code === 'license_fingerprint_mismatch')\n this.error(`${e.message}\\nRun \\`mailctl license transfer\\` to see what moving it here involves.`);\n throw e;\n }\n this.log(formatStatus(status));\n await recordLicenseId(flags.dir, status, (m) => this.log(m));\n }\n}\n\nexport class LicenseTransferCommand extends Command {\n static override id = 'license:transfer';\n static override description =\n 'Move this subscription onto this machine: report the two machine IDs a transfer needs, and install the re-issued key.';\n static override examples = [\n '<%= config.bin %> license transfer',\n '<%= config.bin %> license transfer --key mdl1.\u2026',\n ];\n static override flags = {\n ...common,\n key: Flags.string({ description: 're-issued key to install, once the licence has been transferred' }),\n };\n\n async run() {\n const { flags } = await this.parse(LicenseTransferCommand);\n const api = await apiFor(flags.dir, flags['api-url'], (m) => this.error(m));\n const token = await resolveToken(api, { token: flags.token, email: flags.email });\n const plan = transferPlan(await api.licenseStatus(token));\n\n // With a key in hand the diagnosis is beside the point \u2014 install it and report what happened.\n if (flags.key) {\n const status = await api.activateLicense(token, flags.key);\n this.log('Licence installed on this machine.\\n');\n this.log(formatStatus(status));\n await recordLicenseId(flags.dir, status, (m) => this.log(m));\n return;\n }\n\n this.log(plan.message);\n if (plan.kind === 'mismatch') this.exit(1);\n }\n}\n\n/**\n * Keep `.env`'s `LICENSE_ID` in step with the installed key.\n *\n * It is not what enforces anything \u2014 that is the key itself \u2014 but it is what `install` and `upgrade`\n * exchange for registry credentials, so a server whose licence was activated by hand and whose\n * `.env` still says nothing would fail its next image pull for no visible reason.\n */\nasync function recordLicenseId(dir: string, status: LicenseStatus, log: (m: string) => void) {\n const id = status.license?.licenseId;\n if (!id) return;\n const env = await readEnv(dir);\n if (Object.keys(env).length === 0 || env['LICENSE_ID'] === id) return;\n await writeEnv(dir, { ...env, LICENSE_ID: id });\n log(`\\nRecorded LICENSE_ID=${id} in ${dir}/.env \u2014 image pulls authenticate with it.`);\n}\n", "import type { LicenseStatus } from '@mailserver/core';\nimport { ApiError, type ApiClient } from './api.js';\n\n/**\n * The licence half of `mailctl` (ADR 0005 lists `license` among the host-lifecycle commands).\n *\n * Everything here is formatting and decision-making over what `GET /license` already returns; the\n * state machine itself lives in `@mailserver/license` and runs in the API, so the CLI and the admin\n * page can never disagree about whether a server is in grace.\n */\n\n/** Where a token comes from when `--token` is not given. Matches the `MAILDOCK_*` prefix `install` uses. */\nexport const TOKEN_ENV = 'MAILDOCK_TOKEN';\n\nconst line = (label: string, value: string) => ` ${`${label}:`.padEnd(16)} ${value}`;\nconst date = (iso: string) => new Date(iso).toISOString().replace('T', ' ').slice(0, 16) + ' UTC';\nconst limit = (used: number, max: number) => (max > 0 ? `${used} of ${max}` : `${used} (unlimited)`);\n\nconst HEADLINE: Record<LicenseStatus['state'], string> = {\n unlicensed: 'no licence installed \u2014 nothing is enforced',\n active: 'active',\n grace: 'grace period \u2014 everything still works',\n degraded: 'degraded \u2014 new mailboxes and domains are refused',\n};\n\n/**\n * `mailctl license status`, as an operator reads it.\n *\n * Mail flow is stated explicitly on a degraded server. The single most likely reason someone runs\n * this command is that a banner frightened them, and \"mail is still flowing\" is the answer they\n * need before any of the rest of it matters (ADR 0014).\n */\nexport function formatStatus(s: LicenseStatus): string {\n const out: string[] = [`Licence: ${HEADLINE[s.state]}`];\n if (s.reason) out.push(` ${s.reason}`);\n if (s.state === 'grace' && s.daysRemaining !== null)\n out.push(` ${s.daysRemaining} day(s) before new mailboxes and domains are refused.`);\n if (s.state === 'degraded')\n out.push(' Delivery, IMAP, POP3, webmail and sending are unaffected and always will be.');\n\n out.push('');\n if (s.license) {\n out.push(line('Licence ID', s.license.licenseId));\n out.push(line('Tier', s.license.tier));\n out.push(line('Issued to', s.license.issuedTo ?? '\u2014'));\n out.push(line('Issued', date(s.license.issuedAt)));\n out.push(line('Expires', date(s.license.expiresAt)));\n if (s.license.features.length) out.push(line('Features', s.license.features.join(', ')));\n }\n out.push(line('Mailboxes', limit(s.usage.mailboxes, s.license?.maxMailboxes ?? 0)));\n out.push(line('Domains', limit(s.usage.domains, s.license?.maxDomains ?? 0)));\n out.push(line('Machine ID', s.machine));\n out.push(line('Key bound to', s.license?.boundTo ?? 'any machine'));\n out.push(line('Last check-in', s.lastHeartbeatAt ? date(s.lastHeartbeatAt) : 'not yet'));\n if (s.lastError) out.push(line('Last error', s.lastError));\n return out.join('\\n');\n}\n\nexport type TransferPlan =\n | { kind: 'no_license'; message: string }\n | { kind: 'portable'; message: string }\n | { kind: 'already_here'; message: string }\n | { kind: 'mismatch'; licenseId: string; boundTo: string; machine: string; message: string };\n\n/**\n * What a transfer means on *this* host, decided from the status alone.\n *\n * A machine ID is derived from machine-id, MAC and hostname, so it changes whenever a host is\n * rebuilt, restored onto new hardware or renamed \u2014 which is exactly when someone reaches for this\n * command. Only `mismatch` is a transfer; the other three answers exist so the command says \"there\n * is nothing to move\" instead of walking someone through a procedure they do not need.\n */\nexport function transferPlan(s: LicenseStatus): TransferPlan {\n if (!s.license)\n return {\n kind: 'no_license',\n message:\n 'No licence is installed on this server, so there is nothing to transfer. Run `mailctl license activate <key>` with the key for this subscription.',\n };\n if (s.license.boundTo === null)\n return {\n kind: 'portable',\n message: `Licence ${s.license.licenseId} is not bound to a machine \u2014 it runs here as it stands, and no transfer is needed.`,\n };\n if (s.license.boundTo === s.machine)\n return {\n kind: 'already_here',\n message: `Licence ${s.license.licenseId} is already bound to this machine (${s.machine}). Nothing to transfer.`,\n };\n return {\n kind: 'mismatch',\n licenseId: s.license.licenseId,\n boundTo: s.license.boundTo,\n machine: s.machine,\n message: [\n `Licence ${s.license.licenseId} is bound to machine ${s.license.boundTo}, but this server is ${s.machine}.`,\n '',\n 'To move it here:',\n ` 1. Ask for licence ${s.license.licenseId} to be transferred to machine ${s.machine}.`,\n ' 2. Run this command again with the re-issued key:',\n ' mailctl license transfer --key mdl1.\u2026',\n '',\n 'Until then this server is degraded: it refuses new mailboxes and domains, and nothing else.',\n ].join('\\n'),\n };\n}\n\n/**\n * Prompt for a licence key on stdin when one was not passed as an argument.\n *\n * A key is long enough that pasting it into a prompt beats retyping a command line, and keeping it\n * off argv means it never appears in the host's process list or the operator's shell history.\n */\nexport async function readKey(fromArg: string | undefined): Promise<string> {\n if (fromArg?.trim()) return fromArg.trim();\n const { password } = await import('@inquirer/prompts');\n const key = await password({ message: 'Licence key (mdl1.\u2026)', mask: false });\n return key.trim();\n}\n\n/**\n * A bearer token for the licence endpoints.\n *\n * `--token` or `MAILDOCK_TOKEN` first, for scripts; otherwise sign in as an admin user, which is\n * what an operator sitting at the host actually has. Nothing is cached to disk \u2014 a `mailctl`\n * invocation that leaves a credential behind on the filesystem would be a worse trade than typing\n * a password twice.\n */\nexport async function resolveToken(\n api: ApiClient,\n opts: { token?: string | undefined; email?: string | undefined; interactive?: boolean } = {},\n): Promise<string> {\n const explicit = opts.token?.trim() || process.env[TOKEN_ENV]?.trim();\n if (explicit) return explicit;\n if (opts.interactive === false || !process.stdin.isTTY)\n throw new Error(\n `no credentials: pass --token, or set ${TOKEN_ENV} to an API token with the owner role (Admin UI \u2192 API tokens)`,\n );\n\n const { input, password } = await import('@inquirer/prompts');\n const email = opts.email ?? (await input({ message: 'Admin email' }));\n const pass = await password({ message: 'Password' });\n try {\n return await api.login({ email, password: pass });\n } catch (e) {\n if (e instanceof ApiError && e.code === 'totp_required')\n return api.login({ email, password: pass, totp: await input({ message: 'Two-factor code' }) });\n throw e;\n }\n}\n", "// @maildock/mailctl \u2014 mailctl: install, upgrade, backup, restore, doctor, license (host lifecycle\n// only \u2014 ADR 0005; `license` is a commercial act on the host, not a configuration subcommand)\nexport const PACKAGE_NAME = '@maildock/mailctl' as const;\nexport { default as Install } from './commands/install.js';\nexport { default as Doctor } from './commands/doctor.js';\nexport { default as Backup } from './commands/backup.js';\nexport { default as Restore } from './commands/restore.js';\nexport { default as Upgrade } from './commands/upgrade.js';\nexport { LicenseActivateCommand, LicenseStatusCommand, LicenseTransferCommand } from './commands/license.js';\nexport { formatStatus, transferPlan, resolveToken, TOKEN_ENV } from './lib/license.js';\nexport { preflight, blocking, formatChecks } from './lib/preflight.js';\nexport {\n defaultEnv,\n readEnv,\n writeEnv,\n assetsDir,\n bundledComposeFile,\n bundledCaddyfile,\n} from './lib/install-dir.js';\nexport {\n parseSchemaState,\n restoreSnapshot,\n rollbackDecision,\n snapshot,\n clearSnapshot,\n SNAPSHOT_DIR,\n} from './lib/rollback.js';\nexport { runUpgrade, type UpgradeDeps, type UpgradeResult } from './lib/upgrade.js';\n\nimport Install from './commands/install.js';\nimport Doctor from './commands/doctor.js';\nimport Backup from './commands/backup.js';\nimport Restore from './commands/restore.js';\nimport Upgrade from './commands/upgrade.js';\nimport { LicenseActivateCommand, LicenseStatusCommand, LicenseTransferCommand } from './commands/license.js';\n/** oclif explicit command map (package.json \u2192 oclif.commands). */\nexport const COMMANDS = {\n install: Install,\n doctor: Doctor,\n backup: Backup,\n restore: Restore,\n upgrade: Upgrade,\n // oclif ids use ':' internally; `topicSeparator: ' '` is what makes them `mailctl license status`.\n 'license:status': LicenseStatusCommand,\n 'license:activate': LicenseActivateCommand,\n 'license:transfer': LicenseTransferCommand,\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["fs", "path", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "z", "ApiError", "path", "path", "path", "line", "hostname", "line", "path", "fingerprint", "fs", "hostname", "path", "result", "Command", "Flags", "Command", "Flags", "Command", "Flags", "createHash", "fs", "path", "Command", "Flags", "path", "createHash", "fs", "Command", "Flags", "fs", "path", "Command", "Flags", "fs", "path", "Command", "Flags", "path", "fs", "path", "path", "fs", "path", "Command", "Flags", "Command", "Flags", "ApiError", "Flags", "Command", "ApiError"]
7
7
  }