Review pass over `1.4.0`…`1.9.1`: port 25 no longer shares the submission milter chain, DMARC reports are validated before they are stored, and the auto rate limit is correct for a non-default level-1 window. Schema migration `0010` (index only). Upgrading is a tag bump. Added: - SQLite migration `0010_send_log_app_login_index.sql` — - DMARC aggregate reports are accepted as **zip** as well as gzip or plain XML - e2e: `checkPort25ConfigCombinations` regenerates the Postfix configuration Changed: - README hero line and a **Positioning** block in `docs/product.md` record the Fixed: - The setup account's password is hashed at bcrypt cost `12` like every other - Login/setup rate limiter: protecting blocked buckets from eviction is now - `postqueue -p` output is bounded to 4 MiB (the timeout alone left the size of - [schema-migrations.md](docs/schema-migrations.md): the schema section was - postqueue reader is now time-bounded: `postqueue -p` runs under a 5-second … (11 more in CHANGELOG) Changed: - [roadmap.md](docs/roadmap.md) now carries everything the 2026-08-19 code - Extracted `internal/configsafe` — the empty-value and forbidden-character - Build, deploy and migration comments no longer cite the archived - Dropped the `parseDomainRateLimitForm` / `parseAppRateLimitForm` pass-through - Extracted shared helpers: `internal/atomicfile` (atomic temp-write+rename) and … (7 more in CHANGELOG) Removed: - Unused legacy asset `docs/assets/icon.png` (superseded by the four brand SVGs - Plan documents for already-shipped features: `domain-stats-auto-ratelimit`, Full detail: CHANGELOG.md (1.9.2).
77 lines
2.8 KiB
SQL
77 lines
2.8 KiB
SQL
-- Initial SelfPost schema. One SQLite file under /data holds the whole panel
|
|
-- state so a single directory backup/restore is sufficient.
|
|
|
|
-- Single administrator account. Exactly one row is allowed; the presence of
|
|
-- that row is what marks primary setup as complete, which is why the /setup
|
|
-- route disappears once it exists.
|
|
CREATE TABLE admin (
|
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
username TEXT NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Free-form key/value panel settings (retention overrides, misc flags).
|
|
CREATE TABLE settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL
|
|
);
|
|
|
|
-- Sending domains managed through the panel. DKIM keys themselves live on disk
|
|
-- under /data; this row records the selector and metadata.
|
|
CREATE TABLE domains (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
dkim_selector TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Applications bound to a domain. address_mode is either the domain wildcard or
|
|
-- an explicit address list; the SASL login is globally unique.
|
|
CREATE TABLE applications (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
domain_id INTEGER NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
|
|
login TEXT NOT NULL UNIQUE,
|
|
address_mode TEXT NOT NULL CHECK (address_mode IN ('wildcard', 'list')),
|
|
created_at TEXT NOT NULL
|
|
);
|
|
|
|
-- Explicit sender addresses for applications in 'list' mode. Each address must
|
|
-- belong to the application's domain (validated in the panel).
|
|
CREATE TABLE application_addresses (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
application_id INTEGER NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
|
|
address TEXT NOT NULL,
|
|
UNIQUE (application_id, address)
|
|
);
|
|
|
|
-- Structured send log. One row per (queue-id, recipient); the log-tailer
|
|
-- advances status from queued to a final state.
|
|
CREATE TABLE send_log (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
queue_id TEXT,
|
|
domain TEXT,
|
|
app_login TEXT,
|
|
from_addr TEXT,
|
|
to_addr TEXT,
|
|
subject TEXT,
|
|
status TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
);
|
|
CREATE INDEX idx_send_log_queue_id ON send_log (queue_id);
|
|
CREATE INDEX idx_send_log_domain ON send_log (domain);
|
|
CREATE INDEX idx_send_log_created_at ON send_log (created_at);
|
|
|
|
-- Differentiated rate limits per domain/application. Both the IP binding and
|
|
-- the message limit are optional.
|
|
CREATE TABLE rate_limits (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
scope TEXT NOT NULL CHECK (scope IN ('domain', 'application')),
|
|
ref_id INTEGER NOT NULL,
|
|
allowed_ips TEXT,
|
|
max_messages INTEGER,
|
|
window_seconds INTEGER,
|
|
UNIQUE (scope, ref_id)
|
|
);
|