-- 020_permissions.sql -- Fine-grained permissions on groups (v0.24.2) -- Extend the groups.source CHECK to include 'system' (system-seeded groups). ALTER TABLE groups DROP CONSTRAINT IF EXISTS groups_source_check; ALTER TABLE groups ADD CONSTRAINT groups_source_check CHECK (source IN ('manual', 'oidc', 'system')); -- created_by is meaningless for system-seeded groups; allow NULL. ALTER TABLE groups ALTER COLUMN created_by DROP NOT NULL; ALTER TABLE groups ADD COLUMN IF NOT EXISTS permissions JSONB NOT NULL DEFAULT '[]'::jsonb, ADD COLUMN IF NOT EXISTS token_budget_daily BIGINT, ADD COLUMN IF NOT EXISTS token_budget_monthly BIGINT, ADD COLUMN IF NOT EXISTS allowed_models JSONB; COMMENT ON COLUMN groups.permissions IS 'Array of permission strings granted to group members'; COMMENT ON COLUMN groups.token_budget_daily IS 'Daily token ceiling for members (NULL = unlimited)'; COMMENT ON COLUMN groups.token_budget_monthly IS 'Monthly token ceiling for members (NULL = unlimited)'; COMMENT ON COLUMN groups.allowed_models IS 'Array of model_id strings this group may use. NULL = unrestricted.'; -- Seed the Everyone group with a stable well-known ID. -- All authenticated users receive its permissions implicitly (no membership row needed). -- source='system' prevents deletion via the store guard. INSERT INTO groups (id, name, description, scope, created_by, source, permissions) VALUES ( '00000000-0000-0000-0000-000000000001', 'Everyone', 'Implicit group — all authenticated users receive these permissions.', 'global', NULL, 'system', '["model.use","kb.read","channel.create"]'::jsonb ) ON CONFLICT (id) DO NOTHING;