Repository navigation
Approvals and Evidence Design
Status: proposal, not built. This page and its three parts set out what I plan to change, why, and how, so the logic can be checked before any code is written. Nothing described here exists yet unless it says today. Comments are welcome on issue #180.
The request, from issue #180 (raised by Dr4inder after evaluating 3.7.0 for a small IT team):
"Three functional gaps currently prevent us from using FreeITSM as the sole record of these decisions and their supporting evidence."
- A configured change approval should be enforceable. Today an analyst can set a change straight to Approved without the approval having happened.
- The long text on a change should keep its history. Today a test plan or rollback plan can be overwritten, and the previous text is gone.
- Approving, reviewing and configuring should be separate permissions. Today a Forms approver can also redesign the form, and the only way to let somebody read change records is to let them edit them too. The issue puts this in terms of roles: it was not possible to configure "an approval-only role" or "a useful read-only reviewer role".
The three parts:
| Part | Page | In one line |
|---|---|---|
| 1 | Enforceable change approvals | Approval becomes a recorded event tied to the exact content that was reviewed, and, when switched on, a rule the status cannot get past |
| 2 | Change text history | Every saved version of the eight long-text fields is kept in full, with who and when, in the same transaction as the save |
| 3 | Approver and reviewer access | Roles gain everyday-work permissions (design forms, choose approvers), module access gains a Read only level, and an approver can approve without being given the module |
Related pages: Change Management Β· REST API: Changes Β· Catalogue Request Approvals Β· Roles & Permissions Β· Module Access Control Β· Service Layer
FreeITSM has real installs, and plenty of teams use Change Management happily as a lightweight record. Some of what #180 asks for would be disruptive if it were simply switched on: a team of two cannot have "nobody may approve their own change", and a team that edits the test plan after approval would suddenly find its changes sent back for re-approval.
So every rule that takes something away from somebody is a setting, and the default is today's behaviour. Upgrading changes nothing anybody can see, apart from the bug fixes in Β§3 and some new history that starts being kept.
What does happen on upgrade, quietly: records start being kept. Approval rounds and text revisions are recorded from the first day, whichever settings are chosen. A team that switches enforcement on six months later then has six months of evidence already, rather than none.
| Where | Setting | Choices | Default (= today) | Part |
|---|---|---|---|---|
| Change Management β Settings β Approvals (new tab) | Approval rules | Record approvals / Enforce approvals | Record | 1 |
| β³ same tab | Approving your own change | Not allowed / Allowed | Not allowed - in Record mode this only flags the approval as not independent; it refuses nothing | 1 |
| β³ same tab | Who counts as the change's own people | ticks: Requester, Assigned to, Created by | all three ticked | 1 |
| β³ same tab | Administrator override | Off / On, with a written reason | Off | 1 |
| β³ same tab | Edits that need a fresh approval | ticks, one per field | description, reason, risk evaluation, risk likelihood, risk impact, test plan, rollback plan, type, impact | 1 |
| β³ same tab | When a required CAB member rejects | a status (Draft-stage or Stopped-stage only) | Draft | 1 |
| β³ same tab | Who can choose a change's approvers | Anyone with Change Management / Only people with "Choose approvers" | Anyone | 1 |
| Change Management β Settings β Statuses | Stage of each status | Draft Β· Awaiting approval Β· Approved Β· Implementing Β· Done Β· Stopped | filled in from today's names on upgrade | 1 |
| Change Management β Settings β Types | How it is approved | Needs review / Pre-authorised / Emergency - approve afterwards | Needs review, for every existing type | 1 |
| Forms β Settings β Access (new tab) | Who can design forms | Everyone with Forms / Only people with "Design forms" | Everyone | 3 |
| System β Modules | Access level of each module grant | Full / Read only (only on modules that support it - Changes and Forms first) | Full | 3 |
All of them are dropdowns and tick boxes. None of them is typed.
The new tab is guarded by a new capability, changes.approvals, marked sensitive, because it governs the audit trail. Two new everyday-work permissions appear in System β Roles: Choose approvers (Change Management) and Design forms (Forms). How Roles learns to hold permissions that are not settings tabs is in Part 3 Β§2.
Reading the code for this turned up faults that are worth fixing whatever is decided about the settings. Some are bugs that would bite anybody, today.
| # | Found | Effect today | Fixed by |
|---|---|---|---|
| F1 | A manual move to Approved does not stamp changes.approval_datetime; only a CAB vote does |
Watchtower counts a hand-approved change as awaiting approval for ever, and a Projects gate item "Change approved" never ticks for it | Part 1 - every approval stamps it |
| F2 | CAB votes are kept on the member row across resubmissions | After a required member rejects and the change is resubmitted, that old Reject is still counted, so the next vote by anybody sends it straight back to Draft. The only way out is to remove and re-add the member | Part 1 - votes belong to a round; a new round starts clean |
| F3 | The code finds Approved, Pending Approval and Draft by name (ChangesService, api/change-management/get_approvals.php, assets/js/change-management.js), but names are editable in Settings |
Rename Approved to Authorised and the CAB's deciding vote sets the change's status to nothing (status_id = NULL - the name lookup finds no row); rename Pending Approval and votes never transition at all, and the Approvals page goes empty |
Part 1 - a stage column |
| F4 | Anybody with Change Management can edit a CAB roster at any time, including after votes, including on their own change | Somebody can add themselves as the only required member and approve their own change by vote, without touching the status | Part 1 - roster locked outside Draft; independence |
| F5 | No beginTransaction anywhere in includes/services/changes.php
|
A vote, its audit row and the resulting status change are three separate writes; two last votes landing together can both tip the threshold and fire change.approved twice |
Part 1 / 2 - one transaction, row lock |
| F6 | Two security issues in code this work touches | Details withheld until they are fixed | Separately, as a patch - see Β§5 |
| F7 |
api/v1 never consults the acting analyst's module access |
A key keeps working for a module its analyst has since lost. Only administrators create keys, so this is a decision rather than a hole - but it matters for read-only access | Part 3 - service-level backstop |
| F8 | Nothing is dispatched when a change goes to Pending Approval | The help page says "reviewers are notified to vote"; nothing notifies them, and no workflow could, because there is no event to hang it on | Part 1 - new events |
| F9 | Attachment upload/delete and comment delete write nothing to change_audit
|
The help page tells people to attach "approval emails" as evidence; deleting one leaves no trace | Part 2 |
| Where | Says | Reality |
|---|---|---|
| In-app help, Quick tips | "Every field change is automatically tracked" | The eight long-text fields are deliberately not audited (ChangesService::updateChange, the comment "Longtext bodies - updated but NOT audited") |
| In-app help, Lifecycle | "If CAB is enabled, reviewers are notified to vote" | No notification exists (F8) |
| In-app help, Lifecycle | "Approver or CAB signs off" | The named Approver has no action; only the CAB can approve |
| In-app help, Change types | Standard = "Pre-approved⦠No CAB review needed"; Emergency = "approved after the fact" | A type has no behaviour at all; nothing in the code branches on it |
| Change Management wiki, Post-Implementation Review | "These are mandatory before a change can move to a fully-closed state" | Nothing enforces it |
| Module Access Control, Enforcement | "read-only endpoints are not yet individually gated" | Out of date since 2.10.2, when reads were gated (Module-Access-Reads-Were-Open) |
Each of these gets corrected in the same commit as the code that makes it true, or now if nothing is going to change.
Each slice ships on its own and leaves the product working.
| Slice | What | Setting needed? | Release |
|---|---|---|---|
| 0 | F6 (the two security fixes); F1 (stamp approval on a manual move); F2 (clear votes when a change is resubmitted); the documentation fixes in Β§3 | No - bug fixes | 3.7.1 patch, now (F6 is security) |
| A | Status stages - replaces every name literal (F3) | No - filled in on upgrade | 3.8.0 |
| B | Part 2: text revisions, transactions (F5), attachment and comment audit (F9) | No - records only | 3.8.0 |
| C | Part 1 in Record mode: approval rounds, snapshots, the approver's own Approve/Reject, new events (F8), the Approvals tab | No - Record is the default | 3.8.0 |
| D | Part 1 Enforce mode, independence, routes per type, override | Yes - off by default | 3.8.0 |
| E | Part 3: Roles everyday-work permissions, Design forms, the approver's own way into Forms | Yes - off by default | 3.9.0 |
| F | Part 3: Read only module level for Changes and Forms | Yes - nobody has it until given it | 3.9.0 |
Slice B comes before C on purpose: an approval has to be able to point at the exact revision it approved, so the revisions have to exist first.
- Split #180 into three issues, as the requester offered? I'd say yes. The parts are independent and each needs its own changelog trail and reply.
- F6 now? Security fixes, so under the release rule they are a same-day patch, separate from all of this.
- "Own change" - requester, assigned-to and created-by all ticked by default. Fewer?
- Administrator override - off by default. On a two-person team the administrator may be the only independent person; the setting exists for them.
- Record mode and edits after approval - I propose the approval is kept but shown as "approved against an earlier version", so Record mode really changes nothing. The alternative is to clear it, which would put the change back on Watchtower as awaiting approval. See Part 1 Β§6.
-
Standard changes - pre-authorised by type now. The schema already has an unused
change_templatestable; a proper catalogue of pre-approved templates is the better long-term shape, as a later step. See Part 1 Β§8. - Emergency changes - may they reach Done while the retrospective approval is still open? I propose yes, with the change flagged on Watchtower until it is decided.
- Deleting a change wipes its audit trail today. With approval records in play, should Enforce mode refuse to delete a change that has ever been approved? Not asked for in #180; it is the obvious next gap.
- Read only - Changes and Forms first, because they are what #180 evaluated. Which modules next?
- Report Pack blocks for changes or forms. The issue asks that a reviewer can read "corresponding reports/exports". Report Packs have no Change or Forms blocks today, so the reviewer's reports are the Changes table view with its CSV export, the new evidence export (Part 2 Β§6) and the Forms submissions export. New blocks are a separate piece of work.
- Reconstructing history that was never recorded. Text edited before the upgrade cannot be recovered. The first revision of each field is marked as "as found when history began", and is never presented as the original (Part 2 Β§4).
- Multi-stage approval chains (manager, then CAB, then change manager). One round, decided by a CAB or by one named approver, is what exists today and what #180 asks to enforce.
- Approval by people who are not analysts. Approvers are analysts, as today.
- Part 1 - Enforceable change approvals
- Part 2 - Change text history
- Part 3 - Approver and reviewer access
- Change Management Β· REST API: Changes Β· Roles & Permissions Β· Roles - Developer Guide Β· Module Access Control Β· Catalogue Request Approvals - Developer Guide Β· Service Layer Β· Projects: governance
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
- π§ͺ Developer tests
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- 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
- β³ π« 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
- β³ π Rota copy and paste β Developer Deep Dive
- β³ β Checklists & SOPs
- β³ βοΈ Mandatory 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
- β³ π 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
- β³ π·οΈ 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)
- 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
- β³ πͺ #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