Skip to content

Reference · Declarations

Declarations

A service contract owns types and requirements. A unit owns physical choices. Generated C and metadata are build output. This page documents current-dev TOML; legacy composition is separate.

01 · File ownership

File Owns
services/NAME/service.toml Build inputs, types, config defaults, pins, timers, ports, commands
units/HARDWARE_ID.toml Hardware ID, board, selected services, config values, pin assignments, channels, exposure, budgets
Shared interface.toml and .proto Message identity, delivery policy, fields and wire tags

Contract source paths are relative to the contract; unit source paths are relative to the unit. contract = "service.toml" resolves inside its source directory. build.api is a stable C identifier independent of the instance alias. Multiple instances need distinct deployed names. Interfaces and codecs are generated under build/services/; physical bindings live under build/units/.

02 · Settings and reusable types

# service.toml
[service.types.Brightness]
type = "uint32"
min = 0
max = 255

[service.config]
brightness = { type = "Brightness", default = 8 }

[service.commands.set_brightness]
args = { brightness = "Brightness" }
returns = { brightness = "Brightness" }

The unit selects config = { brightness = 32 } in that service's table. Omitted values use service defaults; values remain fixed for a service lifetime. Startup settings support uint32 and enum; commands have additional types listed in Service commands. Type references cannot override bounds. Enum order is numeric identity, so reordering can change behavior.

Larger service-local types can live in a types.toml selected by the service's types field. The file must stay within that service directory. Use inline types or that file, not duplicate definitions. Types in another service are independent; shared messages use explicit interface references.

03 · Pins and timers

# service.toml
[service]
pins = { button = { type = "pullup" }, temperature = { type = "analog" } }
timers = ["sample"]
# unit file
[services.sensor]
source = "../services/sensor"
contract = "service.toml"
pins = { button = 38, temperature = 34 }
memory = { arena = "RAM", stack_bytes = 4096 }

This is a declaration example; choose actual pins using ./micros pins --all and your board's wiring. Supported types are input, pullup, pulldown, output, and analog. Every role needs one assignment. Reserved pins, physical conflicts, unsupported types, and driver fixed_pin violations fail validation. Disabled new-style services allocate no pins.

Pull requirements need verified board bias or supported runtime configuration; the current ESP32 graph cannot encode internal pulls. Analog input requires an available ADC1 mapping and returns raw 12-bit counts with the ABI's 12 dB attenuation, not calibrated voltage. Exact target capabilities are in chip/board definitions.

Named timers reserve handles, not schedules. Code chooses delay, period, and callback using the generated timer helper. Hook lifecycles close these handles; manual ABI lifecycles own their cleanup. See Write a service.

04 · Channels

Connect services is the complete same-name port/channel example. The shared interface owns protobuf and delivery policy. The unit declares channel names and optional depth, not copies of schema, policy, keys, or versions. Enabled ports attach by name; unrelated groups need distinct names.

Composition rejects mismatched schema or delivery requirements. It derives bounded payload capacity and deterministic numeric keys in sorted channel-name order. Those keys are release-local, not stable external addresses. An explicit depth below a connected minimum fails; omitted depth uses the greatest minimum (default 1). latest always requires depth 1. Plain channels permit multiple publishers and subscribers; coordination has its additional endpoint limits.

05 · Limits and validation

Current format ceiling Limit
Services, including bridges 4
Local channels 4
Stream depth 8 messages
Encoded local message 256 bytes
Handles per service 8
Encoded startup configuration 256 bytes

Target/base admission can be tighter. Arena, stack, executable image, and shared channel storage are separate budgets; see Service API and package compatibility.

./micros generate
./micros compose
./micros contract-diff units/HARDWARE_ID.toml --base HEAD

generate needs no board; compose validates the unit offline. Neither executes service behavior. contract-diff reads an explicit Git baseline; external SDK references use the current dependency checkout, so compare SDK changes there.

Legacy config_schema, hardware/timer tables, and keyed channels remain readable; do not mix old and new definitions of the same setting/resource. Command metadata format 2 requires compatible CLI/server readers; update those before deploying it to an older server. Source validators: declarations, service contracts, and packages.