> ## 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.

# LoT Concepts

> Building blocks, naming conventions, topic architecture, and proven patterns for building maintainable LoT systems

## What You Build With LoT

Everything in LoT (Language of Things) is built by defining four types of entities — **Actions**, **Models**, **Routes**, and **Rules**. Each one handles a different part of your automation, and inside them you use commands like `PUBLISH`, `SET`, and `GET` to make things happen.

<Tip>
  **Like building with LEGO blocks.** You pick the right block for the job — Actions for logic, Models for data structure, Routes for connections, Rules for security — and snap them together into a complete system.
</Tip>

### When to Use This

* You're **learning LoT** and want to understand the building blocks and how they fit together
* You need a **naming and conventions reference** for entities, variables, and topics
* You're **designing a topic hierarchy** for a new or growing system
* You want **proven code patterns** to follow — and anti-patterns to avoid

***

## In This Page

* [The Four Building Blocks](#the-four-building-blocks) — Actions, Models, Routes, and Rules
* [How They Work Together](#how-they-work-together) — A combined example
* [Working with JSON](#working-with-json) — Reading and writing structured data
* [Naming Conventions](#naming-conventions) — How to name every LoT entity
* [Topic Hierarchy Design](#topic-hierarchy-design) — Structuring your MQTT topic tree
* [Code Patterns](#code-patterns) — Proven templates for common LoT logic
* [Anti-Patterns](#anti-patterns) — Common mistakes and how to avoid them

***

## The Four Building Blocks

Everything in LoT is built from four core constructs, each created with the `DEFINE` keyword:

| Construct   | Purpose                               | Trigger Types                      |
| ----------- | ------------------------------------- | ---------------------------------- |
| **Actions** | Execute logic when events occur       | Time, Topic, System events, Manual |
| **Models**  | Structure MQTT data into JSON schemas | Topic values, Action calls         |
| **Routes**  | Connect to external systems           | Topic patterns                     |
| **Rules**   | Control who can publish/subscribe     | Client identity, Topic patterns    |

<AccordionGroup>
  <Accordion title="Actions in Detail" icon="bolt">
    Actions contain your automation logic. They run when triggered:

    | Trigger     | Syntax                 | Use Case                             |
    | ----------- | ---------------------- | ------------------------------------ |
    | Time-based  | `ON EVERY 5 SECONDS`   | Heartbeats, polling, scheduled tasks |
    | Topic-based | `ON TOPIC "sensors/+"` | React to incoming data               |
    | Callable    | No trigger clause      | Utility functions, subroutines       |
    | System      | `ON START`, `ON STOP`  | Initialization, cleanup              |

    Inside an Action, you use these commands to make things happen:

    | Command      | What it does                                                             |
    | ------------ | ------------------------------------------------------------------------ |
    | `PUBLISH`    | Send a message to a topic                                                |
    | `SET`        | Store a value in a variable                                              |
    | `GET`        | Retrieve a value from a topic or variable                                |
    | `GET ENV`    | Read an [environment variable](/lot-language/actions/operations#get-env) |
    | `GET SECRET` | Read an [encrypted secret](/lot-language/actions/operations#get-secret)  |
    | `KEEP`       | Publish and retain a message                                             |
    | `CALL`       | Execute another Action                                                   |
    | `IF/THEN`    | Conditional logic                                                        |
  </Accordion>

  <Accordion title="Models in Detail" icon="database">
    Models define JSON structures that auto-publish when their trigger fields update:

    | Field Type                        | Purpose                                        |
    | --------------------------------- | ---------------------------------------------- |
    | `STRING`, `INT`, `DOUBLE`, `BOOL` | Typed data fields                              |
    | `AS TRIGGER`                      | Field that activates the model when it changes |
    | `FROM`                            | Inherit fields from another model              |

    Models eliminate manual JSON formatting—define the schema once, and data flows automatically.
  </Accordion>

  <Accordion title="Routes in Detail" icon="route">
    Routes connect your MQTT infrastructure to external systems:

    | Category          | Examples                                  |
    | ----------------- | ----------------------------------------- |
    | **Data Storage**  | PostgreSQL, MongoDB, CrateDB, OpenSearch  |
    | **Data Pipeline** | MQTT bridges, email, HTTP webhooks        |
    | **Industrial**    | OPC-UA, Modbus, Siemens S7, Allen-Bradley |
    | **System**        | Broker clustering, replication            |

    You define *what* to connect, and the broker handles the *how*.
  </Accordion>

  <Accordion title="Rules in Detail" icon="shield">
    Rules define access control at the broker level:

    | Control                   | Description                                 |
    | ------------------------- | ------------------------------------------- |
    | **Publish permissions**   | Who can send messages to which topics       |
    | **Subscribe permissions** | Who can receive messages from which topics  |
    | **Client identity**       | Match rules to specific clients or patterns |

    Rules enforce security policies without modifying your Actions or Models.
  </Accordion>
</AccordionGroup>

***

## How They Work Together

A typical LoT solution combines all four building blocks:

```lot theme={null}
-- 1. ROUTE: Connect to PostgreSQL
DEFINE ROUTE SensorDB TO POSTGRESQL
    CONNECTION "Host=db.local;Database=iot"
    TOPIC "sensors/#"

-- 2. MODEL: Structure incoming data
DEFINE MODEL SensorReading WITH TOPIC "sensors/formatted"
    ADD STRING "device_id" WITH TOPIC "sensors/id"
    ADD DOUBLE "temperature" WITH TOPIC "sensors/raw" AS TRIGGER

-- 3. ACTION: Process and alert
DEFINE ACTION TemperatureAlert
ON TOPIC "sensors/formatted" DO
    SET "temp" WITH (GET JSON "temperature" IN PAYLOAD AS DOUBLE)
    IF {temp} > 80 THEN
        PUBLISH TOPIC "alerts/high_temp" WITH PAYLOAD

-- 4. RULE: Restrict who can publish alerts
DEFINE RULE AlertAccess
    DENY PUBLISH "alerts/#" FOR ALL
    ALLOW PUBLISH "alerts/#" FOR "system-*"
```

| Step | What Happens                                      |
| ---- | ------------------------------------------------- |
| 1    | Route stores all `sensors/#` data to PostgreSQL   |
| 2    | Model transforms raw data into structured JSON    |
| 3    | Action checks thresholds and publishes alerts     |
| 4    | Rule ensures only system clients can write alerts |

***

## Working with JSON

Most MQTT payloads in IoT systems are JSON. LoT gives you two built-in ways to handle it — **read** individual fields from incoming JSON with `GET JSON`, and **write** structured JSON output using Models.

### Reading JSON Fields

Use `GET JSON` inside an Action to extract typed values from a JSON payload. Always specify the target type with `AS` to ensure correct handling in comparisons and math.

| Syntax                                | What it does             |
| ------------------------------------- | ------------------------ |
| `GET JSON "key" IN PAYLOAD AS DOUBLE` | Extract a numeric value  |
| `GET JSON "key" IN PAYLOAD AS STRING` | Extract a text value     |
| `GET JSON "key" IN PAYLOAD AS INT`    | Extract an integer value |
| `GET JSON "key" IN PAYLOAD AS BOOL`   | Extract a boolean value  |

### Writing JSON Output

To publish structured JSON, define a COLLAPSED Model with typed fields and use `PUBLISH MODEL ... TO ... WITH` from an Action. The broker builds the JSON for you — no manual string formatting needed.

### Putting It Together

This example reads multiple fields from an incoming JSON payload and publishes them as structured output through a Model:

```lot theme={null}
DEFINE MODEL SensorDataRecord COLLAPSED
    ADD STRING "sensor_id"
    ADD STRING "sensor_type"
    ADD DOUBLE "temperature"
    ADD DOUBLE "pressure"
    ADD STRING "timestamp"
    ADD STRING "quality_status"

DEFINE ACTION ProcessSensorJSON
ON TOPIC "sensors/+/json_data" DO
    SET "sensor_id" WITH TOPIC POSITION 2

    SET "temperature_val" WITH (GET JSON "temperature" IN PAYLOAD AS DOUBLE)
    SET "pressure_val" WITH (GET JSON "pressure" IN PAYLOAD AS DOUBLE)
    SET "sensor_type" WITH (GET JSON "type" IN PAYLOAD AS STRING)
    SET "quality" WITH (GET JSON "quality_status" IN PAYLOAD AS STRING)

    PUBLISH MODEL SensorDataRecord TO "sensors/processed/" + {sensor_id} WITH
        sensor_id = {sensor_id}
        sensor_type = {sensor_type}
        temperature = {temperature_val}
        pressure = {pressure_val}
        timestamp = TIMESTAMP "UTC"
        quality_status = {quality}
```

| Step | What Happens                                                                       |
| ---- | ---------------------------------------------------------------------------------- |
| 1    | Action triggers when any sensor publishes JSON to `sensors/+/json_data`            |
| 2    | `GET JSON` extracts each field with the correct type                               |
| 3    | `PUBLISH MODEL` builds a clean JSON object and publishes it to the processed topic |

***

## Naming Conventions

Consistent naming is the single most impactful practice for maintainable LoT systems. These conventions apply to all LoT entities.

### Entity Names

All LoT entities use **PascalCase**. Names should be descriptive and purpose-driven.

| Entity           | Convention                    | Good Examples                                                    | Bad Examples                          |
| ---------------- | ----------------------------- | ---------------------------------------------------------------- | ------------------------------------- |
| Actions          | PascalCase, verb-first        | `ProcessTemperature`, `MonitorPressure`, `SendDailyReport`       | `temp_process`, `action1`, `myAction` |
| Models           | PascalCase, noun-based        | `SensorReading`, `EquipmentStatus`, `ProductionRecord`           | `sensor_model`, `Model1`, `data`      |
| Rules            | PascalCase, descriptive scope | `AllowAdminActions`, `ProtectSysTopics`, `RestrictDevicePublish` | `Rule1`, `myRule`, `newRule`          |
| Routes           | PascalCase, destination-based | `CloudBridge`, `SensorDatabase`, `AlertEmail`                    | `route1`, `my_route`, `dbRoute`       |
| Callable Actions | PascalCase, function-like     | `CalculateAverage`, `ConvertCelsiusToFahrenheit`                 | `calc`, `helper`, `util`              |

### Variable Names

Variables use **snake\_case** inside double quotes for `SET` declarations, and **curly braces** for references.

```lot theme={null}
DEFINE ACTION ProcessSensorData
ON TOPIC "sensors/+/raw" DO
    SET "sensor_id" WITH TOPIC POSITION 2
    SET "raw_value" WITH (GET JSON "value" IN PAYLOAD AS DOUBLE)
    SET "converted_value" WITH ({raw_value} * 1.8 + 32)
    PUBLISH TOPIC "processed/" + {sensor_id} + "/fahrenheit" WITH {converted_value}
```

| Context             | Convention                    | Examples                                             |
| ------------------- | ----------------------------- | ---------------------------------------------------- |
| Declaration (`SET`) | `snake_case` in double quotes | `"sensor_id"`, `"raw_value"`, `"cycle_time"`         |
| Reference           | Curly braces                  | `{sensor_id}`, `{raw_value}`, `{cycle_time}`         |
| Model fields        | `snake_case` in double quotes | `"equipment_id"`, `"runtime_hours"`, `"last_update"` |
| Action inputs       | `snake_case` after keyword    | `INPUT value AS DOUBLE`, `INPUT threshold AS DOUBLE` |

### Topic Names

Topics use **lowercase with forward-slash separators**. Multi-word segments use **snake\_case**.

Whenever possible, follow a **Unified Namespace (UNS)** format for topic naming. This enables not only proper readability but also scalability of systems.

| Pattern                     | Example                                                           | Use Case               |
| --------------------------- | ----------------------------------------------------------------- | ---------------------- |
| `domain/entity/attribute`   | `sensors/temperature/value`                                       | General data           |
| `domain/instance/attribute` | `sensors/temp001/raw`                                             | Instance-specific data |
| `domain/category/instance`  | `alerts/critical/pump03`                                          | Categorized events     |
| Prefixed namespaces         | `processed/`, `alerts/`, `config/`, `cache/`, `state/`, `system/` | Functional separation  |

***

## Topic Hierarchy Design

A well-designed topic tree is the most critical architectural decision in a LoT system. Every entity — Actions, Models, Rules, and Routes — communicates through topics. A clean hierarchy makes the entire system easier to build, debug, and extend.

### Recommended Namespace Structure

Organize topics into functional namespaces that reflect the data lifecycle:

| Namespace    | Purpose                                      | Example Topics                                       |
| ------------ | -------------------------------------------- | ---------------------------------------------------- |
| `sensors/`   | Raw incoming sensor data                     | `sensors/temp001/raw`, `sensors/+/temperature`       |
| `processed/` | Transformed or enriched data                 | `processed/temp001/fahrenheit`, `processed/+/status` |
| `alerts/`    | Threshold violations and notifications       | `alerts/critical/pump03`, `alerts/+/temperature`     |
| `config/`    | Configuration values read by Actions         | `config/setpoint`, `config/max_temperature`          |
| `state/`     | Internal persistent state (used with `KEEP`) | `state/counter`, `state/last_run`                    |
| `cache/`     | Cached values for quick reference            | `cache/temp001/last_reading`                         |
| `system/`    | Heartbeats, health checks, diagnostics       | `system/heartbeat`, `system/status`                  |
| `commands/`  | Inbound instructions from external systems   | `commands/devices/+/restart`                         |

### Wildcard Guidelines

| Wildcard | Meaning                    | Where to Use                                             |
| -------- | -------------------------- | -------------------------------------------------------- |
| `+`      | Matches exactly one level  | Action triggers, Model topics, Route mappings            |
| `#`      | Matches one or more levels | Route mappings, Rule topic patterns, broad subscriptions |

Use `+` in **Actions and Models** for precise instance matching. Use `#` in **Routes and Rules** for broad coverage.

A wildcard-triggered Action like `ON TOPIC "sensors/+/raw"` automatically resolves `+` in `GET TOPIC` and `PUBLISH TOPIC` to the same matched value — this is called **wildcard context inheritance**:

```lot theme={null}
DEFINE ACTION ProcessWithContext
ON TOPIC "sensors/+/temperature" DO
    SET "sensor_id" WITH TOPIC POSITION 2
    SET "humidity" WITH (GET TOPIC "sensors/+/humidity" AS DOUBLE)
    PUBLISH TOPIC "processed/" + {sensor_id} + "/combined" WITH {humidity}
```

In this example, if triggered by `sensors/temp001/temperature`, the `GET TOPIC` reads from `sensors/temp001/humidity` — the wildcard resolves to the same instance.

***

## Code Patterns

Proven templates and patterns for common LoT logic. Each pattern addresses a specific architectural need — use them as starting points for your own implementations.

### Actions: Single Responsibility

Each Action should do **one thing well**. Break complex logic into callable Actions with `INPUT`/`OUTPUT`:

<Tabs>
  <Tab title="Good: Modular">
    Separate reusable logic into callable Actions, then compose them in a main Action:

    ```lot theme={null}
    DEFINE ACTION ConvertCelsiusToFahrenheit
    INPUT celsius AS DOUBLE
    DO
        SET "fahrenheit" WITH ({celsius} * 9 / 5 + 32)
    RETURN
        OUTPUT fahrenheit

    DEFINE ACTION ProcessTemperatureReading
    ON TOPIC "sensors/+/celsius" DO
        SET "sensor_id" WITH TOPIC POSITION 2
        SET "temp_c" WITH PAYLOAD AS DOUBLE

        CALL ACTION ConvertCelsiusToFahrenheit
            WITH celsius = {temp_c}
            RETURN temp_f

        PUBLISH TOPIC "sensors/" + {sensor_id} + "/fahrenheit" WITH {temp_f}
    ```
  </Tab>

  <Tab title="Bad: Monolithic">
    All logic crammed into one Action, no reuse:

    ```lot theme={null}
    DEFINE ACTION DoEverything
    ON TOPIC "sensors/+/celsius" DO
        SET "sensor_id" WITH TOPIC POSITION 2
        SET "temp_c" WITH PAYLOAD AS DOUBLE
        SET "temp_f" WITH ({temp_c} * 9 / 5 + 32)
        SET "temp_k" WITH ({temp_c} + 273.15)
        SET "max" WITH (GET TOPIC "config/max_temperature" AS DOUBLE)
        SET "pct" WITH ({temp_c} / {max} * 100)
        IF {temp_c} > {max} THEN
            PUBLISH TOPIC "alerts/" + {sensor_id} WITH "High"
        PUBLISH TOPIC "sensors/" + {sensor_id} + "/fahrenheit" WITH {temp_f}
        PUBLISH TOPIC "sensors/" + {sensor_id} + "/kelvin" WITH {temp_k}
        PUBLISH TOPIC "sensors/" + {sensor_id} + "/capacity" WITH {pct}
    ```
  </Tab>
</Tabs>

### Actions: State Management

Use `KEEP TOPIC` for internal persistent state and `PUBLISH TOPIC` for external broadcast. Never use `PUBLISH` for values only your system reads:

```lot theme={null}
DEFINE ACTION PersistentCounter
ON EVERY 10 SECONDS DO
    SET "current" WITH (GET TOPIC "state/counter")
    IF {current} == EMPTY THEN
        KEEP TOPIC "state/counter" WITH 1
    ELSE
        KEEP TOPIC "state/counter" WITH ({current} + 1)
    PUBLISH TOPIC "stats/count" WITH (GET TOPIC "state/counter")
```

| Operation       | Use When                                                                |
| --------------- | ----------------------------------------------------------------------- |
| `PUBLISH TOPIC` | Other systems or subscribers need to see the value                      |
| `KEEP TOPIC`    | Only your own Actions read the value (internal state, caches, counters) |

### Models: Trigger Selection

Mark the **primary data field** as `AS TRIGGER` — never a timestamp or metadata field. The trigger determines when the model publishes, so it should fire when new meaningful data arrives:

```lot theme={null}
DEFINE MODEL SensorReading WITH TOPIC "sensors/formatted/temperature"
    ADD STRING "sensor_id" WITH "TEMP001"
    ADD DOUBLE "value" WITH TOPIC "sensors/raw/temperature" AS TRIGGER
    ADD STRING "unit" WITH "celsius"
    ADD STRING "timestamp" WITH TIMESTAMP "UTC"
```

Use **COLLAPSED models** when you need full control over publishing timing and destination — the Action decides when and where to publish:

```lot theme={null}
DEFINE MODEL AlarmRecord COLLAPSED
    ADD STRING "alarm_id"
    ADD STRING "equipment_id"
    ADD STRING "severity"
    ADD STRING "timestamp"

DEFINE ACTION ProcessAlarm
ON TOPIC "alarms/+/raw" DO
    SET "equip_id" WITH TOPIC POSITION 2
    PUBLISH MODEL AlarmRecord TO "alarms/structured/" + {equip_id} WITH
        alarm_id = (RANDOM UUID)
        equipment_id = {equip_id}
        severity = (GET JSON "severity" IN PAYLOAD AS STRING)
        timestamp = TIMESTAMP "UTC"
```

### Models: Inheritance for Consistency

When you have a family of related data types, define a base COLLAPSED model and extend it:

```lot theme={null}
DEFINE MODEL BaseAlert COLLAPSED
    ADD STRING "alert_id"
    ADD STRING "timestamp"
    ADD STRING "severity"
    ADD STRING "message"

DEFINE MODEL TemperatureAlert FROM BaseAlert
    ADD DOUBLE "temperature_value"
    ADD DOUBLE "threshold"
    ADD STRING "sensor_location"

DEFINE MODEL PressureAlert FROM BaseAlert
    ADD DOUBLE "pressure_value"
    ADD DOUBLE "max_safe_pressure"
    ADD STRING "system_affected"
```

### Rules: Priority and Scope

Rules are evaluated by **priority** (lower number = higher priority). Structure them as specific deny rules first, then broader allow rules:

```lot theme={null}
DEFINE RULE ProtectSysTopics WITH PRIORITY 10 FOR PublishSys
    IF USER IS "root" THEN
        ALLOW
    ELSE
        DENY

DEFINE RULE AllowDevicePublish WITH PRIORITY 50 FOR Publish TO TOPIC "sensors/#"
    IF USER HAS AllowedSensorPublish THEN
        ALLOW
    ELSE
        DENY
```

| Practice        | Guideline                                                                  |
| --------------- | -------------------------------------------------------------------------- |
| Priority 1–20   | Critical deny rules (system protection)                                    |
| Priority 21–50  | Specific allow rules (per-feature access)                                  |
| Priority 51–100 | General allow rules (broad access)                                         |
| Condition style | Prefer `USER HAS <permission>` over `USER IS "<name>"` for maintainability |

### Routes: Clear Configuration

Name route mappings descriptively and group related mappings within a single route definition:

```lot theme={null}
DEFINE ROUTE CloudSync WITH TYPE MQTT_BRIDGE
    ADD SOURCE_CONFIG
        WITH BROKER SELF
    ADD DESTINATION_CONFIG
        WITH BROKER_ADDRESS "iot.cloudprovider.com"
        WITH BROKER_PORT '8883'
        WITH CLIENT_ID "EdgeDevice-Factory1"
        WITH USE_TLS true
    ADD MAPPING sensorData
        WITH SOURCE_TOPIC "sensors/#"
        WITH DESTINATION_TOPIC "factory1/sensors/#"
        WITH DIRECTION "out"
    ADD MAPPING inboundCommands
        WITH SOURCE_TOPIC "local/commands/#"
        WITH DESTINATION_TOPIC "factory1/commands/#"
        WITH DIRECTION "in"
```

***

## Anti-Patterns

These are the most common mistakes in LoT development. Avoid them whether you're writing new code or reviewing existing output.

| Anti-Pattern                         | Why It's Bad                                                               | Do This Instead                                                          |
| ------------------------------------ | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Omitting type casts in math          | Implicit type handling leads to silent errors                              | Always cast: `PAYLOAD AS DOUBLE`, `GET TOPIC ... AS DOUBLE`              |
| Triggering models on timestamps      | Timestamps update every tick — model fires constantly                      | Trigger on the primary data field: `AS TRIGGER` on value, not timestamp  |
| Inconsistent naming across models    | `sensorID` in one model, `sensor_id` in another                            | Standardize on `snake_case` for all field names                          |
| Using Python for native LoT tasks    | Python adds overhead for simple publish/get/if operations                  | Use Python only for math libraries, ML, API calls, or complex parsing    |
| Leaving `$SYS/#` topics unrestricted | System topics contain broker commands and sensitive data                   | Create a priority-10 Rule restricting `PublishSys` and `SubscribeSys`    |
| Generic entity names                 | `Action1`, `Rule1`, `MyRoute` are meaningless in a system with 50 entities | Name by purpose: `MonitorPressure`, `ProtectSysTopics`, `SensorDatabase` |
| `PUBLISH` for internal state         | Broadcasts data that only your own Actions need                            | Use `KEEP TOPIC` for internal state; `PUBLISH` for external subscribers  |
| Monolithic Actions                   | One Action doing 15 things is hard to test and debug                       | Split into callable Actions with `INPUT`/`OUTPUT`                        |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Write Your First Action" icon="bolt" href="/lot-language/actions/syntax">
    Learn the full syntax for triggers, variables, and control flow.
  </Card>

  <Card title="AI-Assisted Development" icon="robot" href="/ai/best-practices">
    Set up your AI assistant with LoT conventions using AGENTS.md templates.
  </Card>
</CardGroup>
