System - edmozley/freeitsm GitHub Wiki
Folder: system/
Colour: Blue-grey #546e7a
System administration and configuration β analyst accounts, teams, encryption keys, module access control, demo data, and global settings.
Unlike other modules, System has too many areas to fit the header navbar, so navigation lives entirely in a searchable grid of cards β one per area, the single way in to everything below; the header "System" title links back here from any sub-page. A search box filters the cards live by title, description and hidden keyword synonyms (typing oidc, saml or idp surfaces the Single Sign-On card). The areas come from one registry (system/includes/areas.php) that drives both the cards and the search, and stays fully i18n β titles, descriptions and keywords are all translation keys.
Searching for a setting inside a card. The search also matches the section headings inside each area: typing name shows the Branding card because it holds System name. A matching card shows a small count in its corner ("1 setting"); hovering over it, focusing it with the keyboard, or tapping the count on a phone opens a panel over the card listing the matching settings, each a link straight to that section (branding/#system-name). The panel floats over the grid, so cards never change height. Arriving by such a link scrolls the section into view and briefly rings its card (system/includes/header.php). Enter on a single remaining card opens it - or its one matching setting.
The headings are listed per card in systemAreaSettings() (system/includes/areas.php) by the same translation keys the pages print, so the search matches the page in every language, and each heading carries the matching id. When you add a section to a System page, add it there too - php tests/system-settings-search/run.php fails if a listed id is missing from its page, so a renamed heading cannot leave a dead link.
The first card on the page. One card for each of over 600 features across every module, with a star that lights once it is set up here, an explainer of what it is and why it is worth doing, a Take me there link, and Not for us for anything that will never apply. Search and filter by module, category and tier. See Feature Bingo.
Manage the analyst accounts that staff sign in with. Promoted out of Tickets β Settings because analysts are the app's users and work across every module, not just tickets. Each row supports:
- Create / edit / deactivate an analyst (username, full name, email, active flag)
- When someone leaves (3.7.0) β the bin icon opens a window that moves their open work to another analyst (or nobody), lists what is set up for them, and offers Deactivate, Anonymise or Delete (only for an account with no history). See When an analyst leaves
- Password reset
- Sign-in method β Follow team (the default for new analysts), local password, or a specific single sign-on provider; this is how existing analysts are migrated to SSO (see Single Sign-On). An analyst who follows their team but whose teams set different methods shows a Sign-in conflict badge; clicking it explains which team says what and the three ways to resolve it
- Team membership β pick which teams the analyst belongs to
- Per-company access (multi-company installs only) β an "Access all companies" toggle, or tick specific companies; the control is hidden at N=1 (see Staff access)
- Effective access display β the company chip and the edit dialog show the companies an analyst can actually reach, whether granted directly or inherited from a team; team-granted companies appear read-only here, with a note pointing to System β Teams
The endpoints stay under api/tickets/ β the move is UI-only, no data migration.
Manage the teams analysts belong to. Teams are a shared entity consumed by Tickets, Tasks, Contracts and the Workflow engine (assignment + access), so they live at the install level rather than inside one module's settings. Teams are global β no per-company scoping β so there's no company filter even on a multi-company install (see Settings rationale).
- Create / edit / deactivate a team, with live department + analyst counts
- Manage departments β pick which departments the team serves. A team's departments decide the department folders its members get in Tickets and what the folder numbers count (those departments, plus every ticket with no department, plus their own); they organise the folder list rather than lock tickets away. A team with members but no departments is flagged with a Check badge and a note at the top of the page (3.7.0), and when no departments exist yet the departments window points to Tickets β Settings β Departments
- Manage members β pick which analysts belong to the team
- Manage companies (multi-company installs only) β grant the team access to specific companies, or toggle Access all companies
- Each team row also carries a company-access chip summarising its grants at a glance
- Sign-in method β a default for members set to Follow team (not set, local password, or a provider); the row shows it as a chip, plus N sign-in conflicts when members' teams disagree. See Setting the method on a team and the developer guide
The departmentβteam and analystβteam links are many-to-many and editable from either side: here on the team side, from the analyst side on Analysts (above), and from the department side on Tickets β Settings β Departments (departments are a ticket concept and stay there). Every side writes the same join tables (department_teams / analyst_teams).
Team company access is additive β an analyst can reach a company if their own access or any team they belong to grants it (see Staff access). Team grants default to nothing (unlike the analyst all-access default), so existing teams never widen access on upgrade; access is enforced through the single getAccessibleTenantIds() choke-point in includes/tenancy.php, so it applies to every company-scoped surface at once. The team stays global β it isn't owned by a company, it just carries company grants (new table team_tenant_access + a teams.can_access_all_tenants flag).
Guided interface for managing the AES-256-GCM encryption key.
- Status card shows whether the key file is configured / missing / invalid, with a colour-coded indicator
-
One-click key generation β writes a fresh 256-bit key directly to
C:\wamp64\encryption_keys\sdtickets.key - Instructions on key placement, backup importance, and what data is encrypted
- No regenerate button β preventing accidental key destruction is more important than convenience. Replacing the key after data is encrypted means losing access to all encrypted values, so the UI is deliberately one-way
See Security for what's encrypted and how.
Organisation-wide logo and default header/footer text used by Network Mapper diagrams (and future PDF/PNG exporters).
-
System name (3.5.0, moved here from Tickets β Settings β General) - the first part of every page title, in the browser tab and bookmarks. Blank = Service Desk.
-
Link back to your site (3.5.0) - a small Back to β¦ link at the top of the self-service portal, the analyst screens or both, for people who reach FreeITSM from an intranet or website. Text, an http(s) address, and where to show it; leave both blank for none. On the analyst screens it shrinks to an arrow on narrower windows.
-
For developers: System β Branding β Developer Guide.
-
Logo upload supports PNG, JPG or SVG (2 MB cap, extension + mime whitelist); SVG is recommended for crisp print/export. Old logos are torn down before the new file is saved
-
Six header/footer slots (header left/centre/right, footer left/centre/right) accept free text mixed with template tokens β
{{logo}},{{title}},{{author}},{{version}},{{modified}}β resolved client-side at render time -
Sensible defaults are preloaded on a fresh install (logo top-left, title top-centre, author + version + modified along the bottom), with a per-page "Reset to defaults" button
-
Stored as seven
system_settingskey/value rows (branding_logo_path,branding_header_left, β¦,branding_footer_right) so no new table is needed; uploads land insystem/uploads/branding/(gitignored) -
The logo path is sanity-checked against disk on every read β a stale DB row pointing at a deleted file surfaces as "no logo" rather than 404ing every diagram open
A card on the same page decides the logo in the corner of the Files desktop, for everyone (each person can only change their own desktop colour):
- Logo - the company logo of the person signed in, or the organisation logo if their company has none, or No logo
- Position - Top right or Bottom right
- Saved with the rest of the page as two
system_settingsrows,files_logo_mode(company|none) andfiles_logo_position(top-right|bottom-right)
Each existing company can have a logo of its own: open it on System β Companies and use Choose under Logo (Remove takes it away). It is saved straight away, not with the company's Save. PNG, JPG, GIF or WebP up to 2 MB - no SVG, because it is served from FreeITSM's own address and SVG can carry script. It is stored under a random name FreeITSM chooses, in system/uploads/branding/companies/ and recorded in tenants.logo_path. Today it is used only by the Files desktop; a company without one falls back to the organisation logo above. See the Files developer guide.
Per-analyst module access control.
- Toggle matrix: analysts as rows, modules as columns
- "All Access" toggle per analyst β the default state, backward-compatible with installs that pre-date this feature
-
The System module sits outside module access β it is gated by the administrator flag (
analysts.is_admin) instead, so an administrator can always reach System β Modules to fix a misconfiguration (see Admin Access Control) - Auto-saves on toggle with debounced API calls and toast notifications
- Enforced on: the homepage cards and the waffle-menu nav so a restricted analyst won't even see modules they're not permitted to use
The model has since grown considerably β grants to teams as well as individuals (team_modules), a configurable most/least-permissive policy, an effective-access checker, and real server-side enforcement (page + JSON guards on ~230 write endpoints) β see Module Access Control for the full picture. The System module itself is now gated separately by the administrator flag rather than by module access β see Admin Access Control.
Grant a non-administrator the right to manage one specific module's settings β down to the individual settings tab β without making them a full System administrator: RBAC "Layer 2", sitting on top of module access. Fully documented on Roles & Permissions and the Roles β Developer Guide; the original design document lives in the repository at docs/design/rbac.md.
Manage keys for the public REST API (api/v1/) and explore it interactively. Key creation (the full fitsm_β¦ key is shown once and stored only as a SHA-256 hash in the api_keys table β separate from the legacy plaintext apikeys table used by api/external/), granular per-resource Γ per-action permissions, acts-as-analyst attribution, company scoping, expiry dates, rate-limit overrides and last-used tracking are all documented on REST API β How It Works; the built-in interactive documentation and live tester (system/api/docs.php) on REST API Documentation Page.
One-click import of realistic sample data across all modules β perfect for evaluation and demos.
Populates:
- Tickets (sample queue with attached emails)
- Assets (devices with disks, network, software, GPU, TPM)
- Knowledge articles
- Changes with risk + CAB votes
- Calendar events
- Morning checks (definitions + sample results)
- Contracts (with terms, suppliers, contacts)
- Services + incidents
- Software inventory + licences
- Forms (definitions + submissions)
- Tasks (with subtasks)
- Process Mapper flowcharts (auto-laid-out β JSON omits coords, server-side layered-DAG pass assigns x/y)
- CMDB β 8 classes, 39 objects, ~30 relationships across six verbs, demonstrating the synthesis layer end-to-end
- Analysts (demo password
demo1234) - End users (for ticket requesters)
Designed for fresh installations β each module can be imported once. Existing data is preserved.
The connections that write analysts' scheduled tickets and tasks into their own calendars - any number, Microsoft 365 and CalDAV servers side by side - each added, tested and deleted on its own. Each analyst picks one under Preferences β My work calendar. Changing a connection's type or server, or deleting it, asks first and resets only the analysts on that connection. Also on this page: whether a change made in a calendar may unschedule a ticket, the Microsoft notification address, who is on which connection, and the install-wide subscription-link policy. Full guide: Scheduled work in your own calendar.
Whether people in the self-service portal can see the tickets of the people they manage, and on what terms: the master switch (off until changed), whether each person's Manager field counts (direct reports or the whole chain), whether managers may reply or close, what they see of a confidential ticket (nothing by default, a stub, or everything), whether leavers' tickets stay visible (on), and who may set up management lines (administrators, or anyone who can edit people). Below the settings, every manager - searched and paged on the server - with how many people they can see, worked out as if managers were on, and warnings for managers who have left and department lines that match nobody. See Portal managers.
Which of their own contact details people may change under My Account in the self-service portal - job title, office, phone and mobile, all four until changed - and, off by default, whether people from a CardDAV address book (with write-back on) may change theirs too, their change going to the address book first. Department, employee ID, manager and email are shown as never offered, with the reason. See Contact details.
Per-analyst preferences page surfaced via the avatar menu (top-right). Replaces the older browser-local approach β every preference is now stored in user_preferences keyed by analyst, so settings follow the user across devices and browsers.
Consolidated panel covers:
- Toast position β visual 3Γ3 grid picker (top-left through bottom-right) and animation choice (slide / fade)
- Knowledge sidebar β collapsed / expanded default
- Process Mapper sidebar β collapsed / expanded default
- Morning Checks chart fill β plain vs gradient bar style (mirrored on Morning Checks β Settings β Chart)
- Interface language β the analyst's UI language (see Internationalisation)
- My work calendar β off, a subscribe link, or appointments written into their own calendar; with more than one calendar connection, Which calendar picks theirs (see Scheduled work in your own calendar)
-
Left panels β per-module control of whether a module's left panel stays pinned open (Always visible) or collapses to a thin 16px strip that expands on hover (Show on hover, freeing space for the main content). Covers every module that has a left panel β Knowledge, Process Mapper, Contracts, Calendar, Tasks, CMDB, Change Management, Asset Management and System Wiki β each stored as
<module>_sidebar_mode. Modules with their own settings page also expose the same toggle on a Left panel tab there, and the module header applies the choice on every page
Module-specific preferences (e.g. the Morning Checks chart split percentage, Forms editor tab memory) live in the same user_preferences table but are managed from each module's own UI rather than this central page.
In-app, step-by-step guides for every System area β written in plain English (System is English-only).
-
Searchable landing card (
system/help/index.php) β a card per System area, driven by the samesystem/includes/areas.phpregistry as the System landing, so the two stay in step - A dedicated help page per area β encryption, modules, database verification, module colours, branding, security, preferences, demo data, debug tools, companies, email routing test, and a flagship Single Sign-On guide
- The SSO guide spells out the two paths clearly: single-company (one IdP, three steps) vs multi-company / MSP (per-client domains + company-owned providers, the email-first router, the 0/1/2+ picker, what to collect from each client, break-glass safety)
- All pages share one indigo layout (
_init/_top/_bottom) with a left-nav + scroll-spy, matching the existing module help style - Discoverability: the System landing has a distinct Help & guides card (rendered directly so it doesn't recurse onto the help landing), and the Companies page carries a banner linking straight to the SSO guide
-
Help registry (
system/help/_registry.php) β one entry per guide, owning its hero, standfirst, section list and search synonyms. It drives three things at once so they cannot drift apart: the card on the landing grid, the guide's own left-hand nav (a page never restates its sections β it just names its slug), and the search index. Cards are produced by joining the registry tosystem/includes/areas.php, which crucially means an area with no guide yet gets no card β the gap is listed as "not yet documented" at the foot of the grid rather than a card linking to a page that doesn't exist -
Search β filters the guides live by title, description, keyword synonyms, standfirst and section headings, indexed by what you'd call the problem ("locked out", "can't see tickets", "not sending", "wrong company") as well as by feature name. A match on a section heading deep-links straight to that section of the guide. Press
/to jump to the box;?q=prefills it, so a search result can be linked to
A read-only, collapsible company-rooted tree that pulls the whole configuration into one screen so you can see how everything fits together.
- Each company expands to its:
- Mailboxes β with signed-in / active badges, and expandable address / kind / folder detail
- Domains & specific senders
- Sign-in providers
- Analysts with access
- Requester count (by email domain) and ticket totals
- A Global / shared node for things that serve every company β shared mailboxes, global SSO providers, all-access analysts. Honours the model's many-to-many shape rather than faking a strict hierarchy
- Health hints β a company with no sendable mailbox is flagged, and mailboxes show a not signed in badge
- Totals bar, company filter, expand / collapse-all, and deep-links out to the relevant editors (Companies, Single Sign-On, Analyst settings)
- Aggregation endpoint
api/system/get_topology.php; searchable System landing card (map / overview / relationships / structure). v1 is read-only
Finds and rehomes tickets stuck in a deleted department.
- A ticket whose
department_idpoints at a department that no longer exists is invisible to every team-filtered queue (neither "no department" nor in anyone's departments), so it silently disappears from the inbox β exactly what the Topology view surfaced - Lists every such ticket (ticket #, subject, company at N>1, requester, status, the dead department id)
- Reassign them to a real department β or to "No department" β one at a time or in bulk (select-all + assign)
- Writes a
ticket_auditentry per change. Shows a clean "β none" state when everything resolves - Endpoints
api/system/get_orphaned_tickets.php(lists orphans + valid departments) andapi/system/assign_ticket_department.php(validates the target, updatestickets.department_id, audits). Searchable System landing card (orphaned / stuck / deleted department / reassign)
Your finance department's cost centres, per company, with a hierarchy - kept in step with your accounting system by .xlsx/CSV import or the REST API sync. See Cost centres. (3.1.0)
Just for fun: your webcam picture drawn in text characters, live, with no AI. Your album is yours alone. See Photo Album. (3.1.0)
A card for each person who has made a substantial contribution to FreeITSM β name, GitHub username, what they did and when. Added in 2.0.0, when the project received its first whole module from outside (Checklists & SOPs, from Santhosh Srinivasan), because a line in a changelog is not adequate thanks for that.
π The list is a code file, not a table β includes/contributors.php. A table would need a migration, a schema entry, an editing screen and a permission deciding who may use it, all so that a list which grows a handful of times a year can change without a deploy. It is also not an operator's data: it is part of what FreeITSM is, so it reads the same on every install rather than being something each site curates.
Checks every table and column exists, auto-creates anything missing, then adds known foreign keys and restores every missing index β strictly non-destructive, it never deletes data. Results are a searchable card grid (one card per table, with a module colour swatch, a status dot and a click-through to that table's live structure). The schema machinery, the index backfill and the orphaned-row Fix flow are documented on Database Integrity; how the page itself works β and how to maintain it β on the Database Verification β Developer Guide.
A searchable catalogue of diagnostics, restructured so each tool lives on its own page.
- A registry (
system/debug-tools/includes/tools.php) drives a grid of cards (ID badge, icon, category, short description) that filters live on every keypress, like the System landing - Each card opens that tool on its own page at
system/debug-tools/<slug>/index.php(e.g.d001/,d002/) instead of everything stacked inline. A per-tool page is two lines β set the slug and include the sharedincludes/tool-page.phprenderer; the diagnostic's metadata lives in the registry. Backend scripts (api/system/debug-tools/Dnnn_*.php) are unchanged - Adding a tool is now: a registry entry + a two-line page + the API script
- Each diagnostic is a single PHP file under
api/system/debug-tools/that emits a plain-text, section-delimited report β designed so the user can click Run, click Copy, and paste the entire report back to support without back-and-forth -
Defensive by design: every section is wrapped so one failure doesn't kill the rest, and each diagnostic carries its own expected-state data so it works even when
db_verify.php,config.php, the import script or the JSON files are missing or broken
Tools:
β οΈ This list is incomplete and should not be used as an inventory. It stops at D004 and resumes at D015 β the tools added in between were never written up here. The registry (system/debug-tools/includes/tools.php) is the only source of truth, and the in-app catalogue at System β Debug Tools is generated from it, so a tool missing from this page still exists and is still searchable in the product. Worth backfilling when somebody has an hour; worth knowing about in the meantime.
-
D001 β Demo Core Data Import. A 9-section report covering environment (PHP, OS, extensions, limits), config files (
config.php,db_config.php, DB constants), required files,core.jsonparse + record counts, DB connection (server version, charset), per-table schema sanity (expected vs actual columns, row counts, redacted sample row), a transactional write probe (a sentinel insert per table inside a rolled-back transaction), and a live import attempt that captures the real response, any PHP warnings and post-import row counts -
D002 β Delete Ticket (SQL trace; keeps its destructive-delete warning). For when a ticket won't delete because of a foreign-key error (e.g.
1451 Cannot delete or update a parent rowonemail_attachments). Its card carries a text input (a diagnostic can declare aninputand the value is passed through to the report) β enter the ticket reference and it resolves the ticket, audits every table the delete touches (existence, key columns, each foreign key and itsON DELETErule), counts the child rows that will go, then performs the delete inside a transaction echoing everyDELETEstatement, its parameters and rows affected, COMMITs, verifies nothing is left and removes the orphaned attachment files. Destructive β on success the ticket and all its data are permanently deleted; on any error the transaction rolls back -
D003 β Self-Service SSO check (by email). Type a requester's email and it traces the whole self-service SSO path end to end and prints a verdict with blockers: schema readiness, global SSO config, single- vs multi-tenant mode, how the email maps to a company (sender override β domain mapping β freemail detection), the user account state (exists / passwordless / TOTP / pinned provider / linked identities), the predicted login outcome β local / sso / choose (mirroring
resolve_login.php), provider health with a live, secret-free OIDC discovery test (issuer match + authorization/token/jwks/end-session endpoints reachable, without the client secret), and the exact redirect URI to register. Read-only and safe to share β secrets/hashes reported as present/absent only, IdP subject masked, and identifiers (client IDs, tenant GUIDs) partially masked (first/last chars). Backendapi/system/debug-tools/D003_selfservice_sso.php; registry slotd003/ -
D004 β Local login check (password / hash). Diagnoses why a username/email + password sign-in fails β built for the "I bulk-imported password hashes but accounts can't log in" case. Pick the account type (analyst / self-service user), enter the username/email, and optionally the password. Runs hash forensics β detects bcrypt/argon vs an imported MD5 / SHA-1 / SHA-256 / phpass / Django / LDAP hash that PHP's
password_verify()can never read β flags whitespace/length anomalies, checks account-state blockers (inactive, locked, password expired, SSO-pinned, TOTP required, IP bans), and for MFA users whether the encryption key can decrypt the TOTP secret. Prints a plain-English verdict + fix. If you supply the password it runspassword_verify()and, when that fails on a raw digest, pinpoints the wrong-hash-type import (e.g. the stored hash equalsMD5(password)). Safe to share: POST-only (never in the URL/logs), the password is never echoed, and the stored hash is never printed (only its format/cost/length). Backendapi/system/debug-tools/D004_local_login.php; registry entry +d004/page. The multi-field debug-tools input framework (text / password / select over POST) was added for D004 -
D015 β CardDAV health. Walks the connection to each address book one rung at a time β reach the server, sign in, read the chosen book, and may this account write to it β because those fail for entirely different reasons and a single verdict leaves four things to check. Reports which authentication scheme the server actually offered, which is usually the whole diagnosis: a stock Baikal ships Digest and answers Basic with a flat 401 that reads exactly like a wrong password. Calls out the mismatch that looks fine and is not: write-back switched on while the account holds no write privilege, where every change saves locally and every one is refused. Also reports FreeITSM's own side β people managed, and how many are missing the card reference a write-back needs, which is what an import from before write-back existed leaves behind. π΄ Writes nothing: the permission question is a
current-user-privilege-setPROPFIND, not a create-and-delete probe that can fail halfway and leave a contact called "FreeITSM test" in a real address book. Server address masked to its shape and the account to its first letter, so the report can be sent on -
D016 β CardDAV drift. A top-to-bottom comparison: every imported person against their card as it stands, detail by detail with both values side by side, plus cards that have vanished from the server and contacts on the server that never came here (usually correct β outside the chosen group or tag). Finishes with which details drift most across everybody, which is the honest evidence for whether two-way sync earns its keep and for which fields. π΄ No "fix it" button, deliberately β which side is right is a judgement, and one button reconciling hundreds of contacts is how a careful integration becomes a data-loss incident, in the one direction that cannot be undone.
β οΈ Names the trap it reveals: a drifted value is exactly what makes a later write-back get refused, so import first and edit second. Slower than the others, because there is no way to ask a CardDAV server which cards changed. Contact names and details appear in the output, so the report is personal data. Full detail on CardDAV write-back β when it goes wrong -
D017 β Config completeness (3.0.0). Compares the
config.phpthis server is running with the list of settings this version knows about (includes/config_requirements.php), one by one: needed (the four database settings, normally indb_config.php), has a default (BASE_URL,SSL_VERIFY_PEER,SSL_CA_BUNDLE- the app copes without them), or optional (ENCRYPTION_KEY_PATH,TRUST_PROXY_HTTPS). For each it says whether your file sets it and, where it does not, what happens instead and the exact line to add. Also lists constants the version does not know about (usually leftovers) and functions declared insideconfig.php- the shape behind GH #129. π Never prints a value: names and yes/no only, so the report is safe to send on. Run it after an upgrade or a server move -
D018 - Ticket folder counts (3.6.1). For when somebody's inbox folder list says 0 on All Tickets, the statuses and the departments while there are plainly tickets. Pick the affected analyst and it follows every ticket through the same filters the counts use - the Trash, merged tickets, snoozed tickets, the company in use, and the departments their teams can see - showing how many each step removes, so the step that takes the number to zero is obvious. π The usual cause is not the database: an analyst in a team gets folders and counts only for the departments that team is linked to, plus tickets with no department and their own (the ticket list itself is not narrowed by team). Up to 3.6.1 a team linked to no department counted nothing at all bar the analyst's own folder - so "My tickets" had a number while everything else said 0, even on an install with no departments yet. 3.7.0 fixed that: such a team now counts the tickets with no department, and the tool only raises it when tickets that ARE in a department are being left out. System β Teams now warns about a team with members but no departments. Also lists every team on the install (active or not, members, departments, ticket count), departments no team covers, analysts in no team, and links left pointing at deleted teams, departments or analysts.
β οΈ An inactive team, or a membership of a deleted team, still narrows what its members see - the tool flags both. Checks the tables and columns the counts read and tickets on a missing or inactive status. Run for yourself, it also calls the real counts endpoint and prints its numbers beside its own. Read-only; shows names and counts, never ticket content
Other system-wide settings live at the Tickets β Settings or appropriate per-module locations. The System module specifically covers cross-cutting administration.
Global notification system shipped at assets/js/toast.js β used across every module. Four types (success, error, warning, info), nine configurable screen positions via a visual grid picker in avatar menu β Preferences. The position and animation are stored per-analyst in user_preferences; pages emit them as window.TOAST_POSITION / window.TOAST_ANIMATION so the toast library picks them up without an extra round-trip. Falls back to localStorage on pages that haven't been signed into yet. Slide-in / fade animations, auto-dismiss after 4s by default, manual close button. showToast() takes an optional third argument to hold one open longer β used where a message is an instruction rather than an acknowledgement (a CardDAV write-back refusal tells you to import and try again, which cannot be read and acted on in four seconds). Omitting it keeps the original behaviour exactly, which matters because most pages load toast.js with no cache-buster and a stale copy simply ignores the argument. Each type carries its own icon and colour bar β success green, error red, warning amber, info blue.
| Table | Purpose |
|---|---|
system_settings |
Key-value store for global config (sensitive values encrypted) |
analyst_module_access |
Per-analyst module access toggles |
user_preferences |
Per-analyst preferences (toast position / animation, sidebar collapsed states, module-specific UI prefs) |
system_event_logs |
Administrative action audit |