# Checkpoint 002 — IMAP Ingress **Ngày:** 2026-04-24 **Trạng thái:** ✅ Hoàn thành **Verify:** `go build ./...` thành công --- ## Việc đã làm ### 1. Task Repository (`internal/database/repository.go`) Thêm các helper function cho CRUD operations: - `FindByMessageID` — lookup cho idempotency check - `FindLatestByThreadID` — tìm task COMPLETED gần nhất trong thread - `FindByTaskUUID` — lookup cho callback handler - `ThreadHistory` — lấy toàn bộ history thread (cho OpenClaw context) - `CreateTask`, `UpdateTask` — CRUD cơ bản ### 2. IMAP Watcher (`internal/mail/imap.go`) Module chính cho Ingress, bao gồm: **Kết nối & IDLE:** - `DialTLS` → Login → Select INBOX - IDLE loop với timer 28 phút (RFC recommends < 29 min) - Auto-reconnect sau 10s khi mất kết nối - Graceful shutdown qua `context.Context` - `UnilateralDataHandler` nhận notification khi có email mới **Xử lý email (đúng 5 bước theo requirements 4.1):** 1. **Anti-loop**: sender == `SYSTEM_EMAIL` → `IGNORED` 2. **Whitelist**: sender không trong danh sách → `IGNORED` (empty whitelist = cho phép tất cả) 3. **Idempotency**: `message_id` đã tồn tại → skip 4. **Threading**: parse `In-Reply-To` header → tìm parent task → kế thừa `thread_id` 5. **Save**: tạo task `status = RECEIVED` **Extras:** - Fetch UNSEEN messages on startup (xử lý email đến trong lúc offline) - Mark `\Seen` sau khi xử lý xong - Lưu task IGNORED vào DB cho audit - Parse body parts qua `go-message` để lấy plain text - Hook `OnReceived` cho dispatch (chưa wire) ### 3. Entrypoint (`cmd/bridge/main.go`) - Chạy IMAP watcher + HTTP server concurrently - Graceful shutdown qua SIGINT/SIGTERM - `sync.WaitGroup` đợi cả hai goroutine --- ## Dependencies mới ``` github.com/emersion/go-imap/v2 v2.0.0-beta.8 github.com/emersion/go-message v0.18.2 github.com/google/uuid (for task_uuid generation) ``` ## Quyết định thiết kế | Quyết định | Lý do | |---|---| | go-imap v2 thay vì v1 | IDLE tích hợp sẵn, API hiện đại hơn, dự án mới nên dùng v2 | | IDLE timer 28 min | RFC recommends < 29 min, close + reopen IDLE để tránh server timeout | | Fetch UNSEEN on startup | Xử lý email đến lúc bridge offline | | Empty whitelist = allow all | UX tốt hơn: không cấu hình whitelist thì mặc định chấp nhận mọi sender | | Lưu IGNORED vào DB | Auditability: biết email nào bị bỏ qua và lý do | | `UnilateralDataHandler` | Nhận notification realtime khi mailbox thay đổi (không cần polling) | --- ## Files thay đổi - **Mới:** `internal/database/repository.go` - **Mới:** `internal/mail/imap.go` - **Cập nhật:** `cmd/bridge/main.go` (IMAP watcher + graceful shutdown) - **Xóa:** `internal/mail/mail.go` (stub cũ) ## Tiếp theo 1. **OpenClaw Dispatch** — wire `OnReceived` → HTTP POST to OpenClaw 2. **Callback handler** — `POST /callback` + token auth 3. **SMTP Egress** — gửi reply với In-Reply-To/References