-
Notifications
You must be signed in to change notification settings - Fork 15
CMDB Developer Guide
The foundations: how a configuration item is modelled, why there are two entirely separate kinds of link, where the write rules actually live, and the rules you cannot break without breaking something else.
Read this before the feature guides. The siblings are Impact analysis (the blast radius) and Multi-tenancy (Β§7 here is a summary of it). The user-facing page is CMDB; the design doc and roadmap is docs/cmdb.md.
Colour key: ποΈ schema Β· βοΈ engine Β· π API Β· π₯οΈ UI Β· π¨ CSS Β· π i18n Β· π§ͺ tests Β· π docs
| π¨ | File | What it does |
|---|---|---|
| βοΈ | includes/services/cmdb.php |
CmdbService β the shared write rules. Object create/update/delete and relationship create/delete. Both the UI endpoints and the REST API call it; it validates and writes, never emits HTTP |
| βοΈ | includes/cmdb_impact.php |
the blast radius + the three direct-impact buckets β see the impact guide |
| βοΈ | includes/cmdb_audit.php |
the data-quality checks |
| ποΈ | database/freeitsm.sql |
the 8 cmdb_* tables, their FKs, and the seeded relationship verbs |
| ποΈ | includes/db_verify_schema.php |
the same columns for the upgrade path. |
| π |
api/cmdb/save_object.php Β· delete_object.php
|
thin wrappers over CmdbService
|
| π |
api/cmdb/save_class.php Β· save_class_property.php Β· save_relationship_type.php (+ their delete_/get_ twins) |
the admin surface. Not in the service layer β UI-only, see Β§4 |
| π |
api/cmdb/get_object.php Β· get_objects.php Β· search_objects.php
|
reads for the detail page, browse table and pickers |
| π |
api/cmdb/get_object_impact.php Β· get_object_tickets.php Β· get_audit.php
|
the synthesis panels |
| π | api/cmdb/create_impact_diagram.php |
the handoff to Network Mapper β turns a blast radius into a real diagram. Checks both module gates, see the impact guide Β§10 |
| π |
api/cmdb/generate_object_summary.php Β· ai_suggest_properties.php Β· ai_suggest_questions.php Β· _ai_helpers.php
|
the AI surface |
| π |
api/cmdb/get_ai_settings.php Β· save_ai_settings.php Β· test_ai_key.php
|
provider / key / model, stored in system_settings under cmdb_ai_*
|
| π | api/v1/resources/cmdb.php |
REST v1. Mirrors the UI through the same service |
| π₯οΈ |
cmdb/index.php + browse.js
|
class sidebar + object table |
| π₯οΈ |
cmdb/object.php + object.js + options-editor.js
|
the detail page β inline editing, hierarchy, relationships, impact, activity, mini-graph |
| π₯οΈ |
cmdb/settings/index.php + settings.js + manifest.php
|
classes, properties, relationship types, AI |
| π₯οΈ |
cmdb/audit.php + audit.js
|
data quality |
| π₯οΈ | cmdb/includes/header.php |
the module nav β $current_page drives the active state |
| π₯οΈ | cmdb/help.php |
the in-module help guide β 14 sections with sticky scroll-spy nav |
| π |
lang/en/cmdb.php + lang/pt-BR/cmdb.php
|
586 keys each, same commit |
This is the single most important thing to understand, and getting it wrong produces a model that looks fine and behaves badly.
Containment β cmdb_objects.parent_id. Ontological dependency: the child is part of the parent and cannot outlive it. A NIC in a server, a database on an instance. One parent, forming a strict tree.
Relationships β cmdb_object_relationships. A user-defined verb between two objects, both of which exist independently. depends on, connects to, hosted on. Many-to-many, no ownership implied.
The consequences are load-bearing:
- Deleting an object deletes its whole descendant subtree. That is correct precisely because parenthood means ontological dependency. Relationships are only unlinked, never followed into a delete.
- Impact travels down containment automatically, but along a relationship only if its type is configured to carry impact. A parent failing takes the children by definition; "A is managed by B" implies nothing.
-
Parent assignment is cycle-checked (
validateParent(), walking up to 100 hops); relationships are not, because a cycle of relationships is legitimate.
π When in doubt, ask "if I deleted the parent, should this vanish?" Yes β containment. No β relationship.
There is a third, quieter kind: an object_ref property, which is a typed pointer at another object. It is a field, not a link β but the module treats it as a real connection for impact and for the audit, because in practice that is how a lot of estates record dependencies.
Eight tables. class_key and property_key are immutable once created; display labels are freely editable, so renaming never breaks a reference.
| Table | Notes |
|---|---|
cmdb_classes |
a type of thing. class_key immutable, name editable |
cmdb_class_properties |
property definitions. property_type β text / number / date / boolean / dropdown / object_ref; target_class_id only for object_ref; is_required; spreads_impact
|
cmdb_class_property_options |
dropdown values with optional colour |
cmdb_objects |
instances. parent_id, is_planned, ai_summary, tenant_id
|
cmdb_object_properties |
values, strongly typed β value_text / value_number / value_date / value_boolean / value_object_id, one populated per row |
cmdb_relationship_types |
the verb library. verb + inverse_verb + impact_direction
|
cmdb_object_relationships |
edges. Unique on (from, to, type) |
cmdb_icons |
curated icon lookup |
A ninth table, ticket_cmdb_objects, carries the ticket β CI links. It is not cmdb_-prefixed because it belongs to neither module outright, and both ends are company-checked on write.
op.id IS NULL OR (β¦all five IS NULL) is the correct emptiness test rather than = ''.
cmdb_objects carries tenant_id. Classes, properties, options and relationship types are install-wide admin config; the child tables inherit their company through their object.
CmdbService owns objects and relationships β the parts that genuinely have two callers (the UI endpoints and REST v1). It takes an ActorContext plus canonical input, validates, writes, and throws ServiceError. It never emits HTTP.
Deliberately outside the service:
- Classes, property definitions and relationship types β UI-only admin settings. REST exposes classes and relationship types read-only, so there is no second writer to keep in step.
- CMDB β ticket links β API-only, with no UI twin.
Extracting those would add indirection with nothing on the other side of it. π The rule is "share what has two callers", not "share everything".
The service accepts two property input shapes and normalises both: properties as a {property_key: value} map (REST's canonical form) and property_values as an id-addressed list (what the UI's inline editors send). Entries whose property_id isn't in the class are dropped rather than rejected, matching the UI's original behaviour.
Canonical behaviour, wherever the write came from: class is immutable after creation; parents are cycle-checked; required properties are enforced on create but only on touched properties on update (so inline editing one field doesn't fail on an unrelated blank); and every value is validated per type β numbers numeric, dropdowns against the option list, dates parsed, object_ref checked for existence, target class, and no self-reference.
deleteObject() removes the descendant tree by hand: nulls inbound object_ref values, deletes properties, cleans Network Mapper nodes and connectors, deletes relationships both directions, removes ticket links, then deletes the objects.
π It does not rely on FK cascades, because they may not exist. Installs grown through Database Verification have no CMDB foreign keys at all β db_verify_schema.php generates columns and primary keys, not FKs. Any cleanup that trusts a cascade works on a fresh freeitsm.sql install and silently leaves orphans on an upgraded one.
That asymmetry is worth holding onto generally: a fresh install and a Verify-grown install are not the same database. It is why the audit's broken-reference check can never fire on a fresh install and matters on an upgraded one.
Four, in includes/capabilities.php:
| Capability | Gates |
|---|---|
cmdb.manage |
umbrella |
cmdb.classes |
the CI schema itself β classes and property definitions |
cmdb.relationship_types |
the verb library |
cmdb.ai |
AI provider + API key |
Every endpoint additionally calls requireModuleAccessJson('cmdb'). Settings tabs are filtered through settingsManifestFor('cmdb'), so a tab an analyst may not see is never rendered rather than merely hidden.
Configuration items are company-scoped and β unlike knowledge articles β there is no "shared with everyone" CI. That one decision drives the rest:
- a CI's parent, its relationships and every
object_refproperty must point within the same company; - the rule binds staff who can access every company too. A permission check alone would not stop them β they can legitimately reach both sides β and a cross-company link is a side channel even when the far CI is unreadable, because you can still confirm it exists;
- a CI outside scope is reported as not found, never forbidden, so the app never confirms it exists.
NULL tenant_id means the default company's, not "shared". On a single-company install every row is NULL, so any comparison must resolve NULL to the default company id first or it matches nothing.
Two features, both using the module's own provider/key (separate from Knowledge, Forms, Workflows β deliberately, for per-feature billing visibility). Configured through get_ai_settings.php / save_ai_settings.php / test_ai_key.php and the shared AI Providers panel:
-
Object summary (
generate_object_summary.php) β 2β3 sentences, cached onai_summary+ai_summary_generated_atso page views cost nothing. The prompt is locked down to factual prose: no speculation, no markdown, no bullets. -
Suggest properties (
ai_suggest_questions.phpβai_suggest_properties.php) β a two-stage wizard that asks clarifying questions first, then proposes properties. Auto-creates a missing target class when it suggests anobject_ref.
docs/cmdb.md lists a third, suggest a relationship, that was never built β there is no endpoint for it. If you are working from the design doc, check api/cmdb/ before assuming a feature exists.
π The AI reads your vocabulary directly. Class names, property labels and relationship verbs go straight into the prompts, which is why the design doc insists on plain English (depends on, not DEP). Obscure abbreviations would force a glossary layer later.
-
Test the service, not the endpoint.
CmdbServiceis where the rules are; a fixture-driven test against it in a rolled-back transaction covers both callers at once. β οΈ A fresh install and a Verify-grown install differ in their foreign keys. If your change relies on referential integrity, decide which one you are relying on, and reproduce the other shape in a fixture (SET FOREIGN_KEY_CHECKS = 0) rather than assuming.β οΈ Pair every "cannot do X across companies" test with a positive control β a same-company case that must succeed. A blanket failure passes the negative half perfectly.β οΈ Check EN/pt-BR key parity in the same commit. An EN-only key falls back silently; nothing errors.β οΈ Rendered markup does not prove the JS parses. Parse-check in headless Chrome with a deliberately broken file as a negative control.
docs/cmdb.md is a design doc, not a status report. It was written before the build and describes intent; divergences are tagged there but this list is the reliable one.
-
No tree view.
docs/cmdb.mdUX principle 7 promises two browse modes; only the flat per-class list exists. Hierarchy navigation happens on the detail page instead (breadcrumb, children cards, mini-graph), which has covered the need so far. - No "suggest a relationship" AI feature β designed in the doc, never built. See Β§8.
-
No class inheritance. Classes are flat by design in v1;
docs/cmdb.mdV2 Β§2 sketches an optionalparent_class_id. - No versioning of property values β there is no history of what a CI used to say.
- No discovery/sync into the CMDB. vCenter, Intune and the asset scripts feed Assets, not this module.
- No CSV import. Only the demo-data importer exists. Probably the single biggest adoption blocker.
- Foreign keys are unguarded on Verify-grown installs β see Β§5.
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
- MobileβFriendly
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)