- Created a CI workflow in `.github/workflows/ci.yml` to run Go tests and verify database migrations. - Added unit tests for the mail API in `internal/api/mail/handlers_test.go`, covering message listing, retrieval, sending, and label updating. - Introduced a service interface for the mail handler in `internal/api/mail/service_iface.go`. - Updated mail handler initialization to accept a service API in `internal/api/mail/handlers.go`. - Implemented test authentication middleware for testing purposes in `internal/api/middleware/testauth.go`. - Added various test cases for IMAP and SMTP functionalities, ensuring robust error handling and validation. - Enhanced project documentation with checklist updates for testing and CI integration. |
||
|---|---|---|
| .github/workflows | ||
| cmd | ||
| deploy | ||
| internal | ||
| migrations | ||
| project-plan | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
Ulti Backend
Backend monolithe Go orchestrant la Ulti Suite — alternative souveraine à Google Suite.
Architecture
┌─────────────────────────────────────────────────────────┐
│ nginx (reverse proxy unique, port 80) │
├─────────────────────────────────────────────────────────┤
│ ultid (Go monolithe) │
│ ├─ /api/v1/mail — Ultimail (IMAP/SMTP custom) │
│ ├─ /api/v1/drive — Ultidrive (→ Nextcloud WebDAV) │
│ ├─ /api/v1/calendar — Agenda (→ Nextcloud CalDAV) │
│ ├─ /api/v1/contacts — Contacts (→ Nextcloud CardDAV) │
│ ├─ /api/v1/meet — Ultimeet (→ Jitsi JWT) │
│ ├─ /api/v1/photos — Ultiphotos (→ Immich API) │
│ ├─ /api/v1/admin — Administration │
│ ├─ /api/v1/search — Recherche transversale │
│ └─ /ws — WebSocket realtime │
├─────────────────────────────────────────────────────────┤
│ Services tiers (Docker) │
│ ├─ PostgreSQL 16 — DB partagée │
│ ├─ KeyDB — Cache/sessions │
│ ├─ RustFS — Object storage S3 │
│ ├─ Authentik — Auth OIDC/SAML │
│ ├─ Nextcloud (nginx+FPM) — Drive/Cal/Contacts (/cloud) │
│ ├─ Jitsi — Visioconférence │
│ └─ Immich — Photos/ML │
└─────────────────────────────────────────────────────────┘
Quick Start
# 1. Copy environment file
cp .env.example .env
# Edit secrets once at the top of .env (POSTGRES_PASSWORD, RUSTFS_SECRET_KEY, etc.)
# Other variables use {{VAR}} placeholders expanded at launch.
# Toggle modules with flags:
# NEXTCLOUD_ENABLED=true|false
# JITSI_ENABLED=true|false
# IMMICH_ENABLED=true|false
# 2. Start stack (core + modules enabled by flags)
./deploy/compose-up.sh up -d
Development
# Run locally (needs PG, KeyDB, RustFS running; loads .env with {{VAR}} expansion)
go run ./cmd/ultid
# Build
go build -o ultid ./cmd/ultid
# Expand .env for external tools (docker, migrate)
go run ./cmd/envexpand -in .env -out .env.resolved
source <(grep -v '^#' .env.resolved | sed 's/^/export /')
# Run migrations (use expanded ULTID_DB_URL; host may need localhost instead of postgres)
migrate -path migrations -database "$ULTID_DB_URL" up
Reverse proxy (nginx)
Un seul nginx expose l’entrée HTTP (:80) et route :
| Chemin | Service |
|---|---|
/api/* |
ultid |
/ws |
ultid (WebSocket) |
/auth/* |
Authentik |
/meet/* |
Jitsi (si JITSI_ENABLED=true) |
/cloud/* |
Nextcloud nginx+FPM (si NEXTCLOUD_ENABLED=true) |
Nextcloud : FPM + nginx dédié ; ultid appelle NEXTCLOUD_URL en interne (http://nextcloud:80).
Caddy retiré : un seul proxy évite la double couche ; TLS plus tard (certbot, Traefik, ou listen 443 nginx).
Centralized secrets
Set passwords and keys once in the Secrets section at the top of .env. Derived values reference them with {{POSTGRES_PASSWORD}}, {{RUSTFS_SECRET_KEY}}, etc. Expansion runs via:
./deploy/compose-up.sh— writes.env.resolvedfor Docker Composego run ./cmd/envexpand -in .env -out .env.resolved— manual export for migrate/scriptsgo run ./cmd/ultid— expands.envin-process before reading config
Runtime secret files are also supported with *_FILE variables (example: ULTID_OIDC_CLIENT_SECRET_FILE=/run/secrets/oidc_client_secret).
Mail credentials are encrypted at rest with AES-GCM using MAIL_CREDENTIAL_KEYS (key_id:base64key,...) and MAIL_ACTIVE_CREDENTIAL_KEY_ID.
Secret rotation policy is enforced through:
SECRET_ROTATION_MAX_AGEULTID_OIDC_CLIENT_SECRET_ROTATED_ATMAIL_CREDENTIAL_KEY_ROTATED_ATMAIL_WEBHOOK_SHARED_SECRET_ROTATED_AT
Observability (Prometheus / Grafana)
ultid exposes Prometheus metrics at /metrics (see internal/observability/metrics.go). The core Docker Compose stack includes Prometheus and Grafana with configs under deploy/observability/:
| File | Purpose |
|---|---|
deploy/observability/prometheus/prometheus.yml |
Scrape ultid + self; loads alert rules |
deploy/observability/prometheus/alerts.yml |
Alert rules: IMAP sync stalled, outbox backlog, HTTP 5xx rate |
deploy/observability/grafana/ultid-baseline.json |
Baseline dashboard (HTTP latency/errors, IMAP sync, outbox, webhooks) |
deploy/observability/grafana/provisioning/ |
Grafana datasource + dashboard auto-load |
Start with the rest of the stack (./deploy/compose-up.sh up -d), then open:
| Service | URL | Notes |
|---|---|---|
| Prometheus | http://localhost:9090 | Targets: ultid, prometheus |
| Grafana | http://localhost:3000 | Login from .env (GRAFANA_ADMIN_USER / GRAFANA_ADMIN_PASSWORD, default admin / admin); dashboard Ultid Baseline under folder Ultid |
Alertmanager — not included in compose; route labels service=ultid and severity (critical, warning) to your on-call channels when you add it.
Stack
| Component | Technology |
|---|---|
| Backend | Go 1.23+ (chi, pgx, go-imap, go-smtp) |
| Database | PostgreSQL 16 |
| Cache | KeyDB (Redis-compatible, multi-threaded) |
| Object Storage | RustFS (S3-compatible, Apache 2.0) |
| Auth | Authentik (OIDC/SAML) |
| Files/Cal/Contacts | Nextcloud headless (WebDAV/CalDAV/CardDAV) |
| Video | Jitsi Meet (JWT auth) |
| Photos | Immich (ML classification) |
| Reverse Proxy | nginx (TLS à ajouter via certbot ou autre) |
| Search | PostgreSQL tsvector + GIN |
Project Structure
├── cmd/ultid/ — Entry point
├── internal/
│ ├── api/ — HTTP handlers (mail, admin, drive, calendar, contacts, meet, photos)
│ ├── mail/ — IMAP sync, SMTP send, rules engine, webhooks
│ ├── nextcloud/ — WebDAV/CalDAV/CardDAV client
│ ├── meet/ — Jitsi JWT generation
│ ├── photos/ — Immich API client
│ ├── auth/ — OIDC verification
│ ├── search/ — Full-text search
│ ├── realtime/ — WebSocket hub
│ └── config/ — Environment config
├── migrations/ — SQL migrations
├── deploy/ — Docker Compose configs
│ ├── docker-compose.yml — Core stack
│ ├── observability/ — Prometheus alerts + Grafana dashboard
│ ├── nginx/
│ ├── nextcloud/
│ ├── jitsi/
│ └── immich/
├── Dockerfile — Multi-stage build
└── .env.example