> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Thing Syntax

> DEFINE THING reference: templates and roots, clauses, params and placeholders, names, references, ownership and permissions (preview)

Preview

> [!WARNING]
>
> `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](/latest/lot-language/things/overview).

## Templates and roots

| Declaration | Meaning |
| - | - |
| `DEFINE THING TEMPLATE <name>` | A template. Stored, never deployed by itself |
| `DEFINE THING <name>` | A root. Adding it deploys its whole instance tree |
| `DEFINE THING <name> OF <template>` | A root that is one instance of a template. Only `WITH PARAM` lines may follow |

* `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`.

| Clause | Expands to |
| - | - |
| `WITH CONNECTOR <name> WITH TYPE <type>` (or `WITH ROUTE …`) | `DEFINE CONNECTOR` |
| `WITH MODEL <name> …` | `DEFINE MODEL` |
| `WITH ACTION <name>` | `DEFINE ACTION` |
| `WITH RULE <name> WITH PRIORITY n FOR …` | `DEFINE RULE` |
| `WITH AGENT <name>` | `DEFINE AGENT` |
| `WITH PANEL <name>` | `DEFINE PANEL` |
| `WITH THING <child> OF <template>` | One more instance of the template |
| `WITH PARAM <name> [value]` | Nothing. Declares a param |

* 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

| Where | `WITH PARAM ip "10.0.0.5"` means | `WITH PARAM ip` means |
| - | - | - |
| In a template | A default | A required param |
| In a plain root | Its value | An error: a root param needs a value |
| Under `WITH THING … OF T` or `DEFINE THING r OF T` | An argument to `T` | An error |

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

| Placeholder | Value |
| - | - |
| `{{thing}}` | The instance's own name |
| `{{parent}}` | The parent instance's name. Empty at the root |
| `{{path}}` | A topic prefix. By default the instance chain joined with `/` (`factory/line1/press1`). Set `WITH PARAM path "plant/north"` on a root to change it |

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.

```lot wrap theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE THING TEMPLATE Cell
    WITH PARAM llm AS CONNECTOR
    WITH PARAM agentUser
    WITH PARAM peer AS AGENT
    WITH AGENT Lead
        USES LLM CONNECTOR {{llm}}
        WITH DEFAULT_USER "{{agentUser}}"
        WITH AGENTS "{{peer}}"
        WITH INTERACTION_TOPIC "{{path}}/lead"
        ADD TOOL "read_mqtt"
            WITH TOPICS "{{path}}/#"

DEFINE THING site
    WITH PARAM agentUser "agent-site"
    WITH CONNECTOR Brain WITH TYPE LLM
        ADD LLM_CONFIG
            WITH PROVIDER "anthropic"
            WITH MODEL "claude-sonnet-4-0"
            WITH API_KEY GET SECRET "ANTHROPIC_API_KEY"
    WITH THING north OF Cell
        WITH PARAM llm "Brain"
        WITH PARAM peer "south.Lead"
    WITH THING south OF Cell
        WITH PARAM llm "Brain"
        WITH PARAM peer "north.Lead"
```

`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:

```text theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
Action 'Oven_inlet_Overheat' is owned by THING Oven (instance Oven/inlet, clause Overheat); edit the Thing instead.
```

`-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

| Operation | Permission |
| - | - |
| `-addThing` | `ThingManagementCreation` |
| `-removeThing` | `ThingManagementRemove` |
| `-listThings` | `CommandCall` |

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

| Symptom | Fix |
| - | - |
| `… is owned by THING …; edit the Thing instead.` | Change the clause and redeploy the Thing |
| `Template 'X' is used by …` on `-removeThing` | Remove or change the Things that use it first |
| `OF` error naming a root | Mark the target `DEFINE THING TEMPLATE` |
| Unresolved placeholder | Declare the param, or pass it |
| A child error points at the Thing line | Run `-addThing --dryRun` and read that child's expanded `code` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.