Commit Graph
10 Commits
Author SHA1 Message Date
Lucas RayanandClaude Opus 5 33d91fa247 #19: fix F1-F3 defects found auditing the phases against PLAN.md
Every fix here is a case where the code and the plan disagreed, and the
code was wrong.

Certificates (section 8.2, 8.4). The parser documented that it skipped
expired entries but never checked, so an expired certificate would be
served until every handshake failed. LastReload() only advanced when
acme.json changed - and it does not change for sixty days - which made
the timestamp risk R2 monitors indistinguishable from a watcher gone
blind. Expiry is now checked on every pass, LastReload records the check
and LastChange the swap, and the <7d/<24h alerts of section 8.4 exist.

ARC (section 5). buildArcSealer resolved the DKIM key at startup, before
runDnsReconciler had provisioned one. On a first boot ARC was disabled
until someone restarted the process, which the log even admitted while
offering no way for it to start. A LazySealer binds on first use instead.

Health (section 8.4). There was no degraded state at all. /health now
reports one - still 200, because restarting cannot fix a certificate
Traefik never issued.

Bounces. DSNs were sent From postmaster@lambdamail.local, a domain that
does not resolve, so the notification was itself undeliverable. They now
carry the configured mail host, a Message-ID, and Auto-Submitted so a
vacation responder cannot loop against them.

Fan-out atomicity (section 6.1). Persist ran one transaction per
recipient, so a failure halfway through an alias fan-out left earlier
recipients with a copy while the sender was told to retry - and the retry
delivered to them twice. PersistAll commits the whole delivery or none of
it. Verified against Postgres by reverting the repository to the old
behaviour and confirming the new test fails on it.

SMTP replies. The use case encoded replies in error text ("550 5.7.1 ...")
which the adapter re-parsed by prefix, so rewording a message could turn a
permanent refusal into a retry. SmtpRejection carries the decision and the
adapter renders it.

Repository tests. These only run with TEST_DATABASE_URL, which CI does
set - so the job was red: copy and expunge seeded a 68-character digest
into a CHAR(64) column, and three others used fixed digests that collide
with the UNIQUE index on any re-run. A shared testBlobDigest() helper
fixes both; the suite now passes twice in a row against one database.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 07:57:22 -03:00
Lucas Rayan d56f29d084 #18: implement SMTP Submission (587/465), SASL PLAIN/LOGIN auth and preflight diagnostics 2026-08-01 22:49:02 -03:00
Lucas Rayan b96be4a2a5 #16: implement DKIM key provisioning, Ed25519 & RSA dual signatures and rotation 2026-08-01 22:48:15 -03:00
Lucas Rayan 6ef427dc09 #15: implement ACME certificate store, Traefik/Coolify cert watcher & secrets vault 2026-08-01 22:47:53 -03:00
Lucas Rayan af35211094 #13: implement Outbound Queue, Retry Backoff Engine & DSN Bounce Generator (RFC 3464) 2026-07-25 10:33:07 -03:00
Lucas Rayan 54d1ea5810 #12: implement Antispam & Antivirus scanning pipeline (Rspamd & ClamAV) 2026-07-25 10:25:52 -03:00
Lucas Rayan 314eb67dda #11: implement MTA-STS, TLS-RPT & Autoconfig HTTP services 2026-07-25 10:18:03 -03:00
Lucas Rayan 10836ea98f #10: implement Cloudflare DNS automation engine (13 records & system aliases) 2026-07-25 05:26:14 -03:00
Lucas Rayan 0e6fc27bc1 #8: implement ManageSieve protocol server (RFC 5804) 2026-07-25 04:20:21 -03:00
Lucas Rayan eb09fa0ced #1: implement F0 foundation - monorepo, migrations, Go domain layer, service skeletons, CI
Stands up the LambdaMail monorepo per PLAN.md's F0 milestone: local dev
scaffolding (.env.example, Makefile, .gitignore excluding PLAN.md/docs/
.superpowers/), the full initial Postgres schema as a golang-migrate
migration, the complete Go domain layer for services/protocols (value
objects, entities, domain events - zero external imports beyond
github.com/google/uuid) with unit tests, health-checked service skeletons
for auth/storage (TypeScript) and webmail (Next.js), a local
docker-compose.yaml wiring all services together, a language-enforcement
lint script, and a GitHub Actions CI workflow.

Built via 11 sequential tasks under subagent-driven development, each with
an independent implementer + reviewer cycle. Fixes applied during review:
DomainName.IsPunycode() added to satisfy the promised interface; ioredis
default-import typing under NodeNext; next.js bumped 15.0.3->15.5.21 to
resolve high/critical CVEs; lang-lint.mjs corrected to not flag its own
banned-terms list; web service healthcheck added so compose --wait
actually waits on it; webmail Dockerfile's missing public/ directory:
protocols go.mod version aligned with CI's Go 1.22 pin.

Known follow-ups for later phases (not blocking F0): go-arch-lint isn't
wired into CI yet; live docker compose up and migration apply have not
been exercised in this dev environment (no Docker/psql available here)
and will get their first real run in CI, which does have Docker.
2026-07-23 05:05:46 -03:00