From 72f32df581f698695c2be52fdb3867b8502008df Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Mon, 9 Feb 2026 17:48:12 +0100 Subject: [PATCH 01/11] New executor documentation (#192) --- src/content/docs/reference/operators.mdx | 12 +++ .../docs/reference/operators/from_stdin.mdx | 74 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/content/docs/reference/operators/from_stdin.mdx diff --git a/src/content/docs/reference/operators.mdx b/src/content/docs/reference/operators.mdx index 468b963f1..34526313a 100644 --- a/src/content/docs/reference/operators.mdx +++ b/src/content/docs/reference/operators.mdx @@ -359,6 +359,10 @@ operators: description: 'Sends and receives HTTP/1.1 requests.' example: 'from_http "0.0.0.0:8080"' path: 'reference/operators/from_http' + - name: 'from_stdin' + description: 'Reads and parses events from standard input.' + example: 'from_stdin { read_json }' + path: 'reference/operators/from_stdin' - name: 'from_kafka' description: 'Receives events from an Apache Kafka topic.' example: 'from_kafka "logs"' @@ -2175,6 +2179,14 @@ from_http "0.0.0.0:8080" + + +```tql +from_stdin { read_json } +``` + + + ```tql diff --git a/src/content/docs/reference/operators/from_stdin.mdx b/src/content/docs/reference/operators/from_stdin.mdx new file mode 100644 index 000000000..b2c63ba46 --- /dev/null +++ b/src/content/docs/reference/operators/from_stdin.mdx @@ -0,0 +1,74 @@ +--- +title: from_stdin +category: Inputs/Events +example: 'from_stdin { read_json }' +--- + +import Op from '@components/see-also/Op.astro'; + +Reads and parses events from standard input. + +```tql +from_stdin { … } +``` + +## Description + +The `from_stdin` operator reads bytes from standard input and passes them +through the provided parsing pipeline to produce events. This is useful when +piping data into the `tenzir` executable as part of a shell script or command +chain. + +### `{ … }` + +The pipeline to parse the incoming bytes into events. The pipeline receives raw +bytes and must produce events. For example, `{ read_json }` parses the input as +JSON. + +## Examples + +### Parse JSON from standard input + +```sh +echo '{"foo": 42}' | tenzir +``` + +```tql +from_stdin { + read_json +} +``` + +```tql +{ + foo: 42, +} +``` + +### Parse CSV data piped from another command + +```sh +cat data.csv | tenzir -f pipeline.tql +``` + +```tql +from_stdin { + read_csv +} +``` + +### Parse Syslog messages + +```sh +tail -f /var/log/syslog | tenzir -f pipeline.tql +``` + +```tql +from_stdin { + read_syslog +} +``` + +## See Also + +- from_file From 1c9f466b58729ccce8af7e8c89cd9b7fe67f4171 Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Mon, 9 Feb 2026 18:03:04 +0100 Subject: [PATCH 02/11] Add MySQL integration documentation and from_mysql operator reference (#190) * Add MySQL integration documentation and from_mysql operator reference Adds comprehensive documentation for the from_mysql operator, including: - New integration page at /integrations/mysql - Detailed operator reference at /reference/operators/from_mysql with parameter descriptions, type mappings, and usage examples - Updates to integration and operator index pages to include MySQL The from_mysql operator enables reading data from MySQL databases using table selection, raw SQL queries, or metadata inspection. Co-Authored-By: Claude Opus 4.5 * Finalize docs --------- Co-authored-by: Claude Opus 4.5 --- src/content/docs/integrations/index.mdx | 2 +- .../docs/integrations/mysql.excalidraw | 947 ++++++++++++++++++ src/content/docs/integrations/mysql.mdx | 46 + src/content/docs/reference/operators.mdx | 12 + .../docs/reference/operators/from_mysql.mdx | 204 ++++ src/sidebar.ts | 1 + 6 files changed, 1211 insertions(+), 1 deletion(-) create mode 100644 src/content/docs/integrations/mysql.excalidraw create mode 100644 src/content/docs/integrations/mysql.mdx create mode 100644 src/content/docs/reference/operators/from_mysql.mdx diff --git a/src/content/docs/integrations/index.mdx b/src/content/docs/integrations/index.mdx index 522d3fd37..6be34f051 100644 --- a/src/content/docs/integrations/index.mdx +++ b/src/content/docs/integrations/index.mdx @@ -30,7 +30,7 @@ communication over numerous protocols and APIs: - **Message queues**: [Kafka](/integrations/kafka), [SQS](/integrations/amazon/sqs), [AMQP](/integrations/amqp) - **Databases**: [Snowflake](/integrations/snowflake), - [ClickHouse](/integrations/clickhouse) + [ClickHouse](/integrations/clickhouse), [MySQL](/integrations/mysql) - **Network protocols**: [TCP](/integrations/tcp), [UDP](/integrations/udp), [HTTP](/integrations/http), [Syslog](/integrations/syslog) diff --git a/src/content/docs/integrations/mysql.excalidraw b/src/content/docs/integrations/mysql.excalidraw new file mode 100644 index 000000000..2beae0e56 --- /dev/null +++ b/src/content/docs/integrations/mysql.excalidraw @@ -0,0 +1,947 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://app.excalidraw.com", + "elements": [ + { + "id": "8MN5I4hT3wVen_QUHAUnu", + "type": "rectangle", + "x": 1029.4206230693742, + "y": 462.25159339855855, + "width": 150.57937693062584, + "height": 143.23101048605469, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zy", + "roundness": { + "type": 3 + }, + "seed": 239993862, + "version": 526, + "versionNonce": 175282956, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Q90BHXvcB9LwBRiLiSjA9" + } + ], + "updated": 1770653676903, + "link": null, + "locked": false + }, + { + "id": "Q90BHXvcB9LwBRiLiSjA9", + "type": "text", + "x": 1067.1423107412302, + "y": 467.25159339855855, + "width": 75.13600158691406, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "Zz", + "roundness": null, + "seed": 1597739846, + "version": 506, + "versionNonce": 1537026956, + "isDeleted": false, + "boundElements": [], + "updated": 1770653655303, + "link": null, + "locked": false, + "text": "Database", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "8MN5I4hT3wVen_QUHAUnu", + "originalText": "Database", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 481, + "versionNonce": 1842653364, + "index": "aU", + "isDeleted": false, + "id": "MdBLs5DrRJv7pPr1ECBdP", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 860, + "y": 380, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 480.0000000000001, + "height": 260, + "seed": 1012386970, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [ + { + "id": "anAP1e8hQ4MPrGd5mJIPw", + "type": "arrow" + } + ], + "updated": 1770653666972, + "link": null, + "locked": true + }, + { + "type": "arrow", + "version": 1418, + "versionNonce": 1665469708, + "isDeleted": true, + "id": "g_xmr_rAUoQx04wSxq2yQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 764.4167779398937, + "y": 525.2324875938532, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "width": 231.80542997696136, + "height": 0, + "seed": 61286746, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1770653628213, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 231.80542997696136, + 0 + ] + ], + "index": "aV" + }, + { + "type": "line", + "version": 1403, + "versionNonce": 690428340, + "isDeleted": true, + "id": "K1eRli9Ss4OCth8Rxisx0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 584.4167779398937, + "y": 485.22529758183066, + "strokeColor": "#000000", + "backgroundColor": "#e9ecef", + "width": 160, + "height": 80, + "seed": 174911002, + "groupIds": [ + "nS55pT0iEmYN13lrATL_r" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1770653628213, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40, + 39.67197250080198 + ], + [ + -1.4210854715202004e-14, + 80 + ], + [ + 160, + 80 + ], + [ + 160, + 0 + ], + [ + 0, + 0 + ] + ], + "index": "aW", + "polygon": false + }, + { + "type": "image", + "version": 3810, + "versionNonce": 867666828, + "index": "aX", + "isDeleted": true, + "id": "Ob5zHrhyWqtZkihkGCreM", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 650.5291677541359, + "y": 499.0482924112763, + "strokeColor": "transparent", + "backgroundColor": "#ffc9c9", + "width": 61.54214099463984, + "height": 50.681763172056336, + "seed": 1704729306, + "groupIds": [ + "fCtmhUGKWexr3OXqoPqQN", + "nS55pT0iEmYN13lrATL_r" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1770653628213, + "link": null, + "locked": false, + "status": "saved", + "fileId": "d1ee05fd41e4fad60a7372901fb1a4d206070a057f9b3a48768235e2c5775230a90d16ba6804ee1fbd79befd1ec8cbbf", + "scale": [ + 1, + 1 + ], + "crop": null + }, + { + "id": "tYpVzQAJgHPTVmA_sLOkS", + "type": "text", + "x": 886.072345512997, + "y": 537.0310114160328, + "width": 108.73332977294922, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ae", + "roundness": null, + "seed": 1953084442, + "version": 241, + "versionNonce": 771867444, + "isDeleted": true, + "boundElements": [], + "updated": 1770653628213, + "link": null, + "locked": false, + "text": "clickhouse-cpp", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "clickhouse-cpp", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "NsxTI00fJ7Tk4JFzEOGL_", + "type": "image", + "x": 994.4490034404632, + "y": 385.84069071345743, + "width": 221.3677416168746, + "height": 67.5359211712499, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ah", + "roundness": null, + "seed": 135755354, + "version": 509, + "versionNonce": 1203241908, + "isDeleted": true, + "boundElements": [], + "updated": 1770653606682, + "link": null, + "locked": false, + "status": "saved", + "fileId": "74235aaa80d5377e7af618a88eaf3165fae35889019cba2a4a62571f5ce6a7f9ad2989f927cd6093f58b899f2e11d669", + "scale": [ + 1, + 1 + ], + "crop": null + }, + { + "id": "grimbjvvQaXcX0fZ1QGkH", + "type": "rectangle", + "x": 1065.2209485133449, + "y": 508.5832717947994, + "width": 79.72278435602915, + "height": 56.34336263774654, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "3KLhhHW84bohbevIB1Hzc" + ], + "frameId": null, + "index": "amd", + "roundness": null, + "seed": 1503711494, + "version": 762, + "versionNonce": 932232454, + "isDeleted": false, + "boundElements": [], + "updated": 1741098374272, + "link": null, + "locked": false + }, + { + "id": "jDCSnbgRZUSNDrfjEd8Fc", + "type": "line", + "x": 1065.581736075349, + "y": 533.2097230148814, + "width": 78.67259902626967, + "height": 0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "3KLhhHW84bohbevIB1Hzc" + ], + "frameId": null, + "index": "aml", + "roundness": { + "type": 2 + }, + "seed": 816627782, + "version": 333, + "versionNonce": 566176838, + "isDeleted": false, + "boundElements": [], + "updated": 1741098374272, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 78.67259902626967, + 0 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null, + "polygon": false + }, + { + "id": "x1F877POVQOiJKca5RDRq", + "type": "line", + "x": 1081.316255880603, + "y": 533.2097230148814, + "width": 0.12437256409457782, + "height": 31.7169114176644, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "3KLhhHW84bohbevIB1Hzc" + ], + "frameId": null, + "index": "amt", + "roundness": { + "type": 2 + }, + "seed": 1996015494, + "version": 330, + "versionNonce": 1927132038, + "isDeleted": false, + "boundElements": [], + "updated": 1741098374272, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.12437256409457782, + 31.7169114176644 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null, + "polygon": false + }, + { + "id": "okS3KPOvVBZwY3ofsj0Uc", + "type": "line", + "x": 1097.3058925944924, + "y": 533.52609872761, + "width": 0.17604688976762406, + "height": 31.400535704935898, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "3KLhhHW84bohbevIB1Hzc" + ], + "frameId": null, + "index": "an", + "roundness": { + "type": 2 + }, + "seed": 2093082310, + "version": 341, + "versionNonce": 86594246, + "isDeleted": false, + "boundElements": [], + "updated": 1741098374272, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.17604688976762406, + 31.400535704935898 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null, + "polygon": false + }, + { + "id": "ZJrOj4tP-Rs6DlBgjxBwu", + "type": "line", + "x": 1113.4949144809275, + "y": 533.5112964970239, + "width": 0.42710638798645123, + "height": 31.415337935521947, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "3KLhhHW84bohbevIB1Hzc" + ], + "frameId": null, + "index": "anV", + "roundness": { + "type": 2 + }, + "seed": 44375558, + "version": 337, + "versionNonce": 1665075718, + "isDeleted": false, + "boundElements": [], + "updated": 1741098374272, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -0.42710638798645123, + 31.415337935521947 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null, + "polygon": false + }, + { + "id": "vYTHyZXj8YpcxC4gKL6cW", + "type": "line", + "x": 1128.9366108631093, + "y": 533.4277221087583, + "width": 0.06915961804813092, + "height": 31.498912323787575, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "3KLhhHW84bohbevIB1Hzc" + ], + "frameId": null, + "index": "ao", + "roundness": { + "type": 2 + }, + "seed": 765422918, + "version": 337, + "versionNonce": 1008458054, + "isDeleted": false, + "boundElements": [], + "updated": 1741098374272, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 0.06915961804813092, + 31.498912323787575 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null, + "polygon": false + }, + { + "id": "AL2_d8YDszRMsO8BuMuM5", + "type": "text", + "x": 1084.4878148765415, + "y": 510.21968757478163, + "width": 42.39994812011719, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "3KLhhHW84bohbevIB1Hzc" + ], + "frameId": null, + "index": "ap", + "roundness": null, + "seed": 2064503942, + "version": 222, + "versionNonce": 1461502086, + "isDeleted": false, + "boundElements": [], + "updated": 1741098374272, + "link": null, + "locked": false, + "text": "Table", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Table", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "sr_2aHlCdgC88c4nWzhF_", + "type": "image", + "x": 1040, + "y": 390, + "width": 140, + "height": 70, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aq", + "roundness": null, + "seed": 1850443956, + "version": 24, + "versionNonce": 500443700, + "isDeleted": false, + "boundElements": [], + "updated": 1770653635369, + "link": null, + "locked": false, + "status": "saved", + "fileId": "2e07883d1d9f64a8e2b93ea39a266e26f42f707d5579bf9a1348070b734c295d8c0dbca08e635fd04d72dbf979c6b05b", + "scale": [ + 1, + 1 + ], + "crop": null + }, + { + "type": "arrow", + "version": 542, + "versionNonce": 407558580, + "isDeleted": false, + "id": "anAP1e8hQ4MPrGd5mJIPw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1224.5442617137614, + "y": 539.1944379296762, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 205.2903283354567, + "height": 0, + "seed": 1781230860, + "groupIds": [], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1770654115841, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 205.2903283354567, + 0 + ] + ], + "index": "ar", + "moveMidPointsWithElement": false + }, + { + "type": "line", + "version": 1174, + "versionNonce": 1814268980, + "isDeleted": false, + "id": "kVLOfrXYUm_bhK00KWUdN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1441.5929310543029, + "y": 500.72686469852636, + "strokeColor": "#000000", + "backgroundColor": "#e9ecef", + "width": 160, + "height": 80, + "seed": 1322380172, + "groupIds": [ + "FqtwZvej4SaxiNmq7rWnL" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1770653674836, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -1.4896611218806584, + 80 + ], + [ + 118.51033887811936, + 79.99999999999999 + ], + [ + 158.51033887811934, + 40.000000000000014 + ], + [ + 118.51033887811934, + -7.105427357601002e-15 + ], + [ + 0, + 0 + ] + ], + "index": "as", + "polygon": false + }, + { + "type": "image", + "version": 3781, + "versionNonce": 1842079156, + "index": "at", + "isDeleted": false, + "id": "qcU_lCbEEJfrEK51E_Uwk", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1480.3186257099073, + "y": 513.6204399910713, + "strokeColor": "transparent", + "backgroundColor": "#ffc9c9", + "width": 61.54214099463984, + "height": 50.681763172056336, + "seed": 119839244, + "groupIds": [ + "SH9Y4XZl0F0a3RqnagE-E", + "FqtwZvej4SaxiNmq7rWnL" + ], + "frameId": null, + "roundness": null, + "boundElements": [], + "updated": 1770653674836, + "link": null, + "locked": false, + "status": "saved", + "fileId": "d1ee05fd41e4fad60a7372901fb1a4d206070a057f9b3a48768235e2c5775230a90d16ba6804ee1fbd79befd1ec8cbbf", + "scale": [ + 1, + 1 + ], + "crop": null + }, + { + "id": "udf33bdWTCHF0aOMjJPnP", + "type": "arrow", + "x": 1199.9999997629093, + "y": 539.6403573296411, + "width": 20, + "height": 0.46871015741385236, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "au", + "roundness": null, + "seed": 1735537332, + "version": 414, + "versionNonce": 1476357516, + "isDeleted": false, + "boundElements": [], + "updated": 1770654102092, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -20, + -0.46871015741385236 + ] + ], + "lastCommittedPoint": null, + "startBinding": null, + "endBinding": null, + "startArrowhead": null, + "endArrowhead": null, + "elbowed": false + }, + { + "id": "2YeGA667pdqopGszKYHHk", + "type": "ellipse", + "x": 1201.3800898153793, + "y": 531.4518874011399, + "width": 13.508293047904658, + "height": 13.508293047904658, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#e9ecef", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "av", + "roundness": { + "type": 2 + }, + "seed": 92388404, + "version": 423, + "versionNonce": 1397873676, + "isDeleted": false, + "boundElements": [], + "updated": 1770654102092, + "link": null, + "locked": false + }, + { + "id": "Y6dgqoPj8ThOFAFcVcc8l", + "type": "text", + "x": 1194.0364892454452, + "y": 549.4106528039305, + "width": 28.195493698120117, + "height": 21.829895655804975, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aw", + "roundness": null, + "seed": 467932596, + "version": 369, + "versionNonce": 1155449268, + "isDeleted": false, + "boundElements": [], + "updated": 1770654104858, + "link": null, + "locked": false, + "text": "MySQL\nAPI", + "fontSize": 8.73195826232199, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "MySQL\nAPI", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff", + "lockedMultiSelections": {} + }, + "files": { + "2e07883d1d9f64a8e2b93ea39a266e26f42f707d5579bf9a1348070b734c295d8c0dbca08e635fd04d72dbf979c6b05b": { + "mimeType": "image/svg+xml", + "id": "2e07883d1d9f64a8e2b93ea39a266e26f42f707d5579bf9a1348070b734c295d8c0dbca08e635fd04d72dbf979c6b05b", + "dataURL": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjAgMCA5LjI1MiA0LjYyNiI+PGcgdHJhbnNmb3JtPSJtYXRyaXgoLjAzNzM3NiAwIDAgLjAzNzM3NiAxLjA2OTk5NCAtMS4zMTkzMzkpIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik04LjUwNCAxMjguMjE1aDUuOHYtMjIuOTc3bDkuMDU4IDIwLjAzM2MxLjAyNiAyLjQwOCAyLjUgMy4zIDUuMzU0IDMuM3M0LjI0LS44OTMgNS4zLTMuM2w5LjAxMy0yMC4wMzN2MjIuOTc3aDUuODQ1di0yMi45NzdjMC0yLjIzLS44OTMtMy4zMDMtMi43NjctMy44ODMtNC40MTctMS4zMzgtNy4zNjItLjE3OC04LjcgMi44MWwtOC44NzggMTkuODEtOC41NjctMTkuODFjLTEuMjk0LTIuOTg4LTQuMjg0LTQuMTQ4LTguNzQ1LTIuODEtMS44My41OC0yLjcyMiAxLjY1Mi0yLjcyMiAzLjg4M2wtLjAwMSAyMi45Nzd6bTQ1LjE5OC0xOC42OTRoNS44NDV2MTIuNjI3Yy0uMDQ0LjcxMy4yMjMgMi4zMiAzLjQgMi4zNjMgMS42NS4wNDUgMTIuNTgyIDAgMTIuNjcgMHYtMTUuMDhoNS44NDV2MjAuNjU4YzAgNS4wODYtNi4zIDYuMi05LjIzNiA2LjI0NmgtMTguMzh2LTMuODhoMTguNDI3YzMuNzQ4LS40MDIgMy4zMDItMi4yNzUgMy4zMDItMi45di0xLjUxOGgtMTIuMzZjLTUuNzU2LS4wNDUtOS40Ni0yLjU4OC05LjUwMy01LjQ4OHYtMTMuMDN6bTEyNS4zNzQtMTQuNjM1Yy0zLjU2OC0uMDktNi4zMzYuMjY4LTguNjU2IDEuMjUtLjY2OC4yNy0xLjc0LjI3LTEuODI4IDEuMTE2LjM1Ny4zNTUuNC45MzYuNzEzIDEuNDI4LjUzNS44OTMgMS40NzMgMi4wOTYgMi4zMiAyLjcyLjkzOC43MTUgMS44NzUgMS40MjggMi44NTUgMi4wNTMgMS43NCAxLjA3IDMuNzAzIDEuNjk1IDUuMzk4IDIuNzY2Ljk4Mi42MjUgMS45NjMgMS40MjggMi45NDUgMi4wOTguNS4zNTcuODAzLjkzOCAxLjQyOCAxLjE2di0uMTM1Yy0uMzEyLS40LS40MDItLjk4LS43MTMtMS40MjgtLjQ0Ny0uNDQ1LS44OTMtLjg0OC0xLjM0LTEuMjkzLTEuMjkzLTEuNzQtMi45LTMuMjU4LTQuNjQtNC41MDYtMS40MjgtLjk4Mi00LjU1LTIuMzItNS4xMy0zLjk3bC0uMDg4LS4wOWMuOTgtLjA5IDIuMTQtLjQ0NyAzLjA3OC0uNzE1IDEuNTE4LS40IDIuOS0uMzEyIDQuNDYtLjcxMy43MTUtLjE4IDEuNDI4LS40MDIgMi4xNDMtLjYyNXYtLjRjLS44MDMtLjgwMy0xLjM4My0xLjg3NC0yLjIzLTIuNjMyLTIuMjc1LTEuOTYzLTQuNzc1LTMuODgyLTcuMzYzLTUuNDg4LTEuMzgzLS44OTItMy4xNjgtMS40NzMtNC42NC0yLjIzLS41MzctLjI2OC0xLjQyOC0uNDAyLTEuNzQtLjg0OC0uODA1LS45OC0xLjI1LTIuMjc1LTEuODMtMy40MzYtMS4yOTMtMi40NTQtMi41NDMtNS4xNzUtMy42NTgtNy43NjMtLjgwMy0xLjc0LTEuMjk1LTMuNDgtMi4yNzUtNS4wODYtNC41OTYtNy41ODUtOS41OTQtMTIuMTgtMTcuMjY4LTE2LjY4Ny0xLjY1LS45MzctMy42MTMtMS4zNC01LjctMS44M2wtMy4zNDYtLjE4Yy0uNzE1LS4zMTItMS40MjgtMS4xNi0yLjA1My0xLjU2Mi0yLjU0My0xLjYwNi05LjEwMi01LjA4Ni0xMC45NzctLjQ5LTEuMjA1IDIuOSAxLjc4NSA1Ljc1NSAyLjggNy4yMjguNzYgMS4wMjYgMS43NCAyLjE4NiAyLjI3NyAzLjM0Ni4zLjc1OC40IDEuNTYyLjcxMyAyLjM2NS43MTMgMS45NjMgMS4zODMgNC4xNSAyLjMyIDUuOTguNS45MzcgMS4wMjUgMS45MiAxLjY1IDIuNzY3LjM1Ny40OS45ODIuNzE0IDEuMTE1IDEuNTE3LS42MjUuODkzLS42NjggMi4yMy0xLjAyNSAzLjM0Ny0xLjYwNyA1LjA0Mi0uOTgyIDExLjI4OCAxLjI5MyAxNC45OS43MTUgMS4xMTUgMi40IDMuNTcgNC42ODYgMi42MzIgMi4wMDgtLjgwMyAxLjU2LTMuMzQ2IDIuMTQtNS41NzcuMTM1LS41MzUuMDQ1LS44OTIuMzEyLTEuMjV2LjA5bDEuODMgMy43MDNjMS4zODMgMi4xODYgMy43OTMgNC40NjIgNS44IDUuOTggMS4wNy44MDMgMS45MTggMi4xODcgMy4yNTYgMi42Nzd2LS4xMzVoLS4wODhjLS4yNjgtLjQtLjY3LS41OC0xLjAyNy0uODkyLS44MDMtLjgwMy0xLjY5NS0xLjc4NS0yLjMyLTIuNjc3LTEuODczLTIuNDk4LTMuNTIzLTUuMjY1LTQuOTk2LTguMTItLjcxNS0xLjM4My0xLjM0LTIuOS0xLjkxOC00LjI4My0uMjctLjUzNi0uMjctMS4zNC0uNzE1LTEuNjA2LS42Ny45OC0xLjY1IDEuODMtMi4xNDMgMy4wMzQtLjg0OCAxLjkxOC0uOTM2IDQuMjgzLTEuMjQ4IDYuNzM3LS4xOC4wNDUtLjEgMC0uMTguMDktMS40MjYtLjM1Ni0xLjkxOC0xLjgzLTIuNDUzLTMuMDc4LTEuMzM4LTMuMTY4LTEuNTYyLTguMjU0LS40MDItMTEuOTEzLjMxMi0uOTM3IDEuNjUyLTMuODgyIDEuMTE3LTQuNzc0LS4yNy0uODQ4LTEuMTYtMS4zMzgtMS42NTItMi4wMDgtLjU4LS44NDgtMS4yMDMtMS45MTgtMS42MDUtMi44NTUtMS4wNy0yLjUtMS42MDUtNS4yNjUtMi43NjYtNy43NjQtLjUzNy0xLjE2LTEuNDczLTIuMzY1LTIuMjMyLTMuNDM1LS44NDgtMS4yMDUtMS43ODMtMi4wNTMtMi40NTMtMy40OC0uMjIzLS40OS0uNTM1LTEuMjk0LS4xNzgtMS44My4wODgtLjM1Ny4yNjgtLjQ5LjYyMy0uNTguNTgtLjQ5IDIuMjMyLjEzNCAyLjgxMi40IDEuNjUuNjcgMy4wMzMgMS4yOTQgNC40MTYgMi4yMy42MjUuNDQ2IDEuMjk1IDEuMjk0IDIuMDk4IDEuNTE4aC45MzhjMS40MjguMzEyIDMuMDMzLjA5IDQuMzcuNDkgMi4zNjUuNzYgNC41MDYgMS44NzQgNi40MjYgMy4wOCA1Ljg0NCAzLjcwMyAxMC42NjQgOC45NjggMTMuOTIgMTUuMjYuNTM1IDEuMDI2Ljc1OCAxLjk2MyAxLjI1IDMuMDM0LjkzOCAyLjE4NyAyLjA5OCA0LjQxNyAzLjAzMyA2LjU2LjkzOCAyLjA5NyAxLjgzIDQuMjQgMy4xNjggNS45OC42Ny45MzcgMy4zNDYgMS40MjcgNC41NSAxLjkxOC44OTMuNCAyLjI3NS43NiAzLjA4IDEuMjUgMS41MTYuOTM3IDMuMDMzIDIuMDA4IDQuNDYgMy4wMzQuNzEzLjUzNCAyLjk0NSAxLjY1IDMuMDc4IDIuNTR6bS00NS41LTM4Ljc3MmE3LjA5IDcuMDkgMCAwIDAtMS44MjguMjIzdi4wOWguMDg4Yy4zNTcuNzE0Ljk4MiAxLjIwNSAxLjQyOCAxLjgzbDEuMDI3IDIuMTQyLjA4OC0uMDljLjYyNS0uNDQ2LjkzOC0xLjE2LjkzOC0yLjIzLS4yNjgtLjMxMi0uMzEyLS42MjUtLjUzNS0uOTM3LS4yNjgtLjQ0Ni0uODQ4LS42Ny0xLjIwNi0xLjAyNnoiIGZpbGw9IiMwMDY3OGMiLz48cGF0aCBkPSJNODUuOTE2IDEyOC4yMTVoMTYuNzc2YzEuOTYzIDAgMy44MzgtLjQgNS4zNTQtMS4xMTUgMi41NDMtMS4xNiAzLjc0OC0yLjcyIDMuNzQ4LTQuNzczdi00LjI4M2MwLTEuNjUtMS4zODMtMy4yMTMtNC4xNDgtNC4yODMtMS40MjgtLjUzNS0zLjIxMy0uODQ4LTQuOTUzLS44NDhoLTcuMDVjLTIuMzY1IDAtMy40OC0uNzE1LTMuNzkzLTIuMjc1LS4wNDQtLjE3OC0uMDQ0LS4zNTctLjA0NC0uNTM1di0yLjYzM2MwLS4xMzUgMC0uMzEyLjA0NC0uNDkuMzEyLTEuMjA1LjkzNy0xLjUxOCAzLTEuNzRoMTcuMTc3di0zLjg4M2gtMTYuMzNjLTIuMzY1IDAtMy42MTQuMTM1LTQuNzMuNDkyLTMuNDM2IDEuMDctNC45NTMgMi43NjYtNC45NTMgNS43NTR2My4zOTNjMCAyLjYzIDIuOTQ1IDQuODYzIDcuOTQyIDUuMzk4LjUzNS4wNDUgMS4xMTUuMDQ1IDEuNjk1LjA0NWg2LjAyNGMuMjIzIDAgLjQ0NSAwIC42MjMuMDQ1IDEuODMuMTc4IDIuNjMzLjQ5IDMuMTY4IDEuMTU4LjM1Ny4zNTcuNDQ3LjY3LjQ0NyAxLjA3MnYzLjM5YzAgLjQtLjI2OC45MzgtLjgwMyAxLjM4M3MtMS4zODUuNzU4LTIuNS44MDNjLS4yMjMgMC0uMzU1LjA0NS0uNTguMDQ1SDg1LjkxNnptNjIuMTk1LTYuNzM2YzAgMy45NyAzIDYuMiA4Ljk3IDYuNjQ4LjU4LjA0NSAxLjExNS4wODggMS42OTUuMDg4aDE1LjE3di0zLjg4aC0xNS4zMDNjLTMuMzkzIDAtNC42ODYtLjg0OC00LjY4Ni0yLjl2LTIwLjA3OEgxNDguMXYyMC4xMjN6bS0zMi42MTUuMTc3di0xMy44M2MwLTMuNTI1IDIuNDk4LTUuNjY4IDcuMzYzLTYuMzM2LjUzNS0uMDQ1IDEuMDctLjA5IDEuNTYtLjA5aDExLjA2NGMuNTggMCAxLjA3Mi4wNDUgMS42NTIuMDkgNC44NjMuNjY4IDcuMzE2IDIuODEgNy4zMTYgNi4zMzZ2MTMuODNjMCAyLjg1NS0xLjAyNSA0LjM3My0zLjQzNiA1LjRsNS43MSA1LjE3NGgtNi43MzZsLTQuNjQtNC4xOTMtNC42ODYuMjY4aC02LjI0NmExMy42NiAxMy42NiAwIDAgMS0zLjM5MS0uNDQ1Yy0zLjctMS4wMjgtNS41My0yLjk5LTUuNTMtNi4yMDR6bTYuMjktLjMxYzAgLjE3OC4xLjM1NS4xMzUuNTguMzEyIDEuNjA1IDEuODI4IDIuNDk4IDQuMTQ4IDIuNDk4aDUuMjY2bC00LjgxOC00LjM3M2g2LjczNmw0LjIzOCAzLjgzOGMuODA1LS40NDcgMS4yOTUtMS4wNzIgMS40NzMtMS44NzUuMDQ1LS4xNzguMDQ1LS40LjA0NS0uNTh2LTEzLjI1MmMwLS4xNzggMC0uMzU1LS4wNDUtLjUzNS0uMzEyLTEuNTE2LTEuODI4LTIuMzYzLTQuMTA0LTIuMzYzaC04Ljc5Yy0yLjU4OCAwLTQuMjgzIDEuMTE1LTQuMjgzIDIuODk4eiIgZmlsbD0iI2NlOGIyYyIvPjwvZz48L3N2Zz4=", + "created": 1770653604842 + }, + "d1ee05fd41e4fad60a7372901fb1a4d206070a057f9b3a48768235e2c5775230a90d16ba6804ee1fbd79befd1ec8cbbf": { + "mimeType": "image/svg+xml", + "id": "d1ee05fd41e4fad60a7372901fb1a4d206070a057f9b3a48768235e2c5775230a90d16ba6804ee1fbd79befd1ec8cbbf", + "dataURL": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4NSIgaGVpZ2h0PSI3MCIgdmlld0JveD0iMCAwIDg1IDcwIiBmaWxsPSJub25lIj4KPHBhdGggZD0iTTI5LjQ1IDQ0LjQyTDQyLjc3IDEyLjkzQzQyLjgyNjkgMTIuODA1OCA0Mi44NTc4IDEyLjY3MTMgNDIuODYwNyAxMi41MzQ4QzQyLjg2MzYgMTIuMzk4MiA0Mi44Mzg2IDEyLjI2MjUgNDIuNzg3IDEyLjEzNkM0Mi43MzU1IDEyLjAwOTUgNDIuNjU4NiAxMS44OTQ5IDQyLjU2MTEgMTEuNzk5M0M0Mi40NjM1IDExLjcwMzcgNDIuMzQ3NSAxMS42MjkgNDIuMjIgMTEuNThDNDIuMDkyOCAxMS41Mjg2IDQxLjk1NzEgMTEuNTAxNSA0MS44MiAxMS41SDkuMjc5OTlDOS4wNzYzMiAxMS40OTU4IDguODc2MjEgMTEuNTUzOCA4LjcwNjQzIDExLjY2NjRDOC41MzY2NiAxMS43NzkgOC40MDUzMiAxMS45NDA3IDguMzI5OTkgMTIuMTNMMy4xMDk5OSAyNC42M0MzLjA1IDI0Ljc1NTggMy4wMTY5NSAyNC44OTI3IDMuMDEyOTggMjUuMDMyQzMuMDA5MDEgMjUuMTcxMyAzLjAzNDE5IDI1LjMwOTkgMy4wODY5MiAyNS40Mzg5QzMuMTM5NjUgMjUuNTY3OSAzLjIxODc1IDI1LjY4NDQgMy4zMTkxNiAyNS43ODExQzMuNDE5NTcgMjUuODc3NyAzLjUzOTA3IDI1Ljk1MjMgMy42Njk5OSAyNkMzLjc5MzMxIDI2LjA1MjUgMy45MjU5NCAyNi4wNzk3IDQuMDU5OTkgMjYuMDhIMTkuODJDMjAuMDg1MiAyNi4wOCAyMC4zMzk2IDI2LjE4NTQgMjAuNTI3MSAyNi4zNzI5QzIwLjcxNDYgMjYuNTYwNCAyMC44MiAyNi44MTQ4IDIwLjgyIDI3LjA4QzIwLjgxODUgMjcuMjE3MiAyMC43OTE0IDI3LjM1MjggMjAuNzQgMjcuNDhMNy41OTk5OSA1OS4wN0M3LjU0MzMyIDU5LjE5NDggNy41MTI5MiA1OS4zMjk5IDcuNTEwNjUgNTkuNDY2OUM3LjUwODM5IDU5LjYwNCA3LjUzNDMyIDU5Ljc0IDcuNTg2ODMgNTkuODY2NkM3LjYzOTM1IDU5Ljk5MzIgNy43MTczMiA2MC4xMDc3IDcuODE1OTIgNjAuMjAyOUM3LjkxNDUyIDYwLjI5OCA4LjAzMTYzIDYwLjM3MiA4LjE1OTk5IDYwLjQyQzguMjgzMzEgNjAuNDcyNSA4LjQxNTk0IDYwLjQ5OTcgOC41NDk5OSA2MC41SDQzQzQzLjIxMzIgNjAuNTE0MSA0My40MjUzIDYwLjQ1OTYgNDMuNjA1MiA2MC4zNDQ1QzQzLjc4NTIgNjAuMjI5MyA0My45MjM1IDYwLjA1OTUgNDQgNTkuODZMNDkuMjIgNDcuMjhDNDkuMjc2NyA0Ny4xNTUyIDQ5LjMwNzEgNDcuMDIwMSA0OS4zMDkzIDQ2Ljg4MzFDNDkuMzExNiA0Ni43NDYgNDkuMjg1NyA0Ni42MSA0OS4yMzMxIDQ2LjQ4MzRDNDkuMTgwNiA0Ni4zNTY4IDQ5LjEwMjcgNDYuMjQyNCA0OS4wMDQxIDQ2LjE0NzJDNDguOTA1NSA0Ni4wNTIgNDguNzg4MyA0NS45NzggNDguNjYgNDUuOTNDNDguNTM2NyA0NS44Nzc1IDQ4LjQwNCA0NS44NTAzIDQ4LjI3IDQ1Ljg1SDMwLjRDMzAuMTM0OCA0NS44NSAyOS44ODA0IDQ1Ljc0NDYgMjkuNjkyOSA0NS41NTcxQzI5LjUwNTMgNDUuMzY5NiAyOS40IDQ1LjExNTIgMjkuNCA0NC44NUMyOS4zODggNDQuNzA0OCAyOS40MDUgNDQuNTU4NiAyOS40NSA0NC40MloiIGZpbGw9IiMxMjEyMTIiLz4KPHBhdGggZD0iTTM3LjE3IDQxLjU4SDUwLjg4QzUxLjA4MzcgNDEuNTg0MyA1MS4yODM4IDQxLjUyNjIgNTEuNDUzNSA0MS40MTM2QzUxLjYyMzMgNDEuMzAxIDUxLjc1NDcgNDEuMTM5MyA1MS44MyA0MC45NUw1Ny44MyAyNi42OUM1Ny45MDUzIDI2LjUwMDcgNTguMDM2NyAyNi4zMzkgNTguMjA2NCAyNi4yMjY0QzU4LjM3NjIgMjYuMTEzOCA1OC41NzYzIDI2LjA1NTcgNTguNzggMjYuMDZINzYuNDFDNzYuNjIyIDI2LjA3NDkgNzYuODMzMiAyNi4wMjE4IDc3LjAxMyAyNS45MDg1Qzc3LjE5MjggMjUuNzk1MyA3Ny4zMzE5IDI1LjYyNzYgNzcuNDEgMjUuNDNMODIuNjMgMTIuOTNDODIuNjg1MiAxMi44MDA3IDgyLjcxMjYgMTIuNjYxMyA4Mi43MTAzIDEyLjUyMDdDODIuNzA3OSAxMi4zODAxIDgyLjY3NiAxMi4yNDE2IDgyLjYxNjYgMTIuMTE0M0M4Mi41NTcxIDExLjk4NjkgODIuNDcxNSAxMS44NzM0IDgyLjM2NTIgMTEuNzgxNEM4Mi4yNTkgMTEuNjg5MyA4Mi4xMzQ1IDExLjYyMDcgODIgMTEuNThDODEuODc2NyAxMS41Mjc1IDgxLjc0NCAxMS41MDAzIDgxLjYxIDExLjVINDguODJDNDguNjA4IDExLjQ4NTEgNDguMzk2OCAxMS41MzgyIDQ4LjIxNyAxMS42NTE1QzQ4LjAzNzEgMTEuNzY0NyA0Ny44OTgxIDExLjkzMjQgNDcuODIgMTIuMTNMMzYuMTcgNDAuMTNDMzYuMTEzMSA0MC4yNTQyIDM2LjA4MjIgNDAuMzg4NyAzNi4wNzkzIDQwLjUyNTJDMzYuMDc2MyA0MC42NjE4IDM2LjEwMTQgNDAuNzk3NSAzNi4xNTI5IDQwLjkyNEMzNi4yMDQ1IDQxLjA1MDUgMzYuMjgxNCA0MS4xNjUxIDM2LjM3ODkgNDEuMjYwN0MzNi40NzY0IDQxLjM1NjMgMzYuNTkyNSA0MS40MzEgMzYuNzIgNDEuNDhDMzYuODYxMiA0MS41NDQ3IDM3LjAxNDYgNDEuNTc4OCAzNy4xNyA0MS41OFoiIGZpbGw9IiMxMjEyMTIiLz4KPC9zdmc+", + "created": 1684733732697 + } + } +} \ No newline at end of file diff --git a/src/content/docs/integrations/mysql.mdx b/src/content/docs/integrations/mysql.mdx new file mode 100644 index 000000000..b5bfd0c47 --- /dev/null +++ b/src/content/docs/integrations/mysql.mdx @@ -0,0 +1,46 @@ +--- +title: MySQL +--- + +import Op from '@components/see-also/Op.astro'; +import { Steps } from '@astrojs/starlight/components'; + +[MySQL](https://www.mysql.com/) is an open-source relational database management +system widely used for web applications, data warehousing, and enterprise +applications. + +![MySQL Diagram](mysql.excalidraw) + +Tenzir connects to MySQL over the network using the MySQL wire protocol. Tenzir +communicates with MySQL via the host and port you specify in the +[`from_mysql`](/reference/operators/from_mysql) operator. This means: + +- **Network**: Tenzir and MySQL can run on the same machine (using `localhost`) + or on different machines in the same network. You just need to make sure that + Tenzir can reach the MySQL server. +- **IPC**: There is no direct inter-process communication (IPC) mechanism; all + communication uses MySQL's network protocol. +- **Co-deployment**: For best performance and security, deploy Tenzir and MySQL + in the same trusted network or use TLS for encrypted connections. + +## Examples + +These examples assume that the MySQL server is running on the same host as +Tenzir. + +### List available tables + +```tql +from_mysql show="tables", host="localhost", database="mydb" +``` + +### Execute a custom SQL query + +```tql +from_mysql sql=r"SELECT id, name, email FROM users WHERE active = 1", + host="localhost", database="mydb" +``` + +## See Also + +- from_mysql diff --git a/src/content/docs/reference/operators.mdx b/src/content/docs/reference/operators.mdx index 34526313a..a23b617e0 100644 --- a/src/content/docs/reference/operators.mdx +++ b/src/content/docs/reference/operators.mdx @@ -367,6 +367,10 @@ operators: description: 'Receives events from an Apache Kafka topic.' example: 'from_kafka "logs"' path: 'reference/operators/from_kafka' + - name: 'from_mysql' + description: 'Reads events from a MySQL database.' + example: 'from_mysql table="users", host="db.example.com", database="mydb"' + path: 'reference/operators/from_mysql' - name: 'from_opensearch' description: 'Receives events via Opensearch Bulk API.' example: 'from_opensearch' @@ -2195,6 +2199,14 @@ from_kafka "logs" + + +```tql +from_mysql table="users", host="db.example.com", database="mydb" +``` + + + ```tql diff --git a/src/content/docs/reference/operators/from_mysql.mdx b/src/content/docs/reference/operators/from_mysql.mdx new file mode 100644 index 000000000..d3de4715b --- /dev/null +++ b/src/content/docs/reference/operators/from_mysql.mdx @@ -0,0 +1,204 @@ +--- +title: from_mysql +category: Inputs/Events +example: 'from_mysql table="users", host="db.example.com", database="mydb"' +--- + +import Op from '@components/see-also/Op.astro'; +import Integration from '@components/see-also/Integration.astro'; + +Reads events from a MySQL database. + +```tql +from_mysql [uri=string], [table=string], [sql=string], [show=string], + [host=string], [port=int], [user=string], [password=string], + [database=string], [tls=bool|record] +``` + +## Description + +The `from_mysql` operator reads data from a MySQL database. You can query data +using a table name, raw SQL, or retrieve database metadata. + +The operator supports three query modes that are mutually exclusive: + +1. **Table mode**: Read all rows from a table using the `table` parameter +2. **SQL mode**: Execute a custom SQL query using the `sql` parameter +3. **Show mode**: List tables or columns using the `show` parameter + +### `uri = string (optional)` + +A MySQL connection URI in the format: + +``` +mysql://[user[:password]@]host[:port][/database] +``` + +When provided, the URI takes precedence over individual connection parameters. +Credentials in the URI can be overridden by explicit `user` and `password` +parameters. + +### `table = string (optional)` + +The name of the table to read from. This is mutually exclusive with `sql` and +`show`. + +### `sql = string (optional)` + +A raw SQL query to execute. This is mutually exclusive with `table` and `show`. + +Use raw strings for complex queries: + +```tql +from_mysql sql=r"SELECT id, name FROM users WHERE active = 1" +``` + +### `show = string (optional)` + +Retrieve database metadata. This is mutually exclusive with `table` and `sql`. + +Supported values: + +- `"tables"`: List all tables in the database +- `"columns"`: List all columns for the table specified in `table` + +### `host = string (optional)` + +The hostname or IP address of the MySQL server. + +Defaults to `"localhost"`. + +### `port = int (optional)` + +The port number of the MySQL server. + +Defaults to `3306`. + +### `user = string (optional)` + +The username for authentication. Supports the `secret` function for secure +credential management. + +Defaults to `"root"`. + +### `password = string (optional)` + +The password for authentication. Supports the `secret` function for secure +credential management. + +Defaults to `""`. + +### `database = string (optional)` + +The database to connect to. + +### `tls = bool|record (optional)` + +TLS configuration for the MySQL connection. Defaults to `false` (no TLS). + +Use `tls=true` to enable TLS with default settings and certificate verification, +or provide a record to customize specific options: + +```tql +{ + skip_peer_verification: bool, // skip certificate verification. + cacert: string, // CA bundle to verify peers. + certfile: string, // client certificate to present. + keyfile: string, // private key for the client certificate. +} +``` + +## Types + +The operator maps MySQL types to [Tenzir types](/reference/types) as follows: + +| MySQL Type | Tenzir Type | Notes | +| :---------------------------- | :------------ | :--------------------- | +| `TINYINT(1)` | `bool` | Boolean representation | +| `TINYINT`, `SMALLINT`, `INT` | `int64` | | +| `BIGINT` | `int64` | | +| `BIGINT UNSIGNED` | `uint64` | | +| `FLOAT`, `DOUBLE` | `double` | | +| `DECIMAL`, `NUMERIC` | `double` | May lose precision | +| `DATE`, `DATETIME` | `time` | | +| `TIMESTAMP` | `time` | | +| `TIME` | `duration` | | +| `CHAR`, `VARCHAR`, `TEXT` | `string` | | +| `BINARY`, `VARBINARY`, `BLOB` | `blob` | | +| `JSON` | `string` | | +| `ENUM` | `enumeration` | | + +## Examples + +### Read all rows from a table + +```tql +from_mysql table="users", host="db.example.com", database="mydb" +``` + +### Use a connection URI + +```tql +from_mysql uri="mysql://admin:secret@db.example.com:3306/production", table="events" +``` + +### Execute a custom SQL query + +```tql +from_mysql sql=r"SELECT id, name, created_at FROM users WHERE active = 1 LIMIT 100", + host="localhost", database="app" +``` + +### Use secure credentials + +```tql +from_mysql table="orders", + host="db.example.com", + user=secret("mysql-user"), + password=secret("mysql-password"), + database="shop" +``` + +### List all tables in a database + +```tql +from_mysql show="tables", host="localhost", database="mydb" +``` + +### List columns for a specific table + +```tql +from_mysql show="columns", table="users", host="localhost", database="mydb" +``` + +### Enable TLS with defaults + +```tql +from_mysql table="events", + host="db.example.com", + database="production", + tls=true +``` + +### Connect with TLS but skip peer verification + +```tql +from_mysql table="events", + host="db.example.com", + database="production", + tls={skip_peer_verification: true} +``` + +### Connect with TLS using a CA certificate + +```tql +from_mysql table="events", + host="db.example.com", + database="production", + tls={cacert: "/path/to/ca.pem"} +``` + +## See Also + +- to_clickhouse +- mysql diff --git a/src/sidebar.ts b/src/sidebar.ts index 29170afe5..df7b2e580 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -419,6 +419,7 @@ export const integrations = [ "integrations/clickhouse", "integrations/elasticsearch", "integrations/graylog", + "integrations/mysql", "integrations/opensearch", "integrations/snowflake", "integrations/splunk", From 3de8cccf06fad71a010dd1bc77c86957473e2b4c Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Tue, 10 Feb 2026 16:42:34 +0100 Subject: [PATCH 03/11] Add from_tcp operator documentation (#199) * Add from_tcp operator documentation Document the new from_tcp operator with TLS support, including the tls record parameter, peer variable, and usage examples. Update the TCP integration page and cross-reference from_tcp in load_tcp and save_tcp See Also sections. Co-Authored-By: Claude Opus 4.6 * Fix from_tcp docs: use $peer syntax and correct types Use $peer instead of peer for let-bound variable references in sub-pipelines. Correct the ip field type from string to ip and the port field type from int to int64. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- src/content/docs/integrations/tcp.mdx | 19 +++- .../docs/reference/operators/from_tcp.mdx | 102 ++++++++++++++++++ .../docs/reference/operators/load_tcp.mdx | 1 + .../docs/reference/operators/save_tcp.mdx | 1 + 4 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 src/content/docs/reference/operators/from_tcp.mdx diff --git a/src/content/docs/integrations/tcp.mdx b/src/content/docs/integrations/tcp.mdx index b99a025d1..ecf332321 100644 --- a/src/content/docs/integrations/tcp.mdx +++ b/src/content/docs/integrations/tcp.mdx @@ -21,8 +21,11 @@ The URL schemes `tcp://` and `tcps://` dispatch to ## SSL/TLS -To enable TLS, use `tls=true`. You can optionally pass a PEM-encoded certificate -and private key via the `certfile` and `keyfile` options. +To enable TLS, pass `tls={}` to enable TLS with defaults or provide a record +with specific options like `certfile` and `keyfile`. Both +[`from_tcp`](/reference/operators/from_tcp) and +[`load_tcp`](/reference/operators/load_tcp) support server-side TLS for +accepting encrypted connections. For testing purposes, you can quickly generate a self-signed certificate as follows: @@ -39,6 +42,14 @@ openssl s_client 127.0.0.1:443 ## Examples +### Listen for incoming JSON over TCP + +```tql +from_tcp "0.0.0.0:8090" { + read_json +} +``` + ### Read data by connecting to a remote TCP server ```tql @@ -47,10 +58,10 @@ from "tcp://127.0.0.1:443", connect=true { } ``` -### Read data by listen on localhost with TLS enabled +### Listen on localhost with TLS enabled ```tql -from "tcp://127.0.0.1:443", tls=true, certfile="cert.pem", keyfile="key.pem" { +from_tcp "127.0.0.1:443", tls={certfile: "cert.pem", keyfile: "key.pem"} { read_json } ``` diff --git a/src/content/docs/reference/operators/from_tcp.mdx b/src/content/docs/reference/operators/from_tcp.mdx new file mode 100644 index 000000000..9a320232f --- /dev/null +++ b/src/content/docs/reference/operators/from_tcp.mdx @@ -0,0 +1,102 @@ +--- +title: from_tcp +category: Inputs/Bytes +example: 'from_tcp "0.0.0.0:8090" { read_json }' +--- + +import Op from '@components/see-also/Op.astro'; +import Integration from '@components/see-also/Integration.astro'; + +Listens for incoming TCP connections and reads bytes from each connection. + +```tql +from_tcp endpoint:string, [tls=record] { … } +``` + +## Description + +The `from_tcp` operator binds to the given endpoint, accepts incoming TCP +connections, and reads bytes from each connection. Each connection spawns a +sub-pipeline that processes the incoming byte stream independently. + +The sub-pipeline has access to a `$peer` variable containing the remote address +information of the connected client. + +### `endpoint: string` + +The endpoint at which the server will listen. Must be of the form +`[tcp://]:`. Use the hostname `0.0.0.0` to accept connections on +all interfaces. + +import TLSOptions from '@partials/operators/TLSOptions.mdx'; + + + +### `{ … }` + +The pipeline to run for each individual TCP connection. Inside the pipeline, the +`$peer` variable is available as a record with the following fields: + +| Field | Type | Description | +| :----- | :------ | :------------------------------------ | +| `ip` | `ip` | The IP address of the connected peer | +| `port` | `int64` | The port number of the connected peer | + +## Examples + +### Listen for incoming JSON over TCP + +Listen on all network interfaces, parsing each individual connection as JSON: + +```tql +from_tcp "0.0.0.0:8090" { + read_json +} +``` + +### Log the peer address of each connection + +```tql +from_tcp "0.0.0.0:8090" { + read_json + this.client_ip = $peer.ip +} +``` + +### Listen with TLS enabled + +Accept TLS-encrypted connections on localhost: + +```tql +from_tcp "127.0.0.1:4000", tls={certfile: "key_and_cert.pem", keyfile: "key_and_cert.pem"} { + read_json +} +``` + +This example may use a self-signed certificate that can be generated like this: + +```bash +openssl req -x509 -newkey rsa:2048 -keyout key_and_cert.pem -out key_and_cert.pem -days 365 -nodes +``` + +You can test the endpoint locally by issuing a TLS connection: + +```bash +openssl s_client 127.0.0.1:4000 +``` + +### Listen with mutual TLS (mTLS) authentication + +Require clients to present valid certificates signed by a trusted CA: + +```tql +from_tcp "0.0.0.0:4000", tls={certfile: "server.pem", keyfile: "server-key.pem", client_ca: "ca.pem", require_client_cert: true} { + read_json +} +``` + +## See Also + +- load_tcp +- save_tcp +- tcp diff --git a/src/content/docs/reference/operators/load_tcp.mdx b/src/content/docs/reference/operators/load_tcp.mdx index e8b529e6b..4446a8a08 100644 --- a/src/content/docs/reference/operators/load_tcp.mdx +++ b/src/content/docs/reference/operators/load_tcp.mdx @@ -132,5 +132,6 @@ openssl s_client -connect 127.0.0.1:4000 -cert client.pem -key client-key.pem -C ## See Also +- from_tcp - save_tcp - tcp diff --git a/src/content/docs/reference/operators/save_tcp.mdx b/src/content/docs/reference/operators/save_tcp.mdx index 2bd15427d..58c7574db 100644 --- a/src/content/docs/reference/operators/save_tcp.mdx +++ b/src/content/docs/reference/operators/save_tcp.mdx @@ -65,5 +65,6 @@ save_tcp "127.0.0.1:4000", tls=true, skip_peer_verification=true ## See Also +- from_tcp - load_tcp - tcp From d9b7347264456c72cedf3cd8fba5928458ef1a3a Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Thu, 12 Feb 2026 13:02:44 +0100 Subject: [PATCH 04/11] Add live streaming mode docs for from_mysql (#198) Document the `live` and `tracking_column` parameters that enable continuous polling for new rows using watermark tracking on an integer column. Add examples for both auto-detected and explicit tracking columns. Co-authored-by: Claude Opus 4.6 --- src/content/docs/integrations/mysql.mdx | 6 ++++ .../docs/reference/operators/from_mysql.mdx | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/content/docs/integrations/mysql.mdx b/src/content/docs/integrations/mysql.mdx index b5bfd0c47..73feea8de 100644 --- a/src/content/docs/integrations/mysql.mdx +++ b/src/content/docs/integrations/mysql.mdx @@ -41,6 +41,12 @@ from_mysql sql=r"SELECT id, name, email FROM users WHERE active = 1", host="localhost", database="mydb" ``` +### Stream new rows + +```tql +from_mysql table="events", live=true, host="localhost", database="mydb" +``` + ## See Also - from_mysql diff --git a/src/content/docs/reference/operators/from_mysql.mdx b/src/content/docs/reference/operators/from_mysql.mdx index d3de4715b..dc385c85a 100644 --- a/src/content/docs/reference/operators/from_mysql.mdx +++ b/src/content/docs/reference/operators/from_mysql.mdx @@ -11,6 +11,7 @@ Reads events from a MySQL database. ```tql from_mysql [uri=string], [table=string], [sql=string], [show=string], + [live=bool], [tracking_column=string], [host=string], [port=int], [user=string], [password=string], [database=string], [tls=bool|record] ``` @@ -62,6 +63,23 @@ Supported values: - `"tables"`: List all tables in the database - `"columns"`: List all columns for the table specified in `table` +### `live = bool (optional)` + +Enables continuous polling for new rows from a table. The operator tracks +progress using a watermark on an integer column and polls every second for rows +above the last-seen value. Mutually exclusive with `sql` and `show`. Requires +`table`. + +Defaults to `false`. + +### `tracking_column = string (optional)` + +The integer column to use for watermark tracking in live mode. The operator +queries for rows where this column exceeds the last-seen watermark. + +When omitted, the tracking column is auto-detected from the table's +auto-increment primary key. Requires `live=true`. + ### `host = string (optional)` The hostname or IP address of the MySQL server. @@ -198,6 +216,20 @@ from_mysql table="events", tls={cacert: "/path/to/ca.pem"} ``` +### Stream new rows from a table + +```tql +from_mysql table="events", live=true, + host="db.example.com", database="mydb" +``` + +### Stream with an explicit tracking column + +```tql +from_mysql table="events", live=true, tracking_column="event_id", + host="db.example.com", database="mydb" +``` + ## See Also - to_clickhouse From 8ae462b98d7644330c6426bf3c7b11b767d29dfd Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Tue, 24 Feb 2026 09:54:13 +0100 Subject: [PATCH 05/11] Document prepared statements security improvement in from_mysql (#215) Add note explaining that internal metadata queries use MySQL prepared statements to prevent SQL injection through user-provided table and column names. Co-authored-by: Claude Sonnet 4.6 --- src/content/docs/reference/operators/from_mysql.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/content/docs/reference/operators/from_mysql.mdx b/src/content/docs/reference/operators/from_mysql.mdx index dc385c85a..a0f5ab6ee 100644 --- a/src/content/docs/reference/operators/from_mysql.mdx +++ b/src/content/docs/reference/operators/from_mysql.mdx @@ -27,6 +27,10 @@ The operator supports three query modes that are mutually exclusive: 2. **SQL mode**: Execute a custom SQL query using the `sql` parameter 3. **Show mode**: List tables or columns using the `show` parameter +Internal metadata queries (such as resolving tracking columns for live mode) use +MySQL prepared statements to prevent SQL injection through user-provided table +and column names. + ### `uri = string (optional)` A MySQL connection URI in the format: From 17b8b2bd15c1ba4684f2823d5f6e02c267b0b2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Christopher=20K=C3=B6hl?= Date: Tue, 24 Feb 2026 15:01:11 +0100 Subject: [PATCH 06/11] Document `route_by` parameter and source mode for `parallel` Co-Authored-By: Claude Opus 4.6 --- .../docs/reference/operators/parallel.mdx | 49 ++++++++++++++++--- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/content/docs/reference/operators/parallel.mdx b/src/content/docs/reference/operators/parallel.mdx index 485ed7d07..4cf6ef190 100644 --- a/src/content/docs/reference/operators/parallel.mdx +++ b/src/content/docs/reference/operators/parallel.mdx @@ -9,38 +9,61 @@ import Op from '@components/see-also/Op.astro'; Runs a subpipeline across multiple parallel workers. ```tql -parallel jobs:int { … } +parallel [jobs:int] [, route_by=any] { … } ``` ## Description -The `parallel` operator distributes incoming events across multiple parallel -instances of a subpipeline. Each event is processed by exactly one worker. +The [`parallel`](/reference/operators/parallel) operator distributes incoming +events across multiple parallel instances of a subpipeline. Each event is +processed by exactly one worker. Use this operator to parallelize CPU-intensive transformations or I/O-bound operations that would otherwise bottleneck on a single thread. +By default, events are distributed across workers using an adaptive round-robin +strategy that keeps worker loads balanced. Use `route_by` to instead route +events deterministically by a key, ensuring that all events with the same key +value go to the same worker. + This operator may reorder the event stream since workers process events concurrently. +When used as a source operator (without upstream input), `parallel` spawns +multiple independent instances of the subpipeline. This is useful for running +the same source pipeline with concurrent connections. + :::caution[Expert Operator] The `parallel` operator is a building block for performance optimization. Use it when you have identified a specific bottleneck that benefits from parallelization. Not all operations scale linearly with parallelism. ::: -### `jobs: int` +### `jobs: int (optional)` + +The number of parallel workers to spawn. Must be greater than zero. Defaults to +the number of available CPU cores. + +### `route_by = any (optional)` -The number of parallel workers to spawn. Must be greater than zero. +An expression evaluated per event to determine which worker processes it. Events +with the same `route_by` value are always sent to the same worker. This +guarantees that related events are grouped together, which is required for +stateful subpipelines like [`deduplicate`](/reference/operators/deduplicate) or +[`summarize`](/reference/operators/summarize). + +Cannot be used when `parallel` is used as a source operator. ### `{ … }` -The subpipeline to run in parallel. The subpipeline receives events as input and -may either: +The subpipeline to run in parallel. The subpipeline may either: - Produce events as output (transformation) - End with a sink (void output) +When `parallel` is used as a source operator, the subpipeline runs as an +independent source producing events or as a full pipeline ending with a sink. + The subpipeline must not produce bytes as output. ## Examples @@ -56,6 +79,18 @@ parallel 4 { } ``` +### Route events by source IP + +Ensure events from the same source IP are always handled by the same worker, +enabling per-source deduplication: + +```tql +subscribe "events" +parallel route_by=src_ip { + deduplicate src_ip, dst_ip, dst_port +} +``` + ### Make parallel HTTP requests Send events to Google SecOps with 4 concurrent connections: From ee8088419a31362c8c8756a6f62477829d9801b6 Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Wed, 25 Feb 2026 22:34:23 +0100 Subject: [PATCH 07/11] Add read_tql operator documentation (#224) * Add read_tql operator documentation Co-Authored-By: Claude Opus 4.6 * Use from_file with read_tql in examples Co-Authored-By: Claude Opus 4.6 * Replace round-trip example with schema and selector examples Co-Authored-By: Claude Opus 4.6 * Revert "Replace round-trip example with schema and selector examples" This reverts commit b2f407851566653c3227413723c4ece418c1eb6e. * Remove round-trip example Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- src/content/docs/reference/operators.mdx | 12 +++ .../docs/reference/operators/read_tql.mdx | 88 +++++++++++++++++++ .../docs/reference/operators/write_tql.mdx | 1 + 3 files changed, 101 insertions(+) create mode 100644 src/content/docs/reference/operators/read_tql.mdx diff --git a/src/content/docs/reference/operators.mdx b/src/content/docs/reference/operators.mdx index a23b617e0..c8c68ce4c 100644 --- a/src/content/docs/reference/operators.mdx +++ b/src/content/docs/reference/operators.mdx @@ -607,6 +607,10 @@ operators: description: 'Parses an incoming Syslog stream into events.' example: 'read_syslog' path: 'reference/operators/read_syslog' + - name: 'read_tql' + description: 'Parses an incoming byte stream of TQL-formatted records into events.' + example: 'read_tql' + path: 'reference/operators/read_tql' - name: 'read_tsv' description: 'Read TSV (Tab-Separated Values) from a byte stream.' example: 'read_tsv auto_expand=true' @@ -1782,6 +1786,14 @@ read_syslog + + +```tql +read_tql +``` + + + ```tql diff --git a/src/content/docs/reference/operators/read_tql.mdx b/src/content/docs/reference/operators/read_tql.mdx new file mode 100644 index 000000000..9920eb36f --- /dev/null +++ b/src/content/docs/reference/operators/read_tql.mdx @@ -0,0 +1,88 @@ +--- +title: read_tql +category: Parsing +example: 'read_tql' +--- + +import Op from '@components/see-also/Op.astro'; + +Parses an incoming byte stream of TQL-formatted records into events. + +```tql +read_tql [schema=string, selector=string, schema_only=bool, + merge=bool, raw=bool, unflatten_separator=string] +``` + +## Description + +Parses an incoming byte stream of TQL-formatted records into events. Each +top-level record expression in the input becomes one event. + +The input format matches the output of [`write_tql`](/reference/operators/write_tql). +This makes `read_tql` useful for round-tripping data through TQL notation, +reading TQL-formatted files, or processing data piped from other Tenzir +pipelines. + +The parser supports all TQL literal types, including `null`, `bool`, `int64`, +`double`, `string`, `duration`, `time`, `ip`, and `subnet`, as well as nested +records and lists. + +import ParsingOptions from '@partials/operators/ParsingOptions.mdx'; + + + +## Examples + +### Read TQL records from a file + +```tql title="events.tql" +{name: "Tenzir", version: 4} +{name: "Suricata", version: 7} +``` + +```tql title="Pipeline" +from_file "events.tql" { + read_tql +} +``` + +```tql title="Output" +{ + name: "Tenzir", + version: 4, +} +{ + name: "Suricata", + version: 7, +} +``` + +### Read records with native types + +TQL notation supports types that JSON cannot represent natively, such as +durations, timestamps, IP addresses, and subnets. + +```tql title="input.tql" +{dur: 5s, ts: 2024-01-01T00:00:00.000000, addr: 192.168.1.1, net: 10.0.0.0/8} +``` + +```tql title="Pipeline" +from_file "input.tql" { + read_tql +} +``` + +```tql title="Output" +{ + dur: 5s, + ts: 2024-01-01T00:00:00Z, + addr: 192.168.1.1, + net: 10.0.0.0/8, +} +``` + +## See Also + +- write_tql +- read_json +- read_ndjson diff --git a/src/content/docs/reference/operators/write_tql.mdx b/src/content/docs/reference/operators/write_tql.mdx index 7a5b2c6ef..a756fc248 100644 --- a/src/content/docs/reference/operators/write_tql.mdx +++ b/src/content/docs/reference/operators/write_tql.mdx @@ -103,5 +103,6 @@ write_tql strip_null_fields=true ## See Also +- read_tql - write_json - map-data-to-ocsf From 907e95c22b0962d1ea51d830e0bab6afc6959fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Mur=20Er=C5=BEen?= Date: Mon, 9 Mar 2026 11:16:04 +0100 Subject: [PATCH 08/11] Doc batch operator timeout and multiple schemas (#238) --- src/content/docs/reference/operators/batch.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/content/docs/reference/operators/batch.md b/src/content/docs/reference/operators/batch.md index 186b74e7a..d50c5e8e4 100644 --- a/src/content/docs/reference/operators/batch.md +++ b/src/content/docs/reference/operators/batch.md @@ -15,12 +15,16 @@ batch [limit:int, timeout=duration] The `batch` operator takes its input and rewrites it into batches of up to the desired size. -:::caution[Expert Operator] +:::caution[Advanced feature] The `batch` operator is a lower-level building block that lets users explicitly control batching, which otherwise is controlled automatically by Tenzir's underlying pipeline execution engine. Use with caution! ::: +Note that the operator maintains separate buffers for each distinct schema. Each +buffer has independent timeout tracking and fills until reaching the `limit`, at +which point it flushes immediately. + ### `limit: int (optional)` How many events to put into one batch at most. @@ -29,5 +33,7 @@ Defaults to `65536`. ### `timeout = duration (optional)` -Specifies a maximum latency for events passing through the batch operator. When -unspecified, an infinite duration is used. +Specifies a maximum latency for events passing through the batch operator. If no +new events arrive within the timeout period, any buffered events are flushed. + +Defaults to `1min`. From 3ed77e55f1ef9b9a1216e13d25e3862f11b850fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Mur=20Er=C5=BEen?= Date: Thu, 12 Mar 2026 08:25:41 +0100 Subject: [PATCH 09/11] Remove measure real_time argument (#244) --- src/content/docs/reference/operators/measure.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/content/docs/reference/operators/measure.md b/src/content/docs/reference/operators/measure.md index 749ad383e..4f6ae2065 100644 --- a/src/content/docs/reference/operators/measure.md +++ b/src/content/docs/reference/operators/measure.md @@ -7,7 +7,7 @@ example: 'measure' Replaces the input with metrics describing the input. ```tql -measure [real_time=bool, cumulative=bool] +measure [cumulative=bool] ``` ## Description @@ -31,13 +31,6 @@ type tenzir.measure.bytes = record{ } ``` -### `real_time = bool (optional)` - -Whether to emit metrics immediately with every batch, rather than buffering -until the upstream operator stalls, i.e., is idle or waiting for further input. - -The is especially useful when `measure` should emit data without latency. - ### `cumulative = bool (optional)` Whether to emit running totals for the `events` and `bytes` fields rather than From 2621f798a151b583bd7345329ae1f46ade4c529e Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Sun, 15 Mar 2026 22:54:46 +0100 Subject: [PATCH 10/11] Use semantic components in branch docs --- src/content/docs/integrations/index.mdx | 26 +++++-------------- src/content/docs/integrations/mysql.mdx | 4 +-- src/content/docs/integrations/tcp.mdx | 14 ++++------ .../docs/reference/operators/from_mysql.mdx | 2 +- .../docs/reference/operators/parallel.mdx | 9 +++---- .../docs/reference/operators/read_tql.mdx | 4 +-- .../docs/reference/operators/save_tcp.mdx | 2 +- 7 files changed, 20 insertions(+), 41 deletions(-) diff --git a/src/content/docs/integrations/index.mdx b/src/content/docs/integrations/index.mdx index a28f8a2a4..251fc4867 100644 --- a/src/content/docs/integrations/index.mdx +++ b/src/content/docs/integrations/index.mdx @@ -14,27 +14,17 @@ deliver instant value. They bundle pipelines, enrichment, and configurations for common security tools like Splunk, CrowdStrike, Elastic, SentinelOne, Palo Alto, and many more. -Browse our freely available [package library on -GitHub](https://github.com/tenzir/library). You can also use - -ai-workbench/use-agent-skills to generate custom packages with -AI assistance. +Browse our freely available [package library on GitHub](https://github.com/tenzir/library). You can also use ai-workbench/use-agent-skills to generate custom packages with AI assistance. ## Core Integrations Core integrations are native connectors to the ecosystem, enabling communication over numerous protocols and APIs: -- **Cloud storage**: amazon/s3, - google/cloud-storage, -microsoft/azure-blob-storage -- **Message queues**: kafka, - amazon/sqs, amqp -- **Databases**: snowflake, - clickhouse, mysql -- **Network protocols**: tcp, - udp, http, -syslog +- **Cloud storage**: amazon/s3, google/cloud-storage, microsoft/azure-blob-storage +- **Message queues**: kafka, amazon/sqs, amqp +- **Databases**: snowflake, clickhouse, mysql +- **Network protocols**: tcp, udp, http, syslog Under the hood, core integrations use a C++ plugin abstraction to provide an [operator](/reference/operators/), [function](/reference/functions/), or @@ -47,9 +37,5 @@ SDK](https://cloud.google.com/cpp), or integrations require a custom implementation. :::note[Dedicated operators] -For some applications, we provide a **dedicated operator** that dramatically -simplifies the user experience. For example, to_splunk and - -from_opensearch offer a streamlined interface compared to composing -generic HTTP or protocol operators. +For some applications, we provide a **dedicated operator** that dramatically simplifies the user experience. For example, to_splunk and from_opensearch offer a streamlined interface compared to composing generic HTTP or protocol operators. ::: diff --git a/src/content/docs/integrations/mysql.mdx b/src/content/docs/integrations/mysql.mdx index 73feea8de..2199bb694 100644 --- a/src/content/docs/integrations/mysql.mdx +++ b/src/content/docs/integrations/mysql.mdx @@ -11,9 +11,7 @@ applications. ![MySQL Diagram](mysql.excalidraw) -Tenzir connects to MySQL over the network using the MySQL wire protocol. Tenzir -communicates with MySQL via the host and port you specify in the -[`from_mysql`](/reference/operators/from_mysql) operator. This means: +Tenzir connects to MySQL over the network using the MySQL wire protocol. Tenzir communicates with MySQL via the host and port you specify in the from_mysql operator. This means: - **Network**: Tenzir and MySQL can run on the same machine (using `localhost`) or on different machines in the same network. You just need to make sure that diff --git a/src/content/docs/integrations/tcp.mdx b/src/content/docs/integrations/tcp.mdx index 1dfdbea73..e14e7a4ca 100644 --- a/src/content/docs/integrations/tcp.mdx +++ b/src/content/docs/integrations/tcp.mdx @@ -11,20 +11,16 @@ TCP sockets in both server (listening) and client (connect) mode. Use the IP address `0.0.0.0` to listen on all available network interfaces. -:::tip[URL Support] -The URL schemes `tcp://` and `tcps://` dispatch to -load_tcp and -save_tcp for seamless URL-style use via -from and to. +:::tip[URL support] +The URL schemes `tcp://` and `tcps://` dispatch to load_tcp and save_tcp for seamless URL-style use via from and to. ::: ## SSL/TLS To enable TLS, pass `tls={}` to enable TLS with defaults or provide a record -with specific options like `certfile` and `keyfile`. Both -[`from_tcp`](/reference/operators/from_tcp) and -[`load_tcp`](/reference/operators/load_tcp) support server-side TLS for -accepting encrypted connections. +with specific options like `certfile` and `keyfile`. Both from_tcp +and load_tcp support server-side TLS for accepting encrypted +connections. For testing purposes, you can quickly generate a self-signed certificate as follows: diff --git a/src/content/docs/reference/operators/from_mysql.mdx b/src/content/docs/reference/operators/from_mysql.mdx index a0f5ab6ee..24b0c5a84 100644 --- a/src/content/docs/reference/operators/from_mysql.mdx +++ b/src/content/docs/reference/operators/from_mysql.mdx @@ -132,7 +132,7 @@ or provide a record to customize specific options: ## Types -The operator maps MySQL types to [Tenzir types](/reference/types) as follows: +The operator maps MySQL types to types as follows: | MySQL Type | Tenzir Type | Notes | | :---------------------------- | :------------ | :--------------------- | diff --git a/src/content/docs/reference/operators/parallel.mdx b/src/content/docs/reference/operators/parallel.mdx index cdea54607..cb1588734 100644 --- a/src/content/docs/reference/operators/parallel.mdx +++ b/src/content/docs/reference/operators/parallel.mdx @@ -12,9 +12,9 @@ parallel [jobs:int] [, route_by=any] { … } ## Description -The [`parallel`](/reference/operators/parallel) operator distributes incoming -events across multiple parallel instances of a subpipeline. Each event is -processed by exactly one worker. +The parallel operator distributes incoming events across multiple +parallel instances of a subpipeline. Each event is processed by exactly one +worker. Use this operator to parallelize CPU-intensive transformations or I/O-bound operations that would otherwise bottleneck on a single thread. @@ -47,8 +47,7 @@ the number of available CPU cores. An expression evaluated per event to determine which worker processes it. Events with the same `route_by` value are always sent to the same worker. This guarantees that related events are grouped together, which is required for -stateful subpipelines like [`deduplicate`](/reference/operators/deduplicate) or -[`summarize`](/reference/operators/summarize). +stateful subpipelines like deduplicate or summarize. Cannot be used when `parallel` is used as a source operator. diff --git a/src/content/docs/reference/operators/read_tql.mdx b/src/content/docs/reference/operators/read_tql.mdx index 9920eb36f..54dd91378 100644 --- a/src/content/docs/reference/operators/read_tql.mdx +++ b/src/content/docs/reference/operators/read_tql.mdx @@ -18,8 +18,8 @@ read_tql [schema=string, selector=string, schema_only=bool, Parses an incoming byte stream of TQL-formatted records into events. Each top-level record expression in the input becomes one event. -The input format matches the output of [`write_tql`](/reference/operators/write_tql). -This makes `read_tql` useful for round-tripping data through TQL notation, +The input format matches the output of write_tql. This makes +`read_tql` useful for round-tripping data through TQL notation, reading TQL-formatted files, or processing data piped from other Tenzir pipelines. diff --git a/src/content/docs/reference/operators/save_tcp.mdx b/src/content/docs/reference/operators/save_tcp.mdx index 126137996..b9047f154 100644 --- a/src/content/docs/reference/operators/save_tcp.mdx +++ b/src/content/docs/reference/operators/save_tcp.mdx @@ -33,7 +33,7 @@ of a numeric port. The amount of time to wait before attempting to reconnect in case a connection attempt fails and the error is deemed recoverable. Defaults to `30s`. -### `max_retry_count = int (optional) +### `max_retry_count = int (optional)` The number of retries to attempt in case of connection errors before transitioning into the error state. Defaults to `10`. From 6956c8c85c19f3f92a9d7e5656922f9b37490dea Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Mon, 16 Mar 2026 07:00:42 +0100 Subject: [PATCH 11/11] Document from_tcp, accept_tcp, and serve_tcp operators (#214) Co-authored-by: Claude Opus 4.6 --- .../docs/explanations/configuration.mdx | 9 +- .../collecting/get-data-from-the-network.mdx | 25 +++--- .../docs/guides/node-setup/configure-tls.mdx | 4 +- .../guides/routing/load-balance-pipelines.mdx | 6 +- .../guides/routing/send-to-destinations.mdx | 7 +- .../microsoft/windows-event-logs.mdx | 14 ++- src/content/docs/integrations/syslog.mdx | 8 +- src/content/docs/integrations/tcp.mdx | 61 +++++-------- .../docs/reference/functions/secret.mdx | 5 +- .../docs/reference/operators/accept_tcp.mdx | 87 +++++++++++++++++++ src/content/docs/reference/operators/from.mdx | 4 +- .../docs/reference/operators/from_tcp.mdx | 86 +++++++----------- .../docs/reference/operators/load_balance.mdx | 3 +- .../docs/reference/operators/load_tcp.mdx | 11 +++ .../operators/read_delimited_regex.mdx | 5 +- .../docs/reference/operators/read_gelf.mdx | 5 +- .../docs/reference/operators/read_lines.mdx | 10 ++- .../docs/reference/operators/read_syslog.mdx | 2 +- .../docs/reference/operators/save_tcp.mdx | 8 ++ .../docs/reference/operators/serve_tcp.mdx | 75 ++++++++++++++++ .../docs/reference/operators/to_tcp.mdx | 60 +++++++++++++ 21 files changed, 346 insertions(+), 149 deletions(-) create mode 100644 src/content/docs/reference/operators/accept_tcp.mdx create mode 100644 src/content/docs/reference/operators/serve_tcp.mdx create mode 100644 src/content/docs/reference/operators/to_tcp.mdx diff --git a/src/content/docs/explanations/configuration.mdx b/src/content/docs/explanations/configuration.mdx index 21a32fb14..c4edf82a6 100644 --- a/src/content/docs/explanations/configuration.mdx +++ b/src/content/docs/explanations/configuration.mdx @@ -141,8 +141,8 @@ Tenzir provides node-level TLS configuration that applies to all operators and connectors using TLS/HTTPS connections. These settings are used by operators that make outbound connections (e.g., to_opensearch, to_splunk, save_email) -and those that accept inbound connections (e.g., load_tcp, -save_tcp). +and those that accept inbound connections (e.g., accept_tcp, +serve_tcp). :::note[Use Only When Required] We do not recommend manually configuring TLS settings unless required for @@ -192,8 +192,9 @@ configuration: - to_opensearch: Applies min version and ciphers to HTTPS connections - to_splunk: Applies min version and ciphers to Splunk HEC connections - save_email: Applies min version and ciphers to SMTP connections -- load_tcp: Applies min version and ciphers to TLS server mode -- save_tcp: Applies min version and ciphers to TLS client and server modes +- accept_tcp: Applies min version and ciphers to TLS server mode +- from_tcp: Applies min version and ciphers to TLS client mode +- serve_tcp: Applies min version and ciphers to TLS server mode - from_opensearch: Applies min version and ciphers to HTTPS connections ## Plugins diff --git a/src/content/docs/guides/collecting/get-data-from-the-network.mdx b/src/content/docs/guides/collecting/get-data-from-the-network.mdx index f20c9a073..d5007f5fa 100644 --- a/src/content/docs/guides/collecting/get-data-from-the-network.mdx +++ b/src/content/docs/guides/collecting/get-data-from-the-network.mdx @@ -8,16 +8,16 @@ capture raw packets from network interfaces. ## TCP sockets -The [Transmission Control Protocol (TCP)](/integrations/tcp) provides reliable, -ordered byte streams. Use TCP when you need guaranteed delivery and message -ordering. +tcp provides reliable, ordered byte streams. Use TCP +when you need guaranteed delivery and message ordering. ### Listen for connections -Start a TCP server that accepts incoming connections: +Use accept_tcp to start a TCP server that accepts incoming +connections: ```tql -from "tcp://0.0.0.0:9000" { +accept_tcp "0.0.0.0:9000" { read_json } ``` @@ -27,10 +27,10 @@ pipeline to convert incoming bytes to events. ### Connect to a remote server -Act as a TCP client by connecting to an existing server: +Use from_tcp to connect to an existing server: ```tql -from "tcp://192.168.1.100:9000", connect=true { +from_tcp "192.168.1.100:9000" { read_json } ``` @@ -40,7 +40,7 @@ from "tcp://192.168.1.100:9000", connect=true { Secure your TCP connections with TLS by passing a `tls` record: ```tql -from "tcp://0.0.0.0:9443", tls={certfile: "cert.pem", keyfile: "key.pem"} { +accept_tcp "0.0.0.0:9443", tls={certfile: "cert.pem", keyfile: "key.pem"} { read_json } ``` @@ -56,9 +56,8 @@ For production TLS configuration, including mutual TLS and cipher settings, see ## UDP sockets -The [User Datagram Protocol (UDP)](/integrations/udp) is a connectionless -protocol ideal for high-volume, loss-tolerant data like syslog messages or -metrics. +udp is a connectionless protocol ideal for +high-volume, loss-tolerant data like syslog messages or metrics. ### Receive UDP datagrams @@ -99,8 +98,8 @@ this = { ## Packet capture -Capture raw network packets from a [network interface card (NIC)](/integrations/nic) -for deep packet inspection or network forensics. +Capture raw network packets with nic for deep +packet inspection or network forensics. ### List available interfaces diff --git a/src/content/docs/guides/node-setup/configure-tls.mdx b/src/content/docs/guides/node-setup/configure-tls.mdx index dde429be4..498841099 100644 --- a/src/content/docs/guides/node-setup/configure-tls.mdx +++ b/src/content/docs/guides/node-setup/configure-tls.mdx @@ -21,8 +21,8 @@ tenzir: ``` These settings apply automatically to operators like from_http, -load_tcp, save_tcp, -to_opensearch, from_opensearch, +accept_tcp, from_tcp, +serve_tcp, to_opensearch, from_opensearch, to_splunk, save_email, and to_fluent_bit. diff --git a/src/content/docs/guides/routing/load-balance-pipelines.mdx b/src/content/docs/guides/routing/load-balance-pipelines.mdx index 094fc4004..1b859a988 100644 --- a/src/content/docs/guides/routing/load-balance-pipelines.mdx +++ b/src/content/docs/guides/routing/load-balance-pipelines.mdx @@ -17,8 +17,7 @@ nested pipelines, enabling you to spread load across multiple destinations. let $endpoints = ["host1:8080", "host2:8080", "host3:8080"] subscribe "events" load_balance $endpoints { - write_json - save_tcp $endpoints + to_tcp $endpoints { write_json } } ``` @@ -36,8 +35,7 @@ let $cfg = ["192.168.0.30:8080", "192.168.0.31:8080"] subscribe "input" load_balance $cfg { - write_json - save_tcp $cfg + to_tcp $cfg { write_json } } ``` diff --git a/src/content/docs/guides/routing/send-to-destinations.mdx b/src/content/docs/guides/routing/send-to-destinations.mdx index 171f54727..5e0deb9f4 100644 --- a/src/content/docs/guides/routing/send-to-destinations.mdx +++ b/src/content/docs/guides/routing/send-to-destinations.mdx @@ -57,8 +57,8 @@ to_opensearch "https://opensearch.example.com:9200", ### Cloud services -Route events to cloud destinations like [Amazon SQS](/integrations/amazon/sqs) -and [Google Cloud Pub/Sub](/integrations/google/cloud-pubsub). +Route events to cloud destinations like amazon/sqs +and google/cloud-pubsub. Send to SQS: @@ -105,8 +105,7 @@ save_file "s3://bucket/logs/events.jsonl" Send NDJSON over tcp: ```tql -write_json -save_tcp "collector.example.com:5044" +to_tcp "collector.example.com:5044" { write_json } ``` ## Expression-based serialization diff --git a/src/content/docs/integrations/microsoft/windows-event-logs.mdx b/src/content/docs/integrations/microsoft/windows-event-logs.mdx index 4fedc2268..83101e2a8 100644 --- a/src/content/docs/integrations/microsoft/windows-event-logs.mdx +++ b/src/content/docs/integrations/microsoft/windows-event-logs.mdx @@ -221,10 +221,8 @@ configuration: Import the logs via TCP: ```tql -load_tcp "127.0.0.1:4000", - tls=true, - certfile="key_and_cert.pem", - keyfile="key_and_cert.pem" { +accept_tcp "127.0.0.1:4000", + tls={certfile: "key_and_cert.pem", keyfile: "key_and_cert.pem"} { read_json } import @@ -251,7 +249,7 @@ configuration to publish to the `nxlog` topic: ``` -Then use our [Kafka integration](/integrations/kafka) to read from the topic: +Then use kafka to read from the topic: ```tql from_kafka "nxlog" @@ -589,7 +587,7 @@ Accept the logs sent with the configuration above into Tenzir via tcp: ```tql -load_tcp "10.0.0.1:1514" { +accept_tcp "10.0.0.1:1514" { read_json } publish "wec" @@ -618,7 +616,7 @@ Security monitoring often focuses on specific event types. Filter for logon events (Event ID 4624) and failed logon attempts (Event ID 4625): ```tql -load_tcp "10.0.0.1:1514" { +accept_tcp "10.0.0.1:1514" { read_delimited "\n", include_separator=true } this = data.parse_winlog() @@ -631,7 +629,7 @@ The `EventData` section contains event-specific fields. For a successful logon event, extract the relevant information: ```tql -load_tcp "10.0.0.1:1514" { +accept_tcp "10.0.0.1:1514" { read_delimited "\n", include_separator=true } this = data.parse_winlog() diff --git a/src/content/docs/integrations/syslog.mdx b/src/content/docs/integrations/syslog.mdx index 297eef267..31a7a47b2 100644 --- a/src/content/docs/integrations/syslog.mdx +++ b/src/content/docs/integrations/syslog.mdx @@ -30,17 +30,17 @@ this = data.parse_syslog() publish "syslog" ``` -To use TCP instead of UDP, use load_tcp with +To use TCP instead of UDP, use accept_tcp with read_syslog: ```tql -load_tcp "0.0.0.0:514" { +accept_tcp "0.0.0.0:514" { read_syslog } publish "syslog" ``` -The pipeline inside `load_tcp` executes _for each accepted connection_. +The pipeline inside accept_tcp executes _for each accepted connection_. ### Parsing CEF, LEEF, or JSON Payloads @@ -125,7 +125,7 @@ original syslog line alongside the parsed fields. Use the `raw_message` parameter to store the unparsed input: ```tql -load_tcp "0.0.0.0:514" { +accept_tcp "0.0.0.0:514" { read_syslog raw_message=raw } ``` diff --git a/src/content/docs/integrations/tcp.mdx b/src/content/docs/integrations/tcp.mdx index e14e7a4ca..f6e1eda45 100644 --- a/src/content/docs/integrations/tcp.mdx +++ b/src/content/docs/integrations/tcp.mdx @@ -4,8 +4,9 @@ title: TCP The [Transmission Control Protocol (TCP)](https://en.wikipedia.org/wiki/Transmission_Control_Protocol) provides a -bidirectional byte stream over IP. Tenzir supports reading from and writing to -TCP sockets in both server (listening) and client (connect) mode. +bidirectional byte stream over IP. Tenzir provides operators for both sides of +a TCP conversation: connecting to remote endpoints, accepting incoming +connections, and serving data to connected clients. ![TCP](tcp.svg) @@ -15,48 +16,30 @@ Use the IP address `0.0.0.0` to listen on all available network interfaces. The URL schemes `tcp://` and `tcps://` dispatch to load_tcp and save_tcp for seamless URL-style use via from and to. ::: -## SSL/TLS - -To enable TLS, pass `tls={}` to enable TLS with defaults or provide a record -with specific options like `certfile` and `keyfile`. Both from_tcp -and load_tcp support server-side TLS for accepting encrypted -connections. - -For testing purposes, you can quickly generate a self-signed certificate as -follows: +## Connecting to remote endpoints -```bash -openssl req -x509 -newkey rsa:2048 -keyout key_and_cert.pem -out key_and_cert.pem -days 365 -nodes -``` +Use from_tcp to connect to a remote TCP endpoint as a client and read +data from it, or to_tcp to send data to a remote endpoint. Both +operators reconnect automatically with exponential backoff on connection failure. -An easy way to test a TLS connection is to try connecting via OpenSSL: +## Accepting incoming connections -```bash -openssl s_client 127.0.0.1:443 -``` +Use accept_tcp to listen on a local endpoint and accept incoming TCP +connections. Each connection spawns a nested pipeline that processes the +incoming byte stream independently. -## Examples +## Serving data to clients -### Listen for incoming JSON over TCP +Use serve_tcp to start a TCP server that broadcasts pipeline output to +all connected clients. A nested pipeline serializes events into bytes before +sending. -```tql -from_tcp "0.0.0.0:8090" { - read_json -} -``` +See collecting/get-data-from-the-network for practical examples. -### Read data by connecting to a remote TCP server - -```tql -from "tcp://127.0.0.1:443", connect=true { - read_json -} -``` - -### Listen on localhost with TLS enabled +## SSL/TLS -```tql -from_tcp "127.0.0.1:443", tls={certfile: "cert.pem", keyfile: "key.pem"} { - read_json -} -``` +All TCP operators support TLS via the `tls` option. Pass an empty record +(`tls={}`) for defaults, or provide specific options like `certfile` and +`keyfile`. + like `certfile` and +`keyfile`. diff --git a/src/content/docs/reference/functions/secret.mdx b/src/content/docs/reference/functions/secret.mdx index 42786297b..4129f28bf 100644 --- a/src/content/docs/reference/functions/secret.mdx +++ b/src/content/docs/reference/functions/secret.mdx @@ -20,8 +20,7 @@ to. If the secret is not found in the node, a request is made to the Tenzir Platform. Should the platform also not be able to find the secret, an error is raised. -See the [explanation page for secrets](/explanations/secrets) for more -details. +See secrets for more details. ### `name: string` @@ -42,7 +41,7 @@ We do not recommend enabling this option. ### Using secrets in an operator ```tql -load_tcp "127.0.0.1:4000" { +accept_tcp "127.0.0.1:4000" { read_ndjson } to_splunk "https://localhost:8088", hec_token=secret("splunk-hec-token") diff --git a/src/content/docs/reference/operators/accept_tcp.mdx b/src/content/docs/reference/operators/accept_tcp.mdx new file mode 100644 index 000000000..f26e5609d --- /dev/null +++ b/src/content/docs/reference/operators/accept_tcp.mdx @@ -0,0 +1,87 @@ +--- +title: accept_tcp +category: Inputs/Events +example: 'accept_tcp "0.0.0.0:8090" { read_json }' +--- + +import Op from '@components/see-also/Op.astro'; +import Integration from '@components/see-also/Integration.astro'; + +Listens for incoming TCP or TLS connections and receives events. + +```tql +accept_tcp endpoint:string, [max_connections=int, tls=record, { … }] +``` + +## Description + +Listens on the specified endpoint for incoming TCP connections. For each +accepted connection, the operator spawns the nested pipeline and feeds it the +bytes received from that connection. + +### `endpoint: string` + +The endpoint to listen on. Must be of the form `[tcp://]:`. Use +`0.0.0.0` as the host to accept connections on all interfaces. + +### `tls = record (optional)` + +import TLSOptions from '@partials/operators/TLSOptions.mdx'; + + + +### `max_connections = int (optional)` + +The maximum number of simultaneous incoming connections to accept. Additional +connections beyond this limit are rejected. + +Defaults to `128`. + +### `{ … } (optional)` + +The pipeline to run for each individual TCP connection. If none is specified, no +transformations are applied to the output streams. Unless you are sure that +there is at most one active connection at a time, it is recommended to specify a +pipeline that parses the individual connection streams into events, for instance +`{ read_json }`. Otherwise, the output can be interleaved. + +Inside the pipeline, the `$peer` variable is available as a record with the +following fields: + +| Field | Type | Description | +| :----- | :------ | :------------------------------------ | +| `ip` | `ip` | The IP address of the connected peer | +| `port` | `int64` | The port number of the connected peer | + +## Examples + +### Accept incoming JSON over TCP + +```tql +accept_tcp "0.0.0.0:8090" { + read_json +} +``` + +### Accept incoming Syslog over TCP + +```tql +accept_tcp "0.0.0.0:514" { + read_syslog +} +``` + +### Accept connections with TLS + +```tql +accept_tcp "0.0.0.0:4443", tls={certfile: "cert.pem", keyfile: "key.pem"} { + read_json +} +``` + +## See Also + +- from_tcp +- to_tcp +- serve_tcp +- tcp diff --git a/src/content/docs/reference/operators/from.mdx b/src/content/docs/reference/operators/from.mdx index f077c073a..47639293e 100644 --- a/src/content/docs/reference/operators/from.mdx +++ b/src/content/docs/reference/operators/from.mdx @@ -97,8 +97,8 @@ just plain `json`. ### The pipeline argument & its relation to the loader -Some loaders, such as the load_tcp operator, accept a sub-pipeline -directly. If the selected loader accepts a sub-pipeline, the `from` operator +Some loaders accept a sub-pipeline directly. +If the selected loader accepts a sub-pipeline, the `from` operator will dispatch decompression and parsing into that sub-pipeline. If a an explicit pipeline argument is provided it is forwarded as-is. If the loader does not accept a sub-pipeline, the decompression and parsing steps are simply performed diff --git a/src/content/docs/reference/operators/from_tcp.mdx b/src/content/docs/reference/operators/from_tcp.mdx index 9a320232f..e8988db20 100644 --- a/src/content/docs/reference/operators/from_tcp.mdx +++ b/src/content/docs/reference/operators/from_tcp.mdx @@ -1,102 +1,78 @@ --- title: from_tcp -category: Inputs/Bytes -example: 'from_tcp "0.0.0.0:8090" { read_json }' +category: Inputs/Events +example: 'from_tcp "example.org:4000" { read_json }' --- import Op from '@components/see-also/Op.astro'; import Integration from '@components/see-also/Integration.astro'; -Listens for incoming TCP connections and reads bytes from each connection. +Connects to a remote TCP or TLS endpoint and receives events. ```tql -from_tcp endpoint:string, [tls=record] { … } +from_tcp endpoint:string, [tls=record, { … }] ``` ## Description -The `from_tcp` operator binds to the given endpoint, accepts incoming TCP -connections, and reads bytes from each connection. Each connection spawns a -sub-pipeline that processes the incoming byte stream independently. +Connects to the specified TCP endpoint as a client and reads bytes from the +connection, running them through an optional nested pipeline. -The sub-pipeline has access to a `$peer` variable containing the remote address -information of the connected client. +If the connection fails, the operator retries with exponential backoff. ### `endpoint: string` -The endpoint at which the server will listen. Must be of the form -`[tcp://]:`. Use the hostname `0.0.0.0` to accept connections on -all interfaces. +The remote endpoint to connect to. Must be of the form +`[tcp://]:`. + +### `tls = record (optional)` import TLSOptions from '@partials/operators/TLSOptions.mdx'; - + -### `{ … }` +### `{ … } (optional)` -The pipeline to run for each individual TCP connection. Inside the pipeline, the -`$peer` variable is available as a record with the following fields: +The pipeline to run for the TCP connection. Use this to parse the incoming byte +stream into events, for instance `{ read_json }`. -| Field | Type | Description | -| :----- | :------ | :------------------------------------ | -| `ip` | `ip` | The IP address of the connected peer | -| `port` | `int64` | The port number of the connected peer | +Inside the pipeline, the `$peer` variable is available as a record with the +following fields: -## Examples +| Field | Type | Description | +| :----- | :------ | :--------------------------------- | +| `ip` | `ip` | The IP address of the remote peer | +| `port` | `int64` | The port number of the remote peer | -### Listen for incoming JSON over TCP +## Examples -Listen on all network interfaces, parsing each individual connection as JSON: +### Connect to a remote server and read JSON ```tql -from_tcp "0.0.0.0:8090" { +from_tcp "example.org:4000" { read_json } ``` -### Log the peer address of each connection +### Connect with TLS ```tql -from_tcp "0.0.0.0:8090" { +from_tcp "example.org:4443", tls={} { read_json - this.client_ip = $peer.ip } ``` -### Listen with TLS enabled - -Accept TLS-encrypted connections on localhost: - -```tql -from_tcp "127.0.0.1:4000", tls={certfile: "key_and_cert.pem", keyfile: "key_and_cert.pem"} { - read_json -} -``` - -This example may use a self-signed certificate that can be generated like this: - -```bash -openssl req -x509 -newkey rsa:2048 -keyout key_and_cert.pem -out key_and_cert.pem -days 365 -nodes -``` - -You can test the endpoint locally by issuing a TLS connection: - -```bash -openssl s_client 127.0.0.1:4000 -``` - -### Listen with mutual TLS (mTLS) authentication - -Require clients to present valid certificates signed by a trusted CA: +### Connect with TLS and a custom CA certificate ```tql -from_tcp "0.0.0.0:4000", tls={certfile: "server.pem", keyfile: "server-key.pem", client_ca: "ca.pem", require_client_cert: true} { +from_tcp "example.org:4443", tls={cacert: "ca.pem"} { read_json } ``` ## See Also -- load_tcp -- save_tcp +- to_tcp +- accept_tcp +- serve_tcp - tcp diff --git a/src/content/docs/reference/operators/load_balance.mdx b/src/content/docs/reference/operators/load_balance.mdx index 4e00e20db..b13403a4f 100644 --- a/src/content/docs/reference/operators/load_balance.mdx +++ b/src/content/docs/reference/operators/load_balance.mdx @@ -49,8 +49,7 @@ let $cfg = ["192.168.0.30:8080", "192.168.0.30:8081"] subscribe "input" load_balance $cfg { - write_json - save_tcp $cfg + to_tcp $cfg { write_json } } ``` diff --git a/src/content/docs/reference/operators/load_tcp.mdx b/src/content/docs/reference/operators/load_tcp.mdx index 7651c832a..c9cb0184a 100644 --- a/src/content/docs/reference/operators/load_tcp.mdx +++ b/src/content/docs/reference/operators/load_tcp.mdx @@ -4,6 +4,14 @@ category: Inputs/Bytes example: 'load_tcp "0.0.0.0:8090" { read_json }' --- +import Op from '@components/see-also/Op.astro'; +import Integration from '@components/see-also/Integration.astro'; + +:::caution[Deprecated] +Use from_tcp for client connections or accept_tcp for server +connections instead. +::: + Loads bytes from a TCP or TLS connection. ```tql @@ -139,5 +147,8 @@ openssl s_client -connect 127.0.0.1:4000 -cert client.pem -key client-key.pem -C ## See Also - from_tcp +- to_tcp +- accept_tcp +- serve_tcp - save_tcp - tcp diff --git a/src/content/docs/reference/operators/read_delimited_regex.mdx b/src/content/docs/reference/operators/read_delimited_regex.mdx index b29d0e68d..02d14e7d9 100644 --- a/src/content/docs/reference/operators/read_delimited_regex.mdx +++ b/src/content/docs/reference/operators/read_delimited_regex.mdx @@ -49,8 +49,9 @@ default, the separator is excluded from the results. ### Split Syslog-like events without newline terminators from a TCP input ```tql -load_tcp "0.0.0.0:514" -read_delimited_regex "(?=<[0-9]+>)" +accept_tcp "0.0.0.0:514" { + read_delimited_regex "(?=<[0-9]+>)" +} this = data.parse_syslog() ``` diff --git a/src/content/docs/reference/operators/read_gelf.mdx b/src/content/docs/reference/operators/read_gelf.mdx index 71a8159a5..3663c9181 100644 --- a/src/content/docs/reference/operators/read_gelf.mdx +++ b/src/content/docs/reference/operators/read_gelf.mdx @@ -25,8 +25,9 @@ import ParsingOptions from '@partials/operators/ParsingOptions.mdx'; ### Read a GELF stream from a TCP socket ```tql -load_tcp "0.0.0.0:54321" -read_gelf +accept_tcp "0.0.0.0:54321" { + read_gelf +} ``` ## See Also diff --git a/src/content/docs/reference/operators/read_lines.mdx b/src/content/docs/reference/operators/read_lines.mdx index 6c5553ee4..d3e175e92 100644 --- a/src/content/docs/reference/operators/read_lines.mdx +++ b/src/content/docs/reference/operators/read_lines.mdx @@ -60,16 +60,18 @@ is_error = line.starts_with("error:") Consider using read_delimited_regex for regex-based splitting: ```tql -load_tcp "0.0.0.0:514" -read_delimited_regex "(?=<[0-9]+>)" +accept_tcp "0.0.0.0:514" { + read_delimited_regex "(?=<[0-9]+>)" +} this = line.parse_syslog() ``` ::: ```tql -load_tcp "0.0.0.0:514" -read_lines split_at_regex="(?=<[0-9]+>)" +accept_tcp "0.0.0.0:514" { + read_lines split_at_regex="(?=<[0-9]+>)" +} this = line.parse_syslog() ``` diff --git a/src/content/docs/reference/operators/read_syslog.mdx b/src/content/docs/reference/operators/read_syslog.mdx index 5b6e7e906..234c96efe 100644 --- a/src/content/docs/reference/operators/read_syslog.mdx +++ b/src/content/docs/reference/operators/read_syslog.mdx @@ -176,7 +176,7 @@ When receiving syslog over TCP from systems that use RFC 6587 octet counting, the parser auto-detects and strips the length prefix: ```tql title="Pipeline" -load_tcp "0.0.0.0:514" { +accept_tcp "0.0.0.0:514" { read_syslog } ``` diff --git a/src/content/docs/reference/operators/save_tcp.mdx b/src/content/docs/reference/operators/save_tcp.mdx index b9047f154..1f85c1f7a 100644 --- a/src/content/docs/reference/operators/save_tcp.mdx +++ b/src/content/docs/reference/operators/save_tcp.mdx @@ -4,6 +4,14 @@ category: Outputs/Bytes example: 'save_tcp "0.0.0.0:8090", tls=true' --- +import Op from '@components/see-also/Op.astro'; +import Integration from '@components/see-also/Integration.astro'; + +:::caution[Deprecated] +Use to_tcp for client connections or serve_tcp for server +connections instead. +::: + Saves bytes to a TCP or TLS connection. ```tql diff --git a/src/content/docs/reference/operators/serve_tcp.mdx b/src/content/docs/reference/operators/serve_tcp.mdx new file mode 100644 index 000000000..f87c751b9 --- /dev/null +++ b/src/content/docs/reference/operators/serve_tcp.mdx @@ -0,0 +1,75 @@ +--- +title: serve_tcp +category: Outputs/Events +example: 'serve_tcp "0.0.0.0:8090" { write_json }' +--- + +import Op from '@components/see-also/Op.astro'; +import Integration from '@components/see-also/Integration.astro'; + +Listens for incoming TCP connections and sends events to all connected clients. + +```tql +serve_tcp endpoint:string, [max_connections=int, tls=record] { … } +``` + +## Description + +The `serve_tcp` operator starts a TCP server on the given endpoint and +broadcasts pipeline output to all connected clients. Input events are run +through a nested pipeline that must produce bytes (e.g., `{ write_json }`). + +Clients that connect receive the serialized output as a continuous byte stream. +Clients that disconnect or fail to keep up are dropped with a warning. + +### `endpoint: string` + +The endpoint to listen on. Must be of the form `[tcp://]:`. Use +`0.0.0.0` as the host to accept connections on all interfaces. + +import TLSOptions from '@partials/operators/TLSOptions.mdx'; + + + +### `max_connections = int (optional)` + +The maximum number of simultaneous client connections to accept. Additional +connections beyond this limit are rejected. + +Defaults to `128`. + +### `{ … }` + +The pipeline to serialize input events into bytes. Must produce bytes as output, +for instance `{ write_json }` or `{ write_csv }`. + +## Examples + +### Serve JSON to all connected TCP clients + +```tql +export +serve_tcp "0.0.0.0:8090" { write_json } +``` + +Connect with: + +```bash +nc localhost 8090 +``` + +### Serve with TLS + +```tql +export +serve_tcp "0.0.0.0:8443", tls={certfile: "cert.pem", keyfile: "key.pem"} { + write_json +} +``` + +## See Also + +- accept_tcp +- from_tcp +- to_tcp +- tcp diff --git a/src/content/docs/reference/operators/to_tcp.mdx b/src/content/docs/reference/operators/to_tcp.mdx new file mode 100644 index 000000000..8a54dccc5 --- /dev/null +++ b/src/content/docs/reference/operators/to_tcp.mdx @@ -0,0 +1,60 @@ +--- +title: to_tcp +category: Outputs/Events +example: 'to_tcp "collector.example.com:5044" { write_json }' +--- + +import Op from '@components/see-also/Op.astro'; +import Integration from '@components/see-also/Integration.astro'; + +Connects to a remote TCP or TLS endpoint and sends events. + +```tql +to_tcp endpoint:string, [tls=record] { … } +``` + +## Description + +Connects to the specified TCP endpoint as a client and writes serialized events +to the connection. Input events are run through a nested pipeline that must +produce bytes (e.g., `{ write_json }`). + +If the connection fails, the operator reconnects automatically with exponential +backoff. + +### `endpoint: string` + +The remote endpoint to connect to. Must be of the form +`[tcp://]:`. + +import TLSOptions from '@partials/operators/TLSOptions.mdx'; + + + +### `{ … }` + +The pipeline to serialize input events into bytes. Must produce bytes as output, +for instance `{ write_json }` or `{ write_csv }`. + +## Examples + +### Send JSON to a remote server + +```tql +export +to_tcp "collector.example.com:5044" { write_json } +``` + +### Send with TLS + +```tql +export +to_tcp "collector.example.com:5044", tls={} { write_json } +``` + +## See Also + +- from_tcp +- serve_tcp +- accept_tcp +- tcp