Commit Graph
6 Commits
Author SHA1 Message Date
Lucas Rayan f330c4324e Add end-to-end tests for TLS RPT report ingestion and WebSocket event handling
- Implemented tests for ingesting TLS RPT reports from emails and ensuring they are stored correctly in the database.
- Added tests to verify that the same report can be ingested multiple times without duplication.
- Created tests for backfilling reports that were previously delivered to the inbox.
- Developed WebSocket event handling tests to ensure events are correctly routed to subscribers based on mailbox ownership.
- Included tests for the mail API to validate session handling, mailbox isolation, and error mapping.
- Ensured that the API rejects unauthorized requests and handles malformed JSON gracefully.
2026-08-21 09:55:37 -03:00
Lucas Rayan 880dced9e4 feat: implement message deletion functionality and improve draft handling
- Added `MoveToTrash` method in `WebmailRepository` to move messages to the Trash folder instead of deleting them permanently.
- Introduced `handleDelete` method in `mailAPI` to handle message deletion requests from the client.
- Updated router to include a new route for message deletion.
- Enhanced the compose page to manage draft integrity, ensuring drafts are correctly associated with sent messages.
- Implemented UI changes to allow users to delete messages from the mail folder view.
- Added tests to verify the correct behavior of message deletion and draft handling.
- Updated database migration scripts to reconcile folder counters and ensure accurate unread counts.
- Added tests for TLS-RPT report parsing and webmail repository functionalities.
2026-08-20 15:32:14 -03:00
Lucas RayanandClaude Opus 5 c0387f6961 #21: make the stack deployable, and document how
Three things stood between this and a real deployment.

A missing JWT_SECRET fell back to a constant that is published in this
repository, so any deployment that forgot the variable would have accepted
session tokens anyone could mint, including an admin one. The auth service
now refuses to start without a secret of at least 32 characters. The test
suites set it through vitest config, because ESM hoists imports and an
assignment inside a test file runs too late.

Session cookies were issued without the Secure attribute: it is driven by
NODE_ENV, which compose never set. Behind a TLS terminator that is a
downgrade waiting to happen.

Nothing created the first mailbox. A fresh install has no domain and no
account, and every route into the console needs one - so there was no
first login. There is now a create-admin command, which also provisions
the standard folders and the four system aliases. It is a deliberate
command rather than a bootstrap on boot: creating an administrator as a
side effect of starting a container is how a deleted account quietly comes
back.

Also: the production images were shipping compiled test files, since the
build compiled everything under src. A separate build config excludes
them while lint still type-checks them.

Documentation. README.md covers what this is, how it is put together, and
what is not finished - stated plainly, because a mail server that half
works loses mail. docs/DEPLOYMENT.md is the Coolify path in the order it
has to happen, starting with the three prerequisites no code can satisfy:
outbound port 25, the PTR record, and the DNSSEC delegation.
docs/OPERATIONS.md is the runbook - health states, certificates, backups,
reading a delivery failure, DKIM rotation, and an explicit list of what is
not automated. .env.production.example replaces guessing which of the dev
defaults are safe: none of them are.

docs/ was gitignored as a place for internal notes; only docs/internal/ is
now, so these guides ship with the code.

Every command the documentation tells an operator to run was executed
against a running stack: create-admin, preflight, the health endpoint, the
published mail ports, and the Secure cookie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 13:33:51 -03:00
Lucas RayanandClaude Opus 5 da1db03880 #21: run the tests that prove the auth fixes, in CI
ts-auth gains a Postgres service and migrations. The security regressions
this branch adds - wrong password, pass-the-hash, wrong TOTP code, lockout,
single-use recovery codes - are all decided in the database, so without one
they skip silently and CI would go green on an unauthenticated server.

webmail-build only ran "npm run build", so i18n.test.ts never executed
anywhere. It now lints and tests too, and the package gained the lint
script it was missing.

*.tsbuildinfo is ignored: tsc --noEmit leaves it behind and it is a local
build cache, not source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 09:34:04 -03:00
Lucas Rayan e501cdc69e #2: implement F1 sub-project 1 - inbound SMTP listener with EML storage
Adds a real :25 SMTP listener to the Go protocols service, giving F1's
"Nucleo SMTP" milestone its first working sub-project: application,
infrastructure, and presentation layers on top of F0's domain-only
Clean Architecture.

- ProcessInboundEmailUseCase resolves recipients against Postgres and
  persists accepted mail (blob dedup by content hash, transactional
  UID allocation + outbox event, never sending 250 OK before durable
  commit per PLAN.md section 6.1).
- Postgres repositories via pgx/v5 (replacing lib/pq entirely).
- Local-disk blob storage with content-hash dedup, rename-before-commit
  ordering so a DB row can never reference a file that isn't actually
  on disk.
- A local-file TLS cert provider for STARTTLS, with an ephemeral
  self-signed fallback so a missing/misconfigured cert degrades
  gracefully instead of crash-looping the service (PLAN.md section 8.4).
- A github.com/emersion/go-smtp Backend/Session adapting real SMTP
  traffic to the use case: unknown recipients get 550 5.1.1 before
  DATA, duplicate RCPT TO for the same address is deduplicated, TLS is
  hardened to the project's required PFS-only cipher/curve policy.
- A dev-seed script and Makefile target for local testing.

Explicitly out of scope (separate follow-up sub-projects): alias
resolution, SPF/DKIM/DMARC/ARC verification, Rspamd/ClamAV, Submission
ports with SASL auth, DKIM signing, outbound delivery, and the real
Traefik acme.json cert watcher.

Verified live end-to-end via WSL Docker: the full compose stack (db,
redis, migrate, auth, storage, protocols, web) reaches healthy, and a
real swaks SMTP session is accepted and durably persisted with correct
sequential UIDs, while an unknown recipient is correctly rejected with
550 5.1.1 and zero rows persisted. Built via 10 sequential tasks under
subagent-driven development, each independently reviewed, plus a final
whole-branch review that caught and fixed a TLS-crash bug, a blob
commit-ordering bug, missing TLS hardening, and missing CI integration
coverage before merge.

Known follow-up, not blocking: email_messages' partition only covers
July 2026 and needs a partition-management job before 2026-08-01.
2026-07-23 08:41:31 -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