Matthew Meszaros
|
3bf8d4bdfa
|
Merge remote-tracking branch 'origin/main' into feature/mailbox-fair-use-allowance
|
2026-09-04 21:25:53 -07:00 |
|
Matthew Meszaros
|
3596a9770f
|
feat: make mailboxes truly unlimited under a fair-use allowance of one mailbox per daily send the plan includes (15,000 on Business), drop the 200 hard cap and the 5-per-day connect throttle, open a request-more dialog with inline limit requests and plan upgrade when the allowance is reached, add bulk CSV mailbox import with live progress and a downloadable failed-rows file, reserve attachment storage atomically under a per-org lock so concurrent uploads cannot pass the quota (#326), report storage and the allowance on the billing usage meters, and honor approved daily-send overrides in the sender
|
2026-09-04 21:00:26 -07:00 |
|
Matthew Meszaros
|
d80816efa1
|
feat: make a successful warmbly auth login always the active host instead of deciding it from three overlapping conditions, and say why the CLI state file tolerates an unwritable home
|
2026-09-04 20:16:56 -07:00 |
|
Matthew Meszaros
|
f68918d7f6
|
Merge remote-tracking branch 'origin/main' into feature/warmbly-cli
|
2026-09-04 20:14:46 -07:00 |
|
Matthew Meszaros
|
f46fc86a7c
|
feat: add the warmbly CLI at cmd/cli, a signed-in multi-host client of the public REST API with 30 command families over one spec table, gh-shaped api passthrough, live event tailing, tables on a terminal and JSON in a pipe, and a confirmation gate in front of everything that puts real mail on the wire
|
2026-09-04 20:14:33 -07:00 |
|
Matthew Meszaros
|
028689fd2e
|
feat: device-code sign-in for the CLI, with a browser approval page at /cli that mints a scoped API key, self-revocation at DELETE /api-keys/self so a read-only credential can always end itself, and app_url plus websocket_url on /auth/config so a client can find the dashboard and the realtime gateway on a self-hosted layout
|
2026-09-04 20:14:27 -07:00 |
|
Matthew Meszaros
|
5fd9aedb80
|
Merge remote-tracking branch 'origin/main' into feature/plan-upgrade-paywall-modal
|
2026-09-04 09:11:51 -07:00 |
|
Matthew Meszaros
|
e5360713e7
|
Merge remote-tracking branch 'origin/main' into feature/install-script-setup-wizard
|
2026-09-04 06:43:12 -07:00 |
|
Matthew Meszaros
|
77058a2cb4
|
feat: address the review on the installer branch by keeping the database password out of pg_dump's argv, excluding backup bundles from the blob root they are written into, tolerating blobs that change or vanish mid-archive, making the instance-settings bootstrap a single atomic insert, and validating the release tag before it is written into .env
|
2026-09-04 06:24:05 -07:00 |
|
Matthew Meszaros
|
fb868dfe40
|
feat: delete the storage objects of a step's attachments when the step is deleted, since the attachment rows cascade away with the sequence and left their bytes counted against the organization's storage quota with no row left to reach them
|
2026-09-04 06:20:26 -07:00 |
|
Matthew Meszaros
|
a0c9d5a5b0
|
feat: add operator notification channels an admin configures in the panel (Discord, Slack, HMAC-signed webhook, or email) with per-channel event subscriptions, a synchronous test probe, SSRF-guarded URLs and credentials redacted on read, wire nine instance events including the enterprise inquiry that previously wrote a row nobody was told about, and reword the plan-specific limits copy so it reads correctly without billing
|
2026-09-04 06:02:40 -07:00 |
|
Matthew Meszaros
|
42c3000383
|
feat: verify the backup bundle's recorded dump digest before warmblyctl restore empties the target schema, so a truncated bundle is refused instead of leaving the instance with neither its own data nor the bundle's
|
2026-09-04 05:56:12 -07:00 |
|
Matthew Meszaros
|
d68bbcd2ab
|
feat: add a one-command self-host installer at warmbly.com/install.sh with an interactive data-control wizard, give docker-compose.yml image keys and per-store volume variables, add an image-mode updater, move engagement/form/audit retention into instance settings, and add warmblyctl backup/restore
|
2026-09-04 05:49:54 -07:00 |
|
Matthew Meszaros
|
ab0c756aa2
|
feat: show a campaign's linked segments on the Leads tab as chips with live enrolled and held-out counts that filter the list, explain an empty list caused by an empty segment or hand-removed members and add them back in one click, add a scope-aware Export to campaign leads and segment members with a campaign-ready preset that carries lead status and engagement, write the links and the enrolment of PUT /campaigns/:id/segments in one transaction so a failed enrolment changes nothing instead of answering added 0, restart a completed campaign when a one-shot enrol adds leads, clear the manual-removal record when a contact is added by hand, count every link's members in one scan, audit sweep enrolments so open Leads tabs refresh, and document it in the campaigns and segments guides and the API reference
|
2026-09-03 20:38:27 -07:00 |
|
Matthew Meszaros
|
701dc404a2
|
Merge remote-tracking branch 'origin/main' into fix/click-without-open-tracking
|
2026-09-03 05:22:05 -07:00 |
|
Matthew Meszaros
|
b2ea1f1961
|
feat: add self-hosted update awareness and one-click updates: every binary is stamped with its version and commit, the backend polls GitHub Releases and a new host-side updater (cmd/updater, compose profile or systemd unit) reports the checkout's commit distance, the admin panel's top bar shows a version pill that turns into an update indicator and opens a dialog with confirmation, live step progress and log, restart tracking and result, the dashboard header shows the same pill to every member of a self-hosted instance with the full update flow for platform admins, Setup and health gains update_available and updater_unreachable checks, warmblyctl status prints the version, make upgrade and scripts/upgrade-bare-metal.sh cover the by-hand paths, and docs gain an Updates page plus configuration, health, deployment and API reference updates
|
2026-09-03 05:04:30 -07:00 |
|
Matthew Meszaros
|
31dabea0a4
|
feat: rebuild the click-without-open fix on top of the per-link click attribution from #298: a person's click now also counts as an open and a burst that withdraws the click withdraws the open it implied unless a real open is on record, routing readers ignore machine opens as the docs promised, every open gets its own log row and every open and click records the mail client or proxy, browser, device, OS, country, region and city (migration 000124: origin columns on email_link_clicks plus an email_opens table), the tracking service publishes only the address's network in a nullable client_ip field which the consumer resolves with GeoLite and drops, the contact Activity tab shows each open and the origin of opens and clicks, the campaign overview gains a who-engaged-from-where breakdown exposed as engagement in campaign analytics, live open and click events carry occurred_at, client and location, the leads table explains why an open is not always counted, both logs are pruned daily after a year, email_opens joins the export registry, the consumer reads GEODB_PATH optionally, and the guides and API references are updated (fixes #294)
|
2026-09-03 03:49:44 -07:00 |
|
Matthew Meszaros
|
579b0ac04c
|
feat: merge main into the unsubscribe branch again, carrying the UTM campaign columns alongside unsubscribe_mode in every campaign scanner, keeping unsubscribe links out of the moved link tracker and its UTM tagging, and renumbering the opt-out migration to 000124 because main released 000123 for link clicks
|
2026-09-03 03:43:47 -07:00 |
|
Matthew Meszaros
|
78c4021053
|
Merge remote-tracking branch 'origin/main' into feat/link-click-attribution
# Conflicts:
# docs/content/docs/api/reference/campaigns.mdx
# docs/content/docs/guides/campaigns.mdx
# internal/repository/pg_campaign.go
# internal/repository/pg_campaign_lifecycle.go
# web/src/components/app/campaigns/NewCampaignDialog.tsx
|
2026-09-03 03:14:19 -07:00 |
|
Matthew Meszaros
|
64f2d1637b
|
feat: address the review on the unsubscribe PR: register the suppression family in warmblyctl, require the confirm field on the browser unsubscribe POST and cap its body, render a chosen A/B variant through the template engine so its merge fields and unsubscribe link resolve, fold curly apostrophes before opt-out phrase matching, write pasted suppression lists in one transaction, clamp copy by runes instead of bytes, add the constraints NOT VALID plus a lower(email) index in migration 000123, scope the unsubscribe link type-ahead to email bodies, and document DELETE /suppressions/:id
|
2026-09-03 02:20:47 -07:00 |
|
Matthew Meszaros
|
9485503e03
|
feat: merge main into the unsubscribe branch, keeping the new campaigns.kind column alongside unsubscribe_mode in every campaign scanner and renumbering the opt-out migration to 000123 because main took 000122 for campaign kind
|
2026-09-03 01:56:38 -07:00 |
|
Matthew Meszaros
|
5eb92c601c
|
feat: give every campaign email a working opt-out: a reply-to-opt-out line by default or an unsubscribe link (workspace setting under Settings > Sending with a per-campaign override and a {{.UnsubscribeLink}} variable), signed per-recipient unsubscribe links served on the API origin so the List-Unsubscribe header no longer points at a dead warmbly.com page, a confirm page on GET with RFC 8058 one-click on POST and a resubscribe button, reply opt-out detection through the whole-word compliance lexicon with quoted history stripped, a first-class suppression list (Contacts tab, GET/POST/DELETE /suppressions with address and domain entries, audited removal, contact drawer action), the contact Subscribed flag enforced in campaign routing, migration 000122 with a shared recipient_suppressed() predicate, and docs for all of it
|
2026-09-03 01:52:40 -07:00 |
|
Matthew Meszaros
|
24b7e26fbb
|
feat: add the one-time email campaign preset from #288: campaigns.kind column with sequence and one_time values and a kind field on POST /campaigns, GET /campaigns?kind= and an one_time count on /campaigns-overview, a POST /campaigns-estimate endpoint projecting deduplicated segment recipients against the mailbox pool's per-day capacity and finish date, a wizard type picker whose one-time flow runs Basics, Email, Audience, Sending and Send with send-now or scheduled start and the estimate panel and then creates, links segments and starts the campaign, a guard refusing a second email step on a one-time campaign, draft/scheduled/sending/sent wording with a One-time badge and Type filter in the campaigns list, kind on the AI create_campaign_draft tool, honouring text_only in the send and test-email paths so plain-text campaigns ship no HTML part or tracking, and the campaigns, segments, API reference and endpoint docs
|
2026-09-03 01:42:00 -07:00 |
|
Matthew Meszaros
|
99be92f159
|
feat: attribute every campaign click to the exact link with a per-link click log (email_link_clicks) behind the contact activity timeline, campaign live feed, recent activity and the email_clicked webhook, add per-campaign automatic UTM tagging (utm_tracking with source, medium and campaign overrides, utm_content from the link text) applied at send time to the stored ticket destination, and classify opens and clicks as machine when they arrive within ten seconds of dispatch or when several links of one email are followed within five seconds, so scanner clicks are logged but never count as engagement, fire automations or send webhooks
|
2026-09-03 01:05:34 -07:00 |
|
Matthew Meszaros
|
290f1f00aa
|
Merge remote-tracking branch 'origin/main' into feature/public-forms
# Conflicts:
# cmd/backend/main.go
# docs/content/docs/guides/workspace-export-import.mdx
|
2026-09-01 09:29:53 -07:00 |
|
Matthew Meszaros
|
05d74dbe9f
|
feat: forms v2, a full redesign of the hosted form and its builder: eight one-click themes, card/wide/split layouts with a brand cover panel, classic paged and Typeform-style focus modes split on a new page_break block, logo/cover/background uploads with size, fit and veil controls, an optional header bar that can span the page or sit with the form, a real HSV colour picker and font preview, per-contact personalized links that prefill and attribute without email verification, a render-token gate so the form JSON cannot be scraped without loading the page, funnel analytics with per-page drop-off and identified visitors, a leads-style forms list and responses table, and an organization custom forms domain verified by CNAME with hourly re-checks
|
2026-09-01 09:27:28 -07:00 |
|
Matthew Meszaros
|
128e314103
|
Merge remote-tracking branch 'origin/main' into fix/issue-277
|
2026-09-01 04:03:37 -07:00 |
|
Matthew Meszaros
|
d6ddf1f170
|
feat: fix implicit-TLS SMTP on 465 and IMAP STARTTLS on 143 behind a stored per-mailbox security mode that accepts any port, stop worker ID churn orphaning mailbox assignments via flock-claimed persistent worker ids, give the unibox a standard mail-folder sidebar (inbox/sent/drafts/archive/spam/trash) backed by a provider-derived folder column, and expose the AI tool registry over REST for non-MCP function-calling agents (#283)
|
2026-09-01 03:53:19 -07:00 |
|
Matthew Meszaros
|
5e87b1bbbf
|
Merge remote-tracking branch 'origin/main' into feature/public-forms
|
2026-09-01 01:17:54 -07:00 |
|
Matthew Meszaros
|
60c9e316d9
|
feat: hosted lead-capture forms end to end: drag-and-drop builder with field settings, design panel, embed/share and submissions tabs in the dashboard, a public TanStack form app (forms/) served by the new standalone forms service (cmd/forms + internal/formserver) on FORMS_DOMAIN with per-form frame-ancestors CSP, honeypot/fill-time/Turnstile/per-IP submit protection and a same-origin JSON API proxying the backend internal API, form submissions creating contacts with categories and campaign enrollment plus realtime, audit, webhook and org-transfer coverage, migration 000114, seed forms, CI jobs, Dockerfile, systemd/nginx/compose manifests and docs
|
2026-09-01 01:17:51 -07:00 |
|
Matthew Meszaros
|
66a105fb9f
|
feat: make segments a live campaign audience and round out lead management (issue #277): a campaign_segments link table with GET/PUT /campaigns/:id/segments (max 20, replace semantics), immediate enrolment of linked-segment members as leads plus targeted re-syncs on contact writes, segment edits, pin-ins and imports and a 2-minute backend sweep for drift, waking active campaigns and restarting completed ones through the full launch checks; a Segments manager dialog and toolbar button on the campaign Leads tab; Remove from campaign as the row action and a selection-bar bulk action so leads can leave a campaign without deleting the contact; campaign and segment pickers in the CSV import wizard backed by a new segment_ids import option that pins imported rows as include overrides; a delete guard naming linked campaigns; orgtransfer registration, live repo tests and docs for all of it
|
2026-08-31 08:45:27 -07:00 |
|
Matthew Meszaros
|
896fc60f4d
|
feat: add the missing mailbox re-verification flow (issue #274): POST /emails/onboarding/oauth/reauth/:id re-runs the provider consent for an existing Gmail/Outlook mailbox (login_hint preselect, same-address check, token rewrite keeping the stored refresh token) and PUT /emails/onboarding/smtp-imap/:id replaces SMTP/IMAP credentials after live validation; both resolve the credential-class error rows and reactivate the mailbox onto its worker, the drawer's Needs attention banner gains Re-authorize and Update credentials buttons, and the mailboxes guide documents reconnecting
|
2026-08-31 05:29:35 -07:00 |
|
Matthew Meszaros
|
7a7c6051bc
|
Merge remote-tracking branch 'origin/main' into feature/contact-segments
|
2026-08-30 02:07:56 -07:00 |
|
Matthew Meszaros
|
52916ab60d
|
feat: verification evidence engine and Greptile fixes for #264: contact_verification_evidence ledger (migration 000111) fed by clean deliveries, human opens, clicks, replies and recipient-naming bounces from the send, tracking, reply and bounce paths, a decaying score that lets real mail outrank a probe and a newer bounce outrank older engagement, verification_confidence on every contact with a reasons list and animated Deliverability card in the contact drawer, per-organization probe breakers, undeliverable counts that ignore finished leads, tighter wiring comments, and docs
|
2026-08-29 23:49:33 -07:00 |
|
Matthew Meszaros
|
daf946fc45
|
feat: add the add_to_segment and remove_from_segment sequence action steps, executed on both the scheduled campaign path and the instant reply path, wired into the backend and consumer
|
2026-08-29 23:45:05 -07:00 |
|
Matthew Meszaros
|
14a21bf9bd
|
feat: expose the segments API (list, fields, preview, create, get, patch, delete, members, member lookup, overrides, add-to-campaign) and GET /contacts/:id/segments behind contact permissions, wire the service into the backend and audit each mutation on the realtime spine
|
2026-08-29 23:45:05 -07:00 |
|
Matthew Meszaros
|
a02ff7c936
|
feat: address verification overhaul for #264: MillionVerifier as a pay-as-you-go integration plugin with org-sealed key and automatic fallback to the built-in check, built-in prober gains domain cache, Microsoft/Yahoo fingerprinting, MX fallback, disposable/role sub-statuses and a self-check breaker, imports and POST /contacts accept verification results in any known provider vocabulary with auto-detected columns, verdict provenance and expiry columns (migration 000110), campaigns park at paused_undeliverable with re-verify/send-anyway instead of finishing, POST/GET /contacts/verification bulk actions and overview, launch gate override via acknowledge_list_risk, animated verification marks, banner and settings card in the dashboard, and docs
|
2026-08-29 23:11:22 -07:00 |
|
Matthew Meszaros
|
44b2c18906
|
feat: address review on cloud-managed mailboxes: the consumer now asks the cloud to vouch for a warmup token in a mailbox it warms (GET /pool-link/instance/mailboxes/:id/warmup-tokens/:token) and files anything unverified as ordinary mail instead of dropping on a sender-controlled header, disconnect keeps local mirrors and the link until the cloud confirms the instance is released so managed mailboxes cannot be stranded, and long narrative comments are cut to one line
|
2026-08-29 10:07:36 -07:00 |
|
Matthew Meszaros
|
2a831e9783
|
feat: let a linked self-hosted instance sign Google and Microsoft mailboxes in through Warmbly Cloud's own OAuth apps and send with cloud-brokered access tokens: the cloud runs the consent (pool_link_mailboxes.managed, brokered state in Redis, the existing /addresses/*/callback completes it and redirects to the instance's /cloud-oauth/done), keeps the refresh grant, mints short-lived tokens at /pool-link/instance/mailboxes/:id/token and refuses them for revoked links, removed, inactive or blocked mailboxes; the instance mirrors such mailboxes without a credential (cloud_link_mailboxes.managed), ships them to the worker as brokered so goog/msgraph init on a token source that pulls from /api/v1/internal/cloud-link/token/:id, lets the consumer ignore cloud warmup tokens for enrolled mailboxes, and can adopt mailboxes connected directly on the workspace; Add account shows the cloud path and the adoptable list, and the Warmbly Cloud guide documents the model
|
2026-08-29 09:50:52 -07:00 |
|
Matthew Meszaros
|
b75fdcf86c
|
Merge remote-tracking branch 'origin/main' into feature/self-hosted-warmup-pool-access
# Conflicts:
# internal/scheduler/warmup_scheduler.go
|
2026-08-29 07:10:38 -07:00 |
|
Matthew Meszaros
|
37b60b59d3
|
feat: let a self-hosted instance warm its mailboxes in the hosted pool: device-code link approved at /connect, instance-token API that enrolls SMTP/IMAP mailboxes as warmup-only accounts (no history import, non-warmup mail dropped), free for 10 mailboxes and unlimited on the seeded $15 pool plan, tier fallback to proven healthy mailboxes when a pool runs thin, local warmup stands down for enrolled mailboxes, Settings > Warmbly Cloud step flow and linked-instances page, docs guide, marketing copy, and fix SetWarmupLifecycle re-reading the row with an org-scoped lookup so every warmup start/pause returned 404
|
2026-08-29 07:09:04 -07:00 |
|
Matthew Meszaros
|
5821fc2a1c
|
feat: merge origin/main into the website tracking branch after PR #260 landed 000106_contact_source, renumber the website tracking migration to 000107, and fold the page_hit timeline type, its query source and its Website filter, meta line and expandable details into the reworked contact Activity tab and models from the contact-activity PR
|
2026-08-29 04:38:35 -07:00 |
|
Matthew Meszaros
|
a4739b63ec
|
feat: add first-touch contact source attribution (migration 000106: contacts.source with a CHECK, source_detail, first_seen_at, existing rows stamped unknown) stamped at every creation site (dashboard manual/campaign, file import, Google Sheets sync, API key, AI assistant), write contact_created / campaign_added / campaign_removed / category_added / category_removed lifecycle events into contact_activities inside the same transactions as the links and merge them into the contact timeline, refactor FindNextRoutedPair's routing into a campaignRouter shared with a per-contact RouteContact and split CalculateNextCampaignTime into campaignSenders + placeCampaignSend so a read-only PreviewContactSend derives a contact's next step through the scheduler's own constraints, expose it as GET /contacts/:id/campaigns behind the contact service, render a per-campaign state panel, Campaigns/Lifecycle filter chips and expandable event rows in the Activity tab plus a Source section on Overview, cover it with TestLive* for the timeline events and the next-action preview (step wait, sending window, paused), and document it in the contacts, campaigns and API reference docs (issue #255)
|
2026-08-29 03:28:16 -07:00 |
|
Matthew Meszaros
|
27630eec0a
|
feat: add website visitor tracking for issue #255 with migration 000106 (website_tracking_settings, website_visitors, website_page_hits, all registered in the orgtransfer spec), a consent-gated dependency-free tracking.js served by the Rust tracking service with a rate-limited, size-capped, prefetch-filtered POST /p ingest that forwards to a new backend internal page-hits endpoint for server-side user-agent and GeoIP enrichment, contact identification only through the click ticket the redirect appends to registered hosts, a per-workspace retention job, page_hit events with an expandable detail view in the contact Activity timeline, a Settings > Website tracking page for the snippet and consent, location and retention configuration, realtime PAGE_HIT fanout, and a website tracking guide plus endpoint, export and configuration docs
|
2026-08-29 03:25:50 -07:00 |
|
Matthew Meszaros
|
4cee178c7a
|
Merge remote-tracking branch 'origin/main' into fix/issue-244-reserve-hold
|
2026-08-28 23:33:26 -07:00 |
|
Matthew Meszaros
|
35e8f95f2a
|
feat: make a release from the mailbox hold land where the rebalancer would put it by running lifecycle.Decide against the mailbox's current warmup health through a new GetLifecycleCandidate repository read, so an unhealthy mailbox goes straight to resting instead of sending cold for up to an hour before the hourly pass rests it again, report that outcome in the drawer toast and the API reference, and condense the new hold comments in the email service, backend wiring, repository, hook, client and drawer to the one-line form the repo convention asks for
|
2026-08-28 23:30:15 -07:00 |
|
Matthew Meszaros
|
84700bae8d
|
Merge remote-tracking branch 'origin/main' into fix/issue-241
# Conflicts:
# cmd/consumer/main.go
# docs/content/docs/guides/deliverability.mdx
# internal/app/auth/login_risk.go
# internal/app/auth/provision.go
# internal/app/contact/import.go
# internal/app/orgrisk/service.go
|
2026-08-28 23:06:07 -07:00 |
|
Matthew Meszaros
|
6bb2a3d0c2
|
Merge remote-tracking branch 'origin/main' into fix/issue-245-resolution
|
2026-08-28 22:51:37 -07:00 |
|
Matthew Meszaros
|
5c832461b0
|
feat: wire the unreachable reserve send-lifecycle state to a real per-mailbox hold (POST /emails/:id/hold and /release behind manage_emails and WRITE_EMAILS, SetSendHold on the email service forcing the lifecycle past the rebalancer guard, a Hold from campaigns toggle on the mailbox drawer's Overview tab with the reserve notice rewritten to point at it, warmblyctl mailbox hold/release, and docs in the mailboxes guide, API reference and scope map), and drop the warming state nothing ever set from the model, the web type, the docs table and the DB check via migration 000104, which folds any legacy warming row back to active
|
2026-08-28 22:47:31 -07:00 |
|
Matthew Meszaros
|
0ab947dc5d
|
Merge remote-tracking branch 'origin/main' into fix/issue-241
|
2026-08-28 22:47:28 -07:00 |
|