Matthew Meszaros
c59446271b
Merge remote-tracking branch 'origin/main' into feature/bulk-select-all
2026-09-08 05:20:02 -07:00
Matthew Meszaros
a6862477b3
feat: add a 'select all matching' selection to every contact list so a bulk action can cover the whole filtered set instead of only the loaded pages: a ContactSelection body (all + filters + exclude) on bulk update, bulk delete, verification, batch research, segment members and CRM push resolved server-side through a new SearchIDs that shares the search's WHERE builder, a Gmail-style banner and shared selection module in the contacts table, campaign Leads and segment members views, an uncapped select-all in the From contacts picker, and fixes for the keyset cursor comparing a row against itself (contact pagination repeated and skipped rows past page one) and the dashboard sending custom-field filters under the wrong wire name
2026-09-08 05:19:59 -07:00
Matthew Meszaros
1dba65f21f
Merge pull request #379 from warmbly/feat/admin-panel-frontend
...
feat: admin panel upgrade (frontend and docs): command palette, mobile nav, merged Overview, Health and Configuration pages, and new Sync, Sends, Jobs, Fleet, Admins and Transfers pages
2026-09-08 04:35:37 -07:00
Matthew Meszaros
c8ecf84e5e
feat: report the highlighted search-picker option only through aria-activedescendant and keep aria-selected false until an option is actually picked, as the CodeRabbit re-review on the admin frontend PR asked
2026-09-07 23:16:18 -07:00
Matthew Meszaros
f9bef0d5f1
feat: address the CodeRabbit review on the admin frontend PR by warning about the last admin who can grant access only when the whole admin list is on screen, resetting the convert-to-dedicated form on Cancel, following a changed ?q= on the mailboxes page instead of only its first value, treating the all sentinel as the sync state default and not as a search term, naming the lease window in the webhook reclaim confirmation only once it is loaded, walking transfer group dependents transitively so unticking a group drops everything that needs it, labelling the shown-page scope of the worker bulk move, closing the mobile drawer when the viewport reaches the desktop breakpoint, dropping the stale plan mention from the users page description, and completing the accessibility contract of the status segments, passphrase fields, icon-only delete button and search combobox
2026-09-07 23:08:56 -07:00
Matthew Meszaros
8ce6e409cc
Merge remote-tracking branch 'origin/main' into feat/admin-panel-frontend
2026-09-07 22:05:29 -07:00
Matthew Meszaros
bb8256c80e
Merge pull request #377 from warmbly/feat/admin-panel-backend
...
feat: admin panel upgrade (backend): remove unrouted admin handlers and retired permission bits, add sync, sends, jobs, fleet, transfer and insight endpoints, and a scheduled job registry every loop records to
2026-09-07 22:05:06 -07:00
Matthew Meszaros
6401feea7d
Merge branch 'feat/admin-panel-backend' into feat/admin-panel-frontend
2026-09-07 21:56:25 -07:00
Matthew Meszaros
9729f608c0
feat: address the CodeRabbit review on the admin backend PR by carrying the keyset boundary inside the sync cursor token instead of re-reading the cursor row's mutable updated_at, clearing only a throttle that is still active so an expired one is not reported as cleared, counting a medium-only UTM attribution in the acquisition with_channel total the way the channel list already does, releasing the exact dedicated assignment the handler read by id so a binding created meanwhile is never released, and returning the underlying failure from the warmup engagement poller, heartbeat sync, danger zone and org transfer housekeeping loops so scheduled_job_runs records an error instead of ok
2026-09-07 21:56:23 -07:00
Matthew Meszaros
800c4a062f
feat: frontend half of the admin panel upgrade: drop ten unused dependencies, the dead worker-load and plan requests, the stub pages and the retired permission names, fix the analytics client that sent the wrong query parameters and the mail test toasts that used an unmounted toaster, add a Cmd-K command palette with live user, organization, mailbox and worker search, a mobile nav drawer, document titles, a Sentry-wired route error boundary and permission gates on every route, replace polling with the realtime spine wherever an event exists, fold Analytics into Overview with a signups-by-channel card, merge System Status into Setup and health and Settings, Notifications and Effective limits into Configuration as tabs with redirects from every old path, add the Sync, Sends, Jobs, Fleet, Admins and Transfers pages plus API-key, webhook and transfer tabs on the organization page, mailbox reassignment on the worker page and abuse and action-history tabs on Warmup, and add the admin-panel docs page with every nav reference updated
2026-09-07 21:40:38 -07:00
Matthew Meszaros
c26300ae5e
feat: backend half of the admin panel upgrade: delete the unrouted provisioning, releases, plan, discount and enterprise-inquiry admin handlers with their service and repository methods, retire the six admin permission bits nothing gated as reserved placeholders so live bit positions and existing masks are unchanged and IsSuperAdmin checks the live set, add forty admin endpoints for mailbox sync governor state with clear-throttle and restart-backfill, in-flight send reservations, cross-workspace dead letters with replay, task failures, webhook delivery health with reclaim, fleet capacity, the control loops decision log, dedicated bindings with release and the routed convert-to-dedicated, operator-driven workspace export and import, per-organization API keys and webhooks, warmup invalid-token abuse and action history, and signups by acquisition channel, and add a scheduled_job_runs table (migration 000135) with a jobrun package that every backend and consumer loop now records through and a run-now request the owning process picks up within fifteen seconds
2026-09-07 21:40:38 -07:00
Matthew Meszaros
9c2c14675f
feat: trigger a marketing site rebuild on Cloudflare Pages so the PUBLIC_POSTHOG_KEY production variable is baked into the served pages
2026-09-07 20:33:55 -07:00
Matthew Meszaros
156a90c39a
feat: default every PostHog capture host (site build, dashboard runtime, backend analytics client) to PostHog Cloud US instead of EU, and update the configuration docs table, since the customers are primarily US-based
2026-09-07 20:28:31 -07:00
Matthew Meszaros
d7623214a6
Merge pull request #375 from joaoppa/feat/email-verify-env
...
Pass EMAIL_VERIFY_HELO_HOST and EMAIL_VERIFY_MAIL_FROM through to the services
2026-09-07 19:46:37 -07:00
Matthew Meszaros
02bcfd988c
Merge branch 'main' into feat/email-verify-env
2026-09-07 19:45:15 -07:00
Matthew Meszaros
1e815ce5e8
Merge pull request #374 from joaoppa/fix/reply-message-id-match
...
Match an inbound Message-ID with and without its angle brackets
2026-09-07 19:44:18 -07:00
Matthew Meszaros
95dc6947b1
feat: forward EMAIL_VERIFY_MILLIONVERIFIER_API_KEY alongside the HELO and MAIL FROM passthroughs in x-selfhost-env, and document all three in a pre-send verification section of .env.example, so every documented verifier setting actually reaches the container
2026-09-07 19:42:36 -07:00
joao-crm
07af7953f7
feat: pass EMAIL_VERIFY_HELO_HOST and EMAIL_VERIFY_MAIL_FROM through to the services so the in-house SMTP verifier can probe on a self-host, since the values otherwise sit in .env, never reach the container, and every contact comes back unknown asking for a public fully-qualified HELO hostname
2026-09-08 00:33:43 +00:00
joao-crm
e116912f4e
fix: log the reply-intent automation error in HandleNewEmail instead of discarding it with a bare underscore, since a silent failure there is indistinguishable from a reply that linked correctly and leaves nothing anywhere to explain why replied_at stayed NULL while stop_on_reply kept mailing someone who had answered
2026-09-08 00:31:45 +00:00
joao-crm
b99997b51f
fix: match an inbound Message-ID with and without its RFC 5322 angle brackets in GetTaskByMessageID, since the outbound stamp always stores the bracketed form while an In-Reply-To header arrives either way depending on the replying client, so every reply whose brackets had been stripped missed its task, replied_at was never stamped, stop_on_reply stayed inert and a contact who had already answered kept receiving the rest of the sequence, and an empty header matched one of the 151 tasks carrying an empty message_id and linked the reply to an arbitrary campaign
2026-09-08 00:31:45 +00:00
Matthew Meszaros
30f6cd5e2f
Merge pull request #373 from warmbly/fix/issue-371-public-images
...
fix: publish images a stranger can actually pull, and fail the release when they cannot
v0.4.1
2026-09-07 09:19:10 -07:00
Matthew Meszaros
de4b1e3aff
Merge remote-tracking branch 'origin/main' into fix/issue-371-public-images
...
# Conflicts:
# site/public/install.sh.sha256
2026-09-07 09:13:22 -07:00
Matthew Meszaros
cf78e394f7
Merge pull request #370 from warmbly/feat/issue-357-loopback-mail-relays
...
Connect a mail server on the instance's own machine (Proton Bridge and other local relays)
2026-09-07 09:11:56 -07:00
Matthew Meszaros
6542047745
Merge remote-tracking branch 'origin/main' into fix/issue-371-public-images
2026-09-07 09:07:22 -07:00
Matthew Meszaros
25484f70ab
Merge remote-tracking branch 'origin/main' into feat/issue-357-loopback-mail-relays
2026-09-07 09:05:11 -07:00
Matthew Meszaros
398bd263bd
Merge pull request #372 from warmbly/fix/issue-356-imap-folder-identity
...
Identify an IMAP folder by its name, not its UIDVALIDITY
2026-09-07 09:04:54 -07:00
Matthew Meszaros
c4bfbaf4a9
feat: address the review on the image publicity gate by passing build-push only the seven services that workflow actually publishes, since web, admin and cli have no :dev tag and would have warned falsely on every push to main, by taking the release tag through the step env instead of interpolating github.ref_name into the shell, and by no longer asserting the tag is fine when a pull is refused, because GHCR denies an unknown namespace exactly as it denies a private one, so a mistyped --registry now gets its own reading in the installer message, the troubleshooting table and the fork note, which also splits the personal and organization paths to the visibility setting
2026-09-07 09:02:23 -07:00
Matthew Meszaros
719a81866c
feat: gate the release on every published image being pullable with no credentials, because GHCR creates each package private and does not inherit the repository's visibility, so the authenticated imagetools inspect in create-release passed for four releases while ghcr.io/warmbly/warmbly/* returned unauthorized to everyone outside the org and no curl | sh self-host install could pull a byte ( #371 ); adds scripts/check-images-public.sh as the anonymous pull test, makes it the gate before create-release and the source of the digests in images.json, warns from build-push on main where a new service image first appears, and teaches install.sh to report a registry refusal as one instead of as a missing tag
2026-09-07 08:51:57 -07:00
Matthew Meszaros
8b757985f3
feat: address the review on the folder identity change by retiring a deleted folder's backfill floor with it, since a name is reusable and an inherited floor silently skips the next folder's history, by claiming a rename only when a UIDVALIDITY has exactly one missing folder and one new one, because two missing folders and one arrival cannot say which was renamed and guessing moves the wrong folder's mail, by deduplicating names before the folder cap rather than after so a name listed twice cannot spend a real folder's slot, by deleting on a legacy UIDVALIDITY-only event only when that number still names exactly one folder, and by moving the folder row and its mail in one transaction so a refused rename cannot leave the messages in a folder nothing renamed
2026-09-07 08:49:23 -07:00
Matthew Meszaros
d48d57464b
feat: hand the SMTP auth negotiation the normalized host, because net/smtp records the name NewClient was given as the server name and PlainAuth refuses to authenticate when its own host does not match it, so a bracketed IPv6 literal failed on an address it was correct about, and stop the bulk CSV calling an unencrypted row invalid while the deployment config is still loading, where a self-hosted instance would have accepted it
2026-09-07 08:44:32 -07:00
Matthew Meszaros
63070fb833
feat: address the review on the loopback mailbox mode by building every mail address with net.JoinHostPort, so an IPv6 literal keeps the brackets a host:port string needs and "::1" on 1143 stops dialling a host called "::1:1143" with no port, by moving the self-hosted half of the rule into the dialers as well as the connect form, because an organization archive exported from a self-hosted instance carries its mailboxes and an import must not hand a hosted worker one that dials its own loopback in the clear, by refusing an unencrypted CSV row on a hosted instance where the API would only reject it a moment later, and by saying in the docs that the port never selects the mode and that the whole 127.0.0.0/8 range counts
2026-09-07 08:27:54 -07:00
Matthew Meszaros
769a05aa90
feat: identify an IMAP folder by its name rather than by its UIDVALIDITY, which RFC 3501 never promised was unique across folders, so a mailbox on a server that stamps that number with the folder's creation time no longer loses the entire sync of every folder in a tree created in the same second, with the folder row keyed on (email_id, mailbox), each stored message stamped with its folder's name alongside the UIDVALIDITY generation its uid belongs to, a rename followed as a move that carries the mail and the cursor instead of orphaning both, and a changed UIDVALIDITY treated as what it is, the cursor going void
2026-09-07 08:21:17 -07:00
Matthew Meszaros
766bd3ae6a
feat: let a self-hosted instance connect a mail server on its own machine by adding a third mailbox security mode, "none", accepted only for a loopback literal and only where the worker shares a host with the relay, so Proton Bridge on 127.0.0.1:1143/1025 and a local Dovecot or Mailpit can be connected at all, with the rule enforced in onboarding and reauth validation, again by the worker against the peer it actually dialled rather than the name it was given, and hidden from the connect form on the hosted product where the worker is never the customer's machine
2026-09-07 08:07:40 -07:00
Matthew Meszaros
b6089b3bf3
Merge pull request #369 from warmbly/fix/issue-341-behavior
...
feat: give a hand-placed unsubscribe link a real anchor instead of the bare API URL
2026-09-07 06:40:11 -07:00
Matthew Meszaros
a5802b53cf
feat: address the review on the unsubscribe-link change by closing the tag scanner over quoted attributes in both the send path and the editor, so a > inside an attribute can no longer split a tag and rewrite the href that follows it into a dead link, and by scanning only the copy a plain-text step actually ships, resolving the campaign's inherited opt-out mode before warning, checking the subject too, and reporting an unreadable step list as a failed check rather than an empty scan
2026-09-07 06:34:45 -07:00
Matthew Meszaros
b25897fe67
feat: render a hand-placed {{.UnsubscribeLink}} as a real anchor labelled with the workspace's unsubscribe link text instead of shipping the bare signed API URL in the body, let the composer turn a text selection into that link so the wording stays the author's, and warn in the step editor and at preflight when a plain-text-only campaign carries a body opt-out link that has nowhere to hide its address
2026-09-07 06:34:45 -07:00
Matthew Meszaros
aa740ee4ed
Merge pull request #364 from warmbly/feat/cookieless-posthog-analytics
...
Cookieless PostHog analytics for the hosted properties, with first-party signup attribution
2026-09-07 06:05:58 -07:00
Matthew Meszaros
f826cf0803
feat: state at the acquisition filter itself that it selects on the presence of a record rather than of a UTM tag, so the query, the search model and the admin panel's labels and Channel column all carry the same definition of a direct signup
2026-09-07 06:02:10 -07:00
Matthew Meszaros
26c88d2426
feat: address the CodeRabbit review on the PostHog PR by dropping a referrer that is not a hostname instead of storing its query string or fragment, redacting email-shaped values out of every acquisition field before they reach the database or an analytics property, counting an invited signup which returned before the count was taken, hanging subscription_started off the persisted trial-to-paid transition so a redelivered webhook cannot report a second start, capturing the validated provider in both OAuth mailbox paths including Warmbly Cloud, making the two acquisition toggles mutually exclusive and naming them after what they actually select, and replacing the unsupported CNIL consent-exemption claim with what the guidance says and an explicit note that qualifying is a deployment-specific assessment this document does not make
2026-09-07 05:45:11 -07:00
Matthew Meszaros
ab62f24009
Merge main into the PostHog branch after the Sentry work landed
2026-09-07 05:25:47 -07:00
Matthew Meszaros
91a7ec516e
Merge pull request #367 from warmbly/feat/sentry-everywhere
...
Complete Sentry coverage across every service, optional everywhere, behind one Go wrapper
2026-09-07 05:25:31 -07:00
Matthew Meszaros
103fa00b55
feat: state in the configuration reference and the troubleshooting guide that a hosted form page takes its error-reporting environment from the forms service's APP_ENV, stamped into the page, rather than from WARMBLY_SENTRY_ENVIRONMENT like the dashboard and the admin panel do
2026-09-07 05:19:24 -07:00
Matthew Meszaros
8843e23138
feat: cover the acquisition record with live repository tests against a real schema, so the LEFT JOIN, the four columns added to the admin projection and the three channel filters are exercised rather than assumed, including that a second signup link cannot rewrite where a workspace came from and that the join does not fan out a row
2026-09-07 05:08:35 -07:00
Matthew Meszaros
7bd812d568
feat: warn once rather than silently when the analytics host is unreachable or rejects the key, so a wrong POSTHOG_KEY does not look like a quiet week, and cover the client with tests that assert the cookieless ingestion contract against a stub capture host: the sentinel distinct id, the mode flag, the three hash inputs, and that no event property names a person
2026-09-07 05:06:13 -07:00
Matthew Meszaros
b204737a05
Merge the Sentry branch (with main) into the PostHog branch
2026-09-07 05:01:38 -07:00
Matthew Meszaros
1239f1d191
Merge main into the Sentry branch, routing the new smtp reporting through the errs wrapper instead of the SDK import main added
2026-09-07 05:00:43 -07:00
Matthew Meszaros
bc4fcefc74
Merge pull request #368 from warmbly/fix/issue-343-behaviour
...
feat: fix hosted form creation failing with a not-null violation on allowed_domains
2026-09-07 04:59:26 -07:00
Matthew Meszaros
8f604921ab
Merge branch 'main' into fix/issue-343-behaviour
2026-09-07 04:49:48 -07:00
Matthew Meszaros
2cfb7fdd76
Merge pull request #366 from warmbly/feat/sync-error-lifecycle
...
feat(worker): let a fixed mailbox problem stop showing as an error
2026-09-07 04:49:22 -07:00
Matthew Meszaros
04dc83da38
Merge the Sentry review fixes into the PostHog branch, keeping the new JavaScript escaping for the PostHog runtime-config values too
2026-09-07 04:48:30 -07:00