Matthew Meszaros
|
88992c396c
|
feat: hand the node its env base64 encoded at join instead of as a raw JSON string, because the POSIX-sh join script has no JSON parser and its sed extraction appended a stray ","heartbeat_seconds line to node.env, and refuse a heartbeat that re-registers an existing node id under the other role, which would have left a worker's mailboxes assigned to a machine that had stopped doing worker work
|
2026-09-09 05:01:13 -07:00 |
|
Matthew Meszaros
|
58b7d81d33
|
Merge remote-tracking branch 'origin/main' into refactor/automatic-worker-mailbox-placement
|
2026-09-09 04:54:04 -07:00 |
|
Matthew Meszaros
|
435dbb522f
|
feat: replace the worker tier/type/risk-pool/egress categories with a scored placement model and make the fleet pull-based, so a machine joins with one command, workers and consumers share one node registry with usage and liveness, nodes self-update to the version the control plane resolves, and the Hetzner provisioning, worker profiles and SSH orchestrator are removed
|
2026-09-09 04:54:01 -07:00 |
|
Matthew Meszaros
|
db2a9ba439
|
feat: stamp the rotating campaign send's real sending mailbox onto tasks.email_account_id before dispatch so budgets, round-robin position, deliverability rates and the contact feed name the mailbox that actually sent, and stop the contact email list and campaign-state steps reporting automated prefetches as recipient opens (issue #392)
|
2026-09-09 03:55:28 -07:00 |
|
Matthew Meszaros
|
e783c3e3a0
|
feat: merge main into the email editor branch
|
2026-09-08 21:27:28 -07:00 |
|
Matthew Meszaros
|
95a4986d9e
|
feat: merge main into the verification reopen branch
|
2026-09-08 21:05:37 -07:00 |
|
Matthew Meszaros
|
e47937432f
|
feat: address the CodeRabbit review on the verification reopen PR by restricting the connection-triggered recheck to built-in verdicts so a verdict a paid verifier already produced is not re-spent when another connection is made, refusing to restore a connection the member disconnected while the credit round trip was in flight, and registering each live fixture's cleanup as it is written so a failed assertion cannot strand an integration row that then breaks the shared org fixture's teardown
|
2026-09-08 21:05:37 -07:00 |
|
Matthew Meszaros
|
9ce1e060dd
|
feat: fix the campaign step body editor for issue #380 by registering TipTap's UndoRedo and HardBreak so Ctrl+Z and Shift+Enter work at all, normalising pasted HTML from Gmail/Outlook/Word so their blank-line scaffolding stops rendering a second gap on top of our paragraph margins, adding a workspace email image library (migration 000139, public email-images/ objects sharing the attachment storage quota, keyset-paginated /email-images endpoints) with toolbar upload, URL and library insertion, drag-drop, clipboard paste and a size/alignment/alt bubble, and adding an HTML source view that names the tags the schema would drop before switching back
|
2026-09-08 21:00:09 -07:00 |
|
Matthew Meszaros
|
0e7525866a
|
feat: merge main into the select-all branch and rebuild the contact selection on top of its new keyset pagination, dropping this branch's own cursor repair now that Search carries the sort key and boundary value in a SortCursor, re-extracting the shared WHERE builder from that version, and pointing SearchIDs at the contactSorts spec and the conditional campaign-count lateral so a resolved selection orders and joins exactly the way the list does
|
2026-09-08 20:26:34 -07:00 |
|
Matthew Meszaros
|
5a1a726fdc
|
Merge branch 'main' into fix/reopen-verification-on-connect
|
2026-09-08 20:10:31 -07:00 |
|
Matthew Meszaros
|
c8042e2942
|
Merge remote-tracking branch 'origin/main' into feat/campaign-entry-delay
|
2026-09-08 09:58:38 -07:00 |
|
joao-crm
|
83ba4b38b4
|
fix: re-check the addresses the built-in probe left unknown as soon as a workspace connects a paid verifier, and withdraw the connection's degraded state once that verifier answers again, since a verdict reached before the connection existed otherwise waited out the 30-day shelf life while the credits sat unused and the card kept reporting whatever the provider said on the very first pass
|
2026-09-08 15:48:12 +00:00 |
|
Matthew Meszaros
|
37ed967919
|
Merge remote-tracking branch 'origin/main' into fix/issue-382-user-experience
|
2026-09-08 08:32:31 -07:00 |
|
Matthew Meszaros
|
4e52e9476b
|
Merge remote-tracking branch 'origin/main' into feat/campaign-entry-delay
|
2026-09-08 07:31:31 -07:00 |
|
Matthew Meszaros
|
78dd032a06
|
feat: address the CodeRabbit review on the contacts pagination PR by refusing a cursor boundary the SQL cast would choke on so a hand-made token is a 400 instead of a database error surfacing as a 500, writing timestamp boundaries with an explicit to_char pattern the Go validator mirrors so a token never depends on the server DateStyle, basing the full-screen error guard on rows loaded rather than rows left after the client-side subscription filter, keeping the failure visible when that filter hides every loaded row, and pointing Try again at the request that actually failed by reading isFetchNextPageError instead of guessing from hasNextPage
|
2026-09-08 06:53:57 -07:00 |
|
Matthew Meszaros
|
687aa6ec5e
|
feat: page through the contact list under a text query, the min and max campaign filters and a segment scope in the pagination regression test, since each appends bound parameters either side of the cursor's or compiles a SQL fragment of its own and a shifted placeholder would otherwise only show up in production
|
2026-09-08 06:00:16 -07:00 |
|
Matthew Meszaros
|
a6862477b3
|
feat: add a 'select all matching' selection to every contact list so a bulk action can cover the whole filtered set instead of only the loaded pages: a ContactSelection body (all + filters + exclude) on bulk update, bulk delete, verification, batch research, segment members and CRM push resolved server-side through a new SearchIDs that shares the search's WHERE builder, a Gmail-style banner and shared selection module in the contacts table, campaign Leads and segment members views, an uncapped select-all in the From contacts picker, and fixes for the keyset cursor comparing a row against itself (contact pagination repeated and skipped rows past page one) and the dashboard sending custom-field filters under the wrong wire name
|
2026-09-08 05:19:59 -07:00 |
|
Matthew Meszaros
|
ed50ad9f2f
|
feat: add a campaign entry delay so a contact's first email can wait a set time after they enter the campaign, with campaigns.entry_delay_minutes and a campaign_leads.added_at anchor (migration 000136), the delay applied in the router's per-lead due check and floored into the placer through ContactSequencePair.NotBefore, a distinct entry_delay constraint in the contact next-action preview, and the control surfaced on the Schedule tab, a new Trigger card at the top of the Steps canvas, the campaign wizard's Schedule step, the launch dialog, the update_campaign AI tool and the iOS schedule page, plus guides, API reference and live scheduler and repository tests
|
2026-09-08 05:06:04 -07:00 |
|
Matthew Meszaros
|
55894a0e9d
|
feat: fix the campaign Leads tab and contact export stopping partway through a large list by rebuilding the contacts keyset cursor: the boundary subquery named the outer row's alias so Postgres read it as a correlated self-reference and the whole comparison collapsed to c.id >= <cursor uuid>, serving each page the newest rows of a randomly shrinking id range until it ran out at a third of the leads, so the opaque token now carries the ordering it was taken under plus the boundary row's own sort value and id, rejects a token replayed under another sort, and pairs with a direction-following id tiebreak, an ORDER BY that names its NULL placement, a new (organization_id, created_at DESC, id DESC) index, EXISTS campaign and category filters and a lateral campaign_count the query only joins when a filter or sort asks for it, taking a page of a 50k-contact organization from 120ms to 3ms, and the dashboard now keeps the rows it already loaded when a later page fails and says how far through the list Load more is
|
2026-09-08 05:01:15 -07:00 |
|
Matthew Meszaros
|
7bac16aab0
|
feat: cover the segment import path with live tests: an import pins its inserted and skipped-as-duplicate rows into the target segment and the segment's own contact query returns them, an unknown segment is refused before any row is written while a recurring source skips one that is gone, and a sheet-sync source round-trips its segment targets through the new column and the ?segment_id= filter
|
2026-09-08 04:59:31 -07:00 |
|
Matthew Meszaros
|
6284eb27ec
|
feat: give a saved Google Sheets sync source segment targets (migration 000136 adds lead_sync_sources.segment_ids) that pin every synced row into those segments on each run, validated against the organization when the source is written and dropped from the run when a segment is deleted later, and filter GET /lead-sync/sources by ?segment_id= through a jsonb containment test so a segment can list the sources feeding it
|
2026-09-08 04:59:18 -07:00 |
|
Matthew Meszaros
|
2818cfa938
|
feat: encode a segment's empty condition list as [] rather than null in segmentRepository Create and Update, so creating the condition-less segment a static imported list wants no longer trips the jsonb array CHECK and answers 500
|
2026-09-08 04:59:10 -07:00 |
|
Matthew Meszaros
|
9729f608c0
|
feat: address the CodeRabbit review on the admin backend PR by carrying the keyset boundary inside the sync cursor token instead of re-reading the cursor row's mutable updated_at, clearing only a throttle that is still active so an expired one is not reported as cleared, counting a medium-only UTM attribution in the acquisition with_channel total the way the channel list already does, releasing the exact dedicated assignment the handler read by id so a binding created meanwhile is never released, and returning the underlying failure from the warmup engagement poller, heartbeat sync, danger zone and org transfer housekeeping loops so scheduled_job_runs records an error instead of ok
|
2026-09-07 21:56:23 -07:00 |
|
Matthew Meszaros
|
c26300ae5e
|
feat: backend half of the admin panel upgrade: delete the unrouted provisioning, releases, plan, discount and enterprise-inquiry admin handlers with their service and repository methods, retire the six admin permission bits nothing gated as reserved placeholders so live bit positions and existing masks are unchanged and IsSuperAdmin checks the live set, add forty admin endpoints for mailbox sync governor state with clear-throttle and restart-backfill, in-flight send reservations, cross-workspace dead letters with replay, task failures, webhook delivery health with reclaim, fleet capacity, the control loops decision log, dedicated bindings with release and the routed convert-to-dedicated, operator-driven workspace export and import, per-organization API keys and webhooks, warmup invalid-token abuse and action history, and signups by acquisition channel, and add a scheduled_job_runs table (migration 000135) with a jobrun package that every backend and consumer loop now records through and a run-now request the owning process picks up within fifteen seconds
|
2026-09-07 21:40:38 -07:00 |
|
joao-crm
|
b99997b51f
|
fix: match an inbound Message-ID with and without its RFC 5322 angle brackets in GetTaskByMessageID, since the outbound stamp always stores the bracketed form while an In-Reply-To header arrives either way depending on the replying client, so every reply whose brackets had been stripped missed its task, replied_at was never stamped, stop_on_reply stayed inert and a contact who had already answered kept receiving the rest of the sequence, and an empty header matched one of the 151 tasks carrying an empty message_id and linked the reply to an arbitrary campaign
|
2026-09-08 00:31:45 +00:00 |
|
Matthew Meszaros
|
8b757985f3
|
feat: address the review on the folder identity change by retiring a deleted folder's backfill floor with it, since a name is reusable and an inherited floor silently skips the next folder's history, by claiming a rename only when a UIDVALIDITY has exactly one missing folder and one new one, because two missing folders and one arrival cannot say which was renamed and guessing moves the wrong folder's mail, by deduplicating names before the folder cap rather than after so a name listed twice cannot spend a real folder's slot, by deleting on a legacy UIDVALIDITY-only event only when that number still names exactly one folder, and by moving the folder row and its mail in one transaction so a refused rename cannot leave the messages in a folder nothing renamed
|
2026-09-07 08:49:23 -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
|
f826cf0803
|
feat: state at the acquisition filter itself that it selects on the presence of a record rather than of a UTM tag, so the query, the search model and the admin panel's labels and Channel column all carry the same definition of a direct signup
|
2026-09-07 06:02:10 -07:00 |
|
Matthew Meszaros
|
8843e23138
|
feat: cover the acquisition record with live repository tests against a real schema, so the LEFT JOIN, the four columns added to the admin projection and the three channel filters are exercised rather than assumed, including that a second signup link cannot rewrite where a workspace came from and that the join does not fan out a row
|
2026-09-07 05:08:35 -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
|
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
|
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
|
d00960ad59
|
Merge feat/sentry-everywhere (with main) into the PostHog branch
|
2026-09-07 04:26:17 -07:00 |
|
Matthew Meszaros
|
be16f77f5d
|
Merge main into the Sentry branch
|
2026-09-07 04:25:41 -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
|
1a87720d5a
|
merge
|
2026-09-07 03:14:42 -07:00 |
|
Matthew Meszaros
|
f2c35cf872
|
feat: sync every kind of IMAP mailbox (issues #345-#349): connect servers without CONDSTORE by following UIDNEXT per folder and mirroring read state with a periodic flag scan, so Outlook.com, Microsoft 365 over IMAP and Yahoo work instead of failing at load; fall back to a STATUS per folder when the server has no LIST-STATUS, which silently made an account look empty; bound the wait between IMAP responses so a peer that vanishes without a FIN cannot park a command forever; keep the inbox and special folders when a mailbox has more folders than the cap (raised to 100) and relay the overflow as a warning instead of failing the mailbox silently; recognize localized folder names and the server's own hierarchy delimiter so a Sent folder called Gesendete Elemente is not filed as inbox; and back off on a widening interval while a mail server is unreachable so one outage is one warning rather than one a minute
|
2026-09-07 03:12:22 -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
|
54b5cba9a5
|
feat: address the CodeRabbit review on the continuous campaigns PR by clearing idle_since in the same update that turns the continuous setting off so the dashboard stops reading a campaign as waiting for leads, adding idle_since to the documented campaign response, qualifying the start endpoint's nothing-left-to-send 400 for continuous campaigns, and noting in the automations and forms guides that a finished campaign restarts through the launch checks and a refusal is written to its activity log
|
2026-09-06 02:58:33 -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
|
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
|
508ee741cf
|
Merge remote-tracking branch 'origin/main' into fix/salvage-expired-batch
|
2026-09-05 21:33:20 -07:00 |
|
Matthew Meszaros
|
c88118661f
|
feat: record an admin cancel on a warmup generation batch job through a conditional MarkBatchCancelling update that only touches a job still running with a non-terminal batch status, so a cancel racing the poller can no longer overwrite the completed state and counts the poller just wrote or send the job back into the active set to be ingested twice
|
2026-09-05 21:24:11 -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
|
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
|
b35e111705
|
fix: address the review on the CLI PR: give the sign-in handshake its own per-IP budget so a 200-poll login cannot lock the address out of the browser login, keep https for a remote host that names a port instead of sending a bearer token in cleartext, report truncation when a paginated walk stops at max-pages, accept a piped secret with no trailing newline, normalise WEBSOCKET_URL on its suffix so a bare /socket becomes a real endpoint, destroy a minted secret the moment its code expires, gate cli-installer-ci on the required status check, print a rejected flag instead of an unbound-variable error, and use a portable sha256 so the packaging runs on macOS
|
2026-09-04 21:03:58 -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 |
|