Runtime and SDK
This page describes the runtime that executes cloud functions: the supported languages and toolchains, what your function reads and writes, how it is triggered, the limits, the SDK, and the execution history. For a step-by-step walkthrough, read the Cloud functions guide.
Runtime model
A cloud function is a WebAssembly module that targets WASI (WebAssembly System Interface). Each invocation runs your module once, in a sandbox:
| Aspect | Behavior |
|---|---|
| Input | A JSON document on stdin |
| Output | Whatever your function writes to stdout |
| Logs | Whatever your function writes to stderr, kept in the execution history |
| Configuration | Your function's variables, exposed as environment variables |
| Filesystem | None |
| Network | None. Use the SDK host functions to reach Fundamentum |
| Concurrency | One execution at a time per function, unless you configure a worker pool. Extra invocations wait in a queue |
Languages and toolchains
Any language that compiles to a WASI module works. These toolchains are known to work:
| Language | Toolchain | Build command |
|---|---|---|
| Go | Standard Go compiler | GOOS=wasip1 GOARCH=wasm go build -o function.wasm . |
| Go | TinyGo | See the TinyGo documentation for the wasip1 target |
| JavaScript | Javy | javy build function.js -o function.wasm |
| Python | py2wasm | py2wasm function.py -o function.wasm |
| Rust | Cargo with a WASI target | cargo build --release --target wasm32-wasip1 |
On macOS, py2wasm does not work with the system Python or the Homebrew Python. Install the official Python build, for example with pyenv.
The language field of a function is descriptive metadata. The runtime executes the WebAssembly module whatever the source language.
Input
Your function receives this JSON document on stdin:
{
"input": "<base64-encoded bytes>",
"context": {
"function_id": "<function-id>",
"project_id": 1
},
"metadata": {
"<key>": "<base64-encoded bytes>"
}
}
| Field | Description |
|---|---|
input |
The payload: the HTTP request body, or the message received on the input topic. Bytes, base64-encoded in the JSON |
context.function_id |
The ID of the function being executed |
context.project_id |
The project the function runs in |
metadata |
Key-value pairs, values base64-encoded. On HTTP invocation, each X-Function-<key> request header becomes a <key> entry |
Output
Write the result to stdout. Exit normally on success. If your module exits with an error, the execution is recorded as failed, with your stderr output and the error.
What happens to the output depends on the trigger:
- HTTP: the output is returned in the invocation response.
- Topic: the output is published on the connector's output topic, if it has one.
Triggers
| Trigger | How | Input |
|---|---|---|
| HTTP | POST /v2/projects/{project_id}/functions/{function_id}:invoke on the Public API, or fun functions invoke |
The request body, sent as application/octet-stream |
| Topic | A connector in fundamentum_iot_connectors: {"input_topic_id": <topic-id>, "output_topic_id": <topic-id>}. output_topic_id is optional |
Each message received on the input topic |
Every function can be invoked over HTTP. Connectors cannot form a cycle: the API rejects a definition whose connectors loop back on themselves. See Topics and Connectors.
The HTTP invocation returns:
{
"status": "success",
"data": {
"started_at": "2026-01-01T12:00:00Z",
"execution_time": 0.42,
"output": {
"format": "json",
"value": { "processed": 10 }
}
}
}
output.format is json when the output is valid JSON, unicode when it is valid UTF-8 text, and base64 otherwise.
| Status | Meaning |
|---|---|
200 |
The function ran |
404 |
No such function in this project |
408 |
The execution reached its timeout and was stopped |
424 |
The function has no WebAssembly file yet |
Function definition
Create and update functions with the v2 functions operations or fun functions new and fun functions edit.
| Field | Type | Description |
|---|---|---|
description |
string, required | Human-readable description |
language |
string, required | Source language, for information only |
execution_timeout |
integer, nanoseconds | Maximum duration of one execution |
keep_alive_timeout |
integer, nanoseconds | How long the compiled function stays loaded after an execution. 0 unloads it right away |
fundamentum_iot_connectors |
array | Topic triggers, see above |
variables |
object | KEY: value pairs exposed as environment variables |
scaling.pool_size |
integer, greater than 1 | Number of workers that execute in parallel |
Upload the WebAssembly binary separately, as multipart/form-data with the file in the function field, on PUT /v2/projects/{project_id}/functions/{function_id}/file, or with fun functions upload <name> <file.wasm>. has_file_set tells you whether a binary was uploaded.
Limits
| Limit | Value |
|---|---|
| Default execution timeout | 5 seconds |
| Maximum execution timeout | 60 seconds |
| Execution history retention | 7 days |
The first execution after an upload, or after the keep-alive timeout expires, includes compiling the module, which takes noticeably longer than later executions. Set keep_alive_timeout to keep a frequently used function loaded.
SDK
These host functions let your module interact with Fundamentum. They are imported from the fundamentum namespace:
| Host function | Purpose |
|---|---|
ProduceMessage(topic, message) |
Publish a message on a topic of your project. topic is the topic's ID as a string (for example "42"), as returned by fun api v1 pub-sub list-pub-sub-topics. Asynchronous: no result is returned to your function |
ProduceMessageWithHeaders(topic, message, headers) |
Same as ProduceMessage, with headers as a JSON object of header names and values |
PublicAPIGet(route, token) |
Send a GET request to the Public API on route, with token as the bearer token, and return the response |
Each argument is passed as a 64-bit integer: the upper 32 bits are a pointer into the module's memory and the lower 32 bits a length in bytes.
Go SDK
The Go SDK wraps the input and the host functions:
package main
import (
"fmt"
"os"
sdk "<go-sdk-module-path>"
)
// ID of the project topic that receives the result.
const outputTopicID = "42"
func main() {
input, err := sdk.ReadInput(os.Stdin)
if err != nil {
fmt.Fprintln(os.Stderr, "failed reading input:", err)
os.Exit(1)
}
fmt.Fprintln(os.Stderr, "project", input.Context.ProjectID)
sdk.ProduceMessage(outputTopicID, string(input.Input))
fmt.Print(`{"ok":true}`)
}
| Function | Description |
|---|---|
sdk.ReadInput(r io.Reader) (FunctionInput, error) |
Parse the input document. Input and Metadata values are decoded to bytes |
sdk.ProduceMessage(topic, msg string) |
Call ProduceMessage |
sdk.ProduceMessageWithHeaders(topic, msg string, headers map[string]string) |
Call ProduceMessageWithHeaders |
sdk.Get(token string) func(route string) (map[string]any, error) |
Call PublicAPIGet with a token and return the decoded JSON |
In other languages, read stdin and parse the JSON yourself. The host functions are available as WASI imports from the fundamentum namespace.
Execution history
Each execution is recorded with:
| Field | Description |
|---|---|
source |
What triggered it: http or pubsub |
started_at |
Start time |
execution_time |
Duration in seconds |
log |
What the function wrote to stderr |
error |
The error message, empty on success |
List executions with fun functions executions list --name <name> or GET /v2/projects/{project_id}/functions/{function_id}/executions. Executions are kept for 7 days.