Matthew Meszaros
331aeb4db3
Merge origin/main into feature/open-tracking-device-client
2026-09-23 02:06:04 -07:00
Matthew Meszaros
714bd6fe11
Merge pull request #658 from warmbly/fix/millionverifier-reverification-source
...
feat: queue a contact re-verify ahead of the backlog whatever its evidence or manual verdict while the current verdict stands, count MillionVerifier renewing subscription credits, let a paid verifier's fresh answer outrank mail older than 30 days, and show Verified with MillionVerifier plus a live Re-verify button on the contact Deliverability card
2026-09-23 05:40:08 +00:00
Matthew Meszaros
f632a2fe6f
feat: document that a click carries client_type app when a mail app made the request itself, in the timeline origin, the campaign surfaces breakdown and the OpenAPI schema
2026-09-22 22:39:59 -07:00
Matthew Meszaros
727ee432cb
feat: keep a manual verdict set while a requested verification check runs, restore a lapsed evidence override from the row's own check status so a check landing mid-rescore wins, give member re-verify requests at most half of each verification batch while the backlog has work, and compare rescores against the stored verification status
2026-09-22 22:35:10 -07:00
Matthew Meszaros
19eb68ecd5
feat: re-read stored opens and clicks by the live origin rules in batched, resumable consumer passes under an advisory lock instead of a boot-time SQL backfill, never read a proxy string on a click as Apple Mail or Gmail, keep recognising Outlook, Proton Mail, Yahoo Mail and HEY by name, show the mail app behind a click in the expanded timeline row, and note that other iOS mail apps count as Apple Mail
2026-09-22 22:31:27 -07:00
Matthew Meszaros
3cf743a0cd
Merge remote-tracking branch 'origin/main' into fix/millionverifier-reverification-source
2026-09-22 22:17:04 -07:00
Matthew Meszaros
72272cfbe7
feat: queue a contact re-verify ahead of the backlog whatever its evidence or manual verdict while the current verdict stands, count MillionVerifier renewing subscription credits, let a paid verifier's fresh answer outrank mail older than 30 days, keep what each check said, and show Verified with MillionVerifier plus a live Re-verify button on the contact Deliverability card
2026-09-22 22:17:04 -07:00
Matthew Meszaros
eca5416a6f
Merge origin/main into feature/open-tracking-device-client
2026-09-22 22:16:51 -07:00
Matthew Meszaros
d44fd38d90
feat: show the device and mail client behind every open and click (iPhone · Apple Mail app, Windows PC · Outlook app, Gmail · device hidden) from a new mailclient detector that recognises Gmail, Yahoo, Apple MPP, HEY, Fastmail and Seznam image proxies instead of reporting their fake browser and data-centre location, record app vs webmail and device_hidden on the open and click logs, backfill stored opens by the same rules, let the logs accept the scanner reason, and surface it on the contact timeline, a new How they read overview, the live campaign feed, recent activity, the campaign Device breakdown (surfaces), webhooks and realtime
2026-09-22 22:16:51 -07:00
Matthew Meszaros
c75b3b3b0d
Merge pull request #651 from warmbly/feature/import-existing-custom-fields
...
feat: map import columns onto the workspace's existing custom fields
2026-09-23 04:45:33 +00:00
Matthew Meszaros
543e982d52
feat: map a column of addresses to Email before existing custom fields can claim it, and ask TypeSafe about import columns only when the Email column has a real header of its own so a contact's row is never read as headers
2026-09-22 21:42:27 -07:00
Matthew Meszaros
7813d77efc
feat: send nothing to TypeSafe when an import's header row holds an address, date or number, skip placeholder Column N headers, accept only the options each column was offered, never infer Subscribed, Categories or Email, prefer an exact existing field name before a folded match on the server as the client does, compute value kinds once per preview, and make Enter in the empty field search a no-op
2026-09-22 21:29:22 -07:00
Matthew Meszaros
c35e05c9eb
Merge pull request #652 from warmbly/feature/sending-window-timezone
...
feat: give each workspace a timezone that mailbox warmup and campaign sending windows follow by default, with a Timezones control centre on Settings > Profile and the first-email delay moved into campaign Settings
2026-09-23 03:42:59 +00:00
Matthew Meszaros
e4de4b35dd
Merge pull request #653 from warmbly/fix/api-bind-errors-650
...
feat: name what is wrong with a request body, and make every CLI command send the body its endpoint binds
2026-09-23 03:40:32 +00:00
Matthew Meszaros
25652476ba
feat: store the timezone a new workspace is created with, return a mailbox's own and workspace timezone from its PATCH, pin following campaigns and mailboxes to the workspace zone on the 000198 down migration, page through every campaign and mailbox before the Timezones summaries and Set all, let the campaign wizard and onboarding pick up a workspace zone that loads late, and qualify the workspace timezone copy to mailboxes that follow it
2026-09-22 20:38:18 -07:00
Matthew Meszaros
9b47f91e89
feat: name a request-body time that is not RFC 3339 in the bind refusal (documented in the error-codes table) and cover warmbly campaign add-step and edit-step in the CLI body test against the sequence update struct
2026-09-22 20:35:15 -07:00
Matthew Meszaros
0e97ccc88d
feat: make every warmbly CLI command send the body its endpoint binds (contact create, mailbox send and set-tracking, form set-domain, campaign test, task due dates, advisor snooze, warmup appeal, integration push, oauth-app scopes, corrected inbox and suppression examples, automations and webhook edit documented as full writes), pinned by a test that decodes each body strictly into the server struct, and let POST /v1/campaigns/:id/steps take the PATCH fields as an optional body so add-step no longer creates a blank step
2026-09-22 20:26:34 -07:00
Matthew Meszaros
c1acded32c
feat: place import columns no header matched with one TypeSafe Jev choice call per preview (headers, value kinds and field names only, never a cell; 0.70 confidence floor, one column per field, 4s fallback to the deterministic mapping), map a column of addresses to Email by its values, report inferred_columns on both import previews, mark them in the mapper, and document what is sent in data control
2026-09-22 20:22:40 -07:00
Matthew Meszaros
6cadcc725d
feat: answer every public request-body bind failure with a 400 that names the problem (empty body, JSON syntax error with its byte offset, wrong JSON type for the body or a named field, missing or out-of-range fields by json key) instead of a blanket malformed-JSON message or a 500, accept a single contact object on POST /v1/contacts as the CLIs send it, and drop the API-key lookup cache whose 300ns TTL made it a Redis round trip that saved nothing
2026-09-22 20:21:41 -07:00
Matthew Meszaros
38f8382cf8
Merge remote-tracking branch 'origin/main' into feature/sending-window-timezone
2026-09-22 20:13:55 -07:00
Matthew Meszaros
45c045a5bb
feat: give each workspace a timezone that mailbox warmup and campaign sending windows follow by default (organizations.timezone, migration 000198), let campaigns and mailboxes follow it or pin their own, add a Timezones control centre on Settings > Profile with inline per-campaign and per-mailbox zones, default new workspaces and the campaign wizard to the browser zone, expose effective_timezone on campaigns, and move the first-email delay from the Schedule tab and wizard into campaign Settings > First email
2026-09-22 20:13:55 -07:00
Matthew Meszaros
2bdbfe5f68
feat: list the workspace's existing custom fields in the contact import and Google Sheets column mapper (searchable, with inline create), auto-map headers to existing fields ignoring case and separators in one shared server-side suggester, flag new fields, near-duplicates and columns sharing a field, and document the matching
2026-09-22 20:07:57 -07:00
Matthew Meszaros
d11b7822c7
Merge pull request #648 from warmbly/feature/imap-folder-exclusion
...
feat: let an IMAP mailbox exclude folders from sync so a folder another tool fills never reaches the unified inbox
v0.5.12
2026-09-22 12:50:42 +00:00
Matthew Meszaros
1520da2fa8
feat: look up moved mail against each skipped folder with one SELECT per folder and cap the reconciliation by searches rather than rows, never settle a row whose lookup failed, refuse a skip name that is a saved folder the matcher keeps and purge only what it will stop following, drop the map entries of parked arrivals too, ignore UIDVALIDITY 0 when matching a rename into the skipped subtree, carry the listing memo and pending mark across a rename, ask before ticking a folder in the drawer since imported mail is removed, share one inbox-deleted publisher between the consumer's removal paths, and say in the CLI help that an emptied list does not restore removed mail
2026-09-22 05:45:38 -07:00
Matthew Meszaros
ee9f5bf84e
feat: make every skipped-folder purge drop the message map entries and parked arrivals with the rows so a message can be imported again if it moves back, persist each folder's delimiter (migration 000197) so the backend purge matches the same subfolders and case the worker skips, fail the mailbox load closed when the skip list cannot be read, mark a folder renamed into the skipped subtree as skipped, never remove a row re-fetched this pass, cap the reconciliation at 50 searches per pass and continue next pass, publish one EMAIL_DELETED after a folder purge, and resolve the account once for GET /emails/:id/sync
2026-09-22 05:33:56 -07:00
Matthew Meszaros
ad353b385a
feat: key the IMAP departure check on the previous listing's count and UIDNEXT per folder instead of the stored cursor, since a walked folder's cursor now advances to the SELECT view and a server whose view lags its STATUS would read as departures every pass; the fake listing in the worker tests returns a copy so a second pass still sees the skipped folder
2026-09-22 05:20:06 -07:00
Matthew Meszaros
9a7ef088cf
Merge remote-tracking branch 'origin/main' into feature/imap-folder-exclusion
2026-09-22 05:17:37 -07:00
Matthew Meszaros
5a967cc3ce
feat: let an IMAP mailbox exclude folders from sync (email_accounts.sync_skip_folders, migration 000196) so a folder another tool fills never reaches the unified inbox: the worker drops skipped folders and their subfolders before the walk, retires an already-synced one with its stored mail, and removes mail that later moves into one only when its Message-ID is found there; PUT /emails/:id/sync and the drawer's Sync card set the list, GET reports it with the server's folder list, warmbly mailbox skip-folders mirrors it, with docs, OpenAPI and error-code invalid_sync_folder
2026-09-22 05:15:49 -07:00
Matthew Meszaros
00a0a67e0b
Merge pull request #647 from warmbly/fix/unibox-sent-folder-messages
...
feat: advance IMAP folder sync cursors to the selected view and drop the message-map entry when NEW_EMAIL fails to publish (#645 )
2026-09-22 12:03:11 +00:00
Matthew Meszaros
fb453921db
feat: keep a failed message-map rollback pending on the mailbox and retry it at the start of every IMAP, Gmail and Graph sync pass, skipping the pass until it succeeds so an unpublished arrival is never read as known
2026-09-22 04:59:27 -07:00
Matthew Meszaros
9009c6ec24
feat: build inbound bounce and complaint reports before NEW_EMAIL but publish them only after it succeeds, so a message re-offered after a failed arrival publish does not apply its deliverability report twice
2026-09-22 04:49:26 -07:00
Matthew Meszaros
4a6f0c17c7
feat: advance each IMAP folder's sync cursor to the SELECT view its search ran against instead of the earlier LIST-STATUS, so Sent copies appended between the two are no longer skipped, and drop the message-map entry when NEW_EMAIL fails to publish so an unpublished message is re-offered instead of read as known ( #645 )
2026-09-22 04:39:18 -07:00
Matthew Meszaros
c01b7b8dd5
Merge pull request #646 from warmbly/fix/permission-denied-error
...
feat: gate the dashboard version pill's update actions on a two-factor session and give admin_mfa_required its own dialog variant pointing at Settings > Security
2026-09-22 10:45:58 +00:00
Matthew Meszaros
62ea7ef906
feat: gate the dashboard version pill's update actions on a session that presented a second factor, carry session_mfa_verified on the web User model, pass the API error code into the permission-denied event and give admin_mfa_required its own two-factor dialog variant linking to Settings > Security instead of the Roles & access advice, and document the rule on the updates page
2026-09-22 03:42:56 -07:00
Matthew Meszaros
cc898cc040
Merge pull request #641 from warmbly/fix/password-change-session-revocation
...
feat: end every session on a password change and answer with a fresh token pair for the calling device
v0.5.11
2026-09-21 12:03:31 +00:00
Matthew Meszaros
e1989f9116
Merge remote-tracking branch 'origin/main' into fix/password-change-session-revocation
...
# Conflicts:
# internal/app/auth/reset_password.go
2026-09-21 04:56:50 -07:00
Matthew Meszaros
711e89f9fe
Merge pull request #643 from warmbly/fix/sso-link-existing-password-account
...
feat: attach a provider identity to an existing password account only after its password is presented
2026-09-21 11:33:46 +00:00
Matthew Meszaros
eac3f6d615
Merge remote-tracking branch 'origin/main' into fix/sso-link-existing-password-account
...
# Conflicts:
# docs/content/docs/guides/security.mdx
2026-09-21 04:28:20 -07:00
Matthew Meszaros
db0f0c6132
feat: carry the caller's session into ReissueSession from the request instead of looking it up, evict every revoked session from the cache and write a revoked tombstone where the delete is refused, and answer a password change whose reissue failed with the distinct 409 password_changed_sign_in_again that the dashboard turns into a sign-out, documented in error-codes, the endpoint reference and OpenAPI
2026-09-21 04:23:46 -07:00
Matthew Meszaros
ff2204f8ef
feat: route the sso_link completion through the one login path (completeLogin) so the ban check, the 2FA gate, login recording and device memory apply before a parked identity is attached, carry the session provider into every 2FA challenge so a Google or Apple sign-in on a 2FA account is recorded as such, make IdentityRepository.Link report a pair another account holds as ErrIdentityTaken instead of silently updating nothing, refuse a spent address budget before charging a link try, drop the dead expiry check and the duplicate link fields on the web Session model
2026-09-21 03:57:29 -07:00
Matthew Meszaros
e0db7d3c72
Merge pull request #642 from warmbly/fix/reset-token-invalidation-after-password-change
...
feat: refuse a password reset link issued before the password was last changed
2026-09-21 10:50:49 +00:00
Matthew Meszaros
ae143caf3f
Merge pull request #644 from warmbly/fix/account-name-validation
...
feat: validate every person, workspace and company name through internal/pkg/displayname on each write path and render stored names safely in platform email
2026-09-21 10:47:13 +00:00
Matthew Meszaros
4578980b34
feat: fall back to My Organization when the first name is blank in displayname.DefaultWorkspace, and only treat a scheme as a link when a non-space follows its colon so names like Big Data: EU pass in both the Go and web validators
2026-09-21 03:42:17 -07:00
Matthew Meszaros
a1b7a8ad9b
feat: attach a parked federated identity only after the ban check and, on a 2FA account, only once the second factor passes by carrying it through the 2FA pending record into twofa.VerifyLogin, charge each sso_link password attempt atomically before the check, treat an identity a parallel challenge already linked as a re-login instead of a refusal, ask for no password when the identity cannot be linked, end an exhausted or expired challenge with sso_link_expired so the dashboard returns to the email step, and document the code in error-codes, the API reference and OpenAPI
2026-09-21 03:35:17 -07:00
Matthew Meszaros
9426c0da51
feat: validate every person, workspace and company name through internal/pkg/displayname on each write path (profile, onboarding, setup, IdP sign-in, org create and rename, org import, enterprise inquiry, admin testers, warmblyctl) with a 400 invalid_name code, render stored names in platform email through the same rules, mirror them in the web forms, check the org slug format, and document the rules in error-codes, security and AGENTS.md
2026-09-21 03:34:03 -07:00
Matthew Meszaros
0f60fd9b84
feat: attach a Google, Apple or OIDC identity to an existing password account only after that account's password is presented: resolveFederatedUser parks the sign-in as link_required with a single-use sso_link pending token, POST /auth/sso/link checks the password against the provider-asserted address on the sign-in failure budget and links then issues the session through finishLoginAs, the dashboard collects it on a new login step, and the API reference, endpoints list, security guide and OpenAPI spec describe the third login result
2026-09-21 03:25:29 -07:00
Matthew Meszaros
36eb5e72e9
feat: stamp users.password_changed_at on every password write and refuse a password reset link issued at or before it, so a link requested earlier dies when the password is changed from settings, by another reset link or by warmblyctl, with the rule documented on the reset endpoint and the security guide
2026-09-21 03:23:14 -07:00
Matthew Meszaros
7626aaefe4
feat: end every session on a password change, the calling one included, and answer POST /auth/me/password with the token pair of a fresh session for that device; the dashboard stores the new pair, and the endpoint reference, security guide and OpenAPI document the response
2026-09-21 03:18:27 -07:00
Matthew Meszaros
f11df9268f
Merge pull request #640 from warmbly/fix/admin-list-pagination
...
feat: make every admin panel list page past the first and filter by id, and report failed admin requests
2026-09-21 09:29:48 +00:00
Matthew Meszaros
4dde9708c9
feat: honour an ascending created_at sort on the admin users list with a matching id tiebreak, and record an admin API failure whose call omitted the method as GET, the method axios sent
2026-09-21 02:24:36 -07:00