Changelog
Public release history, in the open.
squibble/email began as internal infrastructure in 2020, powering email for obseed.me across multiple internal iterations and adding linubra.com as the second production tenant in October 2025. The public release history below starts with v2.0 — the first version offered to external teams in April 2026 — and continues through every change since.
Versions follow SemVer. Breaking changes ship in major versions and are flagged in the Changed section. Security-relevant changes are called out separately.
-
v2.5.2
CurrentMailbox From display name
Operators can now set a human-readable display name on any mailbox. The name appears as the friendly sender label in mail clients ("Acme Corp <hello@example.com>") without affecting the sender-binding invariant — the email address remains irrevocably tied to the authenticated mailbox.
Added
-
climailboxes:update --display-name "…"— sets theFromdisplay name for a mailbox. Pass an empty string to clear it (falls back to the IMAP username). Operator-only; no request-body or token-level override is possible. -
dbMigration 0014 — nullabledisplay_namecolumn on themailboxestable. All existing mailboxes default toNULL(no behaviour change until explicitly set).
-
-
Markdown CI fix
Fixed
-
ciMarkdown Docker image build now pullsnode:22-alpinefrom Docker Hub directly — the internal registry mirror does not carry this image, causing the v2.5.0 pipeline to fail.
-
-
Markdown renderer wired into production
The app_markdown sidecar was present in development but never deployed to production — every POST /api/v1/messages/send-markdown call returned 503. v2.5.0 wires it fully into the Ansible/Docker Swarm stack.
Added
-
ciDedicated CI job builds and pushes the markdown Docker image (app_markdown/Dockerfile) on every tagged release. -
deployMARKDOWN_RENDERER_URLinjected into the app environment via Ansible;app_email__markdown__*vars control replicas, CPU, memory, and port.
Fixed
-
deployPOST /api/v1/messages/send-markdownreturned 503 in production because themarkdownsidecar service was missing from the Docker Swarm stack. Now deployed as a first-class service with health checks and resource limits.
-
-
Outbound attachment support
Both POST /api/v1/messages/send and POST /api/v1/messages/send-markdown now accept an attachments array. Files are base64-encoded by the client, decoded and validated server-side, and assembled into a MIME multipart message before dispatch.
Added
-
apiattachments field added to POST /api/v1/messages/send and POST /api/v1/messages/send-markdown — optional array of base64-encoded files with filename and content_type. -
apiPer-file size limit: 10 MiB decoded. Total size limit: 25 MiB across all attachments in a single request. Max 20 attachments per send. -
apiAttachment validation (base64 decode, per-file size, total size) runs synchronously at request time — the entire request is rejected with 422 if any attachment is invalid. -
docsAttachments section added to the API reference (/docs) with limits, curl examples, and validation error shapes.
-
-
Markdown send · emailmd sidecar
New send-markdown endpoint accepts a Markdown body, renders it to HTML via the emailmd sidecar, and dispatches through the existing outbound pipeline — no client-side HTML templating required.
Added
-
apiPOST /api/v1/messages/send-markdown — Markdown body rendered to HTML by the emailmd sidecar before delivery; all existing send constraints (idempotency, quotas, suppressions) apply. -
markdownemailmd sidecar service (app_markdown/) bundled in the Docker Swarm stack; port configurable via MARKDOWN_RENDERER_PORT (default 3853). -
docssend-markdown endpoint documented across /docs API reference, curl examples, and the /docs/features/markdown-send feature page.
Fixed
-
apiMarkdown sidecar now emits RFC 9457 problem+json error bodies on render failures, consistent with the rest of the API. -
docsAPI base URL corrected throughout docs — api.email.squibble.ch (not email.squibble.ch).
-
-
Legal pages · nginx 404 fix
Added
-
marketingImpressum, Datenschutzerklärung, and AGB pages shipped under /impressum, /datenschutz, and /agb; all linked from the site footer.
Fixed
-
infranginx now serves Astro's custom 404 and 5xx error pages instead of the raw nginx defaults.
-
-
Observability hardening · deploy fixes
Added
-
observabilityLOG_LEVEL environment variable controls runtime log verbosity (debug / info / warning / error) without a redeploy.
Fixed
-
workersGraylog GELF handler wired into all background worker entry points — previously only the main API process shipped structured logs. -
observabilityHealth-check endpoint requests suppressed from access logs to reduce noise. -
deployAlembic migration now runs as a Docker Swarm one-shot service to avoid the non-attachable overlay network constraint. -
deploypackage.json path resolved correctly in both Docker and local-dev contexts. -
cidocs/ directory copied into the nginx_marketing Docker build stage so /docs routes serve correctly after deploy.
-
-
Waitlist double-opt-in · structlog · examples
Waitlist signup gains a double-opt-in confirmation flow. Structured logging ships via structlog + Graylog. The examples directory adds a LangChain agent and a Playwright auth-flow demo.
Added
-
waitlistDouble-opt-in confirmation flow — HMAC-signed token emailed on signup; GET /confirm activates the entry. Adds 0012 migration (confirmation columns). -
waitlist/waitlist/confirmed landing page shown after email link click. -
observabilitystructlog + Graylog GELF integration for structured, searchable log shipping from the API process. -
examplesLangChain email-agent demo (tools: send, list, read, reply) and Playwright auth-flow demo added to examples/. -
examplesIdempotent seed script for spinning up a reproducible demo tenant. -
deployAlembic migrations run as a pre-deploy one-shot step; prod and dev env templates aligned. -
docsArchitecture and feature pages from the repo published at /docs/architecture and /docs/features/* with sidebar cross-links.
Fixed
-
marketingAdmin notification emails HTML-escape user-supplied fields (waitlist XSS hardening). -
waitlistSignup record refreshed from DB after mark_confirmed commit to avoid stale-cache render. -
emailIMAP account creation now accepts non-deliverable usernames (e.g. service accounts without an inbox).
-
-
Legacy API host proxy
Fixed
-
infraLegacy API hostname (email.squibble.ch/api) is now proxied to api.email.squibble.ch instead of issuing a 301 redirect — prevents reconnection loops in mail clients that do not follow redirects.
-
-
Marketing site, /security, /team
Public marketing surface ships. Trust-signal pages (/security, /team), AI-citation polish across home + docs, and full SEO baseline (robots, sitemap, JSON-LD, OG, CSP).
Added
-
marketing/security page — threat model, JWT (RFC 7519) token model, fail-closed STARTTLS (RFC 3207), Fernet at-rest encryption, GDPR Art. 5 PII redaction, RFC 3463/8058 suppression, Switzerland/FADP jurisdiction. -
marketing/team page — founder bio, full role history, Person + BreadcrumbList JSON-LD with sameAs link to canonical CV. -
marketingCustom 404 and 500 error pages. -
marketingTransactional-email integration walkthrough page under /docs/integration. -
marketingcurl examples in every section of the API reference docs. -
marketingWaitlist endpoint + frontend form with backend persistence. -
seorobots.txt, OG image, sitemap-index.xml, JSON-LD schemas (Organization, BreadcrumbList, TechArticle, Person). -
infraCSP + Permissions-Policy headers on the marketing nginx.
Changed
-
marketingHero copy now names obseed.me (since 2020) and linubra.com (since October 2025) as live tenants. -
marketingAI-citation polish: home + /docs claims now anchored to RFCs (3207, 7519, 3463) and named primitives (Stripe idempotency, Fernet AES-128-CBC+HMAC-SHA256). -
docs/docs gains an Overview section framing the three API surfaces; sections renumbered 02–11. -
errorsAPI error type URIs migrated to email.squibble.ch/docs/errors path. -
designEditorial serif palette aligned with squibble.ch brand system.
-
-
API errors → RFC 9457
Added
-
apiError handling upgraded from RFC 7807 to RFC 9457 (Problem Details for HTTP APIs, current standard). -
repoMonorepo restructure — backend moved to app_backend/, marketing site scaffolded under app_marketing/ (Astro).
-
-
CLI completeness · deploy hardening
Added
-
climailboxes:update command with partial-update sentinel (ADR 0003). -
clitokens:issue alias for token issuance. -
cliPer-mailbox progress + timeouts in mailboxes:validate.
Fixed
-
deployWait for postgres before running migrations. -
deploySkip postgres hostname constraint when unset. -
deployUse docker provider for Traefik labels on Galaxy. -
ciConnect to Galaxy as root, not ubuntu (ADR recorded).
-
-
v2.0 — Outbound gateway production cutover
First public production release of the outbound gateway. Five-phase build delivered an end-to-end SMTP send pipeline with signed tracking, RFC-compliant suppressions, and Prometheus observability.
Added
-
apiPOST /api/v1/messages/send with size + recipient-count limits and Idempotency-Key dedup. -
apiPer-token send quotas and recipient-domain allowlists. -
workerAsync background worker with claim/deliver/finalize split (no double-send window). -
workerList-Unsubscribe + List-Unsubscribe-Post headers on outbound MIME (RFC 8058 one-click). -
unsubscribePOST + GET /api/v1/unsubscribe with HMAC-signed tokens; message_id embedded in token. -
verpVERP Return-Path encoding + RFC 3464 bounce parser. -
suppressions422 enforcement at send-time; bounce cron syncs into the suppressions table; pagination + cancel-while-processing block. -
trackingSigned tracking tokens — open redirects removed. -
metricsPrometheus metrics module wired through delivery pipeline, bounce cron, and API. -
deployAnsible-based Docker Swarm deployment under .gitlab/deploy/. -
infraPostgres 18.3 added to the production stack. -
toolingbin/run task runner with gitlab:tags:create. -
climailboxes:destroy command.
Changed
-
schemaAlembic migration framework introduced; baseline + outbound gateway migrations on top.
Security
-
apiSender headers bound to authenticated mailbox (FEEDBACK §1.4) — prevents spoofing across tenants. -
tlsSMTP TLS dispatch by mailbox policy (smtp_tls_mode enum), not by port. -
docsOpenAPI docs hidden in production.
-
-
Inbound IMAP proxy hardening
Pre-cutover hardening of the inbound IMAP proxy carried over from the internal era: secure authorization, rate limiting, admin CLI revamp.
Added
-
imapSecure IMAP proxy authorization + per-key rate limiting. -
cliAdmin CLI revamp — namespacing, list views, backup command. -
clivalidate-mailboxes (now mailboxes:validate) — IMAP connection health checks. -
cliExplicit help command.
-
Ready to integrate?
We onboard a small batch of teams each week and reply within one business day.