Skip to main content

Proxima Query Language (PQL)

PQL is the search language for querying your infrastructure in Proxima Console. It lets you find hosts by any combination of system properties, agent labels, user tags, services, containers, and packages.

Quick Start

The global search bar (Ctrl+K) has three modes:

  • Simple mode (default) — type a hostname, IP, or OS to filter the hosts page inline
  • Advanced mode — click the "PQL" toggle to write structured queries with autocomplete
  • Plain-English mode — click the ✨ toggle to type in plain English and let Proxima translate it to PQL (see Plain-English Search below)

Switch to advanced mode and type:

host.os = "Ubuntu"

This returns all hosts running Ubuntu. In advanced mode, the search bar autocompletes fields, operators, and values as you type.

Don't know the PQL syntax yet? Click the toggle in the search bar to switch to plain-English mode, type your question the way you'd say it, and Proxima translates it into PQL for you:

ubuntu hosts that are offline

→ translates to, and runs:

host.os =~ "^ubuntu" AND host.agent_status = "offline"

The translated PQL is shown in the search bar and is fully editable — so plain-English search doubles as a way to learn PQL: read what your phrasing became, tweak it, and re-run. Cross-entity questions work the same way ("ubuntu hosts with a firing P1 alert" → a host.* AND alert.* query), with no extra syntax to remember.

How it works: the translation is proposed by an AI model and then validated by the same PQL engine that runs every query — so the result is always real, compilable PQL (the model can't invent a field that doesn't exist). If a phrasing can't be translated cleanly, you still get a best-effort starting query to refine rather than an error.

Notes for v1:

  • The generated query is a starting point you refine, not a final answer — read it, edit it, re-run. The visible PQL is the source of truth for what actually runs.
  • Cross-entity English works for free — phrasing that spans hosts, alerts, changes, compliance, and clusters translates into a cross-entity query.
  • The query is RBAC-scoped at execution. Translation itself touches no tenant data; the usual multi-tenancy scoping applies only when the resulting PQL is run, exactly as if you'd typed it yourself.

Syntax

A PQL query is one or more expressions joined by logical operators.

Basic Expression

<field> <operator> <value>

Examples:

QueryWhat it finds
host.os = "Ubuntu"Hosts running Ubuntu
host.hostname ~ "web-*"Hostnames starting with "web-"
host.cpu_cores > 4Hosts with more than 4 CPU cores
tag.env EXISTSHosts that have an "env" tag set

Combining Expressions

Use AND, OR, NOT, and parentheses to build complex queries:

host.os = "Ubuntu" AND host.agent_status = "online"
(host.os = "Debian" OR host.os = "Ubuntu") AND tag.tier = "production"
NOT tag.decommissioned EXISTS

Free Text

A bare word or quoted phrase with no field or operator is free text — it matches the active entity's default text fields (hostname, IP, OS, … on the hosts tab) via case-insensitive substring search:

nginx
"web server"

Two rules:

  • Quote multi-word phrases. active hosts (two bare words) is a syntax error — write "active hosts" to search for the phrase, use field filters (host.agent_status = "online"), or use plain-English mode. The error message tells you exactly this.
  • Free text always searches the entity of the tab you're onnginx on the hosts tab searches host text fields; the same query on the alerts tab searches alert text fields.

Each entity searches a curated set of high-signal text columns (index-backed on the high-volume entities):

TabFree text matchesFor exact matches on other columns, use
Hostshostname, OS, IP addresshost.* fields
Alertsalert namealert.severity, alert.status, alert.source
Changeschange summarychange.type, change.source, change.severity
Assetsname, asset type, statusasset.* fields
Runbooksname, slug, descriptionrunbook.* fields
Compliancecontrol code, framework name, statuscompliance.* fields
Clustersname, status, platform, versioncluster.* fields

Machine-enum columns like change.type or alert.severity are deliberately not part of free text — they are low-cardinality values you filter exactly (alert.severity = "P1"), not text you search.

Free text combines with field predicates using AND:

nginx AND host.agent_status = "online"

Fields

Every field uses a prefix.name format. The prefix determines which data source is queried.

host.* — Host Properties

Direct columns on the host record.

FieldTypeDescriptionExample
host.hostnamestringServer hostnamehost.hostname ~ "db-*"
host.ip_addressstringIP addresshost.ip_address = "10.0.1.5"
host.osstringOperating systemhost.os = "Ubuntu 22.04.3 LTS"
host.os_familystringOS familyhost.os_family = "debian"
host.kernel_versionstringKernel versionhost.kernel_version ~ "6.8*"
host.archstringCPU architecturehost.arch IN ("amd64", "arm64")
host.cpu_coresnumberCPU core counthost.cpu_cores >= 8
host.memory_total_bytesnumberTotal RAM in byteshost.memory_total_bytes > 8000000000
host.disk_total_bytesnumberTotal disk in byteshost.disk_total_bytes > 100000000000
host.agent_statusstringAgent statushost.agent_status = "online"
host.agent_versionstringAgent versionhost.agent_version = "0.2.1"
host.uptime_secondsnumberUptime in secondshost.uptime_seconds > 86400
host.cloud_providerstringCloud providerhost.cloud_provider = "aws"
host.cloud_regionstringCloud regionhost.cloud_region ~ "eu-*"
host.virtualization_typestringVirtualization typehost.virtualization_type = "kvm"
host.environmentstringEnvironment the host belongs to (matches the environment's display name, not its URL slug)host.environment = "production"
host.projectstringProject (client) slug the host belongs tohost.project = "ets"

label.* — Agent Labels

Dynamic key-value pairs set in the agent configuration (labels in YAML or PROXIMA_AGENT_LABELS env var). Stored in the host's tags JSONB column.

QueryWhat it finds
label.env = "production"Hosts with agent label env=production
label.role ~ "web*"Hosts with role labels starting with "web"
label.team EXISTSHosts that have a "team" label defined

tag.* — User Tags

User-defined tags added through the UI or API (PUT /hosts/{id}/tags). Stored in the entity_tags table.

QueryWhat it finds
tag.owner = "platform-team"Hosts tagged with owner "platform-team"
tag.tier = "production"Hosts tagged as production tier
tag.pci EXISTSHosts that have a "pci" tag (any value)
NOT tag.decommissioned EXISTSHosts without a "decommissioned" tag

service.* — Services

Systemd services discovered on the host.

FieldTypeDescriptionExample
service.namestringService nameservice.name = "nginx"
service.versionstringService versionservice.version ~ "1.24*"
service.statusstringService statusservice.status = "running"
service.typestringService typeservice.type = "notify"

Predicates on the same host-child prefix (service.*, container.*, package.*) that are combined with AND at the top level must all match the same rowservice.name = "nginx" AND service.status = "running" means one service that is both. When such predicates are split across nesting levels (e.g. service.name = "nginx" AND (service.status = "running" OR …)), each group is evaluated independently — different service rows may satisfy each part.

container.* — Docker Containers

Docker containers running on the host.

FieldTypeDescriptionExample
container.namestringContainer namecontainer.name ~ "nginx*"
container.imagestringContainer imagecontainer.image ~ "postgres:*"
container.statusstringContainer status textcontainer.status ~ "Up*"
container.statestringContainer statecontainer.state = "running"

package.* — Installed Packages

Packages installed on the host (apt, yum, dnf, etc.).

FieldTypeDescriptionExample
package.namestringPackage namepackage.name = "openssl"
package.versionstringPackage versionpackage.version ~ "3.0*"
package.managerstringPackage managerpackage.manager = "apt"

asset.* — CMDB Asset Properties

Properties from the universal CMDB asset layer. These fields query the assets table joined to hosts.

FieldTypeDescriptionExample
asset.typestringAsset typeasset.type = "host"
asset.namestringAsset display nameasset.name ~ "web-*"
asset.statusstringAsset lifecycle statusasset.status = "active"
asset.tags.*dynamicAsset tag valuesasset.tags.env = "prod"

Asset status values: active, inactive, degraded, unreachable, decommissioned, maintenance

Combine with host fields for powerful queries:

host.os = "Ubuntu" AND asset.status = "active" AND asset.tags.tier = "production"

See Assets & CMDB for full documentation on the asset system.

alert.* — Alert Groups

Active and historical alert groups.

FieldTypeDescriptionExample
alert.namestringAlert rule namealert.name ~ "CPU*"
alert.severitystringAlert severityalert.severity = "P1"
alert.statusstringAlert statusalert.status = "firing"
alert.sourcestringAlert source systemalert.source = "victoria-metrics"
alert.hoststringHostname the alert fired onalert.host ~ "db-*"
alert.environmentstringEnvironment namealert.environment = "production"
alert.fired_attimestampWhen the alert firedalert.fired_at > "2026-01-01"

change.* — Change Events

File, package, and configuration change events recorded by the agent.

FieldTypeDescriptionExample
change.pathstringFile or resource pathchange.path ~ "/etc/*"
change.typestringChange typechange.type = "file_modified"
change.severitystringChange severitychange.severity = "critical"
change.hoststringHostname where change occurredchange.host ~ "web-*"
change.sourcestringDetection sourcechange.source = "fim"
change.scanned_attimestampWhen the change was detectedchange.scanned_at > "2026-01-01"

runbook.* — Runbook Definitions

Runbook catalog entries.

FieldTypeDescriptionExample
runbook.namestringRunbook display namerunbook.name ~ "restart*"
runbook.risk_tierstringRisk classificationrunbook.risk_tier = "destructive"
runbook.descriptionstringRunbook descriptionrunbook.description ~ "*postgres*"
runbook.slugstringURL-safe runbook identifierrunbook.slug = "restart-nginx"
runbook.is_activebooleanWhether runbook is activerunbook.is_active = "true"

Risk tier values: safe, low, medium, high, destructive

compliance.* — Compliance Evaluations

Control evaluation results from compliance frameworks.

FieldTypeDescriptionExample
compliance.controlstringControl ID or namecompliance.control ~ "CIS-1.*"
compliance.frameworkstringCompliance frameworkcompliance.framework = "CIS"
compliance.statusstringEvaluation resultcompliance.status = "fail"
compliance.severitystringControl severitycompliance.severity = "high"
compliance.hoststringHostname evaluatedcompliance.host ~ "db-*"
compliance.scorenumberCompliance score (0–100)compliance.score < 80

Status values: pass, fail, warn, skip, error

Entity Types

PQL supports searching across multiple entity types. The entity type is auto-detected from the first field prefix in the query.

First PrefixEntity TypeResult Shape
host, label, tag, service, container, packagehostsHost records
alertalertsAlert groups
changechangesChange events
assetassetsCMDB assets
runbookrunbooksRunbook definitions
compliancecomplianceControl evaluations
clusterclustersKubernetes clusters

A query may also combine prefixes from different entities — the leading entity is returned and the others become correlated filters. See Cross-Entity Queries below.

Result ordering. Results are deterministically ordered per entity: time-ordered entities return newest first (alerts by first-fired time, changes by creation time, assets by last-seen time, runbooks by last update, compliance by evaluation time), while hosts and clusters are ordered by name (hostname / cluster name, ascending). Pagination is stable across pages.

Cross-Entity Queries

A query led by one entity can filter by conditions on a related entity and still return the leading entity's rows. The leading entity is the one named by the first field prefix (or the entity query-parameter override); every predicate on a different entity is compiled into a correlated EXISTS subquery joined on the shared host. The result shape never changes — you always get rows of the leading entity, just narrowed to those that have a matching related record (no fan-out, no duplicated rows).

# Leading entity = hosts → returns HOSTS that have a P1 alert
host.os = "linux" AND alert.severity = "P1"

# Leading entity = alerts → returns ALERTS that fired on a Linux host
alert.severity = "P1" AND host.os = "linux"

Five entities can participate, all correlated on the host they share. Either side may lead; any of the others may appear as a correlated filter:

EntityPrefixCorrelation key
hostshost.*the host itself
alertsalert.*the alert's host
changeschange.*the change's host
compliancecompliance.*the evaluated host
clusterscluster.*the host's cluster (via the k8s node → cluster linkage)

Worked Examples

host.os = "ubuntu" AND change.scanned_at > now-2h

Returns: Ubuntu hosts that recorded a change in the last 2 hours.

cluster.status = "healthy" AND alert.severity = "P1"

Returns: Healthy clusters that have a P1 alert on one of their nodes (clusters lead; the alert is the correlated filter).

host.agent_status = "offline" AND NOT compliance.status = "pass"

Returns: Offline hosts that are not passing compliance — the NOT compiles to a NOT EXISTS.

host.os ~ "Ubuntu*" AND alert.severity = "P1" AND change.severity = "critical"

Returns: Ubuntu hosts that have both a P1 alert and a critical change (each secondary entity becomes its own correlated EXISTS).

To explicitly override the detected leading entity, pass the entity query parameter:

GET /api/v1/search?q=host.os="Ubuntu"&entity=alerts

Negation Semantics

NOT on a related entity means two different things depending on whether it stands alone:

# Hosts with NO resolved alert at all (set-level: the NOT negates existence)
host.os = "ubuntu" AND NOT alert.status = "resolved"

# Hosts with SOME alert that is critical AND not resolved (row-level)
host.os = "ubuntu" AND alert.severity = "critical" AND NOT alert.status = "resolved"

A standalone NOT negates the existence of a matching related record — it compiles to NOT EXISTS, so the first query returns hosts that have no resolved alert whatsoever. When the NOT appears alongside other predicates on the same entity, all of those predicates describe the same related record, so the negation applies row-by-row inside a positive EXISTS — the second query returns hosts that have at least one alert which is both critical and unresolved. The two are not interchangeable: a host whose only critical alert is already resolved matches neither query above — the first because a resolved alert exists, the second because no single alert is critical and unresolved.

Permissions

A cross-entity query is authorized against every entity it references. Results are scoped to the intersection of the clients on which you hold each referenced entity's read permission. If you lack the read permission for any referenced entity, the query is rejected with an error before it runs — it never half-executes against data you cannot read. For example, a query touching host.* and alert.* requires both hosts:read and alerts:read.

v1 Limits

  • AND / NOT only across entities. Cross-entity predicates must be combined with AND (and a whole secondary may be negated with NOT). OR across different entities is not supported and returns an error — e.g. host.os = "linux" OR alert.severity = "P1" is rejected. (OR within a single entity is fine: (alert.severity = "P1" OR alert.severity = "P2") AND host.os = "linux".) The same rule applies to free text: it belongs to the leading entity, so it combines with cross-entity predicates only via ANDnginx AND alert.severity = "P1" works, nginx OR alert.severity = "P1" is rejected.
  • No aggregation. The aggregate endpoint (group-by counts) is single-entity; a cross-entity query there returns a clean error.
  • No time-windowed correlation. The EXISTS join matches on host only; you cannot require the related record to fall within a time window relative to the leading row. You can still filter the related entity by an absolute or relative time of its own, e.g. change.scanned_at > now-2h.
  • Runbooks are excluded. runbook.* has no host linkage, so it cannot appear in a cross-entity query.
  • Read permission required on every referenced entity (see Permissions above).

cluster.* — Kubernetes Cluster Properties

Properties from the Kubernetes cluster associated with a host via the node-host linkage. These fields use a two-table JOIN chain (k8s_nodes -> clusters), so only hosts that are linked as K8s nodes will match.

FieldTypeDescriptionExample
cluster.namestringCluster namecluster.name = "k8s-prod-01"
cluster.versionstringKubernetes versioncluster.version ~ "v1.29*"
cluster.platformstringCluster platform (rke2, eks, gke, aks, k3s, kubeadm)cluster.platform = "rke2"
cluster.statusstringCluster health statuscluster.status = "healthy"

Cluster status values: healthy, degraded, unreachable, unknown

Combine with host fields to find nodes in a specific cluster:

cluster.name = "k8s-prod-01" AND host.os = "Ubuntu"

See Kubernetes Inventory for full documentation on the cluster data model.

Operators

Comparison Operators

OperatorDescriptionTypesExample
=Equalsstring, numberhost.os = "Ubuntu"
!=Not equalsstring, numberhost.agent_status != "offline"
~Glob wildcard matchstringhost.hostname ~ "web-*"
>Greater thannumberhost.cpu_cores > 4
<Less thannumberhost.uptime_seconds < 3600
>=Greater than or equalnumberhost.memory_total_bytes >= 8000000000
<=Less than or equalnumberhost.disk_total_bytes <= 50000000000

Numeric values may be integers or decimals — compliance.score < 79.5 works.

Set Operators

OperatorDescriptionExample
INValue is in sethost.arch IN ("amd64", "arm64")
NOT INValue is not in sethost.os_family NOT IN ("windows", "darwin")
EXISTSField/key existstag.env EXISTS

Logical Operators

OperatorDescriptionExample
ANDBoth conditions must matchhost.os = "Ubuntu" AND host.arch = "amd64"
OREither condition can matchhost.os = "Debian" OR host.os = "Ubuntu"
NOTNegate the next conditionNOT tag.decommissioned EXISTS
(...)Group conditions(host.os = "Debian" OR host.os = "Ubuntu") AND tag.tier = "production"

Glob Patterns

The ~ operator supports glob-style wildcard matching:

PatternMatches
"web-*"Anything starting with "web-"
"*-prod"Anything ending with "-prod"
"web-*-prod"Anything starting with "web-" and ending with "-prod"
"db-?""db-" followed by exactly one character

Example Queries

Find All Online Ubuntu Hosts

host.os ~ "Ubuntu*" AND host.agent_status = "online"

Returns: All hosts running any Ubuntu version with an active agent connection.

Find Production Web Servers

service.name = "nginx" AND tag.tier = "production"

Returns: Hosts running nginx that are tagged as production tier.

Find High-Resource Hosts

host.cpu_cores >= 16 AND host.memory_total_bytes > 32000000000

Returns: Hosts with 16+ CPU cores and more than 32 GB RAM.

Find Hosts Missing Tags

NOT tag.owner EXISTS

Returns: Hosts that don't have an "owner" tag — useful for compliance auditing.

Find Hosts by Cloud Region

host.cloud_provider = "aws" AND host.cloud_region ~ "eu-*"

Returns: AWS hosts in any European region.

Find Hosts Running a Specific Package Version

package.name = "openssl" AND package.version ~ "3.0*"

Returns: Hosts with OpenSSL 3.0.x installed.

Complex Multi-Condition Query

(host.os = "Ubuntu" OR host.os = "Debian") AND host.agent_status = "online" AND service.name = "postgresql" AND tag.tier = "production"

Returns: Online Ubuntu or Debian hosts running PostgreSQL in the production tier.

Find Stale Hosts

host.agent_status = "stale"

Returns: Hosts whose agent hasn't sent a heartbeat within the stale timeout (default 5 minutes).

Find Hosts with Docker Running

container.state = "running"

Returns: Any host that has at least one running Docker container.

Find All Firing P1 Alerts

alert.severity = "P1" AND alert.status = "firing"

Returns: All active P1 alerts currently firing across any host.

Find Recent Critical Config Changes

change.type = "file_modified" AND change.severity = "critical"

Returns: File modification events classified as critical severity.

Find Destructive Runbooks

runbook.risk_tier = "destructive"

Returns: All runbooks classified as destructive — useful for access auditing.

Find Failing CIS Compliance Controls

compliance.framework = "CIS" AND compliance.status = "fail"

Returns: All CIS control evaluations that are currently failing.

Cross-Entity: Alerts on Ubuntu Hosts

alert.severity = "P1" AND host.os ~ "Ubuntu*"

Returns: P1 alerts that fired on Ubuntu hosts (alerts lead; the host predicate is the correlated filter). See Cross-Entity Queries for the full rules and limits.

API Endpoints

PQL queries can be used through the REST API:

EndpointDescription
GET /api/v1/search?q={pql}Search entities (paginated, supports format=csv, entity= override)
GET /api/v1/search/count?q={pql}Count matching entities
GET /api/v1/search/fieldsList all searchable fields and operators
GET /api/v1/search/values?field={name}Autocomplete values for a field
POST /api/v1/search/validateValidate query syntax
GET /api/v1/search/aggregate?q={pql}&group_by={field}Group results by field

curl Examples

Search hosts:

curl -H "Authorization: Bearer $TOKEN" \
"https://api-console.prxm.uz/api/v1/search?q=host.os+%3D+%22Ubuntu%22"

Count results:

curl -H "Authorization: Bearer $TOKEN" \
"https://api-console.prxm.uz/api/v1/search/count?q=host.agent_status+%3D+%22online%22"

Export to CSV:

curl -H "Authorization: Bearer $TOKEN" \
"https://api-console.prxm.uz/api/v1/search?q=tag.tier+%3D+%22production%22&format=csv" \
-o hosts.csv

Get field value suggestions:

curl -H "Authorization: Bearer $TOKEN" \
"https://api-console.prxm.uz/api/v1/search/values?field=host.os&prefix=Ubu"

Response:

{
"data": ["Ubuntu", "Ubuntu 22.04.3 LTS", "Ubuntu 24.04 LTS"]
}

Saved Searches

Save frequently used queries for quick access:

# Save a search
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Production web servers", "query": "service.name = \"nginx\" AND tag.tier = \"production\"", "is_shared": true}' \
"https://api-console.prxm.uz/api/v1/saved-searches"

Saved searches appear in the search bar autocomplete when the input is empty.

Multi-Tenancy

Search results are automatically scoped to the authenticated user's accessible clients. Super admins see results across all clients. No additional filtering is needed — PQL handles authorization transparently.

Tips

  • Simple vs Advanced: Use simple mode for quick hostname/IP lookups on the Hosts page. Switch to PQL (advanced) mode when you need structured queries with field operators.
  • Use autocomplete: In advanced mode, the search bar suggests fields, operators, and values as you type. Press Tab or Enter to accept a suggestion.
  • Glob for flexibility: Use ~ with wildcards instead of exact = when you want partial matches.
  • EXISTS for auditing: tag.X EXISTS / NOT tag.X EXISTS is powerful for finding hosts that are missing required tags.
  • Parentheses for clarity: When mixing AND and OR, use parentheses to make operator precedence explicit.
  • CSV export: Add &format=csv to any search API call to download results as a spreadsheet.
  • URL sharing: In simple mode on the Hosts page, the search term is stored in the URL (?search=web), so you can share filtered views with teammates.