Repository navigation
Contributing
Pull requests are welcome, and several of FreeITSM's features started as one. This page is the checklist I review against. Every point on it is here because a real pull request (or my own code) got it wrong, so it is worth a read before you start - and worth giving to your AI coding assistant.
Each review is written up in full, so you can see the reasoning: Long forms in a task's Work view (#198) Β· REST custom fields, evidence and inventory (#195-#197) Β· Asset reconciliation, tags and labels (#164) Β· SSO JIT roles and profile sync (#155) Β· Checklists closure gating.
Every pull request gets one of three verdicts:
- Merge - it goes in as it is.
- Merge with fixes - your commit goes in under your name, and my fixes follow in a commit of my own. A review page like the ones above says what changed and why.
- Rewrite - the idea is right but the code would be risky to adapt. I write it myself, credit you, and close the PR.
Nothing is merged without being read line by line and run. That includes tests, which run against a real database.
- Agree the design first. For anything bigger than a fix, open an issue. I write a design on the wiki, and the code follows it - including the names of fields, parameters and permissions. If the code needs to differ, say so in the PR so the design can be updated.
-
Base it on current
main, and keep each PR to one feature. Three small PRs are far easier to review than one big one. - Upgrades must be invisible. Somebody upgrading should see no change in behaviour until they choose a new option. Real installs - including MSPs running many client companies - depend on this.
-
Nothing runs from a URL unless it is meant to. Every file in
tests/andscripts/starts withif (PHP_SAPI !== 'cli') { http_response_code(404); exit; }. CI fails without it (tests/web-exposure-guard.php). Files inincludes/only define functions and classes - opening one in a browser must do nothing. - Check access before you write anything - module access, write access (Read only analysts change nothing), the company (multi-company installs), and for the REST API the key's permission. Before a file reaches the disk, not after.
-
A REST permission does one job. Add a narrow resource or action (
change_attachments.create) rather than borrowing a broad one (changes.update). The verbs areread,create,update,delete(andmanagefor configuration). - Session endpoints check the CSRF token, and error messages never include database or exception text.
-
Schema goes in two places by hand, and one by script:
database/freeitsm.sql(new installs) andincludes/db_verify_schema.php(Database Verification, existing installs). Then runphp scripts/gen_db_verify_indexes.php-includes/db_verify_indexes.phpis generated, and CI fails if it is edited by hand. - Verification adds missing tables, columns and indexes - and nothing else. It does not widen a column, change a unique key or move data. If an existing install needs that, write an upgrade step that is safe to run twice and keeps every row.
-
Every column that names an analyst goes in
analystReferenceRegistry()(includes/analyst_lifecycle.php), with its kind (history,setup,live, ...).tests/analyst-lifecycle.phpfails otherwise.
-
One write path. If the UI already writes it, move that code into the service in
includes/services/and call it from both - never a second copy in the API. - Own the transaction, or join the caller's. Side effects - workflows, emails, calendar sync, notifications - run after the commit, never inside it.
- A failed lookup is not an answer. Never turn an error into "no", "empty" or "not needed". Let it fail, or report it as unknown.
- Never swallow an error to make a test pass, and never return 200 for something you ignored - say what was ignored.
-
Other people's rows are not yours. An importer touches only the rows its own source wrote. Before narrowing or widening a
DELETE, find every writer of that table and check who relied on the old behaviour.
The tests run against the developer's own database, which is usually a copy of real data. So:
-
Create what you need, with a
ZZ/zz_prefix, and delete it by the ids you created - never by name pattern or by "everything matching". A name can be truncated by a narrow column and never found again. - Never go through a path that sends email, fires workflows or notifies people unless that is the thing being tested. Write the fixture row directly.
-
Never change install settings. If you must, snapshot them and put them back in
finally. - Prove the test can fail. A control that fails on the old code is worth more than ten that pass.
-
PHP and Chrome only - no Node.js, no npm. Every test runs as
php tests/<name>.php. A test that needs a real browser drives Chrome over its DevTools socket from PHP: copycdpEvaluate()fromtests/task-form-embed.php(PR #198). - Clean up API keys' rate-limit rows (
api_key_rate_limits) along with the keys.
-
New strings go in
lang/enonly. The other locales are translated together before each release. -
t()has no fallback argument, and existing English wording stays as it is - changing it breaks every translation of it. - Buttons are one word where possible ("Save", "New", "Delete").
- Every operation is in
api/v1/spec.json, andphp api/v1/lib/openapi_check.phppasses. - Errors use
apiError()with a stable code (device_ref_conflict), and the message says what to do next. - Retries are safe: a
client_reference(or similar) returns the original for the same content and a 409 for different content.
- Match the file you are in. Keep the comments that explain why, and add a
TRAP:comment wherever the obvious change would be wrong (grep -rn "TRAP:"lists them all). - Documentation goes on this wiki, not in a
docs/folder in the repo.
php -l <every changed PHP file>
php scripts/gen_db_verify_indexes.php --check
php tests/web-exposure-guard.php
php api/v1/lib/openapi_check.php # if you touched the REST API
php tests/analyst-lifecycle.php # if you added a column that names an analyst
FREEITSM_TEST_URL=http://localhost/your-site php tests/<your-suite>.phpThen run Database Verification (System β Database) on an install that already had data, and check that nothing changed that should not have.
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
- β³ π 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
- β³ π 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
- β³ π 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