RepoPilotOpen in app →

foxcpp/maddy

✉️ Composable all-in-one mail server.

Mixed

Mixed signals — read the receipts

weakest axis
Use as dependencyConcerns

copyleft license (GPL-3.0) — review compatibility

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1w ago
  • 17 active contributors
  • GPL-3.0 licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Concentrated ownership — top contributor handles 75% of recent commits
  • GPL-3.0 is copyleft — check downstream compatibility
What would change the summary?
  • Use as dependency ConcernsMixed if: relicense under MIT/Apache-2.0 (rare for established libs)

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/foxcpp/maddy?axis=fork)](https://repopilot.app/r/foxcpp/maddy)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/foxcpp/maddy on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: foxcpp/maddy

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/foxcpp/maddy shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Mixed signals — read the receipts

  • Last commit 1w ago
  • 17 active contributors
  • GPL-3.0 licensed
  • CI configured
  • Tests present
  • ⚠ Concentrated ownership — top contributor handles 75% of recent commits
  • ⚠ GPL-3.0 is copyleft — check downstream compatibility

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live foxcpp/maddy repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/foxcpp/maddy.

What it runs against: a local clone of foxcpp/maddy — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in foxcpp/maddy | Confirms the artifact applies here, not a fork | | 2 | License is still GPL-3.0 | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 37 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>foxcpp/maddy</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of foxcpp/maddy. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/foxcpp/maddy.git
#   cd maddy
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of foxcpp/maddy and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "foxcpp/maddy(\\.git)?\\b" \\
  && ok "origin remote is foxcpp/maddy" \\
  || miss "origin remote is not foxcpp/maddy (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(GPL-3\\.0)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"GPL-3\\.0\"" package.json 2>/dev/null) \\
  && ok "license is GPL-3.0" \\
  || miss "license drift — was GPL-3.0 at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "cmd/maddy/main.go" \\
  && ok "cmd/maddy/main.go" \\
  || miss "missing critical file: cmd/maddy/main.go"
test -f "config.go" \\
  && ok "config.go" \\
  || miss "missing critical file: config.go"
test -f "directories.go" \\
  && ok "directories.go" \\
  || miss "missing critical file: directories.go"
test -f ".github/CONTRIBUTING.md" \\
  && ok ".github/CONTRIBUTING.md" \\
  || miss "missing critical file: .github/CONTRIBUTING.md"
test -f "HACKING.md" \\
  && ok "HACKING.md" \\
  || miss "missing critical file: HACKING.md"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 37 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~7d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/foxcpp/maddy"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Maddy is a composable all-in-one mail server written in Go that replaces Postfix, Dovecot, OpenDKIM, and related services in a single daemon. It implements SMTP (MTA/MX), IMAP storage, and email security protocols (DKIM, SPF, DMARC, DANE, MTA-STS) with uniform configuration and minimal maintenance overhead. Monolithic daemon structure: cmd/maddy/main.go is entry point; config.go handles configuration; helper utilities in cmd/maddy-pam-helper and cmd/maddy-shadow-helper for authentication. Protocol implementations (SMTP, IMAP, DKIM, etc.) are likely modular packages under root. Kubernetes deployment templates in contrib/kubernetes/chart/ show modern deployment patterns.

👥Who it's for

System administrators and DevOps engineers who need to deploy complete email infrastructure without juggling multiple daemons; open-source contributors interested in mail server implementation; organizations seeking to consolidate email stack into a single Go binary for easier deployment and configuration management.

🌱Maturity & risk

Actively developed and production-ready for SMTP/MX/delivery workflows (CI passing, Go 1.23.5, Kubernetes Helm charts in contrib/kubernetes/). IMAP storage is explicitly marked 'beta' in the README, so not recommended for that component yet. Regular releases via .github/workflows/release.yml indicate active maintenance.

Single maintainer (foxcpp) increases bus-factor risk. Dependency footprint is substantial (40+ external packages from emersion, libdns providers, databases) which increases supply-chain surface. Beta IMAP storage component may not be production-safe. Last commit recency and issue backlog not visible in provided data, so check GitHub directly.

Active areas of work

Recent toolchain bump to Go 1.23.5 visible in go.mod. Active work on IMAP backend (go-imap-sql dependency is pinned with recent git commit hashes). DNS provider integrations (libdns/* packages) suggest ongoing feature expansion. Release workflows (.github/workflows/release.yml) indicate regular version bumps.

🚀Get running

git clone https://github.com/foxcpp/maddy.git && cd maddy && go mod download && go build -o maddy ./cmd/maddy. See build.sh for build options. Requires Go 1.23.1+. For Docker: docker build -f Dockerfile .

Daily commands: ./build.sh to compile; maddy requires config file (see contrib/kubernetes/chart/files/maddy.conf for example). Manual: maddy -config /etc/maddy/maddy.conf. Docker: docker run -v /path/to/maddy.conf:/etc/maddy/maddy.conf foxcpp/maddy. Development: go run ./cmd/maddy -config maddy.conf.

🗺️Map of the codebase

  • cmd/maddy/main.go — Entry point for the mail server daemon; initializes config loading and starts all subsystems
  • config.go — Core configuration parser and module registry; all feature modules must register themselves here
  • directories.go — Defines storage backend abstractions (IMAP, blob storage); critical for understanding how mail is persisted
  • .github/CONTRIBUTING.md — Development guidelines and code standards that all contributors must follow
  • HACKING.md — Architecture overview and internal design patterns for extending the codebase
  • build.sh — Build and packaging script; shows how to compile maddy and its helper utilities

🧩Components & responsibilities

  • SMTP Server (emersion/go-smtp, TLS, STARTTLS) — Accepts messages from clients and remote MTAs; enforces RFC 5321; handles authentication and TLS
    • Failure mode: Rejected messages, delivery delays, or bounces returned to sender
  • Check Pipeline — Validates message authenticity and origin via DKIM

🛠️How to make changes

Add a New Authentication Backend

  1. Study existing auth implementations in docs/reference/auth/ (e.g., pass_table.md, pam.md) to understand the interface (docs/reference/auth/pass_table.md)
  2. Create a new module that implements the authenication interface and register it in config.go under the 'auth' module namespace (config.go)
  3. Add configuration documentation in docs/reference/auth/yourbackend.md with examples (docs/reference/auth)
  4. If it requires privilege separation, add a helper binary in cmd/maddy-yourbackend/ following the pattern of maddy-pam-helper (cmd/maddy-pam-helper/main.go)

Add a New Storage Backend (Directory or Blob)

  1. Review the abstract interfaces defined in directories.go to understand Directory, IMAPBackend, or BlobStore requirements (directories.go)
  2. Implement your backend and register it in config.go under the 'blob' or 'directory' module section (config.go)
  3. Document configuration and usage in docs/reference/blob/yourbackend.md (following the pattern of fs.md or s3.md) (docs/reference/blob/fs.md)

Add a New Mail Check Module (Security Filter)

  1. Study existing check modules in docs/reference/checks/ (e.g., dkim.md, authorize_sender.md) to understand the check interface (docs/reference/checks/dkim.md)
  2. Create your check module and register it in config.go under the 'checks' namespace (config.go)
  3. Document configuration, actions, and decision logic in docs/reference/checks/yourcheck.md (docs/reference/checks/actions.md)

🔧Why these technologies

  • Go 1.23+ — Single compiled binary, excellent concurrency primitives (goroutines), standard library covers most mail protocols natively
  • Composable module architecture — Allows users to pick auth backends (PAM, LDAP, Dovecot, shadow, plain), storage backends (FS, S3), and check modules (DKIM, SPF, DMARC) without recompilation
  • SQLite (implied by docs/internals/sqlite.md) — Lightweight embedded database for user accounts, aliases, and routing metadata without external dependencies
  • TLS/STARTTLS, DANE, MTA-STS — Modern email security standards; replaces external tools like OpenDKIM
  • Docker & Kubernetes support — Cloud-native deployment; Helm chart in contrib/kubernetes reduces operational overhead

⚖️Trade-offs already made

  • IMAP storage marked as beta, not production-ready

    • Why: Complexity of implementing full IMAP with all edge cases; developers prioritized message routing (MTA/MX) stability first
    • Consequence: Users needing stable IMAP must still use Dovecot; maddy excels at mail delivery
  • Monolithic Go binary instead of separate microservices

    • Why: Simpler deployment, lower memory footprint, fewer failure points, easier configuration management
    • Consequence: Scaling individual components (e.g., auth vs. SMTP) requires running multiple maddy instances
  • Helper binaries (maddy-pam-helper, maddy-shadow-helper) written in C/Go

    • Why: PAM requires C bindings; privilege separation avoids running main daemon as root
    • Consequence: Adds compilation complexity and platform-specific dependencies for auth backends
  • Configuration via single config file (not distributed config store)

    • Why: Matches traditional mail server expectations (Postfix, Dovecot); easier local management
    • Consequence: Multi-instance deployments must synchronize config externally; not suited for dynamic service discovery

🚫Non-goals (don't propose these)

  • Real-time message sync (IMAP is beta/incomplete)
  • Webmail UI (mail retrieval only via IMAP/POP3)
  • Per-user rate limiting and quota (implied by configuration scope)
  • Microsoft Exchange compatibility (MAPI, EWS)
  • Clustering or distributed consensus (single-instance or manual replication)

🪤Traps & gotchas

PAM/Shadow authentication requires cgo (CGO_ENABLED=1) and linking against C libraries (cmd/maddy-pam-helper/pam.c). Mail storage requires SQL backend configuration; no in-memory fallback. TLS setup via certmagic may require ACME account setup and DNS delegation. Configuration is strict — typos in maddy.conf will fail daemon startup. Helper binaries (maddy-pam-helper, maddy-shadow-helper) must be compiled separately and available on PATH for auth to work.

🏗️Architecture

💡Concepts to learn

  • emersion/go-smtp — Core SMTP protocol implementation that maddy wraps; required dependency for MTA functionality
  • emersion/go-imap — Core IMAP protocol implementation and base for maddy's mail storage backend
  • foxcpp/go-imap-sql — SQL-backed IMAP storage driver specifically built for maddy to store messages in PostgreSQL/MySQL/SQLite
  • caddyserver/certmagic — TLS certificate lifecycle and ACME automation used by maddy for Let's Encrypt integration
  • mailcow/mailcow-dockerized — Alternative all-in-one mail stack (Docker Compose); shows competing approach to unified mail deployment

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add integration tests for Kubernetes Helm chart deployment

The repo has a Kubernetes Helm chart in contrib/kubernetes/chart/ but no CI workflow tests its validity. The chart templates reference maddy.conf and aliases files, but there's no automated verification that the chart renders correctly, values validate properly, or the generated manifests are syntactically valid. This is critical for users deploying maddy to Kubernetes.

  • [ ] Create .github/workflows/helm-test.yml to run 'helm lint' and 'helm template' on contrib/kubernetes/chart/
  • [ ] Add chart template validation using helm kubeval plugin in the workflow
  • [ ] Test that ConfigMap generation from contrib/kubernetes/chart/files/ works correctly
  • [ ] Verify ServiceAccount and RBAC templates are valid Kubernetes manifests
  • [ ] Add workflow trigger on changes to contrib/kubernetes/chart/** paths

Add unit tests for config.go and directories.go module loading

The config.go and directories.go files are critical core modules that handle configuration parsing and directory initialization, but there's no evidence of dedicated unit tests in the current test.yml workflow. The directories_docker.go variant suggests platform-specific behavior that needs testing coverage.

  • [ ] Create modules/config_test.go with tests for config parser edge cases (malformed config, missing required fields, variable expansion)
  • [ ] Create modules/directories_test.go with tests for both standard and Docker directory resolution paths
  • [ ] Test that config.go properly loads and validates maddy.conf structure across different deployment scenarios
  • [ ] Add tests for directories_docker.go vs standard directories.go to ensure conditional logic works
  • [ ] Update .github/workflows/test.yml to verify config tests are run with -cover flag

Add CI workflow to validate systemd and AppArmor distribution files

The repo includes production-critical distribution files (dist/systemd/maddy.service, dist/systemd/maddy@.service, and dist/apparmor/dev.foxcpp.maddy) with no validation in CI. Service file syntax errors or AppArmor policy issues would only be caught during deployment.

  • [ ] Create .github/workflows/dist-validate.yml with systemd-analyze validate for dist/systemd/*.service files
  • [ ] Add AppArmor policy syntax validation using 'aa-syntaxcheck' (or equivalent parser) for dist/apparmor/
  • [ ] Validate logrotate configuration syntax in dist/logrotate.d/maddy
  • [ ] Verify fail2ban filter and jail configurations in dist/fail2ban/ are valid
  • [ ] Add workflow trigger on changes to dist/** paths and document in HACKING.md

🌿Good first issues

  • Add integration tests for the PostgreSQL backend in cmd/maddy using testcontainers; cmd/maddy lacks explicit DB integration test coverage compared to IMAP backend tests.
  • Document all configuration directives in markdown; contrib/kubernetes/chart/files/maddy.conf exists but no auto-generated schema docs. Generate from config.go struct tags.
  • Add example maddy.conf files for common setups (small org, large deployment, Kubernetes) to contrib/examples/; only Kubernetes example exists currently.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 837b1b8 — maddy 0.9.4 (foxcpp)
  • 6885f85 — Fix formatting issues, linter warnings and test errors after logger refactor (foxcpp)
  • b5bd761 — cli: Remove old-style maddyctl compatibility (pre-0.6 binary split) (foxcpp)
  • 5841e95 — log: Refactor to define proper loggers tree (foxcpp)
  • 88e41ff — Merge pull request #840 from luza/master (foxcpp)
  • b418bd9 — Fixed limiters group configuration (luza)
  • 08a80ba — Merge pull request #839 from osnabrugge/feat/smtp-login-auth (foxcpp)
  • fb2eb37 — smtp: add LOGIN SASL auth directive (osnabrugge)
  • d74f511 — storage/imapsql: Upgrade go-imap-sql to disable Repeatable Read by default (foxcpp)
  • 9db7e61 — storage/imapsql: Fix handling of serialization errors (foxcpp)

🔒Security observations

  • Medium · Outdated Alpine Base Image — Dockerfile (line 17). The Dockerfile uses Alpine 3.21.2 as the base image. While relatively recent, this should be regularly updated to include the latest security patches. Alpine images can contain unpatched vulnerabilities if not kept current. Fix: Implement a process to regularly rebuild Docker images with the latest Alpine base image. Consider using automated dependency scanning tools like Dependabot or Snyk to notify about base image updates.
  • Medium · Go 1.23.5 Toolchain May Contain Vulnerabilities — go.mod (toolchain go1.23.5). The project specifies Go 1.23.5, which should be verified against the Go security advisory list. Go runtime vulnerabilities could affect the entire application. Fix: Subscribe to Go security advisories at golang.org/issue and regularly update to patched versions. Use 'go list -json -m all | nancy sleuth' or similar tools to check for known vulnerabilities.
  • Medium · Potential Transitive Dependency Vulnerabilities — go.mod - Multiple dependencies. The go.mod file shows many third-party dependencies (LDAP, DNS, database drivers, email protocols). While the listed versions appear reasonable, the incomplete go.sum file in the analysis and transitive dependencies could contain unpatched vulnerabilities not visible in the direct dependencies. Fix: Run 'go mod graph' to identify transitive dependencies. Use 'govulncheck ./...' (Go 1.21+) to scan for known vulnerabilities. Keep dependencies updated regularly with 'go get -u'.
  • Medium · Multiple Database Drivers with Variable Support Levels — go.mod - github.com/lib/pq, github.com/go-sql-driver/mysql. The project includes multiple database drivers (PostgreSQL via lib/pq, MySQL via go-sql-driver, and custom SQL backend). Different drivers may have different security update cadences and quality levels. Fix: Regularly audit and update database drivers. Test security updates thoroughly before deployment. Consider standardizing on fewer database backends if possible.
  • Medium · Docker Container Runs as Root — Dockerfile (no USER directive). The Dockerfile does not specify a non-root USER directive. The entrypoint runs as root, which increases the impact of any security vulnerability. Fix: Add 'RUN adduser -D -u 1000 maddy' and 'USER maddy' before the ENTRYPOINT to run the container with minimal privileges. Adjust file permissions accordingly.
  • Medium · Exposed Sensitive Ports — Dockerfile (EXPOSE and VOLUME directives). The Dockerfile exposes multiple mail service ports (25, 143, 993, 587, 465) and a generic /data volume. While necessary for mail server functionality, this significantly increases the attack surface. Fix: Use firewall rules and network policies (e.g., Kubernetes NetworkPolicies) to restrict access to mail ports. Document port-to-service mappings clearly. Implement rate limiting and authentication on all exposed services.
  • Low · Build Script Execution Without Integrity Verification — Dockerfile (line 26: ./build.sh). The Dockerfile runs './build.sh' without verifying its integrity. If the build script is compromised or contains vulnerabilities, it could lead to supply chain attacks. Fix: Implement build script signing with git commit signing. Document build script changes in HACKING.md. Consider using BuildKit secrets for sensitive build parameters.
  • Low · No Security Headers or TLS Configuration Guidance in Docker — Dockerfile and maddy.conf.docker reference. The Dockerfile and entrypoint don't document or enforce TLS/SSL configuration for mail protocols, which is critical for email security. Fix: Ensure maddy.conf.docker includes TLS configuration directives. Document required TLS setup in the Kubernetes chart and Docker documentation.
  • Low · Incomplete Dependency Information in Analysis — go.mod (content appears cut off). The provided go.mod content is truncated, making it impossible to perform complete vulnerability analysis on all dependencies. Fix: Maintain complete go.mod and go

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · foxcpp/maddy — RepoPilot