EMA Vault es la plataforma interna de operaciones, finanzas y
producto del ecosistema EMA Health. Concentra en una sola
aplicación todo lo necesario para correr el negocio: provisioning
cross-país de clientes, pipeline comercial con AI integrada,
contratos, finanzas multi-moneda, configuración fiscal por país,
gestión del catálogo de productos con sync GitHub, brand, documentos,
wiki interno, auditoría cross-product y moderación del asistente
EMI. La diferencia con el resto de los productos del ecosistema es
que Vault no se vende: es back-office exclusivamente interno y
solo accesible a staff de EMA Health autenticado vía Google con
correo @emahealth.io.
| Item | Valor |
|---|
| Versión actual | 0.2.0 (Wave 3 v1.0 + S9 ORGBAC) |
| Decisiones formales | F001 → F085 (ver docs/decisions.md) |
| Estado | 🟢 Producción interna vault.emahealth.io |
| Audiencia | Staff EMA Health (@emahealth.io) |
| Repo | emahealth/emavault |
| Workspace | /home/cristianruiz/Dev/emahealth/emavault |
| Capa | Tecnología | Notas |
|---|
| Frontend | React 19 + React Router 7 | SPA con sidebar de 7 grupos |
| Build | Vite 6 | TypeScript estricto |
| UI | Tailwind 4 directo | DS de 3 capas en styles.css |
| Design system | Custom — violet accent #7C3AED | 2 themes (light + dark), 50+ tokens |
| Backend | Hono | Cloudflare Workers |
| Auth | Supabase OAuth (Google) | Restringido a @emahealth.io |
| Auth check | vault_users.is_active=true | Tabla custom + RBAC por módulo |
| DB | Supabase emahub | Schemas emavault + emashared |
| Charts | Recharts | KPIs, MRR, analytics EMI |
| Icons | lucide-react | Sin SVG-as-component |
| AI | Anthropic API | Sonnet para proposal, contract, summary |
| EMI | Proxy server-side a emi.emahealth.io | Audit log obligatorio |
| GitHub | REST API + webhook | Sync de issues paginado + cron horario |
| Tests | Vitest 6 (unit + integration + regression) | en construcción |
| Package manager | npm | regla global EMA |
| Módulo | Funcionalidad |
|---|
| Clientes / Tenants | Provisioning automático cross-product (clinic, lab, well) vía X-Platform-Key |
| Pipeline | Kanban 7 etapas, conversión lead → cliente, MRR ponderado |
| Directorio | Organizaciones (cliente / proveedor / partner) + personas + contactos |
| Contratos | Lifecycle, alertas vencimiento 30 días |
| Finanzas | Entries, multi-moneda, exchange rates, MRR mensual UF |
| Facturación electrónica | Configuración fiscal por tenant, log de emisiones (en Chile contra SII; en otros países contra el organismo equivalente) |
| Catálogo | Servicios EMA — costos, pricing, plans |
| Pricing tool | Quotes con simulador, plans CRUD |
| Producto | Catálogo productos EMA — releases, roadmap, journeys, tech stack, GitHub sync |
| Brand | Print-to-PDF guidelines |
| Editorial | Posts blog/LinkedIn, métricas |
| Checklist | Tareas operativas |
| Obligaciones | Pagos, declaraciones, alertas |
| Time tracking | Horas por proyecto |
| Wiki | Documentación interna |
| Documentos | Repository ops |
| Infra | Inventario CF Workers, dominios, secrets |
| Equipo / Vault Team | Vault users + roles + permisos |
| EMI gestión | Admin matrix tenant_emi_access + analytics + lead linking |
| Audit log | Cross-product, sanitizado, queryable |
| Ema Well metrics | 5 KPIs read-only desde proyecto separado |
5 roles con permisos a nivel módulo (no recurso):
| Rol | Descripción |
|---|
admin | Bypassea todos los checks — fundadores y admin staff |
technical | Producto, infra, audit, wiki, equipo |
commercial | Pipeline, contratos, directorio, catálogo, pricing |
finance | Finanzas, facturación electrónica, MRR, obligaciones, contratos read |
viewer | Solo lectura limitada |
Tabla vault_permissions(role, module, can_read, can_write) — admin
configura.
| Decisión | Resumen |
|---|
| F003 | RBAC a nivel módulo, no recurso — vault_permissions por rol × módulo |
| F040 | Themes light + dark con override [data-theme="dark"] global |
| F045 | DS 3 capas — primitives @theme + semánticos CSS vars + override dark |
| F068 | Provisioning cross-product orquestado desde /api/internal/tenants |
| F075 | AES-GCM helpers para encrypt/decrypt aidbox client secret |
| F078 | EMI conversacional via proxy server-side /api/emi/chat (no SDK frontend) |
| F080 | emi_invocations audit log — cada llamada a EMI queda con tenant + user + lead |
| F082 | EMI access matrix tenant_emi_access + analytics 30d |
| F083 | Lead-EMI conversation linking — leads con source='emi_chat' muestran badge |
| F085 | Audit trigger SECURITY DEFINER + sanitización secrets en 12 tablas |
Decisiones completas en docs/decisions.md del repo.
- No usa FHIR / Aidbox — datos en Supabase puro (Vault es ops, no
clínico).
- No usa alias
@/ — imports relativos directos.
- Prefiere
type sobre interface (al revés que clinic).
- Audit por trigger SQL (no por handler) —
audit_trigger()
SECURITY DEFINER con sanitización.
- Carpeta
middleware/ agregada en Wave 3 — auth + security
headers centralizados.
- DS con violet accent (vs blue de clinic/lab).
- No replicar Vault auth en otros productos — RBAC por módulo es
específico de Vault. Los productos cliente usan
user_roles con
tenant_id + product.
- No exponer Anthropic API key en frontend —
/api/ai/* server-side
proxy obligatorio.
- No mezclar datos clínicos en Vault — Ema Well tiene proyecto
Supabase separado por Ley 21.719.
- No editar 77 archivos para dark mode — usar
[data-theme="dark"] .bg-white en CSS global.
- No usar Browser Rendering puppeteer en CF Workers para PDF user-triggered
— usar
window.print() con /print route limpia.