Commit Graph
613 Commits
Author SHA1 Message Date
Matthew Meszaros a9e39c469e site: de-AI sections — drop fake counters (487k sent today, mailbox-warming counts, 'biggest open-source' superlative); warmup goes back to dark-sky side-by-side with text + 3 real visual cards (ramp/health/pool); replace worker grid with Sequences feature using new SequenceMock (Q2 outbound, A/B variants, branches, business-hour waits); drop italic-brand pattern from inbox/pricing headlines; replace em-dashes with periods/middots in visible copy; pricing header now states 14-day free trial up front. 2026-05-26 08:11:27 +00:00
Matthew Meszaros d256ba46f4 site: complete section redesign — killed metrics+how-it-works, replaced with continuous marquee activity ribbon; deep-sky Warmup panel with animateMotion spark traveling along ramp curve forever; continuously scrolling Inbox reply stream (50s loop); Sending worker grid with traveling send sparks; narrative two-card Compare; redesigned Pricing with annual-default premium segmented toggle + 'Save 20%' pill + featured-card sweep animation + larger price typography. All loops are CSS-based, prefers-reduced-motion respected. 2026-05-26 07:50:47 +00:00
Matthew Meszaros 57263f1f9e site: open-source pill in hero (GitHub link) 2026-05-26 07:43:18 +00:00
Matthew Meszaros 086d172a43 site: real plan data from web/src/lib/plans.ts (Starter $29 / Grow $89 / Business $329 featured / Enterprise) + motion.dev scroll animations (fade-in cards via is-in class with CSS fallback, animated ring/bar/sparkline, working monthly↔annual toggle that slides + swaps prices) + redesigned feature sections (KPI tile sidebars, animated warmup chart with sky panel + health bands, reply-distribution donut + stacked inbox mock + floating toast, sending worker grid with gradient bars + pool diversity) + GitHub repo link in footer + pnpm-workspace.yaml for esbuild/sharp builds + 5 Perlin-noise WebP cloud assets 2026-05-26 07:37:20 +00:00
Matthew Meszaros dfd91281cd site: added live metrics strip (animated counters, pulsing dots) + 'how it works' section with 4 numbered steps and connecting flow line, between hero and feature sections 2026-05-26 07:10:23 +00:00
Matthew Meszaros 2c2e6d0938 site: real raster clouds — Perlin-noise generated WebPs via Sharp (scripts/gen-clouds.mjs), Cloud.astro renders <img> (Safari-perfect, no filter quirks). 5 organic cumulus variants with multi-lobed white tops + blue underbellies + warm sun-catch. Total ~520 KB across all 5, lazy-loaded. 2026-05-26 07:08:40 +00:00
Matthew Meszaros 4db94c2bc7 site: replace outlined clouds with painterly bezier clouds — Cloud.astro renders 5 silhouette variants with halo + body gradient + underbelly shadow + sun-highlight, no feDisplacementMap (Safari-safe), HeroAtmosphere uses 5 painterly clouds drifting, SectionSky uses 2 2026-05-26 06:56:48 +00:00
Matthew Meszaros 45860b99f4 site: redesigned pricing — 4 equal premium cards, Growth glows (sky ring + cloud + POPULAR pill), monthly/annual toggle, big tight prices, check icons in tinted pill bullets, footer comparison link 2026-05-26 06:28:31 +00:00
Matthew Meszaros 9c9dd8dc23 site: blue-sky theme (matches /web auth palette) — deep sky hero with breathing gradient + drifting clouds + sun pulse + white text, dashboard mock fills container width and overlaps into white section, per-feature SectionSky backdrop on the side hosting the mock, bento pricing with Growth as wide highlighted hero card + Free/Starter/Business in 3-col row, animated bar grow + ring fill + toast-in + live ticking sends counter, prefers-reduced-motion respected 2026-05-26 06:23:42 +00:00
Matthew Meszaros 89a111161c site: Warmbly own theme — cornsilk #fff5d6 hero with animated aurora gradient + drifting outlined clouds, dashboard mockup overlaps down into white section, azure accent, italic 'actually' in headline, redesigned pricing with highlighted middle plan + feature lists, lift-on-hover on bento + mock cards, prefers-reduced-motion respected 2026-05-26 06:00:38 +00:00
Matthew Meszaros c14a3559ae site: shorter+wider dashboard mock (4 rows, compact sidebar, 380px min-h), abstract sky backdrop without literal mountains, removed fake quote band/customers page/about Backed-by/trust uptime numbers, hero CTAs now 'Start free'+'Tour the product' 2026-05-26 05:52:19 +00:00
Matthew Meszaros ba4be92341 site: reset to unified Cursor+Air theme — parchment #f7f7f4 base, Inkwell text, Cursor multi-layer shadows on white cards, single warm painted backdrop behind hero mock (no rainbow), dropped logo strip, filled-ink primary CTA + ghost secondary 2026-05-26 05:46:25 +00:00
Matthew Meszaros 9f21e05111 site: apply Adora design system — pastel gradient-accent cards wrap product mocks, Action Violet filled primary CTA, Rich Violet headings, Slate Text body, decorative outlined sparks, 26-48px radius across cards 2026-05-26 05:40:00 +00:00
Matthew Meszaros be1afb0ef2 site: real painted-landscape SVG backdrop (sky gradient + sun glow + turbulence-distorted cloud clusters + layered mountain silhouettes), replaces flat CSS gradient 2026-05-26 05:31:59 +00:00
Matthew Meszaros a2aa0f4d2f site: PaintedSky now overlays optional /backdrops/hero.webp image with CSS fallback. README in site/public/backdrops/ has prompts for Midjourney/Krea/Leonardo/DALL-E. Drop file in to swap from CSS gradient to AI-painted backdrop. 2026-05-26 05:27:58 +00:00
Matthew Meszaros 4a6806ea30 site: DashboardMock now mirrors full /web shell — AppHeader (logo + crumb + PlanPill + Search), AppNav sidebar (LivePanel with sparkline + nav sections + Settings + UserNav), rounded-tl-2xl white content panel 2026-05-26 05:19:59 +00:00
Matthew Meszaros 9817fce977 site: Cursor-style home — clean white, painted-sky backdrop behind pixel-faithful dashboard mocks (DashboardMock matches /app/emails, InboxMock matches /app/unibox, WarmupMock matches /warmup) 2026-05-26 05:11:23 +00:00
Matthew Meszaros bff49c31c2 site: experimental sky hero (will replace with Cursor-style painted backdrop) 2026-05-26 05:03:23 +00:00
Matthew Meszaros 3a4b0b5d55 site: terms, privacy, DPA, AUP, subprocessors, cookies 2026-05-25 17:00:24 +00:00
Matthew Meszaros fbdb5202cf site: learn section with deep guides on warmup, deliverability, auth, placement, cold-mail rules, pools, recovery, glossary 2026-05-25 16:57:12 +00:00
Matthew Meszaros b66e3f067d site: sending/inbox/campaigns/analytics/crm/developers/integrations/workers/about/customers/contact/brand/trust/roadmap/changelog/faq/404/use-cases 2026-05-25 16:50:56 +00:00
Matthew Meszaros 833bfb9ac7 site: product, warmup, deliverability, security, pricing pages 2026-05-25 16:41:52 +00:00
Matthew Meszaros c9f2ae4a11 site: theme tokens, layout/header/footer, home page 2026-05-25 16:37:17 +00:00
Matthew Meszaros 45f51c2cbb site: bootstrap site/ from warmbly-web (strip old content for redesign) 2026-05-25 16:29:37 +00:00
Matthew Meszaros 3a9495a788 fix: drop burst multiplier so api ceiling stays a flat 100 req/s
BurstMultiplier was 2.0, which let clients briefly hit 200 req/s before
throttling. roll it back to 1.0 — predictable ceiling beats a peak that
can mask real traffic patterns.
2026-05-25 16:09:01 +00:00
Matthew Meszaros 46f0fd0667 feat: seed dev email accounts and sample webhook endpoint in baseline
a fresh `make seed` now produces a fully functional dev environment:
the dev@warmbly.com user owns an org connected to a shared worker with
two warmup-pooled email accounts and a sample (disabled) webhook
endpoint. previously the baseline only seeded the user + org so new
contributors hit empty mailbox screens immediately after `make up`.

rich seed account count expectation bumps to 8 (2 dev + 6 rich); the
`33333333-%` UUID prefix is unchanged so existing cleanup logic and
counts still flow through one filter.
2026-05-25 16:06:16 +00:00
Matthew Meszaros ab445e2207 feat: customer webhook subscriptions with hmac signing and retry
- webhook_endpoints / webhook_deliveries schema (migration 42)
- service: dispatch + endpoint crud + hmac-sha256 signing
- delivery worker drains queue using FOR UPDATE SKIP LOCKED so multiple
  api replicas can run safely without duplicate dispatch
- exponential backoff (30s → 1h cap, 8 attempts then abandoned)
- REST API under /webhooks: list/create/update/delete/rotate-secret/
  list-deliveries. secret only returned at create + rotate
- header convention matches stripe-style: X-Warmbly-Signature: t=<unix>,v1=<hex>
- legacy header X-Warmbly-Token already renamed; new outbound webhook
  headers are X-Warmbly-Signature / X-Warmbly-Event / X-Warmbly-Event-Id
- wired into email account connect/remove and warmup health transitions;
  campaign/tracking/deliverability call sites will reuse the same
  webhookService.Dispatch interface
2026-05-25 16:03:13 +00:00
Matthew Meszaros 8792b97c2c feat: bump default api rate limits to support 100 req/s
DefaultRateLimits now permits 6000 read + 6000 write per minute = 100 req/s
sustained, with burst multiplier 2.0 (200 req/s peak). bulk operations
stay tighter at 600/min since each one is expensive. migration 41 bumps
existing rows that still hold the previous defaults — admin-customized
limits are left untouched.
2026-05-25 15:53:22 +00:00
Matthew Meszaros 43d24bc3d7 feat: customer-defined warmup routing rules on premium pool
new warmup_routing_rules table + repo + REST endpoints under /warmup/
routing. each rule matches a (sender, recipient) pair by domain, TLD,
provider bucket, or any wildcard, with a weight multiplier on the
selector. weight > 1 prefers the pairing, < 1 discourages, 0 excludes.
rules are evaluated in priority order (ascending) and combined with the
existing domain-diversity weighting. example use case: a customer can
say 'send Gmail-recipient warmup only from Google-classified senders'
with one rule; or 'never send to acme.com from this org's mailboxes'
with weight=0. premium pool only — free pool ignores rules.
2026-05-25 15:42:50 +00:00
Matthew Meszaros 4fb5885130 feat: split warmup spam-placement from user complaints
introduce two distinct warmup_spam_reports report types:
  - spam_placement: provider classifier put the message in Junk on arrival
  - user_complaint: recipient explicitly flagged the warmup message

detect placement at warmup arrival via the incoming message flags and
record it through a new WarmupService.RecordSpamPlacement. evaluate the
two signals independently in the health sweep — user complaints now
have their own watch/quarantine/block thresholds (0.5 / 1.5 / 3.0%)
since they are a stronger negative signal per event than placement.
2026-05-25 15:33:51 +00:00
Matthew Meszaros 2c713dfab3 feat: free-trial gets warmup + 1 inbox for 14 days
open CanUseWarmup and CanUseUnibox to free-trial orgs during the 14-day
window. add CanAddInbox feature gate and a FreeTrialInboxLimit of 1 so
trial orgs cannot seed the warmup pool with disposable accounts. enforce
the cap on both OAuthStart (avoids wasting the round-trip) and
OnboardSMTPIMAP, with distinct error messages for cap-reached vs
trial-expired.
2026-05-25 15:29:11 +00:00
Matthew Meszaros b4ece91383 perf: drop dead cipher.Encrypt calls from warmup task
the two cipher.Encrypt calls on subject and body in HandleEmailTask
discarded their ciphertext and the plaintext was what got sent. warmup
mail is not stored at rest, so the only effect was warming the user's
DEK in cache via the preceding Cipher() call — also removed since
nothing else in the warmup path needs it.
2026-05-25 15:25:06 +00:00
Matthew Meszaros e8f741dc82 feat: reduce warmup content + header fingerprint
rename outbound warmup verification header from X-Warmbly-Token to a
generic X-Mailtrace-Verify and accept both on receive during rollout.
add slot-based subject synthesis that fires ~40% of the time, yielding
thousands of unique strings on top of the static corpus so vendors
cannot trivially cluster on a fixed subject list.
2026-05-25 15:23:58 +00:00
Matthew Meszaros bf0693c7f0 feat: weighted, diverse warmup partner selection
extend recent-partner exclusion from 24h to 72h, weight selection by
inverse-frequency of recipient domain over the last 7 days so a single
provider does not absorb the bulk of warmup traffic, and log a warning
when a pool drops below the diversity threshold.
2026-05-25 15:21:27 +00:00
Matthew Meszaros 7cfff22380 feat: warmup replies stay on-topic with original thread
persist conversation_theme on the warmup_token at send time, and look
it back up on reply so the body comes from the same topical bucket.
previously the reply picked a random conversation, so 'Re: time-
blocking question' could come back as a paragraph about travel.
2026-05-25 15:19:18 +00:00
Matthew Meszaros 92e4dd3a69 feat: throttled/watch health state shapes warmup volume and spacing
CalculateNextWarmupTime now consults the participant's health state and
scales target_volume + min_wait_time down for throttled (0.5x volume,
2x spacing) and watch (0.7x volume, 1.5x spacing). previously the
state was set by the sweep but never read by the scheduler, so
'throttled' was a label without behavior.
2026-05-25 15:15:37 +00:00
Matthew Meszaros b819f9fbd5 feat: imap/outlook parity for warmup inbox actions
mark_read, mark_important, remove_from_spam, and move_to_warmbly now run
on outlook and custom smtp/imap mailboxes, not just gmail. routes by
provider in the worker handler. carries source mailbox UIDValidity on
the action so the worker can SELECT the right folder before mutating.
2026-05-25 15:13:37 +00:00
Matthew Meszaros 6c4fad43fc Merge pull request #13 from warmbly/fix/dev-stack-and-worktree-split
Fix dev stack startup and split into infra + app for worktree sharing
2026-05-25 13:50:41 +02:00
Matthew Meszaros ef9ce6a6e2 refactor: split make dev into make infra and make app
Every docker compose invocation in the Makefile now pins
-p warmbly so all git worktrees target the same compose
project. This means infra (postgres, redis, kafka, mailpit,
localstack, stripe-mock, cloud-tasks-emulator, zookeeper,
schema-registry) is brought up once and stays running across
worktree switches. App services (backend, consumer, worker,
tracking, realtime, web) recreate in place per worktree against
the bind-mounted source.

Removed targets:
- dev, dev-down, dev-logs (and the DEV_SVCS / SVCS vars)

Added targets:
- infra, infra-down
- app, app-down, app-logs

Daily flow becomes:

    make infra                     # once, from any worktree
    cd /path/to/worktree-a
    make app                       # bring up app code for branch A
    cd /path/to/worktree-b
    make app                       # recreates app against branch B;
                                   # infra untouched, caches warm

The named cache volumes already shared their content across
worktrees (warmbly_gomodcache, warmbly_gocache, warmbly_cargo_home,
warmbly_cargo_target, warmbly_mix_deps, warmbly_mix_build); pinning
the project name additionally makes container ownership shared,
which is what eliminates the per-worktree cold start.

README.md, resources/local-development.md, resources/deployment-guide.md,
deploy/README.md, and docker-compose.dev.yml all updated to reflect
the new targets.
2026-05-25 11:43:33 +00:00
Matthew Meszaros 1dc600d722 fix: disable Go VCS stamping in air dev build
The bind-mounted .git is owned by the host user but the dev
container runs as root, so git aborts with "dubious ownership"
and Go's automatic VCS stamping fails the build with:

  error obtaining VCS status: exit status 128
  Use -buildvcs=false to disable VCS stamping.

Production Dockerfiles are unaffected because they do not
bind-mount .git; they COPY source into the image and let Go
stamp normally.
2026-05-25 11:43:21 +00:00
Matthew Meszaros 1317ea67d6 fix: rename duplicate migration 000035 to 000037
PR #10 added 000035_api_key_suffix and PR #11 separately added
000035_api_key_smart_limits, both targeting the api_keys table.
golang-migrate refuses to load when two source files share a
version number, so the backend container failed health checks
in fresh stacks.

Bumping api_key_smart_limits to 000037 keeps PR #10 in its
original slot (it landed first) and matches the next free
version after 000036_contact_categories. The two migrations
touch different columns so apply order does not matter.
2026-05-25 11:43:14 +00:00
Matthew Meszaros c790812207 Merge pull request #11 from warmbly/feature/api-keys-dashboard
feat: ship api keys dashboard with smart limits and analytics
2026-05-25 12:26:54 +02:00
Matthew Meszaros e38d571610 Merge pull request #12 from warmbly/feature/campaign-creation-overhaul
feat: fix campaign creation 500 and ship full-config wizard
2026-05-25 12:26:44 +02:00
Matthew Meszaros 1435e0d618 style: gofmt api_key models 2026-05-25 10:23:32 +00:00
Matthew Meszaros b4d5667e8c Merge pull request #10 from warmbly/feature/contacts-power-tools
Contacts: categories, import/export, contact 360 slide-over
2026-05-25 12:19:08 +02:00
Matthew Meszaros 8d351d1f0f fix: satisfy golangci-lint on the contacts power-tools branch
- gofmt-align ContactEngagement fields and drop trailing blank line
  in contact/export.go
- remove no-op self-assignment ac.CustomFields = ac.CustomFields
  flagged by govet
2026-05-25 09:27:10 +00:00
Matthew Meszaros 30a1138583 feat: smart contact activity tab — search, date range, infinite scroll
- useContactTimeline now uses cursor-based useInfiniteQuery (50/page,
  before=oldest cached at) instead of a one-shot fetch
- search input filters subject/content/campaign/sequence/mailbox/
  reason/intent; matches are highlighted in the row
- date range picker (popover with from/to + 7/30/90d presets) filters
  the visible list by event timestamp
- segmented type chips with motion indicator
- IntersectionObserver sentinel auto-fetches next page as user scrolls
- when filters narrow the visible set, lazy-prefetch more pages so
  results aren't artificially short (bounded to 250 events / 5 pages)
- skeleton rows with staggered pulse on first load, inline spinner on
  subsequent page loads, "end of history" marker when done
2026-05-25 09:12:20 +00:00
Matthew Meszaros 149825783e refactor: simplify contact activity tab to match monochrome theme
- drop colored timeline rail and per-type dot colors
- collapse per-event bordered cards into one bordered list container
  with divided rows (matches Overview's "Latest activity" treatment)
- segmented filter strip with motion indicator, matches main tab strip
- quote-style left border for body content (notes, reply snippets)
  instead of a free-floating block
2026-05-25 09:06:17 +00:00
Matthew Meszaros ae091ec28e refactor: simplify contact slide-over to match app's monochrome theme
- solid slate-100 round avatar with slate-700 initials (no gradient)
- single contextual status pill only when something is off
  (suppressed/unsubscribed); no chip when everything's fine
- text-only segmented tab strip, drop icons + side tint band
- flat stat tiles, no gradient hero variant
- keep animated motion indicator on tabs and email copy affordance
2026-05-25 09:04:56 +00:00
Matthew Meszaros d15e3c3c9b feat: redesign contact slide-over header and tab switcher
- gradient avatar deterministically colored by email
- bigger display name, copy-email affordance, status + company chips
- segmented tab strip with icons and animated motion indicator
- 34rem panel, deeper shadow, subtle tinted body background
2026-05-25 08:59:29 +00:00