-- Multi-Tenant Schema Migration -- Phase 10: Multi-Tenant Support -- ============================================================ -- CORE TENANT TABLES -- ============================================================ -- Tenants (organizations/customers) CREATE TABLE IF NOT EXISTS tenants ( tenant_id TEXT PRIMARY KEY, name TEXT NOT NULL, slug TEXT UNIQUE NOT NULL, -- URL-safe identifier owner_email TEXT, subscription_tier TEXT DEFAULT 'free', -- free, pro, enterprise is_active INTEGER DEFAULT 1, settings TEXT, -- JSON: custom config created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); -- Projects (workspaces within a tenant) CREATE TABLE IF NOT EXISTS projects ( project_id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, name TEXT NOT NULL, slug TEXT NOT NULL, -- URL-safe, unique within tenant description TEXT, is_active INTEGER DEFAULT 1, settings TEXT, -- JSON: project-specific config created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE, UNIQUE(tenant_id, slug) ); -- Tenant quotas and limits CREATE TABLE IF NOT EXISTS tenant_quotas ( tenant_id TEXT PRIMARY KEY, max_projects INTEGER DEFAULT 3, max_agents_per_project INTEGER DEFAULT 5, max_concurrent_executions INTEGER DEFAULT 2, max_storage_bytes INTEGER DEFAULT 1073741824, -- 1GB max_api_calls_per_day INTEGER DEFAULT 10000, max_tokens_per_day INTEGER DEFAULT 1000000, cost_budget_cents INTEGER DEFAULT 10000, -- $100 updated_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE ); -- Tenant usage tracking CREATE TABLE IF NOT EXISTS tenant_usage ( id INTEGER PRIMARY KEY AUTOINCREMENT, tenant_id TEXT NOT NULL, period_start TEXT NOT NULL, -- YYYY-MM-DD api_calls INTEGER DEFAULT 0, tokens_used INTEGER DEFAULT 0, storage_bytes INTEGER DEFAULT 0, executions INTEGER DEFAULT 0, cost_cents INTEGER DEFAULT 0, updated_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE, UNIQUE(tenant_id, period_start) ); -- ============================================================ -- RBAC: PROJECT MEMBERS & ROLES -- ============================================================ -- Project membership and roles CREATE TABLE IF NOT EXISTS project_members ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_id TEXT NOT NULL, user_id TEXT NOT NULL, -- External user identifier role TEXT NOT NULL DEFAULT 'viewer', -- viewer, editor, admin, owner invited_by TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE, UNIQUE(project_id, user_id) ); -- API keys for programmatic access CREATE TABLE IF NOT EXISTS api_keys ( key_id TEXT PRIMARY KEY, tenant_id TEXT NOT NULL, project_id TEXT, -- NULL = tenant-wide access key_hash TEXT NOT NULL, -- SHA256 of actual key name TEXT NOT NULL, permissions TEXT, -- JSON: allowed actions rate_limit_per_minute INTEGER DEFAULT 60, expires_at TEXT, last_used_at TEXT, is_active INTEGER DEFAULT 1, created_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (tenant_id) REFERENCES tenants(tenant_id) ON DELETE CASCADE, FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE SET NULL ); -- ============================================================ -- ADD TENANT/PROJECT TO EXISTING TABLES -- ============================================================ -- Add columns to agent_metrics ALTER TABLE agent_metrics ADD COLUMN tenant_id TEXT DEFAULT 'default'; ALTER TABLE agent_metrics ADD COLUMN project_id TEXT DEFAULT 'default'; -- Add columns to agent_actions ALTER TABLE agent_actions ADD COLUMN tenant_id TEXT DEFAULT 'default'; ALTER TABLE agent_actions ADD COLUMN project_id TEXT DEFAULT 'default'; -- Add columns to violations ALTER TABLE violations ADD COLUMN tenant_id TEXT DEFAULT 'default'; ALTER TABLE violations ADD COLUMN project_id TEXT DEFAULT 'default'; -- Add columns to promotions ALTER TABLE promotions ADD COLUMN tenant_id TEXT DEFAULT 'default'; ALTER TABLE promotions ADD COLUMN project_id TEXT DEFAULT 'default'; -- ============================================================ -- INDEXES FOR MULTI-TENANT QUERIES -- ============================================================ CREATE INDEX IF NOT EXISTS idx_projects_tenant ON projects(tenant_id); CREATE INDEX IF NOT EXISTS idx_tenant_usage_tenant ON tenant_usage(tenant_id); CREATE INDEX IF NOT EXISTS idx_tenant_usage_period ON tenant_usage(period_start); CREATE INDEX IF NOT EXISTS idx_project_members_project ON project_members(project_id); CREATE INDEX IF NOT EXISTS idx_project_members_user ON project_members(user_id); CREATE INDEX IF NOT EXISTS idx_api_keys_tenant ON api_keys(tenant_id); CREATE INDEX IF NOT EXISTS idx_api_keys_project ON api_keys(project_id); -- Indexes for tenant filtering on existing tables CREATE INDEX IF NOT EXISTS idx_agent_metrics_tenant ON agent_metrics(tenant_id); CREATE INDEX IF NOT EXISTS idx_agent_metrics_project ON agent_metrics(project_id); CREATE INDEX IF NOT EXISTS idx_agent_actions_tenant ON agent_actions(tenant_id); CREATE INDEX IF NOT EXISTS idx_agent_actions_project ON agent_actions(project_id); CREATE INDEX IF NOT EXISTS idx_violations_tenant ON violations(tenant_id); CREATE INDEX IF NOT EXISTS idx_violations_project ON violations(project_id); CREATE INDEX IF NOT EXISTS idx_promotions_tenant ON promotions(tenant_id); CREATE INDEX IF NOT EXISTS idx_promotions_project ON promotions(project_id); -- ============================================================ -- DEFAULT TENANT & PROJECT -- ============================================================ -- Create default tenant for existing data INSERT OR IGNORE INTO tenants (tenant_id, name, slug, subscription_tier) VALUES ('default', 'Default Tenant', 'default', 'enterprise'); -- Create default project for existing data INSERT OR IGNORE INTO projects (project_id, tenant_id, name, slug) VALUES ('default', 'default', 'Default Project', 'default'); -- Create default quotas (unlimited for default tenant) INSERT OR IGNORE INTO tenant_quotas ( tenant_id, max_projects, max_agents_per_project, max_concurrent_executions, max_storage_bytes, max_api_calls_per_day, max_tokens_per_day, cost_budget_cents ) VALUES ( 'default', 100, 100, 50, 107374182400, 1000000, 100000000, 1000000 );