4.6 KiB
Plan: send-log-retention
Status: candidate
Date: 2026-08-17
Version: 1.x MINOR; no schema migration required (uses existing settings table).
Goal
Let the global administrator change how long delivery journal rows
(send_log, /deliveries) are kept, from the panel — without editing .env.
Context (as-built)
Retention already exists, but only via environment:
SEND_LOG_RETENTION_DAYS(default 90) in.env/ Compose.cmd/panel/main.gopasses it tologtail.Run.retentionLoopprunes viaDeleteSendLogBeforeevery 6 hours.- No panel control;
handlers_monitor.gohardcodes «ninety days» in copy. - Migration
0001_init.sqldescribessettingsas the place for «retention overrides», but no UI writes that key yet.
This plan moves the effective retention into SQLite settings, with env as
bootstrap only.
Scope
In:
- Settings card on
/settings(global administrator only): Send log retention (days). - Key
send_log_retention_daysinsettings. - Validation: integer range 7–365 (exact bounds fixed at implementation).
- On first use: if setting missing, seed from env
(
SEND_LOG_RETENTION_DAYS, default 90) at panel start or first save. - Log-tailer reads the setting each prune cycle (no container restart).
- Delivery pages and guide copy show the current retention, not a hardcoded 90.
- Tests; guide.md; CHANGELOG.md.
Out:
- Per-domain retention (instance-wide only).
mail.logrotation (logrotate, 14 daily files — unchanged).- Immediate prune on save when lowering retention (next 6 h cycle is enough; optional «Prune now» not in v1).
- Domain-admin access to this setting.
Architecture
flowchart LR
settingsPage["/settings form"] --> sqlite["settings.send_log_retention_days"]
env["SEND_LOG_RETENTION_DAYS bootstrap"] --> sqlite
sqlite --> retentionLoop["logtail retentionLoop"]
retentionLoop --> prune["DeleteSendLogBefore"]
- Read path —
GetSendLogRetentionDays(): settings value if valid, else env default. - Write path — POST
/settings(global admin): validate,SetSetting, flash confirmation. - Prune path — change
logtail.retentionLoopto acceptfunc() intorRetentionReaderthat queries settings each cycle (same 6 h ticker). - Copy — inject retention days into delivery list/detail templates and remove hardcoded «ninety days».
SEND_LOG_RETENTION_DAYS remains documented in guide.md as the
initial default until changed in Settings.
Relation to domain-stats-auto-ratelimit
domain-stats-auto-ratelimit uses a 30-day stats window. Requires effective retention ≥ 30 for full accuracy. When retention < 30:
- Stats UI shows a warning and uses
min(30, retention)as the window, or - Settings validation warns when saving a value below 30 while stats/auto are enabled (pick one at implementation; document in guide).
Recommended roadmap order: send-log-retention before or parallel with domain-stats-auto-ratelimit.
Panel UI
New card on settings.html
(global admin block, near rate limits or under a «Deliveries» heading):
- Number input: retention days (7–365).
- Muted copy: rows older than this are deleted from
/deliveries; main driver of/datagrowth; does not affectmail.logrotation.
Domain administrators keep the narrow credentials-only settings page.
Tests
- Save/load setting; reject out-of-range values.
retentionLoopuses updated value without process restart (mock reader).- Bootstrap: empty settings → env default used for prune.
- Template/delivery copy reflects configured days.
go test / go vet on touched packages.
Done when
- Global admin can set retention on
/settings; value persists in SQLite. - Prune uses the panel value on the next cycle; env remains bootstrap default.
- Guide documents panel vs env; CHANGELOG entry added.
- Hardcoded «ninety days» removed from delivery UI.
Risks
- Operator lowers retention while bookmarking old delivery URLs — existing behaviour; copy already notes pruned rows are gone.
- Settings change without restart — must be tested so log-tailer never keeps a stale int from panel start only.
Version: 1.x MINOR.