Synadia Insights
Search
The search API takes an input string and tries to parse it into a structured form with the semantics defined below. If that fails, it falls back to free-text fuzzy search.
Term Structure
A search term can have up to four components:
[[<entity>@]<attribute>:[<operator>]]<value>
value. The only required component. Without a qualifier, the value is treated as a free-text search against a predefined set of string attributes across all applicable entities. Quote a value to allow whitespace. Escape quotes with\".attribute. Qualifies the value against a specific attribute. An attribute can span multiple entities (for example,namematches server, stream, consumer, and so on). Every attribute has a type, and the value is parsed accordingly.operator. An explicit comparison operator for the attribute. Each type has its own set of allowed operators. All operators take a single value except..(between), which takes two, and the implicit is set operator, which takes none.entity. Fully qualifies the term by binding the attribute to a specific entity type.
Examples
nats-1 # free-text: matches name, id, cluster, ip, etc.
name:nats-1 # attribute: name equals "nats-1" (ILIKE) across all entities with name
server@name:nats-1 # entity + attribute: only match servers
rtt:>100ms # duration comparison: rtt greater than 100ms
rtt:20ms..40ms # range: rtt between 20ms and 40ms (inclusive)
in_bytes:>1gib # byte comparison: more than 1 GiB received
name:"With Spaces" # quoted value with spaces
cpu:>80.5 # float comparison
is_mirror:true # boolean match
description: # is set: the attribute holds a value, whatever it is
metadata.env:prod # label: the entity is labeled env=prod
metadata.env: # label: the entity carries an env label at all
NOT slow_consumers:>0 # negation
(name:foo OR name:bar) rtt:<10ms # grouping with implicit AND
Multiple Terms
- Terms are combined with logical operators:
AND,OR, andNOT. - Without an explicit operator, adjacent terms are implicitly ANDed.
- Grouping with parentheses
()overrides default precedence. - Precedence (lowest to highest):
OR<AND<NOT. AND,OR, andNOTare keywords in either case:andandANDboth apply. Quote a value that would otherwise read as a keyword.
Value Types
Every attribute has one of the following types. The value in a search term must be parseable as its attribute's type.
string
Plain text. String equality uses case-insensitive matching (ILIKE). Supports =, !=, and ~ (like) operators.
int
Integer number. Supports =, !=, >, >=, <, <=, and .. (between).
float
Floating-point number. Supports the same operators as int.
bool
Boolean value: true or false. Supports = and !=.
duration
A number with a time unit suffix. Stored internally as nanoseconds. Supports the same operators as int.
| Unit | Suffix |
|---|---|
| Nanosecond | ns |
| Microsecond | us, µs |
| Millisecond | ms |
| Second | s |
| Minute | m |
| Hour | h |
| Day | d |
bytes
A number with a byte size unit suffix (IEC binary units, powers of 1024). Supports the same operators as int.
| Unit | Suffix |
|---|---|
| Byte | b |
| Kibibyte | kib |
| Mebibyte | mib |
| Gibibyte | gib |
| Tebibyte | tib |
SI suffixes (kb, mb, gb, tb) are not accepted. Use the IEC equivalents above.
timestamp
An ISO-8601 date or datetime value. Supports the same operators as int (for ordering and range comparisons).
Examples: 2024-01-15, 2024-01-15T10:30:00Z
string[]
An array of strings. Matched using the same operators as string. The comparison checks if any element in the array matches.
Example: tags:nats-server matches any entity whose tags array contains an element matching nats-server.
Operators
Not all operators apply to all types. The table below shows which operators are valid for each type.
| Operator | Syntax | string | string[] | int | float | duration | bytes | timestamp | bool |
|---|---|---|---|---|---|---|---|---|---|
| Equal | (implicit) | Y | Y | Y | Y | Y | Y | Y | Y |
| Not equal | != | Y | Y | Y | Y | Y | Y | Y | Y |
| Greater than | > | Y | Y | Y | Y | Y | |||
| Greater or equal | >= | Y | Y | Y | Y | Y | |||
| Less than | < | Y | Y | Y | Y | Y | |||
| Less or equal | <= | Y | Y | Y | Y | Y | |||
| Between | .. | Y | Y | Y | Y | Y | |||
| Like | ~ | Y | Y | ||||||
| Is set | (no value) | Y | Y | Y | Y | Y | Y | Y | Y |
String equality (=) is case-insensitive (ILIKE). The ~ operator performs an ILIKE substring match.
An attribute with nothing after the colon, such as description:, asks whether it holds a value. An empty string does not count as set for a string, nor an empty list for a string[]. An operator without its value, such as rtt:>, is still an error.
Entities
There are 13 searchable entity types. Each reads the tables below at the single epoch described under Query Semantics.
| Entity | Reads | Epoch-scoped |
|---|---|---|
server | hx.server_stats + ident and opts | Yes |
cluster | hx.server_stats aggregated per cluster | Yes |
account | hx.account_ident + opts, stats and subscription counts | Identity kept, values at the epoch |
user | hx.user_ident + opts and connection counts | Identity kept, values at the epoch |
connection | hx.conn_stats + ident, with account, server and user names | Yes |
stream | hx.stream_replica_stats leader rows + ident and opts | Yes |
kvstore | The same, for KV-backing streams | Yes |
objectstore | The same, for object-store-backing streams | Yes |
service | $SRV.INFO subscriptions in hx.sub_ident, one row per account and service name | Yes |
consumer | hx.consumer_replica_stats leader rows + ident and opts | Yes |
leaf | hx.leafs | Yes |
route | hx.routes | Yes |
gateway | hx.gateways | Yes |
Each entity selects every attribute listed for it below; an attribute is never offered without a column behind it.
Attributes
Cross-Entity Attributes
These attributes appear on multiple entities. Using one without an entity qualifier searches all entities that have it.
| Attribute | Type | server | cluster | account | user | connection | stream | kvstore | objectstore | service | consumer | leaf | route | gateway |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
name | string | Y | Y | Y | Y | Y | Y | Y | Y | Y | ||||
account | string | Y | Y | Y | Y | Y | Y | Y | ||||||
ip | string | Y | Y | Y | Y | Y | ||||||||
cluster | string | Y | Y | Y | Y | Y | Y | |||||||
rtt | duration | Y | Y | Y | Y | |||||||||
in_msgs | int | Y | Y | Y | Y | |||||||||
out_msgs | int | Y | Y | Y | Y | |||||||||
in_bytes | bytes | Y | Y | Y | Y | |||||||||
out_bytes | bytes | Y | Y | Y | Y | |||||||||
num_subs | int | Y | Y | Y | Y | |||||||||
version | string | Y | Y | |||||||||||
description | string | Y | Y | Y | Y | |||||||||
check | string | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
The check attribute filters entities by check code (for example, check:server-003). It matches against materialized check findings for that entity. On the streams page, a check filter also lists the KV and object-store backing streams the check fired on, so the filtered list matches the stream check's count on the system page.
Server Attributes
| Attribute | Type |
|---|---|
id | string |
host | string |
cores | int |
port | int |
start_time | timestamp |
tags | string[] |
git_commit | string |
go_version | string |
system_account | string |
config_load_time | timestamp |
auth_required | bool |
auth_timeout | duration |
tls_required | bool |
tls_verify | bool |
max_payload | bytes |
max_pending | bytes |
max_control_line | int |
max_connections | int |
ping_interval | duration |
ping_max | int |
write_deadline | duration |
http_host | string |
http_port | int |
https_port | int |
http_base_path | string |
leaf_host | string |
leaf_port | int |
leaf_auth_timeout | duration |
leaf_tls_timeout | duration |
js_domain | string |
js_max_memory | bytes |
js_max_store | bytes |
js_store_dir | string |
js_sync_interval | duration |
js_sync_always | bool |
js_compress_ok | bool |
js_unique_tag | string |
memory | bytes |
cpu | float |
connections | int |
subscriptions | int |
routes | int |
leafs | int |
gateways | int |
js_memory | bytes |
js_storage | bytes |
slow_consumers | int |
ha_assets | int |
total_connections | int |
stale_connections | int |
stalled_clients | int |
remotes | int |
js_reserved_memory | bytes |
js_reserved_storage | bytes |
js_api_total | int |
js_api_errors | int |
js_api_inflight | int |
Cluster Attributes
| Attribute | Type |
|---|---|
cluster | string |
servers | int |
cpu | float |
memory | bytes |
connections | int |
ha_assets | int |
servers, cpu, memory, connections and ha_assets are aggregates over the cluster's servers at the epoch, matching the clusters table: servers counts them, cpu averages per-core utilization, and the rest are sums. A cluster result is one row per cluster, not one per server.
Account Attributes
| Attribute | Type |
|---|---|
is_system | bool |
jetstream_enabled | bool |
conns | int |
subs | int |
msgs_sent | int |
msgs_recv | int |
leafnodes | int |
slow_consumers | int |
expired | bool |
complete | bool |
num_exports | int |
num_imports | int |
issuer_key | string |
label | string |
tags | string[] |
max_subs | int |
max_data | bytes |
max_payload | bytes |
max_conn | int |
max_leaf_conn | int |
max_imports | int |
max_exports | int |
js_mem_storage | bytes |
js_disk_storage | bytes |
js_max_streams | int |
js_max_consumers | int |
js_max_ack_pending | int |
last_update | timestamp |
total_conns | int |
bytes_sent | bytes |
bytes_recv | bytes |
route_bytes_sent | bytes |
route_bytes_recv | bytes |
route_msgs_sent | int |
route_msgs_recv | int |
gateway_bytes_sent | bytes |
gateway_bytes_recv | bytes |
gateway_msgs_sent | int |
gateway_msgs_recv | int |
leaf_bytes_sent | bytes |
leaf_bytes_recv | bytes |
leaf_msgs_sent | int |
leaf_msgs_recv | int |
User Attributes
| Attribute | Type |
|---|---|
account | string |
bearer | bool |
name_tag | string |
connections | int |
issuer_account | string |
tags | string[] |
last_update | timestamp |
Connection Attributes
| Attribute | Type |
|---|---|
kind | string |
type | string |
lang | string |
tls_version | string |
uptime | duration |
idle | duration |
bytes_sent | bytes |
bytes_recv | bytes |
msgs_sent | int |
msgs_recv | int |
pending_bytes | bytes |
id | int |
port | int |
tls_cipher | string |
mqtt_client | string |
start_time | timestamp |
last_activity | timestamp |
stop_time | timestamp |
reason | string |
stalls | int |
server | string |
user | string |
Stream Attributes
| Attribute | Type |
|---|---|
created_at | timestamp |
storage_type | string |
retention_policy | string |
max_bytes | bytes |
max_msgs | int |
max_age | duration |
num_replicas | int |
is_mirror | bool |
discard_policy | string |
sealed | bool |
subjects | string[] |
placement_cluster | string |
placement_tags | string[] |
max_msg_size | bytes |
deny_delete | bool |
deny_purge | bool |
allow_direct | bool |
max_consumers | int |
max_msgs_per_subject | int |
deduplication_window | duration |
config_first_seq | int |
discard_new_per | bool |
allow_rollup | bool |
no_ack | bool |
consumer_inactive_threshold | duration |
consumer_max_ack_pending | int |
mirror_name | string |
mirror_api_prefix | string |
num_sources | int |
store_compression | string |
has_subject_transform | bool |
has_republish | bool |
msgs | int |
bytes | bytes |
num_subjects | int |
num_consumers | int |
first_seq | int |
last_seq | int |
first_ts | timestamp |
last_ts | timestamp |
num_deleted | int |
mirror_lag | int |
mirror_active | duration |
mirror_error | string |
KV Store Attributes
| Attribute | Type |
|---|---|
storage_type | string |
num_replicas | int |
keys | int |
bytes | bytes |
history | int |
ttl | duration |
max_bytes | bytes |
sealed | bool |
deny_delete | bool |
deny_purge | bool |
allow_direct | bool |
store_compression | string |
num_consumers | int |
Object Store Attributes
| Attribute | Type |
|---|---|
storage_type | string |
num_replicas | int |
msgs | int |
bytes | bytes |
max_bytes | bytes |
sealed | bool |
store_compression | string |
num_consumers | int |
Service Attributes
| Attribute | Type |
|---|---|
service_name | string |
instances | int |
The services overview aggregates connections into one row per (account, service name), so service also carries the account and check cross-entity attributes. Per-connection attributes such as lang, version, and ip are not exposed here; filter the connection entity for those.
Consumer Attributes
| Attribute | Type |
|---|---|
stream | string |
created_at | timestamp |
deliver_policy | string |
ack_policy | string |
ack_wait | duration |
max_deliver | int |
max_ack_pending | int |
inactive_threshold | duration |
replay_policy | string |
heartbeat | duration |
opt_start_seq | int |
opt_start_time | timestamp |
max_waiting | int |
max_batch | int |
max_expires | duration |
max_bytes | bytes |
deliver_subject | string |
deliver_group | string |
flow_control | bool |
headers_only | bool |
num_replicas | int |
storage_type | string |
filter_subjects | string[] |
sample_frequency | string |
memory_storage | bool |
priority_policy | string |
num_ack_pending | int |
num_redelivered | int |
num_waiting | int |
num_pending | int |
delivered_stream_seq | int |
delivered_consumer_seq | int |
ack_floor_stream_seq | int |
ack_floor_consumer_seq | int |
paused | bool |
pause_remaining | duration |
push_bound | bool |
Leaf Attributes
| Attribute | Type |
|---|---|
is_spoke | bool |
compression | string |
id | int |
port | int |
is_isolated | bool |
subs | string[] |
Route Attributes
| Attribute | Type |
|---|---|
uptime | duration |
idle | duration |
pending_size | bytes |
did_solicit | bool |
is_configured | bool |
compression | string |
start_time | timestamp |
last_activity | timestamp |
stalls | int |
Gateway Attributes
| Attribute | Type |
|---|---|
uptime | duration |
idle | duration |
pending_size | bytes |
is_outbound | bool |
is_configured | bool |
tls_version | string |
tls_cipher | string |
authorized_user | string |
start_time | timestamp |
last_activity | timestamp |
stalls | int |
Labels
Servers, streams, consumers, KV stores, and object stores carry operator-assigned metadata: the labels set on the NATS resource itself. Because their keys are arbitrary, they are addressed by path rather than listed among the entity's attributes:
metadata.<key>:<value>
metadata.env:prod # labeled env=prod
metadata.env: # labeled env, whatever the value
metadata.env:!=prod # labeled env, but not prod
stream@metadata.io.nats.monitor.enabled:true
A key is matched exactly and is case-sensitive, and dots in a key are part of the key rather than a path into nested JSON: metadata.io.nats.monitor.enabled names one label. Labels are text, so they take the string operators; an ordering operator against one is rejected.
Negation needs the label present: metadata.env:!=prod selects entities labeled with something other than prod, not every entity that is not labeled prod. To include unlabeled entities, negate the whole term with NOT metadata.env:prod.
In a table's filter box, an entity that carries no metadata reports metadata.<key> as an unknown attribute. The global search bar resolves a label term only with an entity qualifier, as in stream@metadata.env:prod; unqualified, it answers that no entities match.
Autocomplete reads label keys and values from the data rather than from a fixed list, ranked by how many entities carry them.
Stages
An expression can end in stages, each introduced by a | with whitespace on both sides, that arrange the rows the filter selected rather than select them:
metadata.env:prod | by region | sort health desc
| by <key>groups rows under the value of a label, with an aggregate header per group. Rows without the label group under "no value", last. When severalbystages are written, the last one applies.| sort <attr> [desc]orders rows by a column or a label. Sort stages stack left to right, so| sort tier | sort nameis a two-level ordering. Anything other thandescsorts ascending.
The Systems and Nodes lists apply stages; a notice under their filter says which stage or attribute it ignored. Entity table filter boxes and their previews drop the stages and apply the filter before them. The global search bar does not accept stages.
A | without surrounding whitespace, as in orders.a|b, is part of the value.
Free-Text Search
When a value is provided without an attribute or entity qualifier, it is treated as a free-text search. The value is matched (case-insensitive substring via ILIKE) against these attributes on every entity that has at least one of them:
name, service_name, id, cluster, ip, host, version, lang, description, go_version, label, reason, tls_cipher, mqtt_client, js_domain, mirror_name, server, user
For example, searching nats-1 checks the name, id, cluster, ip, host, version, go_version, and js_domain columns on the server entity; the name, cluster, ip, version, lang, reason, tls_cipher, mqtt_client, server, and user columns on the connection entity; and so on for each entity.
Fuzzy Suggestions
When a search query references an unknown entity or attribute name, the system attempts to suggest corrections instead of returning a generic error.
How It Works
- The parsed AST is validated against the registry of known entities and attributes.
- Unknown names are compared against valid candidates using Levenshtein distance and prefix matching.
- The adaptive threshold is
max(1, len(name)/3). Shorter names allow fewer edits. - At most 3 suggestions are returned, sorted by edit distance (closest first).
Examples
servr@name:foo → "Did you mean: server" (entity misspelling)
nme:foo → "Did you mean: name" (attribute misspelling)
Suggestions appear as clickable items in the search dropdown. Clicking a suggestion replaces the query with the corrected version and re-executes the search.
Behavior
- Suggestions only appear when a query fails to resolve because of an unknown entity or attribute name
- If no close matches are found, the plain error is shown
- Valid queries produce normal results with no suggestions
Dropped Filters
A filter naming an attribute the table's entity does not have cannot be turned into a query, and the table falls back to showing every row. A notice under the table's filter box reads Filter not applied followed by the reason, naming the unknown attribute, alongside correction chips drawn from the attributes that particular entity actually has. The same error and suggestions appear in that box's autocomplete as you type.
The whole filter is dropped, not just the offending term. When a single bare word falls back to free-text matching, the notice says so too, so a substring match is never mistaken for an exact one.
This applies to the per-table filter box, which takes unqualified attribute names scoped to that table's entity. The global search bar has no such notice.
Query Semantics
Once parsed into an AST, the search expression is evaluated as follows:
Entity resolution. The parser figures out which entities can satisfy the expression. If a term references a specific entity (for example,
server@name:foo), only that entity is queried. If a term references an attribute without an entity, every entity with that attribute is a candidate. Free-text terms match every entity that has at least one free-text attribute. ForAND, entity sets are intersected. ForOR, they're unioned.Fan-out. A separate SQL query is generated for each resolved entity. Each query selects the
pkand matched columns from the entity's view.Epoch scoping. The query reads a single epoch: the one the page's time controls resolve to. A live page reads the latest epoch; a pinned or stepped page reads the epoch it is showing, so a search finds what existed then rather than what exists now. Accounts and users are the exception in one direction only: their identity is retained, so one that has stopped reporting is still findable by name, while the values shown beside it are those of the resolved epoch.
Parameterized queries. All user values are passed as parameterized query arguments, not interpolated into SQL.
Result limit. Each entity query returns at most 10 rows, alongside the total number of matches for that entity.
Match Context
Every attribute an expression names is shown beneath each result, in the order it was written, so a row says why it matched:
server@subscriptions:>50000
nats-east-1 subscriptions: 52,341
nats-east-2 subscriptions: 51,090
- Every named attribute appears, not only the ones a given row satisfies. Under
ORthis tells the two halves apart: a row answeringsubscriptions:>50000 OR connections:>1000shows both numbers. - A
NULLshows as a dash. ForNOT js_domain:the absent value is the reason the row matched. - Values are formatted by type: counts with thousands separators, sizes in IEC units, durations, and dates.
- The displayed name is never repeated underneath itself, and a
check:term adds nothing, because its value is the code in the term. - A free-text match leads. When a bare word matched a column other than the name, that column and value come first.
Values come from the same query that selected the rows, so no extra lookup is made per result.
Table Filter Preview
The per-table filter box shows the first five rows its filter would select, and how many there are, before you press Enter to apply it. It reads the entity's own columns rather than the table's, so attributes the table has no column for still show their values, and it carries the tab's scope and the page's epoch.
Nothing is previewed for a filter the table would not apply; the notice under the input already says why.