Some checks failed
CI / skinny-install (aco) (push) Successful in 1m12s
CI / skinny-install (api) (push) Successful in 30s
CI / skinny-install (bcda) (push) Successful in 36s
CI / skinny-install (bib) (push) Successful in 35s
CI / skinny-install (bls) (push) Successful in 27s
CI / skinny-install (ccw) (push) Successful in 32s
CI / skinny-install (cli) (push) Successful in 41s
CI / skinny-install (cms) (push) Successful in 37s
CI / skinny-install (conf) (push) Successful in 38s
CI / skinny-install (opps) (push) Successful in 33s
CI / skinny-install (perf) (push) Successful in 38s
CI / skinny-install (pfs) (push) Successful in 38s
CI / skinny-install (rex) (push) Successful in 34s
Deploy / build-scan-report (push) Failing after 46s
Infra CI / notebooks (push) Failing after 25s
Infra CI / zotero (push) Successful in 12s
Infra CI / docs (push) Failing after 16s
CI / lint-test (push) Failing after 11m2s
Infra CI / mc (push) Successful in 21s
Infra CI / api (push) Successful in 29s
Package Supply Chain / pkg-supply-chain (push) Failing after 41s
Mail: Maddy on DO (corwins.media+Resend, fhirworx.io+Postmark), touchless/stateless/idempotent. Gitea SMTP via env_file. CMS inbox at cmsupdates@mail.fhirworx.io with IMAP→bib poller. Bib: regulations.gov v4 client, Federal Register discovery, 164K comment backfill (running), IMAP email ingest, Zotero sync routing. PRISMA: altcha PoW solver, CrossRef DOI resolution, 83/129 PDFs. Zotero: schema parity, ops module, CLI, fail-fast guard. CI: docs.Dockerfile COPY glob fix (tracks #341). Infra: Gitea+marimo fhirworx themes, IOM/OIG modules.
148 lines
4.3 KiB
Smarty
148 lines
4.3 KiB
Smarty
# Maddy mail server config — minimal single-domain setup with DKIM
|
|
# autosigning. Tracks the shape of the upstream reference config so
|
|
# upgrades track cleanly.
|
|
#
|
|
# Placeholders (substituted by mail-setup.sh via envsubst):
|
|
# $HOSTNAME -> mail.corwins.media
|
|
# $PRIMARY_DOMAIN -> corwins.media
|
|
|
|
hostname $HOSTNAME
|
|
$(primary_domain) = $PRIMARY_DOMAIN
|
|
# Accept mail at both the apex (e.g. git@fhirworx.io for outbound
|
|
# SMTP submission and DKIM signing) and the host subdomain (e.g.
|
|
# cmsupdates@mail.fhirworx.io, which is what actually MX-routes to
|
|
# this droplet when another provider holds the apex MX).
|
|
$(local_domains) = $(primary_domain) $HOSTNAME
|
|
|
|
tls file /data/tls/fullchain.pem /data/tls/privkey.pem
|
|
|
|
|
|
# ── Authentication / storage ─────────────────────────────────────
|
|
#
|
|
# Accounts live in credentials.db; IMAP mailboxes live in imapsql.db.
|
|
# Both are SQLite files under /data and survive container restarts.
|
|
|
|
auth.pass_table local_authdb {
|
|
table sql_table {
|
|
driver sqlite3
|
|
dsn credentials.db
|
|
table_name passwords
|
|
}
|
|
}
|
|
|
|
storage.imapsql local_mailboxes {
|
|
driver sqlite3
|
|
dsn imapsql.db
|
|
}
|
|
|
|
|
|
# ── SMTP receive (port 25, from the world) ───────────────────────
|
|
#
|
|
# Plain SMTP inbound, STARTTLS opportunistically, no auth (inbound
|
|
# MTAs shouldn't authenticate). DMARC + DKIM + SPF checks gate the
|
|
# inbound stream; anything forging our domains is rejected.
|
|
|
|
smtp tcp://0.0.0.0:25 {
|
|
limits {
|
|
all rate 20 1s
|
|
all concurrency 10
|
|
}
|
|
dmarc yes
|
|
check {
|
|
require_mx_record
|
|
dkim
|
|
spf
|
|
}
|
|
source $(local_domains) {
|
|
reject 501 5.1.8 "Use Submission (port 587) to send as us"
|
|
}
|
|
default_source {
|
|
destination postmaster $(local_domains) {
|
|
deliver_to &local_routing
|
|
}
|
|
default_destination {
|
|
reject 550 5.1.1 "User not found"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# ── SMTP submission (ports 465 TLS, 587 STARTTLS, authenticated) ──
|
|
#
|
|
# Our own outbound send path. Clients (Gitea, Thunderbird, mail-merge
|
|
# scripts) authenticate against local_authdb, and outbound mail is
|
|
# DKIM-signed before hitting the remote queue.
|
|
|
|
submission tls://0.0.0.0:465 tcp://0.0.0.0:587 {
|
|
limits {
|
|
all rate 50 1s
|
|
}
|
|
auth &local_authdb
|
|
source $(local_domains) {
|
|
check {
|
|
authorize_sender {
|
|
prepare_email identity
|
|
user_to_email identity
|
|
}
|
|
}
|
|
destination postmaster $(local_domains) {
|
|
deliver_to &local_routing
|
|
}
|
|
default_destination {
|
|
modify {
|
|
dkim $(primary_domain) default
|
|
}
|
|
deliver_to &remote_queue
|
|
}
|
|
}
|
|
default_source {
|
|
reject 501 5.1.8 "Unknown sender domain"
|
|
}
|
|
}
|
|
|
|
|
|
# Outbound goes through an external SMTP relay (smarthost), not
|
|
# direct MX — DigitalOcean silently blocks outbound port 25 on new
|
|
# droplets. The real `target.smtp outbound_smarthost { … }` block is
|
|
# injected at the top of this file by `stack mail attach-smarthost`.
|
|
# Until then, the stub below lets maddy start cleanly so inbound,
|
|
# IMAP, and DKIM key generation all work — outbound just queues
|
|
# locally and never delivers (intentional).
|
|
target.smtp outbound_smarthost {
|
|
targets tcp://127.0.0.1:9
|
|
auth plain "stub" "stub"
|
|
}
|
|
|
|
target.queue remote_queue {
|
|
target &outbound_smarthost
|
|
autogenerated_msg_domain $(primary_domain)
|
|
bounce {
|
|
destination postmaster $(local_domains) {
|
|
deliver_to &local_routing
|
|
}
|
|
default_destination {
|
|
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# ── IMAP (port 993 TLS, 143 STARTTLS) ────────────────────────────
|
|
|
|
imap tls://0.0.0.0:993 tcp://0.0.0.0:143 {
|
|
auth &local_authdb
|
|
storage &local_mailboxes
|
|
}
|
|
|
|
|
|
# ── Local delivery pipeline ──────────────────────────────────────
|
|
|
|
msgpipeline local_routing {
|
|
destination postmaster $(local_domains) {
|
|
deliver_to &local_mailboxes
|
|
}
|
|
default_destination {
|
|
reject 550 5.1.1 "User doesn't exist"
|
|
}
|
|
}
|