Commit Graph
442 Commits
Author SHA1 Message Date
Matthew Meszaros b9b13344b4 feat: remember the refresh token each OAuth grant last rotated away from (migration 000261) and revoke the grant when that token is presented again, whether in a race or after the rotation finished 2026-10-04 03:49:13 -07:00
Matthew Meszaros a6b72299ae feat: store workspace invitation tokens only as SHA-256 digests (migration 000260 converts pending ones), mint a separate link token each time a manager copies an invite link so the emailed link keeps working, and resolve invitations by either digest 2026-10-04 03:37:41 -07:00
Matthew Meszaros ec77a3e3d2 feat: key unibox snoozes by organization as well as user and thread (migration 000259 backfills from each thread's mailbox organization), scope every snooze read and write and the org-transfer scope to it, and resolve unibox list cursors only within the caller's organization or user 2026-10-04 03:22:09 -07:00
Matthew Meszaros 043d877b58 feat: let one organization per instance verify a custom tracking host across mailbox and campaign tracking domains (database trigger plus 409 tracking_domain_taken on save, verify, sending-domain apply and bulk setup; the sweep leaves a held host unverified), and re-verify tracking domains on the destination after a workspace import 2026-10-04 03:22:02 -07:00
Matthew Meszaros e852a106a9 feat: serve every node-only internal route (data keys, message map, sync lookups, worker config, fleet heartbeat) on NODE_BROKER_TOKEN and leave only tracked links, domain redirects, page hits and forms on INTERNAL_API_TOKEN, render nodes only the node token when one is set, and have the installer generate a distinct NODE_BROKER_TOKEN for new installs and its own UPDATER_TOKEN 2026-10-04 03:21:58 -07:00
Matthew Meszaros f9a2e9af10 feat: state dependency floors, the Sentry HTTP client choice and auth cache budgets as the guarantees they hold, with no advisory identifiers or past behaviour in comments 2026-10-04 03:00:56 -07:00
Matthew Meszaros 33f99b97e5 feat: end every session on sign-out-everywhere through RevokeOtherSessions with cache eviction after commit, refuse refresh on a revoked session, close a user's realtime sockets on any session revocation via a SESSIONS_REVOKED event, and verify tokens without a purpose claim for no flow 2026-10-04 02:56:18 -07:00
Matthew Meszaros 9f7d45a1fb feat: charge every password, emailed-code and TOTP attempt atomically before comparing it (Redis INCR+expire script) with a per-account TOTP budget across challenges, put the signed-in password change on the reauth budget, set the per-account login limit to 50 per hour, give tester passwords an expiry (users.password_expires_at, migration 000257) and clear them plus every session on revoke, mint warmblyctl reset links with the password-reset purpose, expire fleet join tokens (7 days default, 30 max, reusable inside the window), derive captcha from the resolved Turnstile secret, refuse weak bootstrap argon2id hashes, make registration codes single-use, rate-limit the v1 invitation lookup, and draw RIDs and user codes without modulo bias 2026-10-04 02:52:22 -07:00
Matthew Meszaros 1a483cb27f Merge remote-tracking branch 'origin/main' into feature/integrations-page-redesign
# Conflicts:
#	internal/app/integration/service.go
#	web/src/app/app/integrations/page.tsx
#	web/src/hooks/useDocumentTitle.ts
2026-10-04 00:45:48 -07:00
Matthew Meszaros 4bc2417618 Merge remote-tracking branch 'origin/main' into feature/integrations-page-redesign
# Conflicts:
#	docs/content/docs/api/error-codes.mdx
#	docs/content/docs/guides/integrations.mdx
#	web/src/app/app/integrations/page.tsx
2026-10-04 00:32:54 -07:00
Matthew Meszaros 7555f641a5 feat: turn Integrations into an app store with sidebar views, search, sorting and filters, list community apps by link until featured or widely installed, redesign the OAuth app registration dialog, store app logos re-encoded per app like the workspace logo, and add admin suspension, token revocation and developer blocks for OAuth apps 2026-10-04 00:31:52 -07:00
Matthew Meszaros 6b7e254e56 feat: add native Salesforce sync with Lead and Contact matching and links, a leased activity outbox that logs sends, replies, bounces, opt-outs and meetings as Tasks, Lead Status and Email Opt Out writeback, a pull loop with CRM pause rules, list view and Campaign imports, sandbox and My Domain OAuth that refreshes expired sessions, a Salesforce settings page with contact and inbox cards in web, and docs 2026-10-04 08:54:27 +02:00
Matthew Meszaros 900806f328 feat: make HubSpot sync jobs lease-safe with per-claim tokens and one-at-a-time claims, log each email once across retries with the interested-reply outcome as its own job, move merged HubSpot contacts in a transaction, run backfill as one cursor job that skips refused records, rebuild the HubSpot client after a reconnect, accept a single portalId on card requests, and remember the Warmbly property group only after it exists 2026-10-04 08:18:16 +02:00
Matthew Meszaros 37e91fc89f feat: add HubSpot CRM mode where HubSpot deals, pipelines, tasks, notes and owners are mirrored and written through, sends and replies log as HubSpot emails, exit rules hold campaigns, list import, sync health, setup wizard in web, HubSpot app project with record card and workflow action, and docs 2026-10-04 07:47:51 +02:00
Matthew Meszaros 4957214431 feat: redesign the Integrations page with search, category chips, recommended and popularity-ranked integrations, add a community app directory where OAuth apps are published unverified by link and verified in a new admin review queue, with docs and OpenAPI 2026-10-03 09:24:34 -07:00
Matthew Meszaros 61d16dfe9a feat: add Warmbly for Slack with the AI assistant in DMs, mentions and the assistant pane, unified inbox threads with reply, AI draft and lead actions, per-category notification routing and DMs, account linking, dashboard Slack settings, manifest and docs 2026-10-03 15:35:11 +02:00
Claude bb580164e6 feat: compute each active campaign's send plan in a background snapshotter (internal/app/campaign) stored in campaign_send_plan_snapshots so GET /campaigns/:id/send-plan serves a stored snapshot instead of walking the planner on the request 2026-10-03 11:04:22 +00:00
Claude a77f25ce4f feat: bound campaign recipient selection via a cheap ordered-candidate enumeration plus chunked routedLeadsQuery hydration in FindRoutedPairs, with a campaign_leads(campaign_id,position,contact_id) index 2026-10-03 06:40:38 +00:00
Matthew Meszaros 39285173f1 feat: never move the follow-up changed-thread watermark past a page it failed to read, step past a failing page, thread or unreadable mailbox only after three failures spanning at least an hour, fence sweep state saves on the lease owner alone and log a takeover, clear a stale failure count once the walk moves on, and note that imported unibox rows keep their source ingested_at 2026-10-01 23:20:45 -07:00
Matthew Meszaros 0bcbab91ac Merge remote-tracking branch 'origin/main' into fix/inbox-follow-up-sweep-paging
# Conflicts:
#	internal/app/orgtransfer/spec.go
2026-10-01 23:20:32 -07:00
Matthew Meszaros b2310f5d4b feat: renumber the inbox follow-up sweep migrations from 000243-000247 to 000245-000249 so they follow 000243_campaign_task_subject and 000244_unibox_rail_layout on main instead of colliding with them 2026-10-02 08:15:42 +02:00
Matthew Meszaros 244b2e6613 feat: renumber the unibox rail layout migration to 000244 after main took 000243 2026-10-01 22:59:25 -07:00
Matthew Meszaros b8a8ec4743 Merge remote-tracking branch 'origin/main' into feat/unibox-rail-layout-per-member 2026-10-01 22:58:31 -07:00
Matthew Meszaros 1aeb45773b feat: check threads changed since each workspace's last follow-up pass by when the message was stored (new unibox_emails.ingested_at) or its verdict written, behind a persisted watermark, give each workspace's sweep a single leased walker whose saves are conditional on the lease, stop the cursor at the last thread whose label was written and step past a page or thread after three consecutive failures, keep the cycle running when the changed-thread check fails, and accept warmblyctl inbox-tag follow-ups --limit as a deprecated no-op 2026-10-01 22:52:43 -07:00
Matthew Meszaros 945fadb260 Merge pull request #788 from warmbly/fix/ab-variant-thread-subject
feat: thread campaign follow-ups on the A/B arm each contact was actually sent by recording every send's rendered subject on campaign_tasks.subject (migration 000243) and reusing it verbatim in ThreadParentForLead, and list the opening step's variant subjects in the follow-up composer
2026-10-02 05:46:33 +00:00
Matthew Meszaros bce968646f feat: thread campaign follow-ups on the A/B arm each contact was actually sent by recording every send's rendered subject on campaign_tasks.subject (migration 000243) and reusing it verbatim in ThreadParentForLead, and list the opening step's variant subjects in the follow-up composer 2026-10-01 22:41:11 -07:00
Matthew Meszaros 8f89c36756 feat: refresh the open conversation for inbox events that name only a message id, give unibox refresh keys added just before a max-wait flush a trailing flush, cancel a shared unibox overview computation once its last caller leaves and bound it and GET /unibox/overview at 30s, include scheduled sends in the coalesced inbox refresh, and word the slow pgxpool acquire log as acquire time rather than saturation 2026-10-01 22:41:00 -07:00
Matthew Meszaros 54a05027cc feat: save the unibox scope rail's favorites, row and section order and hidden rows to the member's account per workspace through a unibox_rail view with a validated layout on /me/views, synced from the dashboard store so every device shows the same rail while folds and widths stay per browser 2026-10-01 22:37:23 -07:00
Matthew Meszaros 6758bc8035 feat: page the hourly inbox follow-up sweep through each mailbox newest first in bounded keyset pages that resolve thread state only for the threads on the page, resume each workspace's cycle from a cursor persisted in inbox_follow_up_sweeps under a per-pass time budget, check recently active threads first every pass, and add the unibox_emails (email_id, internal_date, id) index the walk reads 2026-10-01 22:29:41 -07:00
Matthew Meszaros dc2cffd786 feat: coalesce and dedupe unibox realtime refreshes in useRealtimeEvents, forward TanStack Query abort signals through every unibox client and the shared Request helper with cancellations kept out of error handling, share one organization-scoped unibox overview computation per second with in-flight dedup and write-time invalidation, bound interactive unibox reads at 30s including pool wait, and log slow pgxpool connection acquisitions 2026-10-01 22:25:53 -07:00
Matthew Meszaros e920b61371 feat: clear a reported warmup send failure only on a confirmed delivery, word the not-loaded send failure as a possible cause, leave placement seeds out of the adopted-mailbox warmup repair, and describe send_failure as a failure reason in the API schema 2026-10-01 03:20:41 -07:00
Matthew Meszaros a844c506a0 feat: make Warmbly Cloud warmup start and explain itself for linked mailboxes: apply the instance's warmup settings, start warmup on adopted mailboxes, read the warmup day mask Monday-first, report the last refused warmup send and the partner cap to the instance and dashboard, hand changed SMTP credentials to the cloud, refuse cleartext mailboxes at enrollment, and show cloud warmup with the warming animation and a clear Cloud marker 2026-10-01 03:01:36 -07:00
Matthew Meszaros 588591684b feat: send a unibox template's HTML body as written by giving the compose and reply composers an HTML mode, keep that HTML in compose and reply drafts, and render the text part server-side for HTML-only sends 2026-09-30 20:46:22 -07:00
Matthew Meszaros 371eafd9da feat: renumber the domain redirect serving migration to 000238 after main released 000237 2026-09-30 07:19:59 -07:00
Matthew Meszaros d64adcc191 Merge remote-tracking branch 'origin/main' into feat/cloud-domain-redirects 2026-09-30 07:18:51 -07:00
Matthew Meszaros 7b526be901 feat: write a Cloud-served root redirect here before asking Warmbly Cloud (one workspace per domain, enforced by a unique index), roll it back on Cloud's refusal and keep it pending through an outage, release Cloud redirects no workspace here has any more from the sweep, resend a target Cloud holds stale, stop a mirrored redirect only on Cloud's definite refusals, read a proxy gateway error as transient and order the certificate and port verdicts correctly, validate linked domains with the shared hostname check, share one proxy name map, and trim comments 2026-09-30 06:59:55 -07:00
Matthew Meszaros 75f764dc67 feat: require a verified Cloud Tasks token with a pinned audience on task webhooks, manage_settings on integration OAuth and a fresh membership check at every mailbox and integration OAuth finish, user verification for passkey sign-in, ended sessions before a password reset or ban reports success, and a revoked session when a rotated refresh token is replayed; remove the internal DEK delete route, log credential path parameters by name, hold remote images in received mail until the reader loads them, add Calendly and Cal.com signing keys with inbound URL rotation, keep automation signing secrets out of connection responses, and apply the password rules to the bootstrap password 2026-09-30 06:46:24 -07:00
Matthew Meszaros d457ad86e3 feat: keep Warmbly Cloud's link-token answers from ever reaching the dashboard as a 401, stop a Cloud-served redirect Cloud refuses to take back, drop a new Cloud row its own check refused, store only reach values this schema knows when mirroring a newer Cloud, read an offer-less Cloud as not serving redirects, confirm bulk moves against the server actually sent, poll the Cloud offer once a minute, and probe without keep-alive connections 2026-09-30 06:21:29 -07:00
Matthew Meszaros 41d43f64be feat: check that a verified root redirect actually reaches visitors by opening the domain over http and https and naming what answered instead (Traefik, nginx, Caddy, a rewritten Host header, a missing certificate, a closed port) with per-proxy fix steps in the sending domain drawer, and let a self-hosted instance linked to Warmbly Cloud have Cloud serve and certify its redirects (connect in place from the redirect tab, the bulk dialog or a page banner), with Cloud-side linked-instance redirect endpoints under a per-instance limit, migration 000237, labelled tracking answers and a 503 when the redirect lookup is unavailable, and the sending domains, Warmbly Cloud, data control, install, OpenAPI, API and error code docs updated 2026-09-30 06:04:00 -07:00
Matthew Meszaros 1f0c6ca6d6 feat: send each mailbox's reply-to as a Reply-To header on campaign, unibox, test and placement mail (never warmup), record it on every campaign send attempt (tasks.reply_to, migration 000236) and credit a reply landing in that shared reply inbox to the campaign and its sending mailbox across reply attribution, the sync priority lane and copied-contact replies, show the sending mailbox on thread messages, recent activity and the reply webhook, start unibox and inbox-agent replies from the mailbox that emailed the contact, flag an untracked reply-to in mailbox settings, let the sandbox simulator answer Reply-To, and document the shared reply inbox (#756) 2026-09-30 05:33:34 -07:00
Matthew Meszaros b2d6d100dd feat: relay unibox Archive, Delete and Move to inbox to the mailbox itself through a new MESSAGE_FOLDER worker command (Gmail label batchModify, Outlook well-known folder moves with the message map re-keyed around each Graph id change, IMAP MOVE after locating each message by Message-ID), answered by relayed UPDATE_FOLDER events that write only provider_folder and the moved handles, relaying every row a filing moved so an Undo right after Archive still reaches the mailbox, with a per-mailbox Mirror Archive and Delete switch (email_accounts.relay_folder_moves, migration 000235, on by default), warmup receipts, drafts and non-Gmail sent copies left in place, and the unibox, mailboxes, API, events and OpenAPI docs updated 2026-09-30 04:41:03 -07:00
Matthew Meszaros a34e1634fc feat: merge main into the placement comparison branch, keeping both the rendered-copy and the placement-batch sentences in the workspace export guide's placement row 2026-09-30 08:20:58 +02:00
Matthew Meszaros d3fc3133da feat: renumber the placement_renders migration from 000230 to 000234 so it follows 000233 on main instead of colliding with the released 000230_campaign_lead_cc 2026-09-30 08:20:39 +02:00
Matthew Meszaros 0ce8a6ee79 feat: claim each held warmup spam move with a lease, fix its verdict before applying any effect and complete it only after every sender and recipient write succeeds, so consumer replicas never charge a strike that disagrees with the recorded verdict and a failed write re-applies the same verdict 2026-09-30 07:08:40 +02:00
Matthew Meszaros 57a26c4ee6 feat: attribute a received warmup email moved to spam on the evidence around it before charging anyone, never for mail that arrived in spam (warmup_received.landed_spam), holding each move 30 minutes in warmup_spam_moves and deciding provider on a cross-workspace correlation or a move straight after arrival with nobody there, owner on provider-reported read, unread or star activity in mailbox_owner_activity or a repeated uncorrelated pattern in a used mailbox, nobody otherwise, weighting a spam move as one strike, withdrawing owner verdicts a later correlation explains, clearing the strikes behind a hold an admin lifts or an appeal approves, migration 000233, docs and guide 2026-09-30 06:52:32 +02:00
Matthew Meszaros e76cdc6f3a Merge remote-tracking branch 'origin/main' into fix/recent-posthog-errors-2 2026-09-29 20:13:29 +02:00
Matthew Meszaros d998619455 feat: renumber the placement batch migration to 000232 after main took 000230 and 000231 for lead CC, and require that version in the batch live tests 2026-09-29 10:53:12 -07:00
Matthew Meszaros 80ecadb1e6 Merge remote-tracking branch 'origin/main' into feature/batch-inbox-placement-tests 2026-09-29 10:51:41 -07:00
Matthew Meszaros ca66b25d8d feat: register the release's bus schemas from the booting backend and hold the fleet on its current release until the backend runs the new one and the schema registry accepts them, check every published schema against a recorded snapshot in CI (make schemas) so a change the registry would refuse fails before merge, pin a FORWARD or FULL registry subject to BACKWARD on refusal, bound advisor narration so the run still lands, store unlabelled 8-bit mail with its invalid UTF-8 replaced, treat a pending or credential-less passkey ceremony as a cancellation, and drop link-scanner rejections from site error tracking 2026-09-29 19:20:07 +02:00
Matthew Meszaros d99a09825f feat: run one inbox placement test across many sending mailboxes as a placement batch (issue #736): server-side sender scopes (a campaign's senders or the whole workspace, filtered by provider, domain, tag and untested days) and sampling (random, percent stratified by provider or domain, per domain, per provider) snapshotted at creation, a runner that starts senders under per-workspace and instance-wide concurrency and a start rate with defer or skip for unavailable mailboxes, aggregate placement by sending domain, sending provider and recipient provider, fleet coverage, cancel, credits agreed per batch, org transfer, operator settings in the admin panel, dashboard pages and dialog, CLI commands, agent tools, OpenAPI and docs 2026-09-29 10:19:46 -07:00