Deployment Architecture
Shared multi-tenant model: one deployment serves all tenants. Isolation is logical (a tenantId column plus a tenant-scoped data layer), not one stack per client — see Multi-Tenancy.
Isolation Boundaries
| Resource | Scope |
|---|---|
| Application container | Shared across all tenants |
| PostgreSQL database | Shared — isolated per tenant by tenantId and the tenant-scoped Prisma client |
| Blob storage | Shared account; blob keys carry tenant context and downloads re-check the requester's tenant |
| Domain + TLS certificate | One platform domain; tenants addressed at /t/{slug} |
| Azure app registration (SSO) | One platform app; per-tenant entraDirectoryId enables verified SSO auto-approval |
| Azure app registration (email-to-ticket) | Shared — the platform SSO app, used against each tenant's entraDirectoryId. Needs Mail.ReadWrite + Mail.Send application permissions |
:::caution Isolation is enforced in code
There is no database-per-tenant or Postgres row-level security. The boundary is the tenant-scoped Prisma client plus the CI guard (scripts/check-tenant-scoping.ts). Any new tenant model must be added to TENANT_MODELS, and any raw-client use must be justified and whitelisted.
:::
Requirements
- Public HTTPS domain (Graph webhooks and Teams both require it)
- Outbound access to Microsoft Graph, Entra ID, Azure Blob, and the SMTP host
- A container host and a PostgreSQL instance sized to the combined tenant volume
- Scheduled-job execution (SLA checks, reminder firing, Graph subscription renewal)
See Deployment Runbook, Docker, and Tenant Provisioning.