Skip to content

NATS subjects

Every request pfCentral sends to a firewall goes to a subject of the form:

pfsense.<device_id>.<resource>.<action>
pfsense.4c4c4544-0042-5a10-8048-b4c04f4d3132.firewall.rules.list

The agent answers these as a NATS micro service named pfsense-agent. Each agent may only use subjects that start with its own pfsense.<device_id>., so one firewall can never answer, or listen to, another firewall’s requests. New to subjects? Read NATS for firewall admins first; it has an interactive subject explorer.

The tables below list the subjects by area. In each, * stands for the usual list, get, create, update and delete. The Subjects panel on the firewall’s Services → pfCentral → Status page lists every subject that agent answers.

SubjectResource
firewall.aliases.*Alias list / get / create / update / delete / apply
firewall.rules.*Firewall rule list / get / create / update / delete / apply
firewall.nat.portforward.*Port forward CRUD
firewall.nat.outbound.*Outbound NAT CRUD
firewall.nat.onetoone.*1:1 NAT CRUD
firewall.schedules.*Schedules CRUD + apply + active status
firewall.states.*Live state table: list with GUI filters / get by ID / delete by source-destination address pair
firewall.traffic-shaper.*ALTQ shapers and limiter pipes
firewall.traffic-shaper.child-queues.*ALTQ child queue CRUD under a root shaper queue (HFSC service curves, priority, bandwidth share)
firewall.traffic-shaper.limiter.child-queues.*Dummynet queue CRUD under a limiter pipe (mask, weight, AQM parameters)
firewall.virtual-ips.*Virtual IP CRUD + apply
SubjectResource
interfaces.assignments.*Interface assignment CRUD
interfaces.vlans.*VLAN CRUD
interfaces.bridges.*Bridge CRUD
interfaces.gres.*GRE tunnel CRUD
interfaces.groups.*Interface group CRUD
interfaces.laggs.*LAGG CRUD
SubjectResource
routing.gateways.*Gateway CRUD + default gateway get/update
routing.gateway-groups.*Gateway group CRUD
routing.static-routes.*Static route CRUD
routing.applyApply routing changes
SubjectResource
services.list / services.get / services.controlService discovery and start/stop/restart
services.dns-resolver.*Unbound settings, host/domain overrides, access lists, apply
services.dns-forwarder.*dnsmasq host overrides CRUD + apply
services.dhcp-server.*DHCP server config, static mappings, address pools, custom options
services.ntp.*NTP settings and servers
services.ssh.*SSH daemon settings
services.cron.*Cron job CRUD
SubjectResource
status.gatewaysGateway status
status.interfacesInterface status
status.carpCARP status
status.dhcp-leasesDHCP leases
status.logs.*Firewall, DHCP, auth, and OpenVPN logs
status.openvpn.*OpenVPN servers, clients, connections
system.statusSystem summary
system.logsSystem logs
system.settingsSystem settings
SubjectResource
system.hostname.updateSet hostname
system.config.exportRead the complete saved active configuration, including secrets; not a historical backup
system.dns.updateSet DNS servers
system.timezone.updateSet timezone
system.console.getConsole menu and serial console settings
system.console.updateReplace and apply console settings
system.tunables.*Sysctl tunable CRUD
system.certificate-authorities.*Certificate authority list / get / create (internal, intermediate, import) / delete; delete is refused while the CA is in use; private keys are never returned
system.certificates.*Certificate list / get / create (internal signed by a CA, import) / delete; delete is refused while the certificate is in use; private keys are never returned
system.packages.*Installed package list, available packages, install / delete
system.infoSystem info
system.upgradeDownload a new agent binary and restart the agent (not a pfSense upgrade)

pfsense.<device_id>.system.config.export takes the normal request envelope with an empty payload: {"request_id":"…","payload":{}}. On success, the normal response envelope contains data: {"content":"<base64-encoded active XML bytes>"}. The byte payload preserves the file’s encoding; HTTP clients receive the decoded raw XML, not base64. The agent reads its configured active path (/cf/conf/config.xml by default), not /cf/conf/backup. There is no timestamp or caller-supplied path. An unreadable file returns error: {"code":503,"message":"active configuration unavailable","source":"pfsense"} with no file contents or path details.

The server treats this as an uncached, read-only request. It does not write the reply to the shared response cache or invalidate other cached configuration. The JSON reply must fit one broker message. There is no chunking, encryption of the export file, redaction, apply, history creation or restore. HTTP access requires admin or owner; direct NATS clients remain governed by broker credentials and subject permissions, not HTTP roles.

SubjectResource
vpn.ipsec.phase1.* / vpn.ipsec.phase2.*IPsec tunnel phases CRUD (Phase 1 includes encryption proposals) + apply
vpn.openvpn.servers.*OpenVPN server CRUD
vpn.openvpn.clients.*OpenVPN client CRUD (list omits keys and passwords)
vpn.openvpn.cso.*OpenVPN client-specific overrides
vpn.openvpn.applyResync OpenVPN servers, clients, and client-specific overrides
vpn.wireguard.tunnels.* / vpn.wireguard.peers.*WireGuard tunnels and peers CRUD
vpn.wireguard.settings.*WireGuard global settings
SubjectResource
diagnostics.pingRun ping from the device
diagnostics.tracerouteTrace an IPv4 route with a bounded hop count
diagnostics.dns-lookupQuery a DNS record using drill on the device
diagnostics.commandRun a diagnostic command
diagnostics.reboot / diagnostics.haltReboot or halt the device
diagnostics.arp.*ARP table list / delete
diagnostics.config-history.*List, read and delete pfSense’s own config backups in /cf/conf/backup
diagnostics.tables.*pf tables list / get / delete entry
diagnostics.packet-capture.startStart a bounded packet capture
diagnostics.packet-capture.statusRead the current capture status
diagnostics.packet-capture.stopStop a capture and keep the captured packets
diagnostics.packet-capture.getDownload the completed capture as base64 PCAP bytes

Traceroute and DNS Lookup require admin or owner in both the API and UI. Both bypass the shared state cache. The agent validates the arguments and runs the executable directly, not through a shell. Each run has a 35-second deadline, up to 5 seconds of process termination grace, and at most 64 KiB of combined stdout/stderr. The server waits up to 45 seconds for the answer.

SubjectPayloadData
diagnostics.traceroute{"host":"192.0.2.10","max_hops":10}{"host","output","result_code"}
diagnostics.dns-lookup{"host":"example.com","record_type":"A","server":"192.0.2.53"}{"host","output","result_code"}

host is required. Traceroute accepts an IPv4 address or hostname. max_hops defaults to 10 when omitted or zero and must otherwise be 1–30. It runs traceroute -n -w 1 -q 1 -m <max_hops> <host>: numeric output, one probe per hop and a one-second probe wait. IPv6 traceroute and source-interface selection are not available.

DNS accepts ASCII DNS names (including underscore labels) or IP literals. record_type is case-insensitive and defaults to A; allowed values are A, AAAA, MX, NS, TXT, CNAME, SOA, PTR, SRV, and CAA. server is optional; when present it must be an IPv4 or IPv6 address without a zone. Omit it to use the firewall’s resolver configuration. The command is drill <absolute-host> [@<server>] <record_type>. The agent adds a trailing dot to the query name so drill cannot interpret names such as AXFR as type tokens. For PTR, supply a reverse DNS name; an IP literal is not automatically converted to one.

result_code is the process exit code, or -1 for a timeout, cancellation or failure to start. A zero exit does not establish that the destination was reached or the DNS answer exists: inspect hop output and the DNS response code (NOERROR, NXDOMAIN, SERVFAIL). Invalid agent payloads return error code 400. Closing the browser does not cancel an agent request already in progress.

Packet captures require an admin or owner role in the API and UI. The agent runs tcpdump without a shell or promiscuous mode, with limits of 30 seconds, 200 packets, and 2048 captured bytes per packet. One capture is kept in memory per device; it expires 10 minutes after completion or is replaced by the next capture. All capture requests bypass the shared state cache. The HTTP download route returns a binary .pcap file. Captures may contain passwords and other sensitive data; store downloaded files securely.

SubjectResource
users.*pfSense user CRUD
users.groups.*pfSense group CRUD
users.auth-servers.*Auth server CRUD
SubjectHow oftenContent
pf.presence.<device_id>Every 5 sHeartbeat: device ID, name, site, agent version, IP address, model
pfsense.<device_id>.telemetry.metricsEvery 2 sTelemetry envelope: system vitals, interfaces, gateways, VPN and services

In embedded mode pfCentral also listens to the broker’s own connect and disconnect events, so a firewall that closes its connection shows offline at once. See Online, offline and telemetry.

The pass pfCentral signs for each agent allows:

Subjects
Publishpfsense.<device_id>.>, pf.presence.<device_id>, $SRV.>, _R_.>
Subscribepfsense.<device_id>.>, _INBOX.>, $SRV.>

$SRV.> is the standard micro service discovery ($SRV.PING, $SRV.INFO, $SRV.STATS). _INBOX.> and _R_.> carry replies.

NameKindKeeps
TELEMETRYStream on pfsense.*.telemetry.metrics6 hours, on disk
device-presenceKey-value bucketOne entry per online device; expires after 15 s without a heartbeat
device-stateKey-value bucketRecent lists from each device, for fast page loads; expire after 5 minutes (PFCENTRAL_STATE_CACHE_TTL)

With an external broker and PFCENTRAL_NATS_RESOURCE_PREFIX set, the names get that prefix, for example prod-TELEMETRY.

pfCentral waits 5 seconds for each reply. If no agent answers (the device is offline), the request fails at once with “no responders”.