Thanks for your interest! mailtrap-local is a single Go binary that embeds a
React SPA — an SMTP catcher + web sandbox + JSON API for local development and
CI. This guide covers how to get set up and what we look for in a change.
- Go 1.26+
- Node 22+
- Optional:
foreman(forbin/dev), andmise/asdfto install the toolchain from.tool-versions.
bin/setup # go mod download + npm install
bin/dev # boots backend + Vite concurrently (Procfile.dev)In dev:
| Port | Served by | Purpose |
|---|---|---|
3535 |
go run ./cmd/... |
SMTP ingest |
3540 |
Vite dev | SPA (HMR) — proxies /api + /cable |
3550 |
Go binary | JSON API + WebSocket |
Open http://127.0.0.1:3540 for the dev UI. The production single binary serves
the same UI at http://127.0.0.1:3550 from the embedded dist/.
To build the real single binary (frontend + Go embed):
scripts/build.sh # → bin/mailtrap-localEverything must pass before a PR is merged — CI runs all of this.
# Go: lint + every package, race detector on (matches CI)
golangci-lint run ./cmd/... ./internal/...
go test -race ./cmd/... ./internal/...
# Frontend (from frontend/)
npm run build # tsc -b && vite build — catches type + import regressions
npm run lint
npm test # vitest (jsdom + @testing-library)The OpenAPI drift test (internal/api/openapi_drift_test.go) fails if a chi
route is added/removed without updating docs/api/openapi.yaml. If you change
routes, update both docs/api/openapi.yaml and the embedded copy at
cmd/mailtrap-local/openapi.yaml.
- Go:
golangci-lint(see.golangci.yml) is enforced in CI. Keep handlers thin; theinternal/packages are split by concern (api,smtpd,store,jobs,relay,webhook,cloud,live,secrets,config). Prefererrors.Is/errors.Asover string matching. - Frontend: TypeScript + React 19 + Tailwind v4. See
.claude/skills/frontend-code-style/SKILL.mdfor the component/styling conventions used here (primitives + shared constants, design tokens via@theme, adjust-state-during-render oversetState-in-effect). - Comments should explain why, not what. Match the surrounding style — this codebase documents trade-offs and past bugs deliberately; keep that up.
- Branch off
main. - Keep PRs focused — one concern per PR. Unrelated cleanups belong in their own PR.
- Add or update tests for behavior changes. Bug fixes should come with a regression test.
- Make sure
golangci-lint, the full test suite (Go + frontend), andgo vetpass locally. - Write a clear description: what changed, why, and how you verified it.
Please do not file security issues as public issues — see SECURITY.md for private reporting.
By contributing, you agree that your contributions are licensed under the project's MIT License.