Commit Graph
41 Commits
Author SHA1 Message Date
Matthew Meszaros 8bd2c2b57a feat: make self-hosting work end to end and rewrite the guide around what was tested (#97) 2026-08-13 09:47:46 +02:00
Matthew Meszaros bebc09289d build: cross compile the go images and fix realtime and the healthchecks 2026-07-21 17:34:59 +02:00
Matthew Meszaros 4962765a79 fix: format the org_channel AI_DRAFT access_unibox clause to satisfy mix format --check-formatted (Elixir CI) 2026-07-15 06:47:07 +02:00
Matthew Meszaros 56de65380b feat: inbox agent that drafts a suggested unibox reply on inbound human replies for human approve/edit/discard - paid + per-org opt-in (organizations.inbox_agent_enabled) feature where the consumer's reply hook, on a non-automated reply, detaches a goroutine (panic-contained, never blocks ingest) that checks entitlement, dedupes via ai_thread_drafts partial unique indexes (one pending per thread, unique source_message_id), pre-checks balance, grounds a reply in the thread history + org voice + skills via generation.Provider.Complete, reserves a draft row then charges 5 credits (idempotency inbox_agent:<draft.ID>, row unwound on a fresh context if the charge fails so no unpaid draft lingers), and emits an org-scoped AI_DRAFT_READY event gated on access_unibox; the agent never sends - only a human POST to /unibox/agent-drafts/:id/approve sends through the normal reply path, claiming pending->approved before send with an approved->pending revert on send failure, alongside list + discard endpoints; plus the unibox awaiting_agent_draft badge + agent_drafts search scope, an AgentDraftCard in the thread view with inline-editable approve-and-send/discard, the workspace settings opt-in toggle, CanUseInboxAgent paid gate, migration 000065, and docs (inbox-agent guide, endpoints, realtime) 2026-07-14 06:13:27 +02:00
Matthew Meszaros f48d51bf16 feat: AI contact research agent that gathers cited web findings per contact with sync and background-batch runs - contact_research_runs table plus a strict save_research schema (every signal and public_artifact must carry a url, confidence high/medium/low, signals<=5 hooks<=3, reject-and-reprompt once then fail), a text/template runtime prompt injecting org voice, contact record, objective, and 5-search/6-fetch budgets, a research service that drives search_web/fetch_url through the provider loop and charges 2 credits on save (billable even for nothing_found, refund-free since charged only on save, balance and abuse-cap pre-checked so a capped org never does free work), POST/GET /contacts/:id/research plus a 500-cap /contacts/research/batch draining through a bounded pool of 4 workers over FOR-UPDATE-SKIP-LOCKED claims (no new Kafka), an org-scoped AI_RESEARCH_PROGRESS realtime event gated to view_contacts, APIPermAIResearch bit 23, a ContactEdit Research tab and a ContactsTable bulk Research action, and docs; also org-scopes every credit and research idempotency key so a client-supplied Idempotency-Key can never replay across tenants 2026-07-13 19:34:11 +02:00
Matthew Meszaros 262d25779c feat: realtime admin dashboard + diagnosability - new Elixir admin:platform channel (JWT-only, gated on users.admin_permissions) mirrors every platform event, the admin app connects through a /getaway-tokenized Phoenix socket client with a throttled react-query invalidation spine, a Live Events firehose page streams the platform with filters/pause/JSON expand, a System Status page runs parallel infra probes (postgres/redis/kafka/schema-registry/realtime/tracking via /admin/system/status), and the worker logs viewer gains follow mode, line-count selection, and copy 2026-07-12 09:00:10 +02:00
Matthew Meszaros 15088a6f64 feat: pin cowboy >= 2.16 in the realtime service so CR/LF response header values are rejected before the wire (EEF-CVE-2026-43966/43969 has no patched cowlib release) 2026-07-07 05:56:47 +02:00
Matthew Meszaros e190571696 feat: harden the live channel with an is_map payload guard, byte-bounded chat/id/resource strings, a per-surface permission gate that strips cursor-chat text, and a websocket max_frame_size cap 2026-07-02 20:33:44 +02:00
Matthew Meszaros 88cabf5cac feat: cap the live:select ids payload before traversal so an oversized selection list is never fully walked by the realtime channel 2026-07-02 14:31:36 +02:00
Matthew Meszaros 02b25724f4 feat: extend the ephemeral live channel with a sanitized chat field on live:cursor frames and a new live:select kind that broadcasts a capped node-id list for canvas selections 2026-07-02 07:40:06 +02:00
Matthew Meszaros 3ffd4416a2 feat: add a generic live:patch channel kind that fans out a small sanitized scalar map for ephemeral collaborative-state hints 2026-06-30 10:34:14 +02:00
Matthew Meszaros 248c886290 feat: add an ephemeral live:cursor/live:node channel path to the realtime org channel that bypasses the Redis ws limiters and the sequenced resume buffer via an in-process token bucket and broadcast_from fan-out 2026-06-30 10:06:19 +02:00
Matthew Meszaros f9d5c85a32 fix(security): move realtime sentry onto a Finch HTTP client and drop the hackney dependency to clear CVE-2026-47071 2026-06-28 06:18:33 +00:00
Matthew Meszaros b83aa51921 feat: make CUSTOM_EVENT fire-event signals an explicit allow on the org channel and return structured {code, reason} errors on a failed channel join 2026-06-14 11:02:12 +02:00
Matthew Meszaros 799e5f10ef feat: surface WebSocket connection-rejection reasons to the client as a structured {code, reason, retry_after_ms} payload (Discord-style 4003/4004/4007/4009/4010) instead of a bare 403, and fix the rate-limit 3-tuple match 2026-06-14 11:02:12 +02:00
Matthew Meszaros 34e579fc0f feat: authenticate the realtime WebSocket with OAuth access tokens (wmat_ with the realtime scope), validated against oauth_access_grants alongside wmbly_ API keys and JWTs, and map not-a-member/forbidden rejections to close code 4010 2026-06-14 11:02:12 +02:00
Matthew Meszaros f2fa0d756c feat: guarantee in-order gateway delivery via a per-org sequencer pool — serialize each org's events (partitioned by org id) so seq order always equals broadcast order even under concurrent ingest, closing the resume race where a higher seq could arrive first and a disconnect would skip the lower one 2026-06-13 11:31:07 +02:00
Matthew Meszaros 4552ab4e31 feat: make the org realtime channel a resumable gateway — assign a monotonic per-org sequence + buffer each event in a capped Redis stream (Realtime.EventLog) before broadcast, advertise the current seq in the HELLO and stamp it on every event, and replay the missed gap (re-filtered) on reconnect via resume.last_seq or signal a full resync when the buffer no longer covers the client 2026-06-13 11:23:07 +02:00
Matthew Meszaros c5ced50868 fix: apply mix format to the realtime service (supervisor child-list indentation in application.ex + org_channel.ex) so the Elixir CI format check passes 2026-06-12 17:45:32 +02:00
Matthew Meszaros 7b34b96b37 feat: keep the per-user realtime connection cap at 10 but reap dead connections via a periodic liveness sweep and process pids, so the count reflects real presence and stale sockets never hold a slot 2026-06-12 07:46:13 +02:00
Matthew Meszaros 3cbfc06bc4 feat: add WebSocket intents (selective event-family subscription) and a HELLO-style org join reply advertising heartbeat cadence, and correct realtime docs on connection rejections and at-most-once delivery 2026-06-12 07:17:09 +02:00
Matthew Meszaros 4f1ac8c273 feat: enforce org team-presence privacy in the realtime OrgChannel — gate Presence.track on show_online, strip viewing/editing detail when show_activity is off, and re-track/untrack live on PRESENCE_POLICY_UPDATED 2026-06-12 05:59:01 +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 70064e9e1d fix: define OrgChannel.handle_out/3 so presence_diff broadcasts are pushed to clients instead of crashing the channel — the missing callback was terminating the org channel and dropping every websocket connection in a reconnect loop 2026-06-11 13:47:11 +02:00
Matthew Meszaros 80d080b982 feat: fix realtime UUID param encoding crashing org-channel joins, remove the permission matrix from Roles & access, compact the role dropdown, and drop the redundant Integrations tab from settings nav 2026-06-11 11:16:13 +02:00
Matthew Meszaros 3473d09bcc feat: fix review findings in the roles redesign — GetMembers role_id column (members endpoint 500), TransferOwnership role_id hygiene, accept-time role re-resolution, race-free in-use delete guard covering invitations, assignment anti-escalation with self-role-change block, canManage-gated members UI, colored RolePills, fresh currentOrganization on refetch, dev JWT_SECRET wiring for make realtime, docs corrections 2026-06-11 10:45:21 +02:00
Matthew Meszaros 371fe0c0a3 feat: add Phoenix.Presence org collaboration layer (online members, viewing/editing/replying activity) with permission-gated org event fanout in the realtime service 2026-06-11 07:51:34 +02:00
Matthew Meszaros 5b735bb528 feat: replace ascii architecture diagrams in the realtime readme and resources docs with prose and tables 2026-06-10 18:27:42 +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 60773b3d8e feat: make dashboard realtime 2026-05-30 04:17:10 +00:00
Matthew Meszaros 99d2bff0cb feat: unify dev-mode hot-reload for go, rust, elixir under make dev 2026-05-25 03:21:42 +00:00
Matthew Meszaros ce870a15b3 ci: fix Elixir formatting / Elixir version / missing pnpm lockfile
- realtime/: `mix format` applied; long Logger calls reformatted across
  config/runtime/application/connections/user_channel/user_socket/
  endpoint. CI's "Check formatting" step now passes.
- CI Elixir bumped from 1.16 → 1.18 (with OTP 27) to match mix.exs's
  `~> 1.18` requirement. Phoenix 1.8.7 + plug 1.19 also expect this.
- web/: generate + commit pnpm-lock.yaml so actions/setup-node@v4's
  pnpm cache step + `pnpm install --frozen-lockfile` can resolve.
2026-05-23 16:17:27 +00:00
Matthew Meszaros d8bb10bf8f ci: fix Bad-credentials + bump deps to clear Trivy CVEs
Workflow:
- Add explicit `permissions: contents: read, pull-requests: read`
  so dorny/paths-filter can list PR files via the GitHub API. Without
  it the "Detect Changes" job dies with "Bad credentials" on PRs and
  every downstream language CI gets skipped.

Go:
- google.golang.org/grpc v1.78.0 → v1.79.3 (CVE-2026-33186 — HTTP/2
  path validation authorization bypass).

Elixir (realtime):
- cowboy 2.14.2 → 2.15.0 (CVE-2026-8466)
- cowlib 2.16.0 → 2.16.1 (CVE-2026-43970, CVE-2026-7790)
- phoenix 1.8.3 → 1.8.7 (CVE-2026-32689 — long-poll memory blow-up)
- plug 1.19.1 → 1.19.2 (CVE-2026-8468 — multipart header overflow)
- plug_cowboy 2.7.5 → 2.8.1 (CVE-2026-32688 — unauth DoS)
- postgrex 0.22.0 → 0.22.2 (CVE-2026-32687 — channel-name SQLi)

Rust (tracking):
- aws-lc-rs 1.15.4 → 1.17.0 (pulls aws-lc-sys to 0.41.0 — fixes
  GHSA-394x-vwmw-crm3, GHSA-65p9-r9h6-22vj, GHSA-9f94-5g5w-gf6r,
  GHSA-hfpc-8r3f-gw53, GHSA-vw5v-4f2q-w9xf)
- openssl 0.10.75 → 0.10.80 (CVE-2026-41676/8/81/898, -42327)
- rustls-webpki 0.103.9 → 0.103.13

- Add .trivyignore for GHSA-82j2-j2ch-gfr8 on the old rustls-webpki
  0.101.7 path that aws-smithy-http-client / hyper-rustls 0.24 still
  pulls in. AWS SDK hasn't migrated to rustls 0.23+ yet; the CRL
  parsing path the advisory covers isn't reachable from our usage
  (SSM + Secrets Manager at startup over the public CA chain).
2026-05-23 16:13:39 +00:00
Matthew Meszaros 695e2b5a33 fix: contacts crash, campaigns panic, websocket — 4 distinct bugs found while triaging the page-blank symptom
1) Contacts crash "c is null":
   contactRepository.Search declared `var contacts []models.Contact` so
   an empty result set returned a nil slice, which Go marshals as JSON
   null. The frontend's flatMap((p) => p.data) over null yields [null],
   and the page then accesses c.subscribed → throws. Initialize as
   make([]models.Contact, 0, limit+1) so the wire format is always [].
   Also defensive on the client: useSearchContacts + useCampaigns now
   coerce p.data ?? [] and drop nulls before returning.

2) Campaigns panic on any non-empty result:
   campaignRepository.Search allocated `make([]models.Campaign, 0, limit+1)`
   (length 0) then did `campaigns[i] = campaign`. That's an
   index-out-of-range on the first iteration. Switched to `append`.
   Anyone with at least one campaign would see a 500 / blank screen.

3) Websocket "Token expired":
   SocketTTL was 60s. The frontend reconnect backoff caps at 30s, so
   after a rejected handshake the next attempt could fire 30-60s
   later. Combined with rare back-pressure on /getaway the token was
   already past exp by the time the realtime saw it. Bumped to 10 min
   — short enough to keep the token low-impact, long enough to outlast
   the backoff schedule.

4) Websocket "Connection limit exceeded":
   Realtime.Connections only untracked on channel terminate, never on
   socket disconnect. Sockets that connected and disconnected without
   joining a channel leaked. Each reconnect loop bumped the counter
   until the per-user limit (10) was hit, after which every legitimate
   connect was rejected even after fixing #3.
   Fix: GenServer Process.monitor's the socket pid on track, and
   `:DOWN` handler calls do_untrack with the right (user_id, ip).

5) Phoenix protocol mismatch:
   Frontend appended vsn=2.0.0 to the WS URL, but sendRaw + joinChannel
   send the V1 object format. Realtime's Phoenix.Socket.V2.JSONSerializer
   crashed with a badmatch on the first phx_join, killing the socket
   right after connect. Switched to vsn=1.0.0 to match what the client
   actually emits.
2026-05-23 05:32:28 +00:00
Matthew Meszaros c12fd93def Add local Sentry logging 2026-02-14 05:49:49 +01:00
Matthew Meszaros 141bc54974 Add sample auth UI theme 2026-02-10 19:30:47 +01:00
Máté Mészáros (Laptop) ed35ab2dbc Realtime Updates 2026-01-30 15:32:58 +01:00
Máté Mészáros (Laptop) 6adb4cdd5a Organization, Subscription, Inqueries, limits. 2026-01-27 05:55:48 +01:00
Máté Mészáros (Laptop) 5ac159d2f8 Realtime, api keys & more 2026-01-26 16:04:42 +01:00
Máté Mészáros (Laptop) 7ae36cc689 Convert realtime submodule to regular directory 2026-01-26 04:50:21 +01:00
Máté Mészáros (Laptop) 81d5970ea8 Realtime, Task Handler, Worker & Consumer Setup 2026-01-26 04:42:19 +01:00