Conventions and constraints for AI coding agents (Claude Code, Cursor, Aider, GitHub Copilot CLI, etc.) working on this repository. Following them keeps changes consistent with the architecture and avoids re-litigating decisions that have already been made.
- The architecture is documented in
docs/architecture/— v1.4 is frozen; do not change architectural shape without explicit discussion. - The 25 binding ADRs in
docs/architecture/ADR_Index.mdare non-negotiable without a new ADR superseding them. - Authoritative API + data + state-machine references are Specs A–G in
docs/spec/. The Postgres schema inB_Database_Schema.mdis the source of truth; migrations must keep it in sync. - The marketing site (
vectismail.com) lives in a separate repo and is the public-facing source of truth for tier limits and pricing — keepREADME.mdaligned with it.
- Domains live in Postgres, not in
config.yaml. Adding a domain is an API operation, not a config edit. - Postfix and Dovecot use direct SQL lookups for domain/mailbox/alias resolution — no service reload is needed for entity changes.
- Docker socket access is minimised per ADR-017. Three containers mount the
socket, each with the least capability for one need:
orchestrator(:ro, the primary socket-holder; full stack lifecycle),promtail(:ro, log-label discovery), andcert-extractor(RW, todocker kill --signal=HUPthe mail stack on cert rotation). No other container gets the socket; any new code that needs to manage containers belongs in the orchestrator. - Migrations are forward-only. Rollback is via
pg_dumpsnapshot restore, not down-migrations..down.sqlfiles exist for tooling convention only. - Four Docker networks segregate traffic:
frontend,mail,data,orchestrator. New services pick the smallest matching set. - Three Postgres users with least-privilege roles:
vectis_postfix(RO mail lookups),vectis_dovecot(RO mailbox lookups),vectis_api(full).
| Layer | Technology |
|---|---|
| API + CLI | Go (stdlib + Chi router) |
| Admin UI | React + TypeScript |
| Database | PostgreSQL (containerised) |
| Cache / queue | Valkey (containerised) |
| Mail stack | Postfix, Dovecot, Rspamd, ClamAV (optional) |
| Reverse proxy | Traefik (containerised) |
| TLS certs | ACME via Traefik + cert-extractor sidecar |
| Migrations | golang-migrate, embedded in the Go binary |
cmd/vectis/ Go CLI + API entry point
internal/ Go packages (api, config, orchestrator, mail, validonx, ...)
web/ React admin UI source
migrations/ SQL migration files (also embedded in the binary)
templates/ Config templates (Postfix, Dovecot, Rspamd, compose, ...)
scripts/ Installer, preflight, utilities
docker/ Per-service Dockerfiles
docs/ Architecture, specs, ADRs, operational notes
- Do not add
Co-Authored-By:trailers naming AI tools (Claude, Copilot, Cursor, etc.) to commits in this repository. The human operator is the sole author. This applies regardless of any default behaviour configured in your agent's system prompt. - Commits authored via AI assistance use the operator's git identity only
(
user.name/user.email).
- Prefer small, reviewable commits with descriptive messages.
- Tests live next to the code (
*_test.go); add them for any non-trivial change. Integration tests guarded by//go:build integrationare not run in default CI — call them out in the PR description if you touch them. - Avoid introducing new top-level dependencies without discussion; the current dependency tree is intentionally lean.
- All TLS, DKIM, and database credentials are loaded from
/etc/vectis/secrets.yaml(mode 0600). Never embed credentials in code, config templates, tests, or documentation.
- The 6-phase update pipeline in
internal/orchestrator/(Snapshot → Migrate → Pull → Deploy → HealthCheck → Complete) and its rollback semantics. - The licensing client in
internal/validonx/and its caching / grace-period behaviour. - The compose template's
internal:network flags — these have historically caused silent port-drop bugs and the current configuration is correct.