High-level components
Component descriptions
Frontend — Next.js (app.causeloop.ai)
The product UI is a Next.js application authenticated via WorkOS AuthKit. After exchanging the WorkOS access token for a platform JWT, the frontend communicates exclusively with the backend API and a WebSocket for realtime updates. Key routes:Backend API — FastAPI (api.causeloop.ai)
The backend is a FastAPI application serving all product functionality under/v1. It is stateless and horizontally scalable. Key responsibilities:
- Auth token issuance and verification
- The deterministic engine pipeline (below) — clustering, criticality, hazard fitting, alerting
- Business logic for issues, patterns, predictions, recommendations, clusters, and connectors
- AI orchestration (dispatching extraction/root-cause/narration jobs to the LLM Router)
- Tenant isolation via database RLS
- Async job coordination via Kafka stubs (Celery-based workers in production)
- Rate limiting (1,000 requests per minute per tenant by default)
LLM Router — AI providers
The LLM Router dispatches AI analysis tasks — attribute extraction, root-cause narration, recommendation drafting — to the configured provider. Pattern detection itself is not one of them: clustering, criticality, and hazard fitting are deterministic math (the engine pipeline below), and LLMs are never allowed to produce a score, probability, date, or dollar figure. Every LLM-derived field is labeled with anorigin, and low-confidence extractions abstain into the review queue instead of guessing (see Abstention & the review queue). Providers are tried in order:
- Anthropic — Claude models (
ANTHROPIC_API_KEY) - OpenAI — GPT-4o / GPT-4o-mini (
OPENAI_API_KEY) - Mock — Deterministic offline fallback (no keys required)
If both
ANTHROPIC_API_KEY and OPENAI_API_KEY are absent, the API falls back to the mock provider automatically. Analysis results are plausible but not real.Database — PostgreSQL with Row-Level Security
All product data lives in PostgreSQL 16. Multi-tenancy is enforced at the database layer using Row-Level Security (RLS):- Every tenant table has a
workspace_idcolumn. - Before executing any query, the API binds
app.current_workspace_idon the session. - RLS policies reject reads and writes that do not match the bound workspace ID.
- The application role (
causeloop_app) does not haveBYPASSRLS; a superuser role is kept separate for migrations only.
Connectors and Ingestion
Connectors pull issues into a workspace from external tools. They operate in two modes:- Poll — the backend fetches issues from the external API on a configurable schedule (
poll_interval_seconds). - Webhook (inbound push) — the external tool POSTs events to
POST /ingest/{connector_token}, verified by HMAC signature.
GET /connectors/{id}/sync-runs.
The engine pipeline — deterministic, one run at a time
POST /engine/runs is the single entry point for turning ingested issues into patterns, predictions, and financials. There is no separate “model training” step — one run executes the whole pipeline, stage by stage, and every stage’s counters/durations land in the run’s manifest:
Every run produces a manifest (
snapshot_hash, event_log_cursor, configs{kind→hash}, models{embed, extract_llm, rca_llm}, seeds, code version) and a stability report (leiden_dsu_agreement, churn per pattern). POST /engine/runs/{id}/replay re-executes a prior run against the pinned manifest and returns {identical: bool, diff_summary} — the mechanism a CI gate uses to prove the pipeline is reproducible before every release. See Event log & provenance for how run outputs trace back to inputs.
WebSocket Gateway — Realtime channel
The backend exposes a WebSocket endpoint at/v1/stream. Authenticated clients connect with their platform JWT as a query parameter (?token=<jwt>). The gateway pushes typed event envelopes to all sockets belonging to the same tenant:
activity.created, issue.updated, pattern.updated, prediction.alert, recommendation.created, and job.updated (for async job progress).
Request and auth flow
1
User signs in via WorkOS AuthKit
The Next.js frontend delegates authentication to WorkOS AuthKit (
@workos-inc/authkit-nextjs) — email/password or Google OAuth, hosted at /sign-in and /callback. On success, WorkOS issues a signed RS256 access token to the browser session.2
Exchange for a platform token
The frontend calls
POST /v1/auth/exchange with the WorkOS access token as subject_token. The backend verifies the token against WorkOS’s JWKS endpoint (RS256), then runs admission: the identity must resolve to an existing membership, an accepted invitation, or (only when the workspace explicitly opts into domain JIT) a verified-domain match. Exchange never creates a user as a side effect otherwise — an unprovisioned identity gets a 403 and an auth.login.denied audit event. On admit, it issues a short-lived HS256 Bearer token bound to the resolved workspace.3
API calls with Bearer token
All subsequent requests include the Bearer token in the
Authorization header. Middleware resolves the token to {user_id, workspace_id, role, scopes} and binds the RLS session variable before any query runs.4
Realtime updates via WebSocket
The frontend upgrades to a WebSocket connection at
/v1/stream?token=<jwt>. The gateway verifies the token identically to the REST path, then pushes events for that tenant as they occur.Deployment topology
In production, each logical layer runs as an independent set of replicas behind a load balancer:
For local development,
docker-compose.yml runs the API, PostgreSQL, and Caddy (reverse proxy) as a single-box stack.
Quickstart
Sign in and see your first insights in 10 minutes.
Deploy & Security
Self-hosting, environment variables, and security controls.