# Insights Configuration Example
#
# This file shows all available configuration options with their defaults.
#
# Loading this config (any subcommand, not just the server):
#   - insights -c config.yaml            (explicit flag, highest precedence)
#   - INSIGHTS_CONFIG=config.yaml insights ...
#   - place it at ./insights.yaml or <user-config-dir>/insights/config.yaml
#     to have it discovered automatically with no flag
# The top-level nats: section drives client subcommands (query, checks, ops, web, mcp, http, ...);
# pin sink.port and nats.server together so one file serves both sides.
#
# The collector build, insights-collector, reads
# the same file but uses only the data-dir/node/system/sys/scraper/sink/license/
# updater/telemetry settings; the db/indexer/web/prometheus sections are inert
# there (there is no local database).
#
# Configuration priority (highest wins):
#   1. CLI flags (e.g., --server; --nats.server is a hidden alias)
#   2. Environment variables (e.g., INSIGHTS_NATS_SERVER)
#   3. Config file (this file)
#   4. Defaults
#
# The subsystem on/off switches below (scraper/indexer/web/simulator/prometheus/
# updater/telemetry `enabled:`) are spelled as bare shorthands outside this file:
# --web=false on the command line, INSIGHTS_WEB=false in the environment. In
# YAML the section is a map, so use its enabled: key as shown here.

# Log level: debug, info, warn, error
log-level: "info"

# License configuration, for trial builds (the -licensed archives and the
# insights-licensed image). Provide either a raw JWT string or a path to a file
# containing the JWT. If both are set, the file takes precedence.
# A trial build requires it unless the simulator is enabled or the node runs
# neither the indexer nor the scraper. Other builds require no license; leave it
# empty there, since a license that is set is still verified at startup.
license:
  # Raw license JWT token string.
  # Env: INSIGHTS_LICENSE_TOKEN
  token: ""
  # Path to a file containing the license JWT.
  # Env: INSIGHTS_LICENSE_FILE
  file: ""

# Base directory for persistent storage (DuckDB database + JetStream data).
# If empty, a temporary directory is created and data is lost on restart.
# Set this for persistent deployments.
data-dir: ""

# Node identity: names this process for the node-scoped API space
# ($INS.node.<id>.ops.pprof, ...) and as a stable label for operator tooling.
# Defaults to the id of the single system this node serves, which survives a
# reschedule where the hostname would not. A node serving several systems must
# be named; a web node, serving none, falls back to the hostname sanitized to a
# subject-safe token. Allowed characters: letters, digits, '_', '-'.
node:
  # Env: INSIGHTS_NODE_ID
  id: ""
  # Operator-assigned labels describing where this process runs. They are
  # reported on $INS.ping, so `insights node list` and the web Nodes tab can
  # filter and group by them (`env=prod`). Nothing addresses a subject with
  # them and no behaviour keys off them. Keys allow letters, digits, '_', '-'
  # and '.'; values are free text without control characters. Max 16 pairs.
  # Set here rather than by flag or env: a map has no readable flag form.
  # metadata:
  #   region: us-east-1
  #   rack: b12

# System identity: names the monitored NATS system's API subtree
# ($INS.sys.<id>.db.query, ...). Operator-assigned; no stable NATS system
# identifier can be derived. Allowed characters: letters, digits, '_', '-'.
system:
  # Env: INSIGHTS_SYSTEM_ID
  id: "default"
  # Display name for the system, shown beside its id on the web Systems tab and
  # in `insights system list`; the id still addresses it. Free text without
  # control characters. Reported on $INS.ping by the node that scrapes the
  # system, so it is declared there and only there: unlike metadata below, a
  # name has no default layer. Unset, the system is shown by its id.
  # Env: INSIGHTS_SYSTEM_NAME
  # name: "Core (prod)"
  # Operator-assigned labels describing the monitored system, on the same terms
  # as node.metadata above. The node scraping the system has the last word: on
  # a node that only indexes a collector-fed system, these are defaults the
  # collector's own system.metadata overrides per key. With the multi-system
  # list below, they are the default every entry's metadata overrides per key.
  # metadata:
  #   env: prod
  #   team: platform

# Multiple systems (optional). One instance can own several monitored systems,
# each with its own DuckDB instance, JetStream stream, and $INS.sys.<id>.* API.
# Set this list ONLY for a node monitoring more than one system; a single-system
# node (the usual deployment, a collector, a federated site node) uses the flat
# system:/sys:/scraper:/sink: config above and leaves this empty.
#
# systems: is mutually exclusive with system.id / system.name / sys / the flat scraper. A node
# with this list always indexes (indexer.enabled must be true), and cannot run the simulator.
# The top-level db: section becomes the per-system default; each entry overrides
# it.
#
# systems:
#   # Outbound scraper: this node both scrapes and indexes this system. A `scrape`
#   # block makes it scraped locally; omit it for a collector-fed system whose
#   # stream a NATS mirror fills.
#   - id: "core-prod"
#     name: "Core (prod)"      # display name; only on an entry with a scrape block
#     scrape:
#       nats:
#         server: "nats://core.internal:4222"
#         creds: /etc/insights/core-prod.creds
#       interval: 20s          # falls back to scraper.interval
#       # timeout: 30s         # falls back to scraper.timeout
#     retention: 24h           # stream buffer depth (falls back to sink.retention); the DB keeps history
#     db:                      # per-instance DuckDB sizing (overrides the top-level db:)
#       memory-limit: "8GiB"
#     check-thresholds:        # per-system overrides, merged per code onto the top-level set
#       server-003:
#         cpu_percent: 80
#     metadata:                # labels for filtering and grouping, merged per key onto system.metadata
#       env: prod
#       team: platform
#     disabled-checks:         # unioned with the top-level list, never subtracted from it:
#       - account-012        # an entry can turn a check off, not back on
#   # Collector-fed: no scrape block. That is the whole declaration: this node
#   # creates the system's stream as a JetStream mirror of the one a collector
#   # fills, and only indexes it.
#   - id: "edge-west"
#     retention: 6h            # buffer depth, enforced by pruning whole epochs
#     metadata:                # defaults the collector's own system.metadata overrides per key
#       tier: edge
#     #
#     # Nothing else is required. The stream is named "mirror_<id>", the origin
#     # is the collector's "scrape_<id>" (the same id, derived on both sides),
#     # and the collector is asked over $INS.sys.<id>.ops.sink.info for the names,
#     # subject prefix, domain and scrape cadence it actually uses.
#     #
#     # Set this only when the origin's JetStream is reached by something other
#     # than a domain: a subject mapping or an account import under a prefix of
#     # your choosing, which exists on this side and so cannot be discovered.
#     # mirror-api-prefix: "JS.edge-west.API"
#     #
#     # Stream shape. Every field is optional; name defaults to "mirror_<id>"
#     # here and "scrape_<id>" for a scraped system, and the sizing falls back to
#     # the top-level sink:. A mirror must not share its origin's name:
#     # $JS.FC.<stream>.> flow-control subjects are not domain-prefixed.
#     # stream:
#     #   name: "mirror_edge_west"
#     #   replicas: 3            # requires a clustered sink (sink.embed: false)
#     #   storage: "file"        # or "memory"

# Top-level NATS connection for serving the API.
# These credentials are used by the API server and as defaults
# for indexer and scraper if not explicitly configured.
nats:
  # NATS server URL
  server: ""
  # Path to credentials file (.creds)
  creds: ""
  # NATS context name (from nats context)
  context: ""
  # Username for basic auth
  user: ""
  # Password for basic auth
  password: ""
  # User NKey seed (SU...). With jwt set it signs the JWT; on its own it
  # authenticates as an nkey user.
  nkey: ""
  # User JWT for authentication.
  # If nkey is also set, used as NKey-signed JWT auth.
  # If standalone, used as a bearer token (server must have bearer_token enabled).
  jwt: ""
  # TLS certificate path
  tls-cert: ""
  # TLS key path
  tls-key: ""
  # TLS CA certificate path
  tls-ca-cert: ""
  # Perform TLS handshake first (before NATS protocol)
  tls-first: false
  # SOCKS proxy URL for connections
  socks-proxy: ""
  # Request/reply inbox prefix. Empty uses the NATS default (_INBOX).
  # Set this when the account is not granted _INBOX.>
  inbox-prefix: ""

# NATS system account credentials for the target system being scraped.
# This connection is used to issue $SYS requests to collect metrics.
# This is required if the simulator is disabled.
sys: {}

# Database configuration for DuckDB.
db:
  # DuckDB memory limit. Caps the buffer pool to prevent swapping on large databases.
  # Examples: "4GiB", "8GiB", "16GiB". Default: "4GiB".
  # Env: INSIGHTS_DB_MEMORY_LIMIT
  memory-limit: "4GiB"
  # DuckDB worker threads. 0 uses DuckDB's default (all cores).
  # Reduce to leave cores for the indexer and web server.
  # Env: INSIGHTS_DB_THREADS
  threads: 0
  # Max wall-clock time a single $INS.db.query statement may run before it is interrupted.
  # Guards against a pathological query (e.g. a count over a cartesian relation, which is pure
  # CPU and never trips the memory limit) pegging the server. Also bounds each checks.findings
  # and checks.context read. 0 disables the cap.
  # Env: INSIGHTS_DB_QUERY_TIMEOUT
  query-timeout: 2m
  # Max rows the non-streaming $INS.db.query endpoint may return. That endpoint buffers the
  # whole result before responding, so an unbounded SELECT * could return enough rows to run
  # the server out of memory (the DuckDB memory_limit does not bound this buffered reply).
  # Oversized results are rejected with a 413 pointing at the streaming endpoint
  # $INS.db.query.stream. 0 disables the cap.
  # Env: INSIGHTS_DB_QUERY_MAX_ROWS
  query-max-rows: 100000
  # Streaming query endpoint ($INS.db.query.stream). Pushes results as windowed Arrow IPC
  # chunks (rendered to CSV/JSON client-side) so arbitrarily large results never accumulate
  # in memory on either side, so it is not subject to the query-max-rows cap
  # (it carries only the large query-stream.max-rows safety limit below).
  query-stream:
    # Max chunks kept in flight unacknowledged before the server pauses producing. Bounds
    # client-side buffering to roughly window × chunk-size.
    # Env: INSIGHTS_DB_QUERY_STREAM_WINDOW
    window: 16
    # Target size of each chunk message (IEC units). Clamped below the connection max_payload.
    # Env: INSIGHTS_DB_QUERY_STREAM_CHUNK_SIZE
    chunk-size: 512KiB
    # Max time to produce each Arrow batch (incl. the first), bounding a stalled or slow-to-
    # first-row query. 0 disables.
    # Env: INSIGHTS_DB_QUERY_STREAM_BATCH_TIMEOUT
    batch-timeout: 30s
    # Abort the scan if the client sends no acknowledgement within this window (dead/Ctrl-C'd
    # consumer). 0 disables.
    # Env: INSIGHTS_DB_QUERY_STREAM_ACK_IDLE_TIMEOUT
    ack-idle-timeout: 30s
    # Per-buffer Arrow IPC compression for streamed chunks: none, lz4, or zstd. Clients
    # decompress transparently. Compression uses additional CPU; leave none unless bandwidth
    # is the bottleneck.
    # Env: INSIGHTS_DB_QUERY_STREAM_COMPRESSION
    compression: none
    # Abort a streamed query after this many rows, failing fast with guidance to scope it.
    # Rows are counted as they are produced, so the cap bounds the work an unscoped SELECT *
    # over a per-epoch table/view (e.g. hx.conns, millions of rows across all history) can do
    # before it is stopped. Bounded queries (epoch-scoped, entity-scoped, or LIMITed) never
    # reach it. 0 disables the cap for operators who need very large exports.
    # Env: INSIGHTS_DB_QUERY_STREAM_MAX_ROWS
    max-rows: 1000000
    # Max streamed queries one system runs at once; further requests wait. Each running read
    # adds a thread to the DuckDB workers every query shares, so reads beyond the CPUs the
    # process may use only time-slice. 0 uses GOMAXPROCS.
    # Env: INSIGHTS_DB_QUERY_STREAM_CONCURRENCY
    concurrency: 0
  # Check-read endpoints (checks.findings, checks.context).
  checks:
    # Max check reads one system runs at once, the two endpoints sharing the limit, apart from
    # the streamed-query pool so a burst of check reads cannot stall page streams. Further
    # requests wait. 0 uses max(2, GOMAXPROCS/2).
    # Env: INSIGHTS_DB_CHECKS_CONCURRENCY
    concurrency: 0
  # Retention policy for automatic epoch cleanup from DuckDB.
  retention:
    # How long to keep data. 0 disables retention.
    # Env: INSIGHTS_DB_RETENTION_DURATION
    duration: 768h
    # Minimum time between retention sweeps. A sweep runs on the indexer right after
    # the first epoch committed past the interval, never concurrently with a commit.
    # Env: INSIGHTS_DB_RETENTION_INTERVAL
    interval: 10m
  # Live memory profiler: samples duckdb_memory() so `ops sizing` and
  # `ops memory` can use measured peaks instead of estimates.
  memory-profile:
    # Enable the background memory profiler.
    # Env: INSIGHTS_DB_MEMORY_PROFILE_ENABLED
    enabled: true
    # How often to sample duckdb_memory() (metadata-cheap).
    # Env: INSIGHTS_DB_MEMORY_PROFILE_INTERVAL
    interval: 2s
    # Rolling window retained in memory for the peak and the Realtime chart.
    # Env: INSIGHTS_DB_MEMORY_PROFILE_WINDOW
    window: 15m

# External database attachment (requires insights_attach build tag).
# When built with -tags insights_attach, a read-only PostgreSQL database
# can be attached into DuckDB for cross-database operational queries.
# The DuckDB postgres extension is auto-installed on first use (requires network).
#
# Configuration is via environment variables only:
#   INSIGHTS_DB_ATTACH       PostgreSQL connection string (required)
#   INSIGHTS_DB_ATTACH_ALIAS Catalog alias (default: "ops")
#
# Example:
#   INSIGHTS_DB_ATTACH="postgresql://user:pass@host:5432/mydb"
#   INSIGHTS_DB_ATTACH_ALIAS="ops"
#
# Once attached, query with the catalog prefix:
#   SELECT * FROM ops.public.table_name

# Scraper configuration for collecting metrics from NATS servers.
scraper:
  # Enable or disable the scraper
  enabled: true
  # Timeout for scrape requests
  timeout: 30s
  # Interval between scrapes
  interval: 1m
  # Optional NATS connection for publishing scraped data.
  # If not specified, uses the indexer NATS config (or top-level NATS).
  nats: {}
  # Per-endpoint settings. Each NATS monitoring endpoint except varz (connz,
  # subsz, routez, jsz, gatewayz, leafz, accountz, accstatz, healthz, raftz,
  # ipqueuesz) takes `disabled` to skip it entirely and `frequency` to scrape
  # it every Nth interval (1 = every interval, 2 = every other, ...).
  endpoints:
    jsz:
      disabled: false
      frequency: 1
      # Request consumer detail. On a fleet with many consumers it is most of
      # the jsz reply; turn it off to keep streams and accounts without it.
      # Consumer pages and checks then have no data, and say so.
      consumers: true
  # Optional filter controlling which discovered servers are scraped. Matching
  # is exact (by server name, cluster name, or tag). Deny takes precedence over
  # allow; when any allow list is set, a server must match it to be scraped.
  # Excluded servers receive no monitoring requests. Omit to scrape every
  # discovered server.
  filter:
    # Scrape only servers matching these. When all allow lists are empty, every
    # server not denied is scraped.
    allow:
      servers: []
      clusters: []
      tags: []
    # Never scrape servers matching these (takes precedence over allow).
    deny:
      servers: []
      clusters: []
      tags: []

# Indexer configuration for processing and storing scraped data.
indexer:
  # Enable or disable the indexer
  enabled: true
  # Start consuming from the latest message in the stream
  stream-latest: false
  # Start consuming from a specific epoch number
  stream-epoch: 0
  # Filter specific Z endpoints to process (comma-separated: varz,jsz,connz)
  stream-filters: ""
  # Downsample interval for consuming epochs
  stream-interval: 0s
  # Optional NATS connection for consuming scraped data.
  # If not specified, uses the top-level NATS config.
  nats: {}

# Sink configuration for the embedded NATS server.
sink:
  # Use an embedded NATS server for storing scraped data.
  embed: true
  # Optional NATS server config file for the embedded server.
  # The file is parsed first, then Insights-specific settings are applied on top.
  # config-file: /path/to/nats-server.conf
  # Host to bind the embedded NATS server to
  host: "127.0.0.1"
  # Port to bind the embedded NATS server to (0 = random)
  port: 0
  # JetStream file storage limit for the embedded NATS server (IEC units).
  # Empty lets the server size it at 75% of the disk free at startup. Overrides
  # a limit set in config-file. Writes fail once the store is full, so size it
  # above what sink.retention keeps.
  # Env: INSIGHTS_SINK_MAX_STORE
  # max-store: 50GiB
  # JetStream stream name for storing scraped data.
  # insights-collector derives this from system.id ("scrape_<id>") when the id is
  # set and this is left at the default, so a central node mirroring the collector lands
  # on the same name without being told it.
  stream: "scrape"
  # Batch size for sink operations
  batch-size: 500
  # How long to keep scraped data in the sink stream. 0 disables retention.
  # Enforced by pruning whole epochs, not by the stream's max age: a
  # message-level limit would leave the indexer a scrape truncated mid-epoch.
  # Env: INSIGHTS_SINK_RETENTION
  retention: 24h
  # Replica count for the sink stream. Requires a clustered sink; the embedded
  # server is a single node, and embed: true with replicas > 1 is rejected.
  # Env: INSIGHTS_SINK_REPLICAS
  replicas: 1
  # Storage class for the sink stream: "file" or "memory". The stream is a
  # buffer (the database holds the history), so memory storage is a reasonable
  # choice for a system that can afford to lose the buffer on restart.
  # Env: INSIGHTS_SINK_STORAGE
  storage: "file"

# Embedded NATS simulator configuration.
# When enabled, starts real in-process NATS clusters with traffic workloads
# so you can try insights with zero configuration.
simulator:
  # Enable the embedded simulator
  enabled: false
  # Profiles: core-{small,medium,large}, js-{small,medium,large},
  #   super-{small,medium,large}, leaf-{small,medium,large},
  #   super-leaf-{small,medium,large}
  profile: "js-small"

# Web server configuration for the Exo-based UI.
web:
  # Enable or disable the web server
  enabled: true
  # Hostname to bind the web server to
  hostname: "127.0.0.1"
  # Port to bind the web server to
  port: 8080
  # Session seed for secure cookies.
  # The default "insights" preserves sessions across restarts but should be
  # changed to a unique value for production deployments.
  session-seed: "insights"
  # Enable TLS (HTTPS). When enabled, HTTP/2 is automatically available via ALPN.
  # Requires tls-cert and tls-key below; enabling TLS without them is an error.
  tls: false
  # Path to TLS certificate file.
  tls-cert: ""
  # Path to TLS private key file.
  tls-key: ""
  # Path to TLS CA certificate file for verifying client certificates.
  tls-ca-cert: ""
  # Public URL of this Insights instance, used in webhook alert payload links.
  # Env: INSIGHTS_WEB_EXTERNAL_URL
  external-url: ""

# Prometheus metrics endpoint configuration.
# Exposes check failure counts as Prometheus gauges for external scraping.
prometheus:
  # Enable or disable the Prometheus metrics endpoint
  enabled: true
  # Hostname to bind the Prometheus metrics server to
  hostname: "127.0.0.1"
  # Port to bind the Prometheus metrics server to
  port: 9091
  # Serve every node's metrics from this endpoint instead of only this node's.
  # Each scrape discovers the deployment's nodes over $INS.ping and asks each one
  # for its snapshot. Enable it on exactly one node: scraping two aggregators
  # reports every series twice, once per endpoint.
  # Env: INSIGHTS_PROMETHEUS_AGGREGATE
  aggregate: false
  # Budget for one aggregated collection: node discovery, then the per-node
  # requests. Must be shorter than the scrape timeout of whatever reads this
  # endpoint, or Prometheus gives up before the answer. Ignored unless aggregate.
  # Env: INSIGHTS_PROMETHEUS_TIMEOUT
  timeout: 2s

# Version checker configuration.
# When enabled, periodically checks GitHub for a newer release and notifies
# via log messages and a UI toolbar badge. Does not download or restart.
# The release repository is compiled into the binary, so there is no URL
# or channel to configure.
# For trial builds, the info dialog shows a direct download link for
# the current OS/architecture. Other builds direct users to
# purchases.synadia.com.
updater:
  # Enable or disable version checking
  enabled: true
  # Interval between update checks
  interval: 1h

# Product telemetry. A pulse every few minutes reporting how this instance is used, so a
# trial can be told apart from an install that was started once and forgotten.
# Only trial builds contain the code that sends it; every other build has no
# reporter at all, and these settings are inert there.
#
# What a pulse contains is listed in full in the reference documentation. In
# short: a random instance id generated here, the release version and platform,
# the scrape and pulse intervals, how large the watched environment is, and counts
# of pages opened in the web UI and requests made by the CLI, MCP and HTTP
# clients. It carries nothing about the monitored system
# itself: no server, cluster, account, subject, stream or consumer names, no
# addresses, no message data, no configuration values, no query text.
#
# The instance is identified only by the license it presents, so the payload
# contains nothing that names you.
# There is no destination setting: where a pulse goes, and the credential it is
# published with, are fixed when the binary is built. You can switch telemetry
# off, or read exactly what it would send, but not redirect it.
telemetry:
  # Enable or disable telemetry
  enabled: true
  # Interval between pulses; the first is sent at startup
  interval: 5m
  # Log every pulse, so you can read exactly what leaves this process
  print: false

# Notification endpoint definitions (optional, YAML-only; there is no equivalent
# flag or environment variable). Endpoints receive JSON payloads when subscribed
# check results change state (firing/resolved). Endpoints are read-only in the web
# UI; all configuration is done here.
#
# Five types are supported:
#   webhook:       Receives the full Payload envelope (status, alerts, groupLabels,
#                  etc.), compatible with Alertmanager webhook_config receivers.
#   alertmanager:  Receives a bare []Alert array, compatible with the Alertmanager
#                  v2 POST /api/v2/alerts API.
#   servicenow:    Receives the webhook envelope, plus every active alert re-sent
#                  each minute with a rolling endsAt, for a ServiceNow Scripted
#                  REST API. auth-type is required: "apikey" (ServiceNow REST
#                  API key, sent as x-sn-apikey) or "basic".
#   splunk-oncall: Receives one Splunk On-Call REST endpoint event per alert,
#                  keyed on the alert fingerprint so a resolve recovers the
#                  incident its fire opened. Takes api-key and routing-key
#                  (both sent in the URL path) instead of auth-type; url
#                  defaults to the public On-Call endpoint.
#   datadog:       Receives one Datadog Events API alert event per alert,
#                  aggregated on the alert and endpoint so Datadog On-Call
#                  resolves the page its fire opened. The oncall_team
#                  annotation names the team paged. Takes api-key (sent as
#                  DD-API-KEY) instead of auth-type, and site instead of url.
#
# All types share the same endpoint fields. Names must be unique across all types.
# A malformed endpoint (missing name/url, non-http(s) url, unknown auth-type, a
# duplicate name, an unknown severities value, or a systems entry naming a system
# this node does not own) fails startup with an error naming the offending entry;
# it is never silently skipped.
# Delivery uses a fixed 10s request timeout and 3 attempts with exponential backoff
# (1 attempt on a 4xx other than 429); a failed delivery is retried for that
# endpoint at up to one-minute intervals until it succeeds.
#
# subscriptions and ignore are lists of check-code patterns: an exact code
# (server-001), a trailing-wildcard prefix (server-*), or "*" for every code.
# ignore is applied after subscriptions, so it carves exceptions out of a
# wildcard. An empty subscriptions list delivers nothing: subscribing is
# explicit. A pattern that matches no known check code fails startup, so a typo
# never silently delivers nothing.
#
# systems is the inverse: an empty list delivers for every system this node owns,
# and a non-empty list scopes the endpoint to those systems (each must be one this
# node owns).
#
# severities limits an endpoint to findings at those severities (info, warning,
# critical); empty delivers every severity. A finding whose severity moves out
# of the list is delivered as a resolve for the severity the endpoint holds, and
# one moving into it fires at the new severity.
#
# labels is an ordered list of rules adding labels and annotations to every
# endpoint's alerts; an endpoint's own labels/annotations apply after them and
# win a shared key. A match field set to a list must match one of its values;
# an empty match selects every alert. Annotation values are Go templates over
# .Code, .Severity, .Scope, .Category, .System, .EntityType, .EntityKey,
# .EntityName and .EntityPK. Values must be strings: quote numbers and booleans.
# Built-in label/annotation names, unknown match values and a rule matching
# entity-name that sets labels fail startup.
# Example (uncomment and adjust):
# notifications:
#   labels:
#     - match: {}                          # every alert
#       labels: {env: prod}
#     - match:
#         checks: [stream-*]               # check-code patterns
#         severity: [critical]             # info, warning, critical
#         # category, scope, systems, entity-type: lists of values
#       labels: {team: data-platform}
#       annotations: {runbook: "https://wiki/insights/{{ .Code }}"}
#     - match: {entity-type: [stream], entity-name: ["ORDERS*"]}
#       annotations: {owner: orders-team}  # entity-name rules set annotations only
#   webhook:
#     - name: "PagerDuty Relay"
#       url: "https://example.com/webhook"
#       enabled: true
#       auth-type: ""          # "", "basic", "bearer", "hmac"
#       auth-user: ""          # username for basic auth
#       auth-token: "${WEBHOOK_TOKEN}"    # password (basic), token (bearer), or secret (hmac);
#                                         # ${NAME} in url/auth-user/auth-token reads that env var
#       subscriptions:         # check-code patterns to subscribe to (empty = deliver nothing)
#         - server-*
#         - stream-002
#       ignore:                # patterns to drop after a subscription match
#         - server-046
#       systems:               # systems to deliver for (empty = every system on this node)
#         - prod-us
#         - prod-eu
#       severities:            # info, warning, critical (empty = every severity)
#         - critical
#       labels:                # added to this endpoint's alerts, after the rules
#         receiver: pagerduty
#       annotations:
#         dashboard: "https://grafana/d/nats?var-system={{ .System }}"
#   alertmanager:
#     - name: "Alertmanager"
#       url: "http://localhost:9093/api/v2/alerts"
#       subscriptions:
#         - "*"
#   servicenow:
#     - name: "ServiceNow"
#       url: "https://<instance>.service-now.com/api/<namespace>/insights/alerts"
#       auth-type: "apikey"
#       auth-token: "${SERVICENOW_API_KEY}"
#       subscriptions:
#         - "*"
#   splunk-oncall:
#     - name: "Splunk On-Call"
#       api-key: "${SPLUNK_ONCALL_KEY}"   # REST endpoint integration key
#       routing-key: "nats-platform"      # picks the team and escalation policy
#       subscriptions:
#         - "*"
#       severities:                       # INFO events never open an incident
#         - warning
#         - critical
#   datadog:
#     - name: "Datadog"
#       api-key: "${DD_API_KEY}"          # Datadog API key; no app key needed
#       site: "datadoghq.com"             # your DD_SITE; picks the intake url
#       subscriptions:
#         - "*"
#       severities:                       # page on warning and critical only
#         - warning
#         - critical
#       annotations:
#         oncall_team: "nats-platform"    # the On-Call team handle to page

# Per-check threshold overrides for the checks (optional, YAML-only; there
# is no equivalent flag or environment variable).
# Many checks accept tunable parameters (a CPU percentage, a lag percentage,
# a byte size, ...) that control when they produce a finding. Defaults suit most
# deployments; override only what you need, keyed by check code and parameter name.
# Run `insights checks list` for codes and `insights checks info <CODE>` for the
# parameters a check accepts. Duration parameters take Go duration strings (e.g.
# 50ms, 5m, 1h); one that bounds a window measured across scrapes must be >= the
# scrape interval. Size parameters take IEC binary strings (e.g. 64 KiB, 1 MiB,
# 10 GiB) or a bare number of bytes. Percentage parameters end in _percent and
# take a 0-100 value. An unknown parameter name or check code fails startup
# with an error naming it. A code in the uppercase, underscored form (SERVER_003)
# is reported with its current spelling, and a renamed parameter with its
# replacement; any other unknown code is reported as unknown.
# Example (uncomment and adjust):
# check-thresholds:
#   server-003:
#     cpu_percent: 80        # flag servers above 80% per-core CPU (default 90)
#   stream-002:
#     lag_percent: 20        # flag stream replica lag above 20% (default 10)
#   stream-022:
#     max_memory: 500 MiB    # flag memory-backed streams above 500 MiB (default 100 MiB)
#   # Optimization checks also accept `lookback`, the analysis window for
#   # range-based checks (default 6h):
#   account-011:
#     lookback: 4h

# Checks to turn off (optional, YAML-only; there is no equivalent flag or
# environment variable).
# A disabled check keeps running and stays visible, marked disabled, with its real
# findings. It is left out of the health grades and never delivered as a
# notification: no advisory, no webhook, no alert.
# Turn a check off when it cannot be acted on in this topology (a cross-cluster
# gateway traffic ratio is above any threshold by construction in a single-account
# supercluster) rather than to quiet a signal you would still want to see.
# A code matching no check fails startup with an error naming it; a code in the
# uppercase, underscored form (ACCOUNT_012) is reported with its current spelling.
# `insights config check` runs the same validation without starting the node.
# Example (uncomment and adjust):
# disabled-checks:
#   - account-012
