Documentation/docs/sandbox/function

#Sandbox Functions

Sandbox functions are one-shot handlers stored in the sandbox filesystem. The first Python runtime maps a function name such as main to /workspace/functions/main.py and invokes the handler function by default.

Function invocation does not add another daemon. Procd stays as the sandbox entry process and starts the injected /procd/runtimes/python-runner binary for each invocation.

Runtime Requirements#

The sandbox image must provide python3 on PATH. The default Sandbox0 template includes Python. Custom images should install Python when they need this function runtime.

The platform injects both binaries into the sandbox pod:

PathPurpose
/procd/bin/procdSandbox entry process
/procd/runtimes/python-runnerPython function runner

Handler Contract#

Create a function module under /workspace/functions. For main, write /workspace/functions/main.py:

python
import base64 import json def handler(request): raw = base64.b64decode(request.get("body_base64") or "e30=") payload = json.loads(raw.decode("utf-8")) name = payload.get("name", "world") return { "status": 200, "headers": {"content-type": "application/json"}, "body": {"message": f"hello {name}"}, }

The request object contains method, path, query, headers, and body_base64. The response object contains status, headers, and body_base64. Returning a Python dict, str, bytes, or None is supported; non-response dictionaries can be returned as {"body": ...}.

Invoke#

POST

/api/v1/sandboxes/{id}/functions/{name}/invoke

go
source := []byte(`import base64 import json def handler(request): raw = base64.b64decode(request.get("body_base64") or "e30=") payload = json.loads(raw.decode("utf-8")) return { "status": 200, "headers": {"content-type": "application/json"}, "body": {"message": "hello " + payload.get("name", "world")}, } `) _, err := sandbox.Mkdir(ctx, "/workspace/functions", true) if err != nil { log.Fatal(err) } _, err = sandbox.WriteFile(ctx, "/workspace/functions/main.py", source) if err != nil { log.Fatal(err) } body := base64.StdEncoding.EncodeToString([]byte(`{"name":"Ada"}`)) resp, err := sandbox.InvokeFunction(ctx, "main", apispec.FunctionInvokeRequest{ Method: apispec.NewOptString("POST"), Path: apispec.NewOptString("/hello"), BodyBase64: apispec.NewOptString(body), }) if err != nil { log.Fatal(err) } decoded, err := base64.StdEncoding.DecodeString(resp.BodyBase64.Or("")) if err != nil { log.Fatal(err) } fmt.Println(string(decoded))

By default, the CLI prints the decoded function body. Use -o json to inspect the full function response, including status, headers, and body_base64.