Skip to main content
Preview
DEFINE THING is a preview feature. It ships in broker 2.22. The syntax and behaviour may still change.
For what a Thing is and a first example, see Things Overview.

Templates and roots

  • OF must name a template. Naming a root or an unknown Thing is an error, and so is a cycle.
  • Templates are stored on the broker. A root added later, from another cell, notebook or project, can use a template added earlier.
  • DEFINE THING north OF Station puts Station’s clauses directly at north_<clause>. A plain root with WITH THING station OF Station adds a level: north_station_<clause>.

Clauses

A clause is a standalone declaration with DEFINE replaced by WITH, indented one level under the Thing. Every kind keeps its full syntax, so you can paste an existing definition into a Thing: indent it one level and change DEFINE to WITH.
  • An action’s trigger, INPUT, DO and RETURN lines sit at the clause’s own level, exactly where they sit under a standalone DEFINE ACTION.
  • Python scripts and themes are not clauses.
  • Children deploy in the order connectors → models → agents → actions → rules → panels, and are removed in reverse.

Params and placeholders

For each param, the broker uses, in order: the argument at the call site, then the template’s default, then (for a required param) the value in the nearest enclosing instance that has one. Otherwise the deploy fails and names the missing param.
  • An argument must name a param the template declares, or path.
  • An argument can use the caller’s placeholders: WITH PARAM ip "{{baseIp}}.11". Defaults are literals.
  • A value may not contain ", a line break or {{.
  • Keep secrets in GET SECRET and GET ENV inside clause bodies, never in params. Params are published with the Thing’s tree.

Reserved placeholders

Substitution is textual and happens before a clause is parsed, so a placeholder can sit in a string ("{{path}}/status") or a bare position (WITH SLAVE_ID {{slave}}). Placeholders are not allowed in Thing, child, clause or param names. Write \{{ for a literal {{. Single-brace {x} (action variables, {payload}, {env.NAME}) is never touched.

Names

  • Thing, instance and clause names match [A-Za-z_][A-Za-z0-9_]*. TEMPLATE can’t be a Thing name.
  • A child is named by the instance chain and the clause name joined with _: factory_line1_press1_modbus.
  • Every expanded name is checked before anything changes. A name owned by another Thing or project is an error.
  • An existing standalone entity of the same kind and name is adopted: the expanded declaration replaces it and the root owns it from then on.

References

A clause can refer to a clause of the same instance by its local name, or to a clause of a descendant by a dotted path (p1.Assistant). The broker rewrites these to expanded names in CALL ACTION, PUBLISH MODEL, GET MODEL, STORE IN, CALL AGENT, CALL LLM, CALL MCP, USES LLM CONNECTOR, WITH AGENTS, agent tool WITH TARGET, and panel bindings.
  • A single name that matches no sibling clause passes through unchanged, so a Thing can use a connector deployed outside it.
  • A dotted reference must resolve, or the Thing is rejected.
  • Topic strings and payloads are never rewritten.
To wire siblings together, declare a reference param: WITH PARAM peer AS AGENT (or AS CONNECTOR, MODEL, ACTION, PANEL). An argument resolves in the caller, so a parent can pass WITH PARAM peer "south.Lead" to its north child.
agent-site must already exist as a broker user. LoT can’t create users.

Ownership

Children run, publish and can be called like any other entity. They are read-only except through their Thing:
-addAction, -removeConnector and the other per-entity commands are refused on a Thing-owned name. -removeAllConnectors, -removeAllModels and -removeAllActions keep owned children and list them as skipped.

Permissions

Every child is also checked against its own kind’s permission, as if you added it directly. One missing permission rejects the whole unit. Children also count toward license limits.

Troubleshooting

Last modified on October 21, 2026