Extend the bot
Start with the built-in commands, then add your own in Lua, test them on your computer and install them on a node without reflashing.
Follow a tutorial: write, install and message a command
Install your Lua file over LoRa · Management admin commands over RF
Built-in commands
Send commands to the bot in a private message. !help lists the commands you may use; !help remember shows arguments and an example. Your permissions and the node's settings decide which commands are available.
| Try | What it does | Needs |
|---|---|---|
!ping, !about, !help, !test | Check the connection, meet the bot and list commands. | Advertise your companion first, then send a private message. |
!calc, !convert, !roll, !choose | Calculate, convert units and make random choices. | Also works in channels the operator enables. |
!status, !signal, !air, !path, !trace | Show node status, signal, shared airtime and route. | Local reflections are labelled apart from RF receptions. |
!remember, !recall, !forget, !notes | Keep personal notes across restarts. | A private message keeps notes in your own space. |
!remind, !reminders, !cancel | Schedule and manage private reminders. | Owner grant and trusted time; delivery needs a fresh direct route. |
!board | Share notes in a channel. | A shared-storage grant and the channel's command/thread policy. Address the bot for channel operations. |
!weather, !service | Call an approved web service. | HTTPS configuration and a network grant on ESP32 or a Go host. |
Write a Lua command
The standalone ESP32, the Go host worker and Pine all run Lua 5.5.1 with the same command, storage and mesh API. A command is a function plus a declaration:
function hello(name)
return "Hello " .. name
end
command("hello", "name:string:32", "Greet a name",
"hello", "public", "!hello mesh")
The declaration gives the command name, typed arguments, help text, handler, permission and an example. The bot checks the arguments and builds the !help entry for you. A returned string becomes the reply.
Install the program on a managed ESP32, a Go host or Pine, then send !hello mesh and expect Hello mesh. One editable program holds up to 4,096 bytes and can contain several commands and modules.
Wrap a built-in: override_command(name, export) lets your handler replace a built-in command, and call_original(name[, argument_text]) runs the original with the same caller and permissions. Restoring the previous program leaves stored data in place. See replacing or wrapping a built-in.
Test a command on your computer
The local runner runs your program with the real bot runtime, an in-memory radio and a test web service. It does not flash, connect to a radio or transmit. From the source repository:
printf '!hello mesh\n' | make -s -C firmware/esp32 bot-local \
SOURCE=firmware/runtime/plugins/template.lua
Expected: the last JSON line contains "text":"Hello mesh". The first run builds the runner and fetches its pinned dependencies.
To check replies automatically, run a scenario; the runner exits nonzero when an assertion fails:
make -s -C firmware/esp32 bot-local \
SOURCE=firmware/runtime/plugins/template.lua \
SCENARIO=firmware/runtime/plugins/examples/hello.scenario.json
Lua API at a glance
| Build with | API | Use it for |
|---|---|---|
| Commands and modules | command, program-local modules | Named handlers, typed arguments, help, aliases and cooperating functions. |
| Replies and messages | reply, mesh.compose/send/wait | Replies to the caller, messages to permitted destinations and waiting for results. The node handles keys and packet encoding. |
| Routes and forwarding | mesh.trace/multitrace/forward | Route diagnostics and permitted message forwarding, with attribution, expiry and loop checks. |
| State and events | kv, events.on, timer, reminder | Private or shared stored data, event handlers, timers and reminders. Jobs pause while waiting so other work can run. |
| Utilities and services | utility, json, node.report | Calculations, conversions, JSON and diagnostics. ESP32 HTTPS builds and the Go host add approved web requests. |
Programs run within instruction, time, memory and job limits. Pine has smaller limits; see operating Pine. The Lua API reference lists every function and the grants it needs. Willow uses the native Lua/Wasm worker or a smaller all-Hew bot; check its service guide for program management and available host controls.
C and Rust with Wasm
An ESP32-S3 with PSRAM and the Linux x86-64 host worker can run one Wasm package alongside the Lua program, using the WAMR 2.4.1 interpreter. Both share the bot identity, permissions, stored data and radio queue.
- The C/Rust SDK's
meshcore_v1interface covers commands, events, replies and waiting for node operations. - Each package fits in 4,096 bytes. Examples include arithmetic, private notes and a configured RPC call.
- Package installation detects Wasm automatically. Use
--runtime wasmfor status, readback, rollback or removal; lifecycle commands default to Lua. - Installing a package cancels pending jobs in that runtime and keeps stored data. The other runtime keeps running.
Run source api runtimes on the node to check that it includes Wasm before uploading a package.
Call a web service from the mesh
ESP32 HTTPS builds and the Go host worker provide http.get, http.post and rpc.call to endpoints the operator approves. Credentials stay in the node's protected storage, out of reach of Lua and Wasm programs.
- TLS checks the certificate authority, hostname and certificate dates against trusted time.
- Each program needs a network grant. Requests are limited by queue time, size and rate: two per caller and four in total per minute.
- Package fetch checks an expected SHA-256 before installing.
Start with the reference service, which provides health, echo and weather endpoints, or configure your own.