Hello everyone,
v3.1.6 is out.
This is a focused fixes-and-hardening release. It restores the built-in Access policies on fresh installs, makes inbound email work when Webmail sits behind Cloudflare Access, tightens security across the mail and Agent paths, and refreshes the Webmail frontend.
A lot of this release started with your reports. A special thank you to @PauloJf for the detailed reproduction in #399, which led to a chain of fixes around system policies and, eventually, a full security pass over the mail pipeline.
Highlights
- Built-in Access policies are created and re-linked correctly again
- Inbound email now reaches Mail Manager when Webmail is behind Cloudflare Access
- Agent names carry from the API key into newly generated deploy commands
- Agent API keys and tunnel tokens are no longer stored in plaintext
- Mail Manager secrets are encrypted at rest
- The outbound email worker is hardened against relay abuse and header injection
- Login and mailbox rate limits can no longer be bypassed with a spoofed header
- Webmail is upgraded to Vite 8 and Tiptap 3, with a clean
npm audit - Dashboard dialogs stay open, and real-time logs no longer reconnect every few seconds
What's New
Built-in Access Policies
On a fresh install, the two built-in policies, public-default-bypass and authenticated-default, could fail to be created in Cloudflare because setup ran before the configuration was written. A stale local-only reference then made DockFlare skip them on every later start, so label-created Access Applications came up with no policies attached. Because Access Applications are default-deny, every login was rejected without a useful explanation in the logs.
DockFlare now verifies both policies against Cloudflare on startup and creates or re-links them when they are missing. Existing installations repair themselves on the next restart.
Two related issues are also fixed: the Access Policies page no longer creates a duplicate public-default-bypass policy on every visit, and an unknown dockflare.access.policy value now logs an error instead of silently removing an existing policy.
Thank you again to @PauloJf for the report and reproduction in #399.
Inbound Email Behind Cloudflare Access
If your Webmail hostname is protected by a Cloudflare Access Application, the inbound email worker's webhook to /api/v1/webhook/inbound was caught by the Access login redirect and never reached Mail Manager. Outbound mail worked, but received mail quietly accumulated in the R2 temp_cache.
DockFlare now creates a path-scoped DockFlare Mail Webhook Bypass Access Application during domain setup, and existing installations are repaired automatically on startup. Only that exact path is opened, the webhook remains protected by its per-domain HMAC signature, and the Webmail UI stays behind Access.
Agent Names From API Keys
When you give an Agent API key an Owner name, that name is now injected as AGENT_DISPLAY_NAME into the generated Compose snippet and one-liner. On first enrollment, the Agent appears in DockFlare with the name you assigned instead of the generic dockflare-agent fallback.
This applies to newly generated deploy commands. Already-deployed Agents keep their current name until they are redeployed with the new command or renamed manually.
Webmail Refresh
The Webmail frontend has been upgraded to Vite 8 and Tiptap 3, along with the Vue plugin, PWA plugin, and Workbox. All 35 previous npm audit findings are gone, and both Webmail and DockFlare now audit cleanly.
The rich-text composer was updated for Tiptap 3, and the build image moved to Node 22. A long-standing build hygiene issue was fixed as well: vue-tsc was emitting compiled .js and .js.map files beside the source files, and 102 of those generated artifacts had been committed. noEmit is now enabled, the committed artifacts have been removed, and the generated output is ignored.
Security
Outbound Email Worker
The outbound worker can no longer be used as a relay for arbitrary senders. It pins the sender to the configured domain or optional allowlist, validates recipients, strips CR/LF characters from every header to block header and MIME injection, preserves the sender display name in From, stops exposing the Bcc: header, encodes attachment filenames, compares the authentication secret in constant time, and enforces hourly and daily limits per sender. Mail Manager now sanitizes header fields before dispatch as well.
Agent Secrets
Agent API keys and Cloudflare tunnel tokens now live only in the encrypted key store. state.json and backups no longer contain them, legacy plaintext values are migrated on first load, and rolling an Agent key now revokes all previously active keys.
Mail Secrets at Rest
R2 secret keys, webhook secrets, and outbound authentication secrets are now encrypted in the Mail Manager SQLite database using AES-GCM. The encryption key comes from MAIL_SECRET_KEY, with the internal bootstrap secret used as a fallback. Existing rows are migrated on startup, database and directory permissions are tightened, and the database is compacted after migration.
Mail Delivery Hardening
Inbound mailbox resolution now trusts the SMTP envelope instead of spoofable To: and Delivered-To: headers, so a sender can no longer place mail in an arbitrary mailbox when catch-all is enabled.
Cross-mailbox folder moves are blocked, folder listings are scoped to the current mailbox, and any legacy mis-filed messages are repaired on startup. Web Push subscriptions must use HTTPS and resolve to public addresses; invalid or legacy endpoints are removed when sending.
Additional Safeguards
- Login and mailbox rate limiting now trusts forwarded client IP headers only from known proxies, so limits cannot be bypassed by rotating a header.
- Agent-supplied fields are HTML-escaped before rendering, and the blanket
Access-Control-Allow-Origin: *header has been removed from the Master UI. /email/statusand the Email page now return a non-secret summary instead of exposing the complete email configuration.- Internal mail bootstrap endpoints require the shared
INTERNAL_BOOTSTRAP_SECRETand a matchingX-Bootstrap-Token. - The Agent API Access Application keeps its admin bypass policy so the same-origin admin UI can reach the API while the endpoints remain protected by the Master and per-Agent API keys. Startup now ensures that policy exists without creating duplicates.
Fixed
- Dashboard dialogs: Removed a duplicate live-update listener that reloaded the Dashboard every few seconds and closed open dialogs such as Add Rule. The remaining fallback reload now waits until dialogs are closed.
- Real-time activity logs: The log panel no longer reconnects every ~10 seconds or opens duplicate connections. It now uses proper SSE heartbeats, one reconnect watchdog, per-viewer log fan-out, and pauses while the browser tab is hidden.
- Access Group country picker: When many countries were selected, the chip list would not scroll and the dropdown was clipped by the modal. The list now scrolls, and the dropdown renders outside the modal.
- Email domain lifecycle: Tearing down a domain now removes its webhook bypass Access Application. Creating a mailbox for an address that already has a routing rule updates it instead of failing with a 409, and quota KV namespace creation no longer logs
already existson every start. - Internal email configuration: Mail bootstrap endpoints fail closed when the shared secret is missing, and the installer generates and preserves that secret automatically.
Before You Update
MAIL_SECRET_KEYmust remain stable once set. Changing or removing it makes already-encrypted mail secrets unreadable, and DockFlare now fails loudly instead of returning ciphertext.- If you use the repository's
docker-compose.yml, the Mail Manager service now expectsMAIL_SECRET_KEYin your.env. The installer generates it automatically; existing installations can add one or rely on the bootstrap-secret fallback. - Consider rolling your Agent API keys once after upgrading so any older
state.jsoncopies or backups no longer contain a working key. - The global
Access-Control-Allow-Origin: *header has been removed. Same-origin UI traffic is unaffected, but external browser-based integrations that relied on it will need their own CORS handling.
For the full technical breakdown, please see the changelog.
Community Thanks
This release grew directly from community reports and testing. Thank you to @PauloJf for the detailed reproduction in #399, and to everyone who reported the smaller UI, logging, Agent, and mail issues addressed here.
Happy tunnelling,
Christian