Search
The search API takes an input string and attempts to parse it into a structured form with semantics defined below, but falls back to free-text fuzzy search.
Term Structure
A search term can have up to four components:
[[<entity>@]<attribute>:[<operator>]]<value>
value— The minimum required component. Without any qualifier, the value is treated as a free-text search against a predefined set of string attributes across all applicable entities. A value can be quoted to allow whitespace; quotes can be escaped with\".attribute— Qualifies the value against an explicit attribute. An attribute can span multiple entities (e.g.namematches server, stream, consumer, etc.). Every attribute has an associated type and the value is parsed accordingly.operator— An explicit comparison operator for the attribute. Each type has a 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 labelled 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. A value that would otherwise read as one is reachable by quoting it.
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 — 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[]. Writing an operator without its value, such as rtt:>, remains an error: the operand is missing, not omitted.
Entities
There are 13 searchable entity types. Each reads the tables below at the single epoch described under Query Semantics.
Each entity's view selects every attribute listed for it below; an attribute is never advertised without a column behind it.
The account and user views are assembled from the columns a query actually
names, rather than being one fixed subquery. A view is a subquery in the FROM
clause, and DuckDB drops the columns a query does not select while keeping the
joins that produce them — so an aggregate nobody asked for is still computed and
then discarded. Against production data the account view's subscription count,
which reaches sub_stats through two identity tables, cost roughly a second on
a search whose only result columns were a primary key and a name; leaving it out
brings that query to well under a tenth of that. Adding a column to one of those
views therefore means adding it to the join's column list too, which the entity
view test enforces by reading the assembled view's real columns.
| 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 |
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 (e.g., 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. The unfiltered listing leaves those to their own pages. A stream check's count on the system page includes them, so the filtered list matches that count.
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
The services overview aggregates connections into one row per (account, service name), so per-connection attributes (lang, version, ip) have no single value to filter on and are not exposed on the service entity. To filter by those, query the connection entity.
| Attribute | Type |
|---|---|
service_name | string |
account | string |
instances | int |
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 being listed among the entity's attributes:
metadata.<key>:<value>
metadata.env:prod # labelled env=prod
metadata.env: # labelled env, whatever the value
metadata.env:!=prod # labelled 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 the same way it is against any other text attribute.
Negation needs the label present: metadata.env:!=prod selects entities labelled with something other than prod, not every entity that is not labelled prod. To reach the unlabelled ones as well, 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 attribute it does not have, the same as any other 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 the 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 original error is shown
- Valid queries produce normal results with no suggestions
Dropped Filters
A per-table filter naming an attribute the table's entity does not have cannot be turned into a query, and the table shows every row. A notice under the filter box reads Filter not applied followed by the reason, with correction chips drawn from the attributes that entity actually has. 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.
Query Semantics
Once parsed into an AST, the search expression is evaluated as follows:
-
Entity resolution — The parser determines which entities can satisfy the expression. If a term references a specific entity (e.g.
server@name:foo), only that entity is queried. If a term references an attribute without an entity, all entities with that attribute are candidates. Free-text terms match all entities that have at least one free-text attribute. ForAND, entity sets are intersected; forOR, they are 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 rather than only that it did:
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 is what tells the two halves apart: a row answeringsubscriptions:>50000 OR connections:>1000shows both numbers, and the reader sees which one put it in the list. - 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, ahead of the attributes named explicitly.
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 Enter applies it. It reads the entity's own view 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.