Skip to content

Build · Service commands

Service commands

Declare commands in a service contract, implement their hooks, and expose selected commands in the unit. Write a service contains a complete counter example; no handwritten protobuf or presentation metadata is needed.

01 · Declare and expose

# service.toml
[service.commands.increment]
returns = { count = "uint32" }
# Within the unit's selected service table
expose_commands = ["increment"]

Omitted/empty exposure lists expose nothing. args and returns default to empty tables. An empty result still reports handler completion. Use build.lifecycle = "hooks" and scaffold-service for the current workflow.

Field type Example
Unsigned integer "uint32"
Bounded signed integer { type = "sint32", min = -10, max = 10 }
Boolean "bool"
Bounded UTF-8 string { type = "string", max_bytes = 32 }
Service-local named type "Color"
Enum { type = "enum", choices = ["red", "blue"] }

Strings require a bound and cannot contain NUL. See Declarations for reusable types. Generated headers are the authority for C hook signatures. Handlers return MICROS_OK with populated outputs or a MICROS_ERROR_* result. They run on the service worker and must finish in bounded time.

02 · Transport budget

Each exposed service adds request and response streams and shares a network-bridge service with other exposed services. This consumes two channels per exposed service and a bridge slot. Channels have depth 4 and 112-byte capacity including the envelope; generation rejects an oversized worst-case protobuf. The format ceilings are listed once in Declarations.

Command schemas permit eight commands, eight fields per argument/result table, eight named types, 1–64 UTF-8 bytes per string, and 1–32 enum choices. Exact validation lives in the command implementation.

The current transport supports ESP32 bases with authenticated IPC bridging and boot/service instance identities. Update the CLI and server for command metadata; a base lacking the required identity support rejects calls.

03 · Call and interpret the result

After deployment, open the shell:

commands
help call example.counter increment
call example.counter increment

All declared arguments are required. Use FIELD=VALUE, quote strings with spaces, and use enum names or true/false for booleans. Completion means the handler returned successfully; it is not independent evidence of a physical effect.

Errors return a failed result. Disconnects, lost replies, or interrupted waits can leave an unknown outcome. Writes are not automatically retried; Ctrl-C stops waiting but may not stop a dispatched handler. Inspect state before repeating a physical action. Request deduplication is not durable exactly-once execution.

Legacy manual lifecycle handlers remain supported in the typed-command example. Their scaffold-commands and schema assertions are not required by the hook workflow.