Matthew Meszaros
|
eb21d978a8
|
feat: stop aws-bootstrap from treating a bucket that exists in another region as done, since head-bucket answers globally and a second region silently kept its blobs in the first, stop it printing the database master password to stdout where a terminal, a CI log or an agent transcript keeps it forever, and add an instance check that reports a tracking domain sharing a registered domain with the product's own URLs
|
2026-09-11 05:24:57 +02:00 |
|
Matthew Meszaros
|
a84ab48729
|
Merge branch 'main' into feature/posthog-error-tracking
|
2026-09-10 10:25:21 -07: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
|
331db196d8
|
feat: locate every content-check issue in the subject or the body with the exact fragments that caused it and a one-line fix, add POST /templates/analyze running the configured LLM over a campaign template for located spam findings quoted verbatim from the copy plus a rewritten subject and an overall score, verify every model quote against the draft so an invented sentence is dropped rather than shown, pin the analysis temperature so re-checking unchanged copy returns the same number, and give the editor panel a Re-check button that re-runs both passes and reports the movement since the last check
|
2026-09-10 09:28:00 -07:00 |
|
Matthew Meszaros
|
2998f8a8c6
|
Merge remote-tracking branch 'origin/main' into feat/worker-capacity-soft-target
|
2026-09-10 06:26:47 -07:00 |
|
Matthew Meszaros
|
fa2b5330d7
|
feat: drop the auth-pressure placement term because worker_capacity_view aggregates auth_errors (per-mailbox credential failures) and not rate_limit_errors (the 454/421 per-IP throttles it claimed to measure), measure projected utilization against an age-free Capacity.Target so a freshly joined node can relieve a full fleet instead of scoring as 200% loaded after one mailbox, bound the isolated-egress override with an explicit OverTarget check now that Eligible no longer caps it, and cap rotation moves per destination since a tick scores every mailbox against one frozen materialized-view snapshot
|
2026-09-10 05:51:17 -07:00 |
|
Matthew Meszaros
|
7fa4fdfbc4
|
feat: make worker capacity a placement target rather than a hard gate, so Eligible refuses only on health and an over-target worker costs enough score to lose to anything with room instead of returning nil and dropping assignment into selectFallback, score projected utilization including the incoming mailbox's own weight, and penalise the 454/421 auth pressure the capacity view already collected and threw away
|
2026-09-10 05:38:17 -07:00 |
|
Matthew Meszaros
|
396bab0e06
|
Merge branch 'main' into feat/issue-414-delete-api-key
|
2026-09-10 05:36:06 -07:00 |
|
Matthew Meszaros
|
64dfaa8c60
|
Merge remote-tracking branch 'origin/main' into feat/split-cloud-hosting
|
2026-09-10 14:20:22 +02:00 |
|
Matthew Meszaros
|
510ee692ba
|
feat: address the review on the split-deployment branch by moving the two broker routes onto their own NODE_BROKER_TOKEN so the internet-facing tracking and forms services no longer hold a credential that can open any organization's data key, refusing to presign any key outside the prefixes a node reaches, fixing IAM policies that named an alias ARN KMS never resolves in a Resource element, bounding both brokered HTTP clients because the sync loop's context never expires, no longer reporting a 403 from the object store as a missing body, and redacting the DSN and URL credentials the dry-run listing printed in clear
|
2026-09-10 14:19:53 +02:00 |
|
Matthew Meszaros
|
9e37ea73a8
|
feat: add a permanent delete for API keys, DELETE /api-keys/:id/permanent plus a Delete key button under a revoked key in the dashboard drawer and warmbly key purge, taking the key's usage logs with it and refusing any key that could still authenticate so revoking stays the step that records why a credential ended (issue #414)
|
2026-09-10 05:02:05 -07:00 |
|
Matthew Meszaros
|
47ba13083e
|
feat: make a split deployment work end to end by fixing the three defects that made an off-host node impossible to configure (nodeEnvKeys shipped S3_BUCKET and KMS_KEY_ID, which nothing reads, so an AWS-backed node silently used the default bucket and key alias; a joined consumer never received PRIMARY_DB and died at boot; and node.env was rewritten on every join with no file an operator could add to), then removing the need for cloud credentials on a node at all with brokered KMS and blob providers that renderNodeEnv hands out automatically, plus deploy/split-cloud, scripts/aws-bootstrap.sh, two fleet instance checks and the docs
|
2026-09-10 13:58:59 +02:00 |
|
Matthew Meszaros
|
08f1420c0b
|
Merge branch 'main' into fix/issue-410-scanner-networks
|
2026-09-10 04:32:36 -07:00 |
|
Matthew Meszaros
|
6684dafe3a
|
feat: classify opens and clicks that arrive from a known mail-filtering network as automated, so Microsoft 365 Defender's delivery-time pixel fetches and Safe Links URL detonations stop counting as engagement (issue #410), with a shipped scanner catalogue, TRACKING_SCANNER_* overrides and optional ASN matching from a trusted edge header
|
2026-09-10 03:38:50 -07:00 |
|
Matthew Meszaros
|
6dcbc6d6a7
|
feat: stop sending prompt=consent on the Outlook/Microsoft 365 authorize request, which made Entra ID re-run the consent eligibility check per sign-in and refuse every non-admin with AADSTS90095 even under a tenant-wide admin grant (issue #409), and ask for prompt=select_account instead so the account picker survives while Google keeps access_type=offline plus forced consent for its refresh token
|
2026-09-10 03:24:16 -07:00 |
|
Matthew Meszaros
|
bb2b2b1743
|
feat: make the one-unresolved-error-per-code rule a partial unique index with a conflict path, collapsing the duplicate rows a repeating mail error already left behind
|
2026-09-09 09:46:39 -07:00 |
|
Matthew Meszaros
|
817233cc09
|
feat: record one unresolved row per mailbox error code instead of one per sync pass, and clear IMAP_UNKNOWN when a pass completes (issue #405)
|
2026-09-09 09:30:17 -07:00 |
|
Matthew Meszaros
|
3591d64404
|
Merge remote-tracking branch 'origin/main' into fix/issue-401
|
2026-09-09 08:58:08 -07:00 |
|
Matthew Meszaros
|
16261de4cb
|
Merge remote-tracking branch 'origin/main' into fix/issue-401
|
2026-09-09 08:51:02 -07:00 |
|
Matthew Meszaros
|
68b5d8f358
|
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
|
2026-09-09 08:51:00 -07:00 |
|
Matthew Meszaros
|
3153f9ff23
|
Merge remote-tracking branch 'origin/main' into fix/self-hosted-unsubscribe-domain
|
2026-09-09 08:44:13 -07:00 |
|
Matthew Meszaros
|
035865e5c5
|
Merge remote-tracking branch 'origin/main' into feat/issue-393-html-css-email
|
2026-09-09 08:30:52 -07: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
|
8157f3c2c0
|
feat: read the effective dashboard URL through the instancecheck package's own appURL helper in the insecure-URL check rather than reaching into config directly
|
2026-09-09 08:19:20 -07:00 |
|
Matthew Meszaros
|
a8d0e80f68
|
feat: address the CodeRabbit review: render EMAIL_BRAND_NAME in every transactional template body and subject instead of a hardcoded product name, name FRONTEND_BASE_URL as the source when emailed links come from it, warn on an insecure effective dashboard URL even when APP_URL was inferred rather than configured, and correct the unsubscribe guide's claim that an unset API_PUBLIC_URL disables link minting
|
2026-09-09 06:52:37 -07:00 |
|
Matthew Meszaros
|
426ea45013
|
feat: full HTML and CSS support for campaign bodies and mailbox signatures, adding a send-time CSS inliner and a structure-aware plain-text renderer in internal/pkg/mailhtml, persisting HTML mode on a step through the previously unused sequences.body_code so a designed email is no longer gutted by the editor schema on reopen, widening the TipTap schema to hold tables, div containers, colours, fonts, sizes and alignment with toolbar controls for each, folding alignment into a single style producer because TipTap merges two style attributes by splitting on the first colon and truncated background-image url(https://...) to url("https"), replacing the regex plain-text stripper that put a whole stylesheet into the text/plain part and cost a content score eight points for a .free-trial-banner class, locating </body> past Outlook conditional comments so the signature and opt-out footer stop landing inside one, forcing the signature source view for markup a contentEditable cannot host safely, and reporting per-client compatibility findings from the preview endpoint
|
2026-09-09 06:34:47 -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
|
4fc6d1db77
|
feat: put every docker mount on one line in the generated unit, because command substitution strips the trailing newline and my previous multi-line blob mount collapsed the continuation so the node restart-looped on any filesystem-blob install, and mount BLOB_FS_ROOT only when it already exists rather than creating and recursively chowning a directory a co-located bare-metal backend owns
|
2026-09-09 05:59:44 -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
|
92470e1528
|
feat: drop the racy liveness re-read from the placement live test, which re-queried whichever worker placement chose and so raced with the email package's live tests creating and deleting their own worker in a parallel package; that placement only ever picks a live worker is already covered by the unit tests, which need no database
|
2026-09-09 05:44:25 -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
|
704fecdcc0
|
feat: settle on WARMBLY_NODE_REGION as the one region variable for every role, because the join script wrote it while the worker still read WORKER_REGION, so a joined worker reported no region and the placement geography term stayed dead for exactly the machines that had been given one
|
2026-09-09 05:04:55 -07:00 |
|
Matthew Meszaros
|
de60bc3784
|
feat: let an isolated-egress reservation move a mailbox without clearing the score gain, because the reserved worker scores lower than the incumbent by construction (the incumbent holds the stickiness bonus) so the rotation loop refused the move on every tick and the organization never converged onto the worker it pays for
|
2026-09-09 05:02:53 -07:00 |
|
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
|
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
|
e783c3e3a0
|
feat: merge main into the email editor branch
|
2026-09-08 21:27:28 -07:00 |
|
Matthew Meszaros
|
7ad49c649a
|
Merge branch 'main' into fix/thread-on-in-reply-to
|
2026-09-08 21:22:31 -07:00 |
|
Matthew Meszaros
|
4f40ab7ec8
|
feat: merge main into the in-reply-to threading branch
|
2026-09-08 21:06:29 -07:00 |
|
Matthew Meszaros
|
95a4986d9e
|
feat: merge main into the verification reopen branch
|
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
|
f8c0899be4
|
feat: address the CodeRabbit review by promoting a rounded wait into the next unit so a contact drawer never reads 60 minutes or 24 hours, describing the entry delay's anchor as the lead's own enrolment moment with the campaign's creation date only as the legacy fallback in both the campaigns and segments guides, replacing the launch dialog's 'sending begins immediately' line when a delay is set, following an outside value change with the picker's preset or custom mode, and chaining the trigger card's saves so two commits inside one round trip cannot land out of order
|
2026-09-08 07:30:24 -07:00 |
|
Matthew Meszaros
|
0f83329936
|
feat: answer the CodeRabbit re-review by guaranteeing the promise the failed-pin warning makes: an import-level note now goes to the front of the error list and is never dropped by the per-row cap, so a file full of bad addresses cannot bury or evict the one note explaining why the rows that did import are not in the segment they were imported into, with a single note however many segments were targeted and a live test that forces the write to fail behind a capped error list
|
2026-09-08 06:54:36 -07:00 |
|