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;
diff --git a/src/content/docs/integrations/nano.mdx b/src/content/docs/integrations/nano.mdx
new file mode 100644
index 000000000..acaef4a98
--- /dev/null
+++ b/src/content/docs/integrations/nano.mdx
@@ -0,0 +1,107 @@
+---
+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"),
+ 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"
+```
+
+## 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",