From 0b7eb22b7838c2444e501fc0712f5b0225ef3c06 Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Tue, 30 Jun 2026 08:47:56 +0200 Subject: [PATCH 1/3] Scope semantic list icons to See Also Only use semantic cross-reference icons as list markers for lists immediately following a See Also heading. This keeps ordinary prose bullets as bullets when they contain inline operator or function links. Assisted-by: GPT-5 (Codex) --- src/assets/styles.css | 49 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/src/assets/styles.css b/src/assets/styles.css index 1f5092963..137477eae 100644 --- a/src/assets/styles.css +++ b/src/assets/styles.css @@ -2039,15 +2039,29 @@ starlight-tabs [role="tabpanel"] > :last-child { padding-inline: 0.2em; } -/* Semantic-link list items use the icon as their marker without affecting - neighboring plain-text bullets in mixed lists. */ -ul:has(> li > .semantic-link) > li:has(> .semantic-link) { +/* See Also list items use the icon as their marker without affecting inline + semantic links in ordinary prose lists. */ +.sl-markdown-content + :is( + .sl-heading-wrapper:has(> :is(h2, h3, h4, h5, h6)[id^="see-also"]), + :is(h2, h3, h4, h5, h6)[id^="see-also"] + ) + + ul + > li:has(> .semantic-link) { position: relative; list-style: none; line-height: 1.75; } -ul:has(> li > .semantic-link) > li:has(> .semantic-link) > .semantic-link > .inline-icon { +.sl-markdown-content + :is( + .sl-heading-wrapper:has(> :is(h2, h3, h4, h5, h6)[id^="see-also"]), + :is(h2, h3, h4, h5, h6)[id^="see-also"] + ) + + ul + > li:has(> .semantic-link) + > .semantic-link + > .inline-icon { display: inline; position: absolute; left: -1.25em; @@ -2056,19 +2070,40 @@ ul:has(> li > .semantic-link) > li:has(> .semantic-link) > .semantic-link > .inl } :root[data-theme="dark"] - ul:has(> li > .semantic-link) + .sl-markdown-content + :is( + .sl-heading-wrapper:has(> :is(h2, h3, h4, h5, h6)[id^="see-also"]), + :is(h2, h3, h4, h5, h6)[id^="see-also"] + ) + + ul > li:has(> .semantic-link) > .semantic-link > .inline-icon { color: var(--tnz-neutral-500); } -ul:has(> li > .semantic-link) > li:has(> .semantic-link) > .semantic-link > svg.inline-icon { +.sl-markdown-content + :is( + .sl-heading-wrapper:has(> :is(h2, h3, h4, h5, h6)[id^="see-also"]), + :is(h2, h3, h4, h5, h6)[id^="see-also"] + ) + + ul + > li:has(> .semantic-link) + > .semantic-link + > svg.inline-icon { width: 14px; height: 14px; } -ul:has(> li > .semantic-link) > li:has(> .semantic-link) > .semantic-link > .fn-icon { +.sl-markdown-content + :is( + .sl-heading-wrapper:has(> :is(h2, h3, h4, h5, h6)[id^="see-also"]), + :is(h2, h3, h4, h5, h6)[id^="see-also"] + ) + + ul + > li:has(> .semantic-link) + > .semantic-link + > .fn-icon { font-family: var(--tnz-font-mono); font-size: 12px; font-weight: 700; From a96535b75614e6a8f5baef414450f3a16053c4a2 Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Tue, 30 Jun 2026 08:59:35 +0200 Subject: [PATCH 2/3] Add Nano integration docs Document how to forward OCSF events from Tenzir to the Nano SIEM: - New integrations/nano.mdx covering the {event, source_type} wire shape, the to_clickhouse sink into nanosiem.ocsf_logs_native_raw, and reading events back from nanosiem.ocsf_logs. - Add Nano to the Security Tools sidebar section. - Add reciprocal nano See Also links to the to_clickhouse and from_clickhouse operator references. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/content/docs/integrations/nano.mdx | 106 ++++++++++++++++++ .../reference/operators/from_clickhouse.mdx | 1 + .../reference/operators/to_clickhouse.mdx | 1 + src/sidebar.ts | 1 + 4 files changed, 109 insertions(+) create mode 100644 src/content/docs/integrations/nano.mdx diff --git a/src/content/docs/integrations/nano.mdx b/src/content/docs/integrations/nano.mdx new file mode 100644 index 000000000..ed7f81ec7 --- /dev/null +++ b/src/content/docs/integrations/nano.mdx @@ -0,0 +1,106 @@ +--- +title: Nano +--- + +[Nano](https://nano.rs) is a cloud-native SIEM that stores security telemetry in +ClickHouse. Tenzir ships OCSF events into nano over the native ClickHouse +protocol with to_clickhouse, writing each event in nano's +`{event, source_type}` wire shape into the `nanosiem.ocsf_logs_native_raw` +ingest table. + +:::tip[Bundled Tenzir node] +Nano can bundle a Tenzir node for you: its installer's "OCSF + Tenzir" option +stands up a Tenzir node with this sink pre-wired, leaving you to own the source +and the OCSF mapping in between. For worked source mappings (Sysmon, Windows +Event Logs, Apache, CloudTrail, proxy), see nano's [direct OCSF ingestion +guide](https://nano.rs/docs/ocsf/integrations/direct-ocsf). +::: + +{/*DIAGRAM: insert the nano.excalidraw image reference here once the asset is provided.*/} + +## Write OCSF events to nano + +Nano accepts a single wire shape: a record with two fields. + +| Field | Type | Notes | +| :--- | :--- | :--- | +| `event` | `record` | The full OCSF 1.8.0 event. Must carry `class_uid` and `time` (epoch milliseconds). Fields with no OCSF home go under `event.unmapped`. | +| `source_type` | `string` | Lowercase per-feed identifier, such as `windows_sysmon`. A missing or uppercase value lands rows as `unknown`. | + +If you already have a stream of OCSF events in Tenzir, forwarding it to nano is +two steps: wrap each event in this shape, then write it to +`nanosiem.ocsf_logs_native_raw` with to_clickhouse. + +```tql +subscribe "ocsf" +this = { event: this, source_type: "suricata" } +to_clickhouse table="nanosiem.ocsf_logs_native_raw", + host="clickhouse", port=9000, + user="nanosiem_ingest", + password=secret("NANO_INGEST_PASSWORD"), + tls=false, mode="append" +``` + +The `this = { event: this, source_type: ... }` assignment is the heart of the +integration: it nests the entire OCSF record under `event` and adds a sibling +`source_type` that names the feed. Nano stores that envelope, then derives the +queryable `nanosiem.ocsf_logs` table from this entrypoint through a +materialized-view chain, so you only ever write the entrypoint, never +`nanosiem.ocsf_logs` directly. + +Sink arguments: + +- `table` is `nanosiem.ocsf_logs_native_raw`, nano's entrypoint for native + ingestion. +- `user` and `password` are nano's INSERT-only + [ingest credential](https://nano.rs/docs/ocsf/integrations/direct-ocsf) + (`nanosiem_ingest`). +- `mode="append"` writes into the existing table. The `json=` option is for + table creation only and errors in `append` mode. +- `tls=false` matches a Tenzir node colocated with nano on a plaintext network. + For a remote cluster, omit it (TLS is the default) and use port `9440`. + +Where the OCSF stream comes from is up to you. When the input is raw logs rather +than OCSF, [map it to OCSF](/guides/normalization/map-to-ocsf) 1.8.0 first; +nano's [direct OCSF ingestion guide](https://nano.rs/docs/ocsf/integrations/direct-ocsf) +has per-source mappings. + +:::caution[Use the native entrypoint] +Write to `nanosiem.ocsf_logs_native_raw`, not `nanosiem.ocsf_logs_raw`. This is a +limitation in to_clickhouse, not in ClickHouse or your data: on +`append`, the operator reads the target table's schema and validates every +column type before it writes any rows, and its type mapping does not recognize +two types that `ocsf_logs_raw` uses. It expects a plain `String` but finds +`LowCardinality(String)`, and a bare `DateTime64` but finds a timezone-qualified +one (`DateTime64(3, 'UTC')`). Either mismatch raises `unsupported ClickHouse +type`, so the pipeline fails immediately and never inserts a row, even though it +would only write the `event` and `source_type` columns. + +The native entrypoint avoids this by exposing only `event` (JSON) and a plain +`source_type` (String). A materialized view forwards from there into +`ocsf_logs_raw` and on into `nanosiem.ocsf_logs`. +::: + +## Read events back from nano + +Nano exposes a native ClickHouse endpoint, so Tenzir can read stored events back +with from_clickhouse using a read-capable credential. Query the +flattened `nanosiem.ocsf_logs` table, whose OCSF paths are dotted columns such as +`src_endpoint.ip`: + +```tql +from_clickhouse host="clickhouse", port=9000, + user="nanosiem", + password=secret("NANO_PASSWORD"), + sql="SELECT time, class_uid, `src_endpoint.ip`, message FROM nanosiem.ocsf_logs WHERE severity_id >= 3 ORDER BY time DESC" +publish "nano-findings" +``` + +## See Also + +- from_clickhouse +- subscribe +- to_clickhouse +- normalization/map-to-ocsf +- routing/send-to-destinations +- clickhouse diff --git a/src/content/docs/reference/operators/from_clickhouse.mdx b/src/content/docs/reference/operators/from_clickhouse.mdx index b36021a37..51366f574 100644 --- a/src/content/docs/reference/operators/from_clickhouse.mdx +++ b/src/content/docs/reference/operators/from_clickhouse.mdx @@ -163,3 +163,4 @@ from_clickhouse sql="DESCRIBE TABLE events", tls=false - to_clickhouse - collecting/read-from-data-stores - clickhouse +- nano diff --git a/src/content/docs/reference/operators/to_clickhouse.mdx b/src/content/docs/reference/operators/to_clickhouse.mdx index c81259285..985b57c92 100644 --- a/src/content/docs/reference/operators/to_clickhouse.mdx +++ b/src/content/docs/reference/operators/to_clickhouse.mdx @@ -247,3 +247,4 @@ This creates the following table: - ocsf::cast - routing/send-to-destinations - clickhouse +- nano diff --git a/src/sidebar.ts b/src/sidebar.ts index 099929867..bc819ed13 100644 --- a/src/sidebar.ts +++ b/src/sidebar.ts @@ -424,6 +424,7 @@ export const integrations = [ "integrations/arcsight", "integrations/crowdstrike", "integrations/graylog", + "integrations/nano", "integrations/qradar", "integrations/sentinelone-data-lake", "integrations/suricata", From ac9f557b537b6871b1361e41b400146e0bc3bca6 Mon Sep 17 00:00:00 2001 From: Matthias Vallentin Date: Tue, 30 Jun 2026 09:07:48 +0200 Subject: [PATCH 3/3] Fix Nano read example TLS setting Assisted-by: GPT-5 (Codex) --- src/content/docs/integrations/nano.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/content/docs/integrations/nano.mdx b/src/content/docs/integrations/nano.mdx index ed7f81ec7..acaef4a98 100644 --- a/src/content/docs/integrations/nano.mdx +++ b/src/content/docs/integrations/nano.mdx @@ -92,6 +92,7 @@ flattened `nanosiem.ocsf_logs` table, whose OCSF paths are dotted columns such a from_clickhouse host="clickhouse", port=9000, user="nanosiem", password=secret("NANO_PASSWORD"), + tls=false, sql="SELECT time, class_uid, `src_endpoint.ip`, message FROM nanosiem.ocsf_logs WHERE severity_id >= 3 ORDER BY time DESC" publish "nano-findings" ```