Skip to content

Asset Type Profiles Design

Ed Mozley edited this page Oct 22, 2026 · 3 revisions

Asset type profiles, connected assets and inventory freshness - design (#200)

Status: agreed - Part A built (see Asset type layouts and its developer guide); parts B, C and D to follow. This page sets out what I am building for issue #200, and why. It was written before any code so the approach could be checked first, and @Dr4inder reviewed it on the issue on 22 October 2026. Their clarifications are folded in below and summarised in Where we landed. The decisions are in section 8.

The request, from issue #200 (raised by @Dr4inder):

"The asset detail view remains strongly oriented towards computers. Other asset types can display fields and tabs that are irrelevant to their purpose. For example, a monitor does not normally need Windows-specific information or a Device Manager tab."

In plain words

FreeITSM already lets you decide which custom fields each asset type records - a monitor can have Screen size and Resolution, a printer can have Toner model. What it does not let you decide is which of the built-in details and tabs a type shows. Every asset, whatever its type, gets CPU, memory, operating system, build number and BIOS version on its Key info tab, and a Devices and a Software tab beside it. On a monitor those are always empty, and an empty box cannot tell you whether the information does not apply, was never collected, or was collected and really is empty.

The request has four parts. In order of priority:

  1. Asset type profiles - per type, choose which built-in sections and which tabs are shown. A monitor gets a focused page; a computer looks exactly as it does today.
  2. Inventory freshness - for each inventory section (software, disks, network adapters, devices), say which tool supplied it, when it was observed, and whether it is current, stale, empty or never collected.
  3. Connected assets - a monitor connected to a computer, visible from both ends, with where the connection was seen and its history.
  4. Reconciliation you can review - ambiguous matches and protected manual values shown to a person, rather than handled silently.

Each part is useful on its own and can ship in its own release.

Where we landed

After review on the issue, the direction is agreed: section switches rather than a field designer, tab ordering, editable presets, profiles as presentation only, and the order A, B, C, D, with Part A the priority and not held up by the others. Five clarifications changed the design, and each is reflected in the section it touches:

# Clarification What changed
1 Hidden is not "not applicable". A team may hide Software on computers to keep the page simple and still collect and use that inventory A hidden section or tab is described as hidden by this profile, never as not applicable, and the profile is never used to decide what an asset has. Collection and freshness stay available when hidden details are shown (section 3, section 4)
2 No second serial number. A required custom Serial number field would duplicate the built-in one that imports and reconciliation use, and the two could disagree The custom-field workaround is withdrawn. Required built-in fields are deferred; any later completeness check refers to the built-in field and never refuses an inventory report (decision 2). The service tag moves into Identity, so it can never be hidden
3 Freshness needs an outcome per source and section. A successful empty result, a skipped section and a failed attempt are three different things Each source and section stores its last outcome - succeeded, empty, skipped or failed - with the last error and when it was tried, beside the last good data and its original observation time. The agent reports every section explicitly, because one that skips empty sections can never establish "reported empty" (section 4)
4 Connections keep source boundaries A REST source may refer to an asset that is already resolved, by its id, subject to company and access checks. A missing or incomplete observation never disconnects anything, and no source replaces another source's observations or a person's confirmation (section 5)
5 Merges keep everything A reviewed merge keeps linked records and each source's identity, as well as both histories (section 6)

Where reasonable people would choose differently, the choice is a setting in Assets β†’ Settings, with today's behaviour (or the agreed default) as the starting point: how an asset says it has hidden details (Part A), and the staleness window (Part B).


1. Is this a natural extension, or a second system?

This was the first question I asked of the request, because the risk with a feature like this is real: a "profile" that grows its own field designer, its own required-field rules and its own layout engine would be a second custom-fields system bolted beside the first, and the two would disagree. That is the outcome to avoid.

It does not have to go that way, because every part of the request lands on something that already exists:

The request What it extends Why it fits
Fields per type Custom fields are already per type - a field goes in a set, a set goes on a type (asset_type_field_sets) Profiles add the built-in half to the same per-type settings. Custom fields do not change at all
Tabs per type The asset page's fixed tab strip A list of tab switches per type, stored the same shape as the type's field sets
Inventory freshness asset_inventory_sections already stores observed_at and imported_at per source and section (#194, 3.12.0). The "Inventory sources" panel was an agreed follow-up from #194 The data is already being recorded for REST sources; this shows it, and teaches the agent to record it too
"Hidden by this profile" Nothing today Comes from part 1 - and is only ever a display state. A hidden tab is not a claim that the asset has no such data (see Where we landed, point 1)
Connected assets Nothing that fits (see section 5) A new, small table - deliberately separate from custody and from the CMDB
Reconciliation The resolver already refuses to guess - resolveAssetIdentity() will not pick between two assets sharing a serial, and will not let a reused hostname write a new serial onto an old laptop (developer guide) The judgement already exists; what is missing is a list a person can review

So the rule for the whole design is: profiles decide what is shown, never what is stored, never what is required, and never who may write. Fields stay in one system (custom fields), required stays where it is (on a field's place in a set), permissions stay in roles, and inventory keeps writing whatever it reports. A profile is presentation only.


2. What FreeITSM does today

Mapped from the code at 3.13.0, not from memory:

The request needs Today Verdict
Custom fields chosen per type, reusable across types Fields, sets, sets on types, sets on single assets; required per placement; values kept when a set is removed (Custom asset fields) βœ… Have it
Hide built-in fields per type Key info always shows Manufacturer, Model, Service tag, Name, CPU, CPU speed, Memory, Operating system, Feature release, Build number, BIOS version, First seen and Last seen ❌ Fixed for every type
Choose and order tabs per type Key info, Financial, Devices, Software, Tickets, Contracts, (Projects), Documents - the same strip on every asset ❌ Fixed for every type
Add dialog without computer fields The Add dialog already leaves out the hardware details the agent reports, and shows the chosen type's custom fields βœ… Have it
Required built-in fields None are required except the name ⚠️ See decision 2
Inventory source per row Software and disk rows show their source; network adapters and physical disks do not ⚠️ Partial
Observed and imported time per section Stored in asset_inventory_sections - for REST sources only. The agent writes none, so for most installs the table is empty ⚠️ Recorded, never shown
Whole-asset freshness First seen, Last seen, "Never reported", amber after 7 days βœ… Have it, per asset not per section
A failed section recorded The REST import answers failed for a section in its response; nothing is kept ❌ Not stored
"Devices: 0" vs "never collected" The tab count is 0 either way ❌ Indistinguishable
Connected assets A custom field of type Link to something else can point at another asset - one way, read-only on the page, no history. The CMDB's "connects to" relationship links configuration items, not assets ❌ Nothing fits
No merging on weak identifiers Serial first; a hostname match only if it cannot contradict the serial; two assets sharing a serial are never guessed between βœ… Have it
Ambiguous matches visible resolveAssetIdentity() returns ambiguous, but no caller shows it to anyone ❌ Flagged, never surfaced
Protected manual values A hand-edited Manufacturer or Model on a reporting machine is overwritten by the next sync; the page warns when you edit it (#1143) ❌ None
Normalised manufacturer names None ❌ None

3. Part A - asset type profiles

What an administrator sees

Assets β†’ Settings β†’ Asset types, open a type, and a new Layout section beside its name and icon:

  • Sections shown on Key info - a tick per built-in section, in a fixed order:
Section Built-in fields in it
Identity Name, Asset tag, Service tag (the serial number), Type, Status, Location, Company (always shown - see below)
Make and model Manufacturer, Model
Hardware CPU, CPU speed, Memory
Operating system Operating system, Feature release, Build number, BIOS version
Reporting First seen, Last seen (and Never reported)
Storage Logical drives and physical drives
  • Tabs - a tick per tab (Financial, Devices, Software, Tickets, Contracts, Projects, Documents, and Connected once part C exists) and drag to reorder. Key info is always first and always shown.

Custom field sets carry on exactly as now, attached under Settings β†’ Custom fields. Nothing about them changes. The Layout section links there ("This type also records 2 sets of custom fields") so the whole picture of a type is one click away.

A new type, and every existing type, starts with everything ticked, so no install looks any different until someone changes a type (acceptance criterion 9). A Start from menu fills the ticks from a sensible preset - Everything (today's page), Peripheral, Network equipment, Mobile device - which is a shortcut for ticking boxes, not a hard-coded restriction: the result is just ticks, and you can change any of them.

Preset Key info sections Tabs hidden
Everything all none
Peripheral (monitor, dock, headset, keyboard) Make and model Devices, Software
Network equipment (switch, router, firewall, access point) Make and model, Operating system, Reporting Devices, Software
Mobile device (phone, tablet) Make and model, Operating system, Reporting, Storage Devices

Why sections, and not a field-by-field layout designer

The request asks to "arrange fields into meaningful sections and control their order". I am proposing fixed sections you can switch on and off, not a designer where any built-in field can be dragged anywhere, for three reasons:

  1. It is the Frankenstein line. A designer that moves built-in fields around, mixes them with custom fields and renames sections is a second field system. Custom fields already have sets for grouping and ordering - that is the place to arrange things.
  2. The built-in fields come in natural groups. Nobody wants CPU without memory, or the operating system without its build number. Switching the group answers the monitor case completely.
  3. Every surface that shows a built-in field would have to learn the layout. The asset page on the desktop and on a phone today, and anything that prints an asset later. Section switches are one check each; a free layout is a renderer each.

If a single built-in field genuinely needs hiding on its own later, a per-field switch inside a section can be added without redoing this.

The rules

  • Presentation only. Hiding a section or tab never deletes, blocks or ignores anything. The agent, Intune and REST imports write exactly what they write today, and the REST API returns every field whatever the profile says. Acceptance criteria 4 and 8, and the request's own "must not silently stop authorised inventory imports".
  • Identity is never hidden. Name, asset tag, service tag, type, status, location and company are how an asset is found, scanned, matched and reported on. The service tag is the serial number that imports and reconciliation match on, so it lives here rather than under Make and model.
  • "Hidden by this profile", never "not applicable". A profile is a display choice and says nothing about what the asset has. A team can hide Software on its computers to keep the page simple and still collect and use that inventory.
  • Hidden data is never invisible data. If an asset holds values in a section or tab its type hides - a computer given the wrong type, a monitor somebody typed a model into under Hardware, or Software hidden on computers that still report it - Key info shows one quiet line naming what is hidden, "Hidden by this type's layout: Software (214) - Show them". Show them puts every hidden section and tab back for that asset, each marked hidden by this profile, so nobody mistakes a revealed tab for part of the normal layout.
  • A setting for how loud that is. Assets β†’ Settings β†’ Asset types, When an asset has details its type hides: Say so on the asset (the default, the line above) or Only offer a Show hidden link (a small link at the end of the tab strip, for teams who chose their layouts deliberately and find the line noisy). Either way the way back is always on the page.
  • The table view is not affected. It lists every type at once, and its columns are each person's own choice. A profile decides what one asset's page shows, not what the estate table offers.
  • Changing an asset's type changes which sections and tabs it shows, and nothing else. The values stay (criterion 4). This also settles one of the open questions on the custom fields developer guide for built-in values.
  • Companies. A profile belongs to the type, so a global type has one layout for every company, and a company that wants a different layout makes its own type - which is how types already work for multi-company installs.
  • Permissions. Editing a type's layout uses the existing asset types right. Nothing new.

Storage

One small table, the same shape as asset_type_field_sets:

asset_type_layout
  asset_type_id   INT     -> asset_types.id, cascade
  item_kind       'section' | 'tab'
  item_key        'hardware', 'devices', ...   (a fixed list in code)
  is_shown        TINYINT
  sort_order      INT
  UNIQUE (asset_type_id, item_kind, item_key)

No row means shown, so existing installs need no migration and a tab added in a later release appears on every type until someone hides it.

Surfaces that must honour it

The asset page, on the desktop and on a phone (one page, asset-management/index.php). Search, the table, exports, handover columns, the REST API and the dashboard are deliberately not affected (they are estate-wide or machine-facing). A built-in field added to the page later goes into one of the sections, so it is covered by the same switch.


4. Part B - inventory sources and freshness

What a person sees

The Devices, Software and Key info Storage areas each get a one-line status above their list, and Key info gets an Inventory sources panel listing each source that reports this asset:

The states are worked out per source, from what each source last did, and never from the type's profile:

State Shown as When
Never collected "No device information has been reported for this asset" No source has ever sent this section
Collected, empty "Reported empty by the agent, 3 hours ago" A source's last attempt succeeded with no entries
Skipped "Not included in the agent's last report (3 hours ago)" - the earlier data stays, with its own age A source's last report left the section out
Current "From the agent, observed 3 hours ago" Observed within the staleness window
Stale The same, in amber Older than the window
Failed "The last import of this section failed: ..." in red, beside the last good data and its original observation time The last attempt for this section failed

Several sources can report the same section, and each keeps its own row, so one source succeeding never hides another failing: the panel lists each source's state.

If the type's profile hides the tab, none of this is lost: Show them (part A) brings the tab back with its status line, and the Inventory sources panel on Key info always lists every source and section whatever the profile hides.

This is what makes "Devices: 0" honest: the badge shows a dash for never collected and 0 only for a real empty report.

The staleness window starts at 7 days, the same as Last seen and Watchtower, and is a setting in Assets β†’ Settings for teams whose sources report less often.

What has to change underneath

  • The agent records its sections in asset_inventory_sections, as REST sources already do. This is the "one implementation, for now two" follow-up from #194. The agent observes what it reports at the moment it reports it, so its observed and imported times are the same - which is correct.
  • The agent reports every section explicitly. Today it leaves out a section that has nothing in it, which makes "reported empty" and "not reported" look the same. It will send an empty section as an empty list, and leave a section out only when it did not collect it - so empty and skipped can be told apart.
  • Each attempt's outcome is stored: last_outcome (succeeded, empty, skipped, failed), last_error and last_attempt_at on asset_inventory_sections. observed_at and the fingerprint keep describing the last good data, so a failure or a skip never makes old data look new and never throws it away.
  • Importing an old observation never makes data look new: the panel shows observed_at, never the import time, as the age. This is already how #194's import stores it.
  • A failed, skipped or partial scan is never treated as "everything was removed". Only a section that succeeded replaces that source's rows; a failed or skipped one leaves them as they were.

5. Part C - connected assets

Why not one of the two things that already link assets

  • A Link to something else custom field is a property: one asset pointing at one other, set by hand, no history, one direction. It is the right tool for "replaced by" or "spare for". A monitor that moves between desks and is reported by a scanner needs history, a source and both directions - turning a field into that would bend it out of shape.
  • The CMDB's relationships are between configuration items and carry impact ("if this fails, that is affected"). The request is explicit that "an observed peripheral connection should not automatically imply a service outage relationship", and assets are not CMDB items.

So connections get their own small table, and each of the three ideas keeps one meaning: a link field is a fact about an asset, a CMDB relationship is a service dependency, a connection is a physical attachment.

What a person sees

A Connected tab (switchable per type in part A) on both assets: "Connected to DESKTOP-0142 - reported by the agent, last seen 2 hours ago" on the monitor, and the monitor listed on the computer. Each row says whether it was confirmed by a person or observed by a source, and "last observed connected to" is worded as exactly that, never as a current assignment. Connect and Disconnect buttons for manual connections, and a history of every connection and disconnection.

Storage

asset_connections
  asset_id            the computer (or switch, or dock)
  connected_asset_id  the monitor (or phone, or printer)
  source              'manual' | 'agent' | 'rest:<name>'
  first_observed_at, last_observed_at
  connected_at, disconnected_at   (NULL = still connected)
  confirmed_by_id     the analyst, for a manual connection

Each row belongs to one source: the same monitor seen by the agent and confirmed by a person is two rows, and each source only ever updates its own.

Connections and disconnections also write to each asset's existing history.

How a source names the other asset. A REST source can refer to an asset that is already resolved - by its FreeITSM id - so an integration that has matched a monitor once does not have to rematch it by serial every time. The id is checked exactly like any other write: the asset must exist, be in a company the source's key may write to, and be one the caller may reach. Otherwise the source names it by serial (a monitor's EDID serial), matched with the same resolver and the same refusal to guess - an unmatched or ambiguous peripheral is listed for review (part D), never created silently.

What never disconnects anything:

  • A missing or incomplete observation. A report that does not mention a connection leaves it as it was; only a report that explicitly says the peripheral is gone, from the source that saw it, closes that source's row. Old rows simply age, and show last observed with their date.
  • Another source. No source closes or rewrites another source's row.
  • A source, against a person. A manual confirmation is only changed by a person; a source disagreeing with it is shown beside it, not over it.

Custody (who holds an asset, handover) is untouched: a monitor can be connected to a computer and signed out to a person, and the two never update each other.


6. Part D - reconciliation you can review

The matching rules stay exactly as they are - they already refuse to merge on a hostname alone and refuse to guess between twins. What is added is visibility:

  • A Review list (Assets β†’ Settings β†’ Review, or a Watchtower card) of the cases the resolver already detects and nobody currently sees: two assets sharing a serial, a reused hostname (hostname_reused, today a history line only), an ambiguous match, and an unmatched peripheral from part C. Each one can be resolved explicitly - these are the same asset (merge), these are different, or ignore. A merge is only ever made by a person, and keeps everything both records had: both histories, every linked record (tickets, contracts, documents, projects, custody, connections, custom field values), and each inventory source's identity for the asset, so every source still recognises the merged asset on its next report. The merge itself is recorded in the history of the asset that remains.
  • Protected values. A padlock beside Manufacturer, Model and the other built-in fields a source writes. A protected value is never overwritten by an import; the import's value is kept beside it and shown as a conflict ("The agent reports HP; you set Hewlett-Packard"), with Release to let the source win again. This replaces today's warning when you edit a reporting machine's Model.
  • Manufacturer and model names - a small mapping list (HP Inc., Hewlett-Packard β†’ HP), applied when values are shown and filtered, with the reported value kept as it came.

This is the largest and least urgent part, which is why it is last. Imports keep updating only their own information throughout.


7. The issue's acceptance criteria

# Criterion Where
1 Standard fields, sections and tab order per type, in the existing settings Part A - sections and tabs; field arrangement stays with custom field sets (section 3)
2 A monitor with a focused view Part A
3 Custom fields and sets reusable Unchanged
4 Profile or type changes keep values and history Part A rules - presentation only
5 Connected assets from both ends, with source and history Part C
6 Missing, empty, stale and failed distinguishable Part B
7 Conflicts and protected values reviewable Part D
8 Validation, company boundaries, permissions Existing rights; profiles never touch writes
9 Existing installs unchanged until configured No row means shown; nothing ticked off by default

8. Decisions

All agreed on the issue on 22 October 2026.

# Decision Agreed
1 Field-level layout: fixed sections switched on and off, or a designer for individual built-in fields Sections, for the reasons in section 3. A per-field switch inside a section can follow if anyone needs it
2 Required built-in fields per type (the request asks for these "where appropriate") Deferred. Required belongs with validation, not presentation, and a built-in field is written by four routes (the page, the import, REST and the agent); an agent report must never be refused. A required custom Serial number field is not the workaround - it would duplicate the built-in serial that imports and reconciliation use. Any later completeness check refers to the built-in field itself and flags rather than refuses
3 Presets: shipped profiles, or blank Shipped Start from presets that only fill the ticks, so nothing is hard-coded and existing types stay fully ticked
4 Staleness window for part B 7 days by default, the same as Last seen and Watchtower, as a setting in Assets β†’ Settings
5 Where agent-reported monitors come from for part C REST sources first (a scanner can already report them as a section). Teaching the PowerShell agent to read monitor EDIDs is a separate agent change
6 Merging two assets in part D Yes, but only by a person from the Review list, keeping both histories, linked records and source identities, and recording the merge. Never automatic
7 How an asset says it has hidden details (part A) A setting: say so on the asset (default), or only offer a Show hidden link

9. The order to build it in

  1. Part A - profiles. Built - Asset type layouts. The highest priority in the request and the smallest real change: one table, one settings section, and the asset pages checking it. Ships in its own minor release.
  2. Part B - freshness. The agent reports every section explicitly, each attempt's outcome is stored, and the panel and per-tab status are added. Independent of part A's switches - a hidden tab keeps its status for when it is shown.
  3. Part C - connected assets. A new table, a tab, manual connections first, source-reported ones after.
  4. Part D - review and protected values. Builds on the resolver and on part C's unmatched peripherals.

10. Deliberately out of scope

As the request says, hardware-component inventory (individual RAM sticks, CPUs as records). Also: no duplicate asset records, no parallel asset system - every part above adds to the one asset record and the one settings screen.


Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally