-- ── Communities ─────────────────────────────────────────────────────────────── -- Conformance: row zero (host binding). The host map. `resolve_host(host)` -- reads exactly one row here to mint the request's TenantContext. This table -- is OPERATOR-GLOBAL: it is the registry of tenants, not itself tenant-scoped, -- so it carries no `community_id` of its own (its `id` IS the community key). -- Listed in the lint allowlist as operator-global. -- -- Host normalization (Lane 0 contract): `host` is stored already-normalized — -- ASCII-lowercased, trailing dot stripped, default port omitted. The UNIQUE is -- on `lower(host)` belt-and-suspenders so `Relay.Example` and `relay.example` -- can never become two tenants even if a writer forgets to normalize. -- `resolve_host()` (buzz-core) applies the identical normalization before -- lookup, so resolution and storage agree by construction. CREATE TABLE communities ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), host VARCHAR(255) NOT NULL, signing_key BYTEA, -- Per-community workspace icon (NIP-11 `icon`), set via kind:9033. -- Added by migration 0003; kept here so desired-state applies match. icon TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), archived_at TIMESTAMPTZ, deletion_state TEXT NOT NULL DEFAULT 'active' CHECK (deletion_state IN ('active', 'quiescing', 'fenced', 'tombstone')), deletion_fence_generation BIGINT NOT NULL DEFAULT 0 CHECK (deletion_fence_generation >= 0), deleted_at TIMESTAMPTZ, CONSTRAINT chk_communities_id_not_nil CHECK (id <> '00000000-0000-0000-0000-000000000000'::uuid) ); CREATE UNIQUE INDEX idx_communities_host ON communities (lower(host));