Skip to main content
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 for the guarantees it enforces.

The two planes

Our env (control plane)              Your env (data plane)
┌────────────────────────┐           ┌──────────────────────────┐
│ Web UI (SPA)           │           │ Agent (NestJS container) │
│ sevvo API              │◀──────────│   Temporal worker        │
│ Self-hosted Temporal   │  outbound │   Local Postgres         │
│ sevvo auth service     │  443      │     (AI chat state)      │
│ Autumn (billing)       │           │   DuckDB workspace       │
└────────────────────────┘           │   Embedded HTTP /healthz │
                                     │                          │
                                     │ 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. Data plane. One agent container per customer, running inside the customer’s network. Handles extraction, canonical transformation, storage, and runtime credential use. 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}). You do not need to open inbound control-plane APIs — everything that drives the agent arrives over its outbound Temporal connection.

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 lifecyclestatic, 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. One exception. previewQueryWorkflow returns actual rows to the control plane so the UI can render a tabular preview. It is bounded: SELECT/WITH only, a single statement, a 100-row cap, per-cell text truncation, and value sanitization. See Security → The preview-query exception for the full constraints. Sync paths do not reuse this transport.

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 }.
  • Agent auth. Each agent deployment has its own sevvo deployment token; revoking it cuts off future bearer exchanges instantly.

Where the code lives

LayerPath
Web UI (TanStack Start + Vite)apps/web/
Control-plane backendpackages/db/convex/
Agent (NestJS + Temporal worker)apps/agent/
Workflow definitionsapps/agent/src/workflows/
Connector implementationsapps/agent/src/connectors/implementations/
Canonical sync activitiesapps/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.