Multi Tenancy Settings - edmozley/freeitsm GitHub Wiki
Multi-tenancy: settings — global vs per-company
Part of the Multi-tenancy section. In active development — the per-company override model is built for ticket types, ticket origins, and the Asset Management types, statuses and locations; the rest of the tabs follow the rules on this page. Building it into a new module? See the Developer Guide.
Once you have more than one company, settings naturally split into two tiers. A single-company install never sees this split — the two tiers collapse into one flat Settings, exactly as today.
The two tiers
Global (install-level) — things you, the operator, control once; not a client's concern, often shared across all clients:
- Connected mailboxes (the email accounts + their authentication)
- AI provider keys, outbound mail, system configuration
- The list of companies itself
- Staff accounts and their cross-company access
Per-company — things that legitimately differ from client to client (reached by selecting the company in the company switcher):
- The company's email domains and reply identity
- SLAs and business hours
- Ticket types, origins, categories, request types
- Branding
- Custom fields, forms, and knowledge-base publishing
(Note: statuses and priorities stay global on purpose — see Why some settings stay global below.)
The principle: no global "mode" switches
The most important rule in the whole design:
Wherever two organisations would want different behaviour, make it a property of the thing — or a global default with per-company overrides — never an install-wide "mode" toggle.
Mode toggles force everyone into a rigid either/or and create a second source of truth that drifts out of sync. Avoiding them is what lets one install accommodate wildly different organisations. You'll see the same trick three times:
- Mailboxes — pinned vs shared is a property of each mailbox, not an "MSP mode".
- Staff — which companies an analyst can see is a property of each analyst, so a "generalist" MSP and an "account-aligned" MSP both work with no switch.
- Shared config — global default + per-company override (below).
Shared config: global defaults + per-company overrides
For things whose global-vs-per-company nature is itself a matter of taste — categories, priorities, SLAs, workflows — FreeITSM won't ask you to flip a "global or per-company?" switch (changing that after you've created data is a nightmare). Instead:
- They're defined globally as sensible defaults.
- A company may override or extend them.
So an MSP that wants one standard taxonomy across every client simply never overrides — and it feels completely global. An MSP that wants each client bespoke overrides per company — and it feels completely per-company. One model, both worlds, and only one idea to learn.
Worked example: mailboxes
Mailboxes show the split neatly:
- Connecting and authenticating a mailbox is a global action — it's infrastructure, and a shared intake mailbox doesn't belong to any single client anyway. The only new choice on that screen is pinned (pick a company) or shared.
- A company's page owns only its genuinely company-specific bits — its domains and reply identity — and shows a plain-English, read-only summary like:
"Mail arrives via the shared MyMSP intake (matched on acme.com, acme.co.uk) and a dedicated [email protected]; replies are sent from [email protected]."
How a per-company list works in practice (add + hide)
For the settings that are lists of options a ticket points at — ticket types, ticket origins, and (coming) categories — the override works like this, while you're in a client company's context:
- Shared defaults you defined once are listed, each with a hide / show toggle (an eye icon). Hiding one removes it from that company's pickers only — every other company keeps it. The default itself is never deleted, so closed/old tickets still show the right label, and you can un-hide any time.
- The company's own items sit in their own group — add, edit and delete freely; only that company ever sees them.
- A safety rule (the in-use guard): you can't hide or delete an option while open tickets in that company still use it — reassign or close them first.
So a client that wants the standard set does nothing; a client that needs to drop one default and add two of its own takes ten seconds — without touching any other client. The shared defaults themselves are edited from the MSP / Default company context.
Why some settings stay global
Not everything should be per-company, even though it could technically be made so. Three reasons a setting is deliberately kept global:
- Cross-company reporting and analyst sanity. Your analysts work across many clients, and "all open tickets" reporting only makes sense if a word means the same thing everywhere. Anything that fragments that shared meaning costs more than it gives.
- Structural invariants. Some settings carry rules the simple add/hide model can't protect — e.g. there must always be at least one "closed" status, and exactly one default. Letting a company freely hide/replace these could leave it with no way to close a ticket.
- It's the MSP's own operation, or shared infrastructure — not a client's. An analyst team, a rota, or a connected mailbox isn't owned by any one client.
What's customisable per company — and what isn't
| Setting | Per company? | Why |
|---|---|---|
| Ticket types | ✅ add + hide | a plain list of options; clients genuinely differ |
| Ticket origins | ✅ add + hide | same |
| Asset types | ✅ add + hide | same shape as ticket types |
| Asset statuses | ✅ add + hide | a client's asset lifecycle can differ (note: ticket statuses stayed global — a judgement call per module) |
| Asset locations | ✅ per company | a tree of a client's physical sites — entirely the company's own (no shared locations; a client with a London office doesn't inherit anyone else's) |
| Suppliers | ❌ shared | one registry shared with Contracts & RFP; a client's private suppliers would leak into those until they're scoped too — kept shared for now |
| Categories / request types | ✅ (planned) | same |
| Departments | ✅ (planned, with extra care) | a department is inside a client — but it's wired to teams, mailbox routing and SLAs, so it needs more than a simple list |
| SLAs & business hours | ✅ (planned) | contractual per client — the headline per-company setting |
| Statuses | ❌ global | structural invariants (closed/default) + cross-company reporting needs shared meaning |
| Priorities | ❌ global | a default priority + the SLA targets hang off them, so they're the stable axis SLAs vary against |
| Teams, analysts, rota | ❌ global | the MSP's own operation; who sees which client is handled by staff access, not by copying teams per client. (A global team can grant its members access to specific clients — additive access, not a per-client copy of the team.) |
| Mailboxes, AI keys, outbound mail | ❌ global | shared infrastructure; a shared intake mailbox spans every client |
| General / CSAT / reply-cleanup | ❌ global (per-company value override later if needed) | these are single settings, not lists — a different override shape |
The guiding rule: default to global; add a per-company override only where a client genuinely needs to differ — and pick the override shape that fits (add/hide for option-lists, a per-company value for single settings, nothing for things the MSP owns).