Skip to content

Commit 8e834ef

Browse files
committed
Add AGENTS.md for LLM coding agents
Some people use these. File itself is generated using Opus 4.6 and hand-edited later. Possibly incomplete and could be merged with HACKING.md.
1 parent a9a0ef5 commit 8e834ef

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

AGENTS.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# AGENTS.md — Maddy Mail Server
2+
3+
## Architecture
4+
5+
Maddy is a composable all-in-one mail server (MTA/MX/IMAP) written in Go. The core abstraction is the **module system**: every functional component (auth, storage, checks, targets, endpoints) implements `module.Module` from `framework/module/module.go` and registers itself via `module.Register(name, factory)` in an `init()` function.
6+
7+
- **`framework/`** — Stable, reusable packages (config parsing, module interfaces, address handling, error types, logging). Interfaces live here to avoid circular imports.
8+
- **`internal/`** — All module implementations. Subdirectories map to module roles: `endpoint/` (protocol listeners), `target/` (delivery destinations), `auth/`, `check/` (message inspectors), `modify/` (header modifiers), `storage/`, `table/` (string→string lookups).
9+
- **`maddy.go`** — Side-effect imports that pull all `internal/` modules into the binary, plus the `Run`/`moduleConfigure`/`RegisterModules` startup sequence.
10+
- **`cmd/maddy/main.go`** — Thin entrypoint; imports root package for module registration, then calls `maddycli.Run()`.
11+
12+
Modules are wired together at runtime via `maddy.conf` configuration. Top-level blocks are lazily initialized through `module.Registry`. The **message pipeline** (`internal/msgpipeline/`) routes messages from endpoints through checks, modifiers, and to delivery targets based on sender/recipient matching rules.
13+
14+
## Build & Test
15+
16+
```sh
17+
# Build (produces ./build/maddy by default):
18+
./build.sh build
19+
20+
# Build with specific tags (e.g. for Docker):
21+
./build.sh --tags "docker" build
22+
23+
# Unit tests (standard Go):
24+
go test ./...
25+
26+
# Integration tests
27+
cd tests && ./run.sh
28+
```
29+
30+
The build embeds version via `-ldflags -X github.com/foxcpp/maddy.Version=...`. A C compiler is needed for SQLite support (`mattn/go-sqlite3`).
31+
32+
## Adding a New Module
33+
34+
1. Create a package under the appropriate `internal/` subdirectory (e.g. `internal/check/mycheck/`).
35+
2. Implement `module.Module` plus the relevant role interface (`module.Check`, `module.DeliveryTarget`, `module.PlainAuth`, `module.Table`, etc.) from `framework/module/`.
36+
3. Register in `init()`: `module.Register("check.mycheck", NewMyCheck)`. Use naming convention: `check.`, `target.`, `auth.`, `table.`, `modify.` prefixes.
37+
4. Add a blank import `_ "github.com/foxcpp/maddy/internal/check/mycheck"` in `maddy.go`.
38+
5. For checks: use the skeleton at `internal/check/skeleton.go` or `check.RegisterStatelessCheck` (see `internal/check/dns/` for a stateless example).
39+
40+
## Error Handling
41+
42+
Use `framework/exterrors` — not bare `fmt.Errorf`. Errors crossing module boundaries must carry:
43+
- SMTP status info via `exterrors.SMTPError{Code, EnhancedCode, Message, CheckName/TargetName}`
44+
- Temporary flag via `exterrors.WithTemporary`
45+
- Module name field
46+
47+
Keep SMTP error messages generic (no server config details). Use `exterrors.WithFields` for unexpected errors. See `HACKING.md` for full guidelines.
48+
49+
## Key Conventions
50+
51+
- **No shared state between messages** — check/modifier code runs in parallel across messages.
52+
- **Panic recovery** — any goroutine you spawn must recover panics to avoid crashing the server.
53+
- **Address normalization** — domain parts must be U-labels with NFC normalization and case-folding. Use `framework/address.CleanDomain`.
54+
- **Configuration parsing** — modules receive config via `config.Map` in their `Configure` method. See `framework/config/` and existing modules for the pattern.
55+
- **Logging** — use `framework/log.Logger`, not `log` stdlib. Per-delivery loggers via `target.DeliveryLogger(...)`.
56+

0 commit comments

Comments
 (0)