How can I listen for TCP data
Opscotch 3.1.1 adds trigger.tcp, allowing workflows to bind to a bootstrap-declared server and process framed TCP input.
The feature is intentionally narrow: the current trigger supports delimiter-based framing or fixed-length framing, with optional batching controls for delimiter-based traffic.
The community repository includes a working example under:
Bootstrap Setup
TCP triggers attach to a server declared in allowHttpServerAccess.
[
{
"deploymentId": "tcp-example",
"remoteConfiguration": "tcp.config.json",
"allowHttpServerAccess": [
{
"id": "tcp-listener",
"port": 13000,
"tcp": true
}
]
}
]
Two things matter here:
- the workflow trigger references the server by
id tcpmust betrueon that server entry
If the server exists but tcp is not enabled, the runtime raises TT3.
Workflow Trigger Shape
The trigger.tcp schema requires:
server- exactly one of
delimiterorfixedLength
The example workflow in tcp.config.json uses delimiter framing with batching:
{
"workflows": [
{
"name": "TCP Listener",
"steps": [
{
"stepId": "tcp-listener",
"trigger": {
"tcp": {
"server": "tcp-listener",
"delimiter": "|",
"stripDelimiter": true,
"maxBatchWindowMillis": 100,
"batchSize": 5
},
"timer": {
"period": 1000
}
},
"resultsProcessor": {
"resource": "apps/tcp-example/throughput-counter.js"
}
}
]
}
]
}
Multiple Triggers on One Step
The example app is also a valid multiple-trigger pattern.
In Patterns, Opscotch documents that a step can have multiple triggers and will execute when any of them fires. The tcp-example app applies that pattern directly:
- the
tcptrigger ingests framed socket traffic - the
timertrigger runs once per second
That combination lets one step both receive TCP data and periodically report on accumulated state. In the example app, the TCP path updates counters and the timer path logs throughput.
Framing Modes
Delimiter-Based
Use delimiter when frames are separated by a known byte sequence. The example app uses |:
{
"trigger": {
"tcp": {
"server": "tcp-listener",
"delimiter": "|",
"stripDelimiter": true
}
}
}
stripDelimiter defaults to true, so the emitted payload excludes the delimiter unless you set it to false.
Fixed-Length
Use fixedLength when every frame is the same size:
{
"trigger": {
"tcp": {
"server": "tcp-listener",
"fixedLength": 1024
}
}
}
Batching
Batching is configured directly on trigger.tcp with:
batchSizemaxBatchWindowMillis
The example app also shows the valid batching fields directly on trigger.tcp:
{
"trigger": {
"tcp": {
"server": "tcp-listener",
"delimiter": "|",
"batchSize": 5,
"maxBatchWindowMillis": 100
}
}
}
Important runtime rule: batching requires delimiter. The implementation rejects batching with fixedLength.
From the API reference:
batchSizedefaults to1maxBatchWindowMillisdefaults to0meaning disabled- when batching is used, the workflow payload is emitted as a JSON array string of UTF-8 frame strings
Driving the Example
The included test.sh script sends repeated blobs to 127.0.0.1:13000 using nc. It builds key=value pairs separated by |, which matches the workflow delimiter:
HOST=127.0.0.1
PORT=13000
DELIMITER="|"
printf "%s\n" "$BLOB" | nc -N "$HOST" "$PORT"
The example workflow's resultsProcessor uses the resource apps/tcp-example/throughput-counter.js, and the same step also has a timer trigger so it can periodically report throughput statistics while the TCP trigger keeps ingesting frames.
Error Codes
The TCP trigger error codes in 3.1.1 are:
| Code | Meaning |
|---|---|
TT1 | The trigger referenced a bootstrap.allowHttpServerAccess.id that does not exist |
TT2 | Invalid TCP framing configuration |
TT3 | TCP was not permitted on the referenced server |
TT4 | Exception while handling the TCP trigger |
In practice, TT2 covers cases such as:
- neither
delimiternorfixedLengthsupplied - both
delimiterandfixedLengthsupplied batchSize < 1maxBatchWindowMillis < 0- batching configured without
delimiter
Practical Guidance
Use delimiter framing for line-oriented or record-oriented protocols where batching is useful.
Use fixed-length framing for binary protocols with a stable frame size.