Edge Agent Configuration
The agent is configured with a single YAML file, config.yaml, that it reads on startup. By default it looks for config.yaml in the working directory; you can also pass a path as the first command-line argument.
Because the file contains your API key, restrict its permissions:
chmod 600 config.yaml
Example config.yaml
# ── Cloud connection (required) ──────────────────────────────
cloud_api_url: "https://api.haltless.io"
api_key: "YOUR_API_KEY"
# ── Collection behaviour ─────────────────────────────────────
collection_interval_seconds: 10 # how often to poll every source
batch_size: 100 # readings per HTTP request (max 1000)
# ── Local buffer (offline resilience) ────────────────────────
buffer_dir: "/var/lib/haltless-agent"
max_buffer_size_mb: 512
# ── TLS / logging ────────────────────────────────────────────
ca_cert_path: null # custom CA bundle, or null for defaults
log_level: "INFO"
# ── Data sources (one entry per System) ──────────────────────
sources:
- type: modbus
system_identifier: "CNC-001"
connection:
host: "192.168.1.100"
port: 502
registers:
- address: 0
count: 1
register_type: holding
metric_name: temperature_celsius
unit: celsius
scale: 0.1
offset: 0.0
- type: opcua
system_identifier: "ROBOT-002"
connection:
endpoint_url: "opc.tcp://192.168.1.200:4840"
username: ""
password: ""
nodes:
- node_id: "ns=2;i=1001"
metric_name: spindle_speed_rpm
unit: rpm
- type: csv
system_identifier: "PRESS-003"
connection:
directory: "/data/press-exports"
delimiter: ","
col_timestamp: "timestamp"
col_metric: "metric_name"
col_value: "value"
col_unit: "unit"
- type: json
system_identifier: "CONVEYOR-004"
connection:
directory: "/data/conveyor-exports"
key_timestamp: "timestamp"
key_metric: "metric_name"
key_value: "value"
key_unit: "unit"
Top-level keys
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
cloud_api_url | string | Yes | Base URL of the Haltless API, e.g. https://api.haltless.io. Must start with http:// or https://. | |
api_key | string | Yes | Your account API key. Sent as the X-API-Key header on every request. Must not be empty. | |
collection_interval_seconds | integer | No | 10 | How often, in seconds, to poll every configured source. Minimum 1. |
batch_size | integer | No | 100 | Number of readings to accumulate before sending one HTTP request. Range 1–1000. When a cycle collects more than one batch (an MQTT source can), it sends full batches while half the collection interval lasts and buffers the rest locally. |
sources | list | No | [] | One entry per machine. See Sources below. |
ca_cert_path | string | No | null | Path to a custom CA certificate bundle for HTTPS verification. Leave null to use the system trust store. If set, the file must exist. |
log_level | string | No | INFO | Log verbosity. One of DEBUG, INFO, WARNING, ERROR, CRITICAL. |
max_buffer_size_mb | integer | No | 512 | Maximum size of the on-disk buffer. When exceeded while offline, the newest readings are dropped to keep storage bounded. Minimum 1. |
buffer_dir | string | No | "." | Directory for the local buffer file. See Buffer directory. |
Buffer directory
buffer_dir is where the agent keeps its crash-safe local buffer (buffer.db) and a small file that stores the agent's stable identity. Point it at a writable directory the agent's service user owns, such as /var/lib/haltless-agent.
The directory must be a normal data location. System directories (for example /etc, /root, /proc, /sys, /dev, /boot, and core binary paths) are rejected at startup. Use a project-owned path instead.
The buffer only holds readings that could not be delivered immediately. In normal operation it stays small or empty. Two limits keep it bounded:
- Size cap:
max_buffer_size_mb. When the buffer reaches this size while offline, the newest readings are dropped. - Retention: buffered readings older than about 24 hours are evicted automatically, even if the size cap has not been reached.
TLS verification
The agent always verifies the TLS certificate of the Haltless API. Leave ca_cert_path as null to use your system's default trust store, which is correct for the standard https://api.haltless.io endpoint. Only set ca_cert_path if you connect through an endpoint that presents a certificate signed by a private or internal certificate authority; in that case, point it at that CA's bundle.
Sources
Each entry in sources describes one System (one connection the agent reads, such as a PLC or a folder of exports) and how to read it. On the portal, machines are built from a System's tags on the Systems page. Every source has the same three top-level fields:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Collector type: modbus, modbus_rtu, opcua, s7, ethernet_ip, melsec, fins, ads, bacnet, mqtt, sparkplug_b, pi_web_api, csv, or json. These are the agent's own names; a System's protocol on the portal and API spells three of them differently (modbus_tcp, opc_ua, bacnet_ip). |
system_identifier | string | Yes | The System this source is, as shown on the portal's Systems page. 1–255 characters, case-sensitive, must match exactly. machine_identifier, its earlier name, still works. |
connection | map | Yes | Collector-specific settings. See Protocols for the full shape of each. |
The connection block differs per collector. In brief:
- Modbus:
host,port, and a list ofregistersto read. - Modbus RTU: the serial
port, itsbaudrate,parity, andstopbits, a defaultunit_id, and a list ofregisters, as for Modbus. - OPC-UA:
endpoint_url, optionalusername/password, and a list ofnodesto read. - Siemens S7:
host,slot(and optionallyrack), and a list oftags, each with anaddresssuch asDB10.DBD4:REAL. - EtherNet/IP:
host,controller(logixorslc), optionallyslot, and a list oftags, each a Logix tag name or an SLC data-file address. - Mitsubishi MELSEC:
host, the MC protocol connection'sport, optionallyxy_numbering, and a list oftags, each a deviceaddresssuch asD100. - Omron FINS:
host, optionally the node numbers, and a list oftags, each anaddresssuch asD100orCIO0.03. - Beckhoff ADS:
host, optionallyams_net_idandams_port, and a list oftags, each a variable'ssymbolsuch asMAIN.speed. - BACnet/IP: the device's
hostand a list ofpoints, each anobjectsuch asanalog-input,1and optionally aproperty. - MQTT: the broker's
hostand thetopicsto subscribe to, optionally a login, TLS files,hold_seconds, andunitsby topic. - Sparkplug B: the broker settings as for MQTT, and a
group_id, optionally narrowed to anedge_node_idanddevice_id. - AVEVA PI Web API: the
base_url, a login, and a list ofpoints, each a PI point or AF attributepath. - CSV: a watched
directoryplus the column names to map (col_timestamp,col_metric,col_value,col_unit). - JSON: a watched
directoryplus the key names to map (key_timestamp,key_metric,key_value,key_unit).
See Protocols for every connection key, its default, and how each maps to a reading.
What a reading contains
Whatever the source, the agent produces readings with the same fields and sends them to Haltless:
| Field | Required | Notes |
|---|---|---|
machine_identifier | Yes | The source's system_identifier. Sent under this name for compatibility; the API accepts system_identifier too. |
timestamp | Yes | ISO 8601, in UTC. |
metric_name | Yes | Up to 255 characters. |
value | Yes | A finite number. NaN and infinite values are skipped. |
unit | Yes | Up to 50 characters. Readings with an empty unit are skipped. |
raw_tag | No | Original source tag or address, up to 500 characters. |
Overly long metric_name, unit, or raw_tag values are truncated to their limits and logged, rather than rejected.
Timestamps
Timestamps must be in UTC. For the PLCs and controllers (Modbus TCP and RTU, OPC-UA, S7, EtherNet/IP, MELSEC, FINS, ADS, and BACnet/IP), the agent stamps each reading with the collection time in UTC. For MQTT, a reading carries the message's own time when it has one (one more than two minutes ahead of the agent's clock is replaced by the arrival time), else the time it arrived; a value sent again from the hold carries the cycle's time. Sparkplug B works the same way with each metric's own timestamp. For PI Web API, a reading keeps the time PI recorded it. For CSV and JSON, the timestamp comes from your exported data. If a value has no timezone offset, the agent assumes UTC and logs a one-time warning. Keep the agent host's clock synchronized (for example with NTP) so timestamps line up with the rest of your data.
Next steps
- Protocols: the full connection reference for each collector
- Troubleshooting: buffering, auth, and clock issues
- API keys: create and rotate the key used by
api_key