Self-hosting Architecture
Understand which component owns data, traffic, and failure before choosing stores and reverse-proxy rules. Provon keeps the same product responsibilities in both runtimes; only the process-to-platform mapping differs.
Logical Planes#
| Plane | Owns | Node location | Cloudflare location |
|---|---|---|---|
| Workbench | User-facing configuration, trace investigation, Findings, operations | Built Workbench served by the Node process | Pages project (provon-demo-app) |
| Control API | Identity, organizations, projects, API keys, provider settings, Rules, connectors | Same Node process | API Worker (provon-demo-api) |
| Request path | Gateway routing, policy, guardrails, provider calls; OTLP acceptance and staging | Same Node process | Gateway Worker, OTLP Worker, MCP Worker |
| Background execution | Ingest decoding, summaries, diagnostics, retention, work-item status sync | In-process periodic loops and SQLite-backed queues | Pipeline, diagnostics, and maintenance Workers on Queues and a cron trigger |
| Storage | Metadata, usage events, telemetry, blobs, queue and runtime state | Local libSQL/SQLite files, DuckDB file, filesystem blobs | D1, R2, R2 Data Catalog, KV, Queues, Durable Objects |
The planes are ownership boundaries, not mandatory processes. Node co-locates them in one process; Cloudflare maps them to separate deployments and managed services.
Runtime Mapping#
Node#
The Node entrypoint (services/node-server) composes the HTTP application, applies metadata
and usage-event migrations before it listens, serves the built Workbench, and starts local
background loops. With default adapters it is a single-instance design:
- metadata, usage events, job queue, and Gateway state are separate libSQL/SQLite files;
- telemetry is a local DuckDB file;
- blobs and queue state live under the configured data directory.
Remote metadata alone does not make the runtime horizontally scalable: multiple active processes would still run independent local queues and schedulers. Move to the Cloudflare runtime for shared queues and independently scaled telemetry storage.
Cloudflare#
| Deployment | Role | Key backing resources |
|---|---|---|
| Pages Workbench | SvelteKit UI; calls the API origin | Static Pages output |
| API Worker | Sessions, control API, telemetry reads; service bindings to Gateway, MCP, warehouse | Meta D1, metering D1, blob R2, KV-less |
| Gateway Worker | Model traffic, routing, policy, guardrails, usage state | Meta D1, Gateway cache KV, blob R2, gateway-telemetry queue, two Durable Objects |
| MCP Worker | Streamable-HTTP MCP ingress and hosted tool state | Meta D1, runtime-state Durable Object |
| OTLP Worker | Auth, validation, staging, queueing | Meta D1, blob R2, ingest and summary queues |
| Telemetry Pipeline Worker | Queue consumers for ingest, gateway telemetry, and summaries | Meta + metering D1, blob R2, three queues, ingest DLQ |
| Diagnostics Worker | Queued diagnosis execution and coordinator state | Meta + metering D1, blob R2, diagnostics queue, coordinator Durable Object |
| Maintenance Worker | Cron housekeeping: retention, metadata maintenance, work-item sync | Meta + metering D1, blob R2, cron trigger |
| Warehouse Worker + Container | Deterministic shard writes, Iceberg append and retention | Catalog coordinates, bounded Container pool, executor Durable Object |
Workbench availability never sits on the Gateway request path. Accepted OTLP payloads queue before telemetry projection completes; a successful ingest response is acceptance, not visibility. Internal Worker-to-Worker traffic uses Service Bindings rather than public routes.
Request And Data Flow#
Gateway Request#
Prompts and responses leave the deployment when the selected provider is external. Hosting the deployment privately does not make an external model provider private.
OTLP Ingest#
Acceptance and visibility are asynchronous. A 2xx ingest response proves the payload was accepted and queued for the configured runtime; trace visibility is a separate verification gate.
Control And Connector Flow#
Browser sessions call the Control API. Provider credentials, connector secrets, and OAuth
tokens are encrypted with key material derived from PROVON_AUTH_SECRET before storage. Connector
actions and OAuth handshakes make outbound requests to the configured external systems.
Storage Boundaries#
| Data class | Contents | Node | Cloudflare |
|---|---|---|---|
| Metadata | Users, projects, keys, Rules, Findings, settings, encrypted secrets | libSQL/SQLite file | Meta D1 |
| Usage events | Idempotent, append-only usage facts | Separate libSQL/SQLite file | Separate metering D1 |
| Telemetry | Spans, logs, metrics, summaries, diagnostic evidence | DuckDB file | Data Catalog / Iceberg; reads through R2 SQL |
| Blobs | OTLP staging, attachments, staged Gateway payloads | Local filesystem directory | Blob R2 bucket |
| Runtime state | Queued jobs, summary work, Gateway usage and target state, leases | Persistent files and SQLite queues | Queues, KV, Durable Objects |
Back up metadata, usage events, and telemetry independently at a coordinated recovery point. Metadata holds the tenant model and encrypted credentials; usage events record what was called; telemetry holds the evidence those records reference.
Network Boundary#
Required inbound paths depend on enabled features:
| Caller | Endpoint |
|---|---|
| Browser | Workbench and authentication callbacks (/auth/*) |
| Agent application | /gateway/v1/* |
| OTel exporter | /v1/traces, /v1/logs, /v1/metrics |
| MCP client | MCP tool endpoint (/mcp/v1 on Node; dedicated MCP origin on Cloudflare) |
| API client | /v1/* routes allowed by its project key |
On Node these paths share one public origin behind a reverse proxy. On Cloudflare they are split across the Pages, API, Gateway, MCP, and OTLP origins; pipeline, diagnostics, maintenance, and warehouse Workers are reached only through Service Bindings, queues, or triggers.
Outbound paths may include configured model providers, sign-in identity providers, Connector OAuth and API endpoints, package registries, and Cloudflare APIs during build or deployment. Document each enabled egress dependency; a private network does not by itself produce an air-gapped deployment.
Failure Boundaries#
| Failure | Expected effect |
|---|---|
| Workbench unavailable | API, Gateway, and OTLP may continue if their runtime processes are healthy |
| Model provider unavailable | Gateway request fails or follows the configured retry or fallback policy |
| Telemetry writer delayed | Accepted evidence remains queued; trace visibility lags |
| Metadata unavailable | Sign-in, project lookup, policy, and most control operations fail |
| Background execution stopped | Ingest projection, summaries, diagnostics, retention, and status sync lag |
| Blob storage unavailable | New staged ingest and attachment operations fail |
/healthz reports runtime liveness and version; it is not a dependency or end-to-end
evidence check. Design health checks and alerts around the boundaries above; the Warehouse
Worker exposes /health instead.