Commit Graph
418 Commits
Author SHA1 Message Date
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 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 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 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 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 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 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 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 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 0a5e7947b4 feat: return a failed warmup retention delete from the worker so the bus redelivers it up to five times, re-key a Graph message in the map on every move so the sender copy's body can be dropped, never expunge a whole IMAP folder for one message (UID EXPUNGE, else MOVE to Trash, else refuse), build the two retention indexes concurrently in their own migrations 000193 and 000194, keep the dashboard stepper off 1 and 2 days, and describe retention as applying wherever the placement files warmup mail 2026-09-21 01:11:22 -07:00
Matthew Meszaros 1513419a2a feat: delete warmup mail from each mailbox once past a per-mailbox retention window (email_accounts.warmup_retention_days, else retention.warmup_mail_days, default 30) via a consumer sweep that retires the receipt and sender copy and a worker delete action that trashes on Gmail, deletes on Graph, expunges on IMAP and drops the stored body, prune per-message warmup records after retention.warmup_event_days, and count a warmup deletion as tampering only within 24 hours of arrival and never for a retired message, judging Gmail's Trash label on the same rule 2026-09-21 00:52:02 -07:00
Matthew Meszaros d6384d3c0e feat: make cross-tier warmup an exchange so a proven free mailbox writes back to the paying mailboxes that wrote to it, favour the inbox owed the most on every draw, cap what any inbox receives per day inside WarmupPartnerCandidates so a thin tier is neither starved nor flooded, and surface received counts in the mailbox drawer, warmup analytics and the API (#633) 2026-09-20 09:42:53 -07:00
Matthew Meszaros 7b93e48bd4 feat: make Archive mean something everywhere by keeping filed conversations out of every working unibox view except All mail and the Archive folder, read a mailbox address out of the raw From header so Awaiting reply stops missing every thread sent as "Name <addr>", file and mark read by thread id rather than by message id, and add per-row triage actions plus a multi-select selection bar to the conversation list 2026-09-20 07:16:35 -07:00
Matthew Meszaros 5b16a09b02 feat: write public objects without a canned ACL so a bucket whose ownership is owner-enforced still stores avatars, form assets, OAuth logos and email-body images, give the passkey login challenge its own per-IP budget separate from the one password sign-in draws on, and surface the API's own message at upload and passkey call sites instead of a generic sentence 2026-09-20 15:40:52 +02:00
Matthew Meszaros 0cac62b4f4 feat: stop a dashboard tab whose token went away in another tab from polling forever, by announcing the ended session from the api client's no-token path and by treating the auth token key disappearing from storage as the session ending in every other tab, so the app redirects to sign-in on the first failed poll instead of filing one unhandled AuthError a minute for the life of the tab 2026-09-20 11:20:38 +02:00
Matthew Meszaros addb956ad6 feat: shared TypeSafe client under internal/pkg/typesafe with inbox tagging phases 2 and 3 (hold, stop, task, suppress behind workspace switches, reversible ones on by default), labels seeded at workspace creation and by the follow-up sweep, premade inbox views (hot leads, needs a reply, follow up, declined, automated), typed reply classification and a reply_intent branch condition, an inbox agent draft gate, Advisor copy judgment with a cached editor re-check, warmup content lint, bounce cause classification that keeps a blocked address sendable, and per-form submission triage 2026-09-19 23:33:37 -07:00
Matthew Meszaros d7d6ecfbbf feat: stop a failing API from signing people out and then spinning forever, by ending a session only when the server actually refuses the refresh token rather than on any 500 or timeout, announcing that end so the app redirects to sign-in once, and standing the websocket's reconnect loop down when there is no token left for it to use 2026-09-19 19:53:54 +02:00
Matthew Meszaros dee54417a3 Merge pull request #612 from warmbly/feature/campaign-daily-send-view
feat: add a Today's sending plan to the campaign overview and feed the sidebar meter and wizard estimate from the scheduler's own clamps (issue #606)
2026-09-19 17:04:19 +00:00
Matthew Meszaros 89daae3f29 feat: make the send plan count a lead bound to a spent mailbox as waiting for it (leads.waiting_on_sender), charge a behaviour profile's spent budget and hourly ceiling to the plan rather than to hours or spacing, fold a foreign-timezone mailbox's 8pm close into its pacing, report the UTC budget day and keep the waterfall adding up when a cap was lowered after sends, read the pool's sends today in one query and cache the plan and workspace capacity for a few seconds, share one cold-ramp notice builder between the drawer and the plan, let the wizard estimate survive a counter miss, and stop a malformed plan payload from taking the campaign overview down 2026-09-19 09:45:03 -07:00
Matthew Meszaros ed50c278fa feat: make view-preference writes partial so a sort click before the layout loads keeps the saved columns (PUT /me/views/:view coalesces on the bound parameter and returns the row in one query, with a live test), validate column ids against each view's known columns and sorts against the contacts search's, reject a malformed custom sort on the bulk select-all and export paths too, key the browser's layout cache by user as well as workspace, commit a drag reorder once on drop instead of once per crossed row, save a Name-only layout as ["name"] so it cannot read as the default, start text sorts ascending from the Sort menu as a header click does, and share the pickers' checkbox square as a ui primitive 2026-09-19 09:38:59 -07:00
Matthew Meszaros 150dc7df6e feat: add a Today's sending plan to the campaign overview (GET /campaigns/:id/send-plan, derived through the scheduler's own gates: per-mailbox cap clamps, warmup graduation, health bands, other campaigns on the same mailbox, hours, spacing, window, plan allowance, new-lead cap and leads due, as a waterfall that adds up), feed the sidebar meter and the wizard estimate from the same clamps instead of summing configured caps, floor the campaign chain's next tick at the pool's spacing rather than one mailbox's whole gap, fold the compact Advisor strip to one line, add warmbly campaign plan and warmblyctl campaign plan, and document it (issue #606) 2026-09-19 09:31:46 -07:00
Matthew Meszaros 62201a2dd3 feat: let each member choose, reorder and persist the contact list's columns (custom fields included) and sort on any of them: a user_view_preferences table (migration 000187) behind GET/PUT/DELETE /v1/me/views/:view, a column registry that renders the contacts and campaign Leads tables from a saved layout, a Columns chooser with drag reorder and a Sort menu on both toolbars, click-to-sort headers, sort_by custom:<key> plus company and phone sorts in POST /contacts/search resolved once for Search and SearchIDs with a nullable keyset cursor, and the contacts guide, API reference, openapi, error codes and export-import docs updated 2026-09-19 09:24:17 -07:00
Matthew Meszaros 530b184748 Merge pull request #607 from warmbly/feature/two-factor-setup-flow
feat: rebuild two-factor setup in Settings > Security as a three-step wizard with a QR code, manual setup key and TOTP parameters, inline code errors, and recovery codes with download, copy and print; show enable date and remaining recovery codes, add POST /auth/2fa/recovery-codes to regenerate them, return two_fa_invalid_code on a mismatched code, and update the security guide, API reference, error codes and OpenAPI
2026-09-19 15:48:03 +00:00
Matthew Meszaros a1d3f3f3f1 feat: open a message details panel in the unibox from the recipient line, sender and an info icon, showing every From, Reply-To, To, Cc and Bcc address, sent and received times in the reader's zone, the mailbox, folder, size, Message-ID and In-Reply-To with one-click copy; summarise all recipients on the header line; add email_id and folder to GET /unibox/:id and document both 2026-09-19 08:22:14 -07:00
Matthew Meszaros 274ff888a5 feat: rebuild two-factor setup in Settings > Security as a three-step wizard with a QR code, manual setup key and TOTP parameters, inline code errors, and recovery codes with download, copy and print; show enable date and remaining recovery codes, add POST /auth/2fa/recovery-codes to regenerate them, return two_fa_invalid_code on a mismatched code, and update the security guide, API reference, error codes and OpenAPI 2026-09-19 08:15:31 -07:00
Matthew Meszaros 464ec521ca feat: present the $15 pool plan as the Warmup plan everywhere: rename the plan row (migration 000185), add it to the dashboard catalog so the header and billing overview name it, show the cloud tier in a self-hosted instance's header pill and a Plan section under Settings > Warmbly Cloud with upgrade and manage links to the cloud billing page, nudge on the mailboxes page only when the free pool is full, drop the self-host framing from the cloud's checkout dialog, paths panel and locked screen, rebuild the checkout dialog in the plan chooser's style, pitch Premium on deliverability from one shared benefit list, and update the billing and Warmbly Cloud guides and the pricing FAQ 2026-09-19 05:57:41 -07:00
Matthew Meszaros 2c886a3202 feat: decide the credential-validation timeout from the subscription context rather than the error's shape so a Redis socket timeout inside a live deadline stays an internal error, and require a synthetic mechanism and an empty stack as well as a default object string before dropping an exception so a message that stringified an object alongside real text is still reported 2026-09-19 13:00:23 +02:00
Matthew Meszaros acecd62c88 feat: scope mailbox disconnect and warmup lifecycle to the workspace rather than the member who connected the mailbox so an admin can act on every mailbox the list already shows them, evict a mailbox whose row is gone from every live worker when its provider errors arrive so a deleted mailbox stops calling the provider once a sync interval forever, subscribe before publishing the credential-validation job and classify a socket deadline as the retryable timeout it is, give the worker's validation reply its own budget so a slow mail host no longer loses a finished verdict, guard every global key handler against a keydown carrying no key, drop exceptions whose whole message is an object's default toString, make the Postgres pool size configurable, and record the CASA and security invariants in AGENTS.md 2026-09-19 12:49:46 +02:00
Matthew Meszaros b09ec39907 Merge pull request #599 from warmbly/chore/casa-al1-security-assessment
feat: complete the ADA CASA AL1 control set and ship the assessment evidence pack
2026-09-19 06:39:12 +00:00
Matthew Meszaros e668a2a36b feat: complete the ADA CASA v2.1.1 AL1 control set across authentication, sessions, access control, cryptography, input validation and configuration, adding a breached-password denylist and per-account login throttling, enforced multi-factor authentication on the admin panel, step-up confirmation before an action that mints a lasting credential, purpose-scoped session tokens, single-use TOTP steps, tenant verification on every cross-referenced identifier, security headers on every surface, encrypted webhook signing secrets, per-organization idempotency, PKCE and a minimal two-scope Gmail consent on the mailbox OAuth flow, bounded spreadsheet and archive decoding, a patched Go toolchain with govulncheck in CI, and the evidence pack under compliance/casa 2026-09-19 08:18:35 +02:00
Matthew Meszaros 6428e6b3e9 Merge pull request #594 from warmbly/feature/disable-google-oauth-new-mailboxes
feat: route new Gmail mailboxes through a guided app-password connect instead of Google sign-in, behind BOX_GOOGLE_OAUTH_CONNECT, leaving existing OAuth mailboxes sending and re-authorizable
2026-09-19 06:11:15 +00:00
Matthew Meszaros 49acd51b64 feat: stop one recurring fault burying error tracking by reporting it once per five minutes with the count it stands for, keep a cache outage from answering every signed-in request with a 500 and from taking realtime down by treating an unreachable Redis as a miss and the websocket handshake nonce nothing reads as best-effort, answer a 5xx with a sentence the reader can act on while the call site's own words go to the log against the same request id, prefer the API's own message over the HTTP class in the admin and dashboard clients, and name the fix on a schema registry refusal, an SES sandbox rejection and a mailbox check that could not be run 2026-09-19 07:39:40 +02:00
Matthew Meszaros ee46cb49e8 feat: route new Gmail and Google Workspace mailboxes through a guided three-step app-password connect over smtp.gmail.com and imap.gmail.com instead of Google sign-in, behind BOX_GOOGLE_OAUTH_CONNECT (off by default) and announced to clients as gmail_oauth_connect on /auth/config, refusing a new gmail OAuth start with 403 mailbox_gmail_oauth_disabled in both the direct and Warmbly Cloud broker paths while leaving mailboxes already connected that way sending, syncing and re-authorizable 2026-09-18 22:06:09 -07:00
Matthew Meszaros e37c5053c2 feat: make warmup refunds atomic, count confirmed partner diversity in local and cloud mailbox views, and document cloud-safe mailbox deletion 2026-09-17 21:15:28 -07:00
Matthew Meszaros 177a0817c4 Merge remote-tracking branch 'origin/main' into fix/closiqode-reported-issues 2026-09-17 21:00:48 -07:00
Matthew Meszaros 68c3676717 feat: keep warmup out of the customer's own mailbox and off their deliverability record: Gmail foldering now removes INBOX and SENT instead of only labelling, sent copies and reply-backs are filed in both directions, filing is configurable per mailbox (folder/inbox/archive via warmup_placement + warmup_folder, migration 000177), IMAP relocates a moved message by Message-ID so read/important stop no-opping, and a warmup send's bounce notice no longer lands in the unibox or suppresses a pool partner 2026-09-17 20:46:03 -07:00
Matthew Meszaros 8ee1c50a1b feat: make a failed SMTP send name the step and the cause behind it instead of one bare SERVER_UNREACHABLE sentinel, give a refused warmup send its day back so sent_today can no longer climb past the target while the cap frees the slot, revoke a mailbox's Warmbly Cloud enrollment when it is deleted so the pool stops holding its password, and prefer warmup partners outside the sender's own workspace while showing the partner diversity a mailbox is actually getting (#574, #575) 2026-09-17 20:02:37 -07:00
Matthew Meszaros b733d09f89 feat: make inbox follow-up labels safe for manual labels and automated mail 2026-09-17 04:57:52 -07:00
SUMAN JANA 3b8761d361 feat(inbox): explain every tag on hover, label live mail, and keep follow-ups working with no external service 2026-09-17 04:57:52 -07:00
Matthew Meszaros dcf26a2361 feat: make automatic inbox tagging atomic live and reviewable in production 2026-09-17 04:57:24 -07:00
SUMAN JANA 260898bf20 feat(inbox): automatic tagging and relevance scoring for inbound mail, optional and off by default 2026-09-17 04:57:24 -07:00
Matthew Meszaros ae1a324801 Merge pull request #562 from rocker1166/feat/direct-mail-analytics
feat: add accurate direct-mail analytics and opt-in engagement tracking
2026-09-17 11:47:50 +00:00
Matthew Meszaros 93a0e39371 Merge pull request #568 from warmbly/fix/reported-issue-resolution
feat: preserve binary dashboard API responses so campaign lead exports remain downloadable
2026-09-17 11:28:29 +00:00
Matthew Meszaros 7402898c0d feat: preserve binary dashboard API responses so campaign lead exports remain downloadable (#565) 2026-09-17 04:24:52 -07:00
SUMAN JANA 2134c7a143 feat(analytics): report on mail written by hand, with opt-in open and click tracking per mailbox 2026-09-17 10:26:25 +00:00
SUMAN JANA ebbe432d2c feat(unibox): search people and partial words, and offer to widen a search that found nothing in the current scope 2026-09-17 10:20:17 +00:00
Matthew Meszaros 0f5ca71155 feat: correct mailbox sending metrics and workspace analytics across dashboard surfaces 2026-09-16 21:44:42 -07:00