Commit Graph
56 Commits
Author SHA1 Message Date
Matthew Meszaros cbee2d3073 feat: answer a node heartbeat sent with INTERNAL_API_TOKEN where NODE_BROKER_TOKEN is required with the node's desired version only, recording nothing, through NodeHeartbeatAuthMiddleware and fleetnode UpdateOnly, so a node on an older release always updates itself onto the broker token without NODE_ACCEPT_INTERNAL_TOKEN, and say so in the configuration and split-deployment docs 2026-10-05 06:54:31 +02:00
Matthew Meszaros ab39429e37 feat: send HSTS from the forms service only on FORMS_DOMAIN and strip it in the installer's Caddy block for customer-owned domains, so no customer apex is pinned to HTTPS 2026-10-04 05:19:22 -07:00
Matthew Meszaros c178dadf81 feat: keep the key and blob brokers on NODE_BROKER_TOKEN only, and let an operator admit INTERNAL_API_TOKEN on the other node routes with NODE_ACCEPT_INTERNAL_TOKEN=true while nodes joined before the split upgrade 2026-10-04 05:18:01 -07:00
Matthew Meszaros cbf0667d8f feat: hold every API key to its creator's current workspace role on each gated route and AI tool, and stop a key, including on the realtime socket, once its creator leaves the workspace (api_key_holder_left) 2026-10-04 05:15:30 -07:00
Matthew Meszaros 5d4b0ad6e5 feat: hold the invite_member, update_member_role and rotate_webhook_secret AI tools to the same recent confirmation their REST routes require, answering reauth_required on /ai/tools and refusing them where no confirmed session exists 2026-10-04 05:14:30 -07:00
Matthew Meszaros 663c3013d1 feat: bind each Remie approval to its tool call and run it once, show every argument and the resolved send on dashboard and Slack approval cards, keep always-allow to settings managers with a revocable list and never for tools that start sending or change access, withhold secrets and unpermitted tool results from the assistant and shared conversations, gate /mcp, /ai/tools and the web and playbook tools on AI_AGENT or use_ai, and scope get_mailbox by organization 2026-10-04 03:45:02 -07:00
Matthew Meszaros e852a106a9 feat: serve every node-only internal route (data keys, message map, sync lookups, worker config, fleet heartbeat) on NODE_BROKER_TOKEN and leave only tracked links, domain redirects, page hits and forms on INTERNAL_API_TOKEN, render nodes only the node token when one is set, and have the installer generate a distinct NODE_BROKER_TOKEN for new installs and its own UPDATER_TOKEN 2026-10-04 03:21:58 -07:00
Matthew Meszaros bf47984cd5 feat: cap every OAuth grant and API key at the delegating member's role (consent narrows scopes and reports the withheld ones, tokens re-check the member's current role at every gate and MCP tool, keys stay within their creator's permissions, mailboxes and IP allowlist), keep OAuth tokens off API key and OAuth app management, require a fresh sign-in to approve an app, revoke a grant whose refresh token is presented twice, count only unexpired grants as installs, seal app webhook secrets under the instance key, name the workspace and flag unverified apps on the consent screen, and let credential managers list and revoke every member's app authorizations 2026-10-04 02:59:10 -07:00
Matthew Meszaros 8bb60e9449 feat: accept only Salesforce domains as an org's API host and call it through the SSRF-guarded client, register the Warmbly Cloud link only on self-hosted instances behind the instance admin with a second factor and dial it through safehttp with fixed error text, keep automation signing secrets in the sealed connection config, answer integration service failures with fixed messages, add security headers to the forms, tracking and docs origins and TLS 1.2+ to the nginx template, compare the captcha bypass in constant time, require TLS 1.2 for IMAP probes, and drop the unused RSA helpers 2026-10-04 02:58:43 -07:00
Matthew Meszaros 610d511307 feat: answer every server-side failure in admin, internal, webhook, warmup routing, Stripe webhook, agent tool and MCP handlers with the fixed internal error and log the detail against the request id, keep correctable webhook and routing refusals as typed errors with their own messages, drop the request URL from MillionVerifier transport errors so the API key never reaches a log or response, and redact :code path parameters in the access log 2026-10-04 02:57:17 -07:00
Matthew Meszaros f0ec39febd feat: scope automation and sequence unsubscribes to the caller's organization, require manage_settings to create, edit, enable or delete automations, run each integration action only on its own provider's connection (400 action_provider_mismatch), refuse org-permission gates when no organization service is wired, read /integrations/bookings like contacts, scope lead claims, research runs and CRM list cursors to the organization, and bind contact note edits to the contact in the path 2026-10-04 02:47:10 -07:00
Matthew Meszaros 75f764dc67 feat: require a verified Cloud Tasks token with a pinned audience on task webhooks, manage_settings on integration OAuth and a fresh membership check at every mailbox and integration OAuth finish, user verification for passkey sign-in, ended sessions before a password reset or ban reports success, and a revoked session when a rotated refresh token is replayed; remove the internal DEK delete route, log credential path parameters by name, hold remote images in received mail until the reader loads them, add Calendly and Cal.com signing keys with inbound URL rotation, keep automation signing secrets out of connection responses, and apply the password rules to the bootstrap password 2026-09-30 06:46:24 -07:00
Matthew Meszaros b3518dd775 feat: resolve the unibox contact panel's sender through the thread's campaign send when the reply comes from an address that is not a contact (alias, second domain, forward, or another workspace mailbox), via thread_id and account_id on GET /contacts/lookup with a match field, gated on unibox access and the API key mailbox allowlist, show a Replied from line and keep a loaded contact on a failed refetch, skip every workspace mailbox when picking the other party, apply the same match to Pause follow-ups when answering the sender, and document it 2026-09-28 09:03:19 -07:00
Matthew Meszaros 1b45c630bb feat: end a removed member's sessions and OAuth app grants through a removal hook detached from the request, refuse OAuth tokens whose holder is no longer a member, clear and detach a session selection that outlived its membership, gate subscription checkout, portal and cancel on manage_billing in the API and the dashboard, re-read org channel permissions live on member, role and ownership changes and withhold gated events while they cannot be read, drop the unrouted GitHub releases webhook handler, and correct v1 and release trigger paths in the docs 2026-09-26 22:07:54 -07:00
Matthew Meszaros 2df8991db5 feat: scope every session request to a workspace its user is still a member of, deselect a removed member's workspace on all their sessions, disconnect their realtime sockets on removal, and correct the singular /organization paths in the API docs 2026-09-26 21:18:49 -07:00
Matthew Meszaros 5b16a09b02 feat: write public objects without a canned ACL so a bucket whose ownership is owner-enforced still stores avatars, form assets, OAuth logos and email-body images, give the passkey login challenge its own per-IP budget separate from the one password sign-in draws on, and surface the API's own message at upload and passkey call sites instead of a generic sentence 2026-09-20 15:40:52 +02:00
Matthew Meszaros 03f59d4d6f docs: state the tenant and key-shape invariants in these comments as the constraints they are, rather than as an account of what each check replaced, since this repository is public and every self-hosted instance that has not updated yet reads the same text 2026-09-19 08:28:41 +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 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 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 c26300ae5e feat: backend half of the admin panel upgrade: delete the unrouted provisioning, releases, plan, discount and enterprise-inquiry admin handlers with their service and repository methods, retire the six admin permission bits nothing gated as reserved placeholders so live bit positions and existing masks are unchanged and IsSuperAdmin checks the live set, add forty admin endpoints for mailbox sync governor state with clear-throttle and restart-backfill, in-flight send reservations, cross-workspace dead letters with replay, task failures, webhook delivery health with reclaim, fleet capacity, the control loops decision log, dedicated bindings with release and the routed convert-to-dedicated, operator-driven workspace export and import, per-organization API keys and webhooks, warmup invalid-token abuse and action history, and signups by acquisition channel, and add a scheduled_job_runs table (migration 000135) with a jobrun package that every backend and consumer loop now records through and a run-now request the owning process picks up within fifteen seconds 2026-09-07 21:40:38 -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 c7da25d126 fix: never leave a per-IP rate-limit counter without a TTL, which would block that address forever once it passed the limit, by dropping the key and failing open when EXPIRE fails and repairing a missing expiry on the reject path, and assert the exit status as well as the message when the CLI installer rejects a flag 2026-09-04 21:12:27 -07:00
Matthew Meszaros b35e111705 fix: address the review on the CLI PR: give the sign-in handshake its own per-IP budget so a 200-poll login cannot lock the address out of the browser login, keep https for a remote host that names a port instead of sending a bearer token in cleartext, report truncation when a paginated walk stops at max-pages, accept a piped secret with no trailing newline, normalise WEBSOCKET_URL on its suffix so a bare /socket becomes a real endpoint, destroy a minted secret the moment its code expires, gate cli-installer-ci on the required status check, print a rejected flag instead of an unbound-variable error, and use a portable sha256 so the packaging runs on macOS 2026-09-04 21:03:58 -07:00
Matthew Meszaros 8ce331a7ac feat: fix the campaign test-email endpoint answering 404 for every caller by scoping its campaign lookup to the organization instead of the user, let it send from any mailbox of the organization and render a real contact through a new contact_id, attach the campaign's files, and extend the template preview with contact_id, campaign_id and account_id so it applies the signature, opt-out footer and plain-text rule the send path does 2026-09-04 02:58:36 -07:00
Matthew Meszaros 3bdb0fb82d feat: address the Greptile review on the pool link: require https for the cloud URL (loopback exempt for local development) since the instance token and mailbox passwords travel on it, remove the cloud copy when the local enrollment row cannot be written so a mailbox never warms in both places, delete the local enrollment row before the cloud one and restore it if the cloud call fails so a mailbox is never left with no warmup anywhere, and trim the new multi-line comments to the one-line style 2026-08-29 07:17:10 -07:00
Matthew Meszaros b75fdcf86c Merge remote-tracking branch 'origin/main' into feature/self-hosted-warmup-pool-access
# Conflicts:
#	internal/scheduler/warmup_scheduler.go
2026-08-29 07:10:38 -07:00
Matthew Meszaros 37b60b59d3 feat: let a self-hosted instance warm its mailboxes in the hosted pool: device-code link approved at /connect, instance-token API that enrolls SMTP/IMAP mailboxes as warmup-only accounts (no history import, non-warmup mail dropped), free for 10 mailboxes and unlimited on the seeded $15 pool plan, tier fallback to proven healthy mailboxes when a pool runs thin, local warmup stands down for enrolled mailboxes, Settings > Warmbly Cloud step flow and linked-instances page, docs guide, marketing copy, and fix SetWarmupLifecycle re-reading the row with an org-scoped lookup so every warmup start/pause returned 404 2026-08-29 07:09:04 -07:00
Matthew Meszaros a4739b63ec feat: add first-touch contact source attribution (migration 000106: contacts.source with a CHECK, source_detail, first_seen_at, existing rows stamped unknown) stamped at every creation site (dashboard manual/campaign, file import, Google Sheets sync, API key, AI assistant), write contact_created / campaign_added / campaign_removed / category_added / category_removed lifecycle events into contact_activities inside the same transactions as the links and merge them into the contact timeline, refactor FindNextRoutedPair's routing into a campaignRouter shared with a per-contact RouteContact and split CalculateNextCampaignTime into campaignSenders + placeCampaignSend so a read-only PreviewContactSend derives a contact's next step through the scheduler's own constraints, expose it as GET /contacts/:id/campaigns behind the contact service, render a per-campaign state panel, Campaigns/Lifecycle filter chips and expandable event rows in the Activity tab plus a Source section on Overview, cover it with TestLive* for the timeline events and the next-action preview (step wait, sending window, paused), and document it in the contacts, campaigns and API reference docs (issue #255) 2026-08-29 03:28:16 -07:00
Matthew Meszaros 0ae4db2c41 feat: make self-hosted auth work without a mail relay by rewriting the platform SMTP transport with real AUTH and TLS (it did neither, so SMTP_USERNAME/SMTP_PASSWORD were dead and every documented relay was unreachable), adding MAIL_TRANSPORT=smtp|log|ses with a log transport that prints codes so a fresh install can sign in with no relay, demoting the emailed login code to AUTH_LOGIN_CODE=always|new_device|off (off on self-host, per NIST SP 800-63B and OWASP ASVS), claiming the first owner through a single-use setup link or WARMBLY_BOOTSTRAP_* instead of register-then-psql, deriving every emailed URL from APP_URL rather than a hardcoded app.warmbly.com that leaked live reset tokens to the vendor, fixing the confirm hooks that read path params against paramless routes and broke login, register and reset confirmation in the dashboard everywhere, adding generic OIDC with PKCE, one-time state, verified nonce and (issuer,subject) identity binding, enforcing 2FA on the social paths that skipped it, adding a per-IP limiter and trusted-proxy handling to the unthrottled auth group, refusing boot on the published default secrets, and dropping mailpit from the default stack (#99) 2026-08-14 14:57:09 +02:00
Matthew Meszaros fc91d71492 feat: make google, apple and turnstile optional at boot 2026-07-20 09:56:17 +02:00
Matthew Meszaros d0e8bc1c97 feat: add one-command MCP OAuth connect on api.warmbly.com/v1/mcp — RFC 7591 dynamic client registration for public PKCE clients (dcr.go), RFC 9728 protected-resource metadata + WWW-Authenticate challenge via MCPAuthMiddleware, /v1/mcp now accepts an API key or OAuth token, public-client auth with no secret and mandatory PKCE reusing the existing OAuth 2.1 server (nullable-org clients, migration 000066), executable-redirect-scheme hardening on the open register endpoint, plus mcp/oauth/authentication/endpoints docs 2026-07-16 08:57:19 +02:00
Matthew Meszaros 9d0c432be7 feat: let OAuth apps subscribe to webhooks by declaring a url/events/secret and materializing per-org endpoints gated by each grant's scopes, with secret rotation and delivery observability (migration 000050) 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 587eae774e feat: serve the entire customer API (auth + resources) only under /v1 with no unversioned alias, and repoint the web and admin clients to the versioned base accordingly 2026-06-13 07:18:23 +02:00
Matthew Meszaros 49b01c6b67 feat: version the public API under /v1 by mounting the customer surface under both /v1 and the bare paths, adding an API-Version response header and Deprecation/Sunset headers nudging API-key callers off the unversioned aliases 2026-06-13 06:27:01 +02:00
Matthew Meszaros e6b2f233a1 feat: expand integration and meeting APIs 2026-06-08 06:25:40 +02:00
Matthew Meszaros 3837cd3898 feat: bound auth login network waits
Add request and mail-delivery timeouts around auth flows so login requests cannot hang indefinitely when notification delivery stalls.

Allow the local admin dev origin through default CORS and update context-aware lint fixes so the repository lint gate passes.
2026-06-01 16:56:24 +02:00
Matthew Meszaros 640da62b32 feat: add api idempotency keys 2026-05-30 04:31:43 +00:00
Matthew Meszaros 878e8d921e feat: enforce api key email scopes 2026-05-30 04:28:25 +00:00
Matthew Meszaros 3076cff5d6 feat: add api request ids 2026-05-30 04:26:12 +00:00
Matthew Meszaros 99226338c9 infra(encryptedkeys): pluggable DEK store with HTTP proxy for workers
New encryptedkeys.Store interface with three impls:

  postgres  - backend default, durable via PG
  dynamodb  - existing AWS path, also covers Scylla Alternator via
              AWS_ENDPOINT_URL_DYNAMODB
  http      - worker-side adapter that talks to the backend's new
              /api/v1/internal/dek/:userID endpoint, so workers never
              connect directly to Postgres

The HTTP endpoint sits behind a new InternalAuthMiddleware that does
constant-time bearer-token compare against INTERNAL_API_TOKEN. Fail-
closed if the env var is unset.

cipher.Service now takes an encryptedkeys.Store instead of a Dynamo
repository. The old internal/repository/dynamo_user_encrypted_keys.go
is deleted (the file also had a pre-existing copy-paste bug using
EmailMessageMapTable in Get/Del that's gone with it).

New migration 38 adds user_encrypted_keys (user_id PK, encrypted_data_key,
created_at, updated_at).

20 tests cover HTTP round-trip, conflict semantics, factory selection,
middleware auth (fail-closed / wrong-scheme / timing-safe / happy path),
and DEK handler responses through gin's test harness.
2026-05-27 14:42:11 +00:00
Matthew Meszaros 9123c4bfcf feat: full api keys product (smart limits, analytics, dashboard) 2026-05-24 16:07:18 +00:00
Matthew Meszaros 8002d56b86 feat: full api key support across data routes 2026-05-24 11:59:05 +00:00
Matthew Meszaros d227038ca0 ci: drop unused/unconvert/gosimple + shadow/nilness, run gofmt
Disable the linters that fire on legacy code without flagging real
bugs: `unused` (orphan repos kept for future feature flags),
`unconvert` (defensive type conversions), `gosimple` (style
suggestions in code we don't want to touch).

govet: disable `shadow` (idiomatic `err :=` re-decls in transaction
patterns) and `nilness` (legitimate defensive nil checks that look
tautological to the analyzer).

Ran `gofmt -w internal/ cmd/` — every Go file now passes
gofmt -l with no output.

Kept: govet, staticcheck, ineffassign, typecheck, bodyclose, noctx,
sqlclosecheck, gofmt, goimports, misspell — the real-bug checks.
2026-05-23 16:54:12 +00:00
Matthew Meszaros 00dac953ae fix: add nil checks on SmtpImapData and avoid slice modification during iteration 2026-04-10 06:58:41 +00:00
Matthew Meszaros 7f90323d55 chore: remove metrics endpoint, prometheus instrumentation, and IP rate limiter 2026-04-10 06:32:30 +00:00
Matthew Meszaros 7e123a2dac fix: remove deep health endpoint and add IP rate limiting to public endpoints 2026-04-09 16:03:37 +00:00
Matthew Meszaros 0799020dac feat: add metrics, warmup content variety, tz-aware scheduling, org budget, admin stubs, and bug fixes 2026-04-09 14:33:45 +00:00