KDCube Runtimes: A Request's Journey Across the Fences
A production AI app is not one runtime. A single request can touch a webhook handler, scheduler, agent turn, app venv, networkless executor, provider app, and remote task. Each crossing carries only what its target should receive.
KDCube serves many users, many apps, and many kinds of work on shared infrastructure, where a later turn may run on another worker or another machine. That operating condition forces a specific shape: a set of genuinely different runtimes — different processes, different lifetimes, different trust — composed into one system by narrow, typed crossings.
This article walks every runtime: what it is for, what runs there, what crosses into it, and what it guarantees. A companion article walks the same system from inside the agent. Throughout, ⚙ marks the main lever — the exact symbol to search for in the SDK to find the crossing being described.
One terminology note: current source and descriptors still use bundle in literal names such as bundle_id and bundles.yaml. In builder-facing prose, that deployable unit is an application or app.
01 One primitive, repeated: the fence
KDCube repeats one design move at runtime boundaries: make the crossing explicit. A fence declares what may cross, what the target reconstructs, and which condition is enforced there. Unrelated process state does not ride along implicitly.
Different fences enforce different properties. The split execution fence removes network and credentials from generated code. The credential fence keeps provider tokens on the trusted side and checks claims per operation. An economic fence reserves allowance before integrated spend. A runtime-context crossing carries an already-bound identity projection; it does not repeat the original login on every hop. Structural isolation belongs specifically to the split executor, not to every boundary called a fence.
| Fence | Source side | What crosses | Target-side enforcement |
|---|---|---|---|
| Execution | generated code in the split executor | tool_call(id, params) over an authenticated socket | trusted supervisor applies the exported catalog and policy; executor has no network or credentials |
| Economic | an integrated paid path that wants to run | an admission reservation, then metered usage | allowance check before work and settlement after observed usage |
| Credential | agent or client without a provider token | bound identity plus requested claim | Connection Hub verifies grants/consent and resolves the token on the trusted side |
| Data scope | trusted app code acting for a bound request | tenant/project/app/user owner keys | storage helpers apply scope; trusted code must preserve those keys |
| Runtime context | a supported trusted-runtime hop | JSON-safe identity, routing, authority projection, accounting subject | target validates the envelope and rebuilds local services |
| Subagent | coordinating parent | serializable child spec in, bounded reduction out | child inventory and authority ceiling are set at spawn |
Most fences are boundaries in space — two processes, two containers, two machines. The economic fence is the same move in time: the decision is placed one step ahead of the power, so a turn that would exceed its funding never starts.
The precision matters, so the claims stay attached to their boundaries: the deployment layer isolates by namespace, the shared runtime enforces identity and grants, and the structural isolation claim belongs to the generated-code boundary alone.
02 The map at a glance: what crosses the runtimes
The rest of this article walks the boxes.
03 The serving runtime: direct surfaces and scheduled work
The processor (proc) hosts the apps and serves two different kinds of entry.
Direct surfaces. An app's declared @api operations (authenticated and public — webhooks enter here), its @mcp doors, and its widgets are served by the integrations router straight into the app: the router binds the request to the normalized event payload — routing, actor, user, authority — and calls the app surface in place, request/response. No lane, no queue, no scheduler. ⚙ call_bundle_op_public, ⚙ _dispatch_bundle_mcp_request.
public means the platform route does not require an authenticated platform session. It does not establish the caller's identity or authorization policy. The app or integration may still carry or resolve an actor and enforce its own requirements.
Scheduled work. Conversational and queued work goes through ordering and scheduling machinery:
| Work kind | Entry | Ordering and exclusivity |
|---|---|---|
| Chat turns | @on_reactive_event → the shared run() entry | the conversation event lane reserves one accepted start batch for one turn; that batch may include same-ingress siblings; same-conversation turns serialize across workers |
| Automations | saved automation records; due-scanner + run-now | scheduled and manual runs converge on one execution path; each execution is its own agent turn |
| Scheduled jobs | @cron(...), auto-discovered | Redis leases coordinate an active owner by declared span — system, instance, or process; jobs still need idempotent or resource-enforced side effects |
| Background jobs | @on_job | claimed fairly off a Redis Stream across processors, deduplicated by key |
The split of responsibilities is deliberate. A public webhook answers fast and enqueues; @cron decides when scheduled work is due; @on_job handles ready work that has been enqueued — so a burst of webhook-triggered work spreads fairly across the fleet instead of hammering one worker, and long-running or per-user work never executes inside a scheduler tick.
One Telegram message exercises both paths end to end:
Delivery guarantee, not transactional promise. The lane guarantees at-least-once delivery: if a worker dies mid-turn, the reservation expires and the event is redelivered elsewhere. An operation that already produced an external side effect before the crash needs an idempotency strategy — the platform pattern is a response record per message id, answered on redelivery instead of re-executing. Exactly-once on external effects is the app's job; the runtime supplies ordered delivery and per-message identity to build on.
04 The Data Bus: the durable message runtime
The Data Bus carries app-scoped JSON messages on Redis Streams — durable across process death and processed by handlers the processor owns. Widgets and frontends publish over Socket.IO or HTTP; tools, services, and other apps publish server-side. The app's @data_bus_handler(...) methods claim messages from the app-scoped stream; serial_per_partition prevents concurrent handlers for one partition while allowing parallelism across partitions. It does not by itself prove strict FIFO under retries or lease loss. Handlers still need durable idempotency and, for mutable state, revision or conditional-write checks.
Its role in the system: the durable app-to-app path — including the leg the named-services relay rides when generated code in an isolated runtime calls a provider app, with the original request identity and consent checks preserved. Two boundaries it does not blur: the Data Bus is not the conversation timeline (conversation events land there only when an explicit bridge writes them), and it is not the live stream to clients — that is the communicator's job.
05 The communication runtime: events to the initiator, from anywhere
The work that needs to speak does not stay in one runtime. A turn's events must reach the person who asked whether they were emitted in the processor, the ISO supervisor, a subagent fence, or a remote supervisor. On communicator-enabled paths, the comm spec crosses with the work (portable, JSON-safe), and the far runtime rebuilds a live communicator from it — ⚙ ChatCommunicator. The live object never crosses; the spec does.
Live and durable converge selectively: recordable events are exported from the communicator — ⚙ comm.export_recorded_events() — and persisted with the turn. Reload hydrates the client from those selected records and durable turn blocks; it is not a byte-for-byte replay of the live stream. One runtime has no communicator at all, by design: the isolated executor. Generated code has no channel of its own; progress surfaces through the supervisor side.
06 The app venv runtime: your dependencies, not the platform's
Each app can run selected helpers in its own cached virtual environment — a real runtime with its own interpreter and its own installed dependencies, behind a subprocess boundary. ⚙ @venv(requirements="requirements.txt").
proc (shared interpreter, shared event loop) | serializable call data | (including context values explicitly passed by the caller) v @venv helper in the app's cached venv subprocess | own interpreter, own deps from the app's requirements.txt | blocking library calls cannot stall the shared event loop v plain serializable result <- the ONLY thing that returns
One cached venv per app; it rebuilds lazily when the referenced requirements.txt content changes — no restart. The decorated callable is the boundary: serializable data in and out. Context needed by the helper must be represented in that call data; the live request-context object does not cross. Communicators, pools, and tool registries also remain in proc, where orchestration stays. The app carries a heavy dependency stack without ever touching the platform's interpreter.
07 The isolated execution runtimes: the fence made physical
Model-generated code gets its own runtime family — the most intricate in the platform, because it is itself composed of runtimes: a trusted supervisor, a restricted executor, and the transport that binds them on one host or across machines. Entry: ⚙ exec_tools.execute_code_python / codegen_tools.codegen_python.
| Profile | What it is | What it guarantees |
|---|---|---|
| in-memory | safe tools in the current process | no isolation; for tools that need none |
| local subprocess | a child process on the same host | crash containment only |
| Docker combined (legacy) | supervisor + UID-dropped executor child in one container | filtered env, no network namespace for the child; one shared mount namespace |
| Docker split (reference) | two sibling containers | executor: no network, read-only root filesystem, narrow work/artifact/log/socket mounts, no secrets, no descriptors; every tool call crosses an authenticated per-execution socket |
| Fargate / external | supervisor and generated-code child in one remote task/container | the same logical supervisor/tool-call contract with snapshot transport, but not the separate-container mount boundary of local Docker split; task IAM, network, filesystem, and child isolation remain deployment concerns |
Seen from the supervisor, an execution is a brokerage: it holds the exported tool catalog for exactly this execution, authenticates every socket request, applies policy, performs the privileged call with real credentials, and returns only the result. Seen from inside the executor, the world is two directories and a socket: code computes freely, and everything privileged is a request across the fence. A named-service call from generated code continues from the supervisor over the Data Bus relay to the provider app — identity intact the whole way. Distributed execution changes how code is transported and run; it does not change the logical result contract the agent sees.
08 Cross-runtime context: reconstruction, not serialization
What crosses a supported trusted-runtime hop is a small JSON-safe portable context: request identity (tenant, project, app, actor, user, roles), routing (session, conversation, turn), authority carried already resolved — downstream code reads the projection, it never re-derives who the actor is — the accounting subject, and the named-service discovery descriptor. Live database pools, Redis clients, provider objects, and service clients do not cross as serialized objects; trusted targets rebuild them from validated configuration. The split executor is narrower: it receives EXEC_CONTEXT identifiers such as tenant/project/user, conversation/turn, app, and execution ids, but not PORTABLE_SPEC_JSON, descriptor payloads, platform or provider credentials, secret-provider material, or live services. The trusted supervisor retains that privileged context. Identity is never model-selectable: a model or tool argument can name an object, but it cannot change the tenant, user, authority, or economics subject.
The carried identity is what unlocks accounts
The execution context is not only who is asking — on a supported trusted runtime it is the key that lets guarded work reach the user's connected accounts and this agent's delegated grants at the moment of use. The context binds the resolved platform/grantor user and the acting agent's own identity (kdcube-agent:<app>:<agent> for a hosted agent; a dcr-… client identity for an external app), and a guarded operation resolves authority from both:
The resolution is the broker's job: it selects the account that holds the claim — fanning out across the user's bound accounts or honoring an explicit account id — refreshes the stored credential at resolution time, and scopes what the tool receives to the claim being exercised. A resolution that cannot succeed comes back structured, never as a bare failure: a reason (connect_required, claim_upgrade_required, reconnect_required, account_required, agent_grant_required), labeled candidates when several accounts match, and a retry hint — the exact fix, addressed to the user and readable by the agent. When a guarded call runs on a supported trusted runtime, the bound identity projection reaches that enforcement point with the work. Because identity is never model-selectable, no prompt can point the resolution at another user's accounts.
09 Named services and MCP: crossings between apps
A provider app is an independently governed app boundary — it may be co-hosted in the same process or reached on another worker or machine. Reaching it uses an explicit bridge and the provider enforces its own contract:
MCP itself runs in both directions, and each is a crossing: as provider, an app's @mcp doors are served on the direct-dispatch path, and an external client enters through the visibility, auth, guard, resource, and grant policy declared for that surface; as consumer, remote MCP servers appear as tools only in the selected agent inventory. MCP is one transport among the bridges, not the identity of the runtime.
10 Accounting across the fences
Spend control is a runtime concern because integrated paid calls can happen in different runtimes — and their attribution must survive the supported crossings on those paths.
The guarantee is scoped honestly: accounting follows integrated paths — model, embedding, web-search, and participating custom calls. A trusted app calling an arbitrary provider library directly is outside the meter; the runtime enforces the economic events it can observe.
11 Cluster critical sections: when work must not parallelize
Distribution means many replicas doing the same thing at the same time: several processors load the same app and would bootstrap the same schema; every worker sees the same due cron tick; several replicas would materialize the same shared storage. Some work must not overlap. Three substrates guard it, chosen by the resource being protected:
| Substrate | Guards | Mechanism |
|---|---|---|
| Postgres advisory transaction locks | schema bootstrap and migrations in on_bundle_load | ⚙ pg_advisory_xact_lock on a stable key; releases automatically on commit, rollback, or connection failure |
| Redis locks | cluster-wide coordination before shared work | @cron leader election by span; the observed Redis lock whose key uses a shared segment with the owner identity in the value — replicas contend on one lock instead of each taking its own |
| Observed file locks | the shared-filesystem mutation itself | ⚙ observed_file_lock — fcntl.flock plus an in-process lock, with owner metadata written inside the lock file, readable during an incident |
Redis coordinates before filesystem work starts; the file lock protects the mutation itself; both are advisory. A Redis TTL lock is a renewable lease, not an exactly-once proof: a stalled owner can continue after expiry. Durable mutations therefore still need idempotency, a transaction, a revision check, or another resource-enforced guard. The canonical guarded-build pattern: fast-path on a signature, acquire, re-check under the lock, build into a safe location, verify readiness, write the signature last.
12 Distribution is the forcing function
Every design above answers one operating condition: a later turn may run on another worker or another machine, and users share the serving infrastructure.
- Durable state is keyed by conversation, never kept in process memory. The agent is rebuilt fresh per turn; a worker-local singleton is ephemeral reuse, not a durable service. Memory is not authority.
- Users share workers; KDCube binds each execution to the actor and authority context established by its entry path. A public route does not require a platform session; an app or integration may still carry or resolve an actor. Storage is scoped by tenant/project, then app/user/conversation/turn. Platform helpers apply those owner keys; trusted app code must preserve them. Structural isolation is a separate guarantee of the generated-code split boundary, not of two trusted app backends in one processor.
- Horizontal scale follows because nothing required to continue a later turn lives only in one process. Adding workers adds capacity, while the conversation lane keeps per-conversation order.
- The at-least-once lane exists for the same reason: a machine can disappear mid-turn, and the work must land somewhere else without losing its place in the conversation.
13 The levers at a glance
Every ⚙ lever in this article, in one place — each is the exact symbol to search for in the SDK for that crossing:
| Crossing | ⚙ Lever |
|---|---|
| Direct surface dispatch | call_bundle_op_public, _dispatch_bundle_mcp_request |
| Scheduled-work entries | @on_reactive_event → run() / execute_core · @cron(...) · @on_job |
| Telegram submit → lane | submit_telegram_turn · enqueue_chat_task_with_lane_events_atomic |
| Data Bus handling | @data_bus_handler(...) |
| Comm rebuild / recorded events | ChatCommunicator · comm.export_recorded_events() |
| App venv boundary | @venv(requirements=...) |
| Exec entry / the execution fence | exec_tools.execute_code_python | codegen_tools.codegen_python · agent_io_tools.tool_call(...) |
| Credential resolution, the two gates | ensure_claim(...) |
| Economic fence / metering | EconomicsGuard · track_llm | track_embedding | track_web_search |
| Subagent spawn | react.delegate |
| Cluster critical sections | pg_advisory_xact_lock · observed_file_lock |