Skip to main content

Multi-Tenancy

Every agency in the system is a row in the agencies table. The agency_type column determines its role:

agency_typeRole
psapA 911/dispatch center — creates incidents, owns CFS configurations, escalation rules, and notification rules
companyA third-party response company — receives notifications, manages users and transport preferences

There is no shared data between agencies. Every agency-scoped entity carries an agency_id foreign key, and all repository queries enforce that scope automatically.

AgencyAwareRepository

AgencyAwareRepository is the base class for all repositories. It holds the currently authenticated agency (resolved from the JWT or API key at request time) and automatically appends AND agency_id = :currentAgency to every query.

Rules:

  • All repositories must extend AgencyAwareRepository
  • Never call findAll() or findBy([]) without an agency scope unless in a superadmin context
  • The superadmin context bypasses agency scoping explicitly — this is not the default

Roles

SlugWhoKey permissions
superadminPlatform adminFull access to all agencies and settings
psap_adminPSAP administratorManage PSAP settings, TCOs, transport channels, escalation/notification rules
psap_agentTCO (Telecommunicator)Create incidents, monitor PSAP dashboard, ACK company replies via web UI
company_adminCompany administratorManage company info, users, escalation hierarchy
company_userCompany responderManage own transport preferences, receive/ACK notifications, send replies
viewerRead-only observerView incidents and notifications

All authorization decisions go through Symfony Voters — never raw role string checks in controllers.