API Reference
API Reference
uControl Insight provides a REST API for programmatic access to asset data. The API is available at /api/v1/ relative to the application context path.
Authentication
The API supports three authentication methods:
| Method | Header | Example |
|---|---|---|
| Integration Key (master) | Authorization: Bearer ucm_... or X-Integration-Api-Key: ucm_... | Service-to-service integration, full access |
| User API Token | Authorization: Bearer uci_... | Per-user tokens, inherits user roles |
| Session Cookie | Standard browser session | Used by the web UI |
Generating Tokens
- Integration Keys — sidebar → Integration Keys (ADMINISTRATOR only)
- User API Tokens — Profile page (requires API_USER role)
Response Format
All API responses use a standard envelope:
{
"data": [ ... ],
"meta": {
"totalCount": 42,
"page": 0,
"size": 50
}
}
Endpoints
Assets
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/assets | Search and list assets |
| GET | /api/v1/assets/{id} | Get asset detail |
| GET | /api/v1/assets/{id}/relationships | Get asset relationships |
Asset Search Parameters
| Parameter | Type | Description |
|---|---|---|
q | string | Search by hostname, FQDN, or display name |
type | string | Filter by asset type. Supports comma-separated OR: type=VM,PHYSICAL_HOST |
category | string | Filter by coarse asset category (Type). Supports comma-separated OR: category=HOST,STORAGE |
status | string | Filter by status. Supports comma-separated OR: status=ACTIVE,STALE |
site | string | Filter by site. Supports comma-separated OR |
environment | string | Filter by environment. Supports comma-separated OR |
page | int | Page number (0-based, default: 0) |
size | int | Page size (default: 50) |
Asset Response Fields
Each asset carries both a coarse category (the "Type" shown in the UI) and a fine-grained sub type (the historical assetType value), plus a virtual flag for host-like assets:
| Field | Type | Description |
|---|---|---|
assetType | string | Fine-grained type (e.g. VM, PHYSICAL_HOST, DATASTORE). Unchanged — retained for backwards compatibility. |
assetCategory | string | Coarse category (e.g. HOST, STORAGE, CLOUD). See Asset Categories below. |
assetCategoryLabel | string | Human label for the category, e.g. HOST → "Host". |
assetSubtype | string | Sub type — currently mirrors assetType. |
assetSubtypeLabel | string | Human label for the sub type, e.g. PHYSICAL_HOST → "Physical Host". |
virtual | boolean / null | true for VMs and containers, false for physical hosts / hypervisors / servers, null for non-host categories. |
The same fields appear in the CSV / JSON exports (/api/v1/export/assets.csv, /api/v1/export/assets.json): columns assetCategory, assetSubtype, virtual were added alongside assetType.
Example Requests
# List all VMs and physical hosts curl -H "Authorization: Bearer ucm_yourkey" \ "https://server/uControlInsight/api/v1/assets?type=VM,PHYSICAL_HOST" # List every Host-category asset (physical, VM, hypervisor, container, server) curl -H "Authorization: Bearer ucm_yourkey" \ "https://server/uControlInsight/api/v1/assets?category=HOST" # Search by name curl -H "Authorization: Bearer ucm_yourkey" \ "https://server/uControlInsight/api/v1/assets?q=webserver&type=VM" # Get asset detail curl -H "Authorization: Bearer ucm_yourkey" \ "https://server/uControlInsight/api/v1/assets/abc123-def456"
Topology
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/topology/{assetId}/neighbors | Get topology graph for an asset |
Query parameter: depth (1-5, default 2) — number of relationship hops to traverse.
Discovery Runs
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/discovery/runs | List recent discovery runs |
| POST | /api/v1/discovery/targets/{id}/scan | Trigger a discovery scan |
| GET | /api/v1/runs/{id}/diagnosis | AI diagnosis of a run (status: OK | ERROR | NONE | DISABLED | NOT_APPLICABLE) |
| POST | /api/v1/runs/{id}/diagnosis | Generate / refresh the AI diagnosis for a FAILED / COMPLETED_WITH_ERRORS run |
AI diagnosis (requires ucontrol.ai.enabled=true on the server) returns a short explanation of why a scan had errors / failed, with likely causes and recommended actions:
{
"data": {
"runId": "…",
"status": "OK",
"summary": "SSH authentication fails on 10.0.2.0/24 — no configured credential matches that subnet.",
"likelyCauses": ["The only SSH credential has match pattern 10.0.1.* which does not cover 10.0.2.*"],
"recommendedActions": ["Add an SSH credential matching 10.0.2.*", "Re-run the scan and check the per-IP scan summary"],
"confidence": "HIGH",
"model": "claude-haiku-4-5",
"createdAt": "2026-05-12T19:40:00"
}
}
When AI is disabled, status is DISABLED; for a run that did not fail it is NOT_APPLICABLE; if no diagnosis has been generated yet it is NONE (POST to generate). The diagnosis is also generated automatically when a scan finishes with errors, and is shown on the Discovery Runs page and included in the Microsoft Teams alert.
Credentials
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/credentials | List credentials (metadata only, no secrets) |
| POST | /api/v1/credentials | Create a credential |
| PUT | /api/v1/credentials/{id} | Update a credential |
| DELETE | /api/v1/credentials/{id} | Delete a credential |
Asset Categories & Types
Valid values for the category filter (coarse "Type") and the fine-grained types (assetType / sub type) they group:
Category (category) | Label | Fine types (assetType) |
|---|---|---|
HOST | Host | PHYSICAL_HOST, HYPERVISOR, VM, CONTAINER, SERVER |
NETWORK_DEVICE | Network Device | NETWORK_DEVICE |
PRINTER | Printer | PRINTER |
IOT_DEVICE | IoT Device | IOT_DEVICE |
VIRTUALIZATION_MANAGER | Virtualization Manager | VIRTUALIZATION_MANAGER |
CLUSTER | Cluster | CLUSTER |
STORAGE | Storage | DATASTORE, STORAGE_POOL |
NETWORK | Network | NETWORK, VSWITCH, PORT_GROUP, INTERFACE |
SOFTWARE | Software | SOFTWARE, SERVICE, SOFTWARE_PACKAGE |
CLOUD | Cloud | CLOUD_ACCOUNT, CLOUD_REGION, CLOUD_DATABASE, CLOUD_STORAGE, CLOUD_LOAD_BALANCER, CLOUD_STACK |
FLOW_EXPORTER | Flow Exporter | FLOW_EXPORTER |
ENDPOINT | Endpoint | UNKNOWN_ENDPOINT |
The type filter still accepts any of the fine-grained values above.
Swagger UI
Interactive API documentation is available at /swagger-ui.html relative to the application context path.
Asset Lifecycle Actions (May 2026)
Administrator endpoints for the asset-lifecycle workflow. Both require
DISCOVERY_ADMIN or ADMINISTRATOR.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/assets/{id}/reactivate |
Flip a STALE / INACTIVE / DECOMMISSIONED asset back to ACTIVE. Resets the
consecutive-failure counter and clears stale_since. |
| POST | /api/v1/assets/{id}/decommission |
Mark the asset DECOMMISSIONED (end of life). The daily aging sweeper never touches DECOMMISSIONED assets. |
See the Asset Aging & Lifecycle page for the full lifecycle model.
Asset Response Fields (May 2026 additions)
| Field | Type | Description |
|---|---|---|
consecutiveScanFailures | integer | Number of consecutive failed discovery runs targeting this asset (0 after any successful scan). |
lastScanOutcome | string | SUCCESS / FAILED / PARTIAL — result of the most recent run
that targeted this asset. |
lastScanAt | timestamp | When that run completed. |
Pagination & navigation links (May 2026)
List endpoints with page and size query parameters now return
absolute navigation URLs in a links object so client integrations
don't have to construct the next URL by hand. The links preserve every other query parameter
the caller sent (filters, sort, custom size) so following next just
works.
The change is additive — existing clients that ignore links continue to work.
Shape
{
"data": [ ... ],
"total": 446,
"page": 0,
"pageSize": 50,
"links": {
"first": "https://insight.example/api/v1/assets?status=ACTIVE&size=50&page=0",
"next": "https://insight.example/api/v1/assets?status=ACTIVE&size=50&page=1",
"last": "https://insight.example/api/v1/assets?status=ACTIVE&size=50&page=8"
}
}
Each link is independently optional:
- page 0 → no
prev - last page → no
next(consumer signal to stop following) - single page → only
first(withfirst==last)
Endpoints that emit full nav (first / prev / next / last)
| Method | Endpoint |
|---|---|
| GET | /api/v1/assets |
| GET | /api/v1/discovery/runs |
Endpoints that emit only first (top-N, no offset)
These return up to a limit capped result set and have no “next page”
concept — they always show only first in the links block:
/api/v1/flow/top-talkers/api/v1/flow/conversations/api/v1/insights/…(AhaAi insight feeds)
Suggested integration pattern
response = http.get("/api/v1/assets?status=ACTIVE&size=200")
while True:
process(response.data)
next_url = response.links.get("next")
if not next_url:
break
response = http.get(next_url)
Client doesn't need to track page manually or recompute URLs — just follow
next until it's absent.
Bulk relationships (May 2026)
Avoids the per-asset N+1 pattern when an importer needs the full edge list.
Each row carries both endpoints' display names + types so the importer doesn't need a
second lookup against /api/v1/assets just to humanise the IDs.
| Method | Endpoint | Notes |
|---|---|---|
| GET | /api/v1/relationships |
Paginated edge list with optional filters. Query params:
{id, relationshipType, active, sourceAssetId,
sourceDisplayName, sourceAssetType, sourceAssetCategory, targetAssetId, …}.
Follows the same links.next pagination contract as /api/v1/assets. |
Software → Host export — one call:
GET /api/v1/relationships?type=RUNS_ON&size=500
Returns every SOFTWARE→HOST edge in a single page (for any reasonable estate).
Computed root parent on every asset (May 2026)
Every /api/v1/assets response (list and detail) now includes a derived
parent block when the asset is a child of another asset — so a 3rd-party
importer can read the host id off a software row directly, without joining via
/api/v1/relationships at all.
Shape:
{
"id": "abc-123",
"assetType": "SOFTWARE_PACKAGE",
"displayName": "nginx",
"assetCategory": "SOFTWARE",
…
"parent": {
"id": "host-456",
"displayName": "web01",
"assetType": "SERVER",
"assetCategory": "HOST",
"via": "RUNS_ON"
}
}
Root assets (PHYSICAL_HOST, NETWORK_DEVICE, PRINTER, CLOUD_ACCOUNT, VIRTUALIZATION_MANAGER,
CLUSTER, FLOW_EXPORTER, UNKNOWN_ENDPOINT) have no parent field — the JSON omits
it entirely (@JsonInclude(NON_NULL)).
Resolution rules (focal asset type → parent):
| Focal asset type | Edge type | Direction | Parent category |
|---|---|---|---|
| VM | HOSTS_VM | incoming | HYPERVISOR |
| CONTAINER | HOSTS_CONTAINER | incoming | HOST |
| HYPERVISOR | MANAGED_BY | outgoing | VIRTUALIZATION_MANAGER |
| SOFTWARE_PACKAGE / SOFTWARE / SERVICE | RUNS_ON | outgoing | HOST |
| DATASTORE / STORAGE_POOL | USES_DATASTORE (or CONTAINS) | incoming | HOST / HYPERVISOR |
| VSWITCH / PORT_GROUP / NETWORK / INTERFACE | CONTAINS (or HAS_INTERFACE) | incoming | HOST / HYPERVISOR |
| CLOUD_REGION / CLOUD_DATABASE / CLOUD_STORAGE / CLOUD_LOAD_BALANCER / CLOUD_STACK | CONTAINS | incoming | CLOUD_ACCOUNT / CLOUD_REGION |
Performance: bulk-resolved per page. A page of N assets always costs exactly 2 extra SQL queries (one for the edges, one for the parent asset rows) regardless of N — no N+1.
Schema introspection — available attributes per asset type (May 2026)
Importers can fetch the list of fields and observed customAttributes keys for
every asset type without trial-and-error against the data. Two endpoints:
GET /api/v1/schema/asset-types— schema for every asset typeGET /api/v1/schema/asset-types/{type}— schema for one asset type (e.g./api/v1/schema/asset-types/SOFTWARE_PACKAGE)
Each entry contains:
| Field | Description |
|---|---|
type / category / label |
Identifiers and human-readable labels for the asset type and its category. |
isRoot |
True for types that are never children (PHYSICAL_HOST, NETWORK_DEVICE, etc.) and so have no parent block on individual assets. |
virtual / cloud |
Tri-state flags driven by the type taxonomy. |
parent |
Resolution rule used to compute the per-asset parent block: edge type, direction (OUT/IN), and the typical parent category. |
typeFields |
Ordered list of { name, type, description } entries: the common fields followed by the fields typically populated for this asset type. |
customAttributeKeys |
Distinct top-level keys observed in custom_attributes for this asset type in the current database (data-driven, not a static list). |
childTypes |
Types commonly discovered downstream — e.g. HYPERVISOR → VM, CONTAINER, DATASTORE, VSWITCH, PORT_GROUP. |
Example fragment:
{
"type": "SOFTWARE_PACKAGE",
"category": "SOFTWARE",
"label": "Software Package",
"isRoot": false,
"parent": { "via": "RUNS_ON", "direction": "OUT", "parentCategory": "HOST" },
"typeFields": [
{ "name": "id", "type": "string", "description": "Stable UUID asset identifier" },
{ "name": "displayName", "type": "string", "description": "Human-friendly label" },
{ "name": "vendor", "type": "string?", "description": "Software vendor / publisher" },
"..."
],
"customAttributeKeys": ["install_date", "package_manager", "version"]
}
Use it when: building or maintaining a 3rd-party importer, generating ORM models from the API, or auditing which connector-specific keys are present in your environment (the keys list reflects your current dataset).
Filter by category: add ?category=HOST (or SOFTWARE,
NETWORK, STORAGE, CLOUD, …) to scope the response to a
single category. Case-insensitive. Unknown values return HTTP 400.
GET /api/v1/schema/asset-types?category=HOST GET /api/v1/schema/asset-types?category=SOFTWARE
Bulk Export Endpoints
For exporting data into BI tools, ITSM CMDBs, spreadsheets, or for backup, the
platform exposes paginated, streaming export endpoints. All responses include a
Content-Disposition attachment header so browsers download rather than
render. CSV fields follow RFC 4180 quoting; JSON is a single array streamed
record-by-record so memory stays bounded for large estates.
| Endpoint | Format | Returns |
|---|---|---|
GET /api/v1/export/assets.csv | CSV | All asset records (one row per asset) |
GET /api/v1/export/assets.json | JSON | Same data as a JSON array |
GET /api/v1/export/software.csv | CSV | Installed software packages, joined to parent asset |
GET /api/v1/export/software.json | JSON | Same as JSON |
GET /api/v1/export/processes.csv | CSV | Running processes, joined to parent asset |
GET /api/v1/export/processes.json | JSON | Same as JSON |
Installed Software
Each row carries assetId, assetDisplayName and
assetHostname so the parent asset can be looked up without a second API call.
Sorted by asset name, then package name, for stable, diffable output.
| Field | Type | Notes |
|---|---|---|
assetId | string (UUID) | Parent asset id — joins to /api/v1/assets/{id} |
assetDisplayName | string | Asset's display name (hostname, FQDN, or fallback) |
assetHostname | string? | Hostname when known |
packageName | string | e.g. openssh-server, Microsoft Office 365 |
version | string? | e.g. 8.9p1-3, 16.0.17029.20132 |
publisher | string? | Software vendor (Microsoft, Adobe, Canonical, etc.) |
installDate | date? | ISO-8601 date (YYYY-MM-DD) when available |
packageManager | string? | dpkg, rpm, snap, brew, msi, winget etc. |
Filters (all optional, combinable):
?assetId={uuid}— only rows for that asset?publisher={name}— exact match, e.g.?publisher=Microsoft?packageManager={name}— e.g.?packageManager=dpkg
# Full inventory (CSV) curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/export/software.csv" \ -o software.csv # Just one asset (JSON) curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/export/software.json?assetId=3eadf3af-6683-4a6a-aa9e-ea4eff6e755a" # Everything Microsoft-published, CSV curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/export/software.csv?publisher=Microsoft"
Example CSV output (header + two rows):
assetId,assetDisplayName,assetHostname,packageName,version,publisher,installDate,packageManager 3eadf3af-6683-4a6a-aa9e-ea4eff6e755a,esx01.example.com,esx01,openssh-server,8.9p1-3,Canonical,2024-11-12,dpkg 3eadf3af-6683-4a6a-aa9e-ea4eff6e755a,esx01.example.com,esx01,nginx,1.24.0-2ubuntu7.1,Canonical,2024-11-12,dpkg
Running Processes
Snapshot of processes captured at the most recent connectorised scan of each asset. Same parent-linking convention. Sorted by asset name, then PID.
| Field | Type | Notes |
|---|---|---|
assetId | string (UUID) | Parent asset |
assetDisplayName | string | Asset display name |
assetHostname | string? | Hostname when known |
pid | integer | Process id at the time of the scan |
ppid | integer? | Parent process id |
processName | string | Executable name (e.g. nginx, java) |
commandLine | string? | Full argv, truncated to fit storage column |
username | string? | Owning user (root, SYSTEM, etc.) |
state | string? | OS-reported run state (Linux R/S/D, Windows Running etc.) |
memoryKb | integer? | Resident memory in KB |
cpuPercent | decimal? | CPU % at sample time |
sourceConnector | string? | Connector that captured this row (SSH, WINRM, etc.) |
softwareAssetId | string (UUID)? | If the process was matched to a SOFTWARE-category asset (cross-link), that asset's id |
Filters (all optional, combinable):
?assetId={uuid}— only rows for that asset?username={name}— exact match, e.g.?username=root
# Every process on every asset, JSON curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/export/processes.json" # Just root processes on one host, CSV curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/export/processes.csv?assetId=3eadf3af-6683-4a6a-aa9e-ea4eff6e755a&username=root"
Performance & pagination
All four endpoints stream results in batches of 500 rows over the response body — memory stays bounded regardless of estate size. For estates with hundreds of thousands of software rows, prefer JSON over CSV (smaller encoding) and pipe directly to a file or parser rather than buffering in memory client-side.
Auth
Standard API key (Authorization: Bearer ucm_…) — same as every other
/api/v1/* endpoint. Requires the user to have the API_USER,
DISCOVERY_ADMIN or ADMINISTRATOR role.
Synthesized asset types (CMDB-style)
Three asset types are returned by GET /api/v1/assets?type=… on demand from
sub-record tables, so CMDB integrators get a uniform asset-shaped response without the
platform needing to migrate that data into the main assets table:
| Type | Source table | Parent link |
|---|---|---|
SOFTWARE_PACKAGE | software_packages | RUNS_ON the host asset |
RUNNING_PROCESS | running_processes | RUNS_ON the host asset |
DISCOVERED_SERVICE | services | RUNS_ON the host asset |
Each synthesized row is a standard AssetSummaryResponse with:
id— the sub-record UUID (stable across re-scans)assetType/assetCategory— the synthesized type / categorydisplayName— the package / process / service namecustomAttributes— JSON bag of type-specific fields (install_date, pid, listen_ports, etc.)parent— {@link ParentRef} pointing to the host asset, withvia=RUNS_ONso the relationship graph stays consistent
Filterable via the standard ?q= free-text search (matches against the
package / process / service name, the asset display name, and the
publisher / command line). Pagination via ?page= + ?size=
mirrors the rest of the assets API.
# All software packages, page 1 curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/assets?type=SOFTWARE_PACKAGE&page=0&size=50" # All Microsoft software across the estate curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/assets?type=SOFTWARE_PACKAGE&q=Microsoft" # Processes named nginx curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/assets?type=RUNNING_PROCESS&q=nginx" # Discovered services curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/assets?type=DISCOVERED_SERVICE"
Example response (truncated):
{
"data": [
{
"id": "8b2c1d3e-...",
"assetType": "SOFTWARE_PACKAGE",
"assetCategory": "SOFTWARE",
"displayName": "openssh-server",
"vendor": "Canonical",
"model": "8.9p1-3",
"customAttributes": "{\"install_date\":\"2024-11-12\",\"package_manager\":\"dpkg\"}",
"status": "ACTIVE",
"parent": {
"id": "3eadf3af-6683-4a6a-aa9e-ea4eff6e755a",
"displayName": "esx01.example.com",
"assetType": "PHYSICAL_HOST",
"assetCategory": "HOST",
"via": "RUNS_ON"
}
}
],
"page": 0, "size": 50, "totalElements": 1247
}
When to use which:
- Bulk dump for ETL / data warehouse → the dedicated
/api/v1/export/software.csvet al. Streaming, lighter payload, simpler shape. - CMDB sync / interactive queries / pagination →
/api/v1/assets?type=…. Same response shape as every other asset, parent relationship baked in.
Discovered Services export
Mirror of the software / processes export pattern.
| Endpoint | Format |
|---|---|
GET /api/v1/export/services.csv | CSV |
GET /api/v1/export/services.json | JSON |
| Field | Type | Notes |
|---|---|---|
assetId | string (UUID) | Parent asset |
assetDisplayName | string | Asset display name |
assetHostname | string? | Hostname when known |
serviceName | string | Unit / service name (e.g. nginx.service, Spooler) |
displayName | string? | Friendly label when the OS provides one |
state | string? | running / stopped / active / etc. |
startMode | string? | auto, manual, disabled, … |
listenPorts | array | JSON array of {protocol, port} objects when the service is bound |
sourceConnector | string? | SSH / WINRM / etc. |
Filters: ?assetId={uuid}, ?state={state}.
# All services, JSON curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/export/services.json" # Only running services on one asset curl -H "Authorization: Bearer ucm_yourkey" \ "https://insight.example.com/uControlInsight/api/v1/export/services.csv?assetId=3eadf3af-6683-4a6a-aa9e-ea4eff6e755a&state=running"
Retention & aging behaviour for sub-records
The three sub-record types persist with different lifecycle rules — the API surface is identical (export + synthesized assets), but how rows accumulate and age out differs by type:
| Type | Persistence model | What first_seen_at means | Retention |
|---|---|---|---|
SOFTWARE_PACKAGE |
Upsert keyed by (asset_id, package_name) |
The first scan that ever reported this package on this host. Stays stable across re-scans and version upgrades. | 30 days since last observation. Configurable via ucontrol.retention.sub-asset-days. |
DISCOVERED_SERVICE |
Upsert keyed by (asset_id, service_name) |
The first scan that ever reported this service. Stays stable as the service starts/stops/restarts. | 30 days since last observation. Same setting. |
RUNNING_PROCESS |
Wipe-and-replace per scan | The most recent scan's timestamp (no stable identity per process across scans — PIDs rotate). | Implicit — the table is rebuilt every credentialed scan of the asset. |
The behavioural difference matters for what you can ask of the data:
- Packages & services have stable identity per host. You can
track "when was this package first installed?",
"this service has been running uninterrupted for 90 days", or
"a new package appeared on this server overnight". The row's
idis stable across scans, so a CMDB integrator can subscribe to a specific row and have it stay valid. - Processes are a snapshot. PIDs rotate, processes start and exit between scans, and there's no stable identity to age. The export and synthesized-asset views always reflect only the most recent scan. Asking "was nginx running yesterday?" is not a question the platform can answer for processes — by design.
The retention sweep runs daily at 02:15 UTC and once shortly after each
application restart. Tunable via application.yml:
ucontrol:
retention:
sub-asset-days: 30 # how long to keep packages/services not observed
cron: "0 15 2 * * *" # when the sweep runs (UTC)
INTERFACE as a first-class CI
Interface rows can be queried directly as if they were assets, with the host
attached as the parent CI. Same synthesis pattern used for
SOFTWARE_PACKAGE / RUNNING_PROCESS /
DISCOVERED_SERVICE — no data migration; the rows are mapped from the
interfaces table on demand.
| Endpoint | Returns |
|---|---|
GET /api/v1/assets?type=INTERFACE |
One AssetSummaryResponse per interface, sorted by host name
then ifIndex. parent populated with the host CI,
via=HAS_INTERFACE. |
GET /api/v1/assets?type=INTERFACE&q=eth0 |
Free-text matches if_name, MAC, host display name. |
Interface-to-interface connections
Cross-asset edges between interfaces are derived in InterfaceTopologyStage of the normalization pipeline from two evidence sources:
| Source | How | Confidence |
|---|---|---|
LLDP / CDP |
The connector returns each neighbour with both
localPort and remotePort. We resolve each side to
its interfaces row by (asset_id, if_name). |
1.00 — precise port-to-port observation |
FDB |
For each switch_port_macs row, JOIN to the
interfaces table by MAC to find the host NIC plugged into that
switch port. |
0.90 — FDB proves L2 reachability but not direct cable |
Stored in the new interface_connections table with unique key
(source_interface_id, target_interface_id, discovered_via) so the
same edge confirmed via two evidence sources is kept as two rows — operators
can see both pieces of corroboration.
Surfaced in the Interfaces tab on every asset detail page in a new Connected to column: each cell links to the neighbour asset and shows its port name (e.g. switch-core-1:Gi1/0/12), with the discovery source + VLAN in the tooltip.
Next phase (not yet shipped): render port-to-port edges on the asset topology graph instead of just chassis-to-chassis. The data is now there; the visualisation work remains.