AGE graph schema and access
This document captures the canonical AGE graph schema (serviceradar), ID formats, and the access expectations for ServiceRadar services (core, SRQL, DIRE).
Graph and access defaults
- Graph name:
serviceradar - Extensions:
age(plustimescaledb) - Database defaults:
search_path=ag_catalog,"$user",public;graph_pathmay be absent in the current CNPG build, so Cypher calls must always pass the graph name explicitly. - Role grants (migration
00000000000012_age_graph_bootstrap.up.sql):GRANT USAGE ON SCHEMA ag_catalogandEXECUTEon its functions to theserviceradarrole.GRANT USAGEon graph schemaserviceradar,ALL PRIVILEGESon its tables, and default privileges for future tables to theserviceradarrole.
Node labels and properties
Device- Required:
id(canonical_device_id) - Optional:
ip,hostname
- Required:
Collector(agent or poller)- Required:
id(serviceradar:agent:<id>orserviceradar:poller:<id>) - Optional:
type(agent|poller),ip,hostname
- Required:
Service(internal services and checker service devices)- Required:
id(service_device_id),type(e.g., sync, mapper, otel, checker) - Optional:
ip,hostname,collector_id(host owner for convenience)
- Required:
Interface- Required:
id(<device_id>/<ifname>or<device_id>/ifindex:<n>),device_id - Optional:
name,descr,alias,mac,ip_addresses,ifindex
- Required:
Capability- Required:
type(e.g.,snmp,otel,sysmon,healthcheck,checker)
- Required:
CheckerDefinition(reserved for future checker metadata)- Required:
id; optional:name,version
- Required:
Edge labels and semantics
HOSTS_SERVICE(Collector → Service): internal services running on a collectorRUNS_CHECKER(Collector → Service/CheckerDefinition): collector executes a checkerTARGETS(Service/CheckerDefinition → Device): checker or service target device; optional edge props:checker_serviceHAS_INTERFACE(Device → Interface): discovered interfacesCONNECTS_TO(Interface → Interface): topology links (mapper/LLDP/CDP); optional edge props:source(lldp/cdp/manual)PROVIDES_CAPABILITY(Service or Device → Capability): metrics/health capabilities; optional edge props:statusREPORTED_BY(Device → Collector): provenance for sightings/updates; optional edge props:source(dire/mapper/checker)
Canonical ID mapping
- Devices:
unified_devices.canonical_device_id→Device.id - Collectors:
serviceradar:agent:<id>/serviceradar:poller:<id>→Collector.id - Services (internal + checker):
service_device_id(e.g.,serviceradar:service:ssh@agent-1,serviceradar:checker:sysmon@agent-1) →Service.id - Interfaces:
<device_id>/<ifname>(fallbackifindex:<n>) →Interface.id- Direction: mapper seeds/neighbor discoveries must flow through DIRE to obtain the canonical device ID before interface/link creation.
Ingestion sources
- DIRE / registry: emits Device nodes, Collector nodes, Service nodes,
REPORTED_BY,HOSTS_SERVICE,RUNS_CHECKER,TARGETS, andPROVIDES_CAPABILITYedges. - Mapper: emits Interface nodes (
HAS_INTERFACE) and topology (CONNECTS_TO). - Checkers: emit RUNS_CHECKER + TARGETS edges without promoting collector host IPs to Device nodes.
DIRE → AGE mapping (required fields)
canonical_device_id→Device.id;ip,hostnameapplied as properties.agent_id/poller_id→Collector.id(serviceradar:agent:<id>/serviceradar:poller:<id>) +REPORTED_BYfrom Device → Collector.service_device_id(internal services + checkers) →Service.id;service_typedrivesService.type;collector_idderived from host agent/poller to createHOSTS_SERVICE(andRUNS_CHECKERwhen type = checker).- Checker-sourced updates:
checker_service+ agent/poller IDs produce aServicenode (checker) withTARGETS→ Device; collector host IPs are not promoted to Device nodes. - Mapper/DIRE-resolved interfaces: use DIRE-resolved
device_idto formInterface.id = <device_id>/<ifname or ifindex>and addHAS_INTERFACE; topology events addCONNECTS_TObetween interface IDs derived from DIRE-managed device IDs.
Query guidance
- Always pass the graph name in
cyphercalls:cypher('serviceradar', $$ ... $$). - Keep
search_pathincludingag_catalogto expose thecypherfunction andagtype.
Common queries
- Device neighborhood (collector-owned filter + optional topology) via stored procedure:
SELECT public.age_device_neighborhood('device-alpha', true, false); - Service → collector → target path with capability badges:
SELECT jsonb_pretty(result)
FROM ag_catalog.cypher(
'serviceradar',
$$MATCH (c:Collector {id:'serviceradar:agent:agent-1'})-[:HOSTS_SERVICE]->(svc:Service {id:'serviceradar:service:ssh@agent-1'})-[:TARGETS]->(t:Device)
OPTIONAL MATCH (t)-[:PROVIDES_CAPABILITY]->(cap:Capability)
RETURN jsonb_build_object('collector', c, 'service', svc, 'target', t, 'capabilities', collect(cap))$$
) AS (result ag_catalog.agtype);