Skip to main content

Systems overview

Synced from bicycle-studio-api

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 a runtime_id, authorises, mints rt-invoke/1, places on runtime-server, brokers every outbound capability call in viewer or service mode, enforces CU and concurrency quotas and fn.call depth ≤ 3, keeps the invocation record, the day index, result reuse and the agent queue.
  • Workflows control: validate and plan through runtime-server, RemoteRunner start/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 (/mcp and /mcp/<toolset>): dataapp, query, design, analysis, workflow, agent, functions, function, notebook, chat; per-tenant fn_<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.

PieceWhat 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-coreThe 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-execContracts (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-sdkThe 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 behind AgentHarness/HarnessFactory, tenant and global in-flight slots, daily caps, agent-env/1 limit verification, nested runs (parent_run_id, depth ≤ 3), trace and events per run.
  • LLM completions POST /api/v1/llm/completions: Anthropic on Vertex; catalog claude-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 eight query_* MCP tools from Studio's /mcp/query, plus the app's functions and workflows from /mcp/chat when 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, table consumption_event, read by GET /api/v1/usage.

The rest of the platform, as Studio uses it​

SystemWhereRole for Studio
tool-servicebicycle-ai, tool-service:8000The 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 SaaSThe 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-workerbicycle-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-serviceplat-svc:8080REST 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
Temporalplat-svc, <temporal host>, server 1.30.2, namespace defaultTask queues: data-app-snapshot[-preview] (snapshot worker), wf-control[-preview], wf-io[-preview] (workflow-runner), agent-service's own worker queues
blob-storeplat-svc:80Every 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-redisplat-svc:6379, Redis 7.4.8, one replica, allkeys-lru, no authStudio's live state (invocation routing, leases, counters, queues, agent runs, analysis jobs, app cache, send claims). Testing-grade durability (#18)
Cloud SQL Postgresprivate IP, one instanceDatabases workflow-runner (the runner) and agent-service (usage ledger). Studio itself has no Postgres yet (BSA_METADATA_STORE=blob)
token-service / tenant-serviceauth-platform (token-service, token-service-preview)Session validation, platform-token validation, OAuth introspection, support-login for the tenant service identity, run-as credentials
config-storeentity-platformMirror of tenant action configs that tool-service reads
biz-entity-serviceplat-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-layertraces-platform:8000The semantic-SQL backend a tenant is routed to when named in the routing ConfigMap or its own profile (GET /domains, /describe, POST /query)
alert-serviceplat-svcEvery 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).