> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getsevvo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> How sevvo's control plane and data plane fit together.

sevvo is a **hybrid SaaS**. The control plane lives in our environment and
handles orchestration; the data plane lives in yours and handles data. The
boundary between them is a real boundary — see [Security](/security) for the
guarantees it enforces.

## The two planes

```
Our env                           Your network / VPN
┌──────────────────────┐       ┌──────────────────┐       ┌──────────────────────────┐
│ Hosted control plane │─SPA──▶│ User's browser   │─HTTPS▶│ Agent (NestJS container) │
│ sevvo API            │       │ sevvo console    │       │   Data-plane HTTP API    │
│ Temporal + auth      │◀────────────────────────────────│   Temporal worker        │
└──────────────────────┘       └──────────────────┘  443  │   Local Postgres         │
                                                     out  │   DuckDB workspace       │
                                                          │ Extract → Transform →    │
                                                          │ Write to your sink       │
                                                          └──────────────────────────┘
```

**Control plane.** Pipeline definitions, schedules, run metadata, schema
fingerprints, connection metadata, and the UI. Runs in our environment, backed
by sevvo's control-plane services and a self-hosted Temporal cluster. Uses
sevvo's authentication service for user and agent auth, and Autumn for
billing. It serves the console application but does not proxy requests to the
customer's data-plane URL.

**Browser path.** A user loads the hosted console, supplies the HTTPS URL for
their agent deployment, and connects to it directly from their browser. The URL
may be reachable only from the customer's VPN. Interactive previews, connection
tests, AI chat streams, and artifact downloads use this path and do not pass
through the control plane.

**Data plane.** One agent container per customer, running inside the
customer's network. Handles extraction, canonical transformation, storage,
runtime credential use, and the browser-facing data-plane API. Connects
outbound to the control plane over TLS on port 443.

The agent is a **Temporal worker first**. It connects outbound to our
Temporal frontend and polls a tenant-scoped task queue (`tenant-{orgId}`).
The sevvo control plane never opens an inbound connection to the agent.
Interactive console features are different: the user's browser must be able to
reach the customer-supplied data-plane URL over the customer's network.

## How an interactive console request runs

1. The user signs in to the hosted sevvo console and selects an agent
   deployment with a customer-supplied data-plane URL.
2. The browser resolves that URL over the customer's network or VPN and calls
   the agent directly.
3. The agent reaches the source from inside the customer environment and
   applies the operation's query, row, timeout, and sanitization limits.
4. The agent returns the result directly to the browser. Convex, Temporal, and
   sevvo's other control-plane services do not receive the result payload.

## How a pipeline runs

1. You define a connection, a canonical model, and a destination in the UI.
   The UI writes to the sevvo control plane; it stores **only identifiers and
   non-secret metadata**.
2. On a schedule (or when you click **Run**), a control-plane action starts a
   Temporal workflow with opaque inputs: `orgId`, `connectionId`, `modelId`,
   `destinationId`.
3. Your agent's Temporal worker picks up the workflow from
   `tenant-{orgId}`. The agent **resolves identifiers to runtime config
   locally** — credentials, DSNs, and OAuth tokens never travel in workflow
   inputs.
4. Workflow activities run inside the agent: Retrieve → Refresh → Build
   model → Resolve associations → Load. Each activity returns **metadata
   only** — row counts, byte counts, durations, schema fingerprints — never
   source rows.
5. The agent writes canonical output to your own sink (S3, warehouse,
   Postgres). The control plane receives a summary metadata payload and
   surfaces it in the UI.

## Agent-local state

The agent uses local Postgres through Prisma for data-plane state that should
not live in the control plane. AI analyst threads, messages, sandbox state,
and artifacts are stored there so prompts, responses, tool output, and
generated files remain inside the data plane.

Connector credentials follow the current connector-specific runtime path:
native database credentials are resolved by the agent from the control plane
when needed, and OAuth-backed connectors resolve provider tokens through
Pipedream. User-facing control-plane connection queries still return only
metadata such as `name`, `type`, `status`, and `safeMetadata`.

### Connector and auth are separate concerns

v1 only ships a Postgres + username/password connector, but the internal
model keeps the **connector** and its **auth strategy** independent. A single
connector (Postgres, Snowflake, Salesforce) can eventually support multiple
auth strategies (`username_password`, `keypair`, `oauth2_auth_code`,
`oauth2_client_credentials`) without becoming a special case. Credentials
carry a **lifecycle** — `static`, `refreshable`, `exchangeable`, or `ambient`
— and refreshable lifecycles will be handled by shared auth drivers in the
data plane rather than duplicated across connectors.

This split exists to keep future connectors small. If you're evaluating
sevvo against systems that mix auth into each provider driver, this is the
core structural difference.

## The data boundary

Every Temporal activity return type is a metadata shape — row counts, byte
counts, durations, schema fingerprints, opaque references into customer
storage. **Activities never return raw rows, secrets, tokens, or PII** to
the control plane. Whatever an activity returns ends up in Temporal history
and is visible to the control plane, so the return type *is* the contract.

The same rule applies to orchestration inputs. Control-plane workflow inputs
contain identifiers (`orgId`, `connectionId`, `sourceConfigId`, `modelId`,
`destinationId`) — not DSNs, OAuth tokens, passwords, refresh tokens, or
embedded cursor payloads.

**Browser-direct previews.** A user-triggered preview may return bounded rows
from the agent directly to the user's browser so the UI can render a table.
Those rows do not travel through Temporal or the control plane. The agent
allows SELECT/WITH only, rejects multiple statements, caps rows at 100,
truncates cell text, and sanitizes values. See
[Security → Browser-direct preview queries](/security#browser-direct-preview-queries)
for the full constraints. Sync paths remain metadata-only.

## Versioning and upgrades

Customer agents lag behind control-plane deploys, so the workflow interface
is treated as a **versioned public API**:

* **Inputs are append-only.** New fields on a workflow input are optional.
  Renames, retypes, and deletes require a new workflow type.
* **Workflow logic changes use Temporal's `patched()`** so in-flight
  histories replay safely after an agent upgrade.
* **New capabilities = new workflow types**, not mutations of existing ones.

Control-plane upgrades do not push code to your agent. The agent runs the
workflow definitions baked into its image; to pick up a new capability, you
pull a new image tag. In-flight executions replay safely across versions.

## Tenancy boundaries

* **Deployment boundary.** One agent per tenant, running in the tenant's own
  environment.
* **Task-queue boundary.** Workflows are routed by a tenant-scoped Temporal
  task queue; workers only pick up work for their own tenant.
* **Auth boundary.** Every user-facing control-plane operation is scoped by a
  signed session that resolves `{ orgId, userId }`.
* **Network boundary.** The customer controls which user networks can reach
  the data-plane URL. A private VPN hostname does not need to resolve from
  sevvo's network.
* **Agent auth.** Each agent deployment has its own sevvo deployment token;
  revoking it cuts off future bearer exchanges instantly.

## Where the code lives

| Layer                            | Path                                         |
| -------------------------------- | -------------------------------------------- |
| Web UI (TanStack Start + Vite)   | `apps/web/`                                  |
| Control-plane backend            | `packages/db/convex/`                        |
| Agent (NestJS + Temporal worker) | `apps/agent/`                                |
| Workflow definitions             | `apps/agent/src/workflows/`                  |
| Connector implementations        | `apps/agent/src/connectors/implementations/` |
| Canonical sync activities        | `apps/agent/src/canonical-sync/`             |
| Shared UI components (shadcn)    | `packages/ui/`                               |

Workflow definitions live in the agent repo, not a shared package. Only the
customer-hosted agent connects to Temporal; Convex/control-plane code must not
import `@temporalio/client` or start workflows directly.
