Functions runtime
Each function a module declares is a handler registered in its module.wasm. The backend runs it in a wazero WebAssembly sandbox with no network, filesystem or environment access.
Transactions
Section titled “Transactions”Every invocation runs inside one Postgres transaction. A handler that returns a result commits. A handler that returns an error, crashes, runs out of time or exceeds the memory limit rolls everything back. Kernel calls are synchronous.
Event handlers run in a savepoint: a failing handler is logged and does not fail the operation that emitted the event.
Limits
Section titled “Limits”These limits are enforced by the host and cannot be configured:
- 200 ms per
client,adminandeventinvocation, 5 s perscheduleinvocation. The budget includes nested module calls. - 128 MB of memory per instance.
- Nested module call depth of 8.
- Console output (stdout and stderr) capped at 64 KB per invocation.
Triggers
Section titled “Triggers”| Trigger | Invoked by |
|---|---|
client | a player: POST /sdk/v1/modules/{module}/{fn} |
admin | the dashboard (POST /platform/v1/projects/{projectId}/modules/{module}/{fn}) or a server with the admin key (POST /game/v1/modules/{module}/{fn}); roles limits which roles may call it |
event | the kernel, when the event named in event is emitted |
schedule | the cron worker, on the function’s cron expression |
module | another module that lists this module in its deps |
Schedules
Section titled “Schedules”A schedule function must have a valid 5-field cron expression in cron (minute, hour, day of month, month, day of week, in UTC). Fields accept *, numbers, ranges (1-5), lists (1,15) and steps (*/15). Publishing or dry-running a package with a missing or invalid cron fails with HTTP 400.
Schedules run in both environments, staging and prod, for every enabled install. The worker checks once a minute. A run that was missed (for example during a deploy) is caught up on the next check, looking back at most 35 days.
Responses and errors
Section titled “Responses and errors”| Function outcome | Transaction | Player response |
|---|---|---|
| returns a result | commit | 200 { result } |
returns a business error (Fail(code, message)) | rollback | 422 { error, code, requestId } with the module’s code |
| crash, panic, no output | rollback | 500 without details |
| timeout | rollback | 504 without details |
| memory limit | rollback | 500 without details |
Module error codes are strings chosen by the module author and declared with rudder.Errors(...). They go into the manifest, and generated module clients expose them as a union type (TypeScript) or a constants class (C#). A module that returns an undeclared code still gets it to the player, and the backend writes a warning to the module log.
Client calls are idempotent: the SDKs send an Idempotency-Key, and the backend stores the response for 24 hours and returns it for a repeated key. A key reused for another function fails with 422 idempotency_key_mismatch.
If a nested module call fails, the whole invocation fails and the player receives the failure of the module that was called.
Failures and console output are written to the module log (dashboard Modules → Logs, kept 7 days).
Dry run
Section titled “Dry run”The dashboard and rudder module dry-run run a function with dryRun: true. Dry run is available only to allowlisted publisher accounts (early access). A dry run always rolls back and returns result, error, console, log (the kernel calls made) and events. The player endpoint never returns these fields.
Kernel API
Section titled “Kernel API”Each SDK wraps the same host operations. The project, environment and calling player come from the invocation context; a module cannot pass them.
| Area | Operations |
|---|---|
| Wallet | get, adjust (a negative amount debits; insufficient funds fails) |
| Storage | player storage get, set |
| Project storage | get, set |
| Counters | get, increment (emits counter.incremented for positive deltas) |
| Resources | list, get, create, update (optional optimistic version), delete, count |
| Segments | match (player against a segment), bucket (stable player bucket for a salt) |
| Notifications | send |
| Events | emit |
| Modules | call (target must be in deps and have trigger module) |
The context also carries the trigger, the player (for client calls), the module config values, the module slug, the environment and the current time.
Kernel events: counter.incremented, player.created, wallet.adjusted, storage.updated. Custom events use <module>.<name>; names starting with counter., player., wallet. or storage. are reserved.
Module SDK
Section titled “Module SDK”Modules are written in Go with hub.rudder.build/rudder/rudder-module-go. Other languages are not supported for now.
Go handlers must be registered in init(), not main():
func init() { rudder.Register("hello", rudder.Typed(hello), rudder.Errors("name_required"))}
type helloArguments struct { Name string `json:"name" rudder:"maxLength=64"`}
type helloResult struct { Message string `json:"message"`}
func hello(ctx *rudder.Context, args helloArguments) (helloResult, error) { if args.Name == "" { return helloResult{}, rudder.Fail("name_required", "name is required") } return helloResult{Message: "hello " + args.Name}, nil}rudder.Typed records the argument and result types; rudder module build turns them into the function’s JSON Schemas (see Module manifest).
In the module SDK, optional scalar fields of kernel calls use omitempty, so an explicit 0, false or "" is not sent. For every such field the zero value is also the default. For example, Resources.Update with Version: 0 updates without a version check.