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.