13. PsySpec XML Reference
Every PsySpec element and attribute, with an honest status badge for each: what ships, what is parsed but inert, and what is still roadmap.
How to read this chapter
This is the lookup chapter, extracted from the current parser code. Each element carries a badge:
- Shipped — parsed and functionally wired.
- Stub — parsed into internal structures but not (or only partially) acted on.
- Roadmap — documented/reserved syntax; no real parsing or behaviour yet.
Status summary
| Element | Status | Notes |
|---|---|---|
<psyspec> | Shipped | Root tag; new SSL policy attributes |
<include> | Shipped | XML file inclusion |
<variable> | Shipped | %var% substitution + new cmdline override |
<node> | Shipped | Multi-node topology; per-node <library> |
<library> | Shipped | DLL/.so registration |
<space> | Shipped | Only type="external" handled at top level |
<interface> | Shipped | Auth/encryption children stubbed; new SSL attrs |
<service> | Stub | Partial: custom services have a NULL receiver |
<psyprobe> | Shipped | Port / location / alias / post |
<module> | Shipped | Per-module verbose/debug/logfile now wired |
<whiteboard> | Shipped | root= persistence is parsed-only |
<catalog> | Shipped | File/Data/Replay + CCM/TimeSeries families |
<stream> | Stub | Component created, backing function empty |
<feed> | Roadmap | Log-only branch, never becomes a component |
<supervisor> | Roadmap | Log-only branch |
<monitoring> / <performance> | Roadmap | “Not yet implemented” log |
<group> | Roadmap | Log-only branch |
<trigger> | Shipped | Filters, interval, context triggers |
<filter> | Shipped | 7 comparison types + maxage |
<triggergroup> | Shipped | Declarative multi-trigger join (all/N-of-M, time windows, snapshot, etc.) |
<triggers> | Stub | Recognised, silently does nothing |
<retrieve> | Shipped | Whiteboard/catalog retrieval spec |
<query> | Shipped | Incl. remote host/port queries |
<retrieves> / <posts> / <signals> | Stub | Empty branches |
<crank> | Shipped | Function / script / inline / external |
<post> | Shipped | |
<signal> | Shipped | Minimal: name + type only |
<context> (in module) | Shipped | Groups triggers under a named context |
<parameter> | Shipped | Typed values incl. ranges/lists/random |
<setup> / <key> / <customview> | Shipped | |
<recording> / <playback> | Stub | Parameters stored, not acted on |
<executable> | Stub | Cmdline stored, never launched |
<alias> (in psyprobe) | Shipped | |
<authentication> (in interface) | Stub | Parser body empty |
<phase> | Roadmap | No parsing code at all |
system= trigger attribute | Roadmap | Use context= instead |
<psyspec> (root) Shipped
The root element of every spec. Besides port= (main system port, see System Guide), it now carries the global SSL client-verification policy:
| Attribute | Meaning | Default |
|---|---|---|
allowselfsigned | New. yes/true/1 loosens verification (accepts self-signed certificates) for all SSL client connections. Per-interface value overrides either way. | secure — verify peer |
cafile | New. Path to a CA PEM file used for SSL verification instead of the OS trust store. | OS trust store |
capath | New. Directory of CA certificates, same mechanism as cafile. | OS trust store |
verbose/debug/logfile on the root element are Roadmap — only per-component logging attributes are wired. System-wide levels are set on the command line.Children: <include>, <variable>, <node>, <library>, <space>, <interface>, <service>, <psyprobe>, <whiteboard>, <catalog>, <module>, <stream>, plus the roadmap tags listed above.
<include> Shipped
<include file="system.xml" /> splices the referenced file's contents into the spec (like a C include) and reparses; errors in the expanded spec report line/column. file is required. Includes and variables are processed before anything else.
<variable> Shipped
<variable name="SystemID" value="2" /> defines a literal %SystemID% text substitution applied over the whole spec before XML parsing — usable in names, cranks, triggers, anywhere, as long as the result stays valid XML. Both name and value are required.
name=value argument on the Psyclone command line overrides the spec default — Psyclone spec=robot.xml SystemID=3. Reserved parameters (spec=, port=, html=, verbose=, debug=, per-topic variants) are respected and not treated as variables.<node> Shipped
| Attribute | Meaning | Default |
|---|---|---|
name | Node name; Main (or no/local address) reconfigures the local node | — |
addr / address | IP or hostname (addr preferred, address accepted) | local |
type | Node type string (adhoc/java types not implemented Roadmap) | — |
port | Port; for the local node a differing port adds Main/PsyProbe/Console interfaces on it | 0 |
timeout | Connect timeout (ms) | 0 |
Child: <library name library path> — libraries loaded only on that node.
<library> Shipped
<library name="MyExamples" library="Examples" path="../libs" />. name is how cranks refer to it (MyExamples::Ping); library is the file name, resolved as Examples.dll (Windows) / libExamples.so (UNIX) — debug builds first try the ...Debug variants; path is an optional directory; node restricts to a node.
<space> Shipped
<space name="GUISpace" type="external" />. At the top level only type="external" is handled (creates a process-map entry waiting for an external program to connect). Regular spaces are created implicitly by the space= attribute on components (default Root). node restricts to a node. See Distributed Systems.
<interface> Shipped
<interface name="MyWebInterface" node="Node0" port="1234" protocol="HTTP"
service="MyWebService" timeout="3000" default="yes" />
| Attribute | Meaning | Default |
|---|---|---|
name | Interface name (required; parse fails without) | — |
service | Target service name (required) | — |
node | Node the interface lives on | any/local |
port | TCP/UDP port (required) | — |
protocol | Message | HTTP | Telnet | RAW (anything else = parse failure) | unset |
ip | UDP selects UDP; otherwise TCP | TCP |
timeout | Timeout ms | unset |
default | yes = default interface on a shared port | no |
allowselfsigned | New. Per-interface SSL verification override; wins over the global setting either way | inherit global |
cafile / capath | New. Per-interface CA file/directory override | inherit global |
Child <authentication>: Stub — parsed by an empty function body; interface encryption attributes are likewise stubbed.
<service> Stub (partial)
Attributes: name, rootdir, subdir, timeout, node. The service is registered and a component is created for its subscription, so <trigger>/<post>/<crank> children parse — but custom services register a NULL receiver, so inbound requests are discarded. Built-in PsyProbe/Console services work. See Services & Interfaces.
<psyprobe> Shipped
| Attribute | Meaning | Default |
|---|---|---|
defaultport | Any value other than no removes PsyProbe from the main port | shares main port |
port | Adds a dedicated PsyProbe HTTP interface on this port | — |
location | Web root directory for PsyProbe | built-in |
Children: <alias name location default> (URL alias to a local directory with optional default file) and <post> (same attributes as a module post plus XML content, registered as a clickable manual post in PsyProbe).
Components: <module> / <whiteboard> / <catalog> / <stream>
All component tags share one parser; the tag name selects the type. <module type="external"> declares an external process component (connects via a space). <module count=N> tag-instancing is Roadmap. Common attributes:
| Attribute | Meaning | Default |
|---|---|---|
name | Component name (required) | — |
tag | Appends _tag to the name; * uses the component's own name as tag | none |
node | Node assignment; modules also accept * = every node | config node |
space | Process space; Python-crank modules auto-get their own space | Root |
selftrigger | allow | warn | anything-else = none | warn |
logfile | New — now wired. Per-module log file | node log |
verbose / debug | New — now wired. Per-module verbosity/debug levels (0–9) | node levels |
migration | yes flags the component movable; runtime migration itself is parsed-only Stub | no |
priority | uint8 stored; scheduling effect parsed-only Stub | 0 |
Extra attributes for whiteboard/catalog/stream/service:
| Attribute | Meaning | Default |
|---|---|---|
function / type | Backing crank Lib::Func; bare names get the PsySystem:: prefix. Defaults: whiteboard→PsySystem::Whiteboard, stream→PsySystem::Stream; a catalog/service with neither is an error. Catalog type= selects the family: File/Data/Replay/RequestStore and the CCM & TimeSeries families are Shipped; the Media and Google catalog types are Stub — do not rely on them. | see left |
root | Storage root directory (whiteboard root= persistence is parsed-only Stub) | — |
subdir / ext | Subdirectory / file extension | — |
operation | Default operation | — |
key + keytype | Adds an index key (repeatable via <key> children) | time index |
timeout / interval | ms | 0 |
maxcount / maxsize | Max entries / max bytes | 0 |
rotate | yes/no rotation (replay looping) | no |
Component children
| Child | Status | Meaning |
|---|---|---|
<recording root filesize overwrite> | Stub | Stores recording parameters; not acted on per-module |
<playback root interval> | Stub | Stores playback parameters; not acted on |
<executable consoleoutput autorestart>cmdline</executable> | Stub | Command line stored but never launched (roadmap: auto-start external processes) |
<customview name template> | Shipped | Registers a PsyProbe custom view; also accepts an inline XML/text body instead of a template= file |
<setup> | Shipped | Free-form XML/text blob, read via getParameterString("componentsetup"); file= variant supported |
<key name type> | Shipped | Extra index keys |
<parameter> | Shipped | See below |
<context name> | Shipped | See below |
<parameter> Shipped
<parameter name=".." type="String|Integer|Float" value=".." interval=".." /> (type defaults to String). Rich value syntax:
| Form | Example | Meaning |
|---|---|---|
| Plain value | value="10" | Fixed initial value |
| Range | value="[0:110]" | Bounded range, initial = lowest |
| Stepped range | value="[0:110:2]" | Step 2; step can also come from interval="2" |
| Range + initial | value="[0:110]=55" | Initial value 55 |
| Random initial | value="[0:110]=*" | Random initial within the range |
| Lists | value="[-2.5;1.5;1.8]", value="['a';'b';'c']" | Enumerated numeric or string choices |
All parameters are queryable, settable and tweakable at runtime (getParameterString/Int, setParameter, tweakParameter, resetParameter) — see Modules.
<context> (in a component) Shipped
<context name="SoB.Alive.Awake">...</context> groups child trigger/post/retrieve/query/crank/signal elements under a named context instead of the default Psyclone.Ready. See Contexts.
<trigger> Shipped
Subscribes the component to messages; a match dispatches its cranks. Valid if it has a name plus at least one of type, from, to; duplicate names are an error.
| Attribute | Meaning | Default |
|---|---|---|
name | Trigger name (required, unique) | — |
type | Message type to match (wildcards allowed: input.audio.*) | — |
from / to | Only messages from this component / addressed to this component | any |
interval | ms — makes it a timer trigger (needs one real message first to start) | 0 |
delay / after | ms delay before firing (after is an alias) | 0 |
context | Fire on context change to this context (forces type CTRL_CONTEXT_CHANGE) | — |
maxage | ms — becomes a MAXAGE filter | ∞ |
tag | Match tag; * = component's own name | any |
Trigger priority/history and the system= attribute are Roadmap — use context= to react to context changes.
<filter> (child of trigger) Shipped
<filter type="haskey" key="SomeKey" /> <!-- entry exists (any type) -->
<filter type="equals" key="camera" value="front-left" /> <!-- string ==, exact, case-sensitive -->
<filter type="notequals" key="state" value="disabled" /> <!-- string != -->
<filter type="equalsnumeric" key="mode" value="3" /> <!-- float == -->
<filter type="notequalsnumeric" key="channel" value="0" /> <!-- float != -->
<filter type="greaterthan" key="temp" value="80" /> <!-- float > (≥ at the epsilon boundary) -->
<filter type="lessthan" key="pressure" value="2.5" /> <!-- float < (≤ at the epsilon boundary) -->
type is one of equals (exact, case-sensitive, no wildcards), notequals, equalsnumeric, notequalsnumeric, greaterthan, lessthan (all take key+value; numeric values are parsed as float64 with an epsilon compare, so exactly-equal values also pass greaterthan/lessthan) and haskey (key only, matches any entry type). The eighth filter operation, maxage, is written as a trigger attribute (milliseconds) rather than a <filter> child. Unrecognised types are silently ignored.
<retrieve> Shipped
On-demand fetch from a whiteboard/catalog when the owning trigger fires (see Whiteboards).
| Attribute | Meaning | Default |
|---|---|---|
name | Required, unique | — |
source | Component to retrieve from (required) | — |
type / from / to | Type / original sender / addressee filters | any |
maxcount | Max messages (0 = all) | 0 |
maxage | Age window, stored ×1000 (exact wall-clock unit under review — see Whiteboards) | ∞ |
tag | Tag filter; * = own name | any |
key / keytype | Index key; keytype string|integer|float|time (invalid drops the spec) | time |
start / end | Range bounds, interpreted per keytype | open |
<query> Shipped
Free-form query to a catalog/component; catalogs dispatch on the Query/Operation fields. Attributes: name (required, unique), source (required), type (string), subdir, ext, binary (yes), maxcount, maxage (stored ×1000; unit under review), key, value, operation, and for remote queries host (DNS-resolved at parse time) + port.
<crank> Shipped
The processing function attached to triggers. Four forms:
<crank name="c1" function="lib::function" /> <!-- C/C++ library crank -->
<crank name="c2" language="python" script="file.py" /> <!-- external script file -->
<crank name="c3" language="python">inline code</crank> <!-- inline script -->
<crank name="c4" /> <!-- external crank slot -->
A function without lib:: is an internal (built-in) crank. Duplicate crank names across components are rejected. Modules with no crank get the built-in passthrough (Internal::Simple) default crank. Java cranks are Roadmap.
<post> Shipped
Message posted when the owning trigger fires. Valid if it has a name and (type or context).
| Attribute | Meaning | Default |
|---|---|---|
name | Required, unique | — |
type | Type of the posted message | — |
context | Post a context change to this context | — |
to | Address to a specific component (also still delivered to subscribers) | broadcast |
ttl | Seconds (stored ×1000 ms); 0 = no expiry | 0 |
guaranteed | no marks the message non-guaranteed | guaranteed |
tag | * = own name | none |
Message content comes from child XML elements; the older contentkey/content attribute form is no longer parsed. time="trigger" is Roadmap.
<signal> Shipped (minimal)
<signal name="Output" type="My.Signal.1" /> — both attributes required; no others are parsed. See Messaging & Signals.
<triggergroup> declarative join Shipped
Joins several triggers into one crank activation: member messages sharing the same tag are buffered on the node until the group completes, then delivered together as one set (wakeups ≈ joins, not one per member). Untagged messages group under the literal empty-string tag "" and never mix with real tags. This is the primitive for multi-modal fusion (audio + text + vision joined before a decision crank runs).
<module name="Simple" node="Node0">
<triggergroup name="ts" count="2" maxage="2000">
<trigger name="t1" type="bla1" />
<trigger name="t2" type="bla2" />
<trigger name="t3" type="bla3" optional="true" maxage="200" />
</triggergroup>
<crank name="c1" function="lib::function" />
<post name="p1" type="bla4" to="WB/Stream/Cat" />
</module>
| Attribute | Meaning | Default |
|---|---|---|
name | Required, unique within the module. A duplicate group name discards the extra group with a level-1 warning; a group missing name or maxage is skipped with a warning. | — |
maxage | ms — required, no default. A partial set living longer than this is evicted. | — |
count | Fire when any N members are present ("all" = every named member). For N-of-M, any N complete the set; members arriving after the set closes are discarded. | all |
min | Fire at N members, then re-fire as more of the same tag-set arrive (growing set). | — |
within | ms — all required members present and their post times (send time) within the window. | — |
time | ms — as within but on content times (created time). time="0" = identical content time. | — |
mode | snapshot — fire on the primary trigger (first child, or primary="true"), attaching the latest value of each other member: no wait, no tag match. | — |
slide | Sliding-window correlation (with within=): re-emit a fresh joined set each time a member updates while the window still holds. | off |
debounce | ms — coalesce fast member streams: fire at most once per N ms with the latest set. | — |
dup | On a duplicate member before completion: latest (latest-wins) or keep (queue all). | latest |
order | strict — only fire if members arrived in declared order (pipeline-stage joins). | off |
timeout / ontimeout | On timeout="<ms>" (or maxage) expiry with an incomplete set: ontimeout="fire" partial-fires (with a level-1 warning) or "discard" drops the set. | fire |
Child <trigger> elements accept all their normal attributes (they still route individually) plus:
| Attribute | Meaning |
|---|---|
optional="true" | Member is not required for completion; included in the delivered map if present. |
maxage (per member) | A member older than its own freshness bound does not count toward completion. |
primary="true" | Marks the primary trigger for mode="snapshot" (defaults to the first child). |
SDK. A completed (or partial-fired) set arrives as one grouped activation. The crank calls waitForNewMessageGroup(ms, groupName), which returns a std::map<std::string, DataMessage*> keyed by trigger name — optional/absent members are simply not in the map (check with .count("t3")). getCurrentTriggerName() returns the group name. Messages are owned by the API for the activation and freed on the next waitForNewMessageGroup()/waitForNewMessage() call. Existing single-message cranks are untouched.
Stub grouping elements Stub
<triggers>, <retrieves>, <posts> and <signals> are recognised by the parser but their handling bodies are empty — they silently do nothing.
Roadmap-only elements Roadmap
<feed>, <supervisor>, <monitoring>, <performance> and <group> are logged and ignored; <phase> has no parsing code at all. None of these produce components or behaviour in the current build — do not rely on them.
Cross-cutting rules
- Config-node scoping: elements without
node=run on the config node; withnode=only on the named node. - Expansion order: variables and includes are processed before all other parsing; the spec is reparsed after each expansion.
- Code escaping: inside the spec, message content and setup blobs must remain valid XML.