Commit Graph

83 Commits

Author SHA1 Message Date
Matthew Meszaros f49816a7b1 feat: construct and bidirectionally wire the referral service into the backend bootstrap 2026-06-28 05:09:50 +00:00
Matthew Meszaros 660d717456 feat: register the webhook and oauth-app-webhook routes and wire the delivery worker, audit-to-webhook bridge, and app domain resolver into the backend 2026-06-15 08:11:20 +02:00
Matthew Meszaros 8c175d4221 feat: add an OAuth 2.1 authorization server (migration 000047 apps/codes/grants, app registration CRUD, authorization-code-with-PKCE authorize+token+refresh-rotation+revoke endpoints, RFC 8414 discovery, bearer-token validation wired into the auth middleware reusing the API-permission gates, scopes mapped to API permission bits) 2026-06-13 13:56:39 +02:00
Matthew Meszaros 20935ef061 feat: add a nil-safe per-org daily outbound-action quota (Redis daily counter, anti-abuse ceiling on the HTTP-request automation node, wired in both backend and consumer, fail-open) to bound webhook relay abuse 2026-06-13 13:41:20 +02:00
Matthew Meszaros 7901e9a92c feat: wire the Pub/Sub realtime provisioning, the instant-branch automation runner, and the campaign-chain reconciler into the backend and consumer binaries 2026-06-13 06:27:06 +02:00
Matthew Meszaros ba89a932cd feat: run the label_email action on reply — campaign instant and scheduled paths via advanced LabelThread/LabelLatestThreadForContact, automations via execNativeAction reading thread_id and _user_id from the reply payload — and move the native-actions adapter to a shared package wired into the consumer too so reply/bounce/warmup automation actions stop silently failing 2026-06-12 16:44:16 +02:00
Matthew Meszaros ba3ab98e1e feat: bridge realtime events over Redis pub/sub when Google Pub/Sub is unconfigured (RedisBus publisher in backend/consumer plus Realtime.Redis.EventSubscriber and a shared EventBroadcaster) so dashboard live updates and presence-driven collaboration actually fire in local dev 2026-06-12 04:49:58 +02:00
Matthew Meszaros 5bcc2baaa8 feat: implement the coming-soon security features — logged-in change-password (verify current, policy-checked, POST /me/password) with a real dialog, and new-device sign-in alerts (security notification category fired from the token service on an unrecognized OS+browser, delivered in-app and by email), removing the comingSoon stub helper and updating docs 2026-06-11 12:30:44 +02:00
Matthew Meszaros 160dc0bc76 feat: implement the coming-soon notification delivery channels — Email (SES/SMTP to the account email) and Slack (posts to the org's connected workspace via a new integration NotifySlack), wired in both backend and consumer with per-channel gating, real toggles replacing the coming-soon labels, and updated docs 2026-06-11 12:21:48 +02:00
Matthew Meszaros 8732805934 feat: replace signed click redirects with server-side link tickets (tracked_links store, internal resolver API, opaque /c/<id> URLs, layered anti-probe caches with miss budget and circuit breaker) removing TRACKING_LINK_SECRET entirely 2026-06-11 09:30:21 +02:00
Matthew Meszaros 2c5e8b2cbd feat: make TRACKING_LINK_SECRET a required boot-time secret on backend and tracking service with no unsigned mode and no rotation grace, so rotating the key revokes old links immediately 2026-06-11 09:00:04 +02:00
Matthew Meszaros abdfd05c34 feat: org-scope realtime events (inbox, campaign, tracking, account health) and emit EMAIL_SENT/EMAIL_REPLIED/EMAIL_DELETED pulses so the whole team's dashboard updates live 2026-06-11 07:43:57 +02:00
Matthew Meszaros 4013b33008 feat: expose security notification APIs
Wires notification and two-factor services into the backend and consumer, adds the authenticated notification feed/preferences endpoints, and exposes the public 2FA login verification endpoint.
2026-06-08 15:04:47 +02:00
Matthew Meszaros 5f7ac801ba feat: execute automation graphs 2026-06-08 12:03:11 +02:00
Matthew Meszaros e6b2f233a1 feat: expand integration and meeting APIs 2026-06-08 06:25:40 +02:00
Matthew Meszaros 62da3c3e3d feat: add organization teams API 2026-06-07 07:03:49 +02:00
Matthew Meszaros da82993846 feat: add campaign workflow backend support
Add sequence action-node storage and execution, template conditional rendering, lead progress state, profile updates, webhook fan-out throttling, and supporting repository fixes.
2026-06-06 07:49:52 +02:00
Matthew Meszaros 3c440a9f0c feat: add campaign attachment endpoints
Persist campaign attachment metadata, enforce attachment storage quotas, and expose list, upload, and delete routes backed by object storage.
2026-06-05 06:03:13 +02:00
Matthew Meszaros d6f055da19 feat: add lead sync backend 2026-06-04 11:49:55 +02:00
Matthew Meszaros e1e02c5c18 feat: add campaign send controls backend 2026-06-04 08:53:23 +02:00
Matthew Meszaros 1afe453eb0 feat: add deliverability controls
Add seed inbox-placement testing with admin management, placement result polling, and seed mailbox persistence.

Add pre-send email verification, invalid-recipient skipping, warmup-health campaign gating, and RFC 8058 one-click unsubscribe headers/endpoints.
2026-06-03 16:47:53 +02:00
Matthew Meszaros 2009780beb feat: add warmup batch generation
Add OpenAI Batch API support for warmup content generation, including job metadata, polling, cancellation, and completed-batch ingestion.

Share the generation prompt between sync and batch modes and humanize/lint generated threads before storing them in the warmup content library.
2026-06-03 11:14:33 +02:00
Matthew Meszaros 15ef9d4994 feat: add warmup content controls
Add warmup content generation and admin review surfaces, plus mailbox warmup appeal/status APIs.

Track warmup engagement and tampering signals so unsafe mailboxes can be handled by the warmup flow.
2026-06-03 05:05:53 +02:00
Matthew Meszaros 2430095197 feat: move infrastructure state off dynamodb
Remove DynamoDB-backed storage paths, add Postgres/HTTP repositories for mailbox state maps, wire the internal message-map API, and add provisioning runner/migration plumbing.
2026-06-02 15:54:12 +02:00
Matthew Meszaros 83eae84d49 feat: merge main into warmup branch
Resolve the main-branch conflict in realtime event publishing by keeping both warmup account-health and audit-created events. Renumber the warmup migrations after the current main migration tail.
2026-06-01 11:36:04 +02:00
Matthew Meszaros aa391b5e0c feat: fix warmup reconciler guards 2026-06-01 11:27:44 +02:00
Matthew Meszaros a04d7450bc feat: guard warmup reconciliation by access
Make the warmup task reconciler re-check account state and org warmup access before scheduling replacement tasks. Remove pool membership for accounts whose org can no longer use warmup to avoid repeated no-access task churn.
2026-06-01 11:19:44 +02:00
Matthew Meszaros fc18b8e470 Merge pull request #23 from warmbly/feature/passkey-login-experience-2 2026-06-01 09:15:21 +02:00
Matthew Meszaros e237986f46 feat: merge main into integrations oauth 2026-06-01 04:49:50 +02:00
Matthew Meszaros 4500aeba0f feat: add integration oauth connections
Adds OAuth-backed integration connection management across the API, repository, event dispatch, migrations, docs, and dashboard UI.

Includes realtime invalidation and small dashboard type compatibility fixes needed for the web typecheck gate.
2026-06-01 04:25:14 +02:00
Matthew Meszaros e0ced97ba1 feat: add organization audit trail
Move audit logs into org-scoped Postgres storage, wire audit events across backend handlers, and surface actor details in the dashboard activity log.

Add realtime audit invalidation and retention pruning so the trail stays current and bounded.
2026-06-01 04:23:00 +02:00
Matthew Meszaros 87f9bf58b0 feat: add Safari-ready passkey login
Add passkey enrollment and login wiring, including a Safari-safe explicit login path that prefetches the WebAuthn challenge before the click and calls the credential ceremony immediately from the user gesture.
2026-06-01 03:09:17 +02:00
Matthew Meszaros 0cb53a25a6 feat: improve warmup pool scheduling
Add explicit warmup participant roles so connected accounts can provide recipient-only pool capacity without becoming senders. Cap warmup scheduling by eligible recipients, avoid same-day recipient reuse, reschedule exhausted pools, and reconcile missing warmup task chains.
2026-05-31 18:07:13 +02:00
Matthew Meszaros 6249d3e1a2 feat: refine unibox reply workflow
Open the reply composer only after the user chooses a message to reply to or forward, and wire per-message actions through the thread view.

Also clean up cancelled scheduled sends from Cloud Tasks on a best-effort basis while keeping the database status as the source of truth.
2026-05-31 05:14:22 +00:00
Matthew Meszaros 72073c669e feat: bootstrap campaign wakeups
Create the initial per-campaign Cloud Tasks wakeup when a campaign starts, using the same idempotent task chain as subsequent sends. Pause or complete campaigns instead of leaving active campaigns with no scheduler when no work can be queued.
2026-05-31 04:51:03 +00:00
Matthew Meszaros 7b5a87d1d1 feat: add unibox scheduled send backend
Add Unibox overview, snooze, and scheduled-send endpoints with task repository support, queue caps, execution-time guards, and snooze persistence.
2026-05-31 04:26:26 +00:00
Matthew Meszaros 44c837d8ac Merge branch 'main' into feature/dashboard-plans
# Conflicts:
#	AGENTS.md
2026-05-30 09:49:12 +00:00
Matthew Meszaros d8a546eca5 feat: add worker enrollment install 2026-05-30 05:10:27 +00:00
Matthew Meszaros 640da62b32 feat: add api idempotency keys 2026-05-30 04:31:43 +00:00
Matthew Meszaros 60773b3d8e feat: make dashboard realtime 2026-05-30 04:17:10 +00:00
Matthew Meszaros 49e21113dc feat: wire analytics and rate limit services into backend handler 2026-05-29 16:49:52 +00:00
Matthew Meszaros 6766031cc5 feat: add discount code support for checkout and plan changes 2026-05-29 05:49:19 +00:00
Matthew Meszaros e7ef328b2d Merge pull request #18 from warmbly/feature/admin-management
feat: admin management surface with overrides, ban scope, and throttles
2026-05-29 05:01:00 +02:00
Matt e73ff0aa86 ci(go): gofmt fixes and AGENTS.md CI rules
Two files were unformatted, tripping the golangci-lint gate that runs
gofmt:

  - cmd/backend/main.go: dailythrottle import out of alphabetical order
  - internal/repository/pg_admin_outreach.go: numbered list comment used
    three-space indentation; gofmt wants two

Both fixed by running gofmt -w against the offending files.

Add a "Working In This Repo" section near the top of AGENTS.md (which
CLAUDE.md symlinks to) documenting the rules this PR violated:

  - go build is not the ship signal; CI runs gofmt via golangci-lint
  - always gofmt -w changed Go files before considering work done
  - run the right typecheck/lint step in each frontend tree before
    pushing
  - commit messages do not carry Co-Authored-By or other AI/agent
    attribution footers
2026-05-29 04:57:31 +02:00
Matt b633e326e5 feat(throttle): per-day creation throttles for campaigns/mailboxes/orgs
The HardCap* constants stop "you have 5000 campaigns on this org"; the
throttles in this commit stop "you created 1000 campaigns today on a
fresh unlimited account." Different shape, different abuse, different
mechanism — Redis-backed per-(scope, resource, UTC-day) counters that
reset by key design at midnight UTC, no scheduled job needed.

New service internal/app/dailythrottle:
  - CheckAndIncrement(scope, resource, ceiling) atomically bumps the
    counter and returns errx.TooManyRequests when the post-increment
    value exceeds the ceiling.
  - 25h TTL so the key always expires after the day rolls over even
    if the process restarts before midnight.
  - Fail-open when the cache is absent (jobs/tests) so creation paths
    that haven't been wired with a cache still work.

Caps (config.DailyThrottleNew*):
  - 20 new campaigns/org/day
  - 5  new mailboxes/org/day
  - 3  new workspaces/owner/day

Wired into three creation paths:
  - campaign.Create — scoped on the orgID when present
  - email.OAuthFinish + email.OnboardSMTPIMAP — scoped on the orgID;
    fires only at actual create, not OAuthStart, so retrying a failed
    OAuth flow doesn't burn the day's budget.
  - organization.Create — scoped on the owner uuid (the org doesn't
    exist yet)

Adds errx.TooManyRequests (HTTP 429) since no caller had one before.

emailService gains WireThrottle alongside the existing WireWebhooks
pattern so jobs / tests can build the service without a cache. Same
treatment in main.go.
2026-05-28 13:34:01 +02:00
Matt d80efc88b4 feat(ban): runtime enforcement for ban-scope bitmask
The bitmask landed in 000045 with schema + UI; this commit wires the
three gates the bits describe.

  - BanScopeLogin    → authService.LoginConfirm checks the scope after
                       password verification and refuses the session
                       with "this account has been suspended"
  - BanScopeOrgCreate → organizationService.Create checks the scope
                       before any other validation and refuses with
                       "this account cannot create new workspaces"
  - BanScopeSend     → emailSendService.SendEmail checks the scope
                       before validating the email account and refuses
                       with "this account cannot send email"

Adds UserRepository.GetBanState(ctx, userID) → uint32 — a single-column
read so the hot paths don't have to fetch the full user row just to
check a flag. Returns 0 when no ban (the column defaults to 0); the
caller treats 0 as "allow."

Threads userRepo into emailSendService — the only constructor change
in this commit. cmd/backend/main.go updated accordingly.
2026-05-28 12:43:50 +02:00
Matt bd6a045751 feat(admin): outreach composer (platform mailer + reply-to + audit log)
Adds a dedicated admin path for sending platform email — distinct from
the campaign emailsend service (which sends through customer mailboxes)
so the two abuse surfaces never share code paths.

Schema (000047) adds admin_outreach_messages: every send is recorded
with sent_by, the resolved to_email, the optional reply_to, subject,
body, and a queued → sent/failed status. Failed sends keep their error
column populated for the audit log.

Extends notify.EmailNotificationService with SendOutreach so both
backends (SES + SMTP) support custom Reply-To: SES via the native
ReplyToAddresses field, SMTP via a forged Reply-To header. The
existing transactional Send() remains unchanged so no other caller is
affected.

Service (internal/app/adminoutreach) resolves recipients three ways:
to_email (raw address), to_user_id (sends to the user's account email),
or to_org_id (sends to the workspace owner). Persist-then-send-then-
mark ensures the audit row exists even if the mailer hangs, and
mark-failed captures the error string verbatim.

Routes:
  POST /admin/outreach            manage_organizations
  GET  /admin/outreach            view_organizations

Admin UI: composer with recipient mode picker (email / user_id / org_id),
configurable Reply-To (defaults to support@warmbly.com so customers can
actually reply), subject + HTML body editor, and an outreach log below
showing the last 50 sends with status badges and error details. Sidebar
entry under Accounts (Send icon).
2026-05-28 12:25:18 +02:00
Matt 1e2577ce97 ci(go): gofmt integration files 2026-05-28 09:53:43 +02:00
Matt 20f1e93c4b feat(integration): backend foundation for tier 1+2 integrations
Adds an integrations app module covering the providers from the tier 1/2
plan: Calendly, Cal.com, Google Sheets, Google Postmaster, Microsoft SNDS,
DMARC ingestion, and Cloudflare/GoDaddy/Namecheap DNS. One unified
migration provisions integration_connections, dmarc_reports + record
rows, postmaster_snapshots, dns_verifications, and meeting_bookings.

The service exposes a generic CRUD surface for connection state with
per-provider files for parsing (calendly.go, dmarc.go), HTTP clients
(cloudflare.go, postmaster.go, google_sheets.go), and DNS verification
(dns.go). Inbound webhook routes use per-org URL-embedded secrets so
Calendly/Cal.com/DMARC providers post directly without Warmbly auth.
DNS verifier resolves SPF/DKIM/DMARC + tracking CNAME and surfaces
fixes when a record is missing.
2026-05-28 08:19:10 +02:00
Matthew Meszaros d2414ad29f ci(go): gofmt all flagged files
Go CI fails on golangci-lint's gofmt check. Ran gofmt -w against
every file the linter named plus a handful of others that drifted
during the autonomous-fleet work. No semantic changes — alignment
of struct field whitespace and one mis-indented import block.

gofmt -l ./... is now empty; go build + go vet are clean.
2026-05-27 16:40:23 +00:00