-- v1.1.6: Explicit registration for externally-subscribable topics.
--
-- Internal-only topics remain implicit per the §5 design-notes
-- decision: anyone can publish_durable("any.topic", msg) and triggers
-- can subscribe without a row here. This table only holds topics that
-- have been explicitly externalized — external SSE subscribers can
-- only subscribe to topics with a row here AND external_subscribable
-- = TRUE.
--
-- The publish path (v1.1.5's publish_durable) does NOT consult this
-- table: publishing to a topic with no row still fans out to triggers
-- and to any in-process external subscribers (none exist for an
-- unregistered topic, since external subscribers can't subscribe to
-- one). The topics table is read by the SSE subscribe path only.
--
-- auth_mode values: 'public' + 'token' in v1.1.6. 'session' arrives in
-- v1.1.8 (users-SDK); 'script' arrives in v1.2 (script-mediated auth).
-- The CHECK constraint extends in those releases.
CREATE TABLE topics (
    app_id                UUID NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
    name                  TEXT NOT NULL,
    external_subscribable BOOL NOT NULL DEFAULT FALSE,
    auth_mode             TEXT NOT NULL DEFAULT 'public'
        CHECK (auth_mode IN ('public', 'token')),
    created_at            TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at            TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    PRIMARY KEY (app_id, name)
);

-- Hot lookup: "is topic T in app X externally subscribable?" The PK
-- (app_id, name) already covers this; an explicit index is redundant.
