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>
160 lines
6.5 KiB
SQL
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
|
|
);
|