Commit Graph
2554 Commits
Author SHA1 Message Date
Matthew Meszaros c91e89980f feat: make reply recording atomic and require exact repair recipient matches 2026-09-16 21:25:43 -07:00
Matthew Meszaros b01984bec2 feat: verify reply direction from persisted mailbox state and repair issue 549 false Sent-folder replies 2026-09-16 21:11:54 -07:00
Matthew Meszaros baf38a4b03 Merge pull request #557 from warmbly/feature/self-host-instance-linking-upsell
Offer subscribing alongside self-hosting, and hide the self-host nudge from subscribers
v0.4.20
2026-09-16 17:56:40 +00:00
Matthew Meszaros be78d46e28 feat: hide the self-host nudge on the hosted Accounts page for subscribed workspaces and offer subscribing alongside self-hosting on free ones in CloudPathsPanel 2026-09-16 19:53:35 +02:00
Matthew Meszaros 0d331cd528 Merge pull request #556 from warmbly/feat/tester-join-existing-workspace
feat: a tester can join an existing workspace instead of always getting an empty one
2026-09-16 17:50:07 +00:00
Matthew Meszaros 1196c23f92 Merge pull request #555 from warmbly/fix/contact-activity-timeline-followup
Fix contact timeline access regression coverage
2026-09-16 17:47:35 +00:00
Matthew Meszaros de8da6396e feat: build the tester sign-in address from DASHBOARD_URL instead of rewriting the admin origin's first label to dev., which handed an OAuth reviewer a different deployment than the dashboard every other link in the panel points at 2026-09-16 19:40:14 +02:00
Matthew Meszaros 03f813dd43 feat: let an admin create a tester account that joins an existing workspace with an explicitly chosen role instead of always minting an empty one, so an OAuth reviewer signing in lands in a workspace that shows the app doing real work 2026-09-16 19:37:32 +02:00
Matthew Meszaros 5072f27b59 feat: correct the AGENTS.md codec note that still claimed Avro cannot serialize the worker command and result envelopes, which the derived union schemas in internal/models/event_schema.go made false 2026-09-16 19:37:32 +02:00
Matthew Meszaros e0ddcd79d2 feat: register contact timeline live-test cleanup before fixture setup can fail 2026-09-16 10:36:37 -07:00
Matthew Meszaros cf1429f571 feat: cover contact timeline access at the HTTP boundary and centralize campaign event tenant scoping 2026-09-16 10:27:18 -07:00
Matthew Meszaros d3cd675d80 Merge pull request #554 from warmbly/fix/worker-disconnect-error-reporting
Fix ResizeObserver error reporting and cloud worker outage notifications
2026-09-16 15:51:53 +00:00
Matthew Meszaros cadf8f849a Merge pull request #553 from warmbly/fix/mail-sync-rate-limit-handling
Fix provider mail throttles without deactivating mailboxes
2026-09-16 15:51:27 +00:00
Matthew Meszaros 0334d2c64a feat: prevent ResizeObserver feedback warnings from reaching PostHog and scope worker outage alerts per workspace on shared cloud workers 2026-09-16 17:42:38 +02:00
Matthew Meszaros 817599d0eb feat: keep provider mail throttles retryable without deactivating mailboxes or flooding error tracking 2026-09-16 17:42:12 +02:00
Matthew Meszaros 27e9e0d7cb Merge pull request #551 from warmbly/fix/contact-activity-timeline
Fix contact activity timelines for workspace teammates
2026-09-16 15:39:48 +00:00
Matthew Meszaros 75d6799f09 Merge pull request #552 from warmbly/fix/issue-root-cause
fix: prevent synced sent follow-ups from counting as replies
2026-09-16 15:35:47 +00:00
Matthew Meszaros 106e3c0b9d feat: cover organization scoping for per-event timeline opens and clicks in the issue 550 live regression 2026-09-16 08:30:35 -07:00
Matthew Meszaros 2255e2145d feat: reject outbound mailbox copies and mismatched campaign threads before they can mark contacts replied for stop-on-reply (issue #549) 2026-09-16 08:24:19 -07:00
Matthew Meszaros 6762b4e491 feat: scope contact activity timelines to the selected organization so teammates can load contacts and campaigns created by other members 2026-09-16 08:20:31 -07:00
Matthew Meszaros 4597156b6f Merge pull request #548 from warmbly/fix/dev-cloud-dashboard-server-errors
feat: fix dashboard Stripe checkout, portal eligibility, webhook recovery and audit rendering
v0.4.19
2026-09-16 13:31:04 +00:00
Matthew Meszaros 15a130e446 Merge pull request #547 from warmbly/fix/login-argon2id-hash-format
feat: reject password login for accounts without a password hash before Argon2 verification
2026-09-16 13:25:19 +00:00
Matthew Meszaros 6872ebbb23 feat: fix dashboard Stripe checkout, portal eligibility, webhook recovery and audit rendering 2026-09-16 15:23:17 +02:00
Matthew Meszaros d96e3182f8 feat: run required CI checks for merge queue groups so validated pull requests can merge through branch protection 2026-09-16 15:15:18 +02:00
Matthew Meszaros 1fae8b18c2 feat: reject password login for external accounts without a stored hash and cover credential verification with PostgreSQL regressions 2026-09-16 15:03:21 +02:00
Suman Jana 94c7e414e2 feat: preserve private Unibox reply drafts and safely collapse quoted conversation history 2026-09-16 04:35:53 -07:00
Matthew Meszaros 31c1f101c2 feat: give EmailSentEvent and WarmupEmailSentEvent their own derived Avro schemas so the email-events and warmup-events analytics streams stop failing at serialize on every send and finally register a subject (#546) v0.4.18 2026-09-16 04:20:15 -07:00
Matthew Meszaros 140c7de436 feat: add the admin panel's Promo codes page and route the six /admin/discounts endpoints that existed as handlers but were never wired, so a launch offer is built in the operator UI instead of an INSERT against production, with caps that an explicit null can actually clear on PATCH 2026-09-16 04:04:09 -07:00
Matthew Meszaros 561f8ec671 feat: keep cloud and local warmup mail out of Unibox with durable verification and automatic cleanup 2026-09-16 03:55:36 -07:00
Matthew Meszaros f72d1f8d5c feat: prevent sender views from tracking opens and keep sent messages out of the default Inbox (#542) 2026-09-16 03:42:58 -07:00
Matthew Meszaros def0a5e5f1 feat: restore workspace campaign access and enforce shared analytics permissions (#543) 2026-09-16 03:31:01 -07:00
Matthew Meszaros ddd76eeb73 fix: stop a Redis outage locking everyone out, and let the tracking encoder register its schema (#537)
* feat: treat a session cache that cannot answer as a miss rather than an authentication failure, because the session lives in Postgres and reading it from there is what the caller already falls back to, so a Redis outage costs a database read per request instead of locking every user out of the dashboard with a 500

* feat: carry the tracking event schema with the subject-name strategy so the encoder registers it instead of only looking one up, because a registry with no tracking-events subject had nothing to find and answered every event with 'Could not get id from response', dropping every open and click on the floor
v0.4.17
2026-09-15 21:19:03 -07:00
Matthew Meszaros 746dd40469 feat: strengthen the Avro round-trip tests after a cutover they failed to catch (#536)
* feat: compare the decoded event body's fields and not only its type, fill arrays so every uuid carries a real value instead of the zero one a codec could drop unnoticed, and decode once in a process that has never encoded, because production is four processes and one of them only ever reads what another wrote

* feat: register the union body types at package load instead of on first schema build, which is what a process that only ever decodes never reached, so every worker command arrived as a map keyed by its branch name, went through the JSON fallback, and became a struct with every field zero and no error anywhere
v0.4.16
2026-09-15 20:25:21 -07:00
Matthew Meszaros f106c8541d feat: make the bus envelopes Avro-encodable (#535)
* feat: make both bus envelopes Avro-encodable by deriving each one's schema from a declared registry of body types, with a union branch per body and our own struct walk that skips unexported fields and honours avro:"-" before descending, so the schema describes exactly what encoding/json already puts on the wire, and narrow the two sync cursors on the wire DTOs to int64 because Avro has no unsigned 64-bit type

* feat: stop the instance health check, the config registry and the docs all claiming Avro cannot serialize a worker envelope, which stopped being true once the envelopes carried a declared union, and check the one thing that is still a real misconfiguration instead: avro selected with no SCHEMA_REGISTRY_URL to resolve against

* feat: emit a reference the second time a record appears in an envelope schema instead of defining it again, because Avro names a record once and a document that defines warmbly.events.Token three times is rejected outright, and keep the Schema Registry round-trip as a skip-by-default test since only a registry judges the document rather than the objects it was built from

* feat: carry uint64 as Avro fixed(8) rather than long, which lets the sync cursors keep their unsigned type instead of being narrowed, name every event field after its json tag so the schema and the JSON wire agree, and populate every field in the round-trip test because zero values are why a uint64 mapped to long passed in the first place

* feat: frame Avro in Confluent's wire format and encode through hamba's default API instead of going through avrov2, whose private avro.API holds a type resolver avro.Register cannot reach, so a union body failed there with unable to resolve type while encoding cleanly against the same schema, and keep the registry round-trip as a skip-by-default test
v0.4.15
2026-09-15 10:50:30 -07:00
Matthew Meszaros 16df37d97b fix: stop the unibox reply composer wiping what you are typing (#534)
* feat: stop the unibox reply composer clearing what is being typed, by resetting only on a restore seed instead of on values derived from replyTo, which the thread rebuilds on every render, so any realtime invalidation while a thread was open wiped the draft between keystrokes and a reply could not be written at all

* feat: drop the stray blank line left where the per-render messages build used to sit in ThreadView
2026-09-15 09:41:54 -07:00
Matthew Meszaros ae012dd13f Clear the live error-tracking issues, and the workspace rename that renamed the wrong workspace (#533)
* feat: stop a managed Kafka cluster refusing topic creation from failing the publish, by treating a topic- or cluster-authorization failure from CreateTopics as a topic the cluster owns rather than one that is missing, which on Confluent Cloud dropped every warmup event and filed one issue per message because the topic never became known

* feat: drop a report whose error is a cancelled context in errs rather than at ninety call sites, so a browser navigating away or a container draining on deploy stops filing one issue per query that happened to be in flight, while a deadline this process set and blew through still reports

* feat: stop renaming one workspace from renaming another, by keying the workspace settings editor on the workspace id so a switch re-seeds the name field instead of leaving the previous workspace's name against the new workspace's autosave baseline, and pinning every save on the workspace, sending and tracking pages to the workspace its draft was hydrated from

* feat: drop Script error. and the ResizeObserver notice on the marketing site and the hosted form page the way the dashboard and admin panel already do, since those two carry no stack and no bug and between them were the largest issues in error tracking, all of it from warmbly.com

* feat: rename the forms Turnstile script module to turnstileScript.ts so it no longer differs only in case from the Turnstile.tsx component, which resolved both imports to one file on a case-insensitive filesystem and failed forms' typecheck with TS1149

* feat: upload source maps from the static build:pages build as well as the image build, so the dashboards served from a static host stop reporting every stack frame as a minified name beside 'Invalid source map: bad json', which is PostHog falling back to fetching the .map from a host that answers with its SPA fallback

* feat: build every admin list in pg_admin.go with make rather than declaring it nil, so an empty page serializes as [] instead of null, and guard the audit table's own empty check, which is what crashed admin.warmbly.com/audit with 'null is not an object (evaluating d.data.length)' whenever a filter matched nothing

* feat: match the whole broker description rather than a substring when deciding a topic create was refused for permissions, since that answer remembers the topic as present, and clear the cached promise and dead tag when the forms Turnstile script fails to load so a blocked first attempt no longer leaves every later mount with the same rejection and the captcha permanently missing
2026-09-15 09:05:53 -07:00
Matthew Meszaros c4c58cc116 feat: report a failed dashboard socket handshake with its actual error, status, code and request id instead of the plain AppError object that console.error rendered as '[WS] Init failed: [object Object]', and keep an offline blip or an already-expired session a warning so only an unexpected answer reaches error tracking (#532) v0.4.14 2026-09-15 05:46:07 -07:00
Matthew Meszaros 3bcd0e0362 feat: stop the consumer reporting an incident every time a worker relays a mailbox error for a mailbox that was just deleted, by treating SQLSTATE 23503 on the email_account_errors insert as a declined write like the duplicate case already is, matched through errors.As so a wrapped driver error still counts 2026-09-15 13:46:37 +02:00
Matthew Meszaros bc89748ec5 feat: add scripts/deploy-railway.sh, the ordered Railway rollout that preflights every image is anonymously pullable then rolls backend first for its boot migrations, gating each service on its deployment reaching SUCCESS on the target tag 2026-09-15 13:39:05 +02:00
Matthew Meszaros e2487296d6 feat: rename the campaign entry-delay picker to EntryDelayPicker.tsx and update its three importers, so the component no longer differs only in case from the entryDelay.ts vocabulary module, which resolved every import of it to the wrong file on a case-insensitive filesystem and failed web's typecheck with ten errors 2026-09-15 13:39:05 +02:00
Matthew Meszaros 179af5815f feat: type the four AdminUserPreview slices as nullable in the admin panel's model so a backend older than the empty-slice fix in pg_admin.go cannot crash a page through an unguarded email_accounts.length again 2026-09-15 13:39:05 +02:00
Matthew Meszaros 8740558ffa feat: stop the dashboard socket's onerror handler reporting every WebSocket error event to PostHog as an exception, since the event carries no detail by spec and onclose already drives the reconnect, so a deploy or a laptop sleep logged '[WS] Error: [object Event]' through capture_console_errors 2026-09-15 13:39:05 +02:00
Matthew Meszaros 853dd29474 feat: answer the forms host's bare root with a short styled page instead of gin's plain-text 404, take over every other unknown path with the same page shape while /api keeps answering JSON, cover both in internal/formserver tests, and document what a visitor who types the forms domain sees 2026-09-15 13:39:05 +02:00
Matthew Meszaros 2f15fecaad feat: document the MaxMind download endpoint that a current licence key actually authenticates against, account id as the username and key as the password in the URL userinfo, because the app/geoip_download permalink every guide still shows refuses a key issued today with Invalid license key (#530) 2026-09-15 03:37:14 -07:00
Matthew Meszaros 4784ee7d39 feat: fetch the MaxMind databases instead of requiring a mounted file (#529)
* feat: let the backend, consumer and tracking service fetch their own MaxMind databases from GEODB_URL and TRACKING_SCANNER_ASN_DB_URL, reading the archive shape from the content so a permalink tar.gz, a gzipped mmdb and a bare mmdb all work, never replacing a file already at the path, opening the bytes before installing them so a licence-key error page cannot become the database forever, skipping the AppleDouble sidecars a macOS tar writes ahead of the real file, and treating both URLs as secrets because the permalink carries the licence key

* feat: drop the trailing blank line cargo fmt --check rejects at the end of tracking/src/asndb.rs

* feat: stream the downloaded ASN archive instead of decompressing it whole, sizing each buffer from the gzip footer and the tar header so the member is allocated exactly once, which drops the peak of unwrapping a permalink tar.gz from 38 MB to 11.9 MB, essentially the database itself

* feat: stop the MaxMind licence key reaching the logs through net/http's and reqwest's own error text, which both print the URL they were given and so defeated the redaction beside them, drop userinfo as well as the query when redacting, refuse plain http for a URL carrying a credential and refuse an https-to-http redirect, and apply the size cap to the decoded database rather than the compressed transfer so a gzip bomb cannot fill the disk

* feat: strip basic-auth userinfo as well as the query when the tracking service redacts its database URL, parsing it rather than cutting at the first question mark so where a credential sits is the URL library's problem and not a guess
v0.4.13
2026-09-15 03:06:34 -07:00
Matthew Meszaros e67b13e57e feat: stop reporting a mailbox's DKIM as missing when its selector was simply never probed, by deriving candidate selectors from the sending domain's own SPF and MX records on top of a wider default set, reporting a miss as the tri-state dkim_status undetermined rather than a red Missing row in the drawer, dropping DKIM from the Advisor's missing-records finding entirely, refusing a revoked p= key, holding the summary back from accusing anything when DNS never answered, and fixing the CLI auth-check table whose columns read mailbox fields the endpoint does not return (#528) 2026-09-15 02:27:38 -07:00
Matthew Meszaros d40a95dff5 fix: give the dashboard's auth errors a real stack and stop reporting an ended session as a crash, by building AuthError per throw instead of sharing two module-level instances whose stack was captured at module evaluation, so every report pointed at "module code" rather than the call that failed, and by dropping AuthError in before_send since normalizeError already turns it into a redirect and UserProvider sends the user to sign in (#527) 2026-09-15 01:52:35 -07:00
Matthew Meszaros 0a1ed6f04e feat: resolve scanner ASNs from a GeoLite2-ASN database so the catalogue's asn: entries match without a Cloudflare transform rule, ship the Proofpoint, Mimecast and Cisco ASNs enabled behind a new probable certainty that widens the consumer's machine window instead of deciding the verdict, make the tracking event dedupe claim one coalesced operation, and report an ASN database that opened cleanly but resolves nothing (#440) 2026-09-15 01:48:45 -07:00
Matthew Meszaros dd98187231 fix: shorten recipient unsubscribe links to 22-character, 128-bit stored tickets (#498) (#525)
* feat: shorten every recipient unsubscribe link from a 96-character signed token to a 22-character stored ticket carrying 128 bits from crypto/rand, minted once per recipient per campaign and reused by every step, so the address the text/plain half of a cold email prints in full fits on one line and cannot be guessed, keeping the signed form working for links already in inboxes and as the fallback when the store cannot be written, and answering a failed lookup with a retryable 'try again shortly' instead of telling the recipient their opt-out is invalid (issue #498)

* fix: restore the disabled-signer guard in URLOn, which factoring the URL builder moved behind a token mint that dereferences the signing key, so a nil or origin-less signer returns the empty string every caller reads as 'no link can be minted' instead of panicking (PR #525 review)
2026-09-15 00:42:45 -07:00
Tung Lam dd4234980b fix: reconcile expunged IMAP drafts so a Gmail autosave replacement stops leaving duplicate copies in a thread, by diffing each drafts folder's live UID set against the rows the backend holds for that UIDVALIDITY generation and removing the ones the server no longer reports, only in drafts and only when the selected generation still matches the listing (issue #516) 2026-09-15 00:00:20 -07:00