Appearance
Data Relationships
See also: Identity and Access, Service Accounts, Model Routing and API Behavior, Provider API Compatibility, Budgets and Spending, Observability and Request Logs, Request Logs, MCP Invocations, MCP Registry and Discovery, ADR: Team Service Accounts for Non-Human Gateway Access, ADR: Identity Foundation for Users, Teams, and API Key Ownership, ADR: Route-Level Provider API Compatibility Profiles, ADR: MCP Tool Cardinality Observability, ADR: External MCP Registry and Discovery Boundary
This document is schema-oriented. It describes the persistent relationships that are hard to infer from a single file, but it does not try to restate every runtime rule owned by neighboring docs.
Source of Truth
- Migrations:
- Core types:
- Runtime behavior:
Core Entity Graph
teams0..Nteam_membershipsusers0..1team_membershipsteams0..Nservice_accountsapi_keysbelongs to exactly one principal owner: user or service accountapi_keysN..Ngateway_modelsthroughapi_key_model_grants- Optional restriction overlays:
user_model_allowlistteam_model_allowlist
budgetsstores user, service-account, and user-model budgets with one active row per canonical scope keyusage_cost_eventsrecords request ownership, model attribution, pricing status, and computed costrequest_logsrecords the final user-visible request outcomerequest_log_payloadsstores sanitized request and response bodies separately from the summary rowrequest_log_attemptsstores ordered upstream provider execution attempts for a request logmcp_tool_invocationsstores individual tool-call audit rows correlated byrequest_id- Request-log purge removes old request-log parents and their payload, tag, and attempt children without touching spend ledger rows
pricing_catalog_cachestores normalized pricing snapshots used by runtime pricing resolutionmodel_pricingstores effective-dated pricing rows used for historical chargingusage_cost_event_duplicates_archivepreserves duplicate-ledger migration/archive contextexternal_mcp_serversstores user-added MCP server registry rows and soft-disable stateexternal_mcp_toolsstores discovered MCP tools, stable tool ids, schema hashes, schema versions, and active/inactive stateexternal_mcp_discovery_runsstores immutable discovery attempt diagnostics
Table Catalog
Foundation Tables
providers: upstream provider config and secret referencesgateway_models: gateway model registry; rows can be provider-backed or alias-backedmodel_routes: execution targets for provider-backed models onlyapi_key_model_grants: model grants attached to an API keyaudit_logs: control-plane audit baseline
model_routes stores two distinct route execution metadata documents:
capabilities_jsoncontrols whether the route may execute a requestcompatibility_jsoncontrols declared provider API compatibility transforms after route selection
capabilities_json includes API-family gates such as chat_completions, responses, and embeddings.
Compatibility metadata is not a provider config fallback and is not an extra_body convention.
Identity and Access Tables
teams- Key columns:
team_id,team_key,status,model_access_mode - Notes:
team_keyis the durable identifier;model_access_modeisall|restricted
- Key columns:
users- Key columns:
user_id,email,global_role,auth_mode,request_logging_enabled,model_access_mode - Notes: case-insensitive uniqueness is enforced through
email_normalized
- Key columns:
team_memberships- Key columns:
team_id,user_id,role - Notes: one-team-per-user is enforced by a unique
user_id
- Key columns:
service_accounts- Key columns:
service_account_id,team_id,name,status,created_by_user_id,deactivated_at - Notes: service accounts are team-owned non-human principals; deletion is deactivation
- Key columns:
oidc_providers- Key columns:
oidc_provider_id,provider_key,provider_type,issuer_url,client_id,enabled - Notes: the current schema supports
okta|generic_oidc
- Key columns:
user_password_auth- Key columns:
user_id,password_hash,password_updated_at
- Key columns:
user_oidc_auth- Key columns:
user_id,oidc_provider_id,subject,email_claim - Notes: unique
(oidc_provider_id, subject)
- Key columns:
user_oauth_auth- Key columns:
user_id,oauth_provider,subject
- Key columns:
user_oidc_links- Purpose: pre-provisioned relationship between a user and the OIDC provider they are allowed to activate against
Authorization Overlay Tables
user_model_allowlist- Relationship:
user_id+model_id
- Relationship:
team_model_allowlist- Relationship:
team_id+model_id
- Relationship:
Ownership, Accounting, and Logging Tables
api_keys- Key columns:
id,public_id,secret_hash,owner_kind,owner_user_id,owner_service_account_id - Constraint: exactly one owner column must be set consistently with
owner_kind - Notes: direct team-owned runtime keys and
system-legacyownership are not supported
- Key columns:
budgets- Key columns:
budget_id,scope_kind,scope_key,user_id,service_account_id,model_id,upstream_model,cadence,amount_10000,hard_limit,timezone,is_active - Constraint: one active budget per canonical
scope_key - Notes: supported scope kinds are
user,service_account, anduser_model; teams are not budget principals
- Key columns:
usage_cost_events- Key columns:
usage_event_id,request_id,ownership_scope_key,api_key_id,user_id,team_id,actor_user_id,model_id,provider_key,upstream_model,pricing_status,unpriced_reason,pricing_row_id,pricing_provider_id,computed_cost_10000,provider_usage,occurred_at - Notes: this is the canonical spend ledger used for enforcement and reporting
- Key columns:
request_logs- Key columns:
request_log_id,request_id,api_key_id,user_id,team_id,model_key,resolved_model_key,provider_key,caller_service,caller_component,caller_env,status_code,referenced_mcp_server_count,exposed_tool_count,invoked_tool_count,filtered_tool_count,user_agent_raw,agent_harness_key,agent_harness_label,metadata_json,occurred_at - Notes: one summary row per final request outcome;
metadata_json.payload_policyrecords the capture mode and limits used for the row when request logging is enabled; tool-cardinality columns are nullable typed facts, with historical or not-yet-observable dimensions leftnull; agent harness usage groups byagent_harness_keywhile preserving bounded rawUser-Agentdetail evidence
- Key columns:
request_log_payloads- Key columns:
request_log_id,request_json,response_json - Notes: summary and payload are intentionally split; rows exist only when the payload policy captures redacted payloads
- Key columns:
request_log_tags- Key columns:
request_log_id,tag_key,tag_value - Notes: bounded bespoke caller tags for request-log filtering and attribution
- Key columns:
request_log_attempts- Key columns:
request_attempt_id,request_log_id,request_id,attempt_number,route_id,provider_key,upstream_model,status,retryable,terminal,produced_final_response,stream,started_at,completed_at,latency_ms - Notes: attempt rows are metadata-only children of
request_logs; they are ordered byattempt_numberand describe provider execution, not pre-provider gateway rejections
- Key columns:
mcp_tool_invocations- Key columns:
mcp_tool_invocation_id,request_log_id,request_id,api_key_id,user_id,team_id,owner_kind,server_id,server_display_key,tool_id,tool_display_key,status,policy_result,latency_ms,error_code,has_payload,arguments_payload_truncated,result_payload_truncated,arguments_payload_redacted,result_payload_redacted,metadata_json,occurred_at - Notes: MCP invocation rows are correlated by
request_id;request_log_idis nullable and non-owning because request-log summaries are written at final outcome and may be absent or purged independently. Server/tool stable IDs are nullable until registry records exist, but display keys are required.
- Key columns:
mcp_tool_invocation_payloads- Key columns:
mcp_tool_invocation_id,arguments_json,result_json - Notes: payload rows exist only when MCP invocation payload policy captures sanitized payloads; summary rows are still recorded when payload capture is disabled.
- Key columns:
External MCP Registry Tables
external_mcp_servers- Key columns:
mcp_server_id,server_key,display_name,transport,server_url,auth_mode,auth_config_json,timeout_ms,status,last_discovery_status,last_discovery_at,last_successful_discovery_at,last_error_summary,last_tool_count,created_at,updated_at,disabled_at - Notes: rows are user-added registry records. Recommended catalog entries are not seeded into this table. Disabling a server is the archive/delete path; hard delete is not exposed.
- Key columns:
external_mcp_tools- Key columns:
mcp_tool_id,mcp_server_id,upstream_name,display_name,description,input_schema_json,schema_hash,schema_version,is_active,first_discovered_at,last_discovered_at,deactivated_at - Notes:
(mcp_server_id, upstream_name)is unique. Rediscovery preservesmcp_tool_idfor stable upstream names and incrementsschema_versiononly whenschema_hashchanges.
- Key columns:
external_mcp_discovery_runs- Key columns:
discovery_run_id,mcp_server_id,status,started_at,finished_at,discovered_tool_count,active_tool_count,schema_set_hash,error_summary,details_json - Notes: diagnostics are bounded and must not contain raw tokens or user credentials.
- Key columns:
Request-log purge treats request_logs as the parent retention boundary. Purging a parent row also removes matching request_log_payloads, request_log_tags, and request_log_attempts rows. The purge does not remove usage_cost_events; spend reporting and budget enforcement stay tied to the ledger.
Pricing Catalog Cache
pricing_catalog_cache- Key columns:
catalog_key,source,etag,fetched_at,snapshot_json - Notes: runtime uses the cached snapshot together with the vendored fallback in the repo
- Key columns:
model_pricing- Key columns:
model_pricing_id,pricing_provider_id,pricing_model_id,effective_start_at,effective_end_at - Notes: effective-dated pricing rows are the durable historical charging source
- Key columns:
usage_cost_event_duplicates_archive- Purpose: preserves duplicate-ledger rows during pricing/ledger migration cleanup and audit backfill flows
Authorization Semantics
Effective model access is the intersection of:
- API key grants from
api_key_model_grants - Team allowlist, only when
teams.model_access_mode='restricted' - User allowlist, only when
users.model_access_mode='restricted'
If neither the team nor the user is restricted, grants remain unchanged.
Service-account credentials use API-key grants plus the owning team's allowlist. User allowlists do not apply to service accounts.
Budget and Pricing Notes
- Pricing lookup comes from the internal pricing catalog layer, not from provider
/v1/modelsresponses - Supported pricing source ids in this slice are
openai,google-vertex, andgoogle-vertex-anthropic openai_compatproviders must declarepricing_provider_idgcp_vertexderives pricing source from theupstream_modelpublisher prefix- Pricing is exact-only in this slice; unsupported billing modifiers, unsupported publisher/location combinations, and unknown model ids resolve as
unpriced - Chargeable requests write usage ledger rows and participate in budget enforcement
- Unpriced requests are not charged and must not be budget-blocked
- Supported budget cadence values are
daily|weekly|monthly - Current UTC window semantics:
- Daily windows start at
00:00:00 UTC - Weekly windows start at
Monday 00:00:00 UTC - Monthly windows start at the first day of the month at
00:00:00 UTC Sunday 23:59:59 UTCremains in the previous weekly window
- Daily windows start at
- Budget threshold alerts persist audit rows plus per-recipient delivery rows and currently deliver owner-only email alerts when remaining budget crosses to
20%or less - Service-account budget alerts are delivered to active owners and admins of the owning team
- Service-account spend remains attributable to the service account and its owning team
Requested vs Resolved Model Identity
gateway_modelscan either point directly to provider routes or alias another modelrequest_logs.model_keystores the requested gateway modelrequest_logs.resolved_model_keystores the canonical execution model after alias resolution
This distinction matters for admin-facing observability and historical debugging. See model-routing-and-api-behavior.md.
Route Viability Note
Schema alone does not determine whether a model can execute.
Operational viability also depends on:
- provider existence
- route
enabledstate - positive route weights
- capability filtering
Those rules are owned by configuration-reference.md and model-routing-and-api-behavior.md.
Ownership Notes
- User-owned and service-account-owned API keys share the same
api_keystable - Service-account usage and request logs exist without an acting user
- Direct team-owned runtime API keys are removed from the schema contract
- There is no reserved
system-legacyownership path
That ownership model is explained operationally in identity-and-access.md, service-accounts.md, and budgets-and-spending.md.
PostgreSQL and libsql Parity
Both runtime backends are expected to stay logically aligned for:
- schema shape
- migrations
- seed behavior
- aliases and request-log model identity
- spend ledger behavior
- request-log summary, payload, tag, and attempt persistence
See ../crates/gateway-store/README.md for the storage-layer overview.
