From e880750247181cf627c8f47eea5759715fa290ec Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:53:33 +0000 Subject: [PATCH 1/4] Initial plan From 4798477838aba9c504070199d584bc6fee4d73f0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:59:15 +0000 Subject: [PATCH 2/4] Add audit log architecture and multi-tenant isolation strategy Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- packages/spec/src/system/audit.test.ts | 598 ++++++++++++++++++++ packages/spec/src/system/audit.zod.ts | 702 ++++++++++++++++++++++++ packages/spec/src/system/index.ts | 1 + packages/spec/src/system/tenant.test.ts | 323 +++++++++++ packages/spec/src/system/tenant.zod.ts | 481 ++++++++++++++++ 5 files changed, 2105 insertions(+) create mode 100644 packages/spec/src/system/audit.test.ts create mode 100644 packages/spec/src/system/audit.zod.ts diff --git a/packages/spec/src/system/audit.test.ts b/packages/spec/src/system/audit.test.ts new file mode 100644 index 000000000..bee7a45fa --- /dev/null +++ b/packages/spec/src/system/audit.test.ts @@ -0,0 +1,598 @@ +import { describe, it, expect } from 'vitest'; +import { + AuditEventType, + AuditEventSeverity, + AuditEventActorSchema, + AuditEventTargetSchema, + AuditEventChangeSchema, + AuditEventSchema, + AuditRetentionPolicySchema, + SuspiciousActivityRuleSchema, + AuditStorageConfigSchema, + AuditEventFilterSchema, + AuditConfigSchema, + DEFAULT_SUSPICIOUS_ACTIVITY_RULES, + type AuditEvent, + type AuditConfig, + type SuspiciousActivityRule, +} from './audit.zod'; + +describe('AuditEventType', () => { + it('should accept valid event types', () => { + const validTypes = [ + 'data.create', + 'data.read', + 'data.update', + 'data.delete', + 'auth.login', + 'auth.login_failed', + 'auth.logout', + 'authz.permission_granted', + 'authz.role_assigned', + 'security.access_denied', + ]; + + validTypes.forEach((type) => { + expect(() => AuditEventType.parse(type)).not.toThrow(); + }); + }); + + it('should reject invalid event types', () => { + expect(() => AuditEventType.parse('invalid.type')).toThrow(); + expect(() => AuditEventType.parse('dataCreate')).toThrow(); + }); +}); + +describe('AuditEventSeverity', () => { + it('should accept valid severity levels', () => { + const validLevels = ['debug', 'info', 'notice', 'warning', 'error', 'critical', 'alert', 'emergency']; + + validLevels.forEach((level) => { + expect(() => AuditEventSeverity.parse(level)).not.toThrow(); + }); + }); + + it('should reject invalid severity levels', () => { + expect(() => AuditEventSeverity.parse('invalid')).toThrow(); + expect(() => AuditEventSeverity.parse('high')).toThrow(); + }); +}); + +describe('AuditEventActorSchema', () => { + it('should accept valid actor configuration', () => { + const validActor = { + type: 'user', + id: 'user_123', + name: 'John Doe', + email: 'john@example.com', + ipAddress: '192.168.1.1', + userAgent: 'Mozilla/5.0', + }; + + expect(() => AuditEventActorSchema.parse(validActor)).not.toThrow(); + }); + + it('should accept minimal actor configuration', () => { + const minimalActor = { + type: 'system', + id: 'system', + }; + + expect(() => AuditEventActorSchema.parse(minimalActor)).not.toThrow(); + }); + + it('should reject invalid email', () => { + const invalidActor = { + type: 'user', + id: 'user_123', + email: 'not-an-email', + }; + + expect(() => AuditEventActorSchema.parse(invalidActor)).toThrow(); + }); +}); + +describe('AuditEventTargetSchema', () => { + it('should accept valid target configuration', () => { + const validTarget = { + type: 'record', + id: 'rec_456', + name: 'Customer Record #456', + metadata: { + objectName: 'customer', + fields: ['name', 'email'], + }, + }; + + expect(() => AuditEventTargetSchema.parse(validTarget)).not.toThrow(); + }); + + it('should accept minimal target configuration', () => { + const minimalTarget = { + type: 'object', + id: 'obj_789', + }; + + expect(() => AuditEventTargetSchema.parse(minimalTarget)).not.toThrow(); + }); +}); + +describe('AuditEventChangeSchema', () => { + it('should accept valid change record', () => { + const validChange = { + field: 'email', + oldValue: 'old@example.com', + newValue: 'new@example.com', + }; + + expect(() => AuditEventChangeSchema.parse(validChange)).not.toThrow(); + }); + + it('should accept change with only new value', () => { + const createChange = { + field: 'name', + newValue: 'John Doe', + }; + + expect(() => AuditEventChangeSchema.parse(createChange)).not.toThrow(); + }); +}); + +describe('AuditEventSchema', () => { + it('should accept complete audit event', () => { + const validEvent: AuditEvent = { + id: 'evt_123', + eventType: 'data.update', + severity: 'info', + timestamp: new Date().toISOString(), + actor: { + type: 'user', + id: 'user_123', + email: 'user@example.com', + }, + target: { + type: 'record', + id: 'rec_456', + }, + description: 'User updated customer record', + changes: [ + { + field: 'email', + oldValue: 'old@example.com', + newValue: 'new@example.com', + }, + ], + result: 'success', + tenantId: 'tenant_789', + requestId: 'req_abc', + }; + + expect(() => AuditEventSchema.parse(validEvent)).not.toThrow(); + }); + + it('should accept minimal audit event', () => { + const minimalEvent = { + id: 'evt_456', + eventType: 'auth.login', + timestamp: new Date().toISOString(), + actor: { + type: 'user', + id: 'user_123', + }, + description: 'User logged in', + }; + + expect(() => AuditEventSchema.parse(minimalEvent)).not.toThrow(); + }); + + it('should default severity to info', () => { + const event = { + id: 'evt_789', + eventType: 'data.read', + timestamp: new Date().toISOString(), + actor: { + type: 'user', + id: 'user_123', + }, + description: 'User viewed record', + }; + + const parsed = AuditEventSchema.parse(event); + expect(parsed.severity).toBe('info'); + }); + + it('should default result to success', () => { + const event = { + id: 'evt_101', + eventType: 'data.create', + timestamp: new Date().toISOString(), + actor: { + type: 'user', + id: 'user_123', + }, + description: 'User created record', + }; + + const parsed = AuditEventSchema.parse(event); + expect(parsed.result).toBe('success'); + }); + + it('should accept failed event with error message', () => { + const failedEvent = { + id: 'evt_202', + eventType: 'auth.login', + severity: 'warning', + timestamp: new Date().toISOString(), + actor: { + type: 'user', + id: 'user_123', + }, + description: 'Login attempt failed', + result: 'failure', + errorMessage: 'Invalid password', + }; + + expect(() => AuditEventSchema.parse(failedEvent)).not.toThrow(); + }); +}); + +describe('AuditRetentionPolicySchema', () => { + it('should accept valid retention policy', () => { + const validPolicy = { + retentionDays: 365, + archiveAfterRetention: true, + archiveStorage: { + type: 's3', + bucket: 'audit-logs-archive', + path: 'logs/', + }, + customRetention: { + 'auth.login_failed': 730, // 2 years + 'security.data_breach': 2555, // 7 years + }, + minimumRetentionDays: 180, + }; + + expect(() => AuditRetentionPolicySchema.parse(validPolicy)).not.toThrow(); + }); + + it('should default to 180 days retention', () => { + const policy = {}; + const parsed = AuditRetentionPolicySchema.parse(policy); + expect(parsed.retentionDays).toBe(180); + }); + + it('should default to archiving enabled', () => { + const policy = {}; + const parsed = AuditRetentionPolicySchema.parse(policy); + expect(parsed.archiveAfterRetention).toBe(true); + }); + + it('should reject negative retention days', () => { + const invalidPolicy = { + retentionDays: -30, + }; + + expect(() => AuditRetentionPolicySchema.parse(invalidPolicy)).toThrow(); + }); +}); + +describe('SuspiciousActivityRuleSchema', () => { + it('should accept valid suspicious activity rule', () => { + const validRule: SuspiciousActivityRule = { + id: 'rule_123', + name: 'Multiple Failed Logins', + description: 'Detects brute force attacks', + enabled: true, + eventTypes: ['auth.login_failed'], + condition: { + threshold: 5, + windowSeconds: 600, + groupBy: ['actor.id'], + }, + actions: ['alert', 'lock_account'], + alertSeverity: 'critical', + notifications: { + email: ['security@example.com'], + slack: 'https://hooks.slack.com/services/xxx', + }, + }; + + expect(() => SuspiciousActivityRuleSchema.parse(validRule)).not.toThrow(); + }); + + it('should default enabled to true', () => { + const rule = { + id: 'rule_456', + name: 'Test Rule', + eventTypes: ['data.export'], + condition: { + threshold: 10, + windowSeconds: 3600, + }, + actions: ['alert'], + }; + + const parsed = SuspiciousActivityRuleSchema.parse(rule); + expect(parsed.enabled).toBe(true); + }); + + it('should default alert severity to warning', () => { + const rule = { + id: 'rule_789', + name: 'Test Rule', + eventTypes: ['data.delete'], + condition: { + threshold: 3, + windowSeconds: 300, + }, + actions: ['alert'], + }; + + const parsed = SuspiciousActivityRuleSchema.parse(rule); + expect(parsed.alertSeverity).toBe('warning'); + }); +}); + +describe('AuditStorageConfigSchema', () => { + it('should accept database storage configuration', () => { + const dbStorage = { + type: 'database', + connectionString: 'postgresql://localhost:5432/audit', + bufferEnabled: true, + bufferSize: 100, + flushIntervalSeconds: 5, + compression: true, + }; + + expect(() => AuditStorageConfigSchema.parse(dbStorage)).not.toThrow(); + }); + + it('should accept elasticsearch storage configuration', () => { + const esStorage = { + type: 'elasticsearch', + connectionString: 'https://elasticsearch:9200', + config: { + index: 'audit-logs', + shards: 5, + }, + }; + + expect(() => AuditStorageConfigSchema.parse(esStorage)).not.toThrow(); + }); + + it('should default buffer settings', () => { + const storage = { + type: 'mongodb', + }; + + const parsed = AuditStorageConfigSchema.parse(storage); + expect(parsed.bufferEnabled).toBe(true); + expect(parsed.bufferSize).toBe(100); + expect(parsed.flushIntervalSeconds).toBe(5); + expect(parsed.compression).toBe(true); + }); +}); + +describe('AuditEventFilterSchema', () => { + it('should accept complete filter configuration', () => { + const validFilter = { + eventTypes: ['data.create', 'data.update'], + severities: ['warning', 'error', 'critical'], + actorId: 'user_123', + tenantId: 'tenant_456', + timeRange: { + from: '2024-01-01T00:00:00Z', + to: '2024-12-31T23:59:59Z', + }, + result: 'failure', + searchQuery: 'password reset', + }; + + expect(() => AuditEventFilterSchema.parse(validFilter)).not.toThrow(); + }); + + it('should accept minimal filter configuration', () => { + const minimalFilter = {}; + expect(() => AuditEventFilterSchema.parse(minimalFilter)).not.toThrow(); + }); + + it('should accept partial filters', () => { + const partialFilter = { + eventTypes: ['auth.login'], + timeRange: { + from: '2024-01-01T00:00:00Z', + to: '2024-01-31T23:59:59Z', + }, + }; + + expect(() => AuditEventFilterSchema.parse(partialFilter)).not.toThrow(); + }); +}); + +describe('AuditConfigSchema', () => { + it('should accept complete audit configuration', () => { + const validConfig: AuditConfig = { + name: 'main_audit', + label: 'Main Audit Configuration', + enabled: true, + eventTypes: ['data.create', 'data.update', 'auth.login'], + minimumSeverity: 'info', + storage: { + type: 'database', + connectionString: 'postgresql://localhost/audit', + }, + retentionPolicy: { + retentionDays: 365, + }, + suspiciousActivityRules: [ + { + id: 'failed_logins', + name: 'Failed Logins', + eventTypes: ['auth.login_failed'], + condition: { + threshold: 5, + windowSeconds: 600, + }, + actions: ['alert'], + }, + ], + includeSensitiveData: false, + logReads: false, + compliance: { + standards: ['gdpr', 'sox'], + immutableLogs: true, + }, + }; + + expect(() => AuditConfigSchema.parse(validConfig)).not.toThrow(); + }); + + it('should accept minimal audit configuration', () => { + const minimalConfig = { + name: 'basic_audit', + label: 'Basic Audit', + storage: { + type: 'database', + }, + }; + + expect(() => AuditConfigSchema.parse(minimalConfig)).not.toThrow(); + }); + + it('should enforce snake_case naming convention', () => { + const invalidConfig = { + name: 'mainAudit', // camelCase, should be snake_case + label: 'Main Audit', + storage: { + type: 'database', + }, + }; + + expect(() => AuditConfigSchema.parse(invalidConfig)).toThrow(); + }); + + it('should accept valid snake_case names', () => { + const validNames = ['audit_config', 'main_audit', 'security_audit_log']; + + validNames.forEach((name) => { + const config = { + name, + label: 'Test Audit', + storage: { type: 'database' }, + }; + expect(() => AuditConfigSchema.parse(config)).not.toThrow(); + }); + }); + + it('should default enabled to true', () => { + const config = { + name: 'test_audit', + label: 'Test Audit', + storage: { type: 'database' }, + }; + + const parsed = AuditConfigSchema.parse(config); + expect(parsed.enabled).toBe(true); + }); + + it('should default minimumSeverity to info', () => { + const config = { + name: 'test_audit', + label: 'Test Audit', + storage: { type: 'database' }, + }; + + const parsed = AuditConfigSchema.parse(config); + expect(parsed.minimumSeverity).toBe('info'); + }); + + it('should default redactFields to common sensitive fields', () => { + const config = { + name: 'test_audit', + label: 'Test Audit', + storage: { type: 'database' }, + }; + + const parsed = AuditConfigSchema.parse(config); + expect(parsed.redactFields).toContain('password'); + expect(parsed.redactFields).toContain('token'); + expect(parsed.redactFields).toContain('apiKey'); + }); + + it('should default logReads to false', () => { + const config = { + name: 'test_audit', + label: 'Test Audit', + storage: { type: 'database' }, + }; + + const parsed = AuditConfigSchema.parse(config); + expect(parsed.logReads).toBe(false); + }); + + it('should default readSamplingRate to 0.1', () => { + const config = { + name: 'test_audit', + label: 'Test Audit', + storage: { type: 'database' }, + }; + + const parsed = AuditConfigSchema.parse(config); + expect(parsed.readSamplingRate).toBe(0.1); + }); + + it('should accept compliance configuration', () => { + const config = { + name: 'compliance_audit', + label: 'Compliance Audit', + storage: { type: 'database' }, + compliance: { + standards: ['sox', 'hipaa', 'gdpr'], + immutableLogs: true, + requireSigning: true, + signingKey: 'secret-key-123', + }, + }; + + expect(() => AuditConfigSchema.parse(config)).not.toThrow(); + }); +}); + +describe('DEFAULT_SUSPICIOUS_ACTIVITY_RULES', () => { + it('should have valid default rules', () => { + expect(DEFAULT_SUSPICIOUS_ACTIVITY_RULES.length).toBeGreaterThan(0); + + DEFAULT_SUSPICIOUS_ACTIVITY_RULES.forEach((rule) => { + expect(() => SuspiciousActivityRuleSchema.parse(rule)).not.toThrow(); + }); + }); + + it('should include multiple failed logins rule', () => { + const rule = DEFAULT_SUSPICIOUS_ACTIVITY_RULES.find( + (r) => r.id === 'multiple_failed_logins' + ); + + expect(rule).toBeDefined(); + expect(rule?.eventTypes).toContain('auth.login_failed'); + expect(rule?.condition.threshold).toBe(5); + }); + + it('should include bulk data export rule', () => { + const rule = DEFAULT_SUSPICIOUS_ACTIVITY_RULES.find( + (r) => r.id === 'bulk_data_export' + ); + + expect(rule).toBeDefined(); + expect(rule?.eventTypes).toContain('data.export'); + }); + + it('should include permission changes rule', () => { + const rule = DEFAULT_SUSPICIOUS_ACTIVITY_RULES.find( + (r) => r.id === 'suspicious_permission_changes' + ); + + expect(rule).toBeDefined(); + expect(rule?.actions).toContain('alert'); + }); +}); diff --git a/packages/spec/src/system/audit.zod.ts b/packages/spec/src/system/audit.zod.ts new file mode 100644 index 000000000..2e1c18b9b --- /dev/null +++ b/packages/spec/src/system/audit.zod.ts @@ -0,0 +1,702 @@ +import { z } from 'zod'; + +/** + * Audit Log Architecture + * + * Comprehensive audit logging system for compliance and security. + * Supports SOX, HIPAA, GDPR, and other regulatory requirements. + * + * Features: + * - Records all CRUD operations on data + * - Tracks authentication events (login, logout, password reset) + * - Monitors authorization changes (permissions, roles) + * - Configurable retention policies (180-day GDPR requirement) + * - Suspicious activity detection and alerting + */ + +/** + * Audit Event Type Enum + * Categorizes different types of auditable events in the system + */ +export const AuditEventType = z.enum([ + // Data Operations (CRUD) + 'data.create', // Record creation + 'data.read', // Record retrieval/viewing + 'data.update', // Record modification + 'data.delete', // Record deletion + 'data.export', // Data export operations + 'data.import', // Data import operations + 'data.bulk_update', // Bulk update operations + 'data.bulk_delete', // Bulk delete operations + + // Authentication Events + 'auth.login', // Successful login + 'auth.login_failed', // Failed login attempt + 'auth.logout', // User logout + 'auth.session_created', // New session created + 'auth.session_expired', // Session expiration + 'auth.password_reset', // Password reset initiated + 'auth.password_changed', // Password successfully changed + 'auth.email_verified', // Email verification completed + 'auth.mfa_enabled', // Multi-factor auth enabled + 'auth.mfa_disabled', // Multi-factor auth disabled + 'auth.account_locked', // Account locked (too many failures) + 'auth.account_unlocked', // Account unlocked + + // Authorization Events + 'authz.permission_granted', // Permission granted to user + 'authz.permission_revoked', // Permission revoked from user + 'authz.role_assigned', // Role assigned to user + 'authz.role_removed', // Role removed from user + 'authz.role_created', // New role created + 'authz.role_updated', // Role permissions modified + 'authz.role_deleted', // Role deleted + 'authz.policy_created', // Security policy created + 'authz.policy_updated', // Security policy updated + 'authz.policy_deleted', // Security policy deleted + + // System Events + 'system.config_changed', // System configuration modified + 'system.plugin_installed', // Plugin installed + 'system.plugin_uninstalled', // Plugin uninstalled + 'system.backup_created', // Backup created + 'system.backup_restored', // Backup restored + 'system.integration_added', // External integration added + 'system.integration_removed',// External integration removed + + // Security Events + 'security.access_denied', // Access denied (authorization failure) + 'security.suspicious_activity', // Suspicious activity detected + 'security.data_breach', // Potential data breach detected + 'security.api_key_created', // API key created + 'security.api_key_revoked', // API key revoked +]); + +export type AuditEventType = z.infer; + +/** + * Audit Event Severity Level + * Indicates the importance/criticality of an audit event + */ +export const AuditEventSeverity = z.enum([ + 'debug', // Diagnostic information + 'info', // Informational events (normal operations) + 'notice', // Normal but significant events + 'warning', // Warning conditions + 'error', // Error conditions + 'critical', // Critical conditions requiring immediate attention + 'alert', // Action must be taken immediately + 'emergency', // System is unusable +]); + +export type AuditEventSeverity = z.infer; + +/** + * Audit Event Actor Schema + * Identifies who/what performed the action + */ +export const AuditEventActorSchema = z.object({ + /** + * Actor type (user, system, service, api_client, etc.) + */ + type: z.enum(['user', 'system', 'service', 'api_client', 'integration']).describe('Actor type'), + + /** + * Unique identifier for the actor + */ + id: z.string().describe('Actor identifier'), + + /** + * Display name of the actor + */ + name: z.string().optional().describe('Actor display name'), + + /** + * Email address (for user actors) + */ + email: z.string().email().optional().describe('Actor email address'), + + /** + * IP address of the actor + */ + ipAddress: z.string().optional().describe('Actor IP address'), + + /** + * User agent string (for web/API requests) + */ + userAgent: z.string().optional().describe('User agent string'), +}); + +export type AuditEventActor = z.infer; + +/** + * Audit Event Target Schema + * Identifies what was acted upon + */ +export const AuditEventTargetSchema = z.object({ + /** + * Target type (e.g., 'object', 'record', 'user', 'role', 'config') + */ + type: z.string().describe('Target type'), + + /** + * Unique identifier for the target + */ + id: z.string().describe('Target identifier'), + + /** + * Display name of the target + */ + name: z.string().optional().describe('Target display name'), + + /** + * Additional metadata about the target + */ + metadata: z.record(z.any()).optional().describe('Target metadata'), +}); + +export type AuditEventTarget = z.infer; + +/** + * Audit Event Change Schema + * Describes what changed (for update operations) + */ +export const AuditEventChangeSchema = z.object({ + /** + * Field/property that changed + */ + field: z.string().describe('Changed field name'), + + /** + * Value before the change + */ + oldValue: z.any().optional().describe('Previous value'), + + /** + * Value after the change + */ + newValue: z.any().optional().describe('New value'), +}); + +export type AuditEventChange = z.infer; + +/** + * Audit Event Schema + * Complete audit event record + */ +export const AuditEventSchema = z.object({ + /** + * Unique identifier for this audit event + */ + id: z.string().describe('Audit event ID'), + + /** + * Type of event being audited + */ + eventType: AuditEventType.describe('Event type'), + + /** + * Severity level of the event + */ + severity: AuditEventSeverity.default('info').describe('Event severity'), + + /** + * Timestamp when the event occurred (ISO 8601) + */ + timestamp: z.string().datetime().describe('Event timestamp'), + + /** + * Who/what performed the action + */ + actor: AuditEventActorSchema.describe('Event actor'), + + /** + * What was acted upon + */ + target: AuditEventTargetSchema.optional().describe('Event target'), + + /** + * Human-readable description of the action + */ + description: z.string().describe('Event description'), + + /** + * Detailed changes (for update operations) + */ + changes: z.array(AuditEventChangeSchema).optional().describe('List of changes'), + + /** + * Result of the action (success, failure, partial) + */ + result: z.enum(['success', 'failure', 'partial']).default('success').describe('Action result'), + + /** + * Error message (if result is failure) + */ + errorMessage: z.string().optional().describe('Error message'), + + /** + * Tenant identifier (for multi-tenant systems) + */ + tenantId: z.string().optional().describe('Tenant identifier'), + + /** + * Request/trace ID for correlation + */ + requestId: z.string().optional().describe('Request ID for tracing'), + + /** + * Additional context and metadata + */ + metadata: z.record(z.any()).optional().describe('Additional metadata'), + + /** + * Geographic location (if available) + */ + location: z.object({ + country: z.string().optional(), + region: z.string().optional(), + city: z.string().optional(), + }).optional().describe('Geographic location'), +}); + +export type AuditEvent = z.infer; + +/** + * Audit Retention Policy Schema + * Defines how long audit logs are retained + */ +export const AuditRetentionPolicySchema = z.object({ + /** + * Retention period in days + * Default: 180 days (GDPR 6-month requirement) + */ + retentionDays: z.number().int().positive().default(180).describe('Retention period in days'), + + /** + * Whether to archive logs after retention period + * If true, logs are moved to cold storage; if false, they are deleted + */ + archiveAfterRetention: z.boolean().default(true).describe('Archive logs after retention period'), + + /** + * Archive storage configuration + */ + archiveStorage: z.object({ + type: z.enum(['s3', 'gcs', 'azure_blob', 'filesystem']).describe('Archive storage type'), + endpoint: z.string().optional().describe('Storage endpoint URL'), + bucket: z.string().optional().describe('Storage bucket/container name'), + path: z.string().optional().describe('Storage path prefix'), + credentials: z.record(z.any()).optional().describe('Storage credentials'), + }).optional().describe('Archive storage configuration'), + + /** + * Event types that have different retention periods + * Overrides the default retentionDays for specific event types + */ + customRetention: z.record(z.number().int().positive()).optional().describe('Custom retention by event type'), + + /** + * Minimum retention period for compliance + * Prevents accidental deletion below compliance requirements + */ + minimumRetentionDays: z.number().int().positive().optional().describe('Minimum retention for compliance'), +}); + +export type AuditRetentionPolicy = z.infer; + +/** + * Suspicious Activity Rule Schema + * Defines rules for detecting suspicious activities + */ +export const SuspiciousActivityRuleSchema = z.object({ + /** + * Unique identifier for the rule + */ + id: z.string().describe('Rule identifier'), + + /** + * Rule name + */ + name: z.string().describe('Rule name'), + + /** + * Rule description + */ + description: z.string().optional().describe('Rule description'), + + /** + * Whether the rule is enabled + */ + enabled: z.boolean().default(true).describe('Rule enabled status'), + + /** + * Event types to monitor + */ + eventTypes: z.array(AuditEventType).describe('Event types to monitor'), + + /** + * Detection condition + */ + condition: z.object({ + /** + * Number of events that trigger the rule + */ + threshold: z.number().int().positive().describe('Event threshold'), + + /** + * Time window in seconds + */ + windowSeconds: z.number().int().positive().describe('Time window in seconds'), + + /** + * Grouping criteria (e.g., by actor.id, by ipAddress) + */ + groupBy: z.array(z.string()).optional().describe('Grouping criteria'), + + /** + * Additional filters + */ + filters: z.record(z.any()).optional().describe('Additional filters'), + }).describe('Detection condition'), + + /** + * Actions to take when rule is triggered + */ + actions: z.array(z.enum([ + 'alert', // Send alert notification + 'lock_account', // Lock the user account + 'block_ip', // Block the IP address + 'require_mfa', // Require multi-factor authentication + 'log_critical', // Log as critical event + 'webhook', // Call webhook + ])).describe('Actions to take'), + + /** + * Severity level for triggered alerts + */ + alertSeverity: AuditEventSeverity.default('warning').describe('Alert severity'), + + /** + * Notification configuration + */ + notifications: z.object({ + /** + * Email addresses to notify + */ + email: z.array(z.string().email()).optional().describe('Email recipients'), + + /** + * Slack webhook URL + */ + slack: z.string().url().optional().describe('Slack webhook URL'), + + /** + * Custom webhook URL + */ + webhook: z.string().url().optional().describe('Custom webhook URL'), + }).optional().describe('Notification configuration'), +}); + +export type SuspiciousActivityRule = z.infer; + +/** + * Audit Log Storage Configuration + * Defines where and how audit logs are stored + */ +export const AuditStorageConfigSchema = z.object({ + /** + * Storage backend type + */ + type: z.enum([ + 'database', // Store in database (PostgreSQL, MySQL, etc.) + 'elasticsearch', // Store in Elasticsearch + 'mongodb', // Store in MongoDB + 'clickhouse', // Store in ClickHouse (for analytics) + 's3', // Store in S3-compatible storage + 'gcs', // Store in Google Cloud Storage + 'azure_blob', // Store in Azure Blob Storage + 'custom', // Custom storage implementation + ]).describe('Storage backend type'), + + /** + * Connection string or configuration + */ + connectionString: z.string().optional().describe('Connection string'), + + /** + * Storage configuration + */ + config: z.record(z.any()).optional().describe('Storage-specific configuration'), + + /** + * Whether to enable buffering/batching + */ + bufferEnabled: z.boolean().default(true).describe('Enable buffering'), + + /** + * Buffer size (number of events before flush) + */ + bufferSize: z.number().int().positive().default(100).describe('Buffer size'), + + /** + * Buffer flush interval in seconds + */ + flushIntervalSeconds: z.number().int().positive().default(5).describe('Flush interval in seconds'), + + /** + * Whether to compress stored data + */ + compression: z.boolean().default(true).describe('Enable compression'), +}); + +export type AuditStorageConfig = z.infer; + +/** + * Audit Event Filter Schema + * Defines filters for querying audit events + */ +export const AuditEventFilterSchema = z.object({ + /** + * Filter by event types + */ + eventTypes: z.array(AuditEventType).optional().describe('Event types to include'), + + /** + * Filter by severity levels + */ + severities: z.array(AuditEventSeverity).optional().describe('Severity levels to include'), + + /** + * Filter by actor ID + */ + actorId: z.string().optional().describe('Actor identifier'), + + /** + * Filter by tenant ID + */ + tenantId: z.string().optional().describe('Tenant identifier'), + + /** + * Filter by time range + */ + timeRange: z.object({ + from: z.string().datetime().describe('Start time'), + to: z.string().datetime().describe('End time'), + }).optional().describe('Time range filter'), + + /** + * Filter by result status + */ + result: z.enum(['success', 'failure', 'partial']).optional().describe('Result status'), + + /** + * Search query (full-text search) + */ + searchQuery: z.string().optional().describe('Search query'), + + /** + * Custom filters + */ + customFilters: z.record(z.any()).optional().describe('Custom filters'), +}); + +export type AuditEventFilter = z.infer; + +/** + * Complete Audit Configuration Schema + * Main configuration for the audit system + */ +export const AuditConfigSchema = z.object({ + /** + * Unique identifier for this audit configuration + */ + name: z.string() + .regex(/^[a-z_][a-z0-9_]*$/) + .describe('Configuration name (snake_case)'), + + /** + * Human-readable label + */ + label: z.string().describe('Display label'), + + /** + * Whether audit logging is enabled + */ + enabled: z.boolean().default(true).describe('Enable audit logging'), + + /** + * Event types to audit + * If not specified, all event types are audited + */ + eventTypes: z.array(AuditEventType).optional().describe('Event types to audit'), + + /** + * Event types to exclude from auditing + */ + excludeEventTypes: z.array(AuditEventType).optional().describe('Event types to exclude'), + + /** + * Minimum severity level to log + * Events below this level are not logged + */ + minimumSeverity: AuditEventSeverity.default('info').describe('Minimum severity level'), + + /** + * Storage configuration + */ + storage: AuditStorageConfigSchema.describe('Storage configuration'), + + /** + * Retention policy + */ + retentionPolicy: AuditRetentionPolicySchema.default({}).describe('Retention policy'), + + /** + * Suspicious activity detection rules + */ + suspiciousActivityRules: z.array(SuspiciousActivityRuleSchema).default([]).describe('Suspicious activity rules'), + + /** + * Whether to include sensitive data in audit logs + * If false, sensitive fields are redacted/masked + */ + includeSensitiveData: z.boolean().default(false).describe('Include sensitive data'), + + /** + * Fields to redact from audit logs + */ + redactFields: z.array(z.string()).default([ + 'password', + 'passwordHash', + 'token', + 'apiKey', + 'secret', + 'creditCard', + 'ssn', + ]).describe('Fields to redact'), + + /** + * Whether to log successful read operations + * Can be disabled to reduce log volume + */ + logReads: z.boolean().default(false).describe('Log read operations'), + + /** + * Sampling rate for read operations (0.0 to 1.0) + * Only applies if logReads is true + */ + readSamplingRate: z.number().min(0).max(1).default(0.1).describe('Read sampling rate'), + + /** + * Whether to log system/internal operations + */ + logSystemEvents: z.boolean().default(true).describe('Log system events'), + + /** + * Custom audit event handlers + */ + customHandlers: z.array(z.object({ + eventType: AuditEventType.describe('Event type to handle'), + handler: z.function() + .args(AuditEventSchema) + .returns(z.promise(z.void())) + .describe('Handler function'), + })).optional().describe('Custom event handlers'), + + /** + * Compliance mode configuration + */ + compliance: z.object({ + /** + * Compliance standards to enforce + */ + standards: z.array(z.enum([ + 'sox', // Sarbanes-Oxley Act + 'hipaa', // Health Insurance Portability and Accountability Act + 'gdpr', // General Data Protection Regulation + 'pci_dss', // Payment Card Industry Data Security Standard + 'iso_27001',// ISO/IEC 27001 + 'fedramp', // Federal Risk and Authorization Management Program + ])).optional().describe('Compliance standards'), + + /** + * Whether to enforce immutable audit logs + */ + immutableLogs: z.boolean().default(true).describe('Enforce immutable logs'), + + /** + * Whether to require cryptographic signing + */ + requireSigning: z.boolean().default(false).describe('Require log signing'), + + /** + * Signing key configuration + */ + signingKey: z.string().optional().describe('Signing key'), + }).optional().describe('Compliance configuration'), +}); + +export type AuditConfig = z.infer; + +/** + * Default suspicious activity rules + * Common security patterns to detect + */ +export const DEFAULT_SUSPICIOUS_ACTIVITY_RULES: SuspiciousActivityRule[] = [ + { + id: 'multiple_failed_logins', + name: 'Multiple Failed Login Attempts', + description: 'Detects multiple failed login attempts from the same user or IP', + enabled: true, + eventTypes: ['auth.login_failed'], + condition: { + threshold: 5, + windowSeconds: 600, // 10 minutes + groupBy: ['actor.id', 'actor.ipAddress'], + }, + actions: ['alert', 'lock_account'], + alertSeverity: 'warning', + }, + { + id: 'bulk_data_export', + name: 'Bulk Data Export', + description: 'Detects large data export operations', + enabled: true, + eventTypes: ['data.export'], + condition: { + threshold: 3, + windowSeconds: 3600, // 1 hour + groupBy: ['actor.id'], + }, + actions: ['alert', 'log_critical'], + alertSeverity: 'warning', + }, + { + id: 'suspicious_permission_changes', + name: 'Rapid Permission Changes', + description: 'Detects rapid permission or role changes', + enabled: true, + eventTypes: ['authz.permission_granted', 'authz.role_assigned'], + condition: { + threshold: 10, + windowSeconds: 300, // 5 minutes + groupBy: ['actor.id'], + }, + actions: ['alert', 'log_critical'], + alertSeverity: 'critical', + }, + { + id: 'after_hours_access', + name: 'After Hours Access', + description: 'Detects access during non-business hours', + enabled: false, // Disabled by default, requires time zone configuration + eventTypes: ['auth.login'], + condition: { + threshold: 1, + windowSeconds: 86400, // 24 hours + }, + actions: ['alert'], + alertSeverity: 'notice', + }, +]; diff --git a/packages/spec/src/system/index.ts b/packages/spec/src/system/index.ts index 0293511b8..bbf889275 100644 --- a/packages/spec/src/system/index.ts +++ b/packages/spec/src/system/index.ts @@ -19,6 +19,7 @@ export * from './policy.zod'; export * from './role.zod'; export * from './territory.zod'; export * from './tenant.zod'; +export * from './audit.zod'; export * from './license.zod'; export * from './webhook.zod'; export * from './translation.zod'; diff --git a/packages/spec/src/system/tenant.test.ts b/packages/spec/src/system/tenant.test.ts index 5053e14f7..094894a76 100644 --- a/packages/spec/src/system/tenant.test.ts +++ b/packages/spec/src/system/tenant.test.ts @@ -3,8 +3,18 @@ import { TenantSchema, TenantIsolationLevel, TenantQuotaSchema, + RowLevelIsolationStrategySchema, + SchemaLevelIsolationStrategySchema, + DatabaseLevelIsolationStrategySchema, + TenantIsolationConfigSchema, + TenantSecurityPolicySchema, type Tenant, type TenantQuota, + type RowLevelIsolationStrategy, + type SchemaLevelIsolationStrategy, + type DatabaseLevelIsolationStrategy, + type TenantIsolationConfig, + type TenantSecurityPolicy, } from './tenant.zod'; describe('TenantIsolationLevel', () => { @@ -182,3 +192,316 @@ describe('TenantSchema', () => { expect(() => TenantSchema.parse(tenant)).not.toThrow(); }); }); + +describe('RowLevelIsolationStrategySchema', () => { + it('should accept valid row-level isolation configuration', () => { + const validConfig: RowLevelIsolationStrategy = { + strategy: 'shared_schema', + database: { + enableRLS: true, + contextMethod: 'session_variable', + contextVariable: 'app.current_tenant', + applicationValidation: true, + }, + performance: { + usePartialIndexes: true, + usePartitioning: false, + poolSizePerTenant: 5, + }, + }; + + expect(() => RowLevelIsolationStrategySchema.parse(validConfig)).not.toThrow(); + }); + + it('should accept minimal row-level isolation configuration', () => { + const minimalConfig = { + strategy: 'shared_schema', + }; + + expect(() => RowLevelIsolationStrategySchema.parse(minimalConfig)).not.toThrow(); + }); + + it('should default enableRLS to true', () => { + const config = { + strategy: 'shared_schema', + database: {}, + }; + + const parsed = RowLevelIsolationStrategySchema.parse(config); + expect(parsed.database?.enableRLS).toBe(true); + }); + + it('should accept different context methods', () => { + const methods = ['session_variable', 'search_path', 'application_name']; + + methods.forEach((method) => { + const config = { + strategy: 'shared_schema', + database: { + contextMethod: method, + }, + }; + expect(() => RowLevelIsolationStrategySchema.parse(config)).not.toThrow(); + }); + }); +}); + +describe('SchemaLevelIsolationStrategySchema', () => { + it('should accept valid schema-level isolation configuration', () => { + const validConfig: SchemaLevelIsolationStrategy = { + strategy: 'isolated_schema', + schema: { + namingPattern: 'tenant_{tenant_id}', + includePublicSchema: true, + sharedSchema: 'public', + autoCreateSchema: true, + }, + migrations: { + strategy: 'parallel', + maxConcurrent: 10, + rollbackOnError: true, + }, + performance: { + poolPerSchema: false, + schemaCacheTTL: 3600, + }, + }; + + expect(() => SchemaLevelIsolationStrategySchema.parse(validConfig)).not.toThrow(); + }); + + it('should accept minimal schema-level isolation configuration', () => { + const minimalConfig = { + strategy: 'isolated_schema', + }; + + expect(() => SchemaLevelIsolationStrategySchema.parse(minimalConfig)).not.toThrow(); + }); + + it('should default schema naming pattern', () => { + const config = { + strategy: 'isolated_schema', + schema: {}, + }; + + const parsed = SchemaLevelIsolationStrategySchema.parse(config); + expect(parsed.schema?.namingPattern).toBe('tenant_{tenant_id}'); + }); + + it('should accept different migration strategies', () => { + const strategies = ['parallel', 'sequential', 'on_demand']; + + strategies.forEach((strategy) => { + const config = { + strategy: 'isolated_schema', + migrations: { + strategy, + }, + }; + expect(() => SchemaLevelIsolationStrategySchema.parse(config)).not.toThrow(); + }); + }); +}); + +describe('DatabaseLevelIsolationStrategySchema', () => { + it('should accept valid database-level isolation configuration', () => { + const validConfig: DatabaseLevelIsolationStrategy = { + strategy: 'isolated_db', + database: { + namingPattern: 'tenant_{tenant_id}', + serverStrategy: 'shared', + separateCredentials: true, + autoCreateDatabase: true, + }, + connectionPool: { + poolSize: 10, + maxActivePools: 100, + idleTimeout: 300, + usePooler: true, + }, + backup: { + strategy: 'individual', + frequencyHours: 24, + retentionDays: 30, + }, + encryption: { + perTenantKeys: true, + algorithm: 'AES-256-GCM', + keyManagement: 'aws_kms', + }, + }; + + expect(() => DatabaseLevelIsolationStrategySchema.parse(validConfig)).not.toThrow(); + }); + + it('should accept minimal database-level isolation configuration', () => { + const minimalConfig = { + strategy: 'isolated_db', + }; + + expect(() => DatabaseLevelIsolationStrategySchema.parse(minimalConfig)).not.toThrow(); + }); + + it('should default database naming pattern', () => { + const config = { + strategy: 'isolated_db', + database: {}, + }; + + const parsed = DatabaseLevelIsolationStrategySchema.parse(config); + expect(parsed.database?.namingPattern).toBe('tenant_{tenant_id}'); + }); + + it('should accept different server strategies', () => { + const strategies = ['shared', 'sharded', 'dedicated']; + + strategies.forEach((serverStrategy) => { + const config = { + strategy: 'isolated_db', + database: { + serverStrategy, + }, + }; + expect(() => DatabaseLevelIsolationStrategySchema.parse(config)).not.toThrow(); + }); + }); + + it('should accept different backup strategies', () => { + const strategies = ['individual', 'consolidated', 'on_demand']; + + strategies.forEach((strategy) => { + const config = { + strategy: 'isolated_db', + backup: { + strategy, + }, + }; + expect(() => DatabaseLevelIsolationStrategySchema.parse(config)).not.toThrow(); + }); + }); +}); + +describe('TenantIsolationConfigSchema', () => { + it('should accept row-level isolation config', () => { + const config: TenantIsolationConfig = { + strategy: 'shared_schema', + database: { + enableRLS: true, + }, + }; + + expect(() => TenantIsolationConfigSchema.parse(config)).not.toThrow(); + }); + + it('should accept schema-level isolation config', () => { + const config: TenantIsolationConfig = { + strategy: 'isolated_schema', + schema: { + namingPattern: 'tenant_{tenant_id}', + }, + }; + + expect(() => TenantIsolationConfigSchema.parse(config)).not.toThrow(); + }); + + it('should accept database-level isolation config', () => { + const config: TenantIsolationConfig = { + strategy: 'isolated_db', + database: { + serverStrategy: 'dedicated', + }, + }; + + expect(() => TenantIsolationConfigSchema.parse(config)).not.toThrow(); + }); + + it('should discriminate by strategy field', () => { + const configs = [ + { strategy: 'shared_schema' }, + { strategy: 'isolated_schema' }, + { strategy: 'isolated_db' }, + ]; + + configs.forEach((config) => { + expect(() => TenantIsolationConfigSchema.parse(config)).not.toThrow(); + }); + }); +}); + +describe('TenantSecurityPolicySchema', () => { + it('should accept complete security policy', () => { + const validPolicy: TenantSecurityPolicy = { + encryption: { + atRest: true, + inTransit: true, + fieldLevel: true, + }, + accessControl: { + requireMFA: true, + requireSSO: true, + ipWhitelist: ['192.168.1.0/24', '10.0.0.0/8'], + sessionTimeout: 3600, + }, + compliance: { + standards: ['sox', 'hipaa', 'gdpr'], + requireAuditLog: true, + auditRetentionDays: 365, + dataResidency: { + region: 'US', + excludeRegions: ['CN', 'RU'], + }, + }, + }; + + expect(() => TenantSecurityPolicySchema.parse(validPolicy)).not.toThrow(); + }); + + it('should accept minimal security policy', () => { + const minimalPolicy = {}; + expect(() => TenantSecurityPolicySchema.parse(minimalPolicy)).not.toThrow(); + }); + + it('should default encryption settings', () => { + const policy = { + encryption: {}, + }; + + const parsed = TenantSecurityPolicySchema.parse(policy); + expect(parsed.encryption?.atRest).toBe(true); + expect(parsed.encryption?.inTransit).toBe(true); + expect(parsed.encryption?.fieldLevel).toBe(false); + }); + + it('should default access control settings', () => { + const policy = { + accessControl: {}, + }; + + const parsed = TenantSecurityPolicySchema.parse(policy); + expect(parsed.accessControl?.requireMFA).toBe(false); + expect(parsed.accessControl?.requireSSO).toBe(false); + expect(parsed.accessControl?.sessionTimeout).toBe(3600); + }); + + it('should accept compliance standards', () => { + const standards = ['sox', 'hipaa', 'gdpr', 'pci_dss', 'iso_27001', 'fedramp']; + + const policy = { + compliance: { + standards, + }, + }; + + expect(() => TenantSecurityPolicySchema.parse(policy)).not.toThrow(); + }); + + it('should default compliance settings', () => { + const policy = { + compliance: {}, + }; + + const parsed = TenantSecurityPolicySchema.parse(policy); + expect(parsed.compliance?.requireAuditLog).toBe(true); + expect(parsed.compliance?.auditRetentionDays).toBe(365); + }); +}); diff --git a/packages/spec/src/system/tenant.zod.ts b/packages/spec/src/system/tenant.zod.ts index 9d2c66532..1e06471d0 100644 --- a/packages/spec/src/system/tenant.zod.ts +++ b/packages/spec/src/system/tenant.zod.ts @@ -82,3 +82,484 @@ export const TenantSchema = z.object({ }); export type Tenant = z.infer; + +/** + * Tenant Isolation Strategy Documentation + * + * Comprehensive documentation of three isolation strategies for multi-tenant systems. + * Each strategy has different trade-offs in terms of security, cost, complexity, and compliance. + */ + +/** + * Row-Level Isolation Strategy (shared_schema) + * + * Recommended for: Most SaaS applications, cost-sensitive deployments + * + * IMPLEMENTATION: + * - All tenants share the same database and schema + * - Each table includes a tenant_id column + * - PostgreSQL Row-Level Security (RLS) enforces isolation + * - Queries automatically filter by tenant_id via RLS policies + * + * ADVANTAGES: + * ✅ Simple backup and restore (single database) + * ✅ Cost-effective (shared resources, minimal overhead) + * ✅ Easy tenant migration (update tenant_id) + * ✅ Efficient resource utilization (connection pooling) + * ✅ Simple schema migrations (single schema to update) + * ✅ Lower operational complexity + * + * DISADVANTAGES: + * ❌ RLS misconfiguration can lead to data leakage + * ❌ Performance impact from RLS policy evaluation + * ❌ Noisy neighbor problem (one tenant can affect others) + * ❌ Cannot easily isolate tenant to different hardware + * ❌ Compliance challenges for regulated industries + * + * SECURITY CONSIDERATIONS: + * - Requires careful RLS policy configuration + * - Must validate tenant_id in all queries + * - Need comprehensive testing of RLS policies + * - Audit all database access patterns + * - Implement application-level validation as defense-in-depth + * + * EXAMPLE RLS POLICY (PostgreSQL): + * ```sql + * CREATE POLICY tenant_isolation ON customers + * USING (tenant_id = current_setting('app.current_tenant')::text); + * + * ALTER TABLE customers ENABLE ROW LEVEL SECURITY; + * ``` + */ +export const RowLevelIsolationStrategySchema = z.object({ + strategy: z.literal('shared_schema').describe('Row-level isolation strategy'), + + /** + * Database configuration for row-level isolation + */ + database: z.object({ + /** + * Whether to enable Row-Level Security (RLS) + */ + enableRLS: z.boolean().default(true).describe('Enable PostgreSQL Row-Level Security'), + + /** + * Tenant context setting method + */ + contextMethod: z.enum([ + 'session_variable', // SET app.current_tenant = 'tenant_123' + 'search_path', // SET search_path = tenant_123, public + 'application_name', // SET application_name = 'tenant_123' + ]).default('session_variable').describe('How to set tenant context'), + + /** + * Session variable name for tenant context + */ + contextVariable: z.string().default('app.current_tenant').describe('Session variable name'), + + /** + * Whether to validate tenant_id at application level + */ + applicationValidation: z.boolean().default(true).describe('Application-level tenant validation'), + }).optional().describe('Database configuration'), + + /** + * Performance optimization settings + */ + performance: z.object({ + /** + * Whether to use partial indexes for tenant_id + */ + usePartialIndexes: z.boolean().default(true).describe('Use partial indexes per tenant'), + + /** + * Whether to use table partitioning + */ + usePartitioning: z.boolean().default(false).describe('Use table partitioning by tenant_id'), + + /** + * Connection pool size per tenant + */ + poolSizePerTenant: z.number().int().positive().optional().describe('Connection pool size per tenant'), + }).optional().describe('Performance settings'), +}); + +export type RowLevelIsolationStrategy = z.infer; + +/** + * Schema-Level Isolation Strategy (isolated_schema) + * + * Recommended for: Enterprise SaaS, B2B platforms with compliance needs + * + * IMPLEMENTATION: + * - All tenants share the same database server + * - Each tenant has a separate database schema + * - Schema name typically: tenant_ + * - Application switches schema using SET search_path + * + * ADVANTAGES: + * ✅ Better isolation than row-level (schema boundaries) + * ✅ Easier to debug (separate schemas) + * ✅ Can grant different database permissions per schema + * ✅ Reduced risk of data leakage + * ✅ Performance isolation (indexes, statistics per schema) + * ✅ Simplified queries (no tenant_id filtering needed) + * + * DISADVANTAGES: + * ❌ More complex backups (must backup all schemas) + * ❌ Higher migration costs (schema changes across all tenants) + * ❌ Schema proliferation (PostgreSQL has limits) + * ❌ Connection overhead (switching schemas) + * ❌ More complex monitoring and maintenance + * + * SECURITY CONSIDERATIONS: + * - Ensure proper schema permissions (GRANT USAGE ON SCHEMA) + * - Validate schema name to prevent SQL injection + * - Implement connection-level schema switching + * - Audit schema access patterns + * - Prevent cross-schema queries in application + * + * EXAMPLE IMPLEMENTATION (PostgreSQL): + * ```sql + * -- Create tenant schema + * CREATE SCHEMA tenant_123; + * + * -- Grant access + * GRANT USAGE ON SCHEMA tenant_123 TO app_user; + * + * -- Switch to tenant schema + * SET search_path TO tenant_123, public; + * ``` + */ +export const SchemaLevelIsolationStrategySchema = z.object({ + strategy: z.literal('isolated_schema').describe('Schema-level isolation strategy'), + + /** + * Schema configuration + */ + schema: z.object({ + /** + * Schema naming pattern + * Use {tenant_id} as placeholder + */ + namingPattern: z.string().default('tenant_{tenant_id}').describe('Schema naming pattern'), + + /** + * Whether to include public schema in search_path + */ + includePublicSchema: z.boolean().default(true).describe('Include public schema'), + + /** + * Default schema for shared resources + */ + sharedSchema: z.string().default('public').describe('Schema for shared resources'), + + /** + * Whether to automatically create schema on tenant creation + */ + autoCreateSchema: z.boolean().default(true).describe('Auto-create schema'), + }).optional().describe('Schema configuration'), + + /** + * Migration configuration + */ + migrations: z.object({ + /** + * Migration strategy + */ + strategy: z.enum([ + 'parallel', // Run migrations on all schemas in parallel + 'sequential', // Run migrations one schema at a time + 'on_demand', // Run migrations when tenant accesses system + ]).default('parallel').describe('Migration strategy'), + + /** + * Maximum concurrent migrations + */ + maxConcurrent: z.number().int().positive().default(10).describe('Max concurrent migrations'), + + /** + * Whether to rollback on first failure + */ + rollbackOnError: z.boolean().default(true).describe('Rollback on error'), + }).optional().describe('Migration configuration'), + + /** + * Performance optimization settings + */ + performance: z.object({ + /** + * Whether to use connection pooling per schema + */ + poolPerSchema: z.boolean().default(false).describe('Separate pool per schema'), + + /** + * Schema cache TTL in seconds + */ + schemaCacheTTL: z.number().int().positive().default(3600).describe('Schema cache TTL'), + }).optional().describe('Performance settings'), +}); + +export type SchemaLevelIsolationStrategy = z.infer; + +/** + * Database-Level Isolation Strategy (isolated_db) + * + * Recommended for: Regulated industries (healthcare, finance), strict compliance requirements + * + * IMPLEMENTATION: + * - Each tenant has a completely separate database + * - Database name typically: tenant_ + * - Requires separate connection pool per tenant + * - Complete physical and logical isolation + * + * ADVANTAGES: + * ✅ Perfect data isolation (strongest security) + * ✅ Meets strict regulatory requirements (HIPAA, SOX, PCI-DSS) + * ✅ Complete performance isolation (no noisy neighbors) + * ✅ Can place databases on different hardware + * ✅ Easy to backup/restore individual tenant + * ✅ Simplified compliance auditing per tenant + * ✅ Can apply different encryption keys per database + * + * DISADVANTAGES: + * ❌ Most expensive option (resource overhead) + * ❌ Complex database server management (many databases) + * ❌ Connection pool limits (max connections per server) + * ❌ Difficult cross-tenant analytics + * ❌ Higher operational complexity + * ❌ Schema migrations take longer (many databases) + * + * SECURITY CONSIDERATIONS: + * - Each database can have separate credentials + * - Enables per-tenant encryption at rest + * - Simplifies compliance and audit trails + * - Prevents any cross-tenant data access + * - Supports tenant-specific backup schedules + * + * EXAMPLE IMPLEMENTATION (PostgreSQL): + * ```sql + * -- Create tenant database + * CREATE DATABASE tenant_123 + * WITH OWNER = tenant_123_user + * ENCODING = 'UTF8' + * LC_COLLATE = 'en_US.UTF-8' + * LC_CTYPE = 'en_US.UTF-8'; + * + * -- Connect to tenant database + * \c tenant_123 + * ``` + */ +export const DatabaseLevelIsolationStrategySchema = z.object({ + strategy: z.literal('isolated_db').describe('Database-level isolation strategy'), + + /** + * Database configuration + */ + database: z.object({ + /** + * Database naming pattern + * Use {tenant_id} as placeholder + */ + namingPattern: z.string().default('tenant_{tenant_id}').describe('Database naming pattern'), + + /** + * Database server/cluster assignment strategy + */ + serverStrategy: z.enum([ + 'shared', // All tenant databases on same server + 'sharded', // Tenant databases distributed across servers + 'dedicated', // Each tenant gets dedicated server (enterprise) + ]).default('shared').describe('Server assignment strategy'), + + /** + * Whether to use separate credentials per tenant + */ + separateCredentials: z.boolean().default(true).describe('Separate credentials per tenant'), + + /** + * Whether to automatically create database on tenant creation + */ + autoCreateDatabase: z.boolean().default(true).describe('Auto-create database'), + }).optional().describe('Database configuration'), + + /** + * Connection pooling configuration + */ + connectionPool: z.object({ + /** + * Pool size per tenant database + */ + poolSize: z.number().int().positive().default(10).describe('Connection pool size'), + + /** + * Maximum number of tenant pools to keep active + */ + maxActivePools: z.number().int().positive().default(100).describe('Max active pools'), + + /** + * Idle pool timeout in seconds + */ + idleTimeout: z.number().int().positive().default(300).describe('Idle pool timeout'), + + /** + * Whether to use connection pooler (PgBouncer, etc.) + */ + usePooler: z.boolean().default(true).describe('Use connection pooler'), + }).optional().describe('Connection pool configuration'), + + /** + * Backup and restore configuration + */ + backup: z.object({ + /** + * Backup strategy per tenant + */ + strategy: z.enum([ + 'individual', // Separate backup per tenant + 'consolidated', // Combined backup with all tenants + 'on_demand', // Backup only when requested + ]).default('individual').describe('Backup strategy'), + + /** + * Backup frequency in hours + */ + frequencyHours: z.number().int().positive().default(24).describe('Backup frequency'), + + /** + * Retention period in days + */ + retentionDays: z.number().int().positive().default(30).describe('Backup retention days'), + }).optional().describe('Backup configuration'), + + /** + * Encryption configuration + */ + encryption: z.object({ + /** + * Whether to use per-tenant encryption keys + */ + perTenantKeys: z.boolean().default(false).describe('Per-tenant encryption keys'), + + /** + * Encryption algorithm + */ + algorithm: z.string().default('AES-256-GCM').describe('Encryption algorithm'), + + /** + * Key management service + */ + keyManagement: z.enum(['aws_kms', 'azure_key_vault', 'gcp_kms', 'hashicorp_vault', 'custom']).optional().describe('Key management service'), + }).optional().describe('Encryption configuration'), +}); + +export type DatabaseLevelIsolationStrategy = z.infer; + +/** + * Tenant Isolation Configuration Schema + * + * Complete configuration for tenant isolation strategy. + * Supports all three isolation levels with detailed configuration options. + */ +export const TenantIsolationConfigSchema = z.discriminatedUnion('strategy', [ + RowLevelIsolationStrategySchema, + SchemaLevelIsolationStrategySchema, + DatabaseLevelIsolationStrategySchema, +]); + +export type TenantIsolationConfig = z.infer; + +/** + * Tenant Security Policy Schema + * Defines security policies and compliance requirements for tenants + */ +export const TenantSecurityPolicySchema = z.object({ + /** + * Encryption requirements + */ + encryption: z.object({ + /** + * Require encryption at rest + */ + atRest: z.boolean().default(true).describe('Require encryption at rest'), + + /** + * Require encryption in transit + */ + inTransit: z.boolean().default(true).describe('Require encryption in transit'), + + /** + * Require field-level encryption for sensitive data + */ + fieldLevel: z.boolean().default(false).describe('Require field-level encryption'), + }).optional().describe('Encryption requirements'), + + /** + * Access control requirements + */ + accessControl: z.object({ + /** + * Require multi-factor authentication + */ + requireMFA: z.boolean().default(false).describe('Require MFA'), + + /** + * Require SSO/SAML authentication + */ + requireSSO: z.boolean().default(false).describe('Require SSO'), + + /** + * IP whitelist + */ + ipWhitelist: z.array(z.string()).optional().describe('Allowed IP addresses'), + + /** + * Session timeout in seconds + */ + sessionTimeout: z.number().int().positive().default(3600).describe('Session timeout'), + }).optional().describe('Access control requirements'), + + /** + * Audit and compliance requirements + */ + compliance: z.object({ + /** + * Compliance standards to enforce + */ + standards: z.array(z.enum([ + 'sox', + 'hipaa', + 'gdpr', + 'pci_dss', + 'iso_27001', + 'fedramp', + ])).optional().describe('Compliance standards'), + + /** + * Require audit logging for all operations + */ + requireAuditLog: z.boolean().default(true).describe('Require audit logging'), + + /** + * Audit log retention period in days + */ + auditRetentionDays: z.number().int().positive().default(365).describe('Audit retention days'), + + /** + * Data residency requirements + */ + dataResidency: z.object({ + /** + * Required geographic region + */ + region: z.string().optional().describe('Required region (e.g., US, EU, APAC)'), + + /** + * Prohibited regions + */ + excludeRegions: z.array(z.string()).optional().describe('Prohibited regions'), + }).optional().describe('Data residency requirements'), + }).optional().describe('Compliance requirements'), +}); + +export type TenantSecurityPolicy = z.infer; From 2882d58528721cb042bf83da12a88d1f539c6afc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:00:25 +0000 Subject: [PATCH 3/4] Complete testing and validation of audit log and tenant isolation features Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- content/docs/references/ai/BillingPeriod.mdx | 14 + content/docs/references/ai/BudgetLimit.mdx | 25 + content/docs/references/ai/BudgetStatus.mdx | 24 + content/docs/references/ai/BudgetType.mdx | 13 + .../references/ai/ConversationAnalytics.mdx | 24 + .../references/ai/ConversationContext.mdx | 17 + .../references/ai/ConversationMessage.mdx | 22 + .../references/ai/ConversationSession.mdx | 22 + .../references/ai/ConversationSummary.mdx | 17 + content/docs/references/ai/CostAlert.mdx | 27 + content/docs/references/ai/CostAlertType.mdx | 12 + content/docs/references/ai/CostAnalytics.mdx | 31 + .../references/ai/CostBreakdownDimension.mdx | 16 + .../docs/references/ai/CostBreakdownEntry.mdx | 17 + content/docs/references/ai/CostEntry.mdx | 29 + content/docs/references/ai/CostMetricType.mdx | 13 + .../ai/CostOptimizationRecommendation.mdx | 24 + .../docs/references/ai/CostQueryFilters.mdx | 25 + content/docs/references/ai/CostReport.mdx | 27 + content/docs/references/ai/FunctionCall.mdx | 12 + content/docs/references/ai/MessageContent.mdx | 15 + .../docs/references/ai/MessageContentType.mdx | 12 + .../references/ai/MessagePruningEvent.mdx | 17 + content/docs/references/ai/MessageRole.mdx | 12 + .../docs/references/ai/TokenBudgetConfig.mdx | 22 + .../references/ai/TokenBudgetStrategy.mdx | 12 + .../docs/references/ai/TokenUsageStats.mdx | 19 + content/docs/references/ai/ToolCall.mdx | 12 + .../docs/references/system/AuditConfig.mdx | 25 + content/docs/references/system/AuditEvent.mdx | 23 + .../references/system/AuditEventActor.mdx | 15 + .../references/system/AuditEventChange.mdx | 12 + .../references/system/AuditEventFilter.mdx | 17 + .../references/system/AuditEventSeverity.mdx | 15 + .../references/system/AuditEventTarget.mdx | 13 + .../docs/references/system/AuditEventType.mdx | 49 + .../system/AuditRetentionPolicy.mdx | 14 + .../references/system/AuditStorageConfig.mdx | 16 + .../system/DatabaseLevelIsolationStrategy.mdx | 14 + .../system/LevelIsolationStrategySchema.mdx | 13 + .../system/RowLevelIsolationStrategy.mdx | 12 + .../system/SuspiciousActivityRule.mdx | 18 + .../system/TenantIsolationConfig.mdx | 5 + .../system/TenantSecurityPolicy.mdx | 12 + packages/spec/json-schema/AuditConfig.json | 581 +++++++++++ packages/spec/json-schema/AuditEvent.json | 232 +++++ .../spec/json-schema/AuditEventActor.json | 48 + .../spec/json-schema/AuditEventChange.json | 25 + .../spec/json-schema/AuditEventFilter.json | 127 +++ .../spec/json-schema/AuditEventSeverity.json | 19 + .../spec/json-schema/AuditEventTarget.json | 33 + packages/spec/json-schema/AuditEventType.json | 53 + .../json-schema/AuditRetentionPolicy.json | 73 ++ .../spec/json-schema/AuditStorageConfig.json | 60 ++ packages/spec/json-schema/BillingPeriod.json | 18 + packages/spec/json-schema/BudgetLimit.json | 111 ++ packages/spec/json-schema/BudgetStatus.json | 91 ++ packages/spec/json-schema/BudgetType.json | 17 + .../json-schema/ConversationAnalytics.json | 86 ++ .../spec/json-schema/ConversationContext.json | 48 + .../spec/json-schema/ConversationMessage.json | 209 ++++ .../spec/json-schema/ConversationSession.json | 458 +++++++++ .../spec/json-schema/ConversationSummary.json | 73 ++ packages/spec/json-schema/CostAlert.json | 107 ++ packages/spec/json-schema/CostAlertType.json | 16 + packages/spec/json-schema/CostAnalytics.json | 589 +++++++++++ .../json-schema/CostBreakdownDimension.json | 20 + .../spec/json-schema/CostBreakdownEntry.json | 60 ++ packages/spec/json-schema/CostEntry.json | 106 ++ packages/spec/json-schema/CostMetricType.json | 17 + .../CostOptimizationRecommendation.json | 100 ++ .../spec/json-schema/CostQueryFilters.json | 112 ++ packages/spec/json-schema/CostReport.json | 964 ++++++++++++++++++ .../DatabaseLevelIsolationStrategy.json | 139 +++ packages/spec/json-schema/FunctionCall.json | 28 + .../LevelIsolationStrategySchema.json | 93 ++ packages/spec/json-schema/MessageContent.json | 46 + .../spec/json-schema/MessageContentType.json | 16 + .../spec/json-schema/MessagePruningEvent.json | 92 ++ packages/spec/json-schema/MessageRole.json | 16 + .../RowLevelIsolationStrategy.json | 74 ++ .../json-schema/SuspiciousActivityRule.json | 175 ++++ .../json-schema/TenantIsolationConfig.json | 298 ++++++ .../json-schema/TenantSecurityPolicy.json | 115 +++ .../spec/json-schema/TokenBudgetConfig.json | 92 ++ .../spec/json-schema/TokenBudgetStrategy.json | 16 + .../spec/json-schema/TokenUsageStats.json | 66 ++ packages/spec/json-schema/ToolCall.json | 49 + 88 files changed, 6573 insertions(+) create mode 100644 content/docs/references/ai/BillingPeriod.mdx create mode 100644 content/docs/references/ai/BudgetLimit.mdx create mode 100644 content/docs/references/ai/BudgetStatus.mdx create mode 100644 content/docs/references/ai/BudgetType.mdx create mode 100644 content/docs/references/ai/ConversationAnalytics.mdx create mode 100644 content/docs/references/ai/ConversationContext.mdx create mode 100644 content/docs/references/ai/ConversationMessage.mdx create mode 100644 content/docs/references/ai/ConversationSession.mdx create mode 100644 content/docs/references/ai/ConversationSummary.mdx create mode 100644 content/docs/references/ai/CostAlert.mdx create mode 100644 content/docs/references/ai/CostAlertType.mdx create mode 100644 content/docs/references/ai/CostAnalytics.mdx create mode 100644 content/docs/references/ai/CostBreakdownDimension.mdx create mode 100644 content/docs/references/ai/CostBreakdownEntry.mdx create mode 100644 content/docs/references/ai/CostEntry.mdx create mode 100644 content/docs/references/ai/CostMetricType.mdx create mode 100644 content/docs/references/ai/CostOptimizationRecommendation.mdx create mode 100644 content/docs/references/ai/CostQueryFilters.mdx create mode 100644 content/docs/references/ai/CostReport.mdx create mode 100644 content/docs/references/ai/FunctionCall.mdx create mode 100644 content/docs/references/ai/MessageContent.mdx create mode 100644 content/docs/references/ai/MessageContentType.mdx create mode 100644 content/docs/references/ai/MessagePruningEvent.mdx create mode 100644 content/docs/references/ai/MessageRole.mdx create mode 100644 content/docs/references/ai/TokenBudgetConfig.mdx create mode 100644 content/docs/references/ai/TokenBudgetStrategy.mdx create mode 100644 content/docs/references/ai/TokenUsageStats.mdx create mode 100644 content/docs/references/ai/ToolCall.mdx create mode 100644 content/docs/references/system/AuditConfig.mdx create mode 100644 content/docs/references/system/AuditEvent.mdx create mode 100644 content/docs/references/system/AuditEventActor.mdx create mode 100644 content/docs/references/system/AuditEventChange.mdx create mode 100644 content/docs/references/system/AuditEventFilter.mdx create mode 100644 content/docs/references/system/AuditEventSeverity.mdx create mode 100644 content/docs/references/system/AuditEventTarget.mdx create mode 100644 content/docs/references/system/AuditEventType.mdx create mode 100644 content/docs/references/system/AuditRetentionPolicy.mdx create mode 100644 content/docs/references/system/AuditStorageConfig.mdx create mode 100644 content/docs/references/system/DatabaseLevelIsolationStrategy.mdx create mode 100644 content/docs/references/system/LevelIsolationStrategySchema.mdx create mode 100644 content/docs/references/system/RowLevelIsolationStrategy.mdx create mode 100644 content/docs/references/system/SuspiciousActivityRule.mdx create mode 100644 content/docs/references/system/TenantIsolationConfig.mdx create mode 100644 content/docs/references/system/TenantSecurityPolicy.mdx create mode 100644 packages/spec/json-schema/AuditConfig.json create mode 100644 packages/spec/json-schema/AuditEvent.json create mode 100644 packages/spec/json-schema/AuditEventActor.json create mode 100644 packages/spec/json-schema/AuditEventChange.json create mode 100644 packages/spec/json-schema/AuditEventFilter.json create mode 100644 packages/spec/json-schema/AuditEventSeverity.json create mode 100644 packages/spec/json-schema/AuditEventTarget.json create mode 100644 packages/spec/json-schema/AuditEventType.json create mode 100644 packages/spec/json-schema/AuditRetentionPolicy.json create mode 100644 packages/spec/json-schema/AuditStorageConfig.json create mode 100644 packages/spec/json-schema/BillingPeriod.json create mode 100644 packages/spec/json-schema/BudgetLimit.json create mode 100644 packages/spec/json-schema/BudgetStatus.json create mode 100644 packages/spec/json-schema/BudgetType.json create mode 100644 packages/spec/json-schema/ConversationAnalytics.json create mode 100644 packages/spec/json-schema/ConversationContext.json create mode 100644 packages/spec/json-schema/ConversationMessage.json create mode 100644 packages/spec/json-schema/ConversationSession.json create mode 100644 packages/spec/json-schema/ConversationSummary.json create mode 100644 packages/spec/json-schema/CostAlert.json create mode 100644 packages/spec/json-schema/CostAlertType.json create mode 100644 packages/spec/json-schema/CostAnalytics.json create mode 100644 packages/spec/json-schema/CostBreakdownDimension.json create mode 100644 packages/spec/json-schema/CostBreakdownEntry.json create mode 100644 packages/spec/json-schema/CostEntry.json create mode 100644 packages/spec/json-schema/CostMetricType.json create mode 100644 packages/spec/json-schema/CostOptimizationRecommendation.json create mode 100644 packages/spec/json-schema/CostQueryFilters.json create mode 100644 packages/spec/json-schema/CostReport.json create mode 100644 packages/spec/json-schema/DatabaseLevelIsolationStrategy.json create mode 100644 packages/spec/json-schema/FunctionCall.json create mode 100644 packages/spec/json-schema/LevelIsolationStrategySchema.json create mode 100644 packages/spec/json-schema/MessageContent.json create mode 100644 packages/spec/json-schema/MessageContentType.json create mode 100644 packages/spec/json-schema/MessagePruningEvent.json create mode 100644 packages/spec/json-schema/MessageRole.json create mode 100644 packages/spec/json-schema/RowLevelIsolationStrategy.json create mode 100644 packages/spec/json-schema/SuspiciousActivityRule.json create mode 100644 packages/spec/json-schema/TenantIsolationConfig.json create mode 100644 packages/spec/json-schema/TenantSecurityPolicy.json create mode 100644 packages/spec/json-schema/TokenBudgetConfig.json create mode 100644 packages/spec/json-schema/TokenBudgetStrategy.json create mode 100644 packages/spec/json-schema/TokenUsageStats.json create mode 100644 packages/spec/json-schema/ToolCall.json diff --git a/content/docs/references/ai/BillingPeriod.mdx b/content/docs/references/ai/BillingPeriod.mdx new file mode 100644 index 000000000..1baf034f5 --- /dev/null +++ b/content/docs/references/ai/BillingPeriod.mdx @@ -0,0 +1,14 @@ +--- +title: BillingPeriod +description: BillingPeriod Schema Reference +--- + +## Allowed Values + +* `hourly` +* `daily` +* `weekly` +* `monthly` +* `quarterly` +* `yearly` +* `custom` \ No newline at end of file diff --git a/content/docs/references/ai/BudgetLimit.mdx b/content/docs/references/ai/BudgetLimit.mdx new file mode 100644 index 000000000..4d21aaeec --- /dev/null +++ b/content/docs/references/ai/BudgetLimit.mdx @@ -0,0 +1,25 @@ +--- +title: BudgetLimit +description: BudgetLimit Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `Enum<'global' \| 'user' \| 'agent' \| 'object' \| 'project' \| 'department'>` | ✅ | | +| **scope** | `string` | optional | Scope identifier (userId, agentId, etc.) | +| **maxCost** | `number` | ✅ | Maximum cost limit | +| **currency** | `string` | optional | | +| **period** | `Enum<'hourly' \| 'daily' \| 'weekly' \| 'monthly' \| 'quarterly' \| 'yearly' \| 'custom'>` | ✅ | | +| **customPeriodDays** | `integer` | optional | Custom period in days | +| **softLimit** | `number` | optional | Soft limit for warnings | +| **warnThresholds** | `number[]` | optional | Warning thresholds (e.g., [0.5, 0.8, 0.95]) | +| **enforced** | `boolean` | optional | Block requests when exceeded | +| **gracePeriodSeconds** | `integer` | optional | Grace period after limit exceeded | +| **allowRollover** | `boolean` | optional | Allow unused budget to rollover | +| **maxRolloverPercentage** | `number` | optional | Max rollover as % of limit | +| **name** | `string` | optional | Budget name | +| **description** | `string` | optional | | +| **active** | `boolean` | optional | | +| **tags** | `string[]` | optional | | diff --git a/content/docs/references/ai/BudgetStatus.mdx b/content/docs/references/ai/BudgetStatus.mdx new file mode 100644 index 000000000..4e1936779 --- /dev/null +++ b/content/docs/references/ai/BudgetStatus.mdx @@ -0,0 +1,24 @@ +--- +title: BudgetStatus +description: BudgetStatus Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **budgetId** | `string` | ✅ | | +| **type** | `Enum<'global' \| 'user' \| 'agent' \| 'object' \| 'project' \| 'department'>` | ✅ | | +| **scope** | `string` | optional | | +| **periodStart** | `string` | ✅ | ISO 8601 timestamp | +| **periodEnd** | `string` | ✅ | ISO 8601 timestamp | +| **currentCost** | `number` | optional | | +| **maxCost** | `number` | ✅ | | +| **currency** | `string` | optional | | +| **percentageUsed** | `number` | ✅ | Usage as percentage (can exceed 1.0 if over budget) | +| **remainingCost** | `number` | ✅ | Remaining budget (can be negative if exceeded) | +| **isExceeded** | `boolean` | optional | | +| **isWarning** | `boolean` | optional | | +| **projectedCost** | `number` | optional | Projected cost for period | +| **projectedOverage** | `number` | optional | Projected overage | +| **lastUpdated** | `string` | ✅ | ISO 8601 timestamp | diff --git a/content/docs/references/ai/BudgetType.mdx b/content/docs/references/ai/BudgetType.mdx new file mode 100644 index 000000000..6a73df905 --- /dev/null +++ b/content/docs/references/ai/BudgetType.mdx @@ -0,0 +1,13 @@ +--- +title: BudgetType +description: BudgetType Schema Reference +--- + +## Allowed Values + +* `global` +* `user` +* `agent` +* `object` +* `project` +* `department` \ No newline at end of file diff --git a/content/docs/references/ai/ConversationAnalytics.mdx b/content/docs/references/ai/ConversationAnalytics.mdx new file mode 100644 index 000000000..2c715617a --- /dev/null +++ b/content/docs/references/ai/ConversationAnalytics.mdx @@ -0,0 +1,24 @@ +--- +title: ConversationAnalytics +description: ConversationAnalytics Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **sessionId** | `string` | ✅ | | +| **totalMessages** | `integer` | ✅ | | +| **userMessages** | `integer` | ✅ | | +| **assistantMessages** | `integer` | ✅ | | +| **systemMessages** | `integer` | ✅ | | +| **totalTokens** | `integer` | ✅ | | +| **averageTokensPerMessage** | `number` | ✅ | | +| **peakTokenUsage** | `integer` | ✅ | | +| **pruningEvents** | `integer` | optional | | +| **summarizationEvents** | `integer` | optional | | +| **tokensSavedByPruning** | `integer` | optional | | +| **tokensSavedBySummarization** | `integer` | optional | | +| **duration** | `number` | optional | Session duration in seconds | +| **firstMessageAt** | `string` | optional | ISO 8601 timestamp | +| **lastMessageAt** | `string` | optional | ISO 8601 timestamp | diff --git a/content/docs/references/ai/ConversationContext.mdx b/content/docs/references/ai/ConversationContext.mdx new file mode 100644 index 000000000..8ffd326fd --- /dev/null +++ b/content/docs/references/ai/ConversationContext.mdx @@ -0,0 +1,17 @@ +--- +title: ConversationContext +description: ConversationContext Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **sessionId** | `string` | ✅ | Conversation session ID | +| **userId** | `string` | optional | User identifier | +| **agentId** | `string` | optional | AI agent identifier | +| **object** | `string` | optional | Related object (e.g., "case", "project") | +| **recordId** | `string` | optional | Related record ID | +| **scope** | `Record` | optional | Additional context scope | +| **systemMessage** | `string` | optional | System prompt/instructions | +| **metadata** | `Record` | optional | | diff --git a/content/docs/references/ai/ConversationMessage.mdx b/content/docs/references/ai/ConversationMessage.mdx new file mode 100644 index 000000000..abc8cf19c --- /dev/null +++ b/content/docs/references/ai/ConversationMessage.mdx @@ -0,0 +1,22 @@ +--- +title: ConversationMessage +description: ConversationMessage Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | Unique message ID | +| **timestamp** | `string` | ✅ | ISO 8601 timestamp | +| **role** | `Enum<'system' \| 'user' \| 'assistant' \| 'function' \| 'tool'>` | ✅ | | +| **content** | `string \| object[]` | ✅ | Message content (text or multimodal) | +| **functionCall** | `object` | optional | Legacy function call | +| **toolCalls** | `object[]` | optional | Tool calls | +| **toolCallId** | `string` | optional | Tool call ID this message responds to | +| **name** | `string` | optional | Name of the function/user | +| **tokens** | `object` | optional | Token usage for this message | +| **pinned** | `boolean` | optional | Prevent removal during pruning | +| **importance** | `number` | optional | Importance score for pruning | +| **embedding** | `number[]` | optional | Vector embedding for semantic search | +| **metadata** | `Record` | optional | | diff --git a/content/docs/references/ai/ConversationSession.mdx b/content/docs/references/ai/ConversationSession.mdx new file mode 100644 index 000000000..769c0f214 --- /dev/null +++ b/content/docs/references/ai/ConversationSession.mdx @@ -0,0 +1,22 @@ +--- +title: ConversationSession +description: ConversationSession Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | Unique session ID | +| **name** | `string` | optional | Session name/title | +| **context** | `object` | ✅ | | +| **modelId** | `string` | optional | AI model ID | +| **tokenBudget** | `object` | ✅ | | +| **messages** | `object[]` | optional | | +| **tokens** | `object` | optional | | +| **status** | `Enum<'active' \| 'paused' \| 'completed' \| 'archived'>` | optional | | +| **createdAt** | `string` | ✅ | ISO 8601 timestamp | +| **updatedAt** | `string` | ✅ | ISO 8601 timestamp | +| **expiresAt** | `string` | optional | ISO 8601 timestamp | +| **tags** | `string[]` | optional | | +| **metadata** | `Record` | optional | | diff --git a/content/docs/references/ai/ConversationSummary.mdx b/content/docs/references/ai/ConversationSummary.mdx new file mode 100644 index 000000000..8f5eff0d4 --- /dev/null +++ b/content/docs/references/ai/ConversationSummary.mdx @@ -0,0 +1,17 @@ +--- +title: ConversationSummary +description: ConversationSummary Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **summary** | `string` | ✅ | Conversation summary | +| **keyPoints** | `string[]` | optional | Key discussion points | +| **originalTokens** | `integer` | ✅ | Original token count | +| **summaryTokens** | `integer` | ✅ | Summary token count | +| **tokensSaved** | `integer` | ✅ | Tokens saved | +| **messageRange** | `object` | ✅ | Range of messages summarized | +| **generatedAt** | `string` | ✅ | ISO 8601 timestamp | +| **modelId** | `string` | optional | Model used for summarization | diff --git a/content/docs/references/ai/CostAlert.mdx b/content/docs/references/ai/CostAlert.mdx new file mode 100644 index 000000000..71839ae17 --- /dev/null +++ b/content/docs/references/ai/CostAlert.mdx @@ -0,0 +1,27 @@ +--- +title: CostAlert +description: CostAlert Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | | +| **timestamp** | `string` | ✅ | ISO 8601 timestamp | +| **type** | `Enum<'threshold_warning' \| 'threshold_critical' \| 'limit_exceeded' \| 'anomaly_detected' \| 'projection_exceeded'>` | ✅ | | +| **severity** | `Enum<'info' \| 'warning' \| 'critical'>` | ✅ | | +| **budgetId** | `string` | optional | | +| **budgetType** | `Enum<'global' \| 'user' \| 'agent' \| 'object' \| 'project' \| 'department'>` | optional | | +| **scope** | `string` | optional | | +| **message** | `string` | ✅ | Alert message | +| **currentCost** | `number` | ✅ | | +| **maxCost** | `number` | optional | | +| **threshold** | `number` | optional | | +| **currency** | `string` | optional | | +| **recommendations** | `string[]` | optional | | +| **acknowledged** | `boolean` | optional | | +| **acknowledgedBy** | `string` | optional | | +| **acknowledgedAt** | `string` | optional | | +| **resolved** | `boolean` | optional | | +| **metadata** | `Record` | optional | | diff --git a/content/docs/references/ai/CostAlertType.mdx b/content/docs/references/ai/CostAlertType.mdx new file mode 100644 index 000000000..1f086c20c --- /dev/null +++ b/content/docs/references/ai/CostAlertType.mdx @@ -0,0 +1,12 @@ +--- +title: CostAlertType +description: CostAlertType Schema Reference +--- + +## Allowed Values + +* `threshold_warning` +* `threshold_critical` +* `limit_exceeded` +* `anomaly_detected` +* `projection_exceeded` \ No newline at end of file diff --git a/content/docs/references/ai/CostAnalytics.mdx b/content/docs/references/ai/CostAnalytics.mdx new file mode 100644 index 000000000..15f44483b --- /dev/null +++ b/content/docs/references/ai/CostAnalytics.mdx @@ -0,0 +1,31 @@ +--- +title: CostAnalytics +description: CostAnalytics Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **periodStart** | `string` | ✅ | ISO 8601 timestamp | +| **periodEnd** | `string` | ✅ | ISO 8601 timestamp | +| **totalCost** | `number` | ✅ | | +| **totalRequests** | `integer` | ✅ | | +| **totalTokens** | `integer` | optional | | +| **currency** | `string` | optional | | +| **averageCostPerRequest** | `number` | ✅ | | +| **averageCostPerToken** | `number` | optional | | +| **averageRequestsPerDay** | `number` | ✅ | | +| **costTrend** | `Enum<'increasing' \| 'decreasing' \| 'stable'>` | optional | | +| **trendPercentage** | `number` | optional | % change vs previous period | +| **byModel** | `object[]` | optional | | +| **byProvider** | `object[]` | optional | | +| **byUser** | `object[]` | optional | | +| **byAgent** | `object[]` | optional | | +| **byOperation** | `object[]` | optional | | +| **byDate** | `object[]` | optional | | +| **topModels** | `object[]` | optional | | +| **topUsers** | `object[]` | optional | | +| **topAgents** | `object[]` | optional | | +| **tokensPerDollar** | `number` | optional | | +| **requestsPerDollar** | `number` | optional | | diff --git a/content/docs/references/ai/CostBreakdownDimension.mdx b/content/docs/references/ai/CostBreakdownDimension.mdx new file mode 100644 index 000000000..f52054965 --- /dev/null +++ b/content/docs/references/ai/CostBreakdownDimension.mdx @@ -0,0 +1,16 @@ +--- +title: CostBreakdownDimension +description: CostBreakdownDimension Schema Reference +--- + +## Allowed Values + +* `model` +* `provider` +* `user` +* `agent` +* `object` +* `operation` +* `date` +* `hour` +* `tag` \ No newline at end of file diff --git a/content/docs/references/ai/CostBreakdownEntry.mdx b/content/docs/references/ai/CostBreakdownEntry.mdx new file mode 100644 index 000000000..9fd1c8ff3 --- /dev/null +++ b/content/docs/references/ai/CostBreakdownEntry.mdx @@ -0,0 +1,17 @@ +--- +title: CostBreakdownEntry +description: CostBreakdownEntry Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **dimension** | `Enum<'model' \| 'provider' \| 'user' \| 'agent' \| 'object' \| 'operation' \| 'date' \| 'hour' \| 'tag'>` | ✅ | | +| **value** | `string` | ✅ | Dimension value (e.g., model ID, user ID) | +| **totalCost** | `number` | ✅ | | +| **requestCount** | `integer` | ✅ | | +| **totalTokens** | `integer` | optional | | +| **percentageOfTotal** | `number` | ✅ | | +| **periodStart** | `string` | optional | | +| **periodEnd** | `string` | optional | | diff --git a/content/docs/references/ai/CostEntry.mdx b/content/docs/references/ai/CostEntry.mdx new file mode 100644 index 000000000..2be46d286 --- /dev/null +++ b/content/docs/references/ai/CostEntry.mdx @@ -0,0 +1,29 @@ +--- +title: CostEntry +description: CostEntry Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | Unique cost entry ID | +| **timestamp** | `string` | ✅ | ISO 8601 timestamp | +| **modelId** | `string` | ✅ | AI model used | +| **provider** | `string` | ✅ | AI provider (e.g., "openai", "anthropic") | +| **operation** | `string` | ✅ | Operation type (e.g., "chat_completion", "embedding") | +| **promptTokens** | `integer` | optional | | +| **completionTokens** | `integer` | optional | | +| **totalTokens** | `integer` | optional | | +| **requestCount** | `integer` | optional | | +| **promptCost** | `number` | optional | Cost of prompt tokens | +| **completionCost** | `number` | optional | Cost of completion tokens | +| **totalCost** | `number` | ✅ | Total cost in base currency | +| **currency** | `string` | optional | | +| **sessionId** | `string` | optional | Conversation session ID | +| **userId** | `string` | optional | User who triggered the request | +| **agentId** | `string` | optional | AI agent ID | +| **object** | `string` | optional | Related object (e.g., "case", "project") | +| **recordId** | `string` | optional | Related record ID | +| **tags** | `string[]` | optional | | +| **metadata** | `Record` | optional | | diff --git a/content/docs/references/ai/CostMetricType.mdx b/content/docs/references/ai/CostMetricType.mdx new file mode 100644 index 000000000..29e70e3f0 --- /dev/null +++ b/content/docs/references/ai/CostMetricType.mdx @@ -0,0 +1,13 @@ +--- +title: CostMetricType +description: CostMetricType Schema Reference +--- + +## Allowed Values + +* `token` +* `request` +* `character` +* `second` +* `image` +* `embedding` \ No newline at end of file diff --git a/content/docs/references/ai/CostOptimizationRecommendation.mdx b/content/docs/references/ai/CostOptimizationRecommendation.mdx new file mode 100644 index 000000000..a5a2ed947 --- /dev/null +++ b/content/docs/references/ai/CostOptimizationRecommendation.mdx @@ -0,0 +1,24 @@ +--- +title: CostOptimizationRecommendation +description: CostOptimizationRecommendation Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | | +| **type** | `Enum<'switch_model' \| 'reduce_tokens' \| 'batch_requests' \| 'cache_results' \| 'adjust_parameters' \| 'limit_usage'>` | ✅ | | +| **title** | `string` | ✅ | | +| **description** | `string` | ✅ | | +| **estimatedSavings** | `number` | optional | | +| **savingsPercentage** | `number` | optional | | +| **priority** | `Enum<'low' \| 'medium' \| 'high'>` | ✅ | | +| **effort** | `Enum<'low' \| 'medium' \| 'high'>` | ✅ | | +| **actionable** | `boolean` | optional | | +| **actionSteps** | `string[]` | optional | | +| **targetModel** | `string` | optional | | +| **alternativeModel** | `string` | optional | | +| **affectedUsers** | `string[]` | optional | | +| **status** | `Enum<'pending' \| 'accepted' \| 'rejected' \| 'implemented'>` | optional | | +| **implementedAt** | `string` | optional | | diff --git a/content/docs/references/ai/CostQueryFilters.mdx b/content/docs/references/ai/CostQueryFilters.mdx new file mode 100644 index 000000000..2824bc8a6 --- /dev/null +++ b/content/docs/references/ai/CostQueryFilters.mdx @@ -0,0 +1,25 @@ +--- +title: CostQueryFilters +description: CostQueryFilters Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **startDate** | `string` | optional | ISO 8601 timestamp | +| **endDate** | `string` | optional | ISO 8601 timestamp | +| **modelIds** | `string[]` | optional | | +| **providers** | `string[]` | optional | | +| **userIds** | `string[]` | optional | | +| **agentIds** | `string[]` | optional | | +| **operations** | `string[]` | optional | | +| **sessionIds** | `string[]` | optional | | +| **minCost** | `number` | optional | | +| **maxCost** | `number` | optional | | +| **tags** | `string[]` | optional | | +| **groupBy** | `Enum<'model' \| 'provider' \| 'user' \| 'agent' \| 'object' \| 'operation' \| 'date' \| 'hour' \| 'tag'>[]` | optional | | +| **orderBy** | `Enum<'timestamp' \| 'cost' \| 'tokens'>` | optional | | +| **orderDirection** | `Enum<'asc' \| 'desc'>` | optional | | +| **limit** | `integer` | optional | | +| **offset** | `integer` | optional | | diff --git a/content/docs/references/ai/CostReport.mdx b/content/docs/references/ai/CostReport.mdx new file mode 100644 index 000000000..556b504ae --- /dev/null +++ b/content/docs/references/ai/CostReport.mdx @@ -0,0 +1,27 @@ +--- +title: CostReport +description: CostReport Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | | +| **name** | `string` | ✅ | | +| **generatedAt** | `string` | ✅ | ISO 8601 timestamp | +| **periodStart** | `string` | ✅ | ISO 8601 timestamp | +| **periodEnd** | `string` | ✅ | ISO 8601 timestamp | +| **period** | `Enum<'hourly' \| 'daily' \| 'weekly' \| 'monthly' \| 'quarterly' \| 'yearly' \| 'custom'>` | ✅ | | +| **analytics** | `object` | ✅ | | +| **budgets** | `object[]` | optional | | +| **alerts** | `object[]` | optional | | +| **activeAlertCount** | `integer` | optional | | +| **recommendations** | `object[]` | optional | | +| **previousPeriodCost** | `number` | optional | | +| **costChange** | `number` | optional | Change vs previous period | +| **costChangePercentage** | `number` | optional | | +| **forecastedCost** | `number` | optional | | +| **forecastedBudgetStatus** | `Enum<'under' \| 'at' \| 'over'>` | optional | | +| **format** | `Enum<'summary' \| 'detailed' \| 'executive'>` | optional | | +| **currency** | `string` | optional | | diff --git a/content/docs/references/ai/FunctionCall.mdx b/content/docs/references/ai/FunctionCall.mdx new file mode 100644 index 000000000..cf5f10605 --- /dev/null +++ b/content/docs/references/ai/FunctionCall.mdx @@ -0,0 +1,12 @@ +--- +title: FunctionCall +description: FunctionCall Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Function name | +| **arguments** | `string` | ✅ | JSON string of function arguments | +| **result** | `string` | optional | Function execution result | diff --git a/content/docs/references/ai/MessageContent.mdx b/content/docs/references/ai/MessageContent.mdx new file mode 100644 index 000000000..e7e9c6212 --- /dev/null +++ b/content/docs/references/ai/MessageContent.mdx @@ -0,0 +1,15 @@ +--- +title: MessageContent +description: MessageContent Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `Enum<'text' \| 'image' \| 'file' \| 'code' \| 'structured'>` | optional | | +| **text** | `string` | optional | Text content | +| **imageUrl** | `string` | optional | Image URL for vision models | +| **fileUrl** | `string` | optional | File attachment URL | +| **mimeType** | `string` | optional | MIME type for files | +| **metadata** | `Record` | optional | Additional metadata | diff --git a/content/docs/references/ai/MessageContentType.mdx b/content/docs/references/ai/MessageContentType.mdx new file mode 100644 index 000000000..234ce5b5b --- /dev/null +++ b/content/docs/references/ai/MessageContentType.mdx @@ -0,0 +1,12 @@ +--- +title: MessageContentType +description: MessageContentType Schema Reference +--- + +## Allowed Values + +* `text` +* `image` +* `file` +* `code` +* `structured` \ No newline at end of file diff --git a/content/docs/references/ai/MessagePruningEvent.mdx b/content/docs/references/ai/MessagePruningEvent.mdx new file mode 100644 index 000000000..eb1528348 --- /dev/null +++ b/content/docs/references/ai/MessagePruningEvent.mdx @@ -0,0 +1,17 @@ +--- +title: MessagePruningEvent +description: MessagePruningEvent Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **timestamp** | `string` | ✅ | ISO 8601 timestamp | +| **strategy** | `Enum<'fifo' \| 'importance' \| 'semantic' \| 'sliding_window' \| 'summary'>` | ✅ | | +| **reason** | `string` | ✅ | Reason for pruning | +| **prunedMessages** | `object[]` | ✅ | | +| **tokensFreed** | `integer` | ✅ | | +| **messagesRemoved** | `integer` | ✅ | | +| **remainingTokens** | `integer` | ✅ | | +| **remainingMessages** | `integer` | ✅ | | diff --git a/content/docs/references/ai/MessageRole.mdx b/content/docs/references/ai/MessageRole.mdx new file mode 100644 index 000000000..a24e4016d --- /dev/null +++ b/content/docs/references/ai/MessageRole.mdx @@ -0,0 +1,12 @@ +--- +title: MessageRole +description: MessageRole Schema Reference +--- + +## Allowed Values + +* `system` +* `user` +* `assistant` +* `function` +* `tool` \ No newline at end of file diff --git a/content/docs/references/ai/TokenBudgetConfig.mdx b/content/docs/references/ai/TokenBudgetConfig.mdx new file mode 100644 index 000000000..f9c01c073 --- /dev/null +++ b/content/docs/references/ai/TokenBudgetConfig.mdx @@ -0,0 +1,22 @@ +--- +title: TokenBudgetConfig +description: TokenBudgetConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **maxTokens** | `integer` | ✅ | Maximum total tokens | +| **maxPromptTokens** | `integer` | optional | Max tokens for prompt | +| **maxCompletionTokens** | `integer` | optional | Max tokens for completion | +| **reserveTokens** | `integer` | optional | Reserve tokens for system messages | +| **bufferPercentage** | `number` | optional | Buffer percentage (0.1 = 10%) | +| **strategy** | `Enum<'fifo' \| 'importance' \| 'semantic' \| 'sliding_window' \| 'summary'>` | optional | | +| **slidingWindowSize** | `integer` | optional | Number of recent messages to keep | +| **minImportanceScore** | `number` | optional | Minimum importance to keep | +| **semanticThreshold** | `number` | optional | Semantic similarity threshold | +| **enableSummarization** | `boolean` | optional | Enable context summarization | +| **summarizationThreshold** | `integer` | optional | Trigger summarization at N tokens | +| **summaryModel** | `string` | optional | Model ID for summarization | +| **warnThreshold** | `number` | optional | Warn at % of budget (0.8 = 80%) | diff --git a/content/docs/references/ai/TokenBudgetStrategy.mdx b/content/docs/references/ai/TokenBudgetStrategy.mdx new file mode 100644 index 000000000..4070b4693 --- /dev/null +++ b/content/docs/references/ai/TokenBudgetStrategy.mdx @@ -0,0 +1,12 @@ +--- +title: TokenBudgetStrategy +description: TokenBudgetStrategy Schema Reference +--- + +## Allowed Values + +* `fifo` +* `importance` +* `semantic` +* `sliding_window` +* `summary` \ No newline at end of file diff --git a/content/docs/references/ai/TokenUsageStats.mdx b/content/docs/references/ai/TokenUsageStats.mdx new file mode 100644 index 000000000..9d4a776b9 --- /dev/null +++ b/content/docs/references/ai/TokenUsageStats.mdx @@ -0,0 +1,19 @@ +--- +title: TokenUsageStats +description: TokenUsageStats Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **promptTokens** | `integer` | optional | | +| **completionTokens** | `integer` | optional | | +| **totalTokens** | `integer` | optional | | +| **budgetLimit** | `integer` | ✅ | | +| **budgetUsed** | `integer` | optional | | +| **budgetRemaining** | `integer` | ✅ | | +| **budgetPercentage** | `number` | ✅ | Usage as percentage of budget | +| **messageCount** | `integer` | optional | | +| **prunedMessageCount** | `integer` | optional | | +| **summarizedMessageCount** | `integer` | optional | | diff --git a/content/docs/references/ai/ToolCall.mdx b/content/docs/references/ai/ToolCall.mdx new file mode 100644 index 000000000..8c31d637a --- /dev/null +++ b/content/docs/references/ai/ToolCall.mdx @@ -0,0 +1,12 @@ +--- +title: ToolCall +description: ToolCall Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | Tool call ID | +| **type** | `Enum<'function'>` | optional | | +| **function** | `object` | ✅ | | diff --git a/content/docs/references/system/AuditConfig.mdx b/content/docs/references/system/AuditConfig.mdx new file mode 100644 index 000000000..6c4ec29e8 --- /dev/null +++ b/content/docs/references/system/AuditConfig.mdx @@ -0,0 +1,25 @@ +--- +title: AuditConfig +description: AuditConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Configuration name (snake_case) | +| **label** | `string` | ✅ | Display label | +| **enabled** | `boolean` | optional | Enable audit logging | +| **eventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | optional | Event types to audit | +| **excludeEventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | optional | Event types to exclude | +| **minimumSeverity** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>` | optional | Minimum severity level | +| **storage** | `object` | ✅ | Storage configuration | +| **retentionPolicy** | `object` | optional | Retention policy | +| **suspiciousActivityRules** | `object[]` | optional | Suspicious activity rules | +| **includeSensitiveData** | `boolean` | optional | Include sensitive data | +| **redactFields** | `string[]` | optional | Fields to redact | +| **logReads** | `boolean` | optional | Log read operations | +| **readSamplingRate** | `number` | optional | Read sampling rate | +| **logSystemEvents** | `boolean` | optional | Log system events | +| **customHandlers** | `object[]` | optional | Custom event handlers | +| **compliance** | `object` | optional | Compliance configuration | diff --git a/content/docs/references/system/AuditEvent.mdx b/content/docs/references/system/AuditEvent.mdx new file mode 100644 index 000000000..a1bc3ed39 --- /dev/null +++ b/content/docs/references/system/AuditEvent.mdx @@ -0,0 +1,23 @@ +--- +title: AuditEvent +description: AuditEvent Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | Audit event ID | +| **eventType** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>` | ✅ | Event type | +| **severity** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>` | optional | Event severity | +| **timestamp** | `string` | ✅ | Event timestamp | +| **actor** | `object` | ✅ | Event actor | +| **target** | `object` | optional | Event target | +| **description** | `string` | ✅ | Event description | +| **changes** | `object[]` | optional | List of changes | +| **result** | `Enum<'success' \| 'failure' \| 'partial'>` | optional | Action result | +| **errorMessage** | `string` | optional | Error message | +| **tenantId** | `string` | optional | Tenant identifier | +| **requestId** | `string` | optional | Request ID for tracing | +| **metadata** | `Record` | optional | Additional metadata | +| **location** | `object` | optional | Geographic location | diff --git a/content/docs/references/system/AuditEventActor.mdx b/content/docs/references/system/AuditEventActor.mdx new file mode 100644 index 000000000..f59a630a4 --- /dev/null +++ b/content/docs/references/system/AuditEventActor.mdx @@ -0,0 +1,15 @@ +--- +title: AuditEventActor +description: AuditEventActor Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `Enum<'user' \| 'system' \| 'service' \| 'api_client' \| 'integration'>` | ✅ | Actor type | +| **id** | `string` | ✅ | Actor identifier | +| **name** | `string` | optional | Actor display name | +| **email** | `string` | optional | Actor email address | +| **ipAddress** | `string` | optional | Actor IP address | +| **userAgent** | `string` | optional | User agent string | diff --git a/content/docs/references/system/AuditEventChange.mdx b/content/docs/references/system/AuditEventChange.mdx new file mode 100644 index 000000000..9ab45b293 --- /dev/null +++ b/content/docs/references/system/AuditEventChange.mdx @@ -0,0 +1,12 @@ +--- +title: AuditEventChange +description: AuditEventChange Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **field** | `string` | ✅ | Changed field name | +| **oldValue** | `any` | optional | Previous value | +| **newValue** | `any` | optional | New value | diff --git a/content/docs/references/system/AuditEventFilter.mdx b/content/docs/references/system/AuditEventFilter.mdx new file mode 100644 index 000000000..013cf44e0 --- /dev/null +++ b/content/docs/references/system/AuditEventFilter.mdx @@ -0,0 +1,17 @@ +--- +title: AuditEventFilter +description: AuditEventFilter Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **eventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | optional | Event types to include | +| **severities** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>[]` | optional | Severity levels to include | +| **actorId** | `string` | optional | Actor identifier | +| **tenantId** | `string` | optional | Tenant identifier | +| **timeRange** | `object` | optional | Time range filter | +| **result** | `Enum<'success' \| 'failure' \| 'partial'>` | optional | Result status | +| **searchQuery** | `string` | optional | Search query | +| **customFilters** | `Record` | optional | Custom filters | diff --git a/content/docs/references/system/AuditEventSeverity.mdx b/content/docs/references/system/AuditEventSeverity.mdx new file mode 100644 index 000000000..41d142d29 --- /dev/null +++ b/content/docs/references/system/AuditEventSeverity.mdx @@ -0,0 +1,15 @@ +--- +title: AuditEventSeverity +description: AuditEventSeverity Schema Reference +--- + +## Allowed Values + +* `debug` +* `info` +* `notice` +* `warning` +* `error` +* `critical` +* `alert` +* `emergency` \ No newline at end of file diff --git a/content/docs/references/system/AuditEventTarget.mdx b/content/docs/references/system/AuditEventTarget.mdx new file mode 100644 index 000000000..040747df5 --- /dev/null +++ b/content/docs/references/system/AuditEventTarget.mdx @@ -0,0 +1,13 @@ +--- +title: AuditEventTarget +description: AuditEventTarget Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `string` | ✅ | Target type | +| **id** | `string` | ✅ | Target identifier | +| **name** | `string` | optional | Target display name | +| **metadata** | `Record` | optional | Target metadata | diff --git a/content/docs/references/system/AuditEventType.mdx b/content/docs/references/system/AuditEventType.mdx new file mode 100644 index 000000000..4f6a42351 --- /dev/null +++ b/content/docs/references/system/AuditEventType.mdx @@ -0,0 +1,49 @@ +--- +title: AuditEventType +description: AuditEventType Schema Reference +--- + +## Allowed Values + +* `data.create` +* `data.read` +* `data.update` +* `data.delete` +* `data.export` +* `data.import` +* `data.bulk_update` +* `data.bulk_delete` +* `auth.login` +* `auth.login_failed` +* `auth.logout` +* `auth.session_created` +* `auth.session_expired` +* `auth.password_reset` +* `auth.password_changed` +* `auth.email_verified` +* `auth.mfa_enabled` +* `auth.mfa_disabled` +* `auth.account_locked` +* `auth.account_unlocked` +* `authz.permission_granted` +* `authz.permission_revoked` +* `authz.role_assigned` +* `authz.role_removed` +* `authz.role_created` +* `authz.role_updated` +* `authz.role_deleted` +* `authz.policy_created` +* `authz.policy_updated` +* `authz.policy_deleted` +* `system.config_changed` +* `system.plugin_installed` +* `system.plugin_uninstalled` +* `system.backup_created` +* `system.backup_restored` +* `system.integration_added` +* `system.integration_removed` +* `security.access_denied` +* `security.suspicious_activity` +* `security.data_breach` +* `security.api_key_created` +* `security.api_key_revoked` \ No newline at end of file diff --git a/content/docs/references/system/AuditRetentionPolicy.mdx b/content/docs/references/system/AuditRetentionPolicy.mdx new file mode 100644 index 000000000..fdb5c24d7 --- /dev/null +++ b/content/docs/references/system/AuditRetentionPolicy.mdx @@ -0,0 +1,14 @@ +--- +title: AuditRetentionPolicy +description: AuditRetentionPolicy Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **retentionDays** | `integer` | optional | Retention period in days | +| **archiveAfterRetention** | `boolean` | optional | Archive logs after retention period | +| **archiveStorage** | `object` | optional | Archive storage configuration | +| **customRetention** | `Record` | optional | Custom retention by event type | +| **minimumRetentionDays** | `integer` | optional | Minimum retention for compliance | diff --git a/content/docs/references/system/AuditStorageConfig.mdx b/content/docs/references/system/AuditStorageConfig.mdx new file mode 100644 index 000000000..2938b8fb3 --- /dev/null +++ b/content/docs/references/system/AuditStorageConfig.mdx @@ -0,0 +1,16 @@ +--- +title: AuditStorageConfig +description: AuditStorageConfig Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `Enum<'database' \| 'elasticsearch' \| 'mongodb' \| 'clickhouse' \| 's3' \| 'gcs' \| 'azure_blob' \| 'custom'>` | ✅ | Storage backend type | +| **connectionString** | `string` | optional | Connection string | +| **config** | `Record` | optional | Storage-specific configuration | +| **bufferEnabled** | `boolean` | optional | Enable buffering | +| **bufferSize** | `integer` | optional | Buffer size | +| **flushIntervalSeconds** | `integer` | optional | Flush interval in seconds | +| **compression** | `boolean` | optional | Enable compression | diff --git a/content/docs/references/system/DatabaseLevelIsolationStrategy.mdx b/content/docs/references/system/DatabaseLevelIsolationStrategy.mdx new file mode 100644 index 000000000..3e0d6ba8a --- /dev/null +++ b/content/docs/references/system/DatabaseLevelIsolationStrategy.mdx @@ -0,0 +1,14 @@ +--- +title: DatabaseLevelIsolationStrategy +description: DatabaseLevelIsolationStrategy Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **strategy** | `string` | ✅ | Database-level isolation strategy | +| **database** | `object` | optional | Database configuration | +| **connectionPool** | `object` | optional | Connection pool configuration | +| **backup** | `object` | optional | Backup configuration | +| **encryption** | `object` | optional | Encryption configuration | diff --git a/content/docs/references/system/LevelIsolationStrategySchema.mdx b/content/docs/references/system/LevelIsolationStrategySchema.mdx new file mode 100644 index 000000000..b7ebe9be3 --- /dev/null +++ b/content/docs/references/system/LevelIsolationStrategySchema.mdx @@ -0,0 +1,13 @@ +--- +title: LevelIsolationStrategySchema +description: LevelIsolationStrategySchema Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **strategy** | `string` | ✅ | Schema-level isolation strategy | +| **schema** | `object` | optional | Schema configuration | +| **migrations** | `object` | optional | Migration configuration | +| **performance** | `object` | optional | Performance settings | diff --git a/content/docs/references/system/RowLevelIsolationStrategy.mdx b/content/docs/references/system/RowLevelIsolationStrategy.mdx new file mode 100644 index 000000000..e15a26aff --- /dev/null +++ b/content/docs/references/system/RowLevelIsolationStrategy.mdx @@ -0,0 +1,12 @@ +--- +title: RowLevelIsolationStrategy +description: RowLevelIsolationStrategy Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **strategy** | `string` | ✅ | Row-level isolation strategy | +| **database** | `object` | optional | Database configuration | +| **performance** | `object` | optional | Performance settings | diff --git a/content/docs/references/system/SuspiciousActivityRule.mdx b/content/docs/references/system/SuspiciousActivityRule.mdx new file mode 100644 index 000000000..7bd718083 --- /dev/null +++ b/content/docs/references/system/SuspiciousActivityRule.mdx @@ -0,0 +1,18 @@ +--- +title: SuspiciousActivityRule +description: SuspiciousActivityRule Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **id** | `string` | ✅ | Rule identifier | +| **name** | `string` | ✅ | Rule name | +| **description** | `string` | optional | Rule description | +| **enabled** | `boolean` | optional | Rule enabled status | +| **eventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | ✅ | Event types to monitor | +| **condition** | `object` | ✅ | Detection condition | +| **actions** | `Enum<'alert' \| 'lock_account' \| 'block_ip' \| 'require_mfa' \| 'log_critical' \| 'webhook'>[]` | ✅ | Actions to take | +| **alertSeverity** | `Enum<'debug' \| 'info' \| 'notice' \| 'warning' \| 'error' \| 'critical' \| 'alert' \| 'emergency'>` | optional | Alert severity | +| **notifications** | `object` | optional | Notification configuration | diff --git a/content/docs/references/system/TenantIsolationConfig.mdx b/content/docs/references/system/TenantIsolationConfig.mdx new file mode 100644 index 000000000..80a0beec7 --- /dev/null +++ b/content/docs/references/system/TenantIsolationConfig.mdx @@ -0,0 +1,5 @@ +--- +title: TenantIsolationConfig +description: TenantIsolationConfig Schema Reference +--- + diff --git a/content/docs/references/system/TenantSecurityPolicy.mdx b/content/docs/references/system/TenantSecurityPolicy.mdx new file mode 100644 index 000000000..42a8fb480 --- /dev/null +++ b/content/docs/references/system/TenantSecurityPolicy.mdx @@ -0,0 +1,12 @@ +--- +title: TenantSecurityPolicy +description: TenantSecurityPolicy Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **encryption** | `object` | optional | Encryption requirements | +| **accessControl** | `object` | optional | Access control requirements | +| **compliance** | `object` | optional | Compliance requirements | diff --git a/packages/spec/json-schema/AuditConfig.json b/packages/spec/json-schema/AuditConfig.json new file mode 100644 index 000000000..ac0677df2 --- /dev/null +++ b/packages/spec/json-schema/AuditConfig.json @@ -0,0 +1,581 @@ +{ + "$ref": "#/definitions/AuditConfig", + "definitions": { + "AuditConfig": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Configuration name (snake_case)" + }, + "label": { + "type": "string", + "description": "Display label" + }, + "enabled": { + "type": "boolean", + "default": true, + "description": "Enable audit logging" + }, + "eventTypes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ] + }, + "description": "Event types to audit" + }, + "excludeEventTypes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ] + }, + "description": "Event types to exclude" + }, + "minimumSeverity": { + "type": "string", + "enum": [ + "debug", + "info", + "notice", + "warning", + "error", + "critical", + "alert", + "emergency" + ], + "default": "info", + "description": "Minimum severity level" + }, + "storage": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "database", + "elasticsearch", + "mongodb", + "clickhouse", + "s3", + "gcs", + "azure_blob", + "custom" + ], + "description": "Storage backend type" + }, + "connectionString": { + "type": "string", + "description": "Connection string" + }, + "config": { + "type": "object", + "additionalProperties": {}, + "description": "Storage-specific configuration" + }, + "bufferEnabled": { + "type": "boolean", + "default": true, + "description": "Enable buffering" + }, + "bufferSize": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 100, + "description": "Buffer size" + }, + "flushIntervalSeconds": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 5, + "description": "Flush interval in seconds" + }, + "compression": { + "type": "boolean", + "default": true, + "description": "Enable compression" + } + }, + "required": [ + "type" + ], + "additionalProperties": false, + "description": "Storage configuration" + }, + "retentionPolicy": { + "type": "object", + "properties": { + "retentionDays": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 180, + "description": "Retention period in days" + }, + "archiveAfterRetention": { + "type": "boolean", + "default": true, + "description": "Archive logs after retention period" + }, + "archiveStorage": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "s3", + "gcs", + "azure_blob", + "filesystem" + ], + "description": "Archive storage type" + }, + "endpoint": { + "type": "string", + "description": "Storage endpoint URL" + }, + "bucket": { + "type": "string", + "description": "Storage bucket/container name" + }, + "path": { + "type": "string", + "description": "Storage path prefix" + }, + "credentials": { + "type": "object", + "additionalProperties": {}, + "description": "Storage credentials" + } + }, + "required": [ + "type" + ], + "additionalProperties": false, + "description": "Archive storage configuration" + }, + "customRetention": { + "type": "object", + "additionalProperties": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "description": "Custom retention by event type" + }, + "minimumRetentionDays": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Minimum retention for compliance" + } + }, + "additionalProperties": false, + "default": {}, + "description": "Retention policy" + }, + "suspiciousActivityRules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Rule identifier" + }, + "name": { + "type": "string", + "description": "Rule name" + }, + "description": { + "type": "string", + "description": "Rule description" + }, + "enabled": { + "type": "boolean", + "default": true, + "description": "Rule enabled status" + }, + "eventTypes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ] + }, + "description": "Event types to monitor" + }, + "condition": { + "type": "object", + "properties": { + "threshold": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Event threshold" + }, + "windowSeconds": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Time window in seconds" + }, + "groupBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Grouping criteria" + }, + "filters": { + "type": "object", + "additionalProperties": {}, + "description": "Additional filters" + } + }, + "required": [ + "threshold", + "windowSeconds" + ], + "additionalProperties": false, + "description": "Detection condition" + }, + "actions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "alert", + "lock_account", + "block_ip", + "require_mfa", + "log_critical", + "webhook" + ] + }, + "description": "Actions to take" + }, + "alertSeverity": { + "type": "string", + "enum": [ + "debug", + "info", + "notice", + "warning", + "error", + "critical", + "alert", + "emergency" + ], + "default": "warning", + "description": "Alert severity" + }, + "notifications": { + "type": "object", + "properties": { + "email": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "description": "Email recipients" + }, + "slack": { + "type": "string", + "format": "uri", + "description": "Slack webhook URL" + }, + "webhook": { + "type": "string", + "format": "uri", + "description": "Custom webhook URL" + } + }, + "additionalProperties": false, + "description": "Notification configuration" + } + }, + "required": [ + "id", + "name", + "eventTypes", + "condition", + "actions" + ], + "additionalProperties": false + }, + "default": [], + "description": "Suspicious activity rules" + }, + "includeSensitiveData": { + "type": "boolean", + "default": false, + "description": "Include sensitive data" + }, + "redactFields": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "password", + "passwordHash", + "token", + "apiKey", + "secret", + "creditCard", + "ssn" + ], + "description": "Fields to redact" + }, + "logReads": { + "type": "boolean", + "default": false, + "description": "Log read operations" + }, + "readSamplingRate": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "description": "Read sampling rate" + }, + "logSystemEvents": { + "type": "boolean", + "default": true, + "description": "Log system events" + }, + "customHandlers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "eventType": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ], + "description": "Event type to handle" + } + }, + "required": [ + "eventType" + ], + "additionalProperties": false + }, + "description": "Custom event handlers" + }, + "compliance": { + "type": "object", + "properties": { + "standards": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "sox", + "hipaa", + "gdpr", + "pci_dss", + "iso_27001", + "fedramp" + ] + }, + "description": "Compliance standards" + }, + "immutableLogs": { + "type": "boolean", + "default": true, + "description": "Enforce immutable logs" + }, + "requireSigning": { + "type": "boolean", + "default": false, + "description": "Require log signing" + }, + "signingKey": { + "type": "string", + "description": "Signing key" + } + }, + "additionalProperties": false, + "description": "Compliance configuration" + } + }, + "required": [ + "name", + "label", + "storage" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditEvent.json b/packages/spec/json-schema/AuditEvent.json new file mode 100644 index 000000000..e7897b27c --- /dev/null +++ b/packages/spec/json-schema/AuditEvent.json @@ -0,0 +1,232 @@ +{ + "$ref": "#/definitions/AuditEvent", + "definitions": { + "AuditEvent": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Audit event ID" + }, + "eventType": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ], + "description": "Event type" + }, + "severity": { + "type": "string", + "enum": [ + "debug", + "info", + "notice", + "warning", + "error", + "critical", + "alert", + "emergency" + ], + "default": "info", + "description": "Event severity" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "Event timestamp" + }, + "actor": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "user", + "system", + "service", + "api_client", + "integration" + ], + "description": "Actor type" + }, + "id": { + "type": "string", + "description": "Actor identifier" + }, + "name": { + "type": "string", + "description": "Actor display name" + }, + "email": { + "type": "string", + "format": "email", + "description": "Actor email address" + }, + "ipAddress": { + "type": "string", + "description": "Actor IP address" + }, + "userAgent": { + "type": "string", + "description": "User agent string" + } + }, + "required": [ + "type", + "id" + ], + "additionalProperties": false, + "description": "Event actor" + }, + "target": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Target type" + }, + "id": { + "type": "string", + "description": "Target identifier" + }, + "name": { + "type": "string", + "description": "Target display name" + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "description": "Target metadata" + } + }, + "required": [ + "type", + "id" + ], + "additionalProperties": false, + "description": "Event target" + }, + "description": { + "type": "string", + "description": "Event description" + }, + "changes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string", + "description": "Changed field name" + }, + "oldValue": { + "description": "Previous value" + }, + "newValue": { + "description": "New value" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "List of changes" + }, + "result": { + "type": "string", + "enum": [ + "success", + "failure", + "partial" + ], + "default": "success", + "description": "Action result" + }, + "errorMessage": { + "type": "string", + "description": "Error message" + }, + "tenantId": { + "type": "string", + "description": "Tenant identifier" + }, + "requestId": { + "type": "string", + "description": "Request ID for tracing" + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "description": "Additional metadata" + }, + "location": { + "type": "object", + "properties": { + "country": { + "type": "string" + }, + "region": { + "type": "string" + }, + "city": { + "type": "string" + } + }, + "additionalProperties": false, + "description": "Geographic location" + } + }, + "required": [ + "id", + "eventType", + "timestamp", + "actor", + "description" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditEventActor.json b/packages/spec/json-schema/AuditEventActor.json new file mode 100644 index 000000000..12b50ae20 --- /dev/null +++ b/packages/spec/json-schema/AuditEventActor.json @@ -0,0 +1,48 @@ +{ + "$ref": "#/definitions/AuditEventActor", + "definitions": { + "AuditEventActor": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "user", + "system", + "service", + "api_client", + "integration" + ], + "description": "Actor type" + }, + "id": { + "type": "string", + "description": "Actor identifier" + }, + "name": { + "type": "string", + "description": "Actor display name" + }, + "email": { + "type": "string", + "format": "email", + "description": "Actor email address" + }, + "ipAddress": { + "type": "string", + "description": "Actor IP address" + }, + "userAgent": { + "type": "string", + "description": "User agent string" + } + }, + "required": [ + "type", + "id" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditEventChange.json b/packages/spec/json-schema/AuditEventChange.json new file mode 100644 index 000000000..b60cf554d --- /dev/null +++ b/packages/spec/json-schema/AuditEventChange.json @@ -0,0 +1,25 @@ +{ + "$ref": "#/definitions/AuditEventChange", + "definitions": { + "AuditEventChange": { + "type": "object", + "properties": { + "field": { + "type": "string", + "description": "Changed field name" + }, + "oldValue": { + "description": "Previous value" + }, + "newValue": { + "description": "New value" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditEventFilter.json b/packages/spec/json-schema/AuditEventFilter.json new file mode 100644 index 000000000..a4a039985 --- /dev/null +++ b/packages/spec/json-schema/AuditEventFilter.json @@ -0,0 +1,127 @@ +{ + "$ref": "#/definitions/AuditEventFilter", + "definitions": { + "AuditEventFilter": { + "type": "object", + "properties": { + "eventTypes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ] + }, + "description": "Event types to include" + }, + "severities": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "debug", + "info", + "notice", + "warning", + "error", + "critical", + "alert", + "emergency" + ] + }, + "description": "Severity levels to include" + }, + "actorId": { + "type": "string", + "description": "Actor identifier" + }, + "tenantId": { + "type": "string", + "description": "Tenant identifier" + }, + "timeRange": { + "type": "object", + "properties": { + "from": { + "type": "string", + "format": "date-time", + "description": "Start time" + }, + "to": { + "type": "string", + "format": "date-time", + "description": "End time" + } + }, + "required": [ + "from", + "to" + ], + "additionalProperties": false, + "description": "Time range filter" + }, + "result": { + "type": "string", + "enum": [ + "success", + "failure", + "partial" + ], + "description": "Result status" + }, + "searchQuery": { + "type": "string", + "description": "Search query" + }, + "customFilters": { + "type": "object", + "additionalProperties": {}, + "description": "Custom filters" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditEventSeverity.json b/packages/spec/json-schema/AuditEventSeverity.json new file mode 100644 index 000000000..fb4540fe8 --- /dev/null +++ b/packages/spec/json-schema/AuditEventSeverity.json @@ -0,0 +1,19 @@ +{ + "$ref": "#/definitions/AuditEventSeverity", + "definitions": { + "AuditEventSeverity": { + "type": "string", + "enum": [ + "debug", + "info", + "notice", + "warning", + "error", + "critical", + "alert", + "emergency" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditEventTarget.json b/packages/spec/json-schema/AuditEventTarget.json new file mode 100644 index 000000000..9e6b21656 --- /dev/null +++ b/packages/spec/json-schema/AuditEventTarget.json @@ -0,0 +1,33 @@ +{ + "$ref": "#/definitions/AuditEventTarget", + "definitions": { + "AuditEventTarget": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Target type" + }, + "id": { + "type": "string", + "description": "Target identifier" + }, + "name": { + "type": "string", + "description": "Target display name" + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "description": "Target metadata" + } + }, + "required": [ + "type", + "id" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditEventType.json b/packages/spec/json-schema/AuditEventType.json new file mode 100644 index 000000000..4f90ca603 --- /dev/null +++ b/packages/spec/json-schema/AuditEventType.json @@ -0,0 +1,53 @@ +{ + "$ref": "#/definitions/AuditEventType", + "definitions": { + "AuditEventType": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditRetentionPolicy.json b/packages/spec/json-schema/AuditRetentionPolicy.json new file mode 100644 index 000000000..55584587b --- /dev/null +++ b/packages/spec/json-schema/AuditRetentionPolicy.json @@ -0,0 +1,73 @@ +{ + "$ref": "#/definitions/AuditRetentionPolicy", + "definitions": { + "AuditRetentionPolicy": { + "type": "object", + "properties": { + "retentionDays": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 180, + "description": "Retention period in days" + }, + "archiveAfterRetention": { + "type": "boolean", + "default": true, + "description": "Archive logs after retention period" + }, + "archiveStorage": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "s3", + "gcs", + "azure_blob", + "filesystem" + ], + "description": "Archive storage type" + }, + "endpoint": { + "type": "string", + "description": "Storage endpoint URL" + }, + "bucket": { + "type": "string", + "description": "Storage bucket/container name" + }, + "path": { + "type": "string", + "description": "Storage path prefix" + }, + "credentials": { + "type": "object", + "additionalProperties": {}, + "description": "Storage credentials" + } + }, + "required": [ + "type" + ], + "additionalProperties": false, + "description": "Archive storage configuration" + }, + "customRetention": { + "type": "object", + "additionalProperties": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "description": "Custom retention by event type" + }, + "minimumRetentionDays": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Minimum retention for compliance" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AuditStorageConfig.json b/packages/spec/json-schema/AuditStorageConfig.json new file mode 100644 index 000000000..81c5359fa --- /dev/null +++ b/packages/spec/json-schema/AuditStorageConfig.json @@ -0,0 +1,60 @@ +{ + "$ref": "#/definitions/AuditStorageConfig", + "definitions": { + "AuditStorageConfig": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "database", + "elasticsearch", + "mongodb", + "clickhouse", + "s3", + "gcs", + "azure_blob", + "custom" + ], + "description": "Storage backend type" + }, + "connectionString": { + "type": "string", + "description": "Connection string" + }, + "config": { + "type": "object", + "additionalProperties": {}, + "description": "Storage-specific configuration" + }, + "bufferEnabled": { + "type": "boolean", + "default": true, + "description": "Enable buffering" + }, + "bufferSize": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 100, + "description": "Buffer size" + }, + "flushIntervalSeconds": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 5, + "description": "Flush interval in seconds" + }, + "compression": { + "type": "boolean", + "default": true, + "description": "Enable compression" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/BillingPeriod.json b/packages/spec/json-schema/BillingPeriod.json new file mode 100644 index 000000000..c3e35f111 --- /dev/null +++ b/packages/spec/json-schema/BillingPeriod.json @@ -0,0 +1,18 @@ +{ + "$ref": "#/definitions/BillingPeriod", + "definitions": { + "BillingPeriod": { + "type": "string", + "enum": [ + "hourly", + "daily", + "weekly", + "monthly", + "quarterly", + "yearly", + "custom" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/BudgetLimit.json b/packages/spec/json-schema/BudgetLimit.json new file mode 100644 index 000000000..966be9734 --- /dev/null +++ b/packages/spec/json-schema/BudgetLimit.json @@ -0,0 +1,111 @@ +{ + "$ref": "#/definitions/BudgetLimit", + "definitions": { + "BudgetLimit": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "global", + "user", + "agent", + "object", + "project", + "department" + ] + }, + "scope": { + "type": "string", + "description": "Scope identifier (userId, agentId, etc.)" + }, + "maxCost": { + "type": "number", + "minimum": 0, + "description": "Maximum cost limit" + }, + "currency": { + "type": "string", + "default": "USD" + }, + "period": { + "type": "string", + "enum": [ + "hourly", + "daily", + "weekly", + "monthly", + "quarterly", + "yearly", + "custom" + ] + }, + "customPeriodDays": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Custom period in days" + }, + "softLimit": { + "type": "number", + "minimum": 0, + "description": "Soft limit for warnings" + }, + "warnThresholds": { + "type": "array", + "items": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "description": "Warning thresholds (e.g., [0.5, 0.8, 0.95])" + }, + "enforced": { + "type": "boolean", + "default": true, + "description": "Block requests when exceeded" + }, + "gracePeriodSeconds": { + "type": "integer", + "minimum": 0, + "default": 0, + "description": "Grace period after limit exceeded" + }, + "allowRollover": { + "type": "boolean", + "default": false, + "description": "Allow unused budget to rollover" + }, + "maxRolloverPercentage": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Max rollover as % of limit" + }, + "name": { + "type": "string", + "description": "Budget name" + }, + "description": { + "type": "string" + }, + "active": { + "type": "boolean", + "default": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "type", + "maxCost", + "period" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/BudgetStatus.json b/packages/spec/json-schema/BudgetStatus.json new file mode 100644 index 000000000..fa2641ffe --- /dev/null +++ b/packages/spec/json-schema/BudgetStatus.json @@ -0,0 +1,91 @@ +{ + "$ref": "#/definitions/BudgetStatus", + "definitions": { + "BudgetStatus": { + "type": "object", + "properties": { + "budgetId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "global", + "user", + "agent", + "object", + "project", + "department" + ] + }, + "scope": { + "type": "string" + }, + "periodStart": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "periodEnd": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "currentCost": { + "type": "number", + "minimum": 0, + "default": 0 + }, + "maxCost": { + "type": "number", + "minimum": 0 + }, + "currency": { + "type": "string", + "default": "USD" + }, + "percentageUsed": { + "type": "number", + "minimum": 0, + "description": "Usage as percentage (can exceed 1.0 if over budget)" + }, + "remainingCost": { + "type": "number", + "description": "Remaining budget (can be negative if exceeded)" + }, + "isExceeded": { + "type": "boolean", + "default": false + }, + "isWarning": { + "type": "boolean", + "default": false + }, + "projectedCost": { + "type": "number", + "minimum": 0, + "description": "Projected cost for period" + }, + "projectedOverage": { + "type": "number", + "minimum": 0, + "description": "Projected overage" + }, + "lastUpdated": { + "type": "string", + "description": "ISO 8601 timestamp" + } + }, + "required": [ + "budgetId", + "type", + "periodStart", + "periodEnd", + "maxCost", + "percentageUsed", + "remainingCost", + "lastUpdated" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/BudgetType.json b/packages/spec/json-schema/BudgetType.json new file mode 100644 index 000000000..00cfbf527 --- /dev/null +++ b/packages/spec/json-schema/BudgetType.json @@ -0,0 +1,17 @@ +{ + "$ref": "#/definitions/BudgetType", + "definitions": { + "BudgetType": { + "type": "string", + "enum": [ + "global", + "user", + "agent", + "object", + "project", + "department" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ConversationAnalytics.json b/packages/spec/json-schema/ConversationAnalytics.json new file mode 100644 index 000000000..23a39cba3 --- /dev/null +++ b/packages/spec/json-schema/ConversationAnalytics.json @@ -0,0 +1,86 @@ +{ + "$ref": "#/definitions/ConversationAnalytics", + "definitions": { + "ConversationAnalytics": { + "type": "object", + "properties": { + "sessionId": { + "type": "string" + }, + "totalMessages": { + "type": "integer", + "minimum": 0 + }, + "userMessages": { + "type": "integer", + "minimum": 0 + }, + "assistantMessages": { + "type": "integer", + "minimum": 0 + }, + "systemMessages": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "averageTokensPerMessage": { + "type": "number", + "minimum": 0 + }, + "peakTokenUsage": { + "type": "integer", + "minimum": 0 + }, + "pruningEvents": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "summarizationEvents": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "tokensSavedByPruning": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "tokensSavedBySummarization": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "duration": { + "type": "number", + "minimum": 0, + "description": "Session duration in seconds" + }, + "firstMessageAt": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "lastMessageAt": { + "type": "string", + "description": "ISO 8601 timestamp" + } + }, + "required": [ + "sessionId", + "totalMessages", + "userMessages", + "assistantMessages", + "systemMessages", + "totalTokens", + "averageTokensPerMessage", + "peakTokenUsage" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ConversationContext.json b/packages/spec/json-schema/ConversationContext.json new file mode 100644 index 000000000..fda95530b --- /dev/null +++ b/packages/spec/json-schema/ConversationContext.json @@ -0,0 +1,48 @@ +{ + "$ref": "#/definitions/ConversationContext", + "definitions": { + "ConversationContext": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Conversation session ID" + }, + "userId": { + "type": "string", + "description": "User identifier" + }, + "agentId": { + "type": "string", + "description": "AI agent identifier" + }, + "object": { + "type": "string", + "description": "Related object (e.g., \"case\", \"project\")" + }, + "recordId": { + "type": "string", + "description": "Related record ID" + }, + "scope": { + "type": "object", + "additionalProperties": {}, + "description": "Additional context scope" + }, + "systemMessage": { + "type": "string", + "description": "System prompt/instructions" + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "sessionId" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ConversationMessage.json b/packages/spec/json-schema/ConversationMessage.json new file mode 100644 index 000000000..8fedfb987 --- /dev/null +++ b/packages/spec/json-schema/ConversationMessage.json @@ -0,0 +1,209 @@ +{ + "$ref": "#/definitions/ConversationMessage", + "definitions": { + "ConversationMessage": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique message ID" + }, + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "role": { + "type": "string", + "enum": [ + "system", + "user", + "assistant", + "function", + "tool" + ] + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "text", + "image", + "file", + "code", + "structured" + ], + "default": "text" + }, + "text": { + "type": "string", + "description": "Text content" + }, + "imageUrl": { + "type": "string", + "format": "uri", + "description": "Image URL for vision models" + }, + "fileUrl": { + "type": "string", + "format": "uri", + "description": "File attachment URL" + }, + "mimeType": { + "type": "string", + "description": "MIME type for files" + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "description": "Additional metadata" + } + }, + "additionalProperties": false + } + } + ], + "description": "Message content (text or multimodal)" + }, + "functionCall": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Function name" + }, + "arguments": { + "type": "string", + "description": "JSON string of function arguments" + }, + "result": { + "type": "string", + "description": "Function execution result" + } + }, + "required": [ + "name", + "arguments" + ], + "additionalProperties": false, + "description": "Legacy function call" + }, + "toolCalls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Tool call ID" + }, + "type": { + "type": "string", + "enum": [ + "function" + ], + "default": "function" + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Function name" + }, + "arguments": { + "type": "string", + "description": "JSON string of function arguments" + }, + "result": { + "type": "string", + "description": "Function execution result" + } + }, + "required": [ + "name", + "arguments" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "function" + ], + "additionalProperties": false + }, + "description": "Tool calls" + }, + "toolCallId": { + "type": "string", + "description": "Tool call ID this message responds to" + }, + "name": { + "type": "string", + "description": "Name of the function/user" + }, + "tokens": { + "type": "object", + "properties": { + "prompt": { + "type": "integer", + "minimum": 0, + "description": "Tokens in prompt" + }, + "completion": { + "type": "integer", + "minimum": 0, + "description": "Tokens in completion" + }, + "total": { + "type": "integer", + "minimum": 0, + "description": "Total tokens" + } + }, + "additionalProperties": false, + "description": "Token usage for this message" + }, + "pinned": { + "type": "boolean", + "default": false, + "description": "Prevent removal during pruning" + }, + "importance": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Importance score for pruning" + }, + "embedding": { + "type": "array", + "items": { + "type": "number" + }, + "description": "Vector embedding for semantic search" + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "timestamp", + "role", + "content" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ConversationSession.json b/packages/spec/json-schema/ConversationSession.json new file mode 100644 index 000000000..b994fa482 --- /dev/null +++ b/packages/spec/json-schema/ConversationSession.json @@ -0,0 +1,458 @@ +{ + "$ref": "#/definitions/ConversationSession", + "definitions": { + "ConversationSession": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique session ID" + }, + "name": { + "type": "string", + "description": "Session name/title" + }, + "context": { + "type": "object", + "properties": { + "sessionId": { + "type": "string", + "description": "Conversation session ID" + }, + "userId": { + "type": "string", + "description": "User identifier" + }, + "agentId": { + "type": "string", + "description": "AI agent identifier" + }, + "object": { + "type": "string", + "description": "Related object (e.g., \"case\", \"project\")" + }, + "recordId": { + "type": "string", + "description": "Related record ID" + }, + "scope": { + "type": "object", + "additionalProperties": {}, + "description": "Additional context scope" + }, + "systemMessage": { + "type": "string", + "description": "System prompt/instructions" + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "sessionId" + ], + "additionalProperties": false + }, + "modelId": { + "type": "string", + "description": "AI model ID" + }, + "tokenBudget": { + "type": "object", + "properties": { + "maxTokens": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Maximum total tokens" + }, + "maxPromptTokens": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Max tokens for prompt" + }, + "maxCompletionTokens": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Max tokens for completion" + }, + "reserveTokens": { + "type": "integer", + "minimum": 0, + "default": 500, + "description": "Reserve tokens for system messages" + }, + "bufferPercentage": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "description": "Buffer percentage (0.1 = 10%)" + }, + "strategy": { + "type": "string", + "enum": [ + "fifo", + "importance", + "semantic", + "sliding_window", + "summary" + ], + "default": "sliding_window" + }, + "slidingWindowSize": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Number of recent messages to keep" + }, + "minImportanceScore": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Minimum importance to keep" + }, + "semanticThreshold": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Semantic similarity threshold" + }, + "enableSummarization": { + "type": "boolean", + "default": false, + "description": "Enable context summarization" + }, + "summarizationThreshold": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Trigger summarization at N tokens" + }, + "summaryModel": { + "type": "string", + "description": "Model ID for summarization" + }, + "warnThreshold": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.8, + "description": "Warn at % of budget (0.8 = 80%)" + } + }, + "required": [ + "maxTokens" + ], + "additionalProperties": false + }, + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique message ID" + }, + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "role": { + "type": "string", + "enum": [ + "system", + "user", + "assistant", + "function", + "tool" + ] + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "text", + "image", + "file", + "code", + "structured" + ], + "default": "text" + }, + "text": { + "type": "string", + "description": "Text content" + }, + "imageUrl": { + "type": "string", + "format": "uri", + "description": "Image URL for vision models" + }, + "fileUrl": { + "type": "string", + "format": "uri", + "description": "File attachment URL" + }, + "mimeType": { + "type": "string", + "description": "MIME type for files" + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "description": "Additional metadata" + } + }, + "additionalProperties": false + } + } + ], + "description": "Message content (text or multimodal)" + }, + "functionCall": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Function name" + }, + "arguments": { + "type": "string", + "description": "JSON string of function arguments" + }, + "result": { + "type": "string", + "description": "Function execution result" + } + }, + "required": [ + "name", + "arguments" + ], + "additionalProperties": false, + "description": "Legacy function call" + }, + "toolCalls": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Tool call ID" + }, + "type": { + "type": "string", + "enum": [ + "function" + ], + "default": "function" + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Function name" + }, + "arguments": { + "type": "string", + "description": "JSON string of function arguments" + }, + "result": { + "type": "string", + "description": "Function execution result" + } + }, + "required": [ + "name", + "arguments" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "function" + ], + "additionalProperties": false + }, + "description": "Tool calls" + }, + "toolCallId": { + "type": "string", + "description": "Tool call ID this message responds to" + }, + "name": { + "type": "string", + "description": "Name of the function/user" + }, + "tokens": { + "type": "object", + "properties": { + "prompt": { + "type": "integer", + "minimum": 0, + "description": "Tokens in prompt" + }, + "completion": { + "type": "integer", + "minimum": 0, + "description": "Tokens in completion" + }, + "total": { + "type": "integer", + "minimum": 0, + "description": "Total tokens" + } + }, + "additionalProperties": false, + "description": "Token usage for this message" + }, + "pinned": { + "type": "boolean", + "default": false, + "description": "Prevent removal during pruning" + }, + "importance": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Importance score for pruning" + }, + "embedding": { + "type": "array", + "items": { + "type": "number" + }, + "description": "Vector embedding for semantic search" + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "timestamp", + "role", + "content" + ], + "additionalProperties": false + }, + "default": [] + }, + "tokens": { + "type": "object", + "properties": { + "promptTokens": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "completionTokens": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "budgetLimit": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "budgetUsed": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "budgetRemaining": { + "type": "integer", + "minimum": 0 + }, + "budgetPercentage": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Usage as percentage of budget" + }, + "messageCount": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "prunedMessageCount": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "summarizedMessageCount": { + "type": "integer", + "minimum": 0, + "default": 0 + } + }, + "required": [ + "budgetLimit", + "budgetRemaining", + "budgetPercentage" + ], + "additionalProperties": false + }, + "status": { + "type": "string", + "enum": [ + "active", + "paused", + "completed", + "archived" + ], + "default": "active" + }, + "createdAt": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "updatedAt": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "expiresAt": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "context", + "tokenBudget", + "createdAt", + "updatedAt" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ConversationSummary.json b/packages/spec/json-schema/ConversationSummary.json new file mode 100644 index 000000000..566a0d9df --- /dev/null +++ b/packages/spec/json-schema/ConversationSummary.json @@ -0,0 +1,73 @@ +{ + "$ref": "#/definitions/ConversationSummary", + "definitions": { + "ConversationSummary": { + "type": "object", + "properties": { + "summary": { + "type": "string", + "description": "Conversation summary" + }, + "keyPoints": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Key discussion points" + }, + "originalTokens": { + "type": "integer", + "minimum": 0, + "description": "Original token count" + }, + "summaryTokens": { + "type": "integer", + "minimum": 0, + "description": "Summary token count" + }, + "tokensSaved": { + "type": "integer", + "minimum": 0, + "description": "Tokens saved" + }, + "messageRange": { + "type": "object", + "properties": { + "startIndex": { + "type": "integer", + "minimum": 0 + }, + "endIndex": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "startIndex", + "endIndex" + ], + "additionalProperties": false, + "description": "Range of messages summarized" + }, + "generatedAt": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "modelId": { + "type": "string", + "description": "Model used for summarization" + } + }, + "required": [ + "summary", + "originalTokens", + "summaryTokens", + "tokensSaved", + "messageRange", + "generatedAt" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostAlert.json b/packages/spec/json-schema/CostAlert.json new file mode 100644 index 000000000..2c5d82d41 --- /dev/null +++ b/packages/spec/json-schema/CostAlert.json @@ -0,0 +1,107 @@ +{ + "$ref": "#/definitions/CostAlert", + "definitions": { + "CostAlert": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "type": { + "type": "string", + "enum": [ + "threshold_warning", + "threshold_critical", + "limit_exceeded", + "anomaly_detected", + "projection_exceeded" + ] + }, + "severity": { + "type": "string", + "enum": [ + "info", + "warning", + "critical" + ] + }, + "budgetId": { + "type": "string" + }, + "budgetType": { + "type": "string", + "enum": [ + "global", + "user", + "agent", + "object", + "project", + "department" + ] + }, + "scope": { + "type": "string" + }, + "message": { + "type": "string", + "description": "Alert message" + }, + "currentCost": { + "type": "number", + "minimum": 0 + }, + "maxCost": { + "type": "number", + "minimum": 0 + }, + "threshold": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "currency": { + "type": "string", + "default": "USD" + }, + "recommendations": { + "type": "array", + "items": { + "type": "string" + } + }, + "acknowledged": { + "type": "boolean", + "default": false + }, + "acknowledgedBy": { + "type": "string" + }, + "acknowledgedAt": { + "type": "string" + }, + "resolved": { + "type": "boolean", + "default": false + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "timestamp", + "type", + "severity", + "message", + "currentCost" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostAlertType.json b/packages/spec/json-schema/CostAlertType.json new file mode 100644 index 000000000..92df165d4 --- /dev/null +++ b/packages/spec/json-schema/CostAlertType.json @@ -0,0 +1,16 @@ +{ + "$ref": "#/definitions/CostAlertType", + "definitions": { + "CostAlertType": { + "type": "string", + "enum": [ + "threshold_warning", + "threshold_critical", + "limit_exceeded", + "anomaly_detected", + "projection_exceeded" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostAnalytics.json b/packages/spec/json-schema/CostAnalytics.json new file mode 100644 index 000000000..5b49a6762 --- /dev/null +++ b/packages/spec/json-schema/CostAnalytics.json @@ -0,0 +1,589 @@ +{ + "$ref": "#/definitions/CostAnalytics", + "definitions": { + "CostAnalytics": { + "type": "object", + "properties": { + "periodStart": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "periodEnd": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "totalRequests": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "currency": { + "type": "string", + "default": "USD" + }, + "averageCostPerRequest": { + "type": "number", + "minimum": 0 + }, + "averageCostPerToken": { + "type": "number", + "minimum": 0 + }, + "averageRequestsPerDay": { + "type": "number", + "minimum": 0 + }, + "costTrend": { + "type": "string", + "enum": [ + "increasing", + "decreasing", + "stable" + ] + }, + "trendPercentage": { + "type": "number", + "description": "% change vs previous period" + }, + "byModel": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byProvider": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byUser": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byAgent": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byOperation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byDate": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "topModels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "topUsers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "topAgents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "tokensPerDollar": { + "type": "number", + "minimum": 0 + }, + "requestsPerDollar": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "periodStart", + "periodEnd", + "totalCost", + "totalRequests", + "averageCostPerRequest", + "averageRequestsPerDay" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostBreakdownDimension.json b/packages/spec/json-schema/CostBreakdownDimension.json new file mode 100644 index 000000000..6adf1cca4 --- /dev/null +++ b/packages/spec/json-schema/CostBreakdownDimension.json @@ -0,0 +1,20 @@ +{ + "$ref": "#/definitions/CostBreakdownDimension", + "definitions": { + "CostBreakdownDimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostBreakdownEntry.json b/packages/spec/json-schema/CostBreakdownEntry.json new file mode 100644 index 000000000..74a712ff0 --- /dev/null +++ b/packages/spec/json-schema/CostBreakdownEntry.json @@ -0,0 +1,60 @@ +{ + "$ref": "#/definitions/CostBreakdownEntry", + "definitions": { + "CostBreakdownEntry": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostEntry.json b/packages/spec/json-schema/CostEntry.json new file mode 100644 index 000000000..3e92a89ea --- /dev/null +++ b/packages/spec/json-schema/CostEntry.json @@ -0,0 +1,106 @@ +{ + "$ref": "#/definitions/CostEntry", + "definitions": { + "CostEntry": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique cost entry ID" + }, + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "modelId": { + "type": "string", + "description": "AI model used" + }, + "provider": { + "type": "string", + "description": "AI provider (e.g., \"openai\", \"anthropic\")" + }, + "operation": { + "type": "string", + "description": "Operation type (e.g., \"chat_completion\", \"embedding\")" + }, + "promptTokens": { + "type": "integer", + "minimum": 0 + }, + "completionTokens": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 1 + }, + "promptCost": { + "type": "number", + "minimum": 0, + "description": "Cost of prompt tokens" + }, + "completionCost": { + "type": "number", + "minimum": 0, + "description": "Cost of completion tokens" + }, + "totalCost": { + "type": "number", + "minimum": 0, + "description": "Total cost in base currency" + }, + "currency": { + "type": "string", + "default": "USD" + }, + "sessionId": { + "type": "string", + "description": "Conversation session ID" + }, + "userId": { + "type": "string", + "description": "User who triggered the request" + }, + "agentId": { + "type": "string", + "description": "AI agent ID" + }, + "object": { + "type": "string", + "description": "Related object (e.g., \"case\", \"project\")" + }, + "recordId": { + "type": "string", + "description": "Related record ID" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "timestamp", + "modelId", + "provider", + "operation", + "totalCost" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostMetricType.json b/packages/spec/json-schema/CostMetricType.json new file mode 100644 index 000000000..aac8b9c47 --- /dev/null +++ b/packages/spec/json-schema/CostMetricType.json @@ -0,0 +1,17 @@ +{ + "$ref": "#/definitions/CostMetricType", + "definitions": { + "CostMetricType": { + "type": "string", + "enum": [ + "token", + "request", + "character", + "second", + "image", + "embedding" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostOptimizationRecommendation.json b/packages/spec/json-schema/CostOptimizationRecommendation.json new file mode 100644 index 000000000..05db81d88 --- /dev/null +++ b/packages/spec/json-schema/CostOptimizationRecommendation.json @@ -0,0 +1,100 @@ +{ + "$ref": "#/definitions/CostOptimizationRecommendation", + "definitions": { + "CostOptimizationRecommendation": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "switch_model", + "reduce_tokens", + "batch_requests", + "cache_results", + "adjust_parameters", + "limit_usage" + ] + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "estimatedSavings": { + "type": "number", + "minimum": 0 + }, + "savingsPercentage": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "priority": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "effort": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "actionable": { + "type": "boolean", + "default": true + }, + "actionSteps": { + "type": "array", + "items": { + "type": "string" + } + }, + "targetModel": { + "type": "string" + }, + "alternativeModel": { + "type": "string" + }, + "affectedUsers": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string", + "enum": [ + "pending", + "accepted", + "rejected", + "implemented" + ], + "default": "pending" + }, + "implementedAt": { + "type": "string" + } + }, + "required": [ + "id", + "type", + "title", + "description", + "priority", + "effort" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostQueryFilters.json b/packages/spec/json-schema/CostQueryFilters.json new file mode 100644 index 000000000..bcd977905 --- /dev/null +++ b/packages/spec/json-schema/CostQueryFilters.json @@ -0,0 +1,112 @@ +{ + "$ref": "#/definitions/CostQueryFilters", + "definitions": { + "CostQueryFilters": { + "type": "object", + "properties": { + "startDate": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "endDate": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "modelIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "providers": { + "type": "array", + "items": { + "type": "string" + } + }, + "userIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "agentIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "operations": { + "type": "array", + "items": { + "type": "string" + } + }, + "sessionIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "minCost": { + "type": "number", + "minimum": 0 + }, + "maxCost": { + "type": "number", + "minimum": 0 + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "groupBy": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + } + }, + "orderBy": { + "type": "string", + "enum": [ + "timestamp", + "cost", + "tokens" + ], + "default": "timestamp" + }, + "orderDirection": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "desc" + }, + "limit": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "offset": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CostReport.json b/packages/spec/json-schema/CostReport.json new file mode 100644 index 000000000..39028ac09 --- /dev/null +++ b/packages/spec/json-schema/CostReport.json @@ -0,0 +1,964 @@ +{ + "$ref": "#/definitions/CostReport", + "definitions": { + "CostReport": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "generatedAt": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "periodStart": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "periodEnd": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "period": { + "type": "string", + "enum": [ + "hourly", + "daily", + "weekly", + "monthly", + "quarterly", + "yearly", + "custom" + ] + }, + "analytics": { + "type": "object", + "properties": { + "periodStart": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "periodEnd": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "totalRequests": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "currency": { + "type": "string", + "default": "USD" + }, + "averageCostPerRequest": { + "type": "number", + "minimum": 0 + }, + "averageCostPerToken": { + "type": "number", + "minimum": 0 + }, + "averageRequestsPerDay": { + "type": "number", + "minimum": 0 + }, + "costTrend": { + "type": "string", + "enum": [ + "increasing", + "decreasing", + "stable" + ] + }, + "trendPercentage": { + "type": "number", + "description": "% change vs previous period" + }, + "byModel": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byProvider": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byUser": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byAgent": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byOperation": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "byDate": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "topModels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "topUsers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "topAgents": { + "type": "array", + "items": { + "type": "object", + "properties": { + "dimension": { + "type": "string", + "enum": [ + "model", + "provider", + "user", + "agent", + "object", + "operation", + "date", + "hour", + "tag" + ] + }, + "value": { + "type": "string", + "description": "Dimension value (e.g., model ID, user ID)" + }, + "totalCost": { + "type": "number", + "minimum": 0 + }, + "requestCount": { + "type": "integer", + "minimum": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0 + }, + "percentageOfTotal": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "periodStart": { + "type": "string" + }, + "periodEnd": { + "type": "string" + } + }, + "required": [ + "dimension", + "value", + "totalCost", + "requestCount", + "percentageOfTotal" + ], + "additionalProperties": false + } + }, + "tokensPerDollar": { + "type": "number", + "minimum": 0 + }, + "requestsPerDollar": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "periodStart", + "periodEnd", + "totalCost", + "totalRequests", + "averageCostPerRequest", + "averageRequestsPerDay" + ], + "additionalProperties": false + }, + "budgets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "budgetId": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "global", + "user", + "agent", + "object", + "project", + "department" + ] + }, + "scope": { + "type": "string" + }, + "periodStart": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "periodEnd": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "currentCost": { + "type": "number", + "minimum": 0, + "default": 0 + }, + "maxCost": { + "type": "number", + "minimum": 0 + }, + "currency": { + "type": "string", + "default": "USD" + }, + "percentageUsed": { + "type": "number", + "minimum": 0, + "description": "Usage as percentage (can exceed 1.0 if over budget)" + }, + "remainingCost": { + "type": "number", + "description": "Remaining budget (can be negative if exceeded)" + }, + "isExceeded": { + "type": "boolean", + "default": false + }, + "isWarning": { + "type": "boolean", + "default": false + }, + "projectedCost": { + "type": "number", + "minimum": 0, + "description": "Projected cost for period" + }, + "projectedOverage": { + "type": "number", + "minimum": 0, + "description": "Projected overage" + }, + "lastUpdated": { + "type": "string", + "description": "ISO 8601 timestamp" + } + }, + "required": [ + "budgetId", + "type", + "periodStart", + "periodEnd", + "maxCost", + "percentageUsed", + "remainingCost", + "lastUpdated" + ], + "additionalProperties": false + } + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "type": { + "type": "string", + "enum": [ + "threshold_warning", + "threshold_critical", + "limit_exceeded", + "anomaly_detected", + "projection_exceeded" + ] + }, + "severity": { + "type": "string", + "enum": [ + "info", + "warning", + "critical" + ] + }, + "budgetId": { + "type": "string" + }, + "budgetType": { + "type": "string", + "enum": [ + "global", + "user", + "agent", + "object", + "project", + "department" + ] + }, + "scope": { + "type": "string" + }, + "message": { + "type": "string", + "description": "Alert message" + }, + "currentCost": { + "type": "number", + "minimum": 0 + }, + "maxCost": { + "type": "number", + "minimum": 0 + }, + "threshold": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "currency": { + "type": "string", + "default": "USD" + }, + "recommendations": { + "type": "array", + "items": { + "type": "string" + } + }, + "acknowledged": { + "type": "boolean", + "default": false + }, + "acknowledgedBy": { + "type": "string" + }, + "acknowledgedAt": { + "type": "string" + }, + "resolved": { + "type": "boolean", + "default": false + }, + "metadata": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "id", + "timestamp", + "type", + "severity", + "message", + "currentCost" + ], + "additionalProperties": false + } + }, + "activeAlertCount": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "recommendations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "switch_model", + "reduce_tokens", + "batch_requests", + "cache_results", + "adjust_parameters", + "limit_usage" + ] + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "estimatedSavings": { + "type": "number", + "minimum": 0 + }, + "savingsPercentage": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "priority": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "effort": { + "type": "string", + "enum": [ + "low", + "medium", + "high" + ] + }, + "actionable": { + "type": "boolean", + "default": true + }, + "actionSteps": { + "type": "array", + "items": { + "type": "string" + } + }, + "targetModel": { + "type": "string" + }, + "alternativeModel": { + "type": "string" + }, + "affectedUsers": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string", + "enum": [ + "pending", + "accepted", + "rejected", + "implemented" + ], + "default": "pending" + }, + "implementedAt": { + "type": "string" + } + }, + "required": [ + "id", + "type", + "title", + "description", + "priority", + "effort" + ], + "additionalProperties": false + } + }, + "previousPeriodCost": { + "type": "number", + "minimum": 0 + }, + "costChange": { + "type": "number", + "description": "Change vs previous period" + }, + "costChangePercentage": { + "type": "number" + }, + "forecastedCost": { + "type": "number", + "minimum": 0 + }, + "forecastedBudgetStatus": { + "type": "string", + "enum": [ + "under", + "at", + "over" + ] + }, + "format": { + "type": "string", + "enum": [ + "summary", + "detailed", + "executive" + ], + "default": "summary" + }, + "currency": { + "type": "string", + "default": "USD" + } + }, + "required": [ + "id", + "name", + "generatedAt", + "periodStart", + "periodEnd", + "period", + "analytics" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/DatabaseLevelIsolationStrategy.json b/packages/spec/json-schema/DatabaseLevelIsolationStrategy.json new file mode 100644 index 000000000..c10de7e89 --- /dev/null +++ b/packages/spec/json-schema/DatabaseLevelIsolationStrategy.json @@ -0,0 +1,139 @@ +{ + "$ref": "#/definitions/DatabaseLevelIsolationStrategy", + "definitions": { + "DatabaseLevelIsolationStrategy": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "const": "isolated_db", + "description": "Database-level isolation strategy" + }, + "database": { + "type": "object", + "properties": { + "namingPattern": { + "type": "string", + "default": "tenant_{tenant_id}", + "description": "Database naming pattern" + }, + "serverStrategy": { + "type": "string", + "enum": [ + "shared", + "sharded", + "dedicated" + ], + "default": "shared", + "description": "Server assignment strategy" + }, + "separateCredentials": { + "type": "boolean", + "default": true, + "description": "Separate credentials per tenant" + }, + "autoCreateDatabase": { + "type": "boolean", + "default": true, + "description": "Auto-create database" + } + }, + "additionalProperties": false, + "description": "Database configuration" + }, + "connectionPool": { + "type": "object", + "properties": { + "poolSize": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 10, + "description": "Connection pool size" + }, + "maxActivePools": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 100, + "description": "Max active pools" + }, + "idleTimeout": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 300, + "description": "Idle pool timeout" + }, + "usePooler": { + "type": "boolean", + "default": true, + "description": "Use connection pooler" + } + }, + "additionalProperties": false, + "description": "Connection pool configuration" + }, + "backup": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "individual", + "consolidated", + "on_demand" + ], + "default": "individual", + "description": "Backup strategy" + }, + "frequencyHours": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 24, + "description": "Backup frequency" + }, + "retentionDays": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 30, + "description": "Backup retention days" + } + }, + "additionalProperties": false, + "description": "Backup configuration" + }, + "encryption": { + "type": "object", + "properties": { + "perTenantKeys": { + "type": "boolean", + "default": false, + "description": "Per-tenant encryption keys" + }, + "algorithm": { + "type": "string", + "default": "AES-256-GCM", + "description": "Encryption algorithm" + }, + "keyManagement": { + "type": "string", + "enum": [ + "aws_kms", + "azure_key_vault", + "gcp_kms", + "hashicorp_vault", + "custom" + ], + "description": "Key management service" + } + }, + "additionalProperties": false, + "description": "Encryption configuration" + } + }, + "required": [ + "strategy" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/FunctionCall.json b/packages/spec/json-schema/FunctionCall.json new file mode 100644 index 000000000..d706a167b --- /dev/null +++ b/packages/spec/json-schema/FunctionCall.json @@ -0,0 +1,28 @@ +{ + "$ref": "#/definitions/FunctionCall", + "definitions": { + "FunctionCall": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Function name" + }, + "arguments": { + "type": "string", + "description": "JSON string of function arguments" + }, + "result": { + "type": "string", + "description": "Function execution result" + } + }, + "required": [ + "name", + "arguments" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/LevelIsolationStrategySchema.json b/packages/spec/json-schema/LevelIsolationStrategySchema.json new file mode 100644 index 000000000..956cd8b0e --- /dev/null +++ b/packages/spec/json-schema/LevelIsolationStrategySchema.json @@ -0,0 +1,93 @@ +{ + "$ref": "#/definitions/LevelIsolationStrategySchema", + "definitions": { + "LevelIsolationStrategySchema": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "const": "isolated_schema", + "description": "Schema-level isolation strategy" + }, + "schema": { + "type": "object", + "properties": { + "namingPattern": { + "type": "string", + "default": "tenant_{tenant_id}", + "description": "Schema naming pattern" + }, + "includePublicSchema": { + "type": "boolean", + "default": true, + "description": "Include public schema" + }, + "sharedSchema": { + "type": "string", + "default": "public", + "description": "Schema for shared resources" + }, + "autoCreateSchema": { + "type": "boolean", + "default": true, + "description": "Auto-create schema" + } + }, + "additionalProperties": false, + "description": "Schema configuration" + }, + "migrations": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "parallel", + "sequential", + "on_demand" + ], + "default": "parallel", + "description": "Migration strategy" + }, + "maxConcurrent": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 10, + "description": "Max concurrent migrations" + }, + "rollbackOnError": { + "type": "boolean", + "default": true, + "description": "Rollback on error" + } + }, + "additionalProperties": false, + "description": "Migration configuration" + }, + "performance": { + "type": "object", + "properties": { + "poolPerSchema": { + "type": "boolean", + "default": false, + "description": "Separate pool per schema" + }, + "schemaCacheTTL": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 3600, + "description": "Schema cache TTL" + } + }, + "additionalProperties": false, + "description": "Performance settings" + } + }, + "required": [ + "strategy" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/MessageContent.json b/packages/spec/json-schema/MessageContent.json new file mode 100644 index 000000000..b02b78075 --- /dev/null +++ b/packages/spec/json-schema/MessageContent.json @@ -0,0 +1,46 @@ +{ + "$ref": "#/definitions/MessageContent", + "definitions": { + "MessageContent": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "text", + "image", + "file", + "code", + "structured" + ], + "default": "text" + }, + "text": { + "type": "string", + "description": "Text content" + }, + "imageUrl": { + "type": "string", + "format": "uri", + "description": "Image URL for vision models" + }, + "fileUrl": { + "type": "string", + "format": "uri", + "description": "File attachment URL" + }, + "mimeType": { + "type": "string", + "description": "MIME type for files" + }, + "metadata": { + "type": "object", + "additionalProperties": {}, + "description": "Additional metadata" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/MessageContentType.json b/packages/spec/json-schema/MessageContentType.json new file mode 100644 index 000000000..446c93b09 --- /dev/null +++ b/packages/spec/json-schema/MessageContentType.json @@ -0,0 +1,16 @@ +{ + "$ref": "#/definitions/MessageContentType", + "definitions": { + "MessageContentType": { + "type": "string", + "enum": [ + "text", + "image", + "file", + "code", + "structured" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/MessagePruningEvent.json b/packages/spec/json-schema/MessagePruningEvent.json new file mode 100644 index 000000000..11cd4ad9f --- /dev/null +++ b/packages/spec/json-schema/MessagePruningEvent.json @@ -0,0 +1,92 @@ +{ + "$ref": "#/definitions/MessagePruningEvent", + "definitions": { + "MessagePruningEvent": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "strategy": { + "type": "string", + "enum": [ + "fifo", + "importance", + "semantic", + "sliding_window", + "summary" + ] + }, + "reason": { + "type": "string", + "description": "Reason for pruning" + }, + "prunedMessages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "messageId": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "system", + "user", + "assistant", + "function", + "tool" + ] + }, + "tokens": { + "type": "integer", + "minimum": 0 + }, + "importance": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + }, + "required": [ + "messageId", + "role", + "tokens" + ], + "additionalProperties": false + } + }, + "tokensFreed": { + "type": "integer", + "minimum": 0 + }, + "messagesRemoved": { + "type": "integer", + "minimum": 0 + }, + "remainingTokens": { + "type": "integer", + "minimum": 0 + }, + "remainingMessages": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "timestamp", + "strategy", + "reason", + "prunedMessages", + "tokensFreed", + "messagesRemoved", + "remainingTokens", + "remainingMessages" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/MessageRole.json b/packages/spec/json-schema/MessageRole.json new file mode 100644 index 000000000..fd0393d13 --- /dev/null +++ b/packages/spec/json-schema/MessageRole.json @@ -0,0 +1,16 @@ +{ + "$ref": "#/definitions/MessageRole", + "definitions": { + "MessageRole": { + "type": "string", + "enum": [ + "system", + "user", + "assistant", + "function", + "tool" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/RowLevelIsolationStrategy.json b/packages/spec/json-schema/RowLevelIsolationStrategy.json new file mode 100644 index 000000000..b30ff6648 --- /dev/null +++ b/packages/spec/json-schema/RowLevelIsolationStrategy.json @@ -0,0 +1,74 @@ +{ + "$ref": "#/definitions/RowLevelIsolationStrategy", + "definitions": { + "RowLevelIsolationStrategy": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "const": "shared_schema", + "description": "Row-level isolation strategy" + }, + "database": { + "type": "object", + "properties": { + "enableRLS": { + "type": "boolean", + "default": true, + "description": "Enable PostgreSQL Row-Level Security" + }, + "contextMethod": { + "type": "string", + "enum": [ + "session_variable", + "search_path", + "application_name" + ], + "default": "session_variable", + "description": "How to set tenant context" + }, + "contextVariable": { + "type": "string", + "default": "app.current_tenant", + "description": "Session variable name" + }, + "applicationValidation": { + "type": "boolean", + "default": true, + "description": "Application-level tenant validation" + } + }, + "additionalProperties": false, + "description": "Database configuration" + }, + "performance": { + "type": "object", + "properties": { + "usePartialIndexes": { + "type": "boolean", + "default": true, + "description": "Use partial indexes per tenant" + }, + "usePartitioning": { + "type": "boolean", + "default": false, + "description": "Use table partitioning by tenant_id" + }, + "poolSizePerTenant": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Connection pool size per tenant" + } + }, + "additionalProperties": false, + "description": "Performance settings" + } + }, + "required": [ + "strategy" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/SuspiciousActivityRule.json b/packages/spec/json-schema/SuspiciousActivityRule.json new file mode 100644 index 000000000..a1ad4d3d2 --- /dev/null +++ b/packages/spec/json-schema/SuspiciousActivityRule.json @@ -0,0 +1,175 @@ +{ + "$ref": "#/definitions/SuspiciousActivityRule", + "definitions": { + "SuspiciousActivityRule": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Rule identifier" + }, + "name": { + "type": "string", + "description": "Rule name" + }, + "description": { + "type": "string", + "description": "Rule description" + }, + "enabled": { + "type": "boolean", + "default": true, + "description": "Rule enabled status" + }, + "eventTypes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "data.create", + "data.read", + "data.update", + "data.delete", + "data.export", + "data.import", + "data.bulk_update", + "data.bulk_delete", + "auth.login", + "auth.login_failed", + "auth.logout", + "auth.session_created", + "auth.session_expired", + "auth.password_reset", + "auth.password_changed", + "auth.email_verified", + "auth.mfa_enabled", + "auth.mfa_disabled", + "auth.account_locked", + "auth.account_unlocked", + "authz.permission_granted", + "authz.permission_revoked", + "authz.role_assigned", + "authz.role_removed", + "authz.role_created", + "authz.role_updated", + "authz.role_deleted", + "authz.policy_created", + "authz.policy_updated", + "authz.policy_deleted", + "system.config_changed", + "system.plugin_installed", + "system.plugin_uninstalled", + "system.backup_created", + "system.backup_restored", + "system.integration_added", + "system.integration_removed", + "security.access_denied", + "security.suspicious_activity", + "security.data_breach", + "security.api_key_created", + "security.api_key_revoked" + ] + }, + "description": "Event types to monitor" + }, + "condition": { + "type": "object", + "properties": { + "threshold": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Event threshold" + }, + "windowSeconds": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Time window in seconds" + }, + "groupBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Grouping criteria" + }, + "filters": { + "type": "object", + "additionalProperties": {}, + "description": "Additional filters" + } + }, + "required": [ + "threshold", + "windowSeconds" + ], + "additionalProperties": false, + "description": "Detection condition" + }, + "actions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "alert", + "lock_account", + "block_ip", + "require_mfa", + "log_critical", + "webhook" + ] + }, + "description": "Actions to take" + }, + "alertSeverity": { + "type": "string", + "enum": [ + "debug", + "info", + "notice", + "warning", + "error", + "critical", + "alert", + "emergency" + ], + "default": "warning", + "description": "Alert severity" + }, + "notifications": { + "type": "object", + "properties": { + "email": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "description": "Email recipients" + }, + "slack": { + "type": "string", + "format": "uri", + "description": "Slack webhook URL" + }, + "webhook": { + "type": "string", + "format": "uri", + "description": "Custom webhook URL" + } + }, + "additionalProperties": false, + "description": "Notification configuration" + } + }, + "required": [ + "id", + "name", + "eventTypes", + "condition", + "actions" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/TenantIsolationConfig.json b/packages/spec/json-schema/TenantIsolationConfig.json new file mode 100644 index 000000000..3769598ef --- /dev/null +++ b/packages/spec/json-schema/TenantIsolationConfig.json @@ -0,0 +1,298 @@ +{ + "$ref": "#/definitions/TenantIsolationConfig", + "definitions": { + "TenantIsolationConfig": { + "anyOf": [ + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "const": "shared_schema", + "description": "Row-level isolation strategy" + }, + "database": { + "type": "object", + "properties": { + "enableRLS": { + "type": "boolean", + "default": true, + "description": "Enable PostgreSQL Row-Level Security" + }, + "contextMethod": { + "type": "string", + "enum": [ + "session_variable", + "search_path", + "application_name" + ], + "default": "session_variable", + "description": "How to set tenant context" + }, + "contextVariable": { + "type": "string", + "default": "app.current_tenant", + "description": "Session variable name" + }, + "applicationValidation": { + "type": "boolean", + "default": true, + "description": "Application-level tenant validation" + } + }, + "additionalProperties": false, + "description": "Database configuration" + }, + "performance": { + "type": "object", + "properties": { + "usePartialIndexes": { + "type": "boolean", + "default": true, + "description": "Use partial indexes per tenant" + }, + "usePartitioning": { + "type": "boolean", + "default": false, + "description": "Use table partitioning by tenant_id" + }, + "poolSizePerTenant": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Connection pool size per tenant" + } + }, + "additionalProperties": false, + "description": "Performance settings" + } + }, + "required": [ + "strategy" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "const": "isolated_schema", + "description": "Schema-level isolation strategy" + }, + "schema": { + "type": "object", + "properties": { + "namingPattern": { + "type": "string", + "default": "tenant_{tenant_id}", + "description": "Schema naming pattern" + }, + "includePublicSchema": { + "type": "boolean", + "default": true, + "description": "Include public schema" + }, + "sharedSchema": { + "type": "string", + "default": "public", + "description": "Schema for shared resources" + }, + "autoCreateSchema": { + "type": "boolean", + "default": true, + "description": "Auto-create schema" + } + }, + "additionalProperties": false, + "description": "Schema configuration" + }, + "migrations": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "parallel", + "sequential", + "on_demand" + ], + "default": "parallel", + "description": "Migration strategy" + }, + "maxConcurrent": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 10, + "description": "Max concurrent migrations" + }, + "rollbackOnError": { + "type": "boolean", + "default": true, + "description": "Rollback on error" + } + }, + "additionalProperties": false, + "description": "Migration configuration" + }, + "performance": { + "type": "object", + "properties": { + "poolPerSchema": { + "type": "boolean", + "default": false, + "description": "Separate pool per schema" + }, + "schemaCacheTTL": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 3600, + "description": "Schema cache TTL" + } + }, + "additionalProperties": false, + "description": "Performance settings" + } + }, + "required": [ + "strategy" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "const": "isolated_db", + "description": "Database-level isolation strategy" + }, + "database": { + "type": "object", + "properties": { + "namingPattern": { + "type": "string", + "default": "tenant_{tenant_id}", + "description": "Database naming pattern" + }, + "serverStrategy": { + "type": "string", + "enum": [ + "shared", + "sharded", + "dedicated" + ], + "default": "shared", + "description": "Server assignment strategy" + }, + "separateCredentials": { + "type": "boolean", + "default": true, + "description": "Separate credentials per tenant" + }, + "autoCreateDatabase": { + "type": "boolean", + "default": true, + "description": "Auto-create database" + } + }, + "additionalProperties": false, + "description": "Database configuration" + }, + "connectionPool": { + "type": "object", + "properties": { + "poolSize": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 10, + "description": "Connection pool size" + }, + "maxActivePools": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 100, + "description": "Max active pools" + }, + "idleTimeout": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 300, + "description": "Idle pool timeout" + }, + "usePooler": { + "type": "boolean", + "default": true, + "description": "Use connection pooler" + } + }, + "additionalProperties": false, + "description": "Connection pool configuration" + }, + "backup": { + "type": "object", + "properties": { + "strategy": { + "type": "string", + "enum": [ + "individual", + "consolidated", + "on_demand" + ], + "default": "individual", + "description": "Backup strategy" + }, + "frequencyHours": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 24, + "description": "Backup frequency" + }, + "retentionDays": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 30, + "description": "Backup retention days" + } + }, + "additionalProperties": false, + "description": "Backup configuration" + }, + "encryption": { + "type": "object", + "properties": { + "perTenantKeys": { + "type": "boolean", + "default": false, + "description": "Per-tenant encryption keys" + }, + "algorithm": { + "type": "string", + "default": "AES-256-GCM", + "description": "Encryption algorithm" + }, + "keyManagement": { + "type": "string", + "enum": [ + "aws_kms", + "azure_key_vault", + "gcp_kms", + "hashicorp_vault", + "custom" + ], + "description": "Key management service" + } + }, + "additionalProperties": false, + "description": "Encryption configuration" + } + }, + "required": [ + "strategy" + ], + "additionalProperties": false + } + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/TenantSecurityPolicy.json b/packages/spec/json-schema/TenantSecurityPolicy.json new file mode 100644 index 000000000..b1744f08b --- /dev/null +++ b/packages/spec/json-schema/TenantSecurityPolicy.json @@ -0,0 +1,115 @@ +{ + "$ref": "#/definitions/TenantSecurityPolicy", + "definitions": { + "TenantSecurityPolicy": { + "type": "object", + "properties": { + "encryption": { + "type": "object", + "properties": { + "atRest": { + "type": "boolean", + "default": true, + "description": "Require encryption at rest" + }, + "inTransit": { + "type": "boolean", + "default": true, + "description": "Require encryption in transit" + }, + "fieldLevel": { + "type": "boolean", + "default": false, + "description": "Require field-level encryption" + } + }, + "additionalProperties": false, + "description": "Encryption requirements" + }, + "accessControl": { + "type": "object", + "properties": { + "requireMFA": { + "type": "boolean", + "default": false, + "description": "Require MFA" + }, + "requireSSO": { + "type": "boolean", + "default": false, + "description": "Require SSO" + }, + "ipWhitelist": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Allowed IP addresses" + }, + "sessionTimeout": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 3600, + "description": "Session timeout" + } + }, + "additionalProperties": false, + "description": "Access control requirements" + }, + "compliance": { + "type": "object", + "properties": { + "standards": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "sox", + "hipaa", + "gdpr", + "pci_dss", + "iso_27001", + "fedramp" + ] + }, + "description": "Compliance standards" + }, + "requireAuditLog": { + "type": "boolean", + "default": true, + "description": "Require audit logging" + }, + "auditRetentionDays": { + "type": "integer", + "exclusiveMinimum": 0, + "default": 365, + "description": "Audit retention days" + }, + "dataResidency": { + "type": "object", + "properties": { + "region": { + "type": "string", + "description": "Required region (e.g., US, EU, APAC)" + }, + "excludeRegions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Prohibited regions" + } + }, + "additionalProperties": false, + "description": "Data residency requirements" + } + }, + "additionalProperties": false, + "description": "Compliance requirements" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/TokenBudgetConfig.json b/packages/spec/json-schema/TokenBudgetConfig.json new file mode 100644 index 000000000..52b803fae --- /dev/null +++ b/packages/spec/json-schema/TokenBudgetConfig.json @@ -0,0 +1,92 @@ +{ + "$ref": "#/definitions/TokenBudgetConfig", + "definitions": { + "TokenBudgetConfig": { + "type": "object", + "properties": { + "maxTokens": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Maximum total tokens" + }, + "maxPromptTokens": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Max tokens for prompt" + }, + "maxCompletionTokens": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Max tokens for completion" + }, + "reserveTokens": { + "type": "integer", + "minimum": 0, + "default": 500, + "description": "Reserve tokens for system messages" + }, + "bufferPercentage": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "description": "Buffer percentage (0.1 = 10%)" + }, + "strategy": { + "type": "string", + "enum": [ + "fifo", + "importance", + "semantic", + "sliding_window", + "summary" + ], + "default": "sliding_window" + }, + "slidingWindowSize": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Number of recent messages to keep" + }, + "minImportanceScore": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Minimum importance to keep" + }, + "semanticThreshold": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Semantic similarity threshold" + }, + "enableSummarization": { + "type": "boolean", + "default": false, + "description": "Enable context summarization" + }, + "summarizationThreshold": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Trigger summarization at N tokens" + }, + "summaryModel": { + "type": "string", + "description": "Model ID for summarization" + }, + "warnThreshold": { + "type": "number", + "minimum": 0, + "maximum": 1, + "default": 0.8, + "description": "Warn at % of budget (0.8 = 80%)" + } + }, + "required": [ + "maxTokens" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/TokenBudgetStrategy.json b/packages/spec/json-schema/TokenBudgetStrategy.json new file mode 100644 index 000000000..514518988 --- /dev/null +++ b/packages/spec/json-schema/TokenBudgetStrategy.json @@ -0,0 +1,16 @@ +{ + "$ref": "#/definitions/TokenBudgetStrategy", + "definitions": { + "TokenBudgetStrategy": { + "type": "string", + "enum": [ + "fifo", + "importance", + "semantic", + "sliding_window", + "summary" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/TokenUsageStats.json b/packages/spec/json-schema/TokenUsageStats.json new file mode 100644 index 000000000..1f66314c5 --- /dev/null +++ b/packages/spec/json-schema/TokenUsageStats.json @@ -0,0 +1,66 @@ +{ + "$ref": "#/definitions/TokenUsageStats", + "definitions": { + "TokenUsageStats": { + "type": "object", + "properties": { + "promptTokens": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "completionTokens": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "totalTokens": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "budgetLimit": { + "type": "integer", + "exclusiveMinimum": 0 + }, + "budgetUsed": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "budgetRemaining": { + "type": "integer", + "minimum": 0 + }, + "budgetPercentage": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Usage as percentage of budget" + }, + "messageCount": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "prunedMessageCount": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "summarizedMessageCount": { + "type": "integer", + "minimum": 0, + "default": 0 + } + }, + "required": [ + "budgetLimit", + "budgetRemaining", + "budgetPercentage" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ToolCall.json b/packages/spec/json-schema/ToolCall.json new file mode 100644 index 000000000..b01eed498 --- /dev/null +++ b/packages/spec/json-schema/ToolCall.json @@ -0,0 +1,49 @@ +{ + "$ref": "#/definitions/ToolCall", + "definitions": { + "ToolCall": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Tool call ID" + }, + "type": { + "type": "string", + "enum": [ + "function" + ], + "default": "function" + }, + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Function name" + }, + "arguments": { + "type": "string", + "description": "JSON string of function arguments" + }, + "result": { + "type": "string", + "description": "Function execution result" + } + }, + "required": [ + "name", + "arguments" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "function" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file From 31c2fca1c170e1ad3218c3e9545e58d1fcbb4095 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:02:25 +0000 Subject: [PATCH 4/4] Address code review feedback - improve validation and documentation Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com> --- content/docs/references/system/AuditConfig.mdx | 4 ++-- packages/spec/json-schema/AuditConfig.json | 14 ++++++++++---- .../spec/json-schema/AuditRetentionPolicy.json | 2 +- packages/spec/src/system/audit.zod.ts | 15 ++++++++------- packages/spec/src/system/tenant.zod.ts | 11 +++++++---- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/content/docs/references/system/AuditConfig.mdx b/content/docs/references/system/AuditConfig.mdx index 6c4ec29e8..feebef7d9 100644 --- a/content/docs/references/system/AuditConfig.mdx +++ b/content/docs/references/system/AuditConfig.mdx @@ -7,7 +7,7 @@ description: AuditConfig Schema Reference | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Configuration name (snake_case) | +| **name** | `string` | ✅ | Configuration name (snake_case, max 64 chars) | | **label** | `string` | ✅ | Display label | | **enabled** | `boolean` | optional | Enable audit logging | | **eventTypes** | `Enum<'data.create' \| 'data.read' \| 'data.update' \| 'data.delete' \| 'data.export' \| 'data.import' \| 'data.bulk_update' \| 'data.bulk_delete' \| 'auth.login' \| 'auth.login_failed' \| 'auth.logout' \| 'auth.session_created' \| 'auth.session_expired' \| 'auth.password_reset' \| 'auth.password_changed' \| 'auth.email_verified' \| 'auth.mfa_enabled' \| 'auth.mfa_disabled' \| 'auth.account_locked' \| 'auth.account_unlocked' \| 'authz.permission_granted' \| 'authz.permission_revoked' \| 'authz.role_assigned' \| 'authz.role_removed' \| 'authz.role_created' \| 'authz.role_updated' \| 'authz.role_deleted' \| 'authz.policy_created' \| 'authz.policy_updated' \| 'authz.policy_deleted' \| 'system.config_changed' \| 'system.plugin_installed' \| 'system.plugin_uninstalled' \| 'system.backup_created' \| 'system.backup_restored' \| 'system.integration_added' \| 'system.integration_removed' \| 'security.access_denied' \| 'security.suspicious_activity' \| 'security.data_breach' \| 'security.api_key_created' \| 'security.api_key_revoked'>[]` | optional | Event types to audit | @@ -21,5 +21,5 @@ description: AuditConfig Schema Reference | **logReads** | `boolean` | optional | Log read operations | | **readSamplingRate** | `number` | optional | Read sampling rate | | **logSystemEvents** | `boolean` | optional | Log system events | -| **customHandlers** | `object[]` | optional | Custom event handlers | +| **customHandlers** | `object[]` | optional | Custom event handler references | | **compliance** | `object` | optional | Compliance configuration | diff --git a/packages/spec/json-schema/AuditConfig.json b/packages/spec/json-schema/AuditConfig.json index ac0677df2..27ea79f2a 100644 --- a/packages/spec/json-schema/AuditConfig.json +++ b/packages/spec/json-schema/AuditConfig.json @@ -7,7 +7,8 @@ "name": { "type": "string", "pattern": "^[a-z_][a-z0-9_]*$", - "description": "Configuration name (snake_case)" + "maxLength": 64, + "description": "Configuration name (snake_case, max 64 chars)" }, "label": { "type": "string", @@ -195,7 +196,7 @@ "properties": { "retentionDays": { "type": "integer", - "exclusiveMinimum": 0, + "minimum": 1, "default": 180, "description": "Retention period in days" }, @@ -523,14 +524,19 @@ "security.api_key_revoked" ], "description": "Event type to handle" + }, + "handlerId": { + "type": "string", + "description": "Unique identifier for the handler" } }, "required": [ - "eventType" + "eventType", + "handlerId" ], "additionalProperties": false }, - "description": "Custom event handlers" + "description": "Custom event handler references" }, "compliance": { "type": "object", diff --git a/packages/spec/json-schema/AuditRetentionPolicy.json b/packages/spec/json-schema/AuditRetentionPolicy.json index 55584587b..861306ea6 100644 --- a/packages/spec/json-schema/AuditRetentionPolicy.json +++ b/packages/spec/json-schema/AuditRetentionPolicy.json @@ -6,7 +6,7 @@ "properties": { "retentionDays": { "type": "integer", - "exclusiveMinimum": 0, + "minimum": 1, "default": 180, "description": "Retention period in days" }, diff --git a/packages/spec/src/system/audit.zod.ts b/packages/spec/src/system/audit.zod.ts index 2e1c18b9b..a4cd3d3fa 100644 --- a/packages/spec/src/system/audit.zod.ts +++ b/packages/spec/src/system/audit.zod.ts @@ -271,7 +271,7 @@ export const AuditRetentionPolicySchema = z.object({ * Retention period in days * Default: 180 days (GDPR 6-month requirement) */ - retentionDays: z.number().int().positive().default(180).describe('Retention period in days'), + retentionDays: z.number().int().min(1).default(180).describe('Retention period in days'), /** * Whether to archive logs after retention period @@ -510,10 +510,13 @@ export type AuditEventFilter = z.infer; export const AuditConfigSchema = z.object({ /** * Unique identifier for this audit configuration + * Must be in snake_case following ObjectStack conventions + * Maximum length: 64 characters */ name: z.string() .regex(/^[a-z_][a-z0-9_]*$/) - .describe('Configuration name (snake_case)'), + .max(64) + .describe('Configuration name (snake_case, max 64 chars)'), /** * Human-readable label @@ -595,14 +598,12 @@ export const AuditConfigSchema = z.object({ /** * Custom audit event handlers + * Note: Function handlers are for runtime configuration only and will not be serialized to JSON Schema */ customHandlers: z.array(z.object({ eventType: AuditEventType.describe('Event type to handle'), - handler: z.function() - .args(AuditEventSchema) - .returns(z.promise(z.void())) - .describe('Handler function'), - })).optional().describe('Custom event handlers'), + handlerId: z.string().describe('Unique identifier for the handler'), + })).optional().describe('Custom event handler references'), /** * Compliance mode configuration diff --git a/packages/spec/src/system/tenant.zod.ts b/packages/spec/src/system/tenant.zod.ts index 1e06471d0..430ab0564 100644 --- a/packages/spec/src/system/tenant.zod.ts +++ b/packages/spec/src/system/tenant.zod.ts @@ -125,10 +125,11 @@ export type Tenant = z.infer; * * EXAMPLE RLS POLICY (PostgreSQL): * ```sql - * CREATE POLICY tenant_isolation ON customers + * -- Example: Apply RLS policy to a table (e.g., "app_data") + * CREATE POLICY tenant_isolation ON app_data * USING (tenant_id = current_setting('app.current_tenant')::text); * - * ALTER TABLE customers ENABLE ROW LEVEL SECURITY; + * ALTER TABLE app_data ENABLE ROW LEVEL SECURITY; * ``` */ export const RowLevelIsolationStrategySchema = z.object({ @@ -240,7 +241,8 @@ export const SchemaLevelIsolationStrategySchema = z.object({ schema: z.object({ /** * Schema naming pattern - * Use {tenant_id} as placeholder + * Use {tenant_id} as placeholder (must contain only alphanumeric and underscores) + * The tenant_id will be sanitized before substitution to prevent SQL injection */ namingPattern: z.string().default('tenant_{tenant_id}').describe('Schema naming pattern'), @@ -359,7 +361,8 @@ export const DatabaseLevelIsolationStrategySchema = z.object({ database: z.object({ /** * Database naming pattern - * Use {tenant_id} as placeholder + * Use {tenant_id} as placeholder (must contain only alphanumeric and underscores) + * The tenant_id will be sanitized before substitution to prevent SQL injection */ namingPattern: z.string().default('tenant_{tenant_id}').describe('Database naming pattern'),