Architecture - edmozley/freeitsm GitHub Wiki
FreeITSM is a classic LAMP-style web application: PHP server-side with vanilla JS/HTML/CSS on the client. No frameworks, no build step. All 18 modules share a common chrome (waffle menu, header, user account dropdown) but each module is self-contained under its own folder.
| Component | Technology |
|---|---|
| Backend | PHP 7.4โ8.4 |
| Database | MySQL 8.0+ via PDO MySQL |
| Frontend | Vanilla JavaScript, HTML5, CSS3 |
| Rich text editing | TinyMCE 6+ |
| Email integration | Microsoft Graph API + Gmail API (OAuth 2.0) |
| Encryption at rest | AES-256-GCM for sensitive values |
| AI features | Pluggable providers โ Anthropic Claude / OpenAI / OpenRouter, per-feature keys (AI Providers) + OpenAI embeddings (Knowledge) |
| Web server | Apache (WAMP/XAMPP/LAMP) or any PHP server |
freeitsm/
โโโ config.php # References external db_config.php
โโโ index.php # Module selection grid (landing page)
โโโ .htaccess # URL routing: /login canonical, legacy redirects, MultiViews off โ see Root-Folder-Tidy
โ
โโโ auth/ # Auth pages (served via root .htaccess rewrites โ see Root-Folder-Tidy)
โ โโโ login.php # Analyst login โ canonical URL /login
โ โโโ analyst_logout.php # Logout handler (also aliased as /logout)
โ โโโ force_password_change.php # Forced password change (expired passwords)
โ โโโ forgot-password.php # Password reset request โ /forgot-password
โ โโโ reset-password.php # Password reset (token from email) โ /reset-password
โ โโโ oauth_callback.php # Microsoft OAuth 2.0 callback (URL unchanged: /oauth_callback.php)
โ โโโ google_oauth_callback.php # Google OAuth 2.0 callback (URL unchanged: /google_oauth_callback.php)
โ
โโโ api/ # ~140 REST endpoints, one folder per module
โ โโโ v1/ # Public REST API v1 (API-key auth) โ see the REST API pages
โ
โโโ assets/
โ โโโ css/ # Shared stylesheets โ inbox.css (core layout), theme.css (palette tokens), per-module css
โ โโโ js/ # Shared scripts โ inbox.js, i18n.js, tz.js, toast.js, safe-html.js, qrcode.min.js (MFA QR codes), โฆ + TinyMCE library
โ โโโ images/ # CompanyLogo.png (replace with your own)
โ
โโโ includes/ # Shared PHP components
โ โโโ functions.php # connectToDatabase() + analyst module helpers
โ โโโ waffle-menu.php # Cross-module nav + user account menu
โ โโโ encryption.php # AES-256-GCM helpers
โ โโโ totp.php # Pure-PHP TOTP / HOTP (RFC 6238 / 4226)
โ โโโ i18n.php # Translation helper โ see the Internationalisation page
โ โโโ timezone.php # Per-analyst display timezone โ see Timezones & Time Handling
โ โโโ theme.php # Palette registry โ see Theming & Dark Mode
โ โโโ rbac.php # Capability guards โ see Roles & Permissions
โ โโโ tenancy.php # Company scoping โ see Multi-Tenancy
โ โโโ gmail.php / mailbox_graph.php / mailbox_imap.php # Mailbox providers (Gmail API / MS Graph / basic IMAP-SMTP)
โ โโโ template_email.php # Automated email templates for ticket events
โ โโโ module-colors.php # Module colour definitions
โ โโโ services/ # Shared service layer โ see Service Layer Architecture
โ
โโโ lang/ # Translations, one folder per locale
โ
โโโ tickets/ # Ticket Management module
โ โโโ index.php # Three-panel inbox interface
โ โโโ users.php # User directory & their tickets
โ โโโ calendar.php # Ticket scheduling calendar
โ โโโ rota.php # Staff rota weekly grid
โ โโโ csat/ # CSAT analytics (index.php) + the public survey page (survey.php โ canonical URL /csat)
โ โโโ settings/ # Departments, types, origins, statuses, priorities, SLA, mailboxes, rota shifts
โ โโโ attachments/ # Email attachment storage (gitignored)
โ
โโโ asset-management/ # Asset Management module
โ โโโ dashboard/ # Per-analyst widget dashboard (Chart.js)
โ โโโ servers/ # vCenter VM inventory with detail modal
โ โโโ settings/ # vCenter connection settings
โ
โโโ knowledge/ # Knowledge Base โ index.php (article list & editor), review/ (review workflow), settings/
โโโ change-management/ # Changes โ index.php, calendar.php (scheduled changes), approvals.php, settings/
โโโ calendar/ # Calendar โ index.php (full calendar view), settings/ (event categories)
โโโ morning-checks/ # Morning Checks โ daily check interface with PDF export
โโโ reporting/ # Reporting โ logs/ (logins, email imports), tickets/ (ticket dashboards)
โโโ software/ # Software โ inventory, dashboard/ (widgets + library.php), licences/, settings/ (API keys)
โโโ forms/ # Forms โ edit/ (form editor), fill.php (A4-style filler), submissions.php, settings/
โโโ lms/ # LMS โ index.php (dashboard), player.php (SCORM player), content/ (uploaded packages)
โโโ process-mapper/ # Process Mapper โ visual flowchart editor
โโโ network-mapper/ # Network Mapper โ index.php (diagrams landing), diagram.php (per-diagram editor)
โโโ tasks/ ยท contracts/ ยท cmdb/ ยท problem-management/ ยท workflow/ # Further modules, same shape
โโโ service-status/ ยท system-wiki/ ยท watchtower/ ยท self-service/ # (index.php + settings/ + includes/)
โ
โโโ system/ # System module (administrators only)
โ โโโ analysts/ # Analyst accounts (passwords, SSO, teams, per-company access, admin role)
โ โโโ teams/ # Team management (used across tickets, tasks, contracts, workflows)
โ โโโ modules/ # Analyst module access control
โ โโโ encryption/ # Encryption key management
โ
โโโ browser-extension/ # Chrome/Edge Watchtower extension (Manifest V3) โ see Browser Extension
โโโ cron/ # Scheduled jobs (SLA breach checks, webhook deliveries, scheduled workflows)
โโโ scripts/ # CLI utilities + background workers (Intune sync workers, asset inventory agent)
โโโ database/
โ โโโ freeitsm.sql # Schema bootstrap
โโโ docs/ # Design docs (e.g. cmdb.md)
โโโ setup/ # Setup verification / diagnostic page (delete after going live)
Each module folder typically contains:
-
index.phpโ landing page -
settings/index.phpโ module config -
help.phpโ in-app guided help page (scroll-spy sidebar, sectioned content) -
includes/header.phpโ module's header bar with its colour gradient and nav tabs
A Microsoft 365-style app launcher in the header of every module page. Each module is registered with a name, path, icon SVG, and colour gradient. Respects $_SESSION['allowed_modules'] to filter visible modules per analyst.
Also contains the user account menu โ an initials avatar circle in the top-right of every page. Clicking opens a dropdown with:
- Change Password (validates current, min 8 chars)
- Multi-Factor Authentication (TOTP setup/disable with QR code)
- Logout (with confirmation)
To add a new module, add an entry to the $modules array and matching CSS.
Pure-PHP implementation of RFC 6238 (TOTP) and RFC 4226 (HOTP). No external dependencies โ uses PHP's built-in hash_hmac() and random_bytes().
- Secret generation: 20 random bytes โ Base32 (32-char string)
- Code generation: HMAC-SHA1, 30-second time step, dynamic truncation โ 6-digit code
-
Verification: ยฑ1 time window (90-second tolerance),
hash_equals()for timing-safe compare -
URI format:
otpauth://totp/FreeITSM:{username}?secret={base32}&issuer=FreeITSM
Secrets are encrypted at rest with AES-256-GCM before being stored in analysts.totp_secret.
AES-256-GCM authenticated encryption for sensitive database values.
-
Key file:
C:\wamp64\encryption_keys\sdtickets.key(outside web root) -
Format:
ENC:+ base64(IV + auth tag + ciphertext) -
Migration-safe: Values without
ENC:prefix pass through unchanged
$encrypted = encryptValue($plaintext);
$plaintext = decryptValue($encrypted);
$mailbox = decryptMailboxRow($mailbox);Encrypted columns include:
-
system_settings:vcenter_*,knowledge_ai_api_key,knowledge_openai_api_key,intune_* -
target_mailboxes:azure_tenant_id,azure_client_id,azure_client_secret,oauth_redirect_uri,imap_server,target_mailbox, plusimap_username,imap_passwordandsmtp_serverfor basic IMAP/SMTP mailboxes analysts.totp_secret
The canonical lists live in the ENCRYPTED_SETTING_KEYS and ENCRYPTED_MAILBOX_COLUMNS constants in includes/encryption.php โ a system_settings key or mailbox column is only encrypted/decrypted if it appears there.
A subset of "true secrets" listed in MASKED_SETTING_KEYS are also masked to ****<last4> in API responses, with a save-time convention that blank or asterisk-prefixed submissions mean "leave unchanged" so the user can save the form without re-typing.
Each module's includes/header.php:
- Checks session auth (redirects to login if missing)
- Sets
$current_modulefor waffle-menu highlighting - Renders the header bar with the module's colour gradient
- Includes the waffle button, module nav tabs, and user account avatar
Every module page follows the same skeleton โ set $current_page and $path_prefix before the markup, pull in the shared stylesheet and the module header, and define an API_BASE constant for the page's JavaScript:
<?php
session_start();
require_once '../config.php';
$current_page = 'module_name';
$path_prefix = '../';
?>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../assets/css/inbox.css">
<!-- Module-specific styles in a <style> block -->
</head>
<body>
<?php include 'includes/header.php'; ?>
<div class="main-container module-container">
<!-- Module content -->
</div>
<script>
const API_BASE = '../api/module_name/';
// Module JavaScript
</script>
</body>
</html>- Create the module folder with
index.phpandincludes/header.php - Create an API folder under
api/ - Register it in
includes/waffle-menu.php(add to the$modulesarray + CSS colours) - Add a card to the
index.phplanding page (icon, colour, link) - Add any new tables to
database/freeitsm.sqland the Database Verification schema
Global notification system. Four types: success (green), error (red), warning (amber), info (blue). 9 configurable screen positions via visual grid picker in System Settings โ General. Position persisted per-browser in localStorage. Slide-in animations, auto-dismiss after 4s, manual close button.
Native multi-language support with a t('namespace.path.to.key') call pattern in both PHP and JavaScript. The first dot-separated segment of the key maps to a file (lang/<locale>/<namespace>.php); everything after walks a nested PHP array inside that file.
return [
'toolbar' => [
'process' => 'Prozess',
'decision' => 'Entscheidung',
],
];Fallback is per key, not per file. If lang/de/tickets.php has 80% of keys translated, you get 80% in German and the missing 20% in English. Last-resort behaviour returns the key itself so unfilled strings are visible during development.
Locale resolution chain: logged-in analyst's interface_language user preference โ browser Accept-Language header (with primary-subtag matching โ pt resolves to pt-BR) โ 'en' default. Selectable in System โ Preferences; on change, persists to user_preferences and reloads the page.
JS bridge: each page declares the namespaces it needs ($translationNamespaces = ['common', 'process-mapper']) and PHP renders window.translations with English fallback already merged into the active locale per key. The JS t() helper does a flat dotted walk โ no fallback logic on the client.
Supported locales (20 as of May 2026): en, af, fr, de, es, pt-BR, nl, it, pl, ru, id, hi, bn, ta, te, mr, kn, ml, gu, pa. BCP 47 codes match the HTML lang attribute. Spans Europe (en/fr/de/es/pt-BR/nl/it/pl/ru), South Africa (af โ Afrikaans), South Asia (the nine Indian languages), and Indonesia (id โ Bahasa Indonesia). Adding a new language is a 2-step code change: add to I18n::SUPPORTED_LOCALES (with native-script display name), and drop .php translation files into lang/<code>/. The System โ Preferences dropdown picks the new locale up automatically.
Translated modules: Process Mapper was the pilot; Tickets and Tasks are now fully translated end-to-end (PHP pages and JavaScript) across all 20 locales. Remaining modules follow in phased sweeps โ the infrastructure is the hard part and it's done.
-
MySQL 8.0+ with
AUTO_INCREMENTfor primary keys -
$conn->lastInsertId()to retrieve new IDs after INSERT -
Foreign keys with cascading where ownership is real (e.g.
cmdb_objects.parent_idcascade-deletes descendants) โ but delete endpoints also remove children explicitly rather than trusting cascades, because installs grown via Database Verification may lack backfilled FKs. Full story, delete-rule conventions, orphan-hunting SQL and the schema-change checklist: Database Integrity -
Soft delete via
is_activeflags rather than physical deletion for user-facing records -
Datetime columns:
created_datetime,last_modified_datetime, etc. (PHP-side defaults)
All endpoints live under api/<module>/ and return JSON. Every endpoint starts with:
session_start();
require_once '../../config.php';
require_once '../../includes/functions.php';
header('Content-Type: application/json');
if (!isset($_SESSION['analyst_id'])) {
echo json_encode(['success' => false, 'error' => 'Not authenticated']);
exit;
}See API Reference for a per-module endpoint summary.
Business logic belongs in a shared service, not in the endpoint. These UI endpoints and their REST API v1 counterparts are thin adapters over a per-module service class, so a rule is written once and can't drift between the UI and the API. If you add a feature with both a UI action and an API endpoint, read Service Layer โ one implementation, two interfaces first (rollout status: progress tracker).
AI features share a single provider-agnostic building block โ drop renderAiSettingsPanel('<ns>') on a settings page and call aiProviderChat() in the backend. Each feature is configured independently with its own provider (Anthropic / OpenAI / OpenRouter), model and key, kept separate for granular billing visibility:
| Feature | Namespace | Settings location |
|---|---|---|
| Knowledge AI chat | knowledge_ai |
Knowledge โ Settings โ AI |
| Reply Cleanup | tickets_reply_cleanup |
Tickets โ Settings โ Reply Cleanup |
| Form generation | forms_ai |
Forms โ Settings โ AI |
| CMDB AI summary + Suggest Properties | cmdb_ai |
CMDB โ Settings โ AI Integration |
| Workflow co-author | workflow_ai |
Workflows โ Settings โ AI |
| RFP Builder | (deferred) | Contracts โ Settings โ RFP AI |
All keys are encrypted at rest. Most AI features stream output via SSE (claude.ai-style live tokens) for long-running calls. OpenRouter adds a single key reaching hundreds of models across vendors. See AI Providers for the full reference.
| Need to... | Look in... |
|---|---|
| Add a new module | Module folder + api/ + includes/waffle-menu.php + index.php
|
| Change the database connection |
db_config.php (outside the web root, referenced from config.php), includes/functions.php
|
| Add an encrypted setting |
includes/encryption.php โ ENCRYPTED_SETTING_KEYS or ENCRYPTED_MAILBOX_COLUMNS
|
| Modify cross-module navigation | includes/waffle-menu.php |
| Change the landing page | index.php |
| Modify the ticket inbox |
tickets/index.php, assets/js/inbox.js
|
| Configure vCenter |
asset-management/settings/, api/assets/get_vcenter.php
|
| Manage knowledge AI |
knowledge/settings/, api/knowledge/ai_chat.php
|
| Design forms |
forms/index.php, api/forms/save_form.php
|
| View form submissions |
forms/submissions.php, api/forms/get_submissions.php
|
| Manage the encryption key |
system/encryption/, api/system/check_encryption.php
|
| Configure module access |
system/modules/, api/system/save_analyst_modules.php
|
| Account menu (avatar / password / MFA) |
includes/waffle-menu.php โ renderHeaderRight(), api/myaccount/
|
| MFA login challenge |
auth/login.php (canonical URL /login), api/myaccount/verify_login_otp.php
|
| TOTP implementation | includes/totp.php |