agent-governance/ledger/migrations/001_multi_tenant.sql
profit 8c6e7831e9 Add Phase 10-12 implementation: multi-tenant, marketplace, observability
Major additions:
- marketplace/: Agent template registry with FTS5 search, ratings, versioning
- observability/: Prometheus metrics, distributed tracing, structured logging
- ledger/migrations/: Database migration scripts for multi-tenant support
- tests/governance/: 15 new test files for phases 6-12 (295 total tests)
- bin/validate-phases: Full 12-phase validation script

New features:
- Multi-tenant support with tenant isolation and quota enforcement
- Agent marketplace with semantic versioning and search
- Observability with metrics, tracing, and log correlation
- Tier-1 agent bootstrap scripts

Updated components:
- ledger/api.py: Extended API for tenants, marketplace, observability
- ledger/schema.sql: Added tenant, project, marketplace tables
- testing/framework.ts: Enhanced test framework
- checkpoint/checkpoint.py: Improved checkpoint management

Archived:
- External integrations (Slack/GitHub/PagerDuty) moved to .archive/
- Old checkpoint files cleaned up

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:39:47 -05:00

160 lines
6.5 KiB
SQL

-- 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
);