Function Mode
This document describes the v0.x Function-mode design and its implemented control-plane and invoke-dataplane baseline. Deferred work is called out in the roadmap .
The goal is to let kruntimes expose low-latency fixed-handler invocations without putting every invocation through Kubernetes reconciliation. Mutable workspaces, arbitrary commands, and file operations belong to the separate Session Mode for Agent Sandboxes design.
Motivation
One-shot Runs are useful for short tasks, CI steps, and automation commands. Some workloads instead expose a stable operation through a fixed handler:
- a caller invokes the same handler repeatedly with different bounded inputs;
- repeated invocations reuse prepared source and the Runtime Server’s registration state;
- the invoke path must be fast enough for request-response use cases;
- high-frequency invocations should not write unbounded history to etcd.
Kubernetes remains the lifecycle control plane. The invoke path should be a runtime dataplane path.
Goals
- Use
Runas the lifecycle object for both one-shot tasks and fixed-handler functions. - Add
Run.spec.mode.functionso a Run can reserve a Runtime Pod and stay callable until deletion or idle timeout. - Expose a stable runtime gateway endpoint from Run status.
- Route invoke requests through runtimed to the Runtime Pod that owns the Run.
- Keep scheduler and runtimed generic. They should not understand agent, workflow, or MCP semantics.
Non-Goals
- kruntimes does not become an agent framework.
- kruntimes does not own prompt management, model routing, memory, tool catalogs, or multi-agent planning.
- Function mode is not a replacement for Workflow APIs.
- Function mode does not provide an arbitrary-command sandbox, mutable workspace API, or file API.
Proposed Run Model
spec.source describes where the code or files come from. It is shared by task
and function modes.
spec.mode is a mutually exclusive mode-specific configuration object:
type RunMode struct {
Task *TaskMode `json:"task,omitempty"`
Function *FunctionMode `json:"function,omitempty"`
}
type TaskMode struct {
Entrypoint string `json:"entrypoint,omitempty"`
Args []string `json:"args,omitempty"`
}
type FunctionMode struct {
Handler string `json:"handler,omitempty"`
IdleTimeoutSeconds *int32 `json:"idleTimeoutSeconds,omitempty"`
}
Exactly one of mode.task or mode.function must be set.
One-shot task Runs remain the default. entrypoint and args belong to task
mode because they describe how to start a process once:
apiVersion: kruntimes.io/v1alpha1
kind: Run
metadata:
name: summarize-once
spec:
runtime: python
source:
inline: |
print("hello")
mode:
task:
entrypoint: main.py
args:
- --verbose
Function-mode Runs reserve a Runtime Pod and register callable code. handler
belongs to function mode because it identifies the callable function entrypoint,
similar to AWS Lambda’s filename.function convention:
apiVersion: kruntimes.io/v1alpha1
kind: Run
metadata:
name: diagnose-service
spec:
runtime: python
source:
inlinePath: main.py
inline: |
def invoke(request):
return {
"outputs": {
"summary": "diagnosis complete"
}
}
mode:
function:
handler: main.invoke
idleTimeoutSeconds: 600
The Run is ready when runtimed has prepared the source, registered it with the local Runtime Server, and can accept invoke traffic:
status:
phase: Ready
assignedPod: runtime-python-7f587b4668-njcks
endpoint:
protocol: HTTPS
url: https://runtime-gateway.kruntimes-system.svc.cluster.local/v1/namespaces/kruntimes-demo/runtimes/python/runs/2c24c1f0-9f8f-4f80-82d5-3dd16a12d1e6:invoke
caBundle: <base64-encoded-PEM>
conditions:
- type: Ready
status: "True"
reason: FunctionRegistered
The exact phase, endpoint, retry, timeout, cleanup, routing, authorization, and invocation semantics are defined in Function Mode Lifecycle and Invoke Dataplane .
Ready is not terminal for function-mode Runs. Deletion, cancellation, failed
registration, or idle timeout ends the reservation.
Scheduling and Capacity
Function-mode Runs still use the normal Runtime capacity model. A Runtime Pod
can own more than one function-mode Run when the Runtime capacity allows it. For
example, a Runtime with runs: "2" can register two ready function-mode Runs on
the same Runtime Pod.
This keeps the scheduler generic. Function mode does not imply Pod exclusivity: the scheduler only decides whether a Runtime Pod has capacity for another Run. Session Mode, in contrast, requires exclusive v0 capacity because it owns a mutable workspace.
Handler Field Placement
Earlier drafts used a top-level handler field:
spec:
handler: module.function
The handler concept is still useful. It is common in FaaS systems, including
AWS Lambda, where a handler selects the concrete callable entrypoint. The
problem is its location. A top-level handler sits next to task-only concepts
such as entrypoint and args, which makes the execution model harder to
understand.
The API keeps handler under function mode:
spec:
source:
git:
url: https://github.com/example/tools.git
ref: main
mode:
function:
handler: diagnose.invoke
Top-level handler, entrypoint, and args fields are not part of the target
Run API. Task mode keeps entrypoint and args under mode.task, while
function mode keeps handler under mode.function.
Runtime Gateway
The detailed gateway routing and authorization contract is defined in Function Mode Lifecycle and Invoke Dataplane .
All Runtimes share one runtime-gateway Deployment and its ClusterIP Service.
The Helm chart installs them when gateway.enabled is true. The gateway
Deployment runs stateless HTTP servers. A Run endpoint identifies the namespace,
Runtime, and Run UID; the gateway calls the Kubernetes Service for that Runtime,
which selects a ready Runtime Pod before runtimed resolves the owner:
client
-> shared runtime-gateway Service
-> runtime-gateway Pod
-> Kubernetes Service for Runtime=python
-> ready Runtime Pod's runtimed
-> owning runtimed when different
-> local Runtime Server
The gateway Service address is stable. Each Runtime controller-created Service selects its ready Pods, and each runtimed resolves ownership only for Runs of its own Runtime:
Run namespace/name/UID -> assigned Runtime Pod UID -> attempt -> readiness
Invoke behavior:
- if the request lands on the owning runtimed, invoke the local Runtime Server;
- if the request lands on another runtimed, proxy to the owning Runtime Pod;
- if the Run is not ready, return a typed 409 or 503 error;
- if the Run does not exist or is not owned by the Runtime, return 404;
- do not synchronously read the Kubernetes API on the invoke path.
Runtime Server Contract
Runtime Servers need a function-mode contract in addition to one-shot execute:
RegisterFunction: prepare code for a Run UID and ownership attempt.InvokeFunction: run a request against a registered function.UnregisterFunction: release runtime-local state.FunctionStatus: report readiness and runtime-local errors.
The idempotency, fencing, timeout, and bounded invoke semantics are defined in Function Mode Lifecycle and Invoke Dataplane .
Invoke responses should contain bounded structured data:
{
"outputs": {
"summary": "1 pending pod found",
"suspected_cause": "insufficient cpu"
},
"artifactRefs": [
{
"name": "diagnosis.json",
"uri": "s3://kruntimes-artifacts/runs/kube-diagnose-agent/diagnosis.json"
}
]
}
High-frequency invocation history should not be written to Run.status by
default. Persisted history can be added later through explicit audit sinks,
metrics, logs, or artifact metadata.
Reliability and Security Requirements
Function mode needs E2E coverage for:
- function registration and ready status;
- local invoke and proxied invoke;
- repeated invocation;
- idle timeout;
- explicit release;
- runtime pod restart recovery;
- cleanup;
Function invocation remains a bounded request-response API. It does not persist
arbitrary command history or workspace contents in Run.status.
Implementation Sequence
- Add the API design and validation for mutually exclusive
spec.mode.taskandspec.mode.function. - Remove top-level
Run.spec.handler,Run.spec.entrypoint, andRun.spec.args; useRun.spec.mode.function.handlerandRun.spec.mode.taskinstead. - Add Helm templates and values for the optional shared runtime-gateway Deployment and Service.
- Add runtimed ownership cache and invoke routing.
- Add Runtime Server register, invoke, unregister, and status APIs.
- Implement built-in Bash/Python function-mode adapters.
- Add
krt invoke. - Add E2E tests covering ready, invoke, proxy, cleanup, and restart recovery.