CMDB
Configuration Management Database — CI classes, all attributes, relationship types, topology graph, and discovery integration.
Overview
The Configuration Management Database (CMDB) stores configuration items (CIs) — every component of your IT infrastructure and the relationships between them. CIs can be hardware, software, services, applications, network devices, or any abstraction you choose to track. The CMDB powers topology visualisation, impact analysis, change risk assessment, and incident enrichment.
CI Classes
CIs are organised into classes. Each class defines the attributes available on that CI type. FlowOS ships with the following built-in classes:
| Class | Slug | Description |
|---|---|---|
| Business Service | business_service | An IT service that delivers value to users. Top level in dependency chains. |
| Application | application | A software application. May depend on servers, databases, and services. |
| Server | server | A physical or virtual server. Has hardware attributes (CPU, RAM, disk). |
| Virtual Machine | virtual_machine | A VM hosted on a hypervisor. Child of a server CI. |
| Container | container | A Docker/Kubernetes container or pod. Child of a VM or cluster. |
| Database | database | A database instance (Postgres, MySQL, Redis, etc.). |
| Network Device | network_device | Router, switch, firewall, load balancer. |
| Storage Device | storage_device | SAN, NAS, object storage bucket. |
| Cloud Resource | cloud_resource | Any cloud-managed resource (EC2, GKE node pool, Lambda, etc.). |
| Middleware | middleware | Message broker, API gateway, cache layer, etc. |
| Software Package | software_package | Installed software tracked for licensing and vulnerability management. |
| Certificate | certificate | TLS/SSL certificate with expiry tracking. |
| DNS Record | dns_record | A DNS entry that resolves to a service endpoint. |
| Endpoint | endpoint | User device: laptop, mobile, workstation. |
| Location | location | Physical location (data centre, office, rack). |
| Team | team | An organisational team CI used to link ownership across resources. |
Base CI Fields (all classes)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| id | string (ci_...) | required | auto | Unique identifier. Format: ci_ + ULID. |
| number | string | required | auto | Human-readable number (CI-NNNN). |
| name | string | required | Display name of the configuration item. | |
| class | enum | required | CI class slug (see table above). | |
| status | enum | optional | active | active · maintenance · decommissioned · retired. Affects incident impact calculations. |
| environment | enum | optional | production | development · staging · production |
| description | text | optional | Free-text description of the item. | |
| ownerTeam | string | optional | Team slug responsible for this CI. | |
| ownerUserId | string (usr_...) | optional | Individual responsible for this CI. | |
| tags | string[] | optional | [] | Free-form labels. |
| externalId | string | optional | ID in an external CMDB, asset management tool, or cloud provider. | |
| attributes | object (jsonb) | optional | {} | Class-specific attributes (see below). |
| relationships | object[] (read-only) | optional | computed | All relationships this CI participates in. Computed. See relationships section. |
| createdAt | timestamp | optional | auto | Record creation time. |
| updatedAt | timestamp | optional | auto | Last modification time. |
Class-Specific Attributes
server / virtual_machine
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| hostname | string | optional | — | FQDN or short hostname. |
| ipAddresses | string[] | optional | — | List of IPv4/IPv6 addresses. |
| os | string | optional | — | Operating system name and version. |
| cpuCores | integer | optional | — | Number of CPU cores. |
| ramGb | decimal | optional | — | RAM in gigabytes. |
| diskGb | decimal | optional | — | Total disk in gigabytes. |
| region | string | optional | — | Cloud region or data centre name. |
| provider | string | optional | — | aws · gcp · azure · on_premise · other |
| instanceType | string | optional | — | Cloud instance type (e.g. m5.2xlarge). |
database
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| engine | string | optional | — | postgres · mysql · oracle · mssql · redis · mongodb · elasticsearch · other |
| version | string | optional | — | Database engine version. |
| host | string | optional | — | Database hostname or endpoint URL. |
| port | integer | optional | — | Database port. |
| databaseName | string | optional | — | Primary database/schema name. |
| replication | string | optional | — | none · primary · replica · multi_master |
| haEnabled | boolean | optional | — | Whether high-availability or clustering is enabled. |
certificate
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| domain | string | optional | — | Primary domain (CN) on the certificate. |
| sanDomains | string[] | optional | — | Subject alternative names. |
| issuedBy | string | optional | — | Certificate authority name. |
| issuedAt | date | optional | — | Issue date. |
| expiresAt | date | optional | — | Expiry date. FlowOS alerts 30 and 7 days before expiry. |
| autoRenew | boolean | optional | — | Whether auto-renewal is configured. |
Relationship Types
Relationships connect CIs in the topology graph. Each relationship has a type and a direction.
| Type | Direction | Description |
|---|---|---|
| depends_on | A → B | A cannot function without B. Used for upstream dependency mapping. |
| runs_on | A → B | A (app/container) runs on B (server/VM). Physical/virtual hosting. |
| hosted_by | A → B | A (DB/service) is hosted by B (cloud provider/data centre). |
| connects_to | A ↔ B | Network connectivity. Non-directional dependency. |
| part_of | A → B | A is a component of B (rack-level or cluster membership). |
| owned_by | A → B | A is owned/managed by B (team CI or user). |
| uses | A → B | A uses B (e.g. app uses a shared library or DNS record). |
| backs_up_to | A → B | A is backed up to B (backup target). |
| fails_over_to | A → B | A fails over to B in a HA or DR setup. |
| monitors | A → B | A (monitoring tool CI) monitors B. |
Hardware, Network & Compliance Fields
Beyond the base fields, every CI (regardless of class) carries a set of hardware, network, data-centre and monitoring/compliance attributes directly on the record — these are plain schema fields, not entries in the free-form attributes bag.
Network & hardware
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| ipAddress | string | optional | null | Primary IPv4/IPv6 address. |
| macAddress | string | optional | null | MAC address of the primary network interface. |
| fqdn | string | optional | null | Fully-qualified domain name. |
| osName | string | optional | null | Operating system name, e.g. "Ubuntu", "Windows Server". |
| osVersion | string | optional | null | OS version or build string. |
| cpuCount | number | optional | null | Number of CPUs / vCPUs. |
| cpuType | string | optional | null | CPU model/type. |
| ramGb | number | optional | null | RAM in gigabytes. |
| storageGb | number | optional | null | Total storage in gigabytes. |
Data centre & virtualisation
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| datacenter | string | optional | null | Data centre or facility name. |
| rack | string | optional | null | Rack identifier. |
| rackUnit | string | optional | null | Rack-unit (U) position. |
| cluster | string | optional | null | Cluster name (VM/container host cluster). |
| virtualHostId | ObjectId (ref CI) | optional | null | The CI this one runs on, e.g. a hypervisor host. |
| containerImage | string | optional | null | Container image name. |
| containerRegistry | string | optional | null | Registry the image was pulled from. |
| networkZone | string | optional | null | Network/security zone, e.g. "dmz", "internal". |
| vlan | string | optional | null | VLAN identifier. |
Monitoring & compliance
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| monitoringEnabled | boolean | optional | false | Whether this CI is actively monitored. |
| showOnStatusPage | boolean | optional | false | Whether health is surfaced on a public status page. |
| monitoringToolId | string | optional | null | Identifier of the external monitoring tool tracking this CI. |
| backupEnabled | boolean | optional | false | Whether backups are configured for this CI. |
| backupSchedule | string | optional | null | Backup schedule description/cron. |
| lastBackupAt | date | optional | null | Timestamp of the last successful backup. |
| patchGroup | string | optional | null | Patch/maintenance group this CI belongs to. |
| lastPatchedAt | date | optional | null | Timestamp this CI was last patched. |
| openIncidentCount | number | optional | 0 | Count of currently open incidents linked to this CI. |
| complianceStatus | enum | optional | unknown | compliant · non_compliant · unknown. Set automatically by CI Compliance drift scans (see below). |
| discoverySource | enum | optional | manual | manual · agent · agentless · import — how this CI record was populated. |
| lastDiscoveredAt | date | optional | null | Last time automated discovery updated this record. |
| dataQualityScore | number (0-100, read-only) | optional | null | Field-completeness score. See Data Quality Score below. |
CI Lifecycle Transition Validation
CIs use a 9-state lifecycle: ordered · installed · configured · active · maintenance · inactive · decommissioning · retired · disposed. A model-level hook enforces which transitions are legal:
| From status | Allowed next statuses |
|---|---|
| ordered | installed, retired |
| installed | configured, active, retired |
| configured | active, maintenance, retired |
| active | maintenance, inactive, decommissioning, retired |
| maintenance | active, inactive, retired |
| inactive | active, maintenance, decommissioning, retired |
| decommissioning | retired |
| retired | disposed |
| disposed | none — terminal state |
Any other transition is rejected with an error of the form Invalid lifecycle transition: active → ordered. Allowed: maintenance, inactive, decommissioning, retired, and the save fails — the status change is not persisted.
pre('save') hook, so it only fires when a CI document is saved via .save() — which happens on CI creation. The standard PATCH /api/cmdb/cis/:id update endpoint applies changes with findOneAndUpdate, which does not run save middleware, so status changes made through that endpoint are not currently checked against this table.CMDB Data Quality Score
Each CI carries a computed dataQualityScore (0–100) that reflects how complete its record is. The score is a weighted sum over 18 fields — a field counts as “present” if it is not null, undefined, an empty string, or 0:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| ownerId | weight 10 | optional | — | Counted if set. |
| supportGroupId | weight 10 | optional | — | Counted if set. |
| name | weight 5 | optional | — | Always set on valid CIs. |
| ciClass | weight 5 | optional | — | Always set on valid CIs. |
| serialNumber | weight 5 | optional | — | |
| assetTag | weight 5 | optional | — | |
| location | weight 5 | optional | — | |
| datacenter | weight 5 | optional | — | |
| environment | weight 5 | optional | — | |
| ipAddress | weight 5 | optional | — | |
| osName | weight 5 | optional | — | |
| ramGb | weight 5 | optional | — | A value of exactly 0 counts as missing. |
| cpuCount | weight 5 | optional | — | A value of exactly 0 counts as missing. |
| healthStatus | weight 5 | optional | — | |
| lastDiscoveredAt | weight 5 | optional | — | |
| monitoringEnabled | weight 5 | optional | — | false counts as missing (falls back to 0). |
| purchaseCost | weight 5 | optional | — | A value of exactly 0 counts as missing. |
| vendorName | weight 5 | optional | — |
The earned weight divided by the total (100) is rounded to the nearest integer and stored on dataQualityScore.
pre('save') hook, so it is recalculated when a CI is created but not when it is edited through the standard update endpoint (which uses findOneAndUpdate).CI Configuration Compliance
Baseline & drift detection for CI configuration, gated behind the feature_ci_compliance flag. A baseline captures the expected value of one or more fields, scoped to either specific CIs (ciIds) or an entire CI class (ciClass). Baselines can be typed by hand or captured from a live CI via snapshotFromCiId.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| name | string | required | — | Baseline name. |
| description | string | optional | — | Free-text description. |
| ciClass | string | optional | — | Scope: apply to all CIs of this class. |
| ciIds | ObjectId[] | optional | — | Scope: specific CIs. Takes precedence over ciClass. |
| snapshot | object | required | — | Map of field name → expected value. |
Running POST /compliance/baselines/:id/scan loads every CI in scope and, for each one, compares the live value of each field named in the snapshot against the expected value (via a JSON string comparison). Any mismatch is recorded as a { field, expected, actual } drift entry. If a CI has one or more drifted fields its status is drifted, otherwise compliant — and the scan writes that result straight back onto the CI's complianceStatus field (non_compliant / compliant). Drift results are upserted per (baseline, CI) pair, so re-running a scan updates the existing record rather than duplicating it.
A drift item can be marked remediated via POST /compliance/drift/:id/remediate — this only changes the drift record's status (and records who/when); it does not modify the CI or re-scan it.
POST /api/cmdb/compliance/baselines
{
"name": "Prod DB baseline",
"ciClass": "database",
"snapshotFromCiId": "665f1...",
"snapshot": { "osVersion": "", "patchGroup": "", "backupEnabled": "" }
}Software Asset Management (SAM)
SAM, gated behind feature_sam, is a set of read-mostly views built on top of the existing InstallBase collection (see Install Base below) filtered to assetClass in software/license. It does not introduce a new storage model.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| GET /dashboard | endpoint | optional | — | Headline counts: total, active (lifecycleState=in_use), expired, expiringSoon (30 days), overAllocated (quantityUsed > quantity), and an overall complianceRate. |
| GET /software | endpoint | optional | — | Paginated list of software/license InstallBase records, filterable by status, licenseType, supplierName, search (product name). |
| GET /compliance | endpoint | optional | — | Groups records by productName and sums quantity (entitled) vs quantityUsed. Status per product: over_allocated, at_limit, or compliant; surplus = entitled − used. |
| GET /expiring | endpoint | optional | — | Records whose licenseExpiry or warrantyExpiry falls within `days` (default 30, max 365). Emits a cmdb.install_base_expiring event per record. |
| POST /normalize | endpoint | optional | — | Stub product-name normalizer: returns existing productName values that fuzzy-match the input (case-insensitive substring), for future canonicalisation. |
| GET /publishers | endpoint | optional | — | Distinct, sorted list of supplierName values across software/license assets. |
CI ↔ ITSM Linking
Bidirectional linking between CIs and Incidents, Changes, and Problems. Attaching or detaching a CI on either side re-resolves the record's business-service linkage (via the CMDB impact-linkage service) so that businessServiceId/impactedServiceIds stay in sync with the current set of affected CIs.
- •
Incident— primaryciIdplus anaffectedCiIdsarray. - •
ChangeRequest—affectedCiIdsarray plus derivedimpactedServiceIds. - •
Problem— a singlerootCauseCiIdplus anaffectedCiIdsarray.
A combined view is available per CI at GET /cis/:id/affected-records, returning all non-closed incidents, changes, and problems that reference it (as primary, root cause, or affected CI).
Alert Storm Suppression
When a CI has autoCreateIncident enabled (or the tenant default is on) and its health status changes to down or degraded, FlowOS auto-creates an incident — but repeated flapping shouldn't flood the queue with duplicate incidents. Alert suppression windows prevent that:
- 1
A health update arrives (manual
PATCH/POST /cis/:id/health, or an external monitoring push toPOST /monitoring/push) with a new status of down or degraded. - 2
FlowOS checks for an
AlertSuppressionrecord for that CI withstatus: 'active'whosestartedAtfalls within the lastci.suppressionWindowMinutes(default 5) minutes. - 3
If one is found, the alert is suppressed:
suppressedCountandalertCountare incremented andlastAlertAtis updated — no new incident is created. - 4
If none is found, a new incident is created (priority
criticalfor down,highfor degraded), and — ifci.maxAlertsBeforeSuppress(default 3) is greater than 0 — a freshAlertSuppressionwindow is opened, linked to that incident.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| ciId | ObjectId (ref CI) | required | — | CI this suppression window applies to. |
| startedAt | date | required | — | When the window opened. |
| lastAlertAt | date | required | — | Timestamp of the most recent alert seen (suppressed or not). |
| alertCount | number | optional | 1 | Total alerts observed in this window, including the triggering one. |
| suppressedCount | number | optional | 0 | How many of those alerts were suppressed (i.e. did not create an incident). |
| linkedIncidentId | ObjectId (ref Incident) | optional | — | The incident created when the window opened. |
| status | enum | optional | active | active · resolved. |
| resolvedAt | date | optional | — | Set when resolved. |
suppressionWindowMinutes (default 5) and maxAlertsBeforeSuppress (default 3). In the current implementation maxAlertsBeforeSuppress only gates whether a suppression window is opened at all (must be > 0) — it is not used as a hard cap on alertCount. There is also no scheduled job that flips old windows to resolved; a window simply stops matching once its startedAt falls outside the lookback, and the next alert for that CI opens a new one.Install Base — Asset Inventory
The InstallBase collection tracks individual hardware, software, cloud, virtual, and license assets — separate from (but optionally linked to) a CMDB CI via ciId. It carries financial, lifecycle, assignment, and ITSM-linkage detail that doesn't belong on the CI record itself.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| ciId | ObjectId (ref CI) | optional | — | Linked configuration item, if any. |
| assetClass | enum | optional | software | hardware · software · cloud · virtual · license · other |
| lifecycleState | enum | optional | in_use | on_order · in_stock · in_use · in_maintenance · retired · disposed · lost_stolen |
| quantity | number | optional | 1 | Units/seats entitled (e.g. licenses purchased). |
| quantityUsed | number | optional | 1 | Units/seats currently allocated. Used by SAM compliance checks. |
| purchaseCost | number | optional | 0 | Original purchase cost. |
| currentValue | number | optional | 0 | Current book value. |
| depreciationMethod | enum | optional | none | straight_line · declining_balance · none |
| depreciationYears | number | optional | 0 | Useful life in years. |
| assignedUserId | ObjectId (ref User) | optional | — | User this asset is assigned to. |
| assignedDepartment | string | optional | — | |
| licenseExpiry | date | optional | — | For software/license assets. |
| warrantyExpiry | date | optional | — | For hardware assets. |
| linkedIncidentIds | ObjectId[] (ref Incident) | optional | — | Incidents raised against this asset. |
| linkedChangeIds | ObjectId[] (ref ChangeRequest) | optional | — | Changes affecting this asset. |
GET /install-base accepts status, type/assetClass, and customerId filters, plus two added for cross-linking asset views into other record types: ciId (powers the “Assets” tab on a CI detail page) and assignedUserId (powers an “Equipment” tab on an employee/user detail page).
name→productName, assetType→assetClass, manufacturer→make, poNumber→purchaseOrderNumber, and a status string (e.g. "ordered", "deployed") that is mapped onto the real lifecycleState enum.Depreciation & Expiry Jobs
Asset Depreciation Calculator
Runs monthly (1st of the month at 02:00 UTC — checked every 15 minutes, guarded so it fires once per month). It recalculates currentValue on CmdbCi records (not InstallBase records) that have depreciationMethod of straight_line or declining_balance, a status other than retired/disposed, purchaseCost > 0, and a purchaseDate.
ageYears = (now - purchaseDate) / 365.25 days
depYears = ci.depreciationYears ?? 5
straight_line:
annualDepreciation = purchaseCost / depYears
newValue = max(0, purchaseCost - annualDepreciation * ageYears)
declining_balance (double-declining):
fixedRate = 2 / depYears
newValue = max(0, purchaseCost * (1 - fixedRate) ^ ageYears)The result is rounded to 2 decimal places and written to currentValue only if it differs from the stored value by more than $0.01.
Asset Expiry Check
Runs daily at 09:00 UTC across every active tenant (also invocable on demand as cron script cmdb_install_base_expiry_check). It scans non-retired/disposed InstallBase records whose licenseExpiry, warrantyExpiry, or contractExpiry falls within the next 30 days.
For each matching record, recipients are the asset's assignedUserId plus, if it is linked to a CI, that CI's ownerId and technicalOwnerId. A system notification is created per recipient for each of the license/warranty dates that is actually within the window — type asset_expiry_critical if ≤ 7 days out, otherwise asset_expiry_warning — with a title, body, and meta: { assetId, expiryType, daysUntilExpiry }.
contractExpiry is part of the query that selects which records are “expiring”, but the notification step only generates alerts for the licenseExpiry and warrantyExpiryfields — a record pulled in solely because its contract is expiring soon won't itself produce a notification.API Quick Reference
/api/v1/cmdb/itemsList CIs with class, status, and tag filters
/api/v1/cmdb/itemsCreate a CI
/api/v1/cmdb/items/:idGet CI with all attributes and relationships
/api/v1/cmdb/items/:idUpdate CI fields and attributes
/api/v1/cmdb/items/:idRetire (soft-delete) a CI
/api/v1/cmdb/items/:id/relationshipsAdd a relationship to another CI
/api/v1/cmdb/items/:id/relationships/:relIdRemove a relationship
/api/v1/cmdb/topologyGet full topology graph as { nodes, edges }
/api/v1/cmdb/items/:id/impactGet upstream/downstream impact chain for a CI
CI Compliance
/api/cmdb/compliance/baselinesList compliance baselines
/api/cmdb/compliance/baselinesCreate a baseline (optionally from a CI snapshot)
/api/cmdb/compliance/baselines/:id/scanRun a drift scan against all CIs in scope
/api/cmdb/compliance/driftList drift results, filterable by baselineId/status/ciId
/api/cmdb/compliance/drift/:id/remediateMark a drift item remediated
/api/cmdb/compliance/summaryAggregate compliance rate across all CIs
Software Asset Management
/api/sam/dashboardHeadline license compliance metrics
/api/sam/softwareList software/license assets
/api/sam/compliancePer-product entitled vs. used license counts
/api/sam/expiringSoftware/license assets expiring within N days
/api/sam/normalizeSuggest canonical product names
/api/sam/publishersDistinct publisher/supplier list
CI ↔ ITSM Linking
/api/cmdb/cis/:id/affected-recordsCombined open incidents, changes, and problems for a CI
/api/cmdb/cis/:id/incidents/:incidentId/linkLink an incident to a CI
/api/cmdb/cis/:id/changes/:changeId/linkLink a change to a CI
/api/cmdb/cis/:id/problems/:problemId/root-causeSet a CI as a problem's root cause
/api/cmdb/incidents/:id/affected-cisAttach one or more CIs to an incident
/api/cmdb/changes/:id/affected-cisAttach one or more CIs to a change
/api/cmdb/problems/:id/affected-cisAttach one or more CIs to a problem
Health & Alert Suppression
/api/cmdb/health/dashboardHealthy/degraded/down counts across all CIs
/api/cmdb/cis/:id/healthSet CI health status (may trigger auto-incident + suppression)
/api/cmdb/cis/:id/health-historyHealth change event history for a CI (last 100 events)
/api/cmdb/monitoring/tokenGenerate a token for external monitoring push
/api/cmdb/monitoring/pushToken-authenticated external health push (no JWT)
Install Base
/api/cmdb/install-baseList assets — filterable by status, assetClass, ciId, assignedUserId
/api/cmdb/install-baseCreate an asset record
/api/cmdb/install-base/:idUpdate an asset record
/api/cmdb/install-base/expiringAssets with license/warranty expiry within 30 days