Isolated Code Execution: Computation Inside, Authority Through Tools
An agent can write useful code: inspect data, join records, transform files, build a chart, or assemble a report. That code needs CPU, libraries, and a workspace. Network access, provider credentials, deployment secrets, and the authority to change an external system belong somewhere else. KDCube separates those jobs. Generated code computes inside a bounded executor; approved tools perform authorized actions through a trusted supervisor.
This separation is the core of KDCube isolated execution. It gives generated Python enough room to solve open-ended problems while keeping external authority in explicit, inspectable interfaces. A mail tool can send mail. A search tool can reach the web. A named service can update a task. The generated program receives their declared contracts and results, while credentials, network clients, identity resolution, grants, budgets, and service connections remain on the trusted side of the boundary.
The result is more useful than a sealed box and more controlled than an ordinary container. The code can think in files and Python. The platform decides how it may reach the world.
one execution, two responsibilities
COMPUTATION AUTHORITY
generated-code executor trusted supervisor
+------------------------------+ +------------------------------+
| Python and installed libs | | approved tool implementations|
| current execution workspace | tool request | request identity + grants |
| materialized input files |---------------->| credentials + service clients|
| artifact output directory |<----------------| network + provider access |
| bounded CPU, time, and bytes | typed result | accounting + audit |
+------------------------------+ +------------------------------+
arbitrary computation bounded external action
The boundary starts with one design decision
An ordinary Python process inherits its surroundings. Environment variables, mounted directories, network routes, SDK clients, and process credentials can all become ambient capabilities. A generated program may only need to calculate a result, yet the process around it may already know how to reach Redis, storage, mail, Slack, model providers, and platform configuration.
KDCube makes capability explicit. The generated program receives a small execution environment. External actions are represented as tools. Tool calls cross one authenticated local bridge and run in a component that can apply the platform's normal rules. That creates two clean questions:
Can the code compute this? -> executor: Python, files, libraries, CPU
May this action happen? -> supervisor/tool: identity, policy, grants, budget
The first question is open-ended. The second is governed. Keeping them separate is what lets an agent write real code without giving every line of generated Python the runtime's full authority.
One request, end to end
The isolated path begins when a tool or ReAct agent asks KDCube to execute Python. The
platform writes two source files: a stable main.py loader owned by the runtime
and the verbatim generated program in user_code.py. It prepares a
per-execution workspace, selects a runtime profile, launches the selected environment, and
later collects artifacts, logs, accounting records, and the structured result.
user asks for a data/file task
|
v
agent decides to use code
|
+-- codegen writes main.py + user_code.py
|
v
runtime selects the configured profile
|
+-- local -------> separate host subprocess --------+
| |
+-- docker ------> combined or split supervisor ----+
| + executor + approved tools |
| |
+-- external ---> remote supervisor + executor -----+
|
v
artifacts + logs + result
|
v
ReAct timeline / Files / caller response
The same high-level result contract survives every runtime mode. The selected mode changes the strength and location of the execution boundary, while the agent still sees one tool call and one result.
The supervisor and executor have different views
In supervised Docker execution, the two roles deliberately see different parts of the system.
| Surface | Trusted supervisor | Generated-code executor |
|---|---|---|
| Network | Deployment network and approved external services | Private network namespace; split mode uses --network none |
| Configuration | Descriptor-backed app and platform configuration | Small sanitized runtime values |
| Secrets | Normal secrets lifecycle and provider resolution | Secret material remains outside the executor environment |
| App code | Read-only app tool modules when required | Generated execution package only |
| App storage | Prepared read-only data when required by tools | Materialized current-execution files only |
| Writable files | Runtime output, diagnostics, and artifact workspace | Work directory, artifact directory, and executor logs |
| Tooling | Real tool implementations and service clients | Typed stubs that call the supervisor |
| Identity and policy | Restored request identity, grants, user selection, accounting | Business parameters only; authorization identity comes from bound request context |
The executor's split-Docker filesystem is intentionally short:
/workspace/work/ main.py, user_code.py, execution inputs
/workspace/out/ generated artifacts
/workspace/logs/executor/ user and executor diagnostics
/supervisor-socket/ authenticated local tool bridge
The supervisor has the broader runtime tree: descriptor material, app tool modules, prepared app data, complete runtime metadata, supervisor diagnostics, and network clients. Those paths are absent from the split executor's mount list. Host-path translation used by Docker-in-Docker identifies the source of each narrow mount while preserving that same executor mount list.
Isolation is a stack of controls
A container name alone says little about the effective boundary. KDCube's supervised path layers several controls so each one answers a different failure mode.
| Layer | Runtime behavior | Boundary it creates |
|---|---|---|
| Process | Generated code runs in its own child process | Crashes and exits stay outside the app server |
| Network | The generated-code child uses a private network namespace | Only supervisor tools make network calls |
| Filesystem | Read-only root plus narrow writable mounts | Writes stay in work, artifacts, and executor logs |
| Identity | Child drops to executor UID/GID and clears supplementary groups | Generated code runs outside the service/root identity |
| Linux capabilities | Split container starts from --cap-drop=ALL; child verifies zero effective capabilities | Kernel privileges stay with the lifecycle entrypoint |
| Privilege escalation | no-new-privileges, stripped setuid/setgid helpers, inherited seccomp deny for socket(AF_ALG, ...) | Helper binaries and alternate socket facilities stay outside the generated-code capability set |
| Tool bridge | Per-execution random token plus Linux SO_PEERCRED UID check | Only the intended executor can ask its supervisor to run tools |
| Tool policy | Tool id, parameter signature, allowlist, user selection, grants, and runtime policy are checked | A socket request becomes an authorized tool call only after validation |
| Resources | Timeout, file-size limit, workspace-growth monitor, and cancellation | One execution remains inside its configured time and storage envelope |
| Observability | Preserved source, component logs, tool-call records, and structured errors | Operators and agents can distinguish code, tool, and harness failures |
In the split strategy, the executor container starts read-only, with all Linux
capabilities dropped. A small root entrypoint prepares bind-source ownership, starts the
UID/GID-dropped child, and retains only the lifecycle ability needed to stop that child on
timeout, cancellation, or quota breach. Generated Python then runs as UID 1001,
GID 1000, with zero effective capabilities. The entrypoint's narrow
CAP_KILL allowance exists for that child-lifecycle step; it is gone from the
generated-code process after the identity and capability drop.
This is defense in depth with a practical purpose. The network rule protects service endpoints. The mount list protects runtime files. The identity and capability rules protect the container boundary. The tool policy protects side effects. Resource controls protect availability. Logs and structured results make every outcome visible.
Two Docker strategies, one trust model
KDCube supports two supervised Docker topologies.
Combined
The compatibility topology places the trusted supervisor and the generated-code executor
child in one py-code-exec container. The container has the mounts and network
required by the supervisor. The child receives a private network namespace, drops identity,
and receives the sanitized environment.
py-code-exec container
+-- supervisor process network, descriptors, tools
+-- executor child private network, reduced uid/gid, narrow env
+-- main.py
+-- user_code.py
Split
The split strategy, which is the current platform default, places the same roles in sibling containers. The supervisor container receives network, descriptors, app modules, prepared app data, and runtime storage. The executor container receives only work, artifacts, executor logs, and the Unix socket volume.
supervisor container executor container
+--------------------------------+ +--------------------------------+
| network + trusted tools | | --network none |
| descriptors + secret resolver |<--socket-->| read-only root |
| app modules + prepared data | | work + artifacts + own logs |
| full runtime output + logs | | uid/gid-dropped user_code.py |
+--------------------------------+ +--------------------------------+
Both strategies implement the same conceptual split. The split topology adds a container-level filesystem boundary between the two roles, making supervisor descriptors, app storage, platform storage, and diagnostic paths absent by construction from the executor container.
Tools are the authority gates
Generated code can call a configured tool through the executor stub. The stub serializes the tool id and parameters, adds the per-execution credential, and sends the request over the Unix socket. The supervisor authenticates the peer, validates the call, runs the approved implementation under restored request context, and returns a bounded result.
generated Python
|
| tool_call("named_services.object_action",
| {"namespace": "mail", "action": "send", ...})
v
executor tool stub
|
| authenticated Unix socket
v
trusted supervisor
|
+-- authenticate peer + per-execution token
+-- resolve qualified tool id
+-- validate protocol + parameter signature
+-- apply configured and per-user allowances
+-- bind original request identity
+-- apply relevant grants, claims, budgets, and service policy
+-- execute approved implementation
+-- record usage, logs, and result
v
sanitized tool result returns to generated Python
This is the central security and productization seam. A tool is trusted code, yet each invocation still passes through policy. The supervisor derives the platform user id, provider credential, roles, and economics subject from the request context that entered the execution. Ordinary tool parameters carry the business request.
The model remains useful because the boundary controls authority rather than removing capabilities. Web search, model calls, storage access, mail, Slack, memory, and app-defined services can all remain available as narrow tools. Their network and credentials live with their implementations. The generated program sees the contract and the result.
Context crosses the boundary as a portable room
Trusted tool code still needs to know which request it serves. KDCube carries a small, JSON-safe portable context room across runtime boundaries. It contains the situation required to reconstruct trusted SDK services:
PORTABLE_SPEC_JSON
request context
tenant / project
user / roles / permissions / identity authority
session / conversation / turn / app / agent
app call context
small request-scoped app metadata
named-service discovery
tenant/project-scoped provider lookup descriptor
named-service client policy
acting client id + configured consumer allowances
accounting context
user / app / conversation / turn / component / agent
Live Redis clients, database pools, callbacks, provider clients, large documents, binary payloads, and secrets remain in their owning runtime. The target restores the portable fields and rebuilds trusted services from descriptor-backed configuration. The supervisor receives the full trusted room; the executor gets the reduced values required for its workspace and tool stubs.
This distinction matters beyond serialization. Discovery answers where a named service lives. Client policy answers which operations this agent may call. Per-user capability choices narrow that configured ceiling. Provider code then authorizes through the carried request identity. The model supplies the business parameters, while the platform supplies identity and authority.
The return path is a reduce boundary
Runtime context has an explicit outbound path as well. The host serializes the portable
room in PORTABLE_SPEC_JSON and the communicator contract in COMM_SPEC,
then starts the fenced runtime. The child reconstructs its SDK surfaces, runs, and writes
bounded output and side files. The host reducer is the only component that merges those
child records back into the parent run.
host task
bind request / app / agent / accounting context
build PORTABLE_SPEC_JSON + COMM_SPEC
|
v
fenced runtime
restore context
rebuild trusted services in supervisor
execute generated code
record artifacts and runtime events
|
v
side files
delta_aggregates.json
comm_recorded_events.json
declared artifacts / execution result
|
v
host reducer
validate + merge selected records
publish artifacts and structured result
This bootstrap/run/reduce shape works across a local child, Docker, and external execution. It avoids passing process-local clients into the child and avoids letting child memory become host state by accident. Communication records carry stable ids for idempotent merge. A hard kill may leave a side file absent; the reducer treats the missing file as an empty contribution and reports the execution failure through the normal result path.
For external execution, snapshots extend the same contract over distance. The host uploads the work and input runtime state, the task restores them, and the output snapshot returns artifacts and side files to the reducer.
Isolated code can still use named services
An in-process agent can call a named-service provider directly through the live app registry. An isolated supervisor lives in another process and often another container. It carries discovery, client policy, and identity; the live registry object remains in the host process. KDCube closes that transport gap with the named-service Data Bus relay:
executor supervisor Data Bus provider app
generated code trusted relay client Redis streams host proc
| | | |
| object.action(...) | | |
|---------------------->| | |
| | publish request | |
| |-------------------->| |
| | | claim + bind actor |
| | |------------------>|
| | | | normal provider
| | | | auth + consent
| | | result + ack |
| |<--------------------|<-------------------|
|<----------------------| | |
The message actor is the restored request identity. A provider applies the same connected-account consent, claims, grants, and visibility rules it applies to a direct call. A missing approval returns the standard structured consent response and action link. Once the user approves, the same generated code can succeed.
The relay uses an at-least-once lane. Side-effecting calls use a required message id and a recorded response. A redelivery with the same id returns the recorded result instead of executing the provider action again. This makes a generated mail send or Slack post safe across worker retry while preserving the provider's normal authorization model.
The executor still owns only computation. The supervisor owns the relay client, the Data Bus carries transport, and the provider app owns the domain action.
Files cross through workspaces and artifact contracts
Generated code works with real bytes without placing those bytes in model messages. Inputs are materialized into the current execution workspace. Outputs are written to the artifact directory. The runtime preserves the exact executed source and collects declared files for delivery.
hosted input / conversation attachment
|
| materialize or pull
v
/workspace/work or current-turn attachment path
|
| generated Python reads/transforms
v
/workspace/out/turn_<id>/files/<deliverable>
|
| validate declared file contract
v
conversation attachment storage + chat.files event
|
v
user's Files surface
The output declaration and the hosted artifact together establish delivery. A hosting
failure places a delivery_failed.file_hosting notice on the timeline, giving
the agent concrete evidence that delivery stopped at the hosting stage. The executor's
ability to create a local file and the platform's ability to host that file are separate,
observable stages.
The complete diagnostic tree remains available to the platform. It includes the verbatim
user_code.py, the stable loader, executor and supervisor logs, Docker launcher
logs, tool-call metadata, and the artifact workspace. In split mode, generated code sees
only its own work, artifacts, and executor logs.
Every execution has a resource envelope
Isolation also protects availability. The runtime enforces limits at the child process boundary, independently of whether the generated program follows its file contract. Current controls include:
- maximum execution time;
- maximum size of one generated file, enforced in the child through
RLIMIT_FSIZEand currently100 MiBby default; - maximum net-new bytes across monitored writable roots, currently
250 MiBper execution by default; - optional maximum total bytes in the active workspace;
- a live workspace monitor, currently polling every
0.5seconds by default; - cancellation propagation and child cleanup;
- declared output validation before files are reported as delivered.
The monitored roots include work, artifact output, and executor logs. A user attachment begins consuming the local envelope when the platform materializes it into the active workspace. Hosted metadata alone remains outside that byte count until the file is pulled.
An app can override the runtime profile for its workload. A document-generation app may need larger artifacts; a small data-transform tool may choose a tighter envelope. The limit belongs to the execution boundary, close to the resource it controls.
Runtime mode is a policy choice
KDCube supports several execution modes because different code has different trust and operational needs.
| Mode | Placement | Primary use |
|---|---|---|
none | Main app process | Small platform-owned tools where direct execution is intended |
local | Separate host subprocess | Crash containment for native libraries and local development |
docker + combined | Supervisor and executor child in one container | Supervised tool mediation with a compact deployment topology |
docker + split | Supervisor and executor in sibling containers | Strong filesystem, descriptor, and secret separation for generated code |
| external / Fargate | Snapshot-based remote execution task | Distributed execution with the same supervised runtime contract |
local gives process separation. The supervised Docker and external modes add
the network, environment, tool, identity, and descriptor boundary described in this article.
Runtime selection can be made for the app or tool profile, so one deployment can keep a
deterministic trusted helper in-process and send generated Python through split isolation.
External execution moves the work through snapshots. The host packages the workdir and runtime state, the remote task restores them, executes under the supervised contract, uploads output deltas, and the host merges the result. Descriptor and launch payloads remain supervisor inputs. The generated-code child still receives the sanitized executor subset.
Large launch context stays on the trusted transport
Real apps can have large descriptor and runtime payloads. In split Docker, large
supervisor values such as RUNTIME_GLOBALS_JSON and
KDCUBE_RUNTIME_*_YAML_B64 move from command-line -e arguments to a
JSON environment map streamed over the supervisor container's stdin. The switch is signaled
by KDCUBE_EXEC_PAYLOAD_STDIN=env_json;
KDCUBE_EXEC_RUNTIME_GLOBALS_INLINE_MAX_BYTES sets the inline threshold, which is
currently 96 KiB. The supervisor hydrates the map before descriptor
materialization.
This avoids the Linux per-argument MAX_ARG_STRLEN / E2BIG failure
while preserving the trust boundary: the transport change applies to the supervisor, and the
executor subset remains the same size and shape. External Fargate execution uses its own
launch transport through AWS Secrets Manager and passes a payload identifier to the task.
This operational detail illustrates the larger rule. Configuration can cross a runtime boundary through a trusted transport. Generated code still receives only the fields assigned to the executor contract.
Failures return evidence
An isolated runtime has more moving parts than an in-process function, so failure reporting is part of the architecture. KDCube separates three questions:
Did the runtime launch and complete? harness / container / IPC
Did user_code.py complete? program exception / exit / timeout
Did it produce the promised output? declared file contract / hosting
The runtime preserves component-specific evidence:
| Evidence | Meaning |
|---|---|
user.log | Program-visible logging |
runtime.err.log | Executor capture and traceback context |
executor.log | Executor lifecycle diagnostics |
supervisor.log | Tool bridge and trusted runtime diagnostics |
docker.out.log, docker.err.log | Container launcher output |
infra.log | Derived cross-component infrastructure summary |
executed_programs/<execution_id>/user_code.py | Exact generated program that ran |
A harness failure that happens before the program starts is returned as an ordinary
react.tool.result block with mime: application/json and a payload
of this shape:
{
"status": "error",
"error": {
"code": "sandbox_execution_failed",
"message": "...",
"details": {"where": "exec.tool.harness"}
}
}
The message identifies the platform/harness boundary and states that execution stopped before the user program started. The agent therefore receives a terminal result for the tool call instead of an empty timeline gap that could be mistaken for success.
Retry guidance is bounded: retry once, then run a minimal probe, then report the infrastructure failure or finish without exec. Re-sending the same oversized or structurally failing payload indefinitely only repeats the same boundary condition.
What a builder configures
Builders can preserve an existing agent and its Python tools. They connect those tools to the KDCube tool subsystem, then assign execution boundaries according to what each one does. A typical adoption path is:
- Keep business actions in narrow Python tools with explicit parameters and results.
- Register the tools in the app's per-agent inventory.
- Route generated Python and higher-risk code through a supervised runtime.
- Let generated code use tools for networked or credentialed actions.
- Write user deliverables into the artifact directory and declare their file contract.
- Configure timeout and workspace limits for the app's workload.
- Test the executor view, tool policy, cancellation, and failure result before release.
The current app descriptor shape for the split runtime is compact:
config:
execution:
runtime:
mode: docker
container_strategy: split
max_file_bytes: 100m
max_exec_workspace_delta_bytes: 250m
workspace_monitor_interval_s: 0.5
The tool inventory can differ per agent. The app runtime profile supplies its execution defaults, and tool isolation policy can route individual tools. An app can host several agents, each with its own tools, skills, model choices, and execution policy. Builders can start with one isolated code path and adopt more of the framework as useful. The runtime boundary works alongside the agent logic, orchestration framework, and frontend that already work.
The trust model, stated plainly
The generated program is untrusted input. The trusted computing base includes the KDCube host process, the supervisor, approved tool implementations, the descriptor and secrets lifecycle, the container runtime, and provider services. That makes responsibilities explicit:
| KDCube runtime guarantees | Builder-owned responsibilities |
|---|---|
| Generated code runs in the selected boundary | Select the runtime profile appropriate to the workload |
| Supervisor-only network and secret resolution | Keep credentials inside SDK/config/secrets APIs |
| Narrow executor mounts and reduced Linux identity | Avoid mounting extra host paths into the executor |
| Authenticated, policy-checked tool calls | Design narrow tools and validate business parameters |
| Carried request identity and accounting context | Declare grants, provider claims, and app visibility accurately |
| Time and filesystem resource limits | Size limits for expected inputs and deliverables |
| Structured errors and preserved diagnostics | Handle managed failures and avoid claiming undelivered output |
| Relay replay protection for named-service calls | Preserve external idempotency where provider APIs require it |
One running KDCube deployment is scoped to one tenant and one project. Inside that deployment scope, each isolated execution receives its own run workspace, its own supervisor credential, its own request identity, and its own resource envelope. App and user policy continue to govern the tools available inside that execution.
The useful form of isolation
The goal is to make the power of generated code legible. Python gets a place to compute. Files get a defined path from input to artifact. Tools get explicit contracts. External actions run under the user's real identity and the app's configured policy. Credentials stay with the trusted implementations that need them. Every paid or state-changing tool call can remain trackable. Every execution ends with a result and evidence; declared artifacts add verified delivery.
That is the practical boundary for production agents:
generated code may compute freely inside its envelope
external authority is exercised only through governed tools
KDCube gives builders both halves: an open-ended Python workspace for the work that is hard to predict, and a production runtime for the actions that must be authenticated, bounded, observable, and accountable.
Read more
- Isolated Runtime: Design and Operations
- Isolated Code Execution Architecture
- Isolated Code Execution Operations Guide
- Runtime Modes for Built-In Tools
- Distributed Execution: Fargate
- Cross-Runtime Context
- Fenced Runtime Bootstrap and Reduce
- Named-Service Calls From Isolated Runtimes
- Exec Logging and Error Propagation
- Tool Subsystem
- Custom Tools and Declared Files
- App Runtime Configuration and Secrets