Repository navigation
Asset CI Link Design
Status: proposal - nothing here is built yet. This page sets out what I plan to build for issue #201, and why, written before any code so the approach can be checked first. Comments on the issue are welcome. The open decisions are in section 9.
The request, from issue #201 (raised by @Dr4inder):
"The same server can exist as an asset and a CI without an obvious connection for the analyst."
A server is usually two records in FreeITSM. The asset says what the box is: make, model, serial number, where it is, who bought it, what the agent last saw on it. The configuration item (CI) says what the box does: what runs on it, what depends on it, and what breaks if it fails. Today nothing joins the two, so an analyst looking at one has no way to reach the other.
This design adds one link between them, meaning "these two records are the same piece of equipment":
- Link and navigate both ways. A Link button on the asset and on the CI, each with a search picker. The other record then shows on both pages as a card you can click.
- Asset details on the CI, read from the asset. The CI's card shows the make, model, serial number, location and inventory freshness, labelled as coming from the asset. Nothing is copied, so there is nothing to keep in step.
- Create the CI from the asset. An administrator says which CMDB class an asset type becomes (servers become "Server" CIs, say). The asset then offers Create linked CI, which asks for anything the class requires and creates the CI and the link together.
- Impact from the asset. The asset's card shows what the linked CI's "If this failed" analysis already says, with a way into the CMDB and the diagram.
The link is an identity, not a dependency. It never appears in the CMDB graph, never spreads impact, and never deletes anything.
A natural extension, as long as it keeps three lines the docs already draw:
- Assets are not CMDB items. Asset type profiles put it this way: "a link field is a fact about an asset, a CMDB relationship is a service dependency, a connection is a physical attachment." This design adds a fourth, narrow kind - the same equipment - and keeps it apart from the other three.
- The overlap is small, and that is fine. Linking equipment to tickets says "a broken mouse is an asset and will never be a configuration item⦠The only real overlap is a server, which can reasonably be both." The link is optional on both sides. An asset never needs a CI, and a service CI never needs an asset.
- No copying between modules. The asset stays the authority for inventory and lifecycle details. The CI keeps its own properties and relationships. The CI page reads the asset's values through the link and never stores them.
Mapped from the code at 3.16.0, not from memory:
| The request needs | Today | Verdict |
|---|---|---|
| A link between an asset and a CI | None. Nothing on the CMDB side refers to an asset (cmdb_objects has no asset column, and no CMDB property type can point at one) |
β |
| An asset custom field that points at a CI | Exists: a Link to something else β A configuration item field, stored in asset_field_values.value_ref_id. Writable through REST and the importer, read-only on the asset page (the picker was never built). It is a fact about the asset, one-way, and nothing on the CI shows it |
|
| Navigation both ways | Neither page shows the other | β |
| A searchable CI picker |
api/cmdb/search_objects.php (company-scoped, can filter by class), with a picker inside the CI page's script |
β To reuse |
| A searchable asset picker | The Connect picker on the asset page, and api/tickets/search_linkable_assets.php for tickets |
β To reuse |
| History of link changes | Assets: asset_history, one row per change, with the analyst. CIs: no history at all - the CMDB developer guide lists "no versioning" as a known gap |
|
| Impact analysis for one CI |
cmdbBlastRadius(), behind GET /cmdb/objects/{id}/impact and the CI page's "If this failed" panel |
β To reuse |
| Create a CI with its required properties |
CmdbService::createObject(), used by the UI and by REST, checks required properties on create |
β To reuse |
| Map an asset type to a CMDB class | None. Asset types and CMDB classes only share an icon library | β |
| Company boundaries | Assets and CIs both belong to one company. A CI's links must stay inside its company (CmdbService::assertSameCompany()) |
β Same rule applies |
| Asset deletion | There is none, except a merge (a person merging two duplicates under Assets β Settings β Review). A test fails if a merge forgets any column that refers to an asset | β Covers a new table |
| CI deletion | A hard delete of the CI and everything inside it, done step by step in CmdbService::deleteObject()
|
On the asset, a Configuration item card on Key info:
- Not linked: No configuration item. with Link (a picker of CIs in the asset's company, searchable by name and filterable by class) and, if the asset's type has a class mapped (Part C), Create linked CI.
- Linked: the CI's icon, name and class as a link into the CMDB, with Unlink. Hovering the link shows who linked it and when.
On the CI, an Asset card, placed under Details:
- Not linked: No asset. with Link (a picker of assets in the CI's company).
- Linked: the asset's name as a link, the details from Part B, and Unlink.
Both cards finish with a small Link history list: linked, unlinked, replaced, each with who, when and how (a person, or which API key). This one list is the history for both records, so the CMDB gets an honest record of its links without first needing a general history of its own.
The asset card is a new Key info section (cmdb) in asset type layouts, so a team can hide it on types that will never be CIs, such as monitors and mice. Hiding it is presentation only, as with every other section.
One new table holds the link. Each row is one period of being linked:
| Column | Meaning |
|---|---|
asset_id, cmdb_object_id
|
The pair |
linked_at, linked_by_id, link_source
|
When, who (analyst), and how (ui, or api:<key name>) |
unlinked_at, unlinked_by_id, unlink_reason
|
NULL while the link is live. The reason is unlinked, replaced, ci_deleted or asset_merged
|
ci_name, ci_class_name
|
A copy of the CI's name and class, taken when the link ends, so the history still reads properly after the CI is deleted |
- One authoritative record. Both pages read this one table, so there is no reverse reference to keep in step (acceptance criterion 2).
-
One live link per asset and one per CI. At most one row with
unlinked_atNULL for any asset, and at most one for any CI, enforced in the service inside a locked transaction and covered by tests. -
Rows are never deleted. Ending a link closes the row. That is the history, and it is also what the asset's
asset_historygets a line for (cmdb_link). - No company column. The pair must be in the same company, checked on every write, and both records carry their own company.
- Linking needs access to both sides: the Assets module, the CMDB module, and both records' company. A person who can see only one side can see the card but not change it.
-
A CI already linked to another asset is refused, and the message names that asset. Replace is a separate, explicit action. It asks for confirmation, ends the old link with reason
replaced, and starts the new one. This applies the other way round too: an asset already linked to a different CI has to be replaced on purpose. - Linking the same pair again does nothing and says it is already linked. The same applies to retries over REST.
- Unlinking never deletes either record (acceptance criterion 4).
-
When a CI is deleted, its live link ends with reason
ci_deletedinside the same delete transaction, and the CI's name is kept on the row. The asset's card then reads No configuration item, and its history says the CI was deleted. The delete confirmation on the CI says "Linked to asset LON-SRV-01. The asset is kept; the link ends." -
When two assets are merged, the link moves with the asset like everything else. If both assets are linked, to different CIs, the kept asset keeps its own link and the other ends with reason
asset_merged, and the merge preview says so before anything happens. The new table joins the merge's list of asset references, which its guard test will insist on. - Existing custom link fields are left exactly as they are. A Link to something else β configuration item field is never read as this link, never converted, and never changed (the issue's last point under section 5). Teams that want to move to the native link can do it one asset at a time, and the docs will say which to use for what.
The CI's Asset card shows, read live from the linked asset:
- the asset's name and type
- manufacturer and model
- serial number and asset tag
- status and location
- inventory freshness - when the asset was last seen, and the worst state in its Inventory sources panel (current, stale or failed), so a CI whose box has gone quiet shows it
Each value is labelled From the asset, and editing happens on the asset (the card links to it). Nothing is stored on the CI. That avoids keeping copies in step, and avoids any field-by-field sync between the modules (acceptance criterion 3).
- Which details show is a setting: CMDB β Settings β Asset card, a list of ticks with all of the above on by default. A CMDB team that only cares about the serial number can trim it.
- Someone with CMDB access but no Assets access sees Linked to an asset and the asset's name, with no details and no link (open decision 3).
-
REST:
GET /cmdb/objects/{id}gains a read-onlylinked_assetblock (id,name), andGET /assets/{id}gainslinked_ci(id,name,class). Both are additive, so existing consumers are unaffected. The details themselves are already available fromGET /assets/{id}.
Assets β Settings β Asset types β (a type) β CMDB class: a dropdown of active CMDB classes, empty by default. Setting it to Server on the Server type does one thing only: it makes Create linked CI appear on assets of that type. It never creates a CI by itself, and never touches existing assets (the issue's own words).
Create linked CI opens a short form:
- The name, filled in with the asset's name and editable.
- Any properties the class marks as required, using the same inputs as the CMDB's own New form, so they are checked by the same rules.
- Possible matches first. Before the form, FreeITSM looks for CIs of that class, in the asset's company, that are not already linked, whose name matches the asset's name (ignoring case). If any exist they are listed, each with Link this one instead. A name match is never linked automatically, because a name alone is not proof of identity (acceptance criterion 6). If none match, the form shows straight away.
Create makes the CI and the link in one transaction, through CmdbService::createObject(), so there is either a CI that is linked or nothing at all. The CI's company is the asset's.
- Repeated requests reuse the link. If the asset is already linked by the time the request lands, nothing is created, and the existing CI is returned and opened (acceptance criterion 5).
- Nothing hardware-shaped is copied into CI properties. The class may have its own "Serial number" property, but the design does not fill it, because Part B already shows the asset's serial on the CI. Filling it would create the second copy the issue asks to avoid.
Automatic CI creation by an integration (the issue's later, opt-in step, and something #199 would want) can use exactly this path: same mapping, same matching, same transaction. It is out of scope here and would be its own design, with a review list for the ambiguous cases like the one under Assets β Settings β Review.
When the asset is linked, its Configuration item card adds a line from the CI's existing analysis:
If this failed: 14 items affected, 3 of them services. Show Β· Open as diagram
- Show opens the CI page at its "If this failed" panel. Open as diagram uses the CI page's existing button (it needs Network Mapper access, as it does today).
- It calls the same
cmdbBlastRadius()the CI page does, with the CI's company scope, so it follows the CMDB's own relationship and impact settings and nothing else (acceptance criterion 7). - The asset is not in the graph. The link adds no edge, no containment, and nothing for the walk to follow. If the analysis finds no impact edges configured, the line says so, as the CI page does.
- Shown only to someone with CMDB access. Others see the card without the line.
The same service sits behind the UI and the API, so they cannot disagree (acceptance criterion 8).
| Operation | Permission | Behaviour |
|---|---|---|
GET /assets/{id}/ci-link |
asset_ci_links.read |
{ current: {...} or null, history: [...] } |
PUT /assets/{id}/ci-link { "cmdb_object_id": 88 }
|
asset_ci_links.create |
Links. The same pair again returns 200 with "unchanged": true. 409 ci_already_linked / 409 asset_already_linked name the other record. "replace": true replaces on purpose |
DELETE /assets/{id}/ci-link |
asset_ci_links.delete |
Ends the link. 404 if there is none |
GET /cmdb/objects/{id}/asset-link |
asset_ci_links.read |
The same record, seen from the CI |
POST /assets/{id}/ci-link/create-ci { "name", "properties" }
|
asset_ci_links.create and cmdb_objects.create
|
Part C. Uses the type's mapped class; 422 no_class_mapped if there is none. Possible matches come back as 409 possible_matches with the list, unless "ignore_matches": true
|
-
Company boundaries: the key must be able to see both records, and they must be in the same company. A record the key cannot see is a
404, as everywhere else in the API. -
Retries are safe:
PUTis idempotent by nature, andcreate-cion an already-linked asset returns the existing CI rather than making a second. - A narrow permission of its own, so an integration can maintain links without being able to edit assets or CIs.
| # | Criterion | Where |
|---|---|---|
| 1 | Link from either module with a searchable picker | Part A |
| 2 | One association, visible and navigable both ways | Part A - one table, read by both pages |
| 3 | Asset information on the CI with provenance, no duplicate maintenance | Part B - read live, labelled From the asset |
| 4 | Changes recorded; unlinking keeps both records | Part A - rows closed, never deleted; asset_history line |
| 5 | Creation honours the type β class mapping and required properties; no duplicates on retry | Part C |
| 6 | Ambiguous matches and conflicting links need an explicit choice | Part C (matches), Part A (Replace / 409) |
| 7 | Impact reachable from the asset, graph unchanged | Part D |
| 8 | UI and REST enforce the same permissions and company rules | Parts A and E - one service |
| 9 | Existing installs unchanged until used | Nothing is linked, mapped or shown until someone does it; existing custom link fields are untouched |
| # | Decision | My recommendation |
|---|---|---|
| 1 | Where the history lives - the CMDB has no history of any kind | In the link table itself, shown on both cards, plus a line in the asset's existing history. A general CMDB history is a bigger, separate piece of work, and this design should not need it. |
| 2 | One-to-one | Yes: one live link per asset and one per CI, as the issue proposes. A cluster, the VMs on a host, and the applications on a server stay ordinary CMDB relationships. |
| 3 | What someone with CMDB access but no Assets access sees on the CI | That it is linked, and the asset's name - no details, no link. Hiding the link entirely would make Link look available when it is not. Showing details would leak a module they cannot open. |
| 4 | Which asset details the CI shows | The list in Part B, each one a tick in CMDB β Settings, all on by default. Teams will differ on this, so it is a setting. |
| 5 | Replacing a link |
A separate, confirmed Replace, never a side effect of Link. Over REST, 409 unless "replace": true. |
| 6 | A merge where both assets are linked to different CIs |
The kept asset keeps its link; the other ends as asset_merged, shown in the merge preview first. The alternative, refusing the merge, would block cleaning up duplicates for a reason the person can fix later. |
| 7 | What counts as a possible match for Create linked CI | Same class, same company, not already linked, and the same name as the asset ignoring case. Never linked automatically. A wider net (serial in a CI property, say) would need the CI's properties to mean something fixed, which they don't. |
| 8 | Who may link | Anyone with access to both modules and both records' company - no new capability. That matches how both modules treat edits today. If a team wants it narrower, a capability can come later. |
| 9 | Existing custom link fields that point at CIs | Left alone. No conversion now. If people ask for it, a one-off "make these native links" tool for a chosen field could follow, with a preview. |
-
Deleting a CI leaves custom link fields pointing at it.
CmdbService::deleteObject()clears CMDB references and ticket links, but an asset's Link to something else β configuration item value keeps the deleted CI's id and then shows as#88. This is separate from this design, but it is the same delete. I'll fix it alongside Part A by clearing those values in the same transaction, with a test. - The asset page's link fields are still read-only. Part A builds a CI picker for the asset page anyway, so the custom link fields could use the same picker. I'll keep that as a follow-up rather than widen this request.
-
Part A + Part B + the read half of Part E: the link, both cards, the history, the CI delete and merge handling, and
GET. This is the issue's stated priority. -
The write half of Part E (
PUT/DELETE). - Part D: impact on the asset card. Small, once A exists.
- Part C: the type β class mapping and Create linked CI, with its REST route.
Each step is usable on its own, and each would ship in a release once it's done.
- Automatic CI creation by integrations (Part C's later step, and #199's mapping). It will reuse Part C's path, but it needs its own review flow.
- A general CMDB history of property changes.
- Copying or syncing any asset value into CI properties, in either direction.
- Links from one asset to several CIs, or a CI made of several assets. Those are CMDB relationships.
- A picker for the existing custom link fields (see section 10).
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
-
Architecture
- β³ π§Ή Root folder tidy (URL routing)
- β³ π Database Integrity (FKs & constraints)
- β³ π οΈ Database Verification β Developer Guide
- β³ π§© Service Layer (UI + API share code)
- β³ πͺ€ Code traps (the TRAP: comments)
- β³ π Commenting for the Engineering wiki
- β³ π οΈ Engineering wiki scanner - Developer Guide
- β³ π Service-Layer progress
- π€ Contributing (read before a pull request)
- π§ͺ Developer tests
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- π± Currencies
- π§ The sidebar (optional module rail)
- π§© Preference templates
- π Display order (modules, Watchtower cards) β Developer Guide
- Theming & Dark Mode
- ποΈ Recent β getting back to what you were doing
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
-
MobileβFriendly
- β³ π« Mobile: Tickets
- β³ π» Mobile: Assets
- β³ π Mobile: Calendar
- β³ π Mobile: Knowledge
- β³ π¦ Mobile: Service Status
- β³ πΌ Mobile: Watchtower
- β³ π§© Mobile: Problem Management
- β³ π Mobile: Change Management
- β³ πΏ Mobile: Software
- β³ β Mobile: Tasks
- β³ π Mobile: Forms
- β³ π Mobile: Contracts
- β³ π Mobile: Domains
- β³ π Mobile: People
- β³ π Mobile: Projects
- β³ ποΈ Mobile: Files
- β³ π Mobile: LMS
- β³ πΊοΈ Mobile: CMDB
- β³ πΊοΈ Mobile: Network Mapper
- β³ π§ Mobile: Process Mapper
- β³ βοΈ Mobile: Workflow
- β³ π₯οΈ Mobile: System
- β³ π Mobile: Reporting
- β³ π Mobile: System Wiki
- β³ π Mobile: Self-Service Portal
- β³ π§° Mobile: Techniques & Tricks
-
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
- β³ π‘οΈ CSRF protection (S4) β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- π CardDAV contact sync
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π Proposal: custom fields, inventory import and change evidence (#194)
- β³ π οΈ Custom fields, evidence + inventory - review (#195-#197)
- β³ π οΈ Long forms in a task's Work view - review (#198)
- β³ π« 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: Domains
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ π·οΈ REST API: Cost centres
- β³ π§βπΌ REST API: Analysts
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ π οΈ Inbox.js map β Developer Guide
- β³ π οΈ Tickets settings β Developer Guide
- β³ π Ticket workspace - design (PR #206)
- β³ π Rota copy and paste β Developer Deep Dive
- β³ β Checklists & SOPs
- β³ βοΈ Mandatory fields
- β³ π§Ύ Custom ticket fields
- β³ π·οΈ Ticket categories
- β³ π₯ Assigning tickets to a team, and escalation
- β³ π’ One board across every company
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ π Attachment size limits and large emails
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
-
β³
βοΈ Telegram channel - β³ β CSAT company scope and filters β Developer Guide
- β³ π₯ Microsoft Teams channel
- β³ π¨οΈ Mattermost channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ β Record previews
- β³ π Ticket notes: internal or shared
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ π¨ Telling the analyst a ticket is theirs
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ π Confidential tickets
- β³ π₯ Portal managers
- β³ π Who has seen a ticket
- β³ π Reading long tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π½ Just my tickets, or no closed ones
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Case reports - a ticket and its work as one PDF
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
- π Projects
-
Assets
- β³ π’ Moving an asset between companies
- β³ π Shared asset locations
- β³ π§βπΌ Assigning assets to analysts
- β³ π Warranty and lease alerts
- β³ π Leases
- β³ π Saved table views
- β³ π¨οΈ Recording anything, and importing it
- β³ π§© Asset type layouts
- β³ π Inventory freshness
- β³ π Connected assets
- β³ π Asset review
- β³ π·οΈ QR asset labels
- β³ π Who holds what, and handover documents
- β³ π₯οΈ The inventory agent (PowerShell)
- β³ ποΈ Proxmox VE servers
- β³ βοΈ VMware Cloud Director servers
- β³ π Linking equipment to tickets
- β³ βοΈ Follow-up tasks on a ticket
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
-
Forms
- β³ π¨ The form designer β Developer Guide
- β³ π Layout & the grid β Developer Guide
- β³ ποΈ Collections β grouping submissions
- β³ π Submissions as PDFs
- β³ β‘ What happens next β a form's own actions
- β³ π οΈ Sections & conditional logic β Developer Guide
- β³ π οΈ Lookup fields β Developer Guide
- β³ π‘οΈ Catalogue request approvals
- People
- Domains
- ποΈ Files
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- π‘ Zabbix (monitoring)
- π¬ Microsoft Teams alerts
- π 3CX (phone system)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ π’ One board across every company
- β³ 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)
- What this is
-
π Bugs resolved
- β³ π³ The Docker image was missing PHP extensions
- β³ π A failed DNS lookup read as a missing record
- β³ π A link could change data
- β³ π Access outlived its removal
- β³ π Knowledge articles could run scripts
- β³ π Knowledge writes ignored permissions
- β³ π Company boundaries (Oct 2026)
- β³ π Smaller gaps (Oct 2026)
- β³ π Escaped text could leave its attribute
- β³ π Task descriptions were not cleaned
- β³ π Task documents ignored company scope
- β³ π Repeating tasks ignored company scope
- β³ π¬ #183 Mailbox checks overlapped
- β³ π #188 Transfer lock shown off when unknown
- β³ β #184 Subtask names and Overdue
- β³ π§© #186 Teams: the buttons did nothing
- β³ π Text in a change could run script
- β³ π AI settings changed without the AI permission
- β³ πͺ #178 Deleting an analyst failed
- β³ π« A team with no departments showed 0 tickets
- β³ π Projects: adding a phase did nothing on Save
- β³ π Leavers could still sign in
- β³ πΌοΈ Logo and courses broke on Apache with PHP-FPM
- β³ π’ Chat tickets ignored your ticket numbering
- β³ π Dates shown as a dash, or in server time
- β³ π Assets β Users showed people from other companies
- β³ π Restricted analysts could read other modules' data
- β³ πΌοΈ Replies with a picture in the thread failed to send
- β³ π Reply attachments never reached the customer
- β³ π οΈ Outbound email attachments β Developer Guide
- β³ π A global SSO provider was missing from the portal
- β³ π Behind a proxy, the SSO redirect said http
- β³ βοΈ The portal tagline moved when you saved it
- β³ π¨ The portal settings screen forgot what you saved
- β³ π‘οΈ The approvals inbox said "Error" and nothing else
- β³ π A table's answers were missing from the PDF
- β³ β A single-select column let you tick every option
- β³ π The portal ignored a form's field widths
- β³ π The tasks board stopped taking clicks
- β³ ποΈ #121 The index list is out of date after upgrading
- β³ π #133 The calendar subscription was empty
- β³ π #131 Tasks always reopened on the board
- β³ π₯ #129 Every page returned HTTP 500 after upgrading
- β³ π³ #127 A PHP warning above the System page
- β³ π #126 Notes stamped with the server's clock
- β³ π Storing every date in UTC
- β³ πͺ The portal was down for everyone signed in
- β³ βοΈ #120 Workflow notes could never be written
- β³ βοΈ #123 Three errors when running Database Verification
- β³ π #122 The description box was a stub in the corner
- β³ π£ Demo data deleted real accounts
- β³ π #117 Sign-in redirected to the wrong address
- β³ π¨ #108 The priority dot was invisible
- β³ β±οΈ #116 Time logged from the right-click menu
- β³ π #114 API keys refused by our own guard
- β³ ποΈ #110 Assigning a task told nobody
- β³ πͺ #107 Signed out while still working
- β³ π #103 "Share with Requester" reached nobody
- β³ π #102 Search found nothing for hyphens
- β³ πͺ #101 Source code editor opened behind
- β³ βοΈ #88 Subtasks could not be ticked off
- β³ π» #84 Asset deep link selected nothing
- β³ π« #79 A new ticket arrived with no status
- β³ π§ #79 A ticket from email did not say so
- β³ π #78 Bell opened to nothing
- β³ π¬ #77 Mail only collected from Inbox
- β³ π #74 The default password could not be changed
- β³ π¦ #70 Renaming an impact level
- β³ π€ #67 App-only mailboxes could not send
- β³ π #45 Verify only ever worked for Microsoft
- β³ π #45 IMAP reported as not authenticated
- β³ βοΈ An email template stopped escaping itself
- β³ π The portal dashboard showed the wrong time
- β³ π’ The folder said 99 and the list showed 96