Matthew Meszaros
|
16349852cc
|
feat: fix issue #381 by scoping the import and sheet-sync entry points on a segment's member list to that segment, so a CSV uploaded from inside a segment pins its rows in instead of creating contacts that are nowhere in it, and round the page out with an empty state offering Add contacts, Import file and New contact plus a pinned-contacts panel that says when it is showing only the newest slice of a large import
|
2026-09-08 04:59:25 -07:00 |
|
Matthew Meszaros
|
5f665a3208
|
feat: teach the contact import wizard a lockedSegment that always travels with the upload, named in the header and as a fixed always-applied row above the segment picker, confirmed on the result step from the API's segments_pinned flag, and refresh the segments query alongside contacts so the page behind the wizard is right before the realtime spine event lands
|
2026-09-08 04:59:25 -07:00 |
|
Matthew Meszaros
|
c9781a1c57
|
feat: surface the sheet-sync segment targets in the dashboard: an Add to segments picker on the sync wizard's options step and in the source edit drawer, a lockedSegment the wizard applies on every run and shows as a fixed row, and a SyncSourcesPanel that scopes its list and its New sync to the segment it was opened from
|
2026-09-08 04:59:18 -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
|
25484f70ab
|
Merge remote-tracking branch 'origin/main' into feat/issue-357-loopback-mail-relays
|
2026-09-07 09:05:11 -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
|
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
|
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
|
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
|
8f604921ab
|
Merge branch 'main' into fix/issue-343-behaviour
|
2026-09-07 04:49:48 -07:00 |
|
Matthew Meszaros
|
4582896f54
|
merge
|
2026-09-07 04:43:45 -07:00 |
|
Matthew Meszaros
|
f1b6345337
|
feat: address the CodeRabbit review on the mailbox error lifecycle PR by bounding the transient-error resolution to failures raised before the sync pass ran, because JetStream is configured with MaxDeliver and no MaxAckPending so a redelivered older success could otherwise clear a newer outage and leave the mailbox looking healthy until the next distinct one, naming the archive folder in the drawer's folder note alongside the others the cap always keeps, and giving the duplicate-folder note a singular verb
|
2026-09-07 04:36:50 -07:00 |
|
Matthew Meszaros
|
d51501bef1
|
Merge branch 'main' into fix/issue-343-behaviour
|
2026-09-07 04:36:42 -07:00 |
|
Matthew Meszaros
|
87a7e3e1ed
|
feat: make a hosted form creatable again by binding an unset embed allowlist and field list as empty arrays instead of NULL in the forms repository writes, which is what made every New form fail with a not-null violation on forms.allowed_domains since the feature shipped (issue #343), and while proving the flow end to end keep the port on the shared forms host so share links and embeds resolve on a ported install, build the embed snippet on the form's own origin so an organization on a verified custom forms domain does not embed from the shared one, and apply the same empty-array fix to webhook endpoints created without event_types, which answered the raw Postgres error instead of the documented subscribe-to-everything
|
2026-09-07 04:35:56 -07:00 |
|
Matthew Meszaros
|
1af5ac1ea6
|
feat: let a fixed mailbox problem stop showing as a permanent error (issues #362, #363): resolve a mailbox's connection errors on the first sync pass that reaches the server again, because nothing but a credential reconnect ever resolved an error row, so a five-minute outage left a red needs-attention on the mailbox for good and held its health at warning; carry what the folder listing had to skip as sync state shown in the drawer's Sync card instead of raising an error row nobody can withdraw, so the note disappears once the mailbox is back under the folder limit or the duplicate folder id is renamed; and keep only a fingerprint per message in the IMAP flag scan rather than the whole flag set and Message-ID, which held 74 MB per mailbox at the window and folder limits against 14 MB for the digests, on a worker whose base capacity is 16 mailboxes
|
2026-09-07 04:24:09 -07:00 |
|
Matthew Meszaros
|
31e4453c29
|
feat: capture dashboard pageviews on history changes rather than page load, since the dashboard is a single-page app and plain capture_pageview would have reported one pageview per session, and mask record uuids out of every reported URL through before_send so a pageview never carries an identifier
|
2026-09-07 04:21:45 -07:00 |
|
Matthew Meszaros
|
8e9f67f46b
|
feat: add cookieless PostHog analytics for the hosted marketing site and dashboard with server-side signup and subscription events, a first-party acquisition record written once at signup on a new organization_acquisition table registered in the org-transfer spec, an acquisition column and channel filter in the admin org list, and never a single request from a self-host because every key is unset by default
|
2026-09-07 04:18:33 -07:00 |
|
Matthew Meszaros
|
51dedc90ee
|
feat: put every runtime behind one optional error-reporting story: a single internal/observability/errs wrapper that is now the only package importing sentry-go, InitSentry for cmd/forms, release and environment tags on every service from the existing build stamp, optional Sentry in the admin panel and the public forms app, the sentry crate in the Rust tracking service, release tagging in realtime, CI source-map upload that only runs when a Sentry token is configured, and docs covering the DSN for each service
|
2026-09-07 03:51:06 -07:00 |
|
Matthew Meszaros
|
20c18bc2f7
|
feat: read the dashboard's Sentry DSN from the container-injected runtime config instead of a literal DSN in web/src/main.tsx, so a self-hosted install reports its users' browser errors, URLs and IPs nowhere unless the operator sets WARMBLY_SENTRY_DSN, and document the variable in configuration.mdx and the no-reporting-by-default stance in data-control.mdx
|
2026-09-07 03:36:27 -07:00 |
|
Matthew Meszaros
|
5d60fe9a21
|
feat: fix campaign restart dead end (issue #340): resolve preflight tracking senders through the same explicit/tags/all pool the scheduler uses so a connected mailbox is never reported missing, fetch the launch dialog's step count from the steps endpoint instead of a field the campaign API never returns, turn on Keep running for new leads when a form or an automation feeds a campaign (migration 000131 backfills existing ones) and when a member starts a campaign whose every lead has finished so it goes active and waits for leads instead of answering 400, return waiting_for_leads from the start endpoint for the dialog's success screen, and document the no_leads and no_remaining_leads codes and the new behaviour in the campaigns, forms, automations and API docs
|
2026-09-07 02:26:50 -07:00 |
|
Matthew Meszaros
|
7b11489c85
|
feat: keep a campaign running for new leads (issue #336): add a continuous campaign setting (migration 000130, on by default once a segment is linked) under which a campaign that runs out of leads stays active and idle with idle_since set instead of finishing, is startable with no leads, is re-checked by the reconciler every pass and shows as waiting for leads in the dashboard list, header and live activity panel with a CAMPAIGN_IDLE realtime event; make WakeCampaigns restart a finished campaign through the full launch checks so a lead added by the contacts API, a bulk update, an import or the add-to-campaign automation action reopens it exactly like a linked segment does, and write a refused restart to the campaign activity log once an hour per reason instead of only the backend log; expose continuous on the campaign API and preferences page and document it in the campaigns, segments, forms, automations, API reference and realtime docs
|
2026-09-06 02:50:16 -07:00 |
|
Matthew Meszaros
|
04a2ed59c9
|
feat: address the CodeRabbit review on the lead-intake PR by giving the webhook sink its own copy of every map payload so the automation goroutine never writes into the map still being marshalled for endpoint delivery, propagating contact lookup errors in the upsert action's skip mode and in the native actions adapter so a failed lookup is never mistaken for a missing contact, adding the contacts source CHECK as NOT VALID in migration 000129 to skip the table scan under the migration lock, defaulting a new create-or-update-contact node's email template to the address key the chosen trigger actually carries, correcting the n8n guide on which lead-form sources have a trigger node, naming API batches over 100 contacts among the silent bulk arrivals in the contacts guide, and updating the marketing automations page from nine to twelve triggers
|
2026-09-06 00:47:33 -07:00 |
|
Matthew Meszaros
|
a3989f9d9c
|
feat: turn automations into a lead-intake path so a Facebook, Instagram, LinkedIn or TikTok lead form pushed by Zapier, Make, n8n or any webhook becomes a tagged, campaign-enrolled contact without leaving Warmbly: add the warmbly.upsert_contact and warmbly.add_to_campaign built-in actions with templated field mapping, custom fields, tags, campaign and an if-exists policy, fire a rich contact.created event from the one contact write path (silent for file imports, sheet syncs and API batches over 100) and expose contact.created and form.submitted as automation triggers with condition fields, variables and sample data, carry an automation depth through events raised by an action so a flow that creates a contact cannot re-trigger itself past five hops, stamp automation-created contacts with the new automation source (migration 000129), share the campaign picker between the sheet sync wizard and the automation builder, document lead intake in the automations, Zapier, Make, contacts, forms, integrations, expressions and webhook pages plus a new n8n guide, mirror the new triggers and actions on the marketing automations page, and drop the 34 MB cli binary that was committed by mistake
|
2026-09-06 00:36:05 -07:00 |
|
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
|
70baaac636
|
feat: describe a paid plan's mailbox capacity in the free-workspace panel of the allowance dialog as one mailbox for every daily send it includes rather than promising unlimited, and name the upgrade feature More mailboxes to match
|
2026-09-04 21:17:58 -07:00 |
|
Matthew Meszaros
|
bf46839fb7
|
feat: address the CodeRabbit review on the mailbox allowance work by reserving the mailbox slot inside the account insert transaction under a per-organization lock so concurrent connects cannot both take the last one, resolving the storage limit under the quota lock so a plan change cannot be raced past, deleting refused attachment objects on a context that outlives the request, settling already-connected rows before the bulk batch spends allowance, leaving password columns out of the failed-rows CSV, stopping click propagation from the portalled allowance dialog, and counting bulk progress once
|
2026-09-04 21:11:25 -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
|
f68918d7f6
|
Merge remote-tracking branch 'origin/main' into feature/warmbly-cli
|
2026-09-04 20:14:46 -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
|
aba176f8aa
|
fix: never highlight a plan at or below the workspace's current one, so an Enterprise customer is no longer shown Business as the best value, and stop the agencies page promising each client its own machine and IP now that the FAQ states we do not sell or manage IPs as a product
|
2026-09-04 09:20:33 -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
|
5c4f87c1aa
|
feat: list the campaign's unscoped attachments under a Sent with every step group in the step composer, so files the step really carries stop being invisible in the panel that claims to show them, and say in the confirm that removing one takes it off every step
|
2026-09-04 06:40:48 -07:00 |
|
Matthew Meszaros
|
af9e852314
|
Merge branch 'main' into fix/step-attachments-mount
|
2026-09-04 06:34:53 -07:00 |
|
Matthew Meszaros
|
d78e4423e4
|
fix: address the review of the notifications work — bound detached alert delivery to a fixed pool that drops and logs rather than spawning a goroutine per event, never echo a transport error that embeds the webhook URL, treat an emptied channel target as cleared so switching transport cannot post Slack payloads to a Discord URL, emit user.registered for invited signups and the worker alert after a successful reassignment, format Stripe amounts by currency exponent, refuse an upgrade while the plan list is still loading instead of silently opening the portal, apply a fixed discount to the annual invoice rather than to each month, convert proration from minor units, contain focus in the nested enterprise dialog, hide the odometer wheels from assistive tech, and drop the last dedicated-IP row from the pricing matrix
|
2026-09-04 06:33:20 -07:00 |
|
Matthew Meszaros
|
85f3dad215
|
feat: send the files a step actually carries by honouring campaign_attachments.sequence_id in the send, test-send and preview paths instead of attaching every campaign file to every step, count them per step in the preflight content score, and refuse an upload naming another campaign's step
|
2026-09-04 06:16:16 -07:00 |
|
Matthew Meszaros
|
81d6ba4520
|
Merge remote-tracking branch 'origin/main' into feature/plan-upgrade-paywall-modal
|
2026-09-04 06:09:14 -07:00 |
|
Matthew Meszaros
|
c089cdfa75
|
fix: address review findings on the upgrade surfaces — resolve a catalog plan to the server plan by exact name before falling back to an unambiguous prefix so an operator-added 'Starter Legacy' cannot capture Starter's checkout, stop a plan that does not unlock the requested feature from offering to buy it, honour the dialog's aria-modal by moving and containing focus and restoring it on close, and pin the odometer digit geometry against flex shrink
|
2026-09-04 06:09:09 -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
|
7d8bb08fa0
|
fix: Talk to sales now opens an enterprise inquiry form instead of a Stripe portal session a free-tier prospect has no customer for, and correct two clients that could never succeed: the portal sent no body against a required return_url and read a url key the endpoint never returns, and the enterprise inquiry sent name/email/company/message against a handler binding company_name/contact_name/contact_email
|
2026-09-04 05:20:31 -07:00 |
|
Matthew Meszaros
|
b3e5394bb5
|
feat: drop the plan card hover lift and the large hover drop-shadow, leaving the same border-darkens-on-hover the rest of the settings controls use
|
2026-09-04 05:16:02 -07:00 |
|
Matthew Meszaros
|
87ada2c99f
|
feat: drop the purple gradient and indigo plan accents for the app's own slate and sky palette (Business wears sky, Enterprise solid ink, no gradients anywhere), highlight the best-value plan instead of whatever sits one rung up, remove the duplicated capability matrix from Overview and the comparison table from Plans, and stop calling dedicated workers 'dedicated IPs' across the dashboard, docs, pricing page, FAQ and agencies page
|
2026-09-04 05:11:47 -07:00 |
|
Matthew Meszaros
|
01dd6e3450
|
feat: rebuild Billing > Plans on the same large plan cards the upgrade dialog uses (extracted to a shared component with rolling prices, interval sheen and a highlighted next-step-up plan), add a side-by-side comparison table of every limit and capability sourced from the server plan records with the current plan column highlighted, and show the proration preview per card
|
2026-09-04 05:03:28 -07:00 |
|
Matthew Meszaros
|
19f8dfbc20
|
feat: rebuild Billing > Overview as a real control panel with cancel and resume, live usage meters against server-enforced limits, a capability matrix, trial and past-due and pending-cancellation banners; price plan switches with a proration preview on the Plans tab; fix the cancel client that sent no body and always 400'd; stop the Payment tab claiming no card is on file
|
2026-09-04 04:48:59 -07:00 |
|
Matthew Meszaros
|
3cca2390be
|
feat: replace the upgrade dialog's count-up prices with a per-digit odometer that rolls each wheel to its new digit, and choreograph the plan section on an interval flip with a staggered sheen sweep across each card, a scale pulse on the price, sliding billing-note and section-description crossfades, and an annual savings pill
|
2026-09-04 04:36:03 -07:00 |
|
Matthew Meszaros
|
1533b83c16
|
feat: open a full-screen upgrade dialog from every plan lock (sidebar rows, the unified inbox frosted overlay, the free-workspace screen) with the four plan cards, a monthly/annual toggle with animated prices, the unlocking plan highlighted, promo codes and one-click Stripe checkout or in-place plan change through a shared useUpgradeFlow hook that the billing page now uses too, return the Stripe redirect to the originating page and settle it in place, and add a plans and billing guide
|
2026-09-04 04:03:44 -07:00 |
|