Alarms & Commands
A device type declares two behavioural surfaces alongside its tags: the conditions that should raise an alarm, and the commands an operator can issue against an instance. Both are plain YAML inside the type package — no code — so they version, review, and ship with the type itself. See The Type System for how a package is laid out and Tags for the tag model these definitions bind against.
consystence.pump.centrifugal/├── manifest.yaml├── simulation.yaml├── alarms/│ └── alarms.yaml # ← alarm definitions├── commands/│ └── commands.yaml # ← command definitions└── components/Alarms
Section titled “Alarms”alarms.yaml is a list of alarm definitions. Each one names a condition over the
instance’s tag values — its live data points; when the condition holds, an active
alarm is raised against the instance.
| Field | Required | Notes |
|---|---|---|
id | yes | Stable identifier, unique within the type. |
name | yes | Human-readable label. |
condition | yes | Expression over bindings; raises the alarm when true. |
priority | yes | One of Low, Medium, High, Critical. |
category | yes | Free-form grouping string. The bundled types use equipment, process, instrumentation, and safety. |
message | yes | Template shown to operators; supports bindings. |
delaySeconds | no | Declared debounce — the intent is that the condition must stay true this long before the alarm activates. Not yet enforced: today’s evaluators raise the alarm on the first scan where the condition is true. Also accepted as delay: with an ISO-8601 duration (for example PT10S) — the form exported packages use. |
Conditions and messages use the same binding syntax as the rest of the type
package. The evaluated namespaces are {tags.X} for a tag value, {config.X}
for instance configuration, {instance.X} for instance metadata
({instance.name} for the label), and — inside commands — {params.X}.
Conditions support comparisons and boolean logic (>, &&, ==, !,
abs(...)).
Here are real definitions from consystence.pump.centrifugal — note the
categories, the priority ladder, and the declared delaySeconds debounce on the
noisy process and bearing readings:
- id: fault name: Pump Fault condition: "{tags.Fault}" priority: High category: equipment message: "{instance.name}: Fault detected (Code: {tags.FaultCode})"
- id: high-bearing-temp name: High Bearing Temperature condition: "{tags.BearingTemp} > 85" priority: High category: equipment message: "{instance.name}: Bearing temperature {tags.BearingTemp}°C exceeds limit" delaySeconds: 10
- id: low-flow name: Low Flow condition: "{tags.Running} && {tags.FlowRate} < 5" priority: Medium category: process message: "{instance.name}: Flow rate {tags.FlowRate} m3/h below minimum" delaySeconds: 60
- id: interlock-active name: Safety Interlock Active condition: "{tags.Interlock}" priority: Low category: safety message: "{instance.name}: Safety interlock preventing operation"Priority
Section titled “Priority”priority is an ordered enum: Low → Medium → High → Critical. Reserve
Critical for conditions that demand immediate intervention. The
consystence.tank.storage type uses it for the high-high and low-low trips:
- id: high-high-level name: Tank High-High Level condition: "{tags.HighHighLevel} == true" priority: Critical category: process message: "{instance.name}: High-high level alarm - overflow risk, shut down inlets" delaySeconds: 2Lifecycle
Section titled “Lifecycle”An alarm is active while its condition holds and clears automatically when it stops holding — there is no latching. Activation and clearance both surface in the instance’s event log.
Operators can acknowledge an active alarm; the acknowledging user and
timestamp are recorded on the alarm (AcknowledgedBy, AcknowledgedAt).
Alarm and event timestamps are recorded and displayed in UTC.
Acknowledgement is an operator action, never an AI action — the AI may
explain an alarm, but it cannot acknowledge it.
Commands
Section titled “Commands”commands.yaml is a list of command definitions. A command is a named,
guard-railed action that, when executed, writes one or more tags on the
instance.
| Field | Required | Notes |
|---|---|---|
id | yes | Stable identifier, unique within the type. |
name | yes | Human-readable label. |
description | no | What the command does. |
confirm | no | If true, renderers prompt the operator before execution. Honoured by the default (wire) web UI and the CLI; the legacy HTML shell (being removed) does not prompt — see Execution flow and audit. |
confirmMessage | no | Custom confirmation prompt (supports bindings). |
parameters | no | Operator-supplied inputs (see below). |
preconditions | no | Gates that must all pass before the writes are applied. |
tagWrites | yes | The tag writes performed on execution. |
audit | no | If true, execution is recorded to the command audit log. |
Preconditions
Section titled “Preconditions”Each precondition pairs a condition expression with the message shown when it
fails. All preconditions must hold before any tag write is attempted; the
first failing one short-circuits with its message.
Precondition evaluation is fail-closed on tag quality: every {tags.X}
reference must resolve to a present, non-null, good-quality value taken from one
atomic snapshot of the instance’s authoritative tag values. If any referenced tag
is missing, or its value is uncertain, cached, stale or bad, the command is rejected as
precondition data unavailable — manifest defaults are never accepted as
evidence, and degraded data never lets a guarded command through. Simulated
devices attest good-quality provenance for the values they produce, so guarded
commands work normally in simulation. These are server-enforced operability
gates (“already running”, “in local mode”), surfaced to give the operator a
clear reason — they are still not a safety function, and not a substitute for
controller-side interlocking.
Parameters
Section titled “Parameters”parameters declares operator inputs. Each has a name, a dataType (Bool,
Int, DInt, Real, String), optional min/max/unit, and a
description. Reference a parameter inside a tag write with {params.<name>}.
# commands/commands.yaml — consystence.pump.centrifugal- id: start name: Start Pump description: Starts the pump at current speed setpoint confirm: true confirmMessage: "Start {instance.name}?" audit: true preconditions: - condition: "!{tags.Running}" message: Pump is already running - condition: "!{tags.Fault}" message: Clear fault before starting - condition: "!{tags.Interlock}" message: Safety interlock is active - condition: "!{tags.LocalMode}" message: Pump is in local mode tagWrites: - tagName: Running value: "true"
- id: set-speed name: Set Speed description: Sets the speed setpoint confirm: false audit: true parameters: - name: speed dataType: Real min: 0 max: 3600 unit: rpm description: Target speed in RPM preconditions: - condition: "!{tags.LocalMode}" message: Pump is in local mode tagWrites: - tagName: SpeedSetpoint value: "{params.speed}"Tag writes
Section titled “Tag writes”Each tagWrites entry names a tagName and a value. The value is a literal
("true", "0", "100") or a binding ("{params.speed}", "{tags.Position}").
Command tagWrites are exempt from the tag access check: a command
defines its own writes and may deliberately target a tag that is read-only for
direct writes (the start command above writes Running, a Read feedback
tag). The access gate applies to direct tag writes, not to command
execution — see Tags for access and physicalRole.
Momentary (pulse) writes
Section titled “Momentary (pulse) writes”Add pulseSeconds (also accepted as pulse: with an ISO-8601 duration such as
PT1S — the form exported packages use) to hold a value for a fixed duration,
after which the tag resets to its inactive value — false for booleans, 0 for
numerics — the right shape for momentary resets and jogs. The consystence.sensor.flow type
uses it for a one-second totaliser reset pulse:
- id: reset-totalizer name: Reset Totalizer description: Resets the accumulated flow totalizer to zero confirm: true confirmMessage: "Reset totalizer for {instance.name}? This cannot be undone." audit: true tagWrites: - tagName: TotalizerReset value: "true" pulseSeconds: 1Execution flow and audit
Section titled “Execution flow and audit”Commands execute over the server-driven UI session, not a public HTTP endpoint. When an operator executes a command against live hardware, parameter values are bound and validated first — missing, unknown, wrong-type, and out-of-range values are rejected before anything else runs; on the local/sim path parameter values are applied as declared (see the caution above). Preconditions are then evaluated against one atomic snapshot of good-quality tag values; if any precondition fails, execution stops with that precondition’s message, and if any referenced tag value is missing or degraded the command is rejected as precondition data unavailable — the evaluation fails closed.
What happens next depends on where the device’s tags live. For a local or
simulated device, the tagWrites are applied in-process (parameter bindings
resolved, pulse tags scheduled to reset). For live hardware, the command is
dispatched from the site to the edge over the authenticated command channel
with an expiry: a command that expires before dispatch is rejected, and a
dispatched command that is not acknowledged by its expiry is marked timed
out — the outcome on the PLC is unknown, and it is surfaced that way rather
than assumed. When audit: true, the execution — caller, parameters, and the
individual tag writes with their outcome and data source — is recorded to the
command audit log (readable via csy instance commands / csy site commands).
flowchart TD
A[Operator issues command<br/>via the UI session] --> V{Parameters bind?<br/>type + range — edge dispatch}
V -->|no| R[Reject with the failing<br/>parameter, precondition<br/>or data-unavailable message]
V -->|yes| B{All preconditions true<br/>on one good-quality<br/>tag snapshot?}
B -->|no| R
B -->|yes| D{Live hardware?}
D -->|local / sim| E[Apply tagWrites in-process<br/>literals and params bindings]
D -->|live| X[Dispatch to edge command<br/>channel with expiry]
X --> K{Acknowledged<br/>before expiry?}
K -->|yes| E2[Edge applies writes<br/>to the controller]
K -->|no| T[Timed out —<br/>PLC outcome unknown]
E --> F[Pulse tags reset<br/>after pulseSeconds]
E2 --> F
E --> G[Write audit log<br/>subject id + tag writes + outcome]
E2 --> G
T --> G
The audited caller is the authenticated principal’s subject identifier. For
a human operating through the CIAM Bearer flow that is the pairwise
per-application sub claim — not the stable Entra oid, so correlating
audit entries against an Entra directory by oid does not work today (a known
limitation); for automation using a tenant X-Api-Key (prefix csk_) it is
that key’s id. Email is a verified attribute, not the identity, and never the
audit key. See
The MultiAuth Scheme and
Authentication for how principals are resolved, and
Roles for org membership (owner, admin, member).
Executing a command requires an authenticated principal with command authority:
org membership on the cloud surface, or the equivalent site role on a site
server — site users authorise on their site role, and org membership is not
required on-site.
Related pages
Section titled “Related pages”- The Type System — package structure and IDs.
- Creating Types — authoring a new type package.
- Tags — the data model commands write and alarms read.
- PLC Communication — how a tag write reaches the field.
- API Reference — the rendered OpenAPI. It carries read-only operational endpoints — live tag values, trends, active alarms, and the command audit log — but no command execution endpoint: commands run over the server-driven UI session.