Skip to main content

Create a Gora app

A Gora app starts as a local folder. You edit it locally, validate it locally, then deploy it to a Gora endpoint.

Choose a starter template

For a pure off-chain app — code that reads request input and returns JSON — the simplest starting point is the generic template with --app-kind offchain-only. It scaffolds no contract or wallet component (contracts: [] in the manifest):
When you want chain callbacks, mobile signing, or an agent wallet, start from one of the richer templates: The offchain-callback starter emits a chain_callback.operation object with structured target and params fields, and it includes a small callback contract/program scaffold for the selected chain. Its operation_schema declares allowed operation keys plus required and allowed target/params fields for the starter callback. Use the operation object for callback submitter routing; the flat target, method, and payload fields remain in the starter for compatibility with simple Dev Bridge flows. Example:
You can override the default language or app shape:
For a callback app that writes back to a developer-owned chain component:
On the public testnet every deploy must set a developer token (gora config set api.token <string>) — it is your identity, and the first token to claim an app id owns it. See Connect to the testnet for how the token works. Algorand is the live chain on the public testnet; Base and Solana callback scaffolds work against a local devnet only. Endpoints and deployed contract IDs are in Connect to the testnet. Supported languages — pick based on what your app needs: WASM is the default for the public testnet, and it is input-driven. A WASM app reads request input (gora::input_get), keeps durable state across requests (gora::state_get / gora::state_put), can call an LLM (gora::llm), fetch allowlisted HTTP (gora::http_get), and return variable-length output (gora::set_output) — all deterministic and fuel-metered. gora init --language wasm scaffolds a capable starter (reads input, keeps a counter in state, echoes it back), not an inert stub. JS, TS, and Python run unsandboxed, so they are only available on a self-hosted node — not the WASM-only public testnet. The full ABI, host functions, and build path are in the WASM runtime reference.
Prefer plain JavaScript (.mjs) over TypeScript for the deployed artifact: the node executes it with plain node, and type stripping requires Node ≥ 22. On a Node 20 host, a TypeScript artifact fails with a syntax error. Keep TS for development and ship compiled/plain JS.
Supported app kinds:
  • offchain-only — with the generic template, scaffolds no contract or wallet component
  • smart-contract-native
  • offchain-program-wallet
  • ai-agent-wallet

Generated files

A TypeScript app looks like this:
Important files:

Mobile UI

The mobile wallet renders app-specific UI as HTML only. Add a ui section to gora.app.json:
gora deploy embeds the html_path file into the manifest as ui.html (up to 1 MiB), so the page ships with the app and the bridge can serve it for apps deployed from anywhere. You can also write ui.html inline yourself, or point ui.url at a page you host. The WebView injects window.Gora so your HTML can ask native code for wallet context, read declared views, invoke the app, and open the native signer:
action() raises a native approval pop-up. On approve, the user’s wallet key signs the request and the network cryptographically verifies the signature before the action runs; your page gets the proof in a gora:actionComplete event (declines arrive as gora:actionCancelled). The full API, event shapes, and the signing protocol are in Web UI and verified signing. Keep private keys, seed phrases, RPC secrets, and algod tokens out of HTML. The HTML is a UI surface; native wallet code still owns signing.

Runtime contract

For JS, TS, and Python apps:
Do not print logs to stdout. Stdout is parsed as the app result. The envelope contains only request_id and input — no state, no capabilities, no network. Everything your app needs must arrive in input; everything it produces must go out in the result. If the result feeds consensus, keep it deterministic: no clocks, no Math.random() — derive randomness from the input (see how the raffle drawer seeds an HMAC with the round number and participant registry). Example app (src/index.mjs):
The WASM equivalent reads the same input with gora::input_get, can persist state with gora::state_get / gora::state_put, call an LLM with gora::llm, fetch allowlisted HTTP with gora::http_get, and return variable-length output with gora::set_output — exported memory, zero-arg entrypoint. See the WASM runtime reference for the full ABI and the build path.

Build

For JS, TS, and Python, this verifies that the manifest-declared artifact exists and is non-empty. For WASM, it builds or verifies artifacts/program.wasm — the module must export memory and the manifest entrypoint, and its sha256 is pinned into the package (full ABI).

Validate

Validation checks:
  • manifest and policy JSON
  • app id and package metadata
  • artifact path and runtime kind
  • allowed actions and chains
  • linked contract metadata
  • obvious secret-looking fields

Package

This writes:
The package is what gora deploy uploads to Gora.

What not to put in an app

Do not put these in source, manifest, policy, fixtures, package metadata, or stdout:
  • private keys
  • seed phrases
  • deployer keys
  • API tokens
  • RPC secrets
  • Algod tokens
Use environment variables or local dev tooling for secrets. The package should be safe to upload. Next: Add chain contracts.