BEGIN; CREATE TABLE IF NOT EXISTS tenants ( tenant_id TEXT PRIMARY KEY, identifier TEXT UNIQUE NOT NULL, grouping_name TEXT NOT NULL, display_name TEXT, contact_email TEXT, lifecycle TEXT NOT NULL DEFAULT 'active' CHECK (lifecycle IN ('active', 'retired')), version BIGINT NOT NULL DEFAULT 1 CHECK (version > 0), -- Pre-lifecycle SQLite rows legitimately carry no timestamp. Nullable here -- preserves them exactly during the store move; new domain writes set both. created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ, retired_at TIMESTAMPTZ, reactivated_at TIMESTAMPTZ ); CREATE TABLE IF NOT EXISTS grants ( grant_id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL REFERENCES tenants(tenant_id), role TEXT NOT NULL, grant_reason TEXT NOT NULL, plan_id TEXT, granted_by TEXT NOT NULL, granted_at TIMESTAMPTZ NOT NULL, correlation_id TEXT NOT NULL, revoked_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS grants_tenant_active_idx ON grants (tenant_id) WHERE revoked_at IS NULL; CREATE TABLE IF NOT EXISTS plans ( tenant_id TEXT PRIMARY KEY REFERENCES tenants(tenant_id), plan_id TEXT NOT NULL, assigned_at TIMESTAMPTZ NOT NULL ); CREATE TABLE IF NOT EXISTS events ( seq BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, event_type TEXT NOT NULL, tenant_id TEXT NOT NULL REFERENCES tenants(tenant_id), at TIMESTAMPTZ NOT NULL, payload JSONB NOT NULL ); CREATE INDEX IF NOT EXISTS events_tenant_seq_idx ON events (tenant_id, seq); CREATE TABLE IF NOT EXISTS idempotency_receipts ( tenant_id TEXT NOT NULL REFERENCES tenants(tenant_id), idempotency_key TEXT NOT NULL, request_fingerprint TEXT NOT NULL, result JSONB NOT NULL, recorded_at TIMESTAMPTZ NOT NULL, PRIMARY KEY (tenant_id, idempotency_key) ); CREATE TABLE IF NOT EXISTS guardrail_overrides ( tenant_id TEXT NOT NULL REFERENCES tenants(tenant_id), limit_key TEXT NOT NULL, kind TEXT NOT NULL, amount TEXT NOT NULL, currency TEXT, period TEXT, PRIMARY KEY (tenant_id, limit_key) ); CREATE TABLE IF NOT EXISTS guardrail_changes ( change_id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL REFERENCES tenants(tenant_id), limit_key TEXT NOT NULL, previous JSONB, current JSONB, changed_by TEXT NOT NULL, reason TEXT NOT NULL, correlation_id TEXT NOT NULL, changed_at TIMESTAMPTZ NOT NULL ); CREATE INDEX IF NOT EXISTS guardrail_changes_tenant_time_idx ON guardrail_changes (tenant_id, changed_at, change_id); COMMIT;