Repository navigation
feat: mail-stack monitoring (postfix/dovecot/rspamd/greylist) - #47
Merged
Merged
Conversation
Add MailConfig struct with Enabled tri-state pointer and RspamdStatURL field to Config, mirroring the AutoUpdateConfig pattern. MailEnabled() defaults to true when unset; RspamdStatURL() falls back to the http://127.0.0.1:11334/stat default constant.
Implement postfixQueue() function that counts regular files recursively in postfix queue directories (active, deferred, hold, incoming) under the spool root. Returns nil if spool root does not exist. Uses filepath.WalkDir for efficient, non-following traversal. Test verifies correct counting with hashed subdirectories and missing directories.
Implement rspamdStat() function that fetches the rspamd /stat endpoint, decodes JSON tolerantly, and maps to apitypes.RspamdStat. Returns Reachable:false on any error. Includes comprehensive test coverage with httptest servers for success and failure scenarios.
Assembles the mail collector from the existing helpers (serviceState, postfixQueue, rspamdStat, checkPort) and registers it in buildCollectors behind cfg.MailEnabled(). Collect probes postfix/dovecot/rspamd/postgrey; if none are present the batch.Mail field is left nil so non-mail hosts produce no payload. Queue, Rspamd stat, and port checks are each scoped to the services that are actually present. New() is isolated to a linux-tagged file so the package tests compile and pass on darwin.
Wire SaveMailReport into handleIngest (best-effort, logged on error)
so mail telemetry from the agent is persisted on each accepted ingest.
Add GET /v1/hosts/{id}/mail (operationId: get-host-mail) returning
{detected bool, report *MailReport}; gated by the same protected
middleware as the security and logins endpoints. Add integration test
(testcontainers-gated via MON_TEST_DOCKER=1). Regenerate api/openapi.yaml.
Adds the Mail tab to the host-detail view. Fetches GET /v1/hosts/{id}/mail
at layout level (same pattern as security) so the nav entry is gated on
detected without a second round-trip from the tab itself. The tab renders
four panels: Services (up/down pills), Queue (active/deferred/hold/total
with warn colouring on non-zero deferred/hold), Rspamd (scanned/greylisted/
rejected, hidden when not reachable), and Ports (port/proto, open pill, TLS
badge, self-signed note, cert-expiry countdown).
Regenerates api-types.generated.ts to pull MailReport, MailService,
MailPortCheck, PostfixQueue, and RspamdStat from the updated spec.
Adds the mail i18n namespace (en + de) and nav.mail key in hostDetail.
Add mail_queue_deferred and mail_queue_total as metric_threshold metric kinds so operators can alert on postfix queue depth. - metricMailQueue() reads the latest metrics_mail row per host via DISTINCT ON (host_id) and fires when the selected column exceeds the threshold; mirrors the one-shot pattern of metricFail2banBanned / metricCrowdSecActive. - Column restricted to an explicit allowlist inside the helper; op is sqlComparator-validated; threshold is parameterised — no SQL injection surface. - Two switch cases added to runMetricRule. - MetricMailQueueDeferred / MetricMailQueueTotal constants added to apitypes.MetricKind; MetricThresholdParams.Metric enum updated so the API rejects unknown metric names at rule-creation time. - TestMetricMailQueueDeferred (testcontainers-gated, MON_TEST_DOCKER=1) seeds a metrics_mail row with queue_deferred=15, fires the evaluator, and asserts alert_history + alert_state rows are written.
Add a periodic evaluator that scans host_mail_status for services with active=false and fires one alert per (host, service) pair using dedup key mail_service_down:<host_id>:<service_name>. - Add ConditionMailServiceDown const and mail_service_down to all three condition_type enum struct tags in apitypes.go - Add mail_service_down to splitDedupKey host-scope case list - Dispatch evalMailServiceDown from runPeriodic, mirroring the security_updates_pending pattern - Implement evalMailServiceDown: JOIN host_mail_status with hosts, unmarshal apitypes.MailReport, fire per inactive service - Add TestMailServiceDown integration test (testcontainers-gated): postfix inactive → alert fires; all-active host → no alert - Regenerate api/openapi.yaml with the updated enum
- Add mail_service_down to NotificationConditionType, CONDITION_TYPES, CATEGORY_MAP (availability), NO_PARAM_CONDITIONS, conditionIcon, and conditionSummary in catalogue.ts - Add mail_queue_deferred and mail_queue_total metric kinds to METRIC_KINDS - Add SuggestedAlertsSection to Mail.tsx (gated on detected=true) with two link-cards pointing to /notifications for mail_service_down and queue alerts - Add suggestedAlerts i18n keys to en/mail.json and de/mail.json (no drift)
Add mail-stack monitoring to the agent feature list in README.md and add section 4b to docs/OPERATIONS.md covering the Mail tab, agent config keys (mail.enabled, mail.rspamd_stat_url), and alert rules (mail_service_down, mail_queue_deferred, mail_queue_total).
…, http.NoBody)
- ports.go: replace net.DialTimeout with (&net.Dialer{}).DialContext and
tls.DialWithDialer with tls.Dialer.DialContext to thread ctx through
(fixes noctx on plain and TLS dials; also resolves unparam on ctx)
- store/mail.go: use errors.Is(err, pgx.ErrNoRows) instead of == (errorlint)
- rspamd.go: pass http.NoBody instead of nil request body (gocritic httpNoBody)
- queue.go: restructure countRegularFiles to assign WalkDir return to _,
skip unreadable dirs with fs.SkipDir — removes nilerr finding cleanly
- alerts_engine_test.go: drop unnecessary fmt.Sprintf wrapper (staticcheck S1039)
- gofmt: mail_test.go, mail_test.go (api), store/mail.go, apitypes.go
- gofumpt: queue.go, queue_test.go, config_mail_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds agent-local mail-stack monitoring. Built subagent-driven from
docs/superpowers/plans/2026-07-16-mail-stack-monitoring.md(design:docs/superpowers/specs/2026-07-16-mail-stack-monitoring-design.md). 15 tasks, each TDD + task-reviewed; final whole-branch review: READY TO MERGE.What
The agent detects postfix/dovecot/rspamd/postgrey and ships a
MailReportin the ingest snapshot (nil when no mail stack → non-mail hosts unaffected). Signals: service up/down (systemd via safeexec), postfix queue depth (spool file count via CAP_DAC_READ_SEARCH), rspamd/greylist stats (localhost:11334/stat), local port + TLS-expiry (verified handshake, self-signed surfaced).Server: migration 0035 (
host_mail_status+metrics_mailhypertable, 90d retention);SaveMailReport/MailStatus/MailQueueSeries; gatedGET /v1/hosts/{id}/mail.Web: host-detail Mail tab (shown only when detected) — services, queue, rspamd, ports/TLS + cert-expiry.
Alerts (Phase 2):
mail_queue_deferred/mail_queue_totalviametric_threshold; newmail_service_downcondition; suggested-alert links on the tab.Verification
go build(+GOOS=linux),go vet,go test ./..., testcontainers integration tests (store/alerts/api,MON_TEST_DOCKER=1),govulncheck(0 in called code), weblint+build— all green. OpenAPI regenerated. New agent config:mail.enabled,mail.rspamd_stat_url.Notes
mail_service_downis fire-only (consistent with sibling periodic evaluators likesecurity_updates_pending).MailQueueSeriesis implemented + tested but not yet surfaced (future queue sparkline).