Matthew Meszaros
|
41a95bbe0e
|
feat: stabilize the sandbox against two live-run failure modes - seed and repair fixture contacts as verified (the live verifier finds no MX for .test domains and the pre-send gate then skips every campaign send) and deactivate the seeded workers the native stack never runs, whose active-but-heartbeatless rows kept winning placement and ping-ponging mailbox assignments with the dead-worker sweep
|
2026-07-11 17:57:21 +02:00 |
|
Matthew Meszaros
|
e2ccd0d75a
|
feat: record the new operational contracts in the agent notes and env example - CODEC_PROVIDER=json wherever workers run, CREDENTIALS_ENCRYPTION_KEY for credential sealing, the two native workers, and the make sandbox entry point
|
2026-07-11 17:31:58 +02:00 |
|
Matthew Meszaros
|
9a3551c5ea
|
feat: document the sandbox at /development/sandbox/ (quick start, seeded data, architecture of the simulation, env vars, demo script, troubleshooting) and refresh /development/local-development/ with dovecot, the sandbox target, and the emulator host.docker.internal callback note
|
2026-07-11 17:31:58 +02:00 |
|
Matthew Meszaros
|
48ec53d781
|
feat: add the sandbox seeder and simulator (cmd/sandbox, internal/sandbox) - seeds the Sunrise Labs showcase org (paid plan, six live mailboxes with sealed mailpit/dovecot credentials, active tracked campaigns, pre-verified contacts, warmup pool membership) and repairs every fixture smtp_imap account and .test contact; the simulator plays the internet by polling mailpit, appending mail into dovecot inboxes for the real IMAP sync to pick up, hitting tracking pixels and click tickets, and replying as hash-stable contact personas with correct In-Reply-To threading
|
2026-07-11 17:31:42 +02:00 |
|
Matthew Meszaros
|
f8cf7e930e
|
feat: native dev targets for the sandbox - make sandbox / sandbox-seed, a second native worker (worker-premium, required because paid orgs place strictly onto premium-tier workers) joined into make run, CODEC_PROVIDER=json + CREDENTIALS_ENCRYPTION_KEY + MAIL_TLS_INSECURE in the dev env, dovecot in INFRA_SVCS, and SELF_HOST defaulting to host.docker.internal so the dockerized cloud-tasks emulator can reach the host-run backend
|
2026-07-11 17:31:42 +02:00 |
|
Matthew Meszaros
|
1634d195d3
|
feat: fix the DEK cache path in the cipher service - a redis cache miss (every first use of an org key) returned redis.Nil as a fatal error before the KMS fallback could run, and a cache hit was fetched then discarded; now a hit returns immediately and any cache miss or error falls through to KMS
|
2026-07-11 17:31:23 +02:00 |
|
Matthew Meszaros
|
9d7e3aa61f
|
feat: make scheduled sending self-healing - dispatch Cloud Tasks callbacks by the task row type (all enqueues share one webhook URL, so campaign callbacks were run through the warmup handler), cancel pending tasks stranded past their slot so the reconcilers re-seed chains after a lost callback or emulator restart, and cast the status enum in the two UPDATE ... CASE queries that failed parameter type deduction on every callback
|
2026-07-11 17:31:23 +02:00 |
|
Matthew Meszaros
|
167cc370b3
|
feat: re-key dedicated_worker_assignments to organizations (migration 000055) - the assignment service has always keyed by org id but the column carried a users FK, so every runtime bind failed with an FK violation and dedicated-plan orgs could never get a worker; renames the column, remaps seed rows, and updates the repository, admin filter, and convert-to-dedicated endpoint to org semantics
|
2026-07-11 17:31:23 +02:00 |
|
Matthew Meszaros
|
6bc2073379
|
feat: honor CODEC_PROVIDER=json across backend and consumer (previously worker-only) and round-trip untyped event bodies into their typed payloads in both dispatchers - the worker command/result envelopes carry any-typed bodies that Avro cannot serialize, so worker messaging only works on the JSON codec; also wires the credentials encrypter into both mains
|
2026-07-11 17:31:02 +02:00 |
|
Matthew Meszaros
|
99c1c4afa9
|
feat: seal SMTP/IMAP credentials at rest with a CREDENTIALS_ENCRYPTION_KEY-backed encrypter - the email repository Encrypt field was never wired anywhere, so smtp_imap accounts could never load onto workers (plaintext rows failed decode; hex-looking rows would nil-panic); adds encrypt.FromEnv and nil-guards that fail with a captured error instead
|
2026-07-11 17:30:49 +02:00 |
|
Matthew Meszaros
|
50aa0a3b3d
|
feat: contain panics in the per-mailbox sync loop - the worker is multi-tenant, so one mailbox hitting a bad server response must not take down every other account on the process
|
2026-07-11 17:30:20 +02:00 |
|
Matthew Meszaros
|
ee35138e37
|
feat: fix the generic IMAP client so it can actually connect and sync - assign the client before auth (was nil and would panic), check CONDSTORE after login where servers advertise it, request LIST-STATUS fields (folders were silently empty), and fetch 1:* instead of an empty SeqSet that panics inside go-imap
|
2026-07-11 17:30:20 +02:00 |
|
Matthew Meszaros
|
932b0bc856
|
feat: add the MAIL_TLS_INSECURE dev knob (netbind) so worker mail clients can skip TLS verification and, for SMTP, tolerate a server with no STARTTLS - local mailpit/dovecot only; production behavior is unchanged when the env var is unset
|
2026-07-11 17:30:20 +02:00 |
|
Matthew Meszaros
|
c8ed667aeb
|
feat: add a dovecot IMAP service to the infra stack and accept any-credential SMTP auth on mailpit so local mailboxes can really send and sync (sandbox mail hosting), plus CODEC_PROVIDER/CREDENTIALS_ENCRYPTION_KEY env for backend, consumer, and workers and a host-gateway alias so the cloud-tasks emulator can call a natively run backend
|
2026-07-11 17:30:03 +02:00 |
|
Matthew Meszaros
|
c3b99e598b
|
Merge pull request #64 from warmbly/feature/ios-dashboard-app
Native iOS dashboard app: browsers, onboarding flows, mailbox connect, analytics, branding
|
2026-07-11 10:59:59 +02:00 |
|
Matthew Meszaros
|
b379be9732
|
feat: qualify self.task in the openSocket admission check - with the type-checker crash worked around, Xcode 26.6 surfaces the real diagnostic: the locked closure's bare 'task' resolves to the local declared later in the function and is rejected as captured before declaration
|
2026-07-11 10:44:57 +02:00 |
|
Matthew Meszaros
|
04cf24c98d
|
feat: hoist the socket url resolution out of the guard condition entirely - across three CI crashes the Xcode 26.6 type checker dies whenever the async urlProvider closure property is bound or awaited inside a guard condition under StrictConcurrency, so the read, the await and the unwrap are now three plain statements
|
2026-07-11 10:41:26 +02:00 |
|
Matthew Meszaros
|
da1a6efbf8
|
feat: make the websocket urlProvider a non-optional closure with a nil-returning default because the Xcode 26.6 type checker also crashes on referencing an optional async closure property, not just on optional-chained calls; behavior is unchanged (nil result still schedules a reconnect)
|
2026-07-11 10:37:33 +02:00 |
|
Matthew Meszaros
|
e5f5623dc4
|
feat: unblock iOS CI - unwrap the websocket urlProvider in two steps because 'await urlProvider?()' crashes the Xcode 26.6 Swift type checker (works on 27), and build only the arm64 simulator slice in CI since the generic destination was doubling the compile for no extra signal
|
2026-07-11 10:34:07 +02:00 |
|
Matthew Meszaros
|
2d00ff2f69
|
feat: rename the Icon Composer icon to AppIcon.icon so Xcode 26's asset catalog linker finds the AppIcon.appiconset fallback (the Warmbly name built on Xcode 27 but failed LinkAssetCatalog on the macos-26 runner), and stop truncating the iOS CI build output so failures are diagnosable
|
2026-07-11 10:28:36 +02:00 |
|
Matthew Meszaros
|
1c76787f24
|
feat: CI gains an iOS job - a paths-gated macos-26 build of ios/Warmbly.xcodeproj against the generic iOS Simulator destination with signing disabled, selecting the newest installed Xcode (the app needs the iOS 26 SDK and the Icon Composer .icon), wired into the ci-status rollup
|
2026-07-11 10:21:46 +02:00 |
|
Matthew Meszaros
|
b4c100b7ff
|
feat: bump excelize to v2.11.0 so the Trivy security scan passes - v2.10.1 carries CVE-2026-54063 (unbounded row index allocation in the worksheet parser, HIGH, used by the contact import/export paths); go.sum picks up the transitive golang.org/x refreshes
|
2026-07-11 10:21:46 +02:00 |
|
Matthew Meszaros
|
956edabca3
|
feat: kafka no longer fails its first boot after an unclean stop - the container now waits for the stale ephemeral /brokers/ids/1 registration to expire in zookeeper before starting (with a 6s session timeout) instead of crashing on NodeExists and relying on the restart policy
|
2026-07-11 10:13:44 +02:00 |
|
Matthew Meszaros
|
56c3a2e988
|
feat: app icon becomes a native Icon Composer document matching the Figma branding 1:1 - Warmbly.icon layers the exact 1024px Logo frame render (sky gradient, clouds) under the plane glyph as a separate Liquid Glass vector layer positioned identically to Figma, replacing the flat AppIcon asset via ASSETCATALOG_COMPILER_APPICON_NAME
|
2026-07-11 10:13:44 +02:00 |
|
Matthew Meszaros
|
76e6ad2466
|
feat: iOS analytics becomes an air-hero drawer browser cover - the sky hero stays (circular hamburger and close, translucent 7d/30d/90d period chips, glass stat chips) while the web-style tab strip and card grids give way to a section drawer (overview/deliverability/warmup/accounts with issue-tinted counts) over flat native lists; Home now opens analytics and mailboxes as covers instead of pushes
|
2026-07-11 10:13:44 +02:00 |
|
Matthew Meszaros
|
de85abc47c
|
feat: iOS mailboxes rebuilt end to end - the list is a full-screen drawer browser cover (warmup status scopes with live counts, search pill, multi-select bulk warmup/remove, circular close) since pushing it fought the system back swipe; connecting is an air onboarding flow with real Google/Outlook brand logos, a web-style authorize step with scope bullets, working in-app OAuth via ASWebAuthenticationSession against the new warmbly:// callback, an SMTP/IMAP wizard, and a start-warmup offer on success; the detail screen becomes editable (warmup ramp steppers, sending window and weekday bitmask chips matching the Go scheduler's Sunday-first layout, sender profile, sending limits, disconnect) with debounced optimistic PATCHes
|
2026-07-11 10:13:10 +02:00 |
|
Matthew Meszaros
|
07a6777536
|
feat: email OAuth callback page gains a native-app fallback - when there is no web opener to postMessage (an ASWebAuthenticationSession has no popup parent) it redirects to warmbly://email-oauth with provider/code/state/error so the iOS app can finish the connect; web popup behavior is unchanged and the docs describe both delivery paths
|
2026-07-11 10:13:10 +02:00 |
|
Matthew Meszaros
|
c12a621d06
|
feat: iOS workspace creation is the air onboarding flow - two sky-chrome steps (name, optional invites) creating the org at the end so it works both as the zero-workspaces gate (Sign out is the only exit, like the web) and as a cover with a circular close wired through explicit onClose bindings; the org picker becomes a sky screen with workspace rows and a New workspace path
|
2026-07-11 10:12:41 +02:00 |
|
Matthew Meszaros
|
928e1924b2
|
feat: iOS session handling survives network blips and explains sign-outs - token refresh only drops the session on definitive rejections (network errors rethrow and keep tokens), a missing token now kicks cleanly to sign-in instead of hanging, and the auth welcome step shows an amber session-expired notice when the app signed the user out
|
2026-07-11 10:12:41 +02:00 |
|
Matthew Meszaros
|
e6b0bd2918
|
feat: iOS More tab and CRM rebuilt in the flat browser language - the hub is one full-bleed list with a workspace header and cover rows, Deals/Tasks/Meetings become full drawer browsers (scope sidebars with sliding tone capsules, search pills, end markers), and billing/notifications/profile/security/team pages drop grouped cards for eyebrow-captioned hairline rows
|
2026-07-11 10:12:41 +02:00 |
|
Matthew Meszaros
|
ac87d80604
|
feat: iOS campaign leads becomes a full drawer browser matching contacts - status scopes (all/processing/replied/bounced/queued/unsubscribed) with live counts in a sky-hero sidebar, advanced contact filters, multi-select bulk edit/remove with optimistic count bumps, an add-leads picker that locks rows already in the campaign, live detail re-derived from the store with presence claims, plus shared contact browser pieces (filters sheet, CSV import, bulk actions, sidebar) and keyboard-safe create-flow sheets
|
2026-07-11 10:12:09 +02:00 |
|
Matthew Meszaros
|
cc0871d1b8
|
feat: contacts search gains an additive lead_status filter plus per-status lead_counts on the first page when exactly one campaign_id is set - the SQL predicates reproduce the read-time status derivation exactly (unsubscribed > bounced > replied > active > pending via LATERAL bool_or rollups), invalid lead_status or multi-campaign combinations return 400, and the web types/table pick up the new lead_counts shape
|
2026-07-11 10:11:36 +02:00 |
|
Matthew Meszaros
|
343fc33b1d
|
feat: campaign schedule board supports multiple timeframes per day - the + button now drops each new window into a free gap kept an hour clear of its neighbours (so a second tap adds a distinct morning-and-afternoon window instead of merging into one), and tapping any bar opens a precise start/end editor sheet with 30-minute wheel pickers plus remove, which is how narrow windows are tuned exactly on mobile where a ~25pt bar can't be dragged pixel-accurately; bars still drag to move and merge when overlapped
|
2026-07-08 07:06:22 +02:00 |
|
Matthew Meszaros
|
3c8abbb713
|
feat: rebuild the iOS campaign schedule as an interactive board reimagined for mobile - each day is its own full-width horizontal 24h track (not a cramped 7-column grid) with draggable window bars: tap + to add, drag a bar sideways to move, drag its white left/right handle to resize, tap × to remove, long-press a day to copy it to all or clear it, snapping to 30 minutes, with presets, a timezone menu and today highlighted; writes the authoritative schedule_windows and supports multiple independent windows per day
|
2026-07-07 19:00:43 +02:00 |
|
Matthew Meszaros
|
80a0ece647
|
feat: make the iOS campaign Settings page fully editable instead of a read-only wall - sender strategy, rotation mode and ESP matching are inline menu pickers, ramp-up is an enable toggle with start/ceiling/step steppers, and lead flow has a new-leads-per-day stepper (0=off) plus a prioritize-new-leads toggle, all PATCHing optimistically; only CC/BCC lists and tracking-domain DNS stay read-only under an 'On the web' handoff
|
2026-07-07 17:33:31 +02:00 |
|
Matthew Meszaros
|
430a31148a
|
fix: campaign GetByID now selects and scans organization_id so the org gate in start/stop, the sender pool, advanced settings and tracking-domain verification stops 404ing every campaign (the field was always nil, failing campaignForOrg's non-nil org check)
|
2026-07-07 17:33:31 +02:00 |
|
Matthew Meszaros
|
326a07d567
|
feat: rebuild the iOS campaign detail as a flat hub-and-spoke instead of a card-stacked overview - the sheet is now one full-bleed native surface (eyebrow captions, bare stat columns for open/reply/bounce rates plus a live 30-day chart, hairline rows) matching the inbox and campaigns list, with Manage rows pushing to Leads (searchable, count in title), a read-only Sequence with per-step HTML reader, and Senders, all carrying a web-handoff banner for editing that needs a big screen; Sending settings (daily-limit two-disc stepper clamped 1-100, stop-on-reply, open/link tracking) edit inline via optimistic PATCH, and new Schedule (day chips, daily window, timezone picker writing schedule_windows; custom per-day schedules stay read-only) and Settings (name/description, delivery toggles, read-only advanced, creator-only delete) pages
|
2026-07-07 17:14:44 +02:00 |
|
Matthew Meszaros
|
836f72129f
|
feat: detail screens get a flat full-width underline tab bar replacing the chunky scrollable pills (every tab always visible, sliding accent underline, shared by campaign, mailbox, contact, and analytics details), the white sheet is now clipped so scrolling content can never bleed over its rounded corners, and the campaign create flow presents instantly by mounting the 40fps flight-scene canvas and raising the keyboard only after the cover's slide-up finishes
|
2026-07-07 10:28:46 +02:00 |
|
Matthew Meszaros
|
7143533bf5
|
feat: make folder-heavy accounts feel first-class - the create flow's folder page switches to a two-up chip grid with a search box once there are more than eight folders (with a no-matches state), and both the campaigns and unibox drawer cascades cap their per-row stagger so long folder, mailbox, or label lists never animate for seconds
|
2026-07-07 10:21:23 +02:00 |
|
Matthew Meszaros
|
ac74b94c8d
|
feat: campaign creation becomes a stepped sky onboarding flow like sign-in - full-screen cover with the flight-scene backdrop, step badge and progress segments, and three sheet pages: name plus description with live character count, sending pace presets (10/25/50 per day with conservative fresh-mailbox guidance) alongside stop-on-reply and open/link tracking toggles, and an optional skippable folder-filing page that only appears when the member has folders; the create body now carries daily_limit, rule toggles, and folder_ids
|
2026-07-07 10:02:20 +02:00 |
|
Matthew Meszaros
|
60e70515c0
|
feat: turn the campaigns tab into a unibox-style browser - hamburger in the search pill slides in a sky-hero drawer with status scopes and campaign folders (counts from campaigns-overview, sliding selection capsule, cascade-in rows, edge swipe to open), scopes and folders now filter server-side so every view has an exact found count and working infinite scroll, replacing the client-side underline tabs
|
2026-07-07 09:40:27 +02:00 |
|
Matthew Meszaros
|
3acb51df6c
|
feat: add server-side status-bucket filtering to GET /campaigns (draft/active/paused/completed, paused matches every paused_* variant, invalid values 400) and a new GET /campaigns-overview endpoint returning status and per-folder counts, with the campaigns API reference and endpoint scope map updated
|
2026-07-07 09:40:27 +02:00 |
|
Matthew Meszaros
|
500ef3419e
|
feat: flatten the campaign detail overview tab from insetGrouped boxes to the flat full-width list - plain style on the system background with inline eyebrow section labels for performance, last 30 days, and per step instead of sticky gray header bands
|
2026-07-07 07:43:11 +02:00 |
|
Matthew Meszaros
|
a5d18f5b3e
|
feat: rebuild the campaigns tab in the flat Gmail language - search pill header with inline presence and a round create button replaces the nav title, underline scope tabs with live counts replace capsule chips, and the list becomes dense full-width hairline rows with a status-toned avatar disc, teammate presence dot, stats line, pulsing status, start/pause and delete swipe actions plus a context menu, and quick start/pause from the row via the new store toggle
|
2026-07-07 07:43:11 +02:00 |
|
Matthew Meszaros
|
9e77738c5f
|
feat: keep users signed in through backend outages - bootstrap distinguishes a real 401 (clear token, show sign-in) from an unreachable or erroring server, which now lands on a sky unreachable screen that keeps the session and retries automatically every few seconds
|
2026-07-07 07:27:16 +02:00 |
|
Matthew Meszaros
|
9cfef4937c
|
feat: eliminate white message bodies - plain-equivalent emails now render as native SwiftUI text instead of a webview, and rich-HTML messages get a hardened webview that reloads after web content process termination, retries failed or never-painted loads with a capped budget, and starts from a realistic frame
|
2026-07-07 07:27:15 +02:00 |
|
Matthew Meszaros
|
2e13eb4d3d
|
Merge pull request #63 from warmbly/feature/coming-soon-banner
Launch prep: coming-soon banner, self-host warmup notice, trial/free-tier copy, and the SEO blog cluster
|
2026-07-07 07:16:55 +02:00 |
|
Matthew Meszaros
|
fb5e22030e
|
feat: wire per-post cover frontmatter into the nine remaining blog posts so each renders its own grid thumbnail and og/twitter card
|
2026-07-07 07:15:06 +02:00 |
|
Matthew Meszaros
|
d2afbf9319
|
feat: rewrite the quarantine-early blog post with the four-band health table, sample-size floors, engagement-signal rule, and earned re-entry
|
2026-07-07 07:15:06 +02:00 |
|
Matthew Meszaros
|
bba7a8fcb3
|
feat: rewrite the bulk sender rules blog post to full cluster length, converting it to mdx and covering the current Google and Microsoft 5000-a-day SPF DKIM DMARC alignment enforcement bar
|
2026-07-07 07:15:06 +02:00 |
|