Data Model & ERD — customer-app (SaaS control plane)¶
Read in your language: English · Português · Español
Referencia de schema del estado actual (audit-v6 DOCS-ARCH — antes, la única narrativa de schema en ARCHITECTURE.md se detenía en la migration 0023). La fuente de verdad es el schema Drizzle
pkg/octopus/customer-app/src/lib/db/schema.tsy las 48 migrations enpkg/octopus/customer-app/src/lib/db/migrations/. El dashboard (cockpit on-prem) tiene su propio schema endashboard/src/db/schema.ts.
1. Dominios¶
Las ~52 tablas del customer-app se agrupan en ocho dominios:
| Dominio | Tablas centrales |
|---|---|
| Tenancy / cuentas | customer_accounts, customer_users, customer_onboarding_profiles, mssp_accounts, mssp_quarterly_volume |
| Auth / sesión | customer_sessions, customer_refresh_tokens, verification_tokens, auth_events, admin_sessions, edu_verifications |
| Token economy (ledger UTXO) | utxos, utxo_mint_events, utxo_spend_events, tsu_budgets, auto_refill_settings, token_transfer_requests |
| Uso & metering | usage_tickets, usage_events, usage_report_nonces, license_heartbeats, licenses |
| Billing (Stripe) | stripe_webhook_events, identity_webhook_events, promo_codes, promo_redemptions, rate_cards, pricing_templates |
| Soporte | customer_support_tickets, customer_support_ticket_messages, customer_support_ticket_attachments, support_saved_replies |
| Compliance / auditoría | admin_audit_events (hash-chain WORM), customer_audit_forward_config, account_enforcement, deployment_anomalies, customer_access_denylist |
| Riesgo / anti-fraude | risk_ip_reputation, risk_user_locations, email_blocklist, rate_limit_buckets, leads, lead_activities |
2. ERD central (tenancy + token economy + billing)¶
erDiagram
customer_accounts ||--o{ customer_users : "has"
customer_accounts ||--o| customer_onboarding_profiles : "onboarding"
customer_accounts ||--o{ customer_sessions : "sessions"
customer_accounts ||--o{ utxos : "owns (RLS)"
customer_accounts ||--o{ utxo_mint_events : "mints"
customer_accounts ||--o{ utxo_spend_events : "spends"
customer_accounts ||--o| tsu_budgets : "budget"
customer_accounts ||--o{ usage_tickets : "meters"
customer_accounts ||--o{ api_keys : "keys"
customer_accounts ||--o{ licenses : "on-prem license"
customer_accounts ||--o{ customer_support_tickets : "tickets"
mssp_accounts ||--o{ customer_accounts : "parent-of (sub-accounts)"
customer_users ||--o{ auth_events : "audit"
utxos ||--o{ utxo_spend_events : "consumed-by"
customer_support_tickets ||--o{ customer_support_ticket_messages : "thread"
customer_support_ticket_messages ||--o{ customer_support_ticket_attachments : "files"
stripe_webhook_events }o--|| customer_accounts : "billing events"
identity_webhook_events }o--|| customer_accounts : "KYC events"
3. Diccionario de datos — tablas load-bearing¶
| Tabla | Columnas clave | Notas |
|---|---|---|
customer_accounts |
id (uuid pk), deployment_id, cell_id, tier, status, kyc_status, country_code, referral_code |
Raíz del tenant. status controla el ciclo de vida (email_pending → kyc_pending → active). Ancla de RLS (account_id en las tablas de tenant). |
utxos |
id, account_id (fk), amount, spent, created_at |
Ledger de unspent-outputs de tokens (ADR-0099). Aplicado con RLS; solo eventos de mint/spend mutan. |
utxo_spend_events |
id, account_id, amount, test_id, intensity, spent_at, refunded_at, refund_reason |
Log de spend inmutable; los refunds se registran, nunca se borran. |
admin_audit_events |
id, seq, prev_hash, hash, actor, action, payload, created_at |
WORM: un trigger en el DB bloquea UPDATE + DELETE temprano; hash chain SHA-256; export RFC 5424. |
stripe_webhook_events |
id (stripe evt id, pk), event_type, outcome, stripe_created_at, livemode |
Idempotencia + retry: un outcome no terminal (NULL/failed) se reprocesa. |
identity_webhook_events |
event_id (pk), event_type, account_id, session_id, outcome |
Mismo contrato de idempotencia/retry para Stripe Identity (KYC). |
licenses |
id, account_id, deployment_id, tier, expires_at, signed_blob |
Licencia on-prem; los heartbeats caen en license_heartbeats. |
4. Row-Level Security¶
Las tablas con scope de tenant (utxos, utxo_mint_events, utxo_spend_events,
usage_tickets, usage_events, api_keys, webhook_endpoints,
outbound_events, tsu_budgets) llevan una política RLS tenant_isolation
(migrations 0026/0029/0034/0041). Las rutas de tenant corren dentro de withTenant() (setea
app.account_id); los caminos cross-tenant de sistema (metrics, crons, admin) lo dejan
sin setear y pasan — ver el guard test de rutas de tenant. El cutover completo a
default-deny (dual DB roles) se rastrea por separado.
5. Regenerando este documento¶
El agrupamiento de dominios y la lista de tablas derivan del schema.ts; cuando agregue una
pgTable, agréguela al dominio correcto en la §1 y, si es load-bearing, a la §3.
Una mejora futura puede auto-generar el ERD desde los metadatos de Drizzle.