Listeners
Let other systems trigger Tangle with their own messages - WebSocket, HTTP, TCP, UDP, OSC or serial - matched by rules that set variables, run steps and automations, and answer the sender.
A listener opens a port (or a serial line) for other systems: a touch panel, a show controller, a Stream Deck through Companion, QLab, TouchDesigner, a building management system, a webhook. They send their own strings, JSON or OSC, in their own shape. Each message is checked against the listener's rules: a matching rule sets variables, runs steps and automations, starts timelines and answers the sender.
The REST API and the WebSocket API do the same with Tangle's own messages and a token. Use a listener when the other system cannot be changed to speak them.
Listeners are stored in the project (project.listeners, contract 0.2.25 and later) and opened by the primary node, or by the node named in node.
Make a listener
- Click Listeners in the project tree, then New listener.
- Give it a Name. Module stays on Built-in listeners unless you installed another listener module.
- Under Where it listens, choose the Transport and fill in its settings:
| Transport | Settings | A message is |
|---|---|---|
websocket | Port, WebSocket path (default /), Listen on address | One text frame. The reply goes back on the same connection. |
http | Port, Listen on address | One request. The reply is the HTTP response. |
tcp | Port, Listen on address, Message format, End of message | One line (or up to the end-of-message characters you set, such as \r\n or \x03). |
udp | Port, Listen on address, Message format | One datagram. With format osc, one OSC message (bundles are split). |
serial | Serial port, Baud rate, Data bits, Parity, Stop bits, Message format, End of message | One line. The serial port can be a network serial port (tcp://gateway:4001), see Devices. |
Message format is text, json, hex (raw bytes shown as hex) or osc. Listen on address 0.0.0.0 listens on every network card, 127.0.0.1 on this computer only. Ports under 1024 need Tangle to run with more rights.
- Under Who may send:
- Allowed hosts: addresses or ranges (one per line,
192.168.1.0/24). Empty: this computer and private networks only.*allows everyone. - Token: a secret senders must give, as
Authorization: Bearer <token>or?token=<token>over HTTP and WebSocket, or as the first line over TCP and serial. It is kept in the credential store and never shown again. - Messages per second (default 50), Burst (100) and Largest message (65536 bytes). Extra messages are dropped and counted, never queued.
- Allowed hosts: addresses or ranges (one per line,
- Add rules, then click Create listener.
Open closes the listener without deleting it when unticked.
Rules
Rules are checked in order and the first that matches wins, unless it has Keep checking the next rules. Each rule has a Match:
| Match | Matches | Captures |
|---|---|---|
| Any message | Every message | |
| Exact text | The whole message (a trailing line end is ignored), optionally ignoring case | |
| Starts with | Messages that start with the text | {{rest}} |
| Regular expression | A regular expression (flags i, m, s, x) | Named groups as {{name}}, every group as {{_1}}, {{_2}}… |
| JSON value | The message parsed as JSON, optionally at a path ($.cue.number) | {{value}}, and named paths |
| OSC address | An OSC address pattern (/cue/*/go, ?, [a-z], {a,b}) | Wildcards as {{_1}}…, arguments as {{args}}, {{arg1}}… |
| HTTP route | A method and a path (/cue/:number/*) | :name as {{name}}, * as {{rest}}, plus {{query}}, {{headers}} and {{body}} |
Every run also gets {{listener}}, {{rule}}, {{message}}, {{json}} (the message as JSON, when it parses) and {{from}} (the sender's address). Captures that look like numbers become numbers, unless the rule has Keep captures as text.
What a matching rule does:
- Set variables: each variable gets a template (
{{number}}) or an expression. A project variable is set for everyone (and its variable triggers fire); any other name is a run variable for the rule's steps. - Steps: any automation step: an intent, a device action, a timeline, Run an automation, an if, and so on, with the captures as
{{name}}. - Answer: a reply template.
"{{name}}"alone keeps the capture's type;"OK {{cue}}"fills it in as text; JSON objects are filled inside. Over HTTP you can also set the status and headers. Answer when the steps are done waits for them (at most 10 s) before replying.
Automations can also start on a rule with the trigger A listener message matches (Automations). That keeps the work in one automation that a schedule or a button can run too.
When no rule matches sets the reply for other messages (HTTP answers 404 by default) and whether they are logged.
Example: start the gallery from another system
This HTTP listener lets any system on the network start and stop the room built in the tutorial with a plain HTTP request:
{
"id": "gallery-http",
"name": "Gallery HTTP",
"module": "tangle.builtin.listener",
"config": { "transport": "http", "port": 9100 },
"rules": [
{
"id": "start", "name": "Start",
"match": { "kind": "http", "method": "POST", "path": "/gallery/start" },
"steps": [{ "kind": "automation", "automationId": "start-installation", "wait": false }],
"reply": "OK"
},
{
"id": "stop", "name": "Stop",
"match": { "kind": "http", "method": "POST", "path": "/gallery/stop" },
"steps": [{ "kind": "automation", "automationId": "stop-installation", "wait": false }],
"reply": "OK"
}
]
}
curl -X POST http://<core address>:9100/gallery/start # OK
Other examples:
- QLab or a lighting console sends OSC
/tangle/cue/12over UDP: an OSC address rule/tangle/cue/*runs a timeline stepgowithcue{{_1}}. - A control system sends
SCENE 3\rover TCP: a Regular expression rule^SCENE (?<n>\d+)$sets the project variablesceneto{{n}}, and an automation that starts whenscenechanges does the rest. - A webhook posts JSON: a JSON value rule at
$.eventwith the valuedoor.openruns the steps.
Messages and tests
The listener page shows its Status (opening, listening, error, closed) and the Messages log, live: each message with its sender, the rule that matched, the captures, the runs it started and the reply. Filter it by matched, unmatched, dropped or test messages.
Try it sends a message to the saved rules without a real sender. By default it is a dry run that only shows what would match; tick Also set variables and run the steps to run them as a real message would.
API
- REST:
GET /api/v1/listeners(with status),POST /api/v1/listeners,GET,PUTandDELETE /api/v1/listeners/{id},GET /api/v1/listeners/{id}/log,GET /api/v1/listeners/{id}/runs,POST /api/v1/listeners/{id}/test({"text": "SCENE 3", "run": false};bytes,oscorhttpfor other kinds of message). - Events:
listener.message:<id>for every message received. - MCP:
get_listeners,get_listener_runs,test_listener, andcreate_listener/update_listener.
Listener modules
The built-in listener (tangle.builtin.listener) covers WebSocket, HTTP, TCP, UDP (text, JSON, hex, OSC) and serial. Other modules can open listeners too: a module of category listener declares its transports and settings in a listener block of its manifest, and only owns the socket and the framing. Core does the matching, the security checks, the rate limits, the log and the tester. See Module SDK.