---
# System prepended metadata

title: Multi-tenant support

---

# Multi-tenant support

## Why do we want a multi-tenant architecture?

- single version of the application (everyone runs same version of code)
    - we have this already 🎉
- cost reduction through shared costs vs multiple single instances
- opens us up to smaller clients (they can afford us vs "enterprise" single tenant)
- deployments are simplified (one deployment for all vs one per tenant)

## Assumptions/(self-imposed) limitations

- All tenants will run inside the same AWS account
    - same VPC?
- All tenants will run the same application software (differences are controlled through configuration, not code)
- [Prerequisite] Database is migrated to Amazon Aurora PostgreSQL
- One set of encryption keys per stack (as we have now)
- each tenant uses the same theme (common to all)
- each tenant shares the same configuration i.e. if glasswall is enabled for one, it's enabled with same settings for all - NO, per-tenant configuration is required

## Open questions

- [Product] What are the reasons for wanting multi-tenant support?
    - ease of managing customers?
    - cost benefits from a single install?
    - speed of adding new customers vs spinning up a new installation?
- [Product] What does sign in look like?
    - different URL (subdomain or slug) for each tenant?
    - choose tenant from dropdown box?
- [Product] Can a client (username) exist on:
    - one tenant only
    - multiple tenants
        - shared (same) user account?
        - different user with same username on each tenant?
- [Product] What's different/the same about each tenant?
- [Product] Is there any inter-connect traffic between tenants? i.e. accessing customers
- [Product] Does each tenant provide their own encryption keys?
- Super-admin functionality
    - what do we need to offer to admins?
- [Product] One set of encryption keys per stack (as we have now)

## Shared Resources

[Engineering] Which services can run independently of tenant?

## Combined resources

Running related services on same ec2 instance

Reducing number of ec2 instances

Benefit from serverless?
- running a container on lambda

## Specific to tenant

The following must be isolated / set per tenant:

S3 - attachments bucket - separate subdirectory for each tenant

- [Security] IAM role for each tenant, restricted to their own subdirectory only

---

## Possible solutions

### Multiple single tenant instances

Exactly what we have now. We spin up a separate stack for each tenant.

### Single database cluster, separate services for each tenant

Effectively what we have now but consolidating the database into a single cluster. Each tenant has their own schema. Benefit is reduction in DB cost and management (managing one DB vs several)

TODO: confirm which services can be shared with:
- none or minimal change to current behaviour
- some change to current behaviour
- extensive change to current behaviour

**Changes required to services:**
| Service                                     | unknown | no change | minimal change | some change | extensive change | cannot support multi-tenancy |
| -- |:--:| -- | -- | -- | -- | -- |
| MQTT                                        |   ☑️    |           |                |             |                  |                              |
| Python_API                                  |   ☑️    |           |                |             |                  |                              |
| DAS                                         |   ☑️    |           |                |             |                  |                              |
| UMS                                         |   ☑️    |           |                |             |                  |                              |
| WhatsApp (lambda)                           |   ☑️    |           |                |             |                  |                              |
| WeChat                                      |   ☑️    |           |                |             |                  |                              |
| Glasswall                                   |   ☑️    |           |                |             |                  |                              |
| DLP                                         |   ☑️    |           |                |             |                  |                              |
| SMS                                         |   ☑️    |           |                |             |                  |                              |
| Conversation                                |   ☑️    |           |                |             |                  |                              |
| MQTT-worker                                 |   ☑️    |           |                |             |                  |                              |
| websocket-MQTT                              |   ☑️    |           |                |             |                  |                              |
| Push-worker                                 |   ☑️    |           |                |             |                  |                              |
| SMS-worker                                  |   ☑️    |           |                |             |                  |                              |
| Task-server                                 |   ☑️    |           |                |             |                  |                              |
| S3 chat attachments                         |   ☑️    |           |                |             |                  |                              |
| SNS topics                                  |   ☑️    |           |                |             |                  |                              |
| DynamoDB                                    |   ☑️    |           |                |             |                  |                              |
| SQL Server                                  |   ☑️    |           |                |             |                  |                              |
| PostgreSQL (WhatsApp)                       |   ☑️    |           |                |             |                  |                              |
| API Gateway                                 |   ☑️    |           |                |             |                  |                              |
| CloudFront                                  |   ☑️    |           |                |             |                  |                              |
| S3 web assets (angular/react/JS/CSS/images) |   ☑️  |
| Route53 | ☑️ | | | | |
| CloudWatch | ☑️ | | | | |


### Single database, same schema, same services

Using the same database and schema, add a `tenant_id` to tables in order to filter content by tenant. Update all data processes to filter by `tenant_id`.

As of 2021-03-09, we have **96** tables in `chatdb`:

| Table | Needs tenant_id? |
| -- |:--:|
| _enum_AUDIT | |
| _enum_email_type | |
| _enum_entity | |
| _enum_mapped_system | |
| _enum_permission | |
| _enum_phone_type | |
| _enum_user_client_type | |
| _enum_user_division | |
| _enum_user_location | |
| _enum_user_staff_profile | |
| _enum_user_staff_profile_permission_XREF | |
| _enum_user_staff_role | |
| _enum_user_title | |
| alembic_version | |
| api_key | |
| attachment | |
| attachment_consultation | |
| authorization | |
| billing_active_coverage_team_dim | |
| billing_date_dim | |
| billing_event_fact | |
| billing_event_type_dim | |
| billing_relationship_owner_dim | |
| billing_staff_user_dim | |
| browser_access | |
| chat | |
| chat_member_XREF | |
| chat_msg | |
| chat_msg_attachment | |
| chat_msg_system | |
| chat_msg_system_auth | |
| chat_msg_system_crm | |
| chat_msg_system_join | |
| chat_msg_system_leave | |
| chat_msg_system_rename | |
| chat_msg_system_webhook | |
| chat_msg_text | |
| chat_msg_transaction | |
| chat_tagged_XREF | |
| chat_title | |
| contact_user_XREF | |
| coverage_team_XREF | |
| crm_chat_msg | |
| crm_chatmsg_relation | |
| crm_chatmsg_user | |
| device_push | |
| dlp_message | |
| event_log | |
| event_log_data | |
| ldap_config | |
| ldap_config_AUDIT | |
| ldap_profile_mapping | |
| ldap_profile_mapping_AUDIT | |
| password_change_attempt | |
| relationship | |
| relationship_AUDIT | |
| relationship_mapping | |
| relationship_mapping_AUDIT | |
| relationship_member_XREF | |
| SYSTEM | |
| team | |
| team_AUDIT | |
| team_invite | |
| team_mapping | |
| team_mapping_AUDIT | |
| team_members_XREF | |
| terms_and_conditions | |
| terms_and_conditions_AUDIT | |
| user | |
| user_AUDIT | |
| user_avatar | |
| user_client | |
| user_client_AUDIT | |
| user_device | |
| user_device_api | |
| user_device_external | |
| user_device_login | |
| user_device_mobile | |
| user_device_provision | |
| user_device_provision_attempts | |
| user_device_session | |
| user_device_web | |
| user_email | |
| user_email_AUDIT | |
| user_ldap_login_AUDIT | |
| user_mapping | |
| user_mapping_AUDIT | |
| user_phonenumber | |
| user_phonenumber_AUDIT | |
| user_staff | |
| user_staff_AUDIT | |
| user_system | |
| user_system_AUDIT | |
| user_visibility | |
| webhook | |
| wechat_usage_alerts_counter | |

## Migrating from multi-tenant shared platform to a dedicated instance

Use case: client trials novastone with 50 seats on our shared SaaS platform. Evaluation proves we bring benefits so they upscale to a dedicated instance. We need to be able to migrate tenant from one platform to another, in a process which delivers a smooth migration for *both* parties.