Skip to content

Multi Tenancy Progress

Ed Mozley edited this page Jun 21, 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-06-22.

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.
  • ⬜ Assets, CMDB, Change Management, Calendar, Forms, Network Mapper
  • ⬜ Knowledge β€” per-company and global articles (two-axis visibility)

Self-service portal ⬜ not started (design done)

  • ⬜ Global requester identity β€” one login, tickets across companies, each badged
  • ⬜ Portal scoping + the "only your own tickets" guardrails
  • ⬜ Per-company branded portals / per-company SSO (optional, later)

Cross-cutting / housekeeping

  • βœ… README β€” an "in development" multi-tenancy section is now in the project README
  • ⬜ 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