Tenant

The tenant app models the tenant organization and how requests are routed to it. It is a shared app (lives in the public schema) and is the backbone of Midsummer’s multi-tenancy.

What it does

  • Tenant — the org that owns one or more events (in practice one event today). Each tenant has its own PostgreSQL schema (schema_name).

  • Domain — the subdomain(s) that map a host to a tenant (e.g. furrydelphia.midsummer.cloud). mode distinguishes event vs. tenant-management domains.

  • CustomDomain — a customer-owned domain (e.g. furrycon.com) pointing at a specific Event. Lifecycle: DNS_VERIFIED → … → SSL_ISSUED. Midsummer issues Let’s Encrypt certificates and generates nginx configs from these records (services/custom_domain_nginx.py).

  • Membership — the per-tenant role for a User. Carries isTenantSuperuser and developer flags. This is how a single shared identity gets different powers in different tenants.

  • Public-schema landing pages — the front/ app serves the marketing/landing pages on the public schema.

How routing uses this

host ──► TenantMainMiddleware ──► Domain → Tenant
                                    │ switch connection to tenant schema
                                    │ set URLconf (tenant vs public)
                                    ▼
        EventSetupMiddleware ──► request.current_event (subdomain or CustomDomain)

See Request resolution and Multi-tenancy for the full middleware walk.

Key models

Tenant (schema_name)
  └─ Domain (is_primary, mode)
  └─ CustomDomain (event, status: DNS_VERIFIED…SSL_ISSUED)
User ── Membership ──► Tenant   (isTenantSuperuser, developer flags)

Warning

A latent bug: the staff roster uses a Membership.isTenantAdmin field that does not exist (short-circuit-masked). Prefer the real Membership.isTenantSuperuser field for any new tenant-admin gating.

Where to look

  • Models: tenant/models.py

  • Custom-domain nginx generation: tenant/services/custom_domain_nginx.py

  • Middleware: midsummer/middleware.py