Two versions in one Monday (and a 90-second 502)
Morning ship (v0.3.0 & v0.3.1): TOTP 2FA, WordPress-parity RBAC, migration fidelity scorecard & GHCR Docker
We started Monday morning by tagging and shipping v0.3.0 (followed shortly by v0.3.1 with profile avatar uploads and 8 admin color schemes). Our goal for 0.3 was closing the core security, multi-user governance, and container deployment gaps so teams can run Daypress in shared production environments:
- RFC 6238 TOTP Two-Factor Authentication & Recovery Codes: Built directly into
/d-admin/profileand/login/2fawith QR provisioning and 8 single-use recovery codes, enforced across interactive sessions, JWT token exchange (POST /wp-json/daypress/v1/auth/token/2fa), and primary-password HTTP Basic Auth (X-Daypress-OTP), with emergency CLI recovery viadaypress reset-password <user> --disable-2fa. - Fine-Grained Per-Role Capabilities (WordPress RBAC Parity): Enforced across every
/d-admin/*screen and/wp-json/*route forAdministrator,Editor,Author,Contributor, andSubscriberroles. - Migration Fidelity Analyzer (
/d-admin/import): A dry-run and post-import AST scorecard (POST /wp-json/daypress/v1/import/analyze) that scores overall and per-builder fidelity across Elementor 3.16+, Divi, and Gutenberg before you commit an import. - Official Multi-Arch Docker Images: Published automatically on every release to
ghcr.io/trooper-lab/daypressfor bothlinux/amd64andlinux/arm64.
A real WordPress replacement cannot stop at rendering posts and pages, it has to replace the twenty paid plugins every company website installs on day one, in a single free binary.
Pausing to map the road to 1.0: planning 0.4 through 0.9 against a 25-plugin WordPress stack
Right after shipping v0.3.0, we paused and noticed something missing on our homepage: our roadmap jumped straight from 0.4 to 1.0. So we asked a harder question: if an agency or independent company moves a fully decked-out WordPress site to Daypress today, what are the 20 to 30 plugins they normally install, and what are we still missing to offer that entire package for free in one binary?
Instead of scattering individual plugin clones across thin point releases, we mapped out every milestone from 0.4 to 0.9 (now tracked both on daypress.dev/roadmap/ and in ROADMAP.md + AGENTS.md so AI agents working in the repo track the exact same checklist) and moved the most urgent production essentials to the front of the line:
0.4.0· Urgent Production Essentials (Shipped Today): All-in-one FormStudio & Fail-Safe SQLite Submissions Vault, the full Plausible-Style Cookieless Analytics Dashboard (/d-admin/analytics), a native zero-bloat GDPR / Privacy & Cookie Consent Banner, and the<50µsPre-Compressed Gzip SSR Hot Cache with Cloudflare CDN purge.0.5· Dynamic Theme Templates, Synced Components (Symbols), Mega Menus, Media DAM & Edge TLS/R2: Visual Single Post, Archive, and 404 templates with dynamic field tokens ({{post.title}}), Figma-style Synced Global Components with per-instance overrides, Mega Menus & Announcement Bars, a Media Library DAM (virtual folders, Replace File Without Changing URL, AVIF encoding, focal-point crop, safe SVG sanitizer), built-in Let's Encrypt ACME TLS, and S3/Cloudflare R2 media offload.0.6· Custom Post Types, Structured Fields (ACF Parity) & Filterable Collection Grids: Zero-code Custom Post Types (Case Studies, Team, Docs, Jobs, Events, Portfolio) and typed custom field schemas (repeaters, relations, dates) with 1-click Advanced Custom Fields (ACF) migration and filterable Daystudio Collection Grids.0.7· SQLite FTS5 Instant Search, Auto-OG Social Cards, Rich Schema.org & Live SEO/WCAG Linter: Built-in SQLite FTS5 full-text search (Cmd+Kmodal with BM25 ranking), Rust-rendered dynamic OpenGraph social cards (og:image), rich Schema.org JSON-LD + Auto Table of Contents, and a real-time Daystudio WCAG Contrast, Broken-Link & SEO Linter.0.8· Multilingual (i18n), Editorial Workflows, Visual Revision Diffs & 1-Click SQLite Staging: Multi-language content linking (/en/,/de/,/nl/) withhreflangsitemaps and WPML/Polylang migration, 1-click post duplication, shareable secret draft preview links, Visual Side-by-Side AST Revision Diffs, and 1-Click SQLite Sandbox Staging.0.9· Built-in Newsletters, Outbound Webhooks, Zero-Flicker A/B Testing & WebAssembly Crates: Native double opt-in newsletters & member-gated posts (MailPoet/Ghost parity), branded shortlinks (/go/*), HMAC outbound webhooks, sub-millisecond server-side A/B split testing with Core Web Vitals telemetry, and sandboxed WebAssembly extension crates.
Afternoon ship (v0.4.0): all-in-one FormStudio & fail-safe SQLite submissions vault
After shipping v0.3.0, we audited our roadmap from 0.4 to 1.0 against a fully decked-out WordPress company website. One gap stood out immediately: forms. On WordPress, sites stitch together Contact Form 7, Flamingo, Ninja Forms, or WPForms, and if SMTP credentials expire, customer leads vanish into thin air.
So this afternoon we built and shipped FormStudio (/d-admin/forms) and the Fail-Safe SQLite Submissions Vault in v0.4.0:
- SQLite-First Lead Persistence: Every submission is written to
daypress_form_entriesbefore SMTP or webhook delivery is attempted, recordingemail_status(sent,failed,queued,disabled) and exact diagnostic errors with a 1-Click Retry / Resend Email button and 1-click CSV/JSON export. - CF7 + Ninja Forms + WPForms Combined: Multi-column rows (
1/2,1/3,2/3), multi-step wizards with progress bars, conditional field visibility & conditional email routing, 15 field types (including isolated private file uploads, star ratings, range sliders, live calculated formulas for instant price quotes, and hidden UTM capture), and user confirmation auto-responders with{{field}}merge tags. - 6 New MCP FormStudio Tools: AI agents can list, inspect, create, update, and query form submissions directly via
POST /mcp.
Plausible-style cookieless analytics (/d-admin/analytics), native privacy banner & <50µs hot cache
Next, we noticed that while Daypress's cookieless beacon was already recording daily-salted SHA-256 visitor hashes into daypress_analytics_events, the admin dashboard only showed three summary counters and wasn't even pinned in the left sidebar.
In v0.4.0, /d-admin/analytics is now a full Plausible-style analytics suite pinned in the main navigation: interactive SVG time-series charts across 24h, 7d, 30d, 90d, 12m, and all ranges; breakdown tables for Top Pages, Referrers, UTM Sources/Mediums/Campaigns, Countries, Devices, and Form/Goal Conversions; plus a new daypress_get_analytics MCP tool.
Alongside it, we shipped a zero-bloat <2 KB Native GDPR / Privacy & Cookie Consent Banner (/d-admin/options-privacy) with script-category gating, and our new In-Memory Pre-Compressed Gzip SSR Hot Cache that serves public pages in under 50 microseconds with ETag / 304 Not Modified, X-Daypress-Cache: HIT|MISS|BYPASS headers, automatic cache invalidation on edit, and optional Cloudflare CDN Zone purge.
The 90-second post-mortem: why updating daypress.dev to v0.4.0 triggered a brief 502
Shipping two versions in one day also gave us a classic real-world database migration lesson. Moments after clicking the 1-click self-updater on daypress.dev to upgrade from v0.3.1 to v0.4.0, the binary swapped cleanly, restarted under systemd, and returned 502 Bad Gateway.
Inspecting journalctl -u daypress immediately revealed the culprit:
CRITICAL SERVER ERROR: no such column: status in CREATE INDEX IF NOT EXISTS idx_form_entries_form_status ON daypress_form_entries(form_id, status, created_at);
Here is why every automated test passed in CI while the live upgrade failed:
- On a brand-new database (like in CI tests),
CREATE TABLE IF NOT EXISTS daypress_form_entriescreated the table with the newstatuscolumn immediately, so theCREATE INDEXline right below it inddl.rssucceeded. - On our existing
v0.3production database,daypress_form_entriesanddaypress_analytics_eventsalready existed without the newstatusandevent_typecolumns. BecauseCREATE TABLE IF NOT EXISTSis a no-op on existing tables, SQLite hitCREATE INDEX ... ON daypress_form_entries(form_id, status, created_at)insideexecute_batch(SUBSYSTEMS_SCHEMA_SQL)before reaching the idempotentALTER TABLE ... ADD COLUMNmigration loop 35 lines later ininit_schema()!
We applied the ALTER TABLE statements on the production SQLite file, restarted daypress.service in under 90 seconds, removed the premature index creation from ddl.rs so indexes on migrated columns are only created after ALTER TABLE runs, and added a dedicated regression test (test_v03_to_v04_schema_upgrade_adds_columns_before_indexes) that simulates upgrading a pre-v0.4 schema.