Commit Graph
132 Commits
Author SHA1 Message Date
Matthew Meszaros 9426c0da51 feat: validate every person, workspace and company name through internal/pkg/displayname on each write path (profile, onboarding, setup, IdP sign-in, org create and rename, org import, enterprise inquiry, admin testers, warmblyctl) with a 400 invalid_name code, render stored names in platform email through the same rules, mirror them in the web forms, check the org slug format, and document the rules in error-codes, security and AGENTS.md 2026-09-21 03:34:03 -07:00
Matthew Meszaros f11df9268f Merge pull request #640 from warmbly/fix/admin-list-pagination
feat: make every admin panel list page past the first and filter by id, and report failed admin requests
2026-09-21 09:29:48 +00:00
Matthew Meszaros 4dde9708c9 feat: honour an ascending created_at sort on the admin users list with a matching id tiebreak, and record an admin API failure whose call omitted the method as GET, the method axios sent 2026-09-21 02:24:36 -07:00
Matthew Meszaros cc244e5159 feat: make every admin panel list page past the first and filter by id: bind query-string ids through models.ParamUUID since gin cannot set a uuid.UUID, page the explorers and secondary lists by an opaque offset cursor with an id tiebreak instead of an id keyset that disagreed with the sort, page the audit log on (created_at, id) with an inclusive YYYY-MM-DD end day and read next_cursor on its page, cast every before-date bound to timestamptz, coalesce nullable audit ip and user agent, and report failed admin queries and 5xx mutations to PostHog or Sentry with method, path, status, code and request id 2026-09-21 02:11:38 -07:00
Matthew Meszaros 0a5e7947b4 feat: return a failed warmup retention delete from the worker so the bus redelivers it up to five times, re-key a Graph message in the map on every move so the sender copy's body can be dropped, never expunge a whole IMAP folder for one message (UID EXPUNGE, else MOVE to Trash, else refuse), build the two retention indexes concurrently in their own migrations 000193 and 000194, keep the dashboard stepper off 1 and 2 days, and describe retention as applying wherever the placement files warmup mail 2026-09-21 01:11:22 -07:00
Matthew Meszaros 1513419a2a feat: delete warmup mail from each mailbox once past a per-mailbox retention window (email_accounts.warmup_retention_days, else retention.warmup_mail_days, default 30) via a consumer sweep that retires the receipt and sender copy and a worker delete action that trashes on Gmail, deletes on Graph, expunges on IMAP and drops the stored body, prune per-message warmup records after retention.warmup_event_days, and count a warmup deletion as tampering only within 24 hours of arrival and never for a retired message, judging Gmail's Trash label on the same rule 2026-09-21 00:52:02 -07:00
Matthew Meszaros eceed10d89 feat: invoke the PostHog CLI as pnpm dlx @posthog/cli@<version> sourcemap ... in the web, admin and forms source map scripts, because pnpm dlx --package <pkg> <bin> is only understood by pnpm 10 and later and Cloudflare Pages builds the dashboards with an older pnpm that reads --package as the package name and fails the deploy on a registry 404 2026-09-19 17:23:10 +02:00
Matthew Meszaros 834da184d9 feat: clear every dependency advisory that has an upstream fix, dropping the AWS SDK's legacy-rustls-ring default feature that was pulling a second hyper 0.14, rustls 0.21 and rustls-webpki 0.101 into the tracking service alongside the current ones, moving async-nats to 0.50 for the last old webpki and reqwest to 0.12, boxing the NATS producer variant the bigger client made oversized, refreshing the node trees with overrides for the esbuild and postcss-selector-parser that fumadocs pins, recording why the two unpatched cowlib advisories cannot be reached from a service that sets no cookie, and deciding the credential-validation timeout from the subscription context's deadline rather than the error's shape 2026-09-19 13:25:33 +02:00
Matthew Meszaros acecd62c88 feat: scope mailbox disconnect and warmup lifecycle to the workspace rather than the member who connected the mailbox so an admin can act on every mailbox the list already shows them, evict a mailbox whose row is gone from every live worker when its provider errors arrive so a deleted mailbox stops calling the provider once a sync interval forever, subscribe before publishing the credential-validation job and classify a socket deadline as the retryable timeout it is, give the worker's validation reply its own budget so a slow mail host no longer loses a finished verdict, guard every global key handler against a keydown carrying no key, drop exceptions whose whole message is an object's default toString, make the Postgres pool size configurable, and record the CASA and security invariants in AGENTS.md 2026-09-19 12:49:46 +02:00
Matthew Meszaros e668a2a36b feat: complete the ADA CASA v2.1.1 AL1 control set across authentication, sessions, access control, cryptography, input validation and configuration, adding a breached-password denylist and per-account login throttling, enforced multi-factor authentication on the admin panel, step-up confirmation before an action that mints a lasting credential, purpose-scoped session tokens, single-use TOTP steps, tenant verification on every cross-referenced identifier, security headers on every surface, encrypted webhook signing secrets, per-organization idempotency, PKCE and a minimal two-scope Gmail consent on the mailbox OAuth flow, bounded spreadsheet and archive decoding, a patched Go toolchain with govulncheck in CI, and the evidence pack under compliance/casa 2026-09-19 08:18:35 +02:00
Matthew Meszaros 49acd51b64 feat: stop one recurring fault burying error tracking by reporting it once per five minutes with the count it stands for, keep a cache outage from answering every signed-in request with a 500 and from taking realtime down by treating an unreachable Redis as a miss and the websocket handshake nonce nothing reads as best-effort, answer a 5xx with a sentence the reader can act on while the call site's own words go to the log against the same request id, prefer the API's own message over the HTTP class in the admin and dashboard clients, and name the fix on a schema registry refusal, an SES sandbox rejection and a mailbox check that could not be run 2026-09-19 07:39:40 +02:00
Matthew Meszaros d6025ea4c0 feat: address worker capacity review findings with safe migrations and recovery reporting 2026-09-17 06:24:14 -07:00
Matthew Meszaros bab9f86727 feat: correct worker capacity, mailbox distribution, observed IPv4, fleet pagination, and premium pool promotion 2026-09-17 04:15:05 -07: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 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 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 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 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 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 92e298b6ec fix: clear every open issue in error tracking by fixing the bugs behind them rather than the reports: a document-level mouseleave handing RippleProvider the document itself, whose classList is undefined; the admin panel posting /getaway without the /v1 its baseURL omits, so its realtime socket 404d on every page; Gmail throttles classified from the 403 status alone and told to re-authorize instead of back off; consumer flag and folder events retrying forever on a message the unibox never stored; a lost token-refresh race answered 500 instead of the documented 401; a nil email_accounts slice crashing the admin user page; Turnstile mounted with an empty sitekey; conditional passkey autofill run after the user navigated away; a boot log filed as an issue; and one publish failure per message on a topic the broker refuses (#519) 2026-09-14 21:06:14 -07:00
Matthew Meszaros d74d5e6836 fix: retire the warmup spam score, a counter that grew with volume rather than misbehaviour and that no band could act on (#508)
* fix: retire the warmup spam score, a ratchet that grew with volume rather than misbehaviour and that no band ever read, dropping the column from the pool row and the reputation ledger and explaining a pool finding with the band's own reason instead (#491)

* test: pin the advisor snapshot's pool columns against the scan, since the band's reason now reaches the finding through that select alone (#491)

* fix: hold a warmup sentence's score and reason with the sentence itself, keep the retired spam_score key on the published analytics payload as a deprecated zero, seed the sandbox with severity-shaped scores, and record the raw spam report when the warmup service is absent (#491)
2026-09-14 07:44:34 -07:00
Matthew Meszaros 7300b3b021 feat: full PostHog coverage: identify the signed-in user and workspace in the dashboard and admin panel with autocapture, heatmaps, dead and rage clicks, web vitals, network timing, console capture and session replay masking only password fields, send server-side signup, trial and subscription events under the user id with the organization as a group, keep the marketing site and form pages cookieless while capturing everything stateless plus a form funnel, upload the form app's source maps, and add WARMBLY_POSTHOG_SESSION_REPLAY 2026-09-13 20:58:22 -07:00
Matthew Meszaros 1dc4aedc3c fix: retire the warmup invalid-token band with its table, metric query, service and repository methods and admin tab, since nothing has fed it since #481 and no attributable forged-token signal exists; key the live pool fixtures on the canonical pool ids so the warmup, repository routing and tasks routing suites run on a fresh database, and correct every doc, site and advisor line that still described the retired signal or a spam-score threshold nothing implements (#490) 2026-09-13 08:09:01 -07:00
Matthew Meszaros 8799680166 fix: never charge a mailbox for a warmup token that arrived in its inbox, hold a quarantine or block for its full term against fresh metrics, and keep a penalised address's standing across removal, pool exit and export through a trigger-maintained mirror, since the recipient never controlled the token, the bands read seven days against 30-day terms, and the pool row died on paths a snapshot at deletion never saw (#481) 2026-09-13 04:34:44 -07:00
Matthew Meszaros 43dcbde06c feat: tester accounts, creatable from the admin panel (#483)
* feat: excuse one named account from the emailed login code, so a vendor reviewer who cannot read this instance's mail can sign in without turning codes off for everyone, with the reason recorded beside it and every run of warmblyctl status naming the accounts that hold one

* feat: create and manage tester accounts from the admin panel, so letting a reviewer in is a form rather than a shell, with the password shown once and every live exemption listed on one page because forgetting one is the way this goes wrong

* fix: give tester management its own permission bit rather than borrowing ban_users, create the account and its exemption in one transaction so no invisible orphan survives a failure, require an accountable operator on the CLI grant, stop a halted row scan reading as the whole exempt list, and show a failed query as an error instead of as no testers

* chore: re-run CI after the aggregator tripped on a cancelled job from the branch update, with every underlying job green

* chore: retrigger CI, the previous run sat queued indefinitely while other branches ran

* feat: roll back a half-created tester when its workspace step fails and backfill the manage-testers bit onto admins already holding every other permission, so the address is not left taken by an unusable account and the new routes are not 403 for the existing admin

* feat: make the 000151 manage-testers backfill one-way, because clearing bit 22 on the way down would also revoke it from an admin granted it explicitly afterwards and the up migration would not restore that
2026-09-13 03:07:10 -07:00
Matthew Meszaros 7f74664c72 fix: a granted plan unlocks nothing, and Turnstile never renders (#474)
* fix: let a granted plan unlock the dashboard, since the client decided paid from the Stripe status a managed subscription never touches, and replace the Turnstile size Cloudflare removed so the widget renders and can issue a token at all

* feat: tell people on a preview deployment that it is a public beta, once in a dialog and thereafter as a header pill they can reopen, driven by a config value rather than a hostname so one image stays reusable, and bind both Turnstile modals through onLoad because the component is not forwardRef and execution=execute never fires without the widget instance

* fix: keep the beta pill outside the desktop-only header group so the notice stays reopenable on a phone, and say in the docs that the value is baked into config.js at container start rather than read per load
2026-09-12 21:45:05 -07:00
Matthew Meszaros 017f4cf60f feat: plans an operator can grant, visible in the admin panel (#467)
* feat: add operator-granted plans so a workspace can be paid without Stripe, surfaced in the admin panel as a badge, a filter and a card carrying who granted it and why, because the only alternative was writing a fake stripe subscription id into the database

* fix: hold a granted plan beside the paid one rather than over it so a Stripe workspace returns to the plan it pays for when the grant ends, route entitlement lookups through EffectivePlanID, separate a repository failure from an unknown plan, end a grant at local end of day, and drop an index that served no query
2026-09-12 09:45:31 -07:00
Matthew Meszaros 47defafa09 feat: fix the six self-host defects reported in issue #439 (#456)
* feat: fix the six defects reported in issue #439 by mapping the IMAP UNAVAILABLE, INUSE and NONEXISTENT response codes to retry-level errors instead of a critical reconnect prompt, synthesising a stable no-msgid key so one message with no Message-ID header can no longer 400 the internal map endpoint and wedge every later sync pass with its cursors held, adding mailhtml.FromText and HasContent so an API or agent-created step with a plain body stops shipping the composer's empty div placeholder as its text/html part (derived on create and plain-only update, exposed as body_html on update_campaign_step, dropped at send and preview time, and refused at campaign start with empty_step_body), honouring sender_strategy='explicit' in ResolveCampaignSenderPool and ValidateCampaignReady so an emptied explicit pool parks the campaign instead of widening it to every mailbox in the workspace, making the paused_no_accounts auto-pause loud with an error log line, an error-level activity-feed entry and an org-scoped CAMPAIGN_PAUSED realtime pulse, gating the admin sign-in's Turnstile widget on GET /v1/auth/config so a self-host with CAPTCHA_PROVIDER=none is not locked out, and parsing NATS_URL down to its host:port so a credentialed bus URL no longer reports NATS down

* feat: act on the self-review of the issue #439 fixes by dropping the campaign wizard's own escapeHtml body_html builder, which entity-escaped the quotes in a conditional and made the template fail to parse at send time, and letting the backend's FromText render that part instead so wizard-written steps also get their bare URLs linked for click tracking, correcting the docs and openapi description that claimed an explicit sender pool never falls back when it still unions its tags as migration 000013 designed, extracting the duplicated blank-HTML-part guard into dropBlankHTMLPart shared by the send path and the preview, and recording why the no-msgid key keeps the folder name despite a RENAME changing it

* feat: address the CodeRabbit review on the issue #439 fixes by holding the admin sign-in's Turnstile widget unmounted until /v1/auth/config resolves so an instance with no route to Cloudflare cannot raise a widget error on a screen nobody submitted, failing StartCampaign closed when the sequence read errors rather than skipping both the malformed-template and empty-body refusals, giving TCPCheck the default port its protocol assumes so a portless NATS_URL is no longer reported down, leaving a URL that carries a merge field unanchored because the send path renders bodies with text/template and a quoted contact value would break out of the href, and correcting the sequences guide and the Campaign and CampaignUpdate openapi descriptions that named the wrong tag field
2026-09-12 03:13:38 -07:00
Matthew Meszaros 80c3c79a31 feat: add live coverage that the machine windows survive the settings document's jsonb round trip and reach the classifier, a guard that every shipped scanner CIDR is written as its own network address since the loader truncates host bits silently, and correct the comments that claimed an edit lands on the very next event when the consumer reads through a thirty second cache in its own process 2026-09-11 03:11:35 -07:00
Matthew Meszaros 184a3dc08e feat: make the automated-open and automated-click windows operator-editable under Instance settings and raise their defaults to 60s and 30s, because the ten-second window was anchored on dispatch to the worker rather than on delivery and routinely expired before the recipient-side gateway it was meant to catch had even seen the message, and add Barracuda's published Email Gateway Defense blocks to the scanner catalogue with Proofpoint, Mimecast and Cisco shipped commented out because browser isolation renders a clicked page from the vendor's own network 2026-09-11 02:53:34 -07:00
Matthew Meszaros 170c33780a feat: pass ui_host to PostHog in the dashboard, admin panel and marketing site from its own environment variable so a proxied api_host stops breaking toolbar and session-replay links, which the SDK builds against whatever it sends events to and which a reverse proxy does not serve, defaulting to us.posthog.com so an install that does not proxy is unaffected 2026-09-11 11:34:02 +02:00
Matthew Meszaros ced741e352 feat: make PostHog the default error tracker across every runtime while keeping Sentry fully supported alongside or instead of it, by turning internal/observability/errs into a two-sink fan-out with a local-log fallback, adding $exception capture to the Go services, the Rust tracking service, the Elixir realtime service and the dashboard, admin and form apps, reporting gin panics with their route, request id, workspace and user, attaching that identity plus a route and failed-request trail to browser exceptions, and wiring POSTHOG_ERROR_TRACKING, the node join env, compose, source-map upload and the docs to match 2026-09-10 19:11:32 +02:00
Matthew Meszaros bbe9d9055a feat: let web and admin be served from a static host by teaching each app's own entrypoint to render config.js wherever WARMBLY_CONFIG_OUT points, so one definition of the runtime key set serves both the container that renders it at start and a build:pages script that renders it into dist, ship a _redirects in each so a deep link stops 404ing without nginx try_files, and add scripts/check-pages-build.sh to make lint because a malformed config.js reads fine in a diff and leaves the app blank at runtime 2026-09-10 18:03:00 +02:00
Matthew Meszaros 6ebf9cfdcf Merge remote-tracking branch 'origin/main' into fix/self-hosted-unsubscribe-domain 2026-09-09 08:24:04 -07:00
Matthew Meszaros 7d58b874b8 feat: keep every recipient-facing and self-host-facing address on the deployment's own domain: mint unsubscribe links on a workspace's verified tracking domain (served by the tracking service, proxied to the backend that owns the pages), attach RFC 8058 one-click only over https, resolve all branding through config.Brand() gated on SelfHosted() so a self-host's email footer, sign-in links, stats card, API example and public form badge name nobody else, drop the app.warmbly.com fallback from AppBaseURL, blank TRACKING_DOMAIN and FORMS_DOMAIN on core-only installs, and have install.sh offer to configure a fresh interactive install instead of silently defaulting to localhost 2026-09-09 06:34:43 -07:00
Matthew Meszaros 0a08983158 feat: keep the node's write path reachable and the image reference root-owned, by granting traverse on the state directory without making it writable, re-owning image-ref on every join so a machine joined by the earlier chown-everything script converges, mounting BLOB_FS_ROOT separately when blobs live under the state directory the mount no longer covers, and renaming the reserve dialog's remaining convert-to-dedicated button 2026-09-09 05:53:28 -07:00
Matthew Meszaros 65fe82c472 feat: stop the node container being able to choose the image root runs, by giving it a dedicated uid-1000 subdirectory to write its update target into instead of making the whole state directory writable, validating the tag it asks for before the updater acts on it, and no longer recursively chowning a directory a bare-metal install also keeps its blobs in 2026-09-09 05:41:16 -07:00
Matthew Meszaros bfdbd77b04 feat: fix the eight defects the second review pass found, including three where the previous fix did not land: the bind-mounted state dir was root-owned so the node running as uid 1000 still could not write its update target, elevating the reserved-worker eviction did nothing because the rotation loop bailed on target-equals-current before the urgency was consulted, and the warmup-pool assertion was vacuous which hid that warmupPoolFor checked the subscription repo before the billing provider and answered free on a self-host install 2026-09-09 05:32:30 -07:00
Matthew Meszaros 8b83062eff feat: fix ten defects the review found, the worst being that every seeder still wrote the worker columns migration 000141 moved to fleet_nodes so make dev could not seed, that the generated systemd unit used a command substitution systemd never expands so a joined machine restart-looped while the script reported success, that the state directory was not bind-mounted so the node wrote its update target inside the container where the host timer never saw it, and that placement stopped assigning warmup pool membership so paid mailboxes silently warmed in the free pool 2026-09-09 05:21:17 -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 c8ecf84e5e feat: report the highlighted search-picker option only through aria-activedescendant and keep aria-selected false until an option is actually picked, as the CodeRabbit re-review on the admin frontend PR asked 2026-09-07 23:16:18 -07:00
Matthew Meszaros f9bef0d5f1 feat: address the CodeRabbit review on the admin frontend PR by warning about the last admin who can grant access only when the whole admin list is on screen, resetting the convert-to-dedicated form on Cancel, following a changed ?q= on the mailboxes page instead of only its first value, treating the all sentinel as the sync state default and not as a search term, naming the lease window in the webhook reclaim confirmation only once it is loaded, walking transfer group dependents transitively so unticking a group drops everything that needs it, labelling the shown-page scope of the worker bulk move, closing the mobile drawer when the viewport reaches the desktop breakpoint, dropping the stale plan mention from the users page description, and completing the accessibility contract of the status segments, passphrase fields, icon-only delete button and search combobox 2026-09-07 23:08:56 -07:00
Matthew Meszaros 800c4a062f feat: frontend half of the admin panel upgrade: drop ten unused dependencies, the dead worker-load and plan requests, the stub pages and the retired permission names, fix the analytics client that sent the wrong query parameters and the mail test toasts that used an unmounted toaster, add a Cmd-K command palette with live user, organization, mailbox and worker search, a mobile nav drawer, document titles, a Sentry-wired route error boundary and permission gates on every route, replace polling with the realtime spine wherever an event exists, fold Analytics into Overview with a signups-by-channel card, merge System Status into Setup and health and Settings, Notifications and Effective limits into Configuration as tabs with redirects from every old path, add the Sync, Sends, Jobs, Fleet, Admins and Transfers pages plus API-key, webhook and transfer tabs on the organization page, mailbox reassignment on the worker page and abuse and action-history tabs on Warmup, and add the admin-panel docs page with every nav reference updated 2026-09-07 21:40:38 -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 04dc83da38 Merge the Sentry review fixes into the PostHog branch, keeping the new JavaScript escaping for the PostHog runtime-config values too 2026-09-07 04:48:30 -07:00
Matthew Meszaros f043bfc761 feat: address the CodeRabbit review on the Sentry PR by escaping every runtime-config value the web and admin entrypoints write into a JavaScript string literal so a quote or newline in a DSN cannot inject or discard the whole config, reporting only the count and types of a failed query's parameters instead of their values now that the db scope actually applies, cloning the fallback hub in errs.Hub so scope cannot leak between requests, flushing the tracking service before every fatal exit since process::exit skips the guard's drop, treating a blank WARMBLY_RELEASE or APP_ENV as unset in realtime, stamping the environment and a service tag into the forms page, and correcting the docs claim that an unset DSN means the SDK is never initialised 2026-09-07 04:47:51 -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 b0c090e521 feat: keep an admin-cancelled warmup generation batch job running with batch_status cancelling instead of marking it failed on the spot, so PollBatches reaches the cancelled branch that PR 333 added and ingests the conversations OpenAI finished before the cancel landed while the admin's reason survives the ingest, refuse a second cancel on a batch already cancelling, hide the admin Cancel button while a batch is cancelling, and refresh the PollBatches, CancelBatch and WarmupBatchPoller doc comments that still said expired and cancelled batches are marked failed 2026-09-05 21:14:46 -07:00