@maildock/mailctl 0.1.0-rc.2

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.
@@ -0,0 +1,7 @@
1
+ {
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"]
7
+ }
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@maildock/mailctl",
3
+ "version": "0.1.0-rc.2",
4
+ "type": "module",
5
+ "description": "mailctl — install, upgrade, backup, restore and health-check a MailDock mail server",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "scripts": {
13
+ "assets": "mkdir -p assets && cp ../../compose/compose.yaml assets/compose.yaml && cp ../../docker/caddy/Caddyfile assets/Caddyfile",
14
+ "bundle": "node scripts/bundle.mjs",
15
+ "build": "pnpm run assets && pnpm run bundle",
16
+ "prepack": "pnpm run build",
17
+ "typecheck": "tsc -p tsconfig.json --noEmit"
18
+ },
19
+ "dependencies": {
20
+ "@inquirer/prompts": "^8.7.0",
21
+ "@oclif/core": "^4.14.0",
22
+ "yaml": "^2.9.0",
23
+ "zod": "^4.4.3"
24
+ },
25
+ "devDependencies": {
26
+ "@mailserver/core": "workspace:*",
27
+ "esbuild": "^0.25.0",
28
+ "tsx": "^4.23.12"
29
+ },
30
+ "bin": {
31
+ "mailctl": "./bin/run.js"
32
+ },
33
+ "files": [
34
+ "bin",
35
+ "dist",
36
+ "assets"
37
+ ],
38
+ "oclif": {
39
+ "bin": "mailctl",
40
+ "dirname": "mailctl",
41
+ "commands": {
42
+ "strategy": "explicit",
43
+ "target": "./dist/index.js",
44
+ "identifier": "COMMANDS"
45
+ },
46
+ "topicSeparator": " "
47
+ },
48
+ "license": "UNLICENSED",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "git+https://github.com/mail-dock/mailserver.git",
52
+ "directory": "apps/cli"
53
+ },
54
+ "engines": {
55
+ "node": ">=24"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public",
59
+ "registry": "https://registry.npmjs.org"
60
+ }
61
+ }