Back to Netdata

Spec - Topology Function Schema

.agents/skills/project-create-topology/topology-function-schema.md

2.11.039.7 KB
Original Source

Spec - Topology Function Schema

Status

Active for new topology Function work. Existing deployed topology producers are to be migrated to this contract.

Contract

Topology Functions return normal Function envelopes with type: "topology" and data.schema_version: "netdata.topology.v1".

The production schema is defined by:

  • src/plugins.d/FUNCTION_TOPOLOGY_SCHEMA.json
  • src/plugins.d/FUNCTION_TOPOLOGY_DEVELOPER_GUIDE.md

The schema is generic across topology domains. It applies to network connections, streaming, SNMP, vSphere, and future topology producers.

Planes

Topology payloads separate these planes:

  • actors: observed entities such as nodes, processes, containers, ports, devices, streaming agents, or virtualization objects;
  • graph links: renderable relationship groups between actors;
  • evidence: canonical relationship facts behind graph links;
  • detail tables: actor-owned or relationship-owned drilldown data;
  • actor labels: actor-owned key/value rows for modal labels and filters, not a replacement for canonical identity or grouping columns;
  • presentation: backend-selected UI-token composition for labels, colors, icons, legends, highlighting, link styles, scale keys, and graph port bullets;
  • modal composition: actor/link modal recipes over existing actors, links, evidence, labels, and detail tables;
  • correlation: producer-visible rules, loose-side resolution, replacement, enrichment, visible correlation points, and claims used by an aggregator to correlate independently produced topology maps without exposing aggregator internal states;
  • overlay refs: compact references for refreshable metrics or Function-backed snapshots.

Graph links are projections. Evidence rows are the facts used by Cloud aggregation, matching, and detailed drilldowns.

Mode Requests

Mode requests use __topology_mode when a producer has a real detailed vs aggregated output difference. Valid values are detailed and aggregated. Mode-invariant topologies should not expose a selector only to return identical payloads. Mode-capable producers declare data.view.supported_modes; absent or single-value supported_modes means the topology is mode-invariant for UI control purposes. The Cloud topology aggregator consumes detailed payloads for mode-capable producers before returning an aggregated view, so producers must preserve correlation-grade evidence in detailed mode.

Compact Tables

Large sections use compact columnar tables with:

  • rows
  • columns
  • values

Supported column codecs are:

  • const
  • values
  • dict

Every decoded column must produce exactly rows values. Producers should use shared helpers for table building, dictionary encoding, validation, and deterministic sorting rather than hand-rolling encoders in each plugin.

Column type json is allowed only for actor-owned or custom detail cells that must preserve nested producer-owned values. It is not the default for relationship evidence, because high-cardinality evidence needs typed scalar, reference, array, or dictionary-backed columns for compactness and aggregation semantics.

Identity And Aggregation

Actor types declare:

  • source-local identity;
  • cross-payload merge_identity;
  • optional parent_identity;
  • supported aggregation_scopes.
  • optional search policy over actor table columns and actor label keys.
  • optional presentation with UI-owned tokens, safe label policy, size policy, actor repulsion policy, and graph port-bullet policy.

Link types declare:

  • orientation: directed, undirected, hierarchical, or observed_bidirectional;
  • direction_role: none, flow, dependency, ownership, or observation;
  • optional semantic_role: normal, discovery, ownership, traffic, correlation, or control;
  • aggregation policy for direction, evidence, metrics, tables, and overlays.
  • optional presentation with UI-owned color, line style, width, curve, arrow, tokenized layout strength/distance, and one variable visual channel.

Port types are optional and declare graph port-bullet presentation only. Actor/link modal composition is declared separately under actor/link type presentation and table type presentation. Modal recipes are selectors and projections over existing facts; they are not duplicate row stores.

Helper actors that represent grouping constructs may use distinct actor types instead of overloading a generic segment type. Each such type must declare its own identity, merge identity, presentation, modal sections, legend entry, and aggregation behavior. Consumers must use the declared type metadata and must not infer semantics only from a type id containing words such as segment, subnet, or network.

Cloud aggregation may only canonicalize endpoint order when link type policy explicitly allows unordered aggregation. Direction-significant links must preserve direction.

Evidence And Tables

Relationship evidence is not actor custom data.

Evidence tables carry matchable relationship facts such as socket tuples, streaming hops, LLDP/CDP observations, or vSphere inventory edges. Actor-detail tables carry actor-owned data such as streaming path inventory or local status tables. Table metadata must state role and aggregation policy.

Production payloads must not duplicate the same evidence under every actor only to populate drilldown modals. The UI and aggregator derive drilldown views from shared evidence and typed table references.

Actor labels use a compact actor-owned table, normally tables.actor.actor_labels, with rows shaped as:

text
actor, key, value, source?, kind?, value_index?

key, value, source, and kind are logical strings and may be encoded as string or string_ref. Producers should prefer string_ref when they already maintain a local dictionary; aggregators and UI adapters must normalize both encodings as equivalent label strings.

Host/node actors should expose the complete host label set when available. Non-node actors should expose useful producer-known labels and metadata, such as process command line, user, group, namespace, interface role, or virtualization object properties. Repeated values are repeated rows ordered by value_index, not JSON arrays. Facts needed for identity, correlation, grouping, sorting, filtering, or aggregation must remain typed canonical columns too.

actor_labels inherits the topology Function sensitive-data classification. Consumers must preserve the same access-control assumptions as the source Function because labels may include command lines, users, host labels, system contact/location fields, or other operator-controlled metadata.

Actor modal identification is producer-selected through presentation.modal.labels.identification.fields[]. Each field references an existing actor_labels.key, provides the label to render near the actor title, and may limit the number of displayed values with max_values. The full Labels tab remains complete; identification is a curated header projection, not a second table.

Presentation

Presentation belongs in the production netdata.topology.v1 payload, not in Function info, except for old-schema compatibility during rollout.

Function info responses are metadata responses. They may omit data and advertise only parameters, help text, and timing. The topology JSON schema applies to full topology responses, not metadata-only info responses.

Type definitions carry type-local presentation:

  • types.actor_types.<id>.presentation
  • types.link_types.<id>.presentation
  • types.port_types.<id>.presentation

Graph-level data.presentation carries legend order, actor-click highlight behavior, port tooltip field labels, and scale-key definitions.

Actor and link type presentation may also carry modal composition:

  • presentation.modal.labels describes the actor label table, usually actor_labels;
  • presentation.modal.mini_topology describes a depth-1 modal graph preview built from incident links and opposite actors;
  • presentation.modal.sections[] describes table sections over existing actors, links, evidence, actor detail tables, or relationship tables.

Table types may carry presentation defaults for table label, order, default visibility, and column display metadata. Table type presentation does not replace actor/link modal sections; it gives reusable defaults for existing table rows.

Modal sections require non-empty id, non-empty label, a source, and at least one column. empty_label is section-only empty-state text. Modal columns require non-empty id, non-empty label, and a projection. badge_map, align, and sortable are presentation hints over projected values and must not introduce new facts.

label_lookup uses label_key and optionally an actor_column; omitted actor_column means the selected modal actor. json_path always requires both the JSON column and the scalar path to extract.

Presentation uses closed UI-owned tokens. Producers must not emit raw SVG, raw CSS, coordinates, component names, raw force-layout physics, or viewport state.

Closed token values are part of the schema contract:

  • color slots: primary, secondary, accent, self, neutral, muted, dim, derived, info, structural, warning, success, danger, blue, green, orange, purple, cyan, yellow, teal, gray;
  • opacity tokens: normal, muted, faded;
  • width tokens: thin, normal, thick, emphasis;
  • link layout strength tokens: weakest, weaker, normal, stronger, strongest;
  • link layout distance tokens: closest, closer, normal, farther, farthest;
  • actor layout repulsion tokens: weakest, weaker, normal, stronger, strongest;
  • actor size scale tokens: compact, normal, emphasized;
  • link semantic roles: normal, discovery, ownership, traffic, correlation, control;
  • icons: router, switch, firewall, access_point, server, storage, load_balancer, printer, phone, ups, camera, process, agent, netdata-agent, parent, remote-endpoint, local-endpoint, segment, self, ip, cloud, container, docker, kubernetes, lxc, nspawn, podman, systemd, user, vm, database, service, datacenter, cluster, host, network, datastore, datastore_cluster, resource_pool, device, endpoint, correlation, interface, group, unknown.

The UI owns token rendering and must treat producer labels as plain text. If a new producer emits a schema-valid token that an older UI does not know, the UI must use a safe fallback and record diagnostics.

Actor label_policy is the only approved way to choose display labels from actor rows. Canonical identity is not display text. The UI must reject array values by default so aggregated identities do not become long actor names. Label-policy columns must be safe scalar actor-table columns. Producers must not reference secrets, tokens, customer-identifying fields, or unbounded arrays from label_policy.columns.

Actor search is the only approved way to choose graph search content for v1. search.columns[] references scalar actor-table columns. search.label_keys[] references values in the actor label table, normally actor_labels.key. Set search.enabled: false for helper actors that should not be searchable. The UI must not traverse producer-specific details, match, attributes, or label paths when rendering v1.

Link types may define one variable visual channel using variable.channel, variable.scale_key, and variable.value_column. Producers emit raw domain values, such as socket counts or traffic bytes. Cloud or the UI scales values per scale_key across the visible graph. variable.min and variable.max are visual tokens for the chosen channel: width variables use width tokens, opacity variables use opacity tokens.

Link types may also define tokenized force-layout hints using presentation.layout.strength and presentation.layout.distance. These are relative UI-owned tokens, not numeric physics. Current producer tuning keeps strength at normal and varies only distance where the topology needs semantic separation. Do not reintroduce non-normal strength tokens for graph polish unless a later product decision explicitly re-enables force-strength tuning.

Actor types may define tokenized force-layout hints using presentation.layout.repulsion. Repulsion is separate from link strength: repulsion pushes actors apart, while link strength pulls endpoints together. Producers must not emit raw charge values. Actor size may define a type-level size.scale token for deliberate fixed emphasis, such as current/self actors; the UI must not infer this from actor type names or labels.

link_types.<id>.semantic_role is behavior metadata, not visual styling. It drives UI behavior such as discovery-link filtering, ownership/coherence treatment, traffic emphasis, and correlation treatment without hardcoded protocol or type-name checks. Link appearance still comes from presentation.

When link presentation.arrow is auto or omitted, the UI derives arrows from orientation and direction_role:

  • undirected -> no arrow;
  • observed_bidirectional -> no arrow;
  • direction_role: none -> no arrow;
  • direction_role: observation -> no arrow;
  • directed with flow or dependency -> forward from src_actor to dst_actor;
  • hierarchical with ownership -> forward from src_actor to dst_actor;
  • all other combinations -> no arrow and a diagnostic if the combination is schema-valid but semantically unusual.

observed_bidirectional means observation completeness, not "draw both arrows". Producers that need reverse or both arrows must set presentation.arrow explicitly.

direction_role is required by the v1 schema. Missing direction_role is schema-invalid and must not produce an inferred arrow from orientation alone. The UI should render auto as no arrow and emit the normal missing-field diagnostic for that invalid input.

For schema-valid values, the auto semantic diagnostic boundary is:

  • no diagnostic for directed+flow, directed+dependency, hierarchical+ownership, undirected+none, undirected+observation, observed_bidirectional+none, or observed_bidirectional+observation;
  • diagnostic for directed+none, directed+observation, directed+ownership, hierarchical+none, hierarchical+flow, hierarchical+dependency, hierarchical+observation, undirected+flow, undirected+dependency, undirected+ownership, observed_bidirectional+flow, observed_bidirectional+dependency, or observed_bidirectional+ownership.

Initial UI-owned mappings are:

  • size.scale: compact=0.85, normal=1.0, emphasized=1.18;
  • layout.repulsion: weakest=-200, weaker=-300, normal=-450, stronger=-700, strongest=-1000.

These numeric values are not schema and may be tuned after visual QA. size.scale composes with size.mode; it does not override data-driven sizing. Missing optional fields use neutral defaults (scale: normal, repulsion: normal) and must not trigger v1 UI fallback heuristics.

Actor port bullets require explicit ports.sources[] when show_bullets: true. The source may be links, evidence, or an actor_table. Source column names are table-local and must remain unchanged during Cloud aggregation. Type ids inside row values and default_type values are rewritten only when they refer to type registries. ports.sources[].evidence is an evidence type id. name_column must reference a scalar display column, not a raw actor/link/evidence reference, array, or JSON cell. ports.sources[].value_column is optional and must reference a numeric source column. When present, the UI sums values for matching bullet keys and uses the sum for bullet multiplicity, overflow, and sizing. Aggregated producers should use this instead of sending repeated rows only to drive presentation.

Modal/table composition uses closed source, projection, cell, and visibility tokens. Supported source kinds are actors, links, evidence, actor_table, and relationship_table. Supported projections include direct column values, actor-ref labels, opposite actor labels, formatted endpoints, selected-side endpoints, label-table lookups, coalesced columns, constants, and explicitly declared scalar JSON paths. Supported cell types include text, number, badge, actor link, timestamp, duration, endpoint, array count, and debug JSON. Raw JSON belongs behind debug visibility or an explicit scalar projection; it must not be the default polished actor modal rendering. Selected-side endpoint projections must be self-contained: the projection names source and destination actor-ref columns, plus at least one source-side endpoint column and one destination-side endpoint column.

selection.actor_click.mode: highlight_path requires path_table, path_actor_column, and path_order_column. path_actor_column identifies path members. When one table contains different paths for different clicked actors, path_owner_column identifies the actor that owns each path row. highlight_connections requires no path table.

Presentation conflict policy:

  • producer-local type ids, port ids, scale keys, evidence ids, table type ids, and overlay template ids are namespaced before aggregation;
  • identical definitions are deduplicated after canonicalization;
  • conflicting local definitions are preserved as distinct canonical ids rather than hard-failing aggregation;
  • label_policy belongs to the actor type presentation and follows the same namespace/deduplicate rule;
  • profile_version is diagnostic. It may help choose a preferred display profile later, but it is not a comparable semantic-version contract and must not be used to drop facts or rows.

Correlation Contract

Correlation is producer-visible graph semantics, not aggregator state. Producers must not encode correlation as hidden flags on real actors, and must not expose aggregator internal states such as absorbed, candidate, equivalence class, or rewrite plan. The final aggregated output is always a normal topology payload.

Correlation can resolve several shapes:

  • loose relationship sides, where one side of a detailed row has endpoint facts but no known actor;
  • visible correlation actors, where the input graph intentionally materializes unresolved peers;
  • weaker placeholder actors that should be replaced by stronger managed actors;
  • equivalent actors that should be merged and enriched with facts from multiple payloads.

data.correlation.rules defines how independent payloads of the same topology kind can be correlated. Each rule defines:

  • optional class: resolve_loose_side, replace_actor, or merge_enrich_actor;
  • action: absorb for exact matches that remove visible correlation actors or consume loose-side placeholders and rewrite incident correlation relationships, or link for partial/broader matches that keep the visible correlation/materialized actor and add a weak correlation link;
  • priority: lower numbers run first;
  • key_space: namespace for exact string-key matching;
  • key: a declarative template built from point/claim table columns and literals;
  • point_actor_types: actor types that are visible correlation points when the input graph materializes points;
  • optional claim_actor_types: actor types that may satisfy the point;
  • optional correlation_link_types: link types that connect real actors to correlation actors and may be consumed/replaced by the rule;
  • output_link_type: link type emitted for rewritten absorb links or visible partial correlation links.

data.correlation.points is a compact table of visible correlation actors and keys. data.correlation.claims is a compact table of real actors and keys they can satisfy. Both tables require actor, rule, and the key columns referenced by their rules.

The aggregator is intentionally agnostic. It builds normalized keys from declared columns and literals, applies rule priority, and handles ambiguity conservatively. It must not need new code to understand every future IP, port, MAC, chassis id, object id, label, or topology-domain key.

No match keeps the visible correlation actor or loose-side materialization visible. Ambiguous matches remain unresolved and produce diagnostics. NAT or other alias evidence can be modeled by adding extra point/claim rows for the same actor and rule; aliases add facts without mutating the original observation.

Correlation links must be semantic link types even for single-node payloads. The legend must include visible correlation actors and links when they are visible, so users can distinguish unresolved, partial, inferred, and resolved graph relationships.

Telemetry Overlays

Refreshable traffic, state, error, packet, or utilization data is represented by overlay templates and per-actor/per-link refs.

Templates define the query mechanism once. Refs provide only template ids, one owner reference, and selector parameters. The refs table uses schema ids for column names, so template ids, selector params, and refs column ids must match the topology $defs/id contract: start with a letter and then use only letters, digits, _, ., :, or -.

The refs-table convention is:

  • template: string or string_ref template name resolving to data.types.overlay_templates;
  • exactly one convention owner column: actor with type actor_ref or link with type link_ref; every row must have a non-null owner value;
  • one column for each selector param required by the referenced template.

Selector params must not use reserved refs-table convention column names: template, actor, or link.

No other actor_ref or link_ref columns are valid in overlay refs. Consumers can identify ownership from the fixed actor / link column ids instead of scanning all columns by type.

The template column and all selector-param columns required by a row's resolved template must be string or string_ref. Required selector-param row values must resolve to non-empty strings. Selector-param columns used by other templates may be nullable and null on rows whose template does not require them. Future selectors that need ip_ref, mac_ref, or empty-string matching must relax this contract explicitly.

For provider: "netdata.metrics", selector params are interpreted as:

  • node_id: node-scope selector, not a chart label;
  • collect_job: chart label _collect_job;
  • other params: same-named chart labels.

Aggregated actors or links merge overlay refs according to the template merge policy. merge.refs controls ref-list handling, currently append or set. merge.values controls how multiple matching metric values collapse, currently sum, min, max, avg, last, or none.

Compatibility

Production payloads carry canonical topology facts, not compatibility reconstruction instructions. Projection code for parity with deployed compatibility consumers is test or rollout code only.

Agent/backend contracts and docs should point new work to netdata.topology.v1. Temporary compatibility handling belongs in isolated Cloud frontend adapters during rollout.

Validation

Topology producer changes must include:

  • JSON Schema validation against FUNCTION_TOPOLOGY_SCHEMA.json;
  • semantic validation for table column lengths and reference bounds;
  • fixture or corpus tests for payload size and evidence preservation;
  • tests proving direction and aggregation policy are honored;
  • checks that evidence is not silently truncated.

Cloud topology aggregation service readiness also requires service-level fixtures for every topology kind covered by this contract. network-connections is the high-cardinality benchmark, but the service is not considered ready if the UI can use it for only some topology kinds while bypassing it for others.

Migration Notes

topology:network-connections now emits netdata.topology.v1 directly from the C network-viewer Function. Aggregated mode is the default and emits compact actor, graph-link, and actor-owned socket_ports tables. Detailed mode adds a shared socket relationship-evidence table for exact tuple matching and drilldowns. Process actor size uses the actor row socket_count metric, while process port bullets read the socket_ports.socket_count value column so an aggregated port row can represent several sockets. The producer no longer emits the superseded old-schema presentation object or duplicated actor-nested socket modal tables. It now emits compact graph presentation metadata in type definitions plus data.presentation, and repeated string columns use dictionary encoding when it reduces raw payload size.

topology:network-connections enforces a producer-side fixed response budget of 64 MiB. When a requested live topology would exceed that budget, the Function returns a normal Function error with HTTP status 413 instead of emitting a partial topology. This keeps the producer below the plugins.d deferred-response hard stop and preserves the contract that evidence is never silently truncated.

Network-connections distinguishes unresolved endpoint links from aggregator correlation output. endpoint_socket connects a process to a visible unresolved endpoint actor and must not use the farthest layout distance because that makes single-node maps zoom out unnecessarily. correlated_socket is the output link type after exact endpoint absorption by an aggregator and may use farthest distance to keep independent topology clusters from blending.

Network-connections modal composition is producer-declared. Self/node actors use a Processes section over ownership graph links filtered by link type. Network-connections socket link types use direction_role: "dependency" and are client-to-server: src_actor is the client/dependant and dst_actor is the server/dependency target. Non-node actors therefore use two primary sections in both aggregated and detailed mode: Dependencies, filtered to rows where the selected actor is src_actor, and Dependants, filtered to rows where the selected actor is dst_actor. Aggregated mode reads these sections from tables.relationship.connections; detailed mode reads them from evidence.socket. socket_ports is an actor inventory for process port bullets only; it is not a standalone modal tab for network-connections.

Network-connections supports three actor grouping levels: process_name, pid, and container. group_by:pid emits one process actor per PID and may emit raw variable fields as scalar actor columns: PID, UID, network namespace, command line, cgroup status/path/name, and detailed container metadata. group_by:process_name emits grouped process actors; fields that can vary across grouped PIDs must be represented as merged/set-valued actor labels or columns with declared set aggregation, not as arbitrary scalar replacements. group_by:container emits actors grouped by canonical container_name; runtime-specific actor types such as Docker containers, Kubernetes containers, VMs, systemd services, user slices, and process fallbacks are producer-declared actor types that share the container aggregation scope. Systemd services use the service unit name. user.slice/user-UID.slice paths use a composed user actor only when cgroups did not return a usable container name: the grouped actor name is the process UID's resolved username, or user${UID} when username resolution is unavailable, while leaf scopes stay in cgroup/detail evidence. Rootless Docker and any other known cgroup with a container label or cgroup name keep the cgroups-provided container identity instead of being absorbed into the user actor. Known host/root processes fall back to process name, and unresolved retry-later lookups remain explicit pending/unknown container identity instead of being fabricated as final process-name containers. The three aggregation scopes use evidence_policy: "preserve".

Network-connections topology keeps cgroup metric-monitoring rules separate from topology display classification rules. apps.plugin exposes per-PID facts; network-connections may compose user-activity actors from those facts for the dependency map. Topology producers may derive systemd_unit_name, systemd_unit_kind, actor_kind, specific actor types, and display icons from cgroup path/orchestrator rule modules without widening the netipc orchestrator enum for each display subtype.

Network-connections actor modals expose producer-declared actor-owned processes and cgroups tables in addition to labels and relationship sections. processes contains contributing PID/process/user/namespace/command rows. cgroups contains contributing PID cgroup status/path/name, container/service name, effective display orchestrator, actor kind, Kubernetes, Docker/Podman, and systemd unit fields. These are topology schema tables, not Cloud/UI hardcoded container views. The Function info response advertises v: 3, and data requests consume the selected grouping from the v3 POST payload shape {"selections":{"group_by":["..."]}}; legacy function-string aliases such as group_by:pid and processes:by_pid remain accepted for compatibility.

The orchestrator column renders netipc orchestrators as systemd, docker, k8s, kvm, lxc, podman, or nspawn. It additionally renders APPS_LOOKUP host-root cgroup status as host_root; this is a producer-local topology value, not a netipc enum member.

Free-form cgroup labels are denied by default in Function output. Operators opt in with labels:<pattern> using pipe-separated simple_pattern tokens. The whitelist is applied at network-viewer Function emission only; upstream cgroup caches and IPC payloads carry raw labels. Raw cgroup paths are scalar actor columns only in group_by:pid; grouped views preserve them as deduplicated actor-label values when available.

topology:snmp now emits netdata.topology.v1 from the Function handler through an adapter over the existing SNMP topology engine output. The adapter preserves actors, links, L2 observation evidence, bounded L3 adjacency evidence, actor metadata, and actor custom detail tables. Remaining SNMP refinement is to promote interface metric lookup fragments into first-class overlay templates/refs.

SNMP l3_subnet links are logical L3 adjacency, not physical or L2 adjacency. They use orientation: observed_bidirectional, direction_role: observation, and semantic_role: normal. The producer emits them only between resolved managed SNMP device actors; unresolved or ambiguous L3 endpoints remain diagnostic/suppression state and must not create IP-only graph actors.

SNMP l3_subnet evidence must preserve the relationship facts needed for drilldown and aggregation, including a link_ref, source/destination actors, source/destination interface IPs, subnet, network, netmask, prefix, source, and logical inference/attachment metadata. The evidence is an actor-modal source for L3 adjacency drilldowns.

SNMP ospf_adjacency links are logical OSPF control-plane adjacency, not physical, L2, discovery, or port-neighbor evidence. They use orientation: observed_bidirectional, direction_role: observation, and semantic_role: control. Their graph presentation should use a dashed line style and purple color slot so operators do not read them as physical cabling or generic success/health state. The producer emits graph links only for full OSPF neighbors between resolved managed SNMP device actors. Non-full or unresolved OSPF neighbor rows remain diagnostic actor-owned detail rows and must not create loose router/IP graph actors. Current SNMP OSPF topology scope is OSPFv2 non-virtual neighbors from ospfNbrTable; OSPFv3 and OSPF virtual-neighbor tables are future scope unless a later topology feature explicitly adds their distinct semantics.

When ospf_adjacency and l3_subnet describe the same resolved actor pair and endpoint/subnet relationship, OSPF is the stronger protocol-specific signal for the graph. The producer may suppress the matching l3_subnet graph link while preserving typed OSPF evidence and actor-owned neighbor detail rows.

SNMP modal composition must be port-centric for managed device actors. A managed device modal uses actor-label identification for important device facts, a primary Ports section over actor_ports, and a Port Neighbors section over actor_port_links. It may also expose an L3 Adjacencies section sourced from l3_subnet relationship evidence. Generic graph-link Links sections are reserved for endpoint, segment, or custom actors that do not own port inventory.

SNMP actor_ports exposes real port identity and status as typed columns: SNMP if_index as the visible numeric port ID when known, source port_id, display name, if_name, if_descr, if_alias, MAC, speed, status, mode, role, VLAN, FDB, link, and neighbor counts. It must never fabricate numeric port IDs; row order and generated sequences must not be used. if_index must come from the device/SNMP facts and must align with actor_port_links.if_index.

SNMP actor_ports may also carry compact expanded-row neighbor columns such as nullable neighbor_actor and neighbor_port_name, derived from graph-link endpoint facts. These columns make the port row clickable without duplicating raw LLDP/CDP/FDB/ARP/STP evidence.

SNMP actor_port_links is a compact actor-owned modal index over existing graph links and evidence. It has one row per incident actor side and carries the local if_index/port name, remote actor, remote port facts, protocol, link type, state, evidence count, confidence, inference, attachment mode, and timestamps. It exists so device modals can align neighbor rows with the same port identity shown in actor_ports; it is not a second copy of raw evidence.

SNMP actor_port_links must remain port-neighbor oriented. It must not include l3_subnet or ospf_adjacency links, because shared-subnet and routing-protocol L3 adjacency do not prove a physical or L2 port neighbor.

SNMP protocol-neighbor detail sections, such as OSPF neighbors, should be actor-owned detail tables. They may include unresolved or non-full neighbor observations for diagnostics even when no graph link is emitted.

SNMP polished UI must not depend on raw actor_metadata and endpoint JSON. Important scalar/count summary values live in typed actor or actor-detail columns and are also available through actor_labels. Nested neighbors, VLANs, unknown custom port attributes, and endpoint objects stay in expanded or debug sections unless a structured child table is defined. Link endpoint port labels must come only from real port fields such as port_name, if_name, if_descr, or source port_id; actor labels such as display_name or sys_name must not be used as port-name fallbacks.

topology:streaming now emits netdata.topology.v1 directly from the C Function. It models streaming agents as compact actor rows, streaming/virtual/ stale relationships as directed graph links with matching evidence types, and keeps stream_path, retention, inbound, and outbound modal data as typed actor-detail or relationship-summary tables. Streaming also emits graph presentation metadata for highlight-path behavior, legend, link styles, and port bullets. Streaming hops remain signed so stale path values are not corrupted.

Streaming parent actor size is data-driven by the actor row retained_node_count metric, not by graph degree or direct child count. This count represents nodes for which the parent has retained DB data, including self, virtual nodes, stale nodes, and transit descendants when they have retention state. The parent actor type must declare presentation.size: {"mode":"metric","metric_column":"retained_node_count"}. Parent port bullets represent child or vnode streams attached to the parent side of streaming graph links, so the parent ports.sources[] entry over links must use actor_column: "dst_actor" with a scalar child/node display column such as port_name.

Streaming modal composition emits actor_labels, complete host labels where available, host/system metadata labels needed by old summaries, and typed OS/architecture/CPU columns. The actor modal header must select important identity/status labels from actor_labels, including hostname, node type, stream status, ingest status, health status, retained-node count where applicable, direct-child count where applicable, OS/platform labels, and Agent version. Existing stream_path, retention, and inbound tables have the right actor-ref shape for recipe-based table rendering. The outbound table must use the parent-owned shape described below; a table that only records the selected actor's own upstream destination is insufficient for parent operator workflows.

Streaming actor modal identification is role-specific. Host-like actors (parent, child, and stale) should expose operational status plus OS/hardware/platform labels such as OS, OS version, kernel, architecture, CPU model, cores, RAM, virtualization, container, cloud provider/type/region, and Agent version. Parents additionally expose retained_node_count and child_count so the visual size and direct attachments are both explainable. Vnode actors should expose inventory/device labels such as vnode type, vendor, model, address, location, sys object id, LLDP system name, and status. Long stable identifiers such as machine_guid and node_id remain in the full Labels tab by default unless a future product decision explicitly promotes them.

Streaming actor modals must keep those tables as the single source of truth and must not duplicate rows only for modal display. The default visible sections are:

  • Stream path: rows from stream_path filtered by actor, ordered by path_index. The table shows the selected actor's own path only; virtual nodes and children have their own actors and therefore their own path rows. since and first_time must be populated from the best canonical source available for every path row. Synthetic path rows added only for rendering or highlighting must still carry timestamps when the producer can derive them from the adjacent path edge, the selected actor's ingest status, or database first-time status. They may be null only when the Agent genuinely does not know the value.
  • Retained nodes: rows from the same retention table filtered by observer_actor; this view answers which nodes' data the selected actor maintains. The table must include self, virtual nodes, direct children, transit descendants, and stale/archived hosts when those hosts are present in the Agent root index and have retention state. It must show retained node, node type, retention status, from/to timestamps, duration, metrics, instances, and contexts. db_from and db_to may be null only when the database status genuinely has no time range.
  • Received nodes: rows from inbound filtered by parent_actor; this view represents children, virtual nodes, stale nodes, and descendants received or transiting through the selected parent. source_actor is the immediate actor from which the selected parent receives the row. For direct local receipt, source_actor should be the child/vnode actor itself; it should be null only when the immediate source is genuinely unknown.
  • Outbound streams: rows from outbound filtered by the sending parent actor, not by the streamed node actor. This view answers which node payloads the selected parent currently streams, and where it streams each one. Each row must include the streamed node actor, destination actor when known, status, age, hops, TLS, compression, and useful stream/replication/count metrics when available. In clustered-parent setups, the selected parent must list self, virtual nodes, direct children, and transit descendants that are sent to each upstream destination.

The old Retention for node default section is not part of the current streaming modal contract. The canonical retention table still keeps both actor and observer_actor so Cloud aggregation can preserve multiple retaining parents for the same node. If a future modal needs a selected-node "who retains me" view, it must be explicitly named Retained by and must not replace the parent-owned Retained nodes view.