WASM runtime reference
Gora runs two kinds of app artifacts:
This page is the WASM reference. For the sandbox contract, see
the runtime contract in Create a Gora app.
Pick the right runtime (read this first)
Capabilities differ — this table is the honest state of the runtime today:
Rule of thumb: WASM is now the capable, safe default — it can read input,
keep durable state, call an LLM, and fetch over HTTP, all under fuel metering and
an attestation transcript. Build agents and oracles in WASM. The JS/TS/Python
sandbox runs as an unsandboxed subprocess, so it is disabled on the public
testnet and only available on a node you run yourself
(
--allow-unsafe-runtimes).
The manifest artifact block
The ABI contract
Your module must satisfy exactly three things:- Export linear memory named
memory. - Export the entrypoint declared in the manifest: a function with zero
parameters returning
i32— a pointer into linear memory where your output starts. - Place your output at that pointer. The runtime reads exactly
output_lengthbytes from it and UTF-8 decodes them. Invalid UTF-8 fails the execution (runtime.output_utf8). By convention the output is JSON.
"entrypoint": "run" and "output_length": 27.
output_length is fixed at deploy time, and the runtime reads exactly that
many bytes. Pad your buffer (trailing spaces inside a JSON string field work
well) or size the buffer to your maximum and make the JSON self-terminating.Rust example
"output_length": 256 to match the buffer. Since the runtime reads a
fixed number of bytes, make the unused tail parseable: end your JSON with a
padding string field sized to fill the buffer, for example
{"status":"ok","value":42,"pad":" "}. Consumers read the fields they
care about and ignore pad.
Host imports
All host functions live in thegora import module.
gora::http_get — allowlisted HTTP fetch
[url_ptr, url_ptr+url_len), performs a GET, writes the
response body to [out_ptr, ...), and returns the number of bytes written.
Negative return values are errors:
The capability is declared under
capabilities.http in gora.app.json
(gora init scaffolds it for you):
user@ prefixes are stripped before matching). Every call
is recorded in the execution result’s http_requests transcript — URL, host,
status code, response sha256, byte count, and elapsed ms — which is what lets
validators agree on what was fetched.
gora::input_get — read the request input
out_ptr (up to out_cap
bytes) and returns the byte length, so a WASM app can branch on its input.
Always available. Negative codes:
gora::set_output — return a variable-length result
output_length (from your manifest) at the
pointer your entrypoint returns. Call set_output(ptr, len) to return a result
whose size isn’t known at deploy time — an LLM reply, a dynamic JSON blob, an
echoed input. After calling it, the runtime returns exactly len bytes at
ptr. Always available; if you never call it, the fixed output_length applies.
gora::state_get / gora::state_put — read and write your own state
state_get reads a value your app persisted on an earlier invocation into
out_ptr (returns -1 if the key doesn’t exist). state_put buffers a write
that is committed only if the run succeeds. Together they give an app durable
memory across invocations — the foundation for a stateful agent that reads its
prior state, acts, and writes new state each run. Both require state to be
enabled (state.kv_enabled in the deploy metadata — the CLI sets this on by
default); importing them otherwise fails instantiation.
(
gora::state_put_input, which writes the input to the fixed key last_input,
still exists for backward compatibility.)
gora::llm — call a language model
out_ptr. The API key lives on
the node, never in your app — you get model access without holding secrets.
Every call is recorded in the attestation transcript (prompt and response
hashes), so the committee can see what was asked and answered. Pair it with
set_output to return the (variable-length) reply. Negative codes:
gora::llm is operator-dependent: it works only if the node you deploy to
has an endpoint configured (check with the Gora team for the public testnet). A
-61 means no model is available on that node. This is what lets a WASM app be
an AI agent: read input/state → prompt the model → act.
With input_get + state_get/state_put + llm + a
schedule, a WASM app can run as an autonomous
loop: wake on a timer, read its memory, think with an LLM, act (via http_get
or a chain callback), and persist new state — with every step attested.
Building a WASM app
You have two toolchains; both produce the.wasm that gora deploy uploads.
WAT (no extra toolchain). gora build compiles a src/program.wat file
directly to artifacts/program.wasm. gora init --language wasm scaffolds a
working WAT app (reads input, keeps a counter in state, echoes both) — edit it
and rebuild:
cdylib crate that declares the
host imports and compile to wasm32-unknown-unknown, then point the manifest’s
artifact.path at the output .wasm:
out_cap, and remember every host function returns a byte length (or a
negative error you should check). Export memory and a zero-arg entrypoint
returning an i32 pointer (see ABI contract).
A worked example: an autonomous agent
An app that, on each (scheduled) invocation, reads its input, asks a model what to do, remembers the last decision, and returns the model’s reply — attested end to end. In WAT (JSON building elided for brevity):gora::llm requires the node to have a model endpoint configured; a -61 return
means it’s unavailable there.
Limits and metering
Every execution returns metering you can see in the request record:
exit_status values: succeeded, failed (trap, fuel exhausted, bad
entrypoint signature), timed_out, policy_violation.
Sandbox runtimes, for contrast
JS/TS/Python artifacts are executed asnode <path> / python3 <path> with:
- stdin —
{"request_id": "...", "input": <your JSON>}(one envelope) - stdout — your JSON result (nothing else; logs go to stderr)
- exit 0 — success; non-zero fails the request with stderr as the error
- limits: same 1,000 ms wall clock, stdout capped at
output_length(≤ 1 MiB), stderr at 64 KiB
Math.random() if the result feeds
consensus — derive randomness from the input, like the
raffle drawer’s HMAC draw).
Validation checklist
Beforegora deploy, the package step re-checks:
- artifact exists, is non-empty, sha256 matches the recorded digest
entrypointnon-empty,output_length > 0capabilities.http.allowed_hostsentries are bare lowercase hostnames (no scheme, port, path, or leading/trailing dots)