Skip to main content

Overview

Shannon uses PostgreSQL with pgvector extension for persistent storage. The database is organized into two schemas:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Total Tables: 18 tables Extensions: uuid-ossp, pg_trgm, btree_gin, pgcrypto

Entity Relationship Diagram

┌─────────────────┐
│  auth.tenants   │
└────────┬────────┘

         │ 1:N

┌─────────────────┐        ┌──────────────────┐
│   auth.users    │◄───────│  auth.api_keys   │
└────────┬────────┘   N:1  └──────────────────┘

         │ 1:N

┌─────────────────┐        ┌──────────────────┐
│   public.users  │◄───────│   sessions       │
└────────┬────────┘   1:N  └──────────────────┘

         │ 1:N

┌──────────────────────────┐
│   task_executions        │
│  (workflow_id = PK)      │
└──────────┬───────────────┘

           │ 1:N

┌──────────────────────────┐        ┌──────────────────┐
│   agent_executions       │◄───────│  tool_executions │
└──────────┬───────────────┘   1:N  └──────────────────┘

           │ 1:N

┌──────────────────────────┐
│     tool_calls           │
└──────────────────────────┘

┌──────────────────────────┐        ┌──────────────────┐
│   event_logs             │        │  token_usage     │
│  (workflow_id)           │        └──────────────────┘
└──────────────────────────┘

┌──────────────────────────┐        ┌───────────────────┐
│  usage_daily_aggregates  │        │  learning_cases   │
└──────────────────────────┘        └───────────────────┘

Schema: auth (Authentication & Multi-Tenancy)

auth.tenants

Purpose: Multi-tenant organization management
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT gen_random_uuid()Tenant identifier
nameVARCHAR(255)NOT NULLOrganization name
slugVARCHAR(100)UNIQUE, NOT NULLURL-safe identifier
planVARCHAR(50)DEFAULT ‘free’Subscription plan (free, pro, enterprise)
token_limitINTEGERDEFAULT 10000Monthly token allowance
monthly_token_usageINTEGERDEFAULT 0Current month usage
rate_limit_per_hourINTEGERDEFAULT 1000API rate limit
is_activeBOOLEANDEFAULT trueTenant status
created_atTIMESTAMPDEFAULT NOW()Creation time
updated_atTIMESTAMPDEFAULT NOW()Last update time
metadataJSONBDEFAULT ''Additional tenant data
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Example:
-- Get all active tenants
SELECT id, name, plan, token_limit
FROM auth.tenants
WHERE is_active = true;

-- Check token usage
SELECT name, monthly_token_usage, token_limit,
       (monthly_token_usage::FLOAT / token_limit * 100) as usage_percentage
FROM auth.tenants
WHERE monthly_token_usage > token_limit * 0.8;

auth.users

Purpose: User authentication and profile management
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT gen_random_uuid()User identifier
emailVARCHAR(255)UNIQUE, NOT NULLUser email
usernameVARCHAR(100)UNIQUE, NOT NULLUsername
password_hashVARCHAR(255)NOT NULLBcrypt password hash
full_nameVARCHAR(255)Full name
tenant_idUUIDFK → auth.tenants, NOT NULLOrganization
roleVARCHAR(50)DEFAULT ‘user’Role (user, admin, owner)
is_activeBOOLEANDEFAULT trueAccount status
is_verifiedBOOLEANDEFAULT falseEmail verification
email_verified_atTIMESTAMPVerification timestamp
created_atTIMESTAMPDEFAULT NOW()Account creation
updated_atTIMESTAMPDEFAULT NOW()Last update
last_loginTIMESTAMPLast login time
metadataJSONBDEFAULT ''Additional user data
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Example:
-- Get all users in a tenant
SELECT u.email, u.username, u.role, u.last_login
FROM auth.users u
WHERE u.tenant_id = '...'
  AND u.is_active = true
ORDER BY u.last_login DESC NULLS LAST;

-- Find inactive users
SELECT email, created_at, last_login
FROM auth.users
WHERE last_login < NOW() - INTERVAL '90 days'
   OR last_login IS NULL;

auth.api_keys

Purpose: API key management for programmatic access
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT gen_random_uuid()Key identifier
key_hashVARCHAR(255)UNIQUE, NOT NULLSHA256 hash of key
key_prefixVARCHAR(20)NOT NULLFirst 8 chars (for display)
user_idUUIDFK → auth.usersKey owner
tenant_idUUIDFK → auth.tenants, NOT NULLOrganization
nameVARCHAR(100)NOT NULLKey name/description
descriptionTEXTDetailed description
scopesTEXT[]DEFAULT […]Permissions array
rate_limit_per_hourINTEGERDEFAULT 1000Key-specific rate limit
last_usedTIMESTAMPLast usage timestamp
expires_atTIMESTAMPExpiration time
is_activeBOOLEANDEFAULT trueKey status
created_atTIMESTAMPDEFAULT NOW()Creation time
metadataJSONBDEFAULT ''Additional key data
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Default Scopes:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Example:
-- List active API keys
SELECT key_prefix, name, last_used, expires_at
FROM auth.api_keys
WHERE is_active = true
  AND (expires_at IS NULL OR expires_at > NOW())
ORDER BY last_used DESC NULLS LAST;

-- Find unused keys
SELECT key_prefix, name, created_at, last_used
FROM auth.api_keys
WHERE last_used IS NULL
  AND created_at < NOW() - INTERVAL '30 days';

-- Check expiring keys
SELECT key_prefix, name, expires_at
FROM auth.api_keys
WHERE expires_at BETWEEN NOW() AND NOW() + INTERVAL '7 days'
  AND is_active = true;

auth.refresh_tokens

Purpose: JWT refresh token storage and revocation
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT gen_random_uuid()Token identifier
token_hashVARCHAR(255)UNIQUE, NOT NULLSHA256 hash
user_idUUIDFK → auth.usersToken owner
tenant_idUUIDFK → auth.tenants, NOT NULLOrganization
expires_atTIMESTAMPNOT NULLExpiration time
revokedBOOLEANDEFAULT falseRevocation status
revoked_atTIMESTAMPRevocation time
ip_addressINETClient IP
user_agentTEXTClient user agent
created_atTIMESTAMPDEFAULT NOW()Creation time
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Example:
-- Revoke all tokens for a user
UPDATE auth.refresh_tokens
SET revoked = true, revoked_at = NOW()
WHERE user_id = '...'
  AND revoked = false;

-- Clean up expired tokens
DELETE FROM auth.refresh_tokens
WHERE expires_at < NOW() - INTERVAL '7 days';

auth.audit_logs

Purpose: Security event audit trail
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT gen_random_uuid()Log entry ID
event_typeVARCHAR(100)NOT NULLEvent type
user_idUUIDFK → auth.usersUser (nullable)
tenant_idUUIDFK → auth.tenantsTenant (nullable)
ip_addressINETClient IP
user_agentTEXTClient user agent
detailsJSONBDEFAULT ''Event details
created_atTIMESTAMPDEFAULT NOW()Event time
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Event Types:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Example:
-- Recent security events
SELECT event_type, user_id, ip_address, created_at
FROM auth.audit_logs
WHERE created_at > NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC
LIMIT 100;

-- Failed login attempts
SELECT user_id, ip_address, COUNT(*) as attempts
FROM auth.audit_logs
WHERE event_type = 'login_failed'
  AND created_at > NOW() - INTERVAL '1 hour'
GROUP BY user_id, ip_address
HAVING COUNT(*) > 5;

Schema: public (Core Application)

users

Purpose: Application user profiles (legacy, linked to auth.users)
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()User identifier
external_idVARCHAR(255)UNIQUE, NOT NULLExternal system ID
emailVARCHAR(255)Email address
tenant_idUUIDTenant reference
metadataJSONBDEFAULT ''User metadata
created_atTIMESTAMPTZDEFAULT NOW()Creation time
updated_atTIMESTAMPTZDEFAULT NOW()Last update
Indexes:
  • idx_users_tenant_id ON (tenant_id)
  • idx_users_external_id ON (external_id)
Note: This table exists for backward compatibility. New code should use auth.users.

sessions

Purpose: User session management and context
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()Session identifier
user_idUUIDFK → usersSession owner
tenant_idUUIDTenant reference
contextJSONBDEFAULT ''Session context data
token_budgetINTEGERDEFAULT 10000Token allocation
tokens_usedINTEGERDEFAULT 0Tokens consumed
created_atTIMESTAMPTZDEFAULT NOW()Session start
updated_atTIMESTAMPTZDEFAULT NOW()Last activity
expires_atTIMESTAMPTZExpiration time
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
External IDs (non-UUID session IDs) are stored in context->>'external_id' to enable dual-ID lookups. Example:
-- Active sessions
SELECT id, user_id, tokens_used, token_budget, created_at
FROM sessions
WHERE expires_at > NOW()
  OR expires_at IS NULL
ORDER BY created_at DESC;

-- Session token usage
SELECT
    COUNT(*) as session_count,
    AVG(tokens_used) as avg_tokens,
    SUM(tokens_used) as total_tokens
FROM sessions
WHERE created_at > NOW() - INTERVAL '24 hours';

task_executions

Purpose: Task/workflow execution history and metrics
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()Task identifier
workflow_idVARCHAR(255)UNIQUE, NOT NULLTemporal workflow ID
user_idUUIDFK → usersTask creator
tenant_idUUIDTenant reference
session_idVARCHAR(255)Session identifier
queryTEXTNOT NULLTask query/prompt
modeVARCHAR(50)Execution mode (SIMPLE, STANDARD, COMPLEX)
statusVARCHAR(50)NOT NULLTask status
started_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Start time
completed_atTIMESTAMPTZCompletion time
resultTEXTFinal result
responseJSONBDEFAULT ''Structured response
error_messageTEXTError details
total_tokensINTEGERDEFAULT 0Total tokens used
prompt_tokensINTEGERDEFAULT 0Input tokens
completion_tokensINTEGERDEFAULT 0Output tokens
total_cost_usdDECIMAL(10,6)DEFAULT 0Total cost
duration_msINTEGERExecution time (ms)
agents_usedINTEGERDEFAULT 0Number of agents
tools_invokedINTEGERDEFAULT 0Number of tool calls
cache_hitsINTEGERDEFAULT 0Cache hit count
complexity_scoreDECIMAL(3,2)Complexity (0.0-1.0)
metadataJSONBAdditional metadata
created_atTIMESTAMPTZDEFAULT NOW()Record creation
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Status Values:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Example:
-- Recent tasks for user
SELECT workflow_id, query, status, duration_ms, total_cost_usd
FROM task_executions
WHERE user_id = '...'
ORDER BY started_at DESC
LIMIT 20;

-- Failed tasks analysis
SELECT
    DATE(started_at) as date,
    COUNT(*) as failed_count,
    AVG(duration_ms) as avg_duration
FROM task_executions
WHERE status = 'FAILED'
  AND started_at > NOW() - INTERVAL '7 days'
GROUP BY DATE(started_at)
ORDER BY date DESC;

-- Cost analysis
SELECT
    user_id,
    COUNT(*) as task_count,
    SUM(total_cost_usd) as total_cost,
    AVG(total_cost_usd) as avg_cost,
    SUM(total_tokens) as total_tokens
FROM task_executions
WHERE started_at > NOW() - INTERVAL '30 days'
GROUP BY user_id
ORDER BY total_cost DESC;

agent_executions

Purpose: Individual agent execution details within tasks
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()Execution ID
task_execution_idUUIDFK → task_executionsParent task
agent_idVARCHAR(255)NOT NULLAgent identifier
execution_orderINTEGERNOT NULLExecution sequence
inputTEXTNOT NULLAgent input
outputTEXTAgent output
modeVARCHAR(50)Execution mode
stateVARCHAR(50)FSM state
tokens_usedINTEGERDEFAULT 0Tokens consumed
cost_usdDECIMAL(10,6)DEFAULT 0Execution cost
model_usedVARCHAR(100)LLM model
duration_msINTEGERExecution time
memory_used_mbINTEGERMemory usage
created_atTIMESTAMPTZDEFAULT NOW()Start time
completed_atTIMESTAMPTZCompletion time
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
State Values:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Example:
-- Agent execution chain for a task
SELECT agent_id, execution_order, state, duration_ms, tokens_used
FROM agent_executions
WHERE task_execution_id = '...'
ORDER BY execution_order;

-- Agent performance metrics
SELECT
    agent_id,
    COUNT(*) as execution_count,
    AVG(duration_ms) as avg_duration,
    AVG(tokens_used) as avg_tokens,
    SUM(cost_usd) as total_cost
FROM agent_executions
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY agent_id
ORDER BY execution_count DESC;

tool_executions

Purpose: Tool invocation history and performance
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()Execution ID
agent_execution_idUUIDFK → agent_executionsParent agent
task_execution_idUUIDFK → task_executionsParent task
tool_nameVARCHAR(255)NOT NULLTool identifier
tool_versionVARCHAR(50)Tool version
categoryVARCHAR(100)Tool category
input_paramsJSONBInput parameters
outputJSONBTool output
successBOOLEANDEFAULT trueSuccess status
error_messageTEXTError details
duration_msINTEGERExecution time
tokens_consumedINTEGERDEFAULT 0Tokens used
sandboxedBOOLEANDEFAULT trueWASI sandbox used
memory_used_mbINTEGERMemory usage
executed_atTIMESTAMPTZDEFAULT NOW()Execution time
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Tool Categories:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Example:
-- Tool usage statistics
SELECT
    tool_name,
    COUNT(*) as invocation_count,
    COUNT(CASE WHEN success THEN 1 END) as successful,
    AVG(duration_ms) as avg_duration
FROM tool_executions
WHERE executed_at > NOW() - INTERVAL '7 days'
GROUP BY tool_name
ORDER BY invocation_count DESC;

-- Failed tool executions
SELECT tool_name, error_message, executed_at
FROM tool_executions
WHERE success = false
  AND executed_at > NOW() - INTERVAL '24 hours'
ORDER BY executed_at DESC;

-- Tool performance
SELECT
    tool_name,
    AVG(duration_ms) as avg_ms,
    MAX(duration_ms) as max_ms,
    STDDEV(duration_ms) as stddev_ms
FROM tool_executions
WHERE success = true
GROUP BY tool_name
ORDER BY avg_ms DESC;

event_logs

Purpose: Streaming event storage for audit and replay
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()Event ID
workflow_idVARCHAR(255)NOT NULLWorkflow identifier
task_idUUIDTask reference (nullable)
typeVARCHAR(100)NOT NULLEvent type
agent_idVARCHAR(255)Agent identifier
messageTEXTEvent message
payloadJSONBDEFAULT ''Event payload
timestampTIMESTAMPTZNOT NULL, DEFAULT NOW()Event time
seqBIGINTSequence number
stream_idVARCHAR(64)Redis stream ID
created_atTIMESTAMPTZNOT NULL, DEFAULT NOW()Record creation
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Event Types:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Example:
-- Get all events for a task
SELECT type, agent_id, message, timestamp
FROM event_logs
WHERE workflow_id = '...'
ORDER BY timestamp;

-- Event frequency analysis
SELECT
    type,
    COUNT(*) as event_count,
    DATE_TRUNC('hour', timestamp) as hour
FROM event_logs
WHERE timestamp > NOW() - INTERVAL '24 hours'
GROUP BY type, hour
ORDER BY hour DESC, event_count DESC;

token_usage

Purpose: Detailed token usage tracking per task
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()Record ID
user_idUUIDFK → usersUser
task_idUUIDFK → task_executionsTask
providerVARCHAR(50)NOT NULLLLM provider
modelVARCHAR(255)NOT NULLModel name
prompt_tokensINTEGERNOT NULLInput tokens
completion_tokensINTEGERNOT NULLOutput tokens
total_tokensINTEGERNOT NULLTotal tokens
cost_usdDECIMAL(10,6)NOT NULLCost in USD
created_atTIMESTAMPTZDEFAULT NOW()Record time
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Example:
-- Token usage by provider
SELECT
    provider,
    model,
    COUNT(*) as call_count,
    SUM(total_tokens) as total_tokens,
    SUM(cost_usd) as total_cost
FROM token_usage
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY provider, model
ORDER BY total_cost DESC;

-- Daily token trend
SELECT
    DATE(created_at) as date,
    SUM(total_tokens) as tokens,
    SUM(cost_usd) as cost
FROM token_usage
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date;

usage_daily_aggregates

Purpose: Pre-computed daily usage statistics
ColumnTypeConstraintsDescription
idUUIDPK, DEFAULT uuid_generate_v4()Record ID
user_idUUIDFK → usersUser
dateDATENOT NULLAggregation date
total_tasksINTEGERDEFAULT 0Total task count
successful_tasksINTEGERDEFAULT 0Successful count
failed_tasksINTEGERDEFAULT 0Failed count
total_tokensINTEGERDEFAULT 0Total tokens
total_cost_usdDECIMAL(10,6)DEFAULT 0Total cost
model_usageJSONBModel distribution
tools_invokedINTEGERDEFAULT 0Tool invocation count
tool_distributionJSONBTool usage distribution
avg_duration_msINTEGERAverage task duration
cache_hit_rateDECIMAL(3,2)Cache hit percentage
created_atTIMESTAMPTZDEFAULT NOW()Record time
Indexes:
  • idx_sessions_user_id ON (user_id)
  • idx_sessions_tenant_id ON (tenant_id)
  • idx_sessions_expires_at ON (expires_at)
  • idx_sessions_external_id ON ((context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL
  • idx_sessions_user_external_id UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • idx_sessions_not_deleted ON (id) WHERE deleted_at IS NULL
Unique Constraint: (user_id, date) Example:
-- User usage summary
SELECT date, total_tasks, successful_tasks, total_cost_usd
FROM usage_daily_aggregates
WHERE user_id = '...'
  AND date > CURRENT_DATE - INTERVAL '30 days'
ORDER BY date DESC;

-- Aggregate metrics
SELECT
    SUM(total_tasks) as total_tasks,
    SUM(total_cost_usd) as total_cost,
    AVG(cache_hit_rate) as avg_cache_rate
FROM usage_daily_aggregates
WHERE date > CURRENT_DATE - INTERVAL '7 days';

Additional Tables

tool_calls

Purpose: Legacy tool call tracking (use tool_executions instead)

prompts

Purpose: Prompt versioning and A/B testing

learning_cases

Purpose: Reinforcement learning case storage

session_archives

Purpose: Long-term session snapshots from Redis

audit_logs (public)

Purpose: Application audit trail (separate from auth.audit_logs)

Database Functions

update_updated_at_column()

Purpose: Automatically update updated_at on row changes Usage: Attached as trigger to users and sessions tables
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();

update_daily_aggregate(user_id, date)

Purpose: Update or create daily usage aggregate for a user Usage: Called automatically via trigger on task completion
-- Manual call
SELECT update_daily_aggregate(
    '00000000-0000-0000-0000-000000000002',
    '2025-10-22'
);

trigger_update_daily_aggregate()

Purpose: Trigger function to update aggregates on task status changes Usage: Automatically fires on task_executions INSERT/UPDATE

Sample Queries

Task Analytics

-- Task success rate by mode
SELECT
    mode,
    COUNT(*) as total,
    COUNT(CASE WHEN status = 'COMPLETED' THEN 1 END) as completed,
    ROUND(100.0 * COUNT(CASE WHEN status = 'COMPLETED' THEN 1 END) / COUNT(*), 2) as success_rate
FROM task_executions
WHERE started_at > NOW() - INTERVAL '7 days'
GROUP BY mode;

-- Average execution time by complexity
SELECT
    CASE
        WHEN complexity_score < 0.3 THEN 'Low'
        WHEN complexity_score < 0.7 THEN 'Medium'
        ELSE 'High'
    END as complexity,
    COUNT(*) as task_count,
    AVG(duration_ms) as avg_ms,
    AVG(total_cost_usd) as avg_cost
FROM task_executions
WHERE complexity_score IS NOT NULL
GROUP BY complexity;

Cost Analysis

-- Top cost users
SELECT
    u.email,
    COUNT(t.id) as task_count,
    SUM(t.total_cost_usd) as total_cost,
    AVG(t.total_cost_usd) as avg_cost
FROM task_executions t
JOIN users u ON t.user_id = u.id
WHERE t.started_at > NOW() - INTERVAL '30 days'
GROUP BY u.email
ORDER BY total_cost DESC
LIMIT 10;

-- Cost breakdown by model
SELECT
    tu.provider,
    tu.model,
    COUNT(*) as usage_count,
    SUM(tu.total_tokens) as total_tokens,
    SUM(tu.cost_usd) as total_cost,
    AVG(tu.cost_usd) as avg_cost_per_call
FROM token_usage tu
WHERE tu.created_at > NOW() - INTERVAL '7 days'
GROUP BY tu.provider, tu.model
ORDER BY total_cost DESC;

Performance Monitoring

-- Slow queries (tasks > 60 seconds)
SELECT
    workflow_id,
    query,
    duration_ms,
    agents_used,
    tools_invoked
FROM task_executions
WHERE duration_ms > 60000
  AND started_at > NOW() - INTERVAL '24 hours'
ORDER BY duration_ms DESC;

-- Tool execution success rate
SELECT
    tool_name,
    COUNT(*) as total,
    COUNT(CASE WHEN success THEN 1 END) as successful,
    ROUND(100.0 * COUNT(CASE WHEN success THEN 1 END) / COUNT(*), 2) as success_rate,
    AVG(duration_ms) as avg_duration
FROM tool_executions
GROUP BY tool_name
ORDER BY total DESC;

Best Practices

1. Indexing

Always use indexes for:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Current index coverage: Excellent (all foreign keys and common queries indexed)

2. Partitioning

For high-volume tables, consider partitioning:
-- Monthly partitioning for task_executions
CREATE TABLE task_executions_2025_10 PARTITION OF task_executions
    FOR VALUES FROM ('2025-10-01') TO ('2025-11-01');

3. Data Retention

Recommended policies:
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
  • UNIQUE ON (user_id, (context->>‘external_id’)) WHERE context->>‘external_id’ IS NOT NULL AND deleted_at IS NULL
Cleanup script:
-- Delete old events (run monthly)
DELETE FROM event_logs
WHERE created_at < NOW() - INTERVAL '90 days';

-- Archive old tasks (run annually)
INSERT INTO task_executions_archive
SELECT * FROM task_executions
WHERE started_at < NOW() - INTERVAL '1 year';

DELETE FROM task_executions
WHERE started_at < NOW() - INTERVAL '1 year';

4. Query Optimization

Use EXPLAIN ANALYZE:
EXPLAIN ANALYZE
SELECT * FROM task_executions
WHERE user_id = '...'
  AND started_at > NOW() - INTERVAL '7 days';
Optimize with covering indexes:
CREATE INDEX idx_task_user_date_status
ON task_executions(user_id, started_at, status)
INCLUDE (total_cost_usd, duration_ms);

5. Connection Pooling

Configuration (already configured in Shannon):
DB_MAX_OPEN_CONNS=50
DB_MAX_IDLE_CONNS=10

Maintenance

Vacuum and Analyze

-- Manual vacuum (run weekly)
VACUUM ANALYZE task_executions;
VACUUM ANALYZE event_logs;

-- Configure autovacuum
ALTER TABLE task_executions SET (autovacuum_vacuum_threshold = 1000);

Statistics

-- Update statistics
ANALYZE task_executions;

-- Check table bloat
SELECT
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;