Skip to content

Multi Tenancy Progress

Ed Mozley edited this page Jul 5, 2026 · 14 revisions

Multi-tenancy: progress tracker

Part of the Multi-tenancy section. Planned / in active development. Tracks the build against the plan. Last updated: 2026-07-05.

Legend: βœ… done Β Β·Β  🚧 in progress / next Β Β·Β  ⬜ not started

Where things stand: the tickets module is now solid for real multi-company use. The foundation is complete; tickets scope by company; per-company email routing works end-to-end (pinned/shared mailboxes, sender-domain and individual-address routing, a triage safety net, and diagnostics); the first per-company settings are live (ticket types & origins); the whole ticket layer is fully isolated (every ticket read/write is access-checked β€” see Data isolation & the audit); and you can now control which companies each analyst may access. What's left is rolling per-company scoping through the other modules, plus per-company departments/SLAs and the self-service portal.


Phase 0 β€” Foundation βœ… complete

The invisible plumbing. None of this changes anything on a single-company install.

  • βœ… tenants table + a silent Default company that owns all existing data
  • βœ… tenant_domains table (for future email-domain routing)
  • βœ… Analyst β†’ company access model (which staff can see which companies, + an "all companies" flag) β€” now with an admin UI to grant/deny it per analyst (Settings β†’ Analysts; see Staff access)
  • βœ… tenant_id on tickets (every existing ticket assigned to the Default company)
  • βœ… Shared tenancy helper β€” the single choke-point all tenant logic routes through
  • βœ… Active-company session context
  • βœ… Company switcher in the header (appears only once a 2nd company exists)
  • βœ… System β†’ Companies admin page (list / create / rename companies)

Phase 1 β€” Tickets βœ… core complete

Making the tickets module company-aware.

  • βœ… Ticket list scopes to the active company
  • βœ… New tickets are stamped with the active company
  • βœ… Folder count badges (departments / statuses / analysts / unassigned) scope to the active company
  • βœ… Full ticket-layer isolation (Phase 1.5) β€” every ticket endpoint (detail, thread, attachments, notes, time, audit, CMDB, SLA, CSAT, and every action) is now access-checked, verified by a three-sweep audit. The tickets module is safe for real multi-company use.

Phase 2 β€” Per-company mailboxes & email routing βœ… core complete

So an MSP can receive each client's email and have it land under the right company.

  • βœ… Link mailboxes to companies (pinned = one mailbox per company, shared intake = one inbox sorted by sender) β€” set in Tickets β†’ Settings β†’ Mailboxes
  • βœ… Register each company's email domains β€” managed in System β†’ Companies
  • βœ… Route incoming email to the right company by sender domain (covers both Microsoft & Gmail import)
  • βœ… Specific senders β€” map an individual address to a company (e.g. jane@gmail.com β†’ Acme), matched before the domain, so personal/webmail senders route correctly
  • βœ… Triage queue for unmatched / unknown senders β€” reached from the company switcher, with one-click filing and "route this domain" or "always file this sender" (each sweeps the other queued mail and auto-routes future mail)
  • βœ… Public-email (Gmail/Outlook/…) domains are never auto-mapped (different clients share them); the built-in list is customisable (add-only β€” extend it, can't un-protect a built-in)
  • βœ… "How email reaches this company" β€” a read-only routing summary on each company's editor
  • βœ… Email routing test β€” a dry-run diagnostic: type a sender + mailbox, see where it would land and why
  • ⬜ A per-company reply-identity override (replies already go out from the mailbox the ticket arrived on; this is the only outbound piece left) (next up)

Routing rule (first match wins): reply to an existing ticket β†’ inherits its company; otherwise a pinned mailbox β†’ its company (sender ignored); otherwise a shared mailbox β†’ matched by the exact sender address, then the sender domain β†’ that company, or triage if nothing matches. On a single-company install this is invisible β€” everything lands under the one Default company exactly as before.

Phase 3+ β€” Roll scoping through the other modules 🚧 started

  • 🚧 Per-module Settings: global default + per-company override (the add + hide model). Built: ticket types, ticket origins. Next: categories, departments (with extra wiring), SLAs. Statuses/priorities/teams/mailboxes stay global on purpose β€” see Settings rationale.
  • βœ… Change Management β€” company-scoped like tickets/problems (list + calendar scope to the active company; new changes stamped with it; every by-id read/write, UI and REST API, isolated by company; a change in one company is a 404 to another). changes.tenant_id, NULL = Default; invisible at N=1.
  • ⬜ CMDB, Calendar, Forms, Network Mapper, then Assets (hostname-per-company) + Knowledge (two-axis global/per-company)

Self-service portal 🚧 SSO built; ticket isolation still pending

  • ⬜ Global requester identity β€” one login, tickets across companies, each badged
  • ⬜ Portal scoping + the "only your own tickets" guardrails
  • βœ… Per-company SSO β€” requesters sign in via their own company's identity provider, routed by email domain (0 / 1 / 2+ IdPs β†’ local / straight-through / picker); single-company and MSP both supported, invisible at N=1. Uses each provider's own credentials today; the credential-free "MSP multi-tenant app + consent" model is designed but not built. See Single Sign-On.

Cross-cutting / housekeeping

  • βœ… README β€” an "in development" multi-tenancy section is now in the project README
  • βœ… Admin visibility tools β€” System β†’ Topology (a company-rooted tree of mailboxes, domains, providers, analysts, ticket/requester counts across every company) and System β†’ Orphaned tickets (find + rehome tickets stuck in a deleted department, which are hidden from every team queue). See System.
  • ⬜ Isolation beyond the tickets module β€” other modules that read tickets (self-service portal, CMDBβ†’tickets, Tasks↔tickets, Watchtower, the SLA cron, the workflow engine, templated/CSAT email) are not yet company-scoped; only matters at N>1, fix as each module gets its Phase-3 scoping. See Data isolation.
  • ⬜ Companies: delete / archive management (today you can only deactivate)
  • ⬜ Automated isolation tests (guarding against one company seeing another's data)
  • ⬜ Tidy-up: remove the api/tickets/debug_get_attachment.php diagnostic (discloses server paths)

This page is maintained by hand as work lands β€” see the GitHub roadmap issue (#24) for live discussion.

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally