Systems overview
Source: docs/architecture/overview.md at origin/platform (c2ce9cf), synced 2026-09-26. Do not edit this page here; change the source and run yarn sync:studio.
Bicycle Studio is the part of the Bicycle platform where a tenant builds and runs data apps, functions, agents and
workflows on top of the semantic layer. This page names every system involved, what it owns, and how it is deployed on
preview. Verified against Studio API a78212f, data-app-runtime b21a421, studio-snapshot-worker 2241304,
agent-service 95c607eb4 and the live preview-* deployments (read-only kubectl, 26 Sep 2026).
The picture
Systems and responsibilities
Studio API (bicycle-studio-api) — the control plane
Python/FastAPI, MCP mounted in-process. Namespace bicycle-studio, Deployment preview-bicycle-studio-api (2 replicas)
and bicycle-studio-api (prod, 3 replicas), Service port 8000. It owns:
- Auth: the principal resolver (session, platform token, OAuth), the support-login service identity, the OAuth
bridge for remote MCP clients, and the EdDSA grant signer with its JWKS (
/.well-known/bicycle-grants.json). - Visibility (#23): tenant, use case (semantic domain) and staff rules, applied by every list and get.
- Registry: data apps (versions, permissions, links, schedules), functions (drafts, versions, packages, evals, badges), workflows (records, numbered revisions, files, approvals, policies) and their disable/enable state.
- Drafts and publish: draft heads, the publish review (
diff_sha256, a person within 900 s for gated changes). - Runtime broker (
runtime_broker/): resolves aruntime_id, authorises, mintsrt-invoke/1, places on runtime-server, brokers every outbound capability call in viewer or service mode, enforces CU and concurrency quotas andfn.calldepth ≤ 3, keeps the invocation record, the day index, result reuse and the agent queue. - Workflows control: validate and plan through runtime-server,
RemoteRunnerstart/cancel on the workflow-runner, approvals and decisions, the events relay into invocations, failure routing, mail and action policies. - Internal routes for the snapshot worker (
/api/internal/snapshots/*,/api/snapshots/session) and the workflow-runner (/api/internal/workflows/*,/api/internal/runtime-session,/api/internal/runtime-caps/*). - MCP (
/mcpand/mcp/<toolset>): dataapp, query, design, analysis, workflow, agent, functions, function, notebook, chat; per-tenantfn_<name>/wf_<slug>tools; SSE progress on/mcp/functions.
It also still carries the deprecated in-process workflow runner and its vendored spec copies (#48 removes them; see storage.md "changing" notes and operations.md (in the bicycle-studio-api repository, not published)).
UI (ui-bicycle-studio)
React SPA, namespace ui, Deployments preview-ui-bicycle-studio / ui-bicycle-studio, served under
/{tenant}/apps/... on the product host. It talks only to the Studio API (same origin, session cookie); the data-app
frame reaches the API through the host page's bridge (SandboxFrame / fnBridge), never with a credential of its own.
Evals are hidden (SHOW_EVALS=false).
data-app-runtime (one repo, two services, three packages)
Namespace bicycle-studio. CI on platform deploys the preview-* releases, main the prod ones.
| Piece | What it is |
|---|---|
runtime-server (preview-bicycle-runtime-server, 1 replica, ROLE=api,exec, port 8080, SA bicycle-runtime-server) | api role: GET /v1/registry (kinds, schema, prices, runnable), `POST /v1/workflows:validate |
bicycle-exec sandbox pods (namespace fn-pool, node pool wf-sandbox-gvisor-spot-v1, runtimeClassName: gvisor) | One fresh pod per code invocation attempt (PodPool, mode pod; there is no warm pool yet). Image bda-python by digest; classes fn-xs (0.5 vCPU / 512 MiB), fn-small (1 / 2 GiB), fn-medium (2 / 4 GiB); package and SDK arrive in a per-invocation ConfigMap; dnsPolicy: None, no service-account token, PSA restricted; the only reachable endpoint is runtime-server:8080 (FN1_GATEWAY with a per-invocation FN1_TOKEN) |
workflow-runner (preview-bicycle-workflow-runner-all, 1 replica, ROLE=all; prod values define -api × 2 and -worker × 2) | runner-api on port 8080 (`/v1/tenants/{t}/workflows/{wf}/revisions |
| bicycle-workflow-core | The one workflow spec: workflow.schema.json, kinds.json, validate, plan, fingerprint, lock, sends rules (contract 1.6.0). Runnable in v1: query, sql, llm, action, function, classify, snapshot. Draft/cut: detect, explain (no executor in v1), python, input, source |
| bicycle-exec | Contracts (RUNTIME_CONTRACT 1.6.0, protocol fn/1), grant verification, capabilities (cache, blob, llm.call, query, fn.call, agent), the stores factory (memory / redis / postgres-stub), sandbox pods, lifecycle, metering, schemas |
| bicycle-fn-sdk | The in-sandbox ctx API a function uses: query, store, cache, blob, agent, llm, call |
agent-service
Namespace bicycle-ai, StatefulSet preview-agent-service (1 replica, 4 CPU / 16 GiB) and agent-service (prod), port
8000. Studio-facing responsibilities (the service also hosts older product flows and the pattern-engine, data-story,
navigator-theme and d2c-onboarding Temporal workers, which are not Studio's):
- Agent runs
/agents/*: named agents and custom agents (inline specs), the Claude Agent SDK harness behindAgentHarness/HarnessFactory, tenant and global in-flight slots, daily caps,agent-env/1limit verification, nested runs (parent_run_id, depth ≤ 3), trace and events per run. - LLM completions
POST /api/v1/llm/completions: Anthropic on Vertex; catalogclaude-sonnet-5(default),claude-sonnet-4-6,claude-haiku-4-5,claude-opus-5; per-tenant allowlist; sampling and thinking mapped per model capability. - Data-app chat
/data-app/chat/*: the app's own chat with the eightquery_*MCP tools from Studio's/mcp/query, plus the app's functions and workflows from/mcp/chatwhen the app inherits them. - Analysis
/analysis/detect(explain is a mode of detect), synchronous with an in-process pool and per-mode caps. - Usage ledger: Postgres database
agent-service, tableconsumption_event, read byGET /api/v1/usage.
The rest of the platform, as Studio uses it
| System | Where | Role for Studio |
|---|---|---|
| tool-service | bicycle-ai, tool-service:8000 | The tenant's connections, tools and actions. Agents read through it (as the viewer); workflow send steps with channel: action execute actions:execute_action through it (as the tenant service identity); GET /workflow-actions lists actions:list_actions |
| Jev (typesafe.ai) | external SaaS | The classify function kind and workflow node (runtime-server, POST /v1/systemone, model jev-1.13.0) and the runner's failure routing (failures:route, closed set of role names, confidence ≥ 0.7). Key: Secret bicycle-ai / jevApiKey, by reference only |
| studio-snapshot-worker | bicycle-studio, preview-studio-snapshot-worker (KEDA 1–6 on queue depth) | Temporal worker on task queue data-app-snapshot-preview (data-app-snapshot in prod) for Schedule Mode: Playwright capture per tab, PDF merge, upload, delivery through Studio; the same one-shot renders workflow snapshot nodes; it also runs the data-app-workflow-tick workflow type that turns a cron fire into POST /api/internal/workflows/{wf}/ticks. Holds no credential: it trades the run grant for a support session |
| scheduling-service | plat-svc:8080 | REST front to Temporal for crons and one-shot starts (POST /api/v1/schedules, POST /api/v1/workflows:start). Studio has no Temporal client of its own |
| Temporal | plat-svc, <temporal host>, server 1.30.2, namespace default | Task queues: data-app-snapshot[-preview] (snapshot worker), wf-control[-preview], wf-io[-preview] (workflow-runner), agent-service's own worker queues |
| blob-store | plat-svc:80 | Every document and byte Studio keeps (metadata namespace bicycle-studio-meta, data namespace data-apps, runtime namespace rt); tenant prefix derived from the credential |
| plat-svc-redis | plat-svc:6379, Redis 7.4.8, one replica, allkeys-lru, no auth | Studio's live state (invocation routing, leases, counters, queues, agent runs, analysis jobs, app cache, send claims). Testing-grade durability (#18) |
| Cloud SQL Postgres | private IP, one instance | Databases workflow-runner (the runner) and agent-service (usage ledger). Studio itself has no Postgres yet (BSA_METADATA_STORE=blob) |
| token-service / tenant-service | auth-platform (token-service, token-service-preview) | Session validation, platform-token validation, OAuth introspection, support-login for the tenant service identity, run-as credentials |
| config-store | entity-platform | Mirror of tenant action configs that tool-service reads |
| biz-entity-service | plat-svc:80 (biz-entity-service-preview for preview) | Bicycle query (BSA_SEMANTIC_MODE=bizentity, the default backend), use cases / domains, drivers and actions records |
| semantic-layer | traces-platform:8000 | The semantic-SQL backend a tenant is routed to when named in the routing ConfigMap or its own profile (GET /domains, /describe, POST /query) |
| alert-service | plat-svc | Every email Studio sends (POST /api/alert/email); returns no Message-ID |
What is preview-only today
Everything below the data-app lifecycle is switched on only in preview values: functions, workflows, agent runs,
analysis, evals, lookups, notebooks, the remote runner and runtime engine, send claims. Prod runs the Studio API and the
snapshot worker only: no bicycle-runtime-server or bicycle-workflow-runner-* deployment exists in the cluster yet
(the charts and CI jobs do), the prod Studio values do not point at them, and prod has no grant key. See
operations.md (in the bicycle-studio-api repository, not published).