runtime.toml Retain, Watchdog, And Fault Configuration¶
runtime.toml defines how one truST runtime instance executes, exposes control
surfaces, and participates in discovery, mesh, and runtime-cloud workflows.
Unknown fields are rejected. The file is validated by the same schema path used
by trust-runtime validate, the browser IDE, and runtime startup.
This is the main reference for runtime retain policy, watchdog settings, and runtime fault policy.
Minimal Example¶
[bundle]
version = 1
[resource]
name = "main"
cycle_interval_ms = 100
[runtime.control]
mode = "production"
[runtime.log]
level = "info"
[runtime.retain]
mode = "none"
save_interval_ms = 1000
[runtime.watchdog]
enabled = false
timeout_ms = 5000
action = "halt"
[runtime.fault]
policy = "halt"
Core Sections¶
[bundle]¶
| Key | Type | Required | Notes |
|---|---|---|---|
version |
integer | yes | Must currently be 1. |
[resource]¶
| Key | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | Logical resource/runtime name. Must not be empty. |
cycle_interval_ms |
integer | yes | Main scan interval in milliseconds. Must be >= 1. |
All millisecond values converted to the runtime's signed-nanosecond duration
must also be no greater than 9223372036854 (i64::MAX / 1_000_000); larger
TOML integers are rejected rather than overflowing or panicking.
Documented defaults apply only when an optional field is omitted. An explicit path, listen/connect endpoint, interface, producer path, symbol pattern, or version entry must remain nonempty after trimming. Lists and maps reject blank entries rather than silently dropping them, and an explicit blank scalar is not replaced by its default.
Optional task overrides:
[[resource.tasks]]
name = "Fast"
interval_ms = 10
priority = 1
programs = ["Main"]
single = "Main"
Each task needs:
nameinterval_ms >= 1priority- at least one entry in
programs - optional
single
Runtime Sections¶
[runtime]¶
| Key | Type | Required | Default | Notes |
|---|---|---|---|---|
execution_backend |
string | no | none | Selects nothing: every runtime runs the compiled program image. vm and native load, so a project written before still starts; interpreter and any other value are refused. |
[runtime.control]¶
| Key | Type | Required | Default | Notes |
|---|---|---|---|---|
endpoint |
string | no | platform default | unix://... or tcp://.... Omit it to use the default described below. Must not be empty when set. |
auth_token |
string | no | none | Required for an explicit tcp:// endpoint. Omit it with the default: on Windows the runtime then generates a token for each start. |
mode |
string | no | production |
production or debug. |
debug_enabled |
bool | no | derived | Defaults to true in debug mode and false in production. |
Default control endpoint and the endpoint file¶
When endpoint is omitted, the runtime uses a default for its platform. The default never collides
with another project:
- On Unix-like systems it is
<project>/.trust-runtime/control.sock. When that path is longer than the platform allows (108 bytes on Linux, 104 on macOS), or when the runtime cannot create the.trust-runtimefolder (a read-only or foreign project folder), the socket istrust-<hash>.sockin a per-user runtime directory:$XDG_RUNTIME_DIRwhen it belongs to you and is closed to other users (mode 0700), elsetrust-runtime-<uid>in the system temporary directory, which the runtime creates with mode 0700 and which must pass the same checks.<hash>is the first 16 hexadecimal digits of the SHA-256 of the canonical project path. - On Windows it is
tcp://127.0.0.1:0: a free loopback port chosen at each start, with a token generated for that start.
After it binds the endpoint, the runtime writes <project>/.trust-runtime/control.json with the bound
endpoint and its process ID. On Unix-like systems the file has mode 0600 and also holds a token that
the runtime generated. On Windows a generated token never goes into the project folder: it is kept
in %LOCALAPPDATA%\trust\runtimes\<hash>.json, which only your Windows user can read. The runtime
removes its files when its process ends cleanly: after Ctrl+C, SIGINT or SIGTERM, and after
ctl shutdown --exit. A plain ctl shutdown stops the program and keeps the runtime, its control
endpoint and its files.
A runtime that cannot write the endpoint file still starts: the PLC program never depends on a
tooling file. It logs the warning control_endpoint_file_unavailable, records an event of the same
name, and status reports endpoint_file as unavailable with the reason.
A client resolves the endpoint in this order: an explicit --endpoint, the endpoint file, the
configured endpoint, then the platform default. An endpoint file that you cannot read, for
example one that a runtime running as root wrote, is skipped with its reason, so a configured
endpoint and token still work. If nothing listens at the endpoint from the file or at the platform
default, the client reports that the runtime is not running; it does not fall back to another
endpoint.
trust-runtime ctl --project <dir> endpoint --json prints the resolved endpoint, its token and its
source, so editors and scripts do not need to read the files. Every failure is printed as
{"error":{"code":"<code>","message":"<text>"}} with a non-zero exit:
| Code | Meaning |
|---|---|
no_project |
--project is missing, or the folder does not exist |
invalid_argument |
--endpoint was given |
invalid_config |
runtime.toml cannot be loaded |
endpoint_file_invalid |
the endpoint file is damaged or from a newer version |
endpoint_file_unreadable |
the endpoint file cannot be read and nothing else names an endpoint |
not_running |
the Windows default has no endpoint file: the runtime is not running |
default_unavailable |
the per-user runtime directory is not private, or the socket path is too long |
[runtime.log]¶
| Key | Type | Required | Notes |
|---|---|---|---|
level |
string | yes | error, warn (warning), info, debug, or trace, case-insensitively. Empty and unknown values are rejected. |
[runtime.events]¶
| Key | Type | Required | Default | Notes |
|---|---|---|---|---|
capacity |
integer | no | 1,000 on a computer; on a chip, its chip file's default (200 on the ESP32-C6, 1,000 on the STM32U5A9) | The newest events the event store keeps, from 1 through 1,000,000. A newer event displaces the oldest. A chip refuses a value above its chip file's maximum (200 on the ESP32-C6, 1,000 on the STM32U5A9). status reports it as event_capacity. |
[runtime.identify]¶
| Key | Type | Required | Default | Notes |
|---|---|---|---|---|
output |
string | no | none | The output that identify blinks, in one of two forms. An I/O address written exactly %QX<byte>.<bit> in decimal without leading zeros, such as "%QX0.7": one bit of the output process image, which the runtime sets in a copy of the image it hands to the I/O drivers, so an I/O driver of io.toml must carry that bit to the real output. A GPIO line written gpio:<line> in decimal, such as "gpio:17": a line of the chip that the first enabled gpio driver names, or of /dev/gpiochip0 without one, which the runtime requests as an output and drives directly. Spaces around the value do not matter. Another form, an empty string and an unknown key are rejected. |
A section without output means the runtime has no identify output, and identify answers
identify_not_configured. The program must not use the output: a project whose program binds the
bit, whose io.toml maps the GPIO line, or whose address lies beyond the output image is refused by
run, play, validate, check and ide serve, and by a restart that reloads a stopped program,
which reads this section again. Use trust-runtime identify to
blink the output; it works while the resource runs, is paused, is stopped or is faulted. An output
that [[io.safe_state]] gives a safe value keeps it while a stop or a fault is in force. A chip
does not read this section yet.
[runtime.retain] (retain policy)¶
| Key | Type | Required | Default | Notes |
|---|---|---|---|---|
mode |
string | yes | none | none or file. |
path |
string | only for file |
none | Required when mode = "file". |
save_interval_ms |
integer | yes | none | Must be >= 1. |
A function block instance in a RETAIN variable keeps its whole state, a library block's own
memory included: after a warm restart or a power loss a TOF's running off-delay and a TP's
running pulse go on, and a counter or an R_TRIG/F_TRIG whose input is TRUE counts no new edge.
Only a timer's time base starts over, so its first call after the restart adds no time
(specification 10 §4.8).
On an ESP32-C6 chip, mode = "file" saves the retained values into the chip's checkpoint flash, so
path is not used there. The chip saves at save_interval_ms and at stop or shutdown, and a reset or
a warm restart restores the newest complete save. An interrupted save loses at most the save in
progress. A retained image larger than 16,300 bytes cannot be saved on the C6.
[runtime.watchdog] (watchdog and fault policy)¶
| Key | Type | Required | Notes |
|---|---|---|---|
enabled |
bool | yes | Enables scan watchdog enforcement. |
timeout_ms |
integer | yes | Must be >= 1. |
action |
string | yes | halt, safe_halt, or restart. |
hang_action |
string | no | fault (default) or abort. Used when the watchdog is enabled and a scan thread does not return. A hang is recorded as a watchdog-timeout fault while the blocked call is still running; fault keeps the process serving the control plane, which then refuses pause, resume, restart and bytecode reload until the process is restarted. abort exits the process with status 70 right after the fault is recorded, so no destructors run and a service manager can restart it. With enabled = false there is no supervisor, so a hang is not detected. Changing it through config.set takes effect at the next start. |
[runtime.fault] (fault policy)¶
| Key | Type | Required | Notes |
|---|---|---|---|
policy |
string | yes | halt, safe_halt, or restart. |
A halting fault (halt or safe_halt, a watchdog halt or safe_halt action, or an exhausted restart budget) leaves the resource Faulted. The runtime process keeps serving the control plane. ctl restart warm or ctl restart cold recovers it, and the process exits with status 1 when it stops while faulted.
Networked / Optional Interfaces¶
[runtime.web]¶
Defaults when omitted:
[runtime.web]
enabled = true
listen = "0.0.0.0:8080"
auth = "local"
tls = false
Accepted keys:
enabledlistenauth = "local" | "token"tls
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
auth = "token" |
runtime.control.auth_token must be set |
auth = "token" with runtime.control.auth_token = "secret" |
tls = true |
runtime.tls.mode must not be "disabled" |
enable [runtime.tls] before serving HTTPS |
remote listen + runtime.tls.require_remote = true |
tls must be true |
listen = "0.0.0.0:8080" requires tls = true |
[runtime.tls]¶
Defaults when omitted:
[runtime.tls]
mode = "disabled"
require_remote = false
Accepted keys:
mode = "disabled" | "self-managed" | "provisioned"cert_pathkey_pathca_pathrequire_remote
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
mode != "disabled" |
set both cert_path and key_path |
mode = "self-managed" with PEM files |
mode = "provisioned" |
set ca_path in addition to cert/key |
provisioned PKI bundle |
[runtime.deploy]¶
| Key | Type | Default | Notes |
|---|---|---|---|
require_signed |
bool | false |
Enforces signed deployment artifacts. A deployment is checked against the installed file's policy, so it cannot lift the requirement or name its own keyring with the runtime.toml it carries. |
keyring_path |
string | none | Required when require_signed = true. |
[runtime.discovery]¶
Defaults when omitted:
[runtime.discovery]
enabled = true
service_name = "truST"
advertise = true
interfaces = []
Accepted keys:
enabledservice_nameadvertiseinterfaces = ["eth0", ...]host_group
[runtime.mesh]¶
Defaults when omitted:
[runtime.mesh]
enabled = false
role = "peer"
listen = "0.0.0.0:5200"
connect = []
tls = false
publish = []
subscribe = {}
zenohd_version = "1.7.2"
plugin_versions = {}
Accepted keys:
enabledrole = "peer" | "client" | "router"listenconnecttlsauth_tokenpublishsubscribezenohd_versionplugin_versions
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
runtime.mesh.tls = true |
runtime TLS must be enabled | mesh listener using the runtime TLS certificate set |
remote mesh listen + runtime.tls.require_remote = true |
mesh TLS must be on | listen = "0.0.0.0:5200" with tls = true |
[runtime.ads]¶
This optional section enables the Beckhoff ADS client runtime path. The ADS
point grammar lives in ads.toml; runtime.toml only controls
whether that project source file is loaded and how often the background worker
wakes.
Defaults when omitted:
[runtime.ads]
enabled = false
config_path = "ads.toml"
worker_tick_interval_ms = 20
Accepted keys:
| Key | Type | Default | Notes |
|---|---|---|---|
enabled |
bool | false |
Loads ads.toml and starts ADS workers at runtime startup. |
config_path |
string | ads.toml |
Path to the ADS client config. Relative paths resolve against the runtime bundle root. |
worker_tick_interval_ms |
integer | 20 |
Background ADS worker wake interval. Must be >= 1. The scan cycle does not do socket I/O. |
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
enabled = true |
config_path must point at a readable ADS config |
config_path = "ads.toml" |
enabled = true at startup |
runtime binary must be built with ads-wire |
feature-enabled runtime package |
| always | worker_tick_interval_ms >= 1 |
worker_tick_interval_ms = 20 |
[runtime.ads_server]¶
This optional section exposes selected truST runtime globals as Beckhoff ADS
symbols. It is the ADS-server direction: external ADS clients connect to truST.
The ADS client import path above uses [runtime.ads] and ads.toml; server
mode is configured entirely here.
Defaults when omitted:
[runtime.ads_server]
enabled = false
ads_port = 851
insecure_transport = false
writes_enabled = false
symbol_namespace = ""
allow_unpinned_clients = false
unsafe_allow_public_bind = false
max_symbols = 256
max_clients = 8
max_subscriptions_per_client = 64
max_total_subscriptions = 256
max_frame_bytes = 65536
max_sumup_items = 512
max_write_bytes = 8192
max_string_bytes = 4096
read_timeout_ms = 5000
idle_timeout_ms = 60000
min_notification_cycle_ms = 50
expose = []
writable = []
allow_clients = []
Minimal enabled example:
[runtime.ads_server]
enabled = true
listen = "192.168.77.10"
ams_net_id = "192.168.77.10.1.1"
ads_port = 851
insecure_transport = true
writes_enabled = false
expose = ["global.TankLevel", "global.PumpRunning", "global.StatusWord"]
writable = []
[[runtime.ads_server.clients]]
ams_net_id = "192.168.77.20.1.1"
source_ip = "192.168.77.20"
Accepted keys:
| Key | Type | Default | Notes |
|---|---|---|---|
enabled |
bool | false |
Starts the ADS server listener when the runtime binary is built with feature ads-server. |
listen |
IP string | none | Required when enabled. Must be one concrete local IP address; 0.0.0.0 and :: are rejected. |
ams_net_id |
string | derived from IPv4 listen |
Six-byte AMS Net ID exposed by truST. Required when listen is not IPv4. |
ads_port |
integer | 851 |
Logical AMS target port served by truST. The TCP listener still uses ADS router port 48898. |
insecure_transport |
bool | false |
Required acknowledgement for classic plain ADS. |
writes_enabled |
bool | false |
Global ADS write-back gate. |
symbol_namespace |
string | "" |
Optional prefix for exposed ADS symbol names. |
expose |
string array | [] |
Glob allowlist of runtime globals to publish. Empty means publish nothing. |
writable |
string array | [] |
Glob allowlist of published symbols that ADS clients may write when writes_enabled = true. |
allow_unpinned_clients |
bool | false |
Lab/loopback escape hatch for AMS-Net-ID-only clients. Does not satisfy production-ready proof. |
unsafe_allow_public_bind |
bool | false |
Explicit override for public/NAT-suspect listen addresses. |
max_symbols |
integer | 256 |
Maximum exposed symbols. |
max_clients |
integer | 8 |
Maximum concurrent ADS clients. |
max_subscriptions_per_client |
integer | 64 |
Per-client notification subscription cap. |
max_total_subscriptions |
integer | 256 |
Global notification subscription cap. |
max_frame_bytes |
integer | 65536 |
Maximum accepted AMS/TCP frame size. |
max_sumup_items |
integer | 512 |
Maximum sum-up request items. |
max_write_bytes |
integer | 8192 |
Maximum single write payload. |
max_string_bytes |
integer | 4096 |
Maximum string payload exposed or accepted. |
read_timeout_ms |
integer | 5000 |
Socket read timeout. |
idle_timeout_ms |
integer | 60000 |
Idle client timeout. |
min_notification_cycle_ms |
integer | 50 |
Lower bound for accepted notification cycle requests. |
Structured clients:
[[runtime.ads_server.clients]]
ams_net_id = "192.168.77.20.1.1"
source_ip = "192.168.77.20"
[[runtime.ads_server.clients]]
ams_net_id = "192.168.77.30.1.1"
source_cidr = "192.168.77.0/24"
Legacy unpinned clients are accepted only when explicitly enabled for lab work:
[runtime.ads_server]
allow_unpinned_clients = true
allow_clients = ["127.0.0.1.1.100"]
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
enabled = true |
listen must be set and must not be unspecified |
listen = "192.168.77.10" |
enabled = true |
insecure_transport = true is required for v1 plain ADS |
explicit acknowledgement |
enabled = true at startup |
runtime binary must be built with ads-server |
feature-enabled runtime package |
listen is public or NAT-suspect |
rejected unless unsafe_allow_public_bind = true |
avoid exposing plain ADS on public networks |
| production client entries | each [[runtime.ads_server.clients]] needs ams_net_id plus source_ip or source_cidr |
source-pinned allowlist |
source_ip and source_cidr |
only one may be set per client | no ambiguous source pin |
allow_clients |
requires allow_unpinned_clients = true |
lab/loopback only |
writable |
every entry must be covered by expose |
do not write hidden symbols |
| size/time limits | each numeric limit must be >= 1 |
max_clients = 8 |
Classic ADS is cleartext and route-based. Keep ADS server mode on a trusted OT
segment, source-pin every production client, and enable writes only for the
smallest safe symbol set. Accepted and rejected ADS writes are audited as
ads.server.write.
Secure ADS is not supported in this release; [runtime.ads_server] only serves
classic plain ADS and requires insecure_transport = true when enabled.
[runtime.cloud]¶
This section shapes runtime-cloud policy inside runtime.toml.
| Key | Type | Default | Notes |
|---|---|---|---|
profile |
string | dev |
dev, plant, or wan |
Optional subsections:
[runtime.cloud.wan]
allow_write = [
{ action = "cfg_apply", target = "site-b/*" }
]
[runtime.cloud.links]
transports = [
{ source = "runtime-a", target = "runtime-b", transport = "zenoh" }
]
Allowed transport values:
realtimezenohmeshmqttmodbus-tcpopcuadiscoveryweb
[runtime.openot]¶
This optional section enables OpenOT telemetry publishing to a shared-memory ring. It is separate from plant I/O drivers: the runtime publishes semantic audit records after output dispatch and fails the scan if the configured telemetry append fails.
For the attribute-driven authoring path, see
OpenOT Attribute Authoring. That compiler
path generates Main.OotProducer by default; use that qualified instance path
with source = "st-fb".
Defaults when omitted:
[runtime.openot]
enabled = false
path = ""
capacity = 4096
fence_mode = "fenced"
allow_unfenced_for_proof = false
source = "heartbeat"
Accepted keys:
| Key | Type | Default | Notes |
|---|---|---|---|
enabled |
bool | false |
Enables OpenOT shared-memory telemetry publishing. |
path |
string | "" |
Shared-memory backing file. Relative paths resolve against the runtime bundle root. Required when enabled = true. |
capacity |
integer | 4096 |
Ring byte capacity. Must be >= 1. |
fence_mode |
string | "fenced" |
"fenced" for product use, or "unfenced" only for controlled proof runs. |
allow_unfenced_for_proof |
bool | false |
Must be true when fence_mode = "unfenced". |
source |
string | "heartbeat" |
"heartbeat" publishes a runtime heartbeat record per scan. "st-fb" publishes encoded records emitted by configured ST OpenOT producer FB instances. |
producer_instance |
string | unset | Back-compatible alias for one ST-FB producer. Qualified path to the producer FB instance, for example "Main.OotProducer" for attribute-generated programs or "Main.Producer" for a hand-authored producer FB. |
producer_instances |
array of strings | unset | Preferred when draining more than one ST-FB producer into one ring. Paths are drained in array order. Do not set this together with producer_instance. |
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
enabled = true |
path must not be empty |
path = "openot.shm" |
| always | capacity >= 1 |
capacity = 4096 |
fence_mode = "unfenced" |
set allow_unfenced_for_proof = true |
proof-only A/B run |
source = "st-fb" |
set a qualified producer_instance or non-empty producer_instances |
producer_instances = ["First.OotProducer", "Second.OotProducer"] |
source = "heartbeat" |
omit producer_instance and producer_instances |
default smoke publisher |
Multi-PROGRAM OpenOT authoring generates one hidden producer per PROGRAM.
Configure each generated producer explicitly:
[runtime.openot]
enabled = true
path = "openot.shm"
source = "st-fb"
producer_instances = ["First.OotProducer", "Second.OotProducer"]
The runtime drains the listed instances in order and serializes their records through one shared-memory writer.
[runtime.openot.persistence]¶
This optional child section starts the off-scan canonical document consumer.
Both [runtime.openot].enabled and persistence enabled must be true. The
backend discriminator is the only database-selection authority; truST never
falls back to a different backend.
| Key | Type | Default | Notes |
|---|---|---|---|
enabled |
bool | false |
Enables the persistence worker. |
backend |
string | unset | Required when enabled: sqlite, postgresql, timescaledb, mysql, sqlserver, or influxdb3. |
batch_size |
integer | 256 |
Maximum canonical documents prepared per durable batch. |
flush_interval_ms |
integer | 250 |
Maximum idle interval between worker attempts. |
queue_capacity |
integer | 4096 |
Bounded host-side work capacity. |
shutdown_timeout_ms |
integer | 5000 |
Graceful worker shutdown budget. |
retry_initial_ms |
integer | 250 |
First transient-failure backoff. |
retry_max_ms |
integer | 30000 |
Backoff ceiling. |
retry_multiplier |
integer | 2 |
Bounded exponential multiplier. |
retry_max_attempts |
integer | 20 |
Consecutive transient failures before persistence faults. |
All sizes, intervals, and retry values must be greater than zero;
retry_initial_ms must not exceed retry_max_ms. Exactly the table matching
the selected backend is required, and tables for unselected backends are
rejected.
Official release binaries compile every supported adapter. Custom builds may
omit adapters with the openot-database-sqlite,
openot-database-postgresql, openot-database-timescaledb,
openot-database-mysql, openot-database-sqlserver, and
openot-database-influxdb3 Cargo features. TOML parsing remains stable in a
reduced build: selecting a recognized omitted adapter fails startup with
backend_not_available before truST reads that backend's secret environment
variables or attempts a connection. It never falls back.
Backend tables:
| Table | Required keys | Notes |
|---|---|---|
.sqlite |
path |
Relative paths resolve against the bundle root. |
.postgresql |
connection_url_env, schema, tls, ca_cert_path |
tls = "require"; the named environment variable contains the URL. |
.timescaledb |
connection_url_env, schema, tls, ca_cert_path |
Requires the real TimescaleDB extension and creates the OpenOT hypertable. |
.mysql |
connection_url_env, database, tls, ca_cert_path |
Shared by separately verified MySQL and MariaDB products. |
.sqlserver |
connection_url_env, schema, tls, ca_cert_path |
Encrypted TDS; Azure SQL is not implied by local SQL Server support. |
.influxdb3 |
host_env, token_env, database, spool_path, max_bytes, ca_cert_path |
Host/token come from environment; the bundle-relative SQLite spool and its positive byte limit are mandatory. |
Complete examples and operational semantics are in OpenOT Database Persistence.
[runtime.observability]¶
Defaults when omitted:
[runtime.observability]
enabled = false
sample_interval_ms = 1000
mode = "all"
include = []
history_path = "history/historian.jsonl"
max_entries = 20000
prometheus_enabled = true
prometheus_path = "/metrics"
alerts = []
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
| always | sample_interval_ms >= 1 |
sample_interval_ms = 1000 |
| always | max_entries >= 1 |
max_entries = 20000 |
| always | mode is all or allowlist |
mode = "allowlist" |
mode = "allowlist" |
include must not be empty |
include = ["PROGRAM Main.Pressure"] |
prometheus_enabled = true |
prometheus_path must start with / |
prometheus_path = "/metrics" |
Alert entries support:
[[runtime.observability.alerts]]
name = "HighPressure"
variable = "PROGRAM Main.Pressure"
above = 8.5
debounce_samples = 3
hook = "log"
Each alert needs:
| Field | Requirement | Example |
|---|---|---|
name |
required | "HighPressure" |
variable |
required | "PROGRAM Main.Pressure" |
above / below |
provide at least one threshold | above = 8.5 |
debounce_samples |
must be >= 1 |
debounce_samples = 3 |
[runtime.opcua]¶
Defaults when omitted:
[runtime.opcua]
enabled = false
listen = "0.0.0.0:4840"
endpoint_path = "/"
namespace_uri = "urn:trust:runtime"
publish_interval_ms = 250
max_nodes = 128
expose = []
security_policy = "basic256sha256"
security_mode = "sign_and_encrypt"
allow_anonymous = false
Validation constraints:
| Condition | Requirement | Example |
|---|---|---|
| always | listen, endpoint_path, and namespace_uri must not be empty |
listen = "0.0.0.0:4840" |
| always | endpoint_path must start with / |
endpoint_path = "/" |
| always | publish_interval_ms >= 1 |
publish_interval_ms = 250 |
| always | max_nodes >= 1 |
max_nodes = 128 |
enabled = true |
allow anonymous access or set both username and password |
authenticated endpoint with user/password |
security_policy |
must be none, basic256sha256, or aes128sha256rsaoaep |
security_policy = "basic256sha256" |
security_mode |
must be none, sign, or sign_and_encrypt |
security_mode = "sign_and_encrypt" |
Validation Workflow¶
Use this loop whenever you edit runtime.toml:
trust-runtime build --project ./my-plc --sources src
trust-runtime validate --project ./my-plc
trust-runtime ctl --project ./my-plc status