--- type: ARQ asset_id: ARQ-EL-IntelliBanks-ConfidencialidadAcceso-v01 version: v01 tipo: ARQ — Arquitectura de sistema status: 🟠 Draft v01 · generado con Fable 5 (11-jul-2026) · WARGAME Opus ejecutado (12-jul-2026 → OUT-EL-IntelliBanks-WargameConfidencialidad-Huecos-v01: 12 huecos, canal de render C2 a re-diseñar) · pendiente recalibración a v01-r1 · pendiente confirmación de supuestos por Alex · pendiente ratificación Victor (L3) owner: Victor Heredia sherpa: Jay (SherpaX · Fable 5) ratificador: Victor Heredia (L3) implementa: Alex (backend/app) intellibank: IB-XX-Maestro experimento: EXP-2026-13 proposito: > Sistema de Confidencialidad + Control de Acceso de IntelliBanks: documentos confidenciales que se consultan DENTRO del OS sin descargarse a la máquina del usuario, con identidad y autorización enforced server-side; un no-autorizado no puede usar el sistema. Gemelo de seguridad del Zero-Trust (EXP-09): reutiliza su enforcement, audit trail y atribución — no los duplica. insumos: - SP-EL-IntelliBanks-ConfidencialidadAcceso-Fable-v01 (pack de arranque · EXP-2026-13) - ARQ-EL-IntelliBanks-MultiEmpresa-ZeroTrust-v01 (EXP-09 · base sobre la que se monta) - RFI-XX-IntelliBanks-BlindajeGobernanzaSync-v01 (Capas 0–5 · decisiones tomadas) - TP-EL-IntelliBanks-MultiEmpresa-ZeroTrust-FableBrief-v01 (contexto y wargame space) gate_g0: PASS · vault consultado; construye sobre el ARQ de EXP-09 y el RFI de blindaje, no los re-deriva tags: [ARQ, intellibanks, confidencialidad, control-acceso, rbac, consulta-in-place, zero-trust, kill-switch, EXP-2026-13] --- # ARQ · Confidencialidad + Control de Acceso en IntelliBanks ## La confidencialidad es la segunda regla del mismo ruleset: clase de documento × clearance del usuario, enforced donde ya se enforcea todo — en el servidor > **Tesis de diseño.** EXP-09 resolvió *¿quién puede escribir qué, dónde?* Esta capa resuelve *¿quién puede VER qué, y por qué canal?* La respuesta usa la misma maquinaria: la **clase de confidencialidad** es un atributo más del ruleset de la Capa 0, y el pipeline de `gate.php` gana un paso de **clearance** junto al paso de tenant. Lo nuevo de verdad es el **canal**: los documentos confidenciales dejan de viajar por sync (que aterriza en disco) y pasan a un canal de **consulta-in-place** (streaming autenticado, render en memoria, cero copia local). Un documento que nunca aterriza no se puede exfiltrar desde el disco; un servidor que nunca entrega bytes a un no-autorizado no necesita confiar en el cliente. **Marco de honestidad (léelo antes del wargame).** Contra un usuario **no autorizado**, este diseño da garantía dura: cero bytes salen del servidor. Contra un usuario **autorizado pero malicioso**, ningún sistema del mundo impide fotografiar la pantalla; ahí la garantía cambia de *prevención* a **fricción + disuasión + atribución forense** (watermark identificado + log individual por vista). Los controles de cliente (bloquear copiar/exportar/imprimir) son fricción, no seguridad — la seguridad vive en el servidor. Esta distinción está marcada en todo el documento para que Opus ataque lo correcto. **Convención:** supuestos por falta de contexto van marcados **⚠️ SUPUESTO-n** y listados en §7 para confirmación de Alex. --- ## 1. Modelo de clasificación (la base de todo) Tres clases, definidas en la Capa 0 (`governance.json`), heredables por carpeta y sobreescribibles por documento: | Clase | Nombre | Canal | Aterriza en disco | Quién la ve | |---|---|---|---|---| | **C0** | Sync (default) | Sync bidireccional actual | ✅ Sí (comportamiento de hoy) | Toda la empresa (scoping EXP-09) | | **C1** | Restringido | **Consulta-in-place** — lectura y edición dentro del OS | ❌ Nunca | Solo principals con grant C1 al scope | | **C2** | Sellado | **Consulta-in-place** — solo lectura, watermark, log por vista | ❌ Nunca | Solo lista nominal con grant C2 ratificado | Reglas estructurales: - La clase vive **en el servidor** (columna `class` en `skills`, default `C0`) y en la Capa 0 como regla por ruta (ej.: todo bajo `IB-Clientes/*/Legal/` nace `C2`). El cliente jamás declara ni degrada la clase de nada — la clase es un veredicto del servidor. ⚠️ SUPUESTO-1. - **Monotonía:** reclasificar hacia arriba (C0→C1→C2) es acción de un `company_admin`; hacia abajo (desclasificar) exige ratificación del **ratificador del activo** (tripleta WORX) y queda logueada. Los overrides por empresa en Capa 0 solo pueden **subir** clase por ruta, nunca bajarla — misma filosofía "solo restringir" del contrato EXP-09 §c. - **Efecto de subir la clase de un doc que ya estaba en C0:** el servidor lo saca del sync map (§4) y emite una orden de **retiro local** best-effort a los clientes (borrar la copia local sincronizada). El retiro del cliente es cortesía, no garantía — las copias C0 históricas en máquinas ya autorizadas son un **riesgo residual declarado** (R-4, §7): la garantía dura de "nunca aterriza" aplica desde que el documento es C1/C2, no retroactivamente. Extensión del contrato Capa 0 (se agrega al `CP-XX-IntelliBanks-Governance-v01.json` de EXP-09 — mismo archivo, misma fuente de verdad, tres consumidores): ```json "confidentiality": { "default_class": "C0", "class_rules": [ { "path_prefix": "IB-Clientes/*/Legal/", "class": "C2" }, { "path_prefix": "IB-EL-EmpowerLabs/FIN-EL-Finanzas/", "class": "C1" } ], "class_policy": { "C1": { "view": true, "edit": "in-place", "copy_text": true, "export": false, "print": false, "watermark": "footer" }, "C2": { "view": true, "edit": false, "copy_text": false, "export": false, "print": false, "watermark": "forensic" } }, "view_ticket_ttl_seconds": 300, "heartbeat_seconds": 60 } ``` --- ## 2. Identidad + autorización (RBAC) **Identidad: la de EXP-09, sin agregar nada.** Token de dispositivo (`user_tokens`), `require_identity()` resuelve token→user→company en el servidor, `company_id` jamás viene del cliente. Esta capa no introduce un segundo sistema de identidad — introduce **clearance sobre la identidad existente**. **Autorización — tabla de grants:** ``` doc_grants( id, company_id, scope_type ENUM('intellibank','folder','doc'), scope_id, -- ruta canónica o skill_id según scope principal_type ENUM('user','role'), principal_id, class_max ENUM('C1','C2'), -- clearance que otorga en ese scope permissions SET('view','edit'), -- subconjunto de class_policy, nunca superconjunto granted_by, ratified_by, expires_at, revoked, created_at ) ``` Resolución de una petición de lectura (server-side, siempre): 1. Clase efectiva del doc = max(clase de columna, clase por regla de ruta en Capa 0). 2. C0 → aplica solo el scoping de tenant de EXP-09 (`WHERE company_id IN (0,:cid)`), fin. 3. C1/C2 → **deny-by-default**: se busca grant vigente (no revocado, no expirado) del usuario o de su rol, en el scope más específico que cubra el doc (doc > folder > intellibank). Sin grant → **404** (no 403: un no-autorizado no debe poder ni confirmar que el documento existe — mismo criterio que el serving cross-tenant de EXP-09). 4. Permisos efectivos = `class_policy[clase] ∩ grant.permissions`. La política de clase es techo; el grant solo puede recortar. **Gobernanza de grants (esto es WORX, no solo software):** - Grant C1: lo otorga `company_admin`; queda logueado. - Grant C2: lo otorga `company_admin` **y requiere `ratified_by`** (segunda firma, el ratificador del activo o Victor L3) antes de estar vigente. Un grant C2 sin ratificar existe pero no autoriza. - Todo grant tiene `expires_at` opcional (accesos temporales: un abogado externo 30 días) y es revocable en caliente (§5). - Roles v01: `admin_global` (Victor/Alex/Jay — hereda del ACL de `IB-XX-Maestro` de EXP-09), `company_admin`, `member`, `guest` (solo C0 de scope explícito, pensado para externos). ⚠️ SUPUESTO-2. --- ## 3. Consulta-in-place (el canal nuevo) **Principio:** para C1/C2 el servidor nunca entrega "el archivo" — entrega una **vista efímera**: contenido renderizable, con ticket de un solo contexto, que el cliente pinta en memoria y suelta. ### 3.1 Flujo de una vista ``` Usuario abre doc C1/C2 en el OS → POST /view_open.php {skill_id} [X-IB-Token] gate.php: identidad → versión → tenant → CLEARANCE (§2) → emite view_ticket {uuid, skill_id, user, permisos efectivos, exp: +300s} → log 'view_open' (usuario, doc, clase, ip, device) → GET/POST /view_stream.php {ticket} → stream del render (HTML sanitizado o PDF, según formato §3.3) → watermark inyectado SERVER-SIDE antes de salir (§3.4) → Cliente: render en viewer efímero (§3.2). NADA se escribe a disco. → Heartbeat cada 60s revalida ticket → si revocado/expirado: el viewer se cierra. ``` - El ticket es de **un solo uso por stream** y está atado al token del dispositivo que lo pidió — un ticket filtrado no sirve desde otra máquina. - `view_stream` responde con `Cache-Control: no-store, private` y vía **POST** — cero exposición al riesgo CDN identificado en EXP-09 (§h). El CDN nunca ve contenido confidencial cacheable. - **Edición C1 in-place:** el editor trabaja sobre buffer en memoria; guardado = `POST /view_save.php {ticket, contenido}` que entra al pipeline de escritura de EXP-09 (pasos 3–10: tenant, ruta, naming, versión Git, atribución). No hay autosave local, no hay draft en disco; si el app crashea, se pierde el delta no guardado — ese es el trade-off correcto para C1. ### 3.2 El viewer efímero (Electron) - `BrowserView`/ventana con **partición de sesión en memoria y no persistente** (`partition: 'view:'` SIN prefijo `persist:`): cache, cookies y storage de esa vista viven solo en RAM del proceso. ⚠️ SUPUESTO-3. - `session.on('will-download')` → **cancel** incondicional en particiones de vista: ningún "guardar como" posible desde el viewer. - Menú contextual, atajos de impresión (`Cmd/Ctrl+P`) y `window.print()` deshabilitados en el viewer según `class_policy`; selección/clipboard deshabilitados para C2 (`copy_text: false`). - Al cerrar la vista (o al fallar el heartbeat): destruir la BrowserView y su partición. Crash del app = la RAM se libera con el proceso; no queda artefacto porque nunca hubo escritura. - **Declaración honesta:** todo §3.2 es **fricción** (un cliente recompilado puede saltárselo). La seguridad real es §3.1 + §3.4: el servidor solo entrega a autorizados, y lo que entrega sale marcado y logueado. El wargame debe tratar el viewer como comprometible y verificar que aun así no hay fuga *no atribuible*. ### 3.3 Formatos (por qué el render es server-side) Los formatos de oficina (`.docx/.pptx/.xlsx`) hoy se renderizan en el previewer del cliente, ⚠️ SUPUESTO-4 sobre si eso escribe archivos temporales. Para C1/C2 el diseño lo corta de raíz: **el servidor convierte y entrega solo el render** (HTML sanitizado o PDF paginado), nunca el binario original: | Formato | Render entregado | |---|---| | `.md` `.html` | HTML sanitizado (mismo motor del previewer, corrido server-side) | | `.pdf` | PDF con watermark estampado, servido a pdf.js en el viewer | | `.docx` `.pptx` `.xlsx` | Conversión server-side a HTML/PDF (LibreOffice headless u equivalente ⚠️ SUPUESTO-5), cacheada por versión del doc en zona privada del servidor | Beneficios colaterales: el binario original jamás cruza la red hacia un cliente para C1/C2; el punto de inyección del watermark es único; y el "render por versión" cachea en servidor sin re-convertir. ### 3.4 Watermark - **C1 · `footer`:** franja con `usuario · empresa · fecha-hora` en el render. Disuasión ligera. - **C2 · `forensic`:** marca visible (diagonal tenue con usuario+timestamp) **y** marca invisible por espaciado/微-variaciones en el render por-usuario, de modo que una foto de pantalla o un screenshot filtrado sea **atribuible a la vista exacta** (usuario, dispositivo, momento — cruzable con el log `view_open`). Se estampa server-side: el cliente no puede pedir la versión "limpia" porque no existe endpoint que la sirva. --- ## 4. Gating a nivel-OS (bloquear, no ocultar) **Qué significa "no puede usar el sistema":** el OS es útil solo mientras el servidor le responde. Un no-autorizado no recibe un OS "con carpetas vacías" — recibe un OS que **no opera**: | Punto de gating | Mecanismo | Cliente o servidor | |---|---|---| | **Entrada** | Sin login válido no hay token; sin token, `require_identity()` devuelve 401 en TODO endpoint (gate.php EXP-09, paso 1). El app sin token queda en pantalla de login: no monta vault, no arranca daemon, no muestra nada. | Servidor decide · cliente obedece | | **Sesión viva** | Heartbeat de sesión (60s): el app revalida token. Token revocado → el app se **bloquea** (cierra viewers, congela sync con el freeze de EXP-09 §e, pantalla de bloqueo). Aunque un cliente hackeado ignore el bloqueo, cada request individual muere en 401 — el bloqueo de UI es cortesía; el bloqueo real es por-request. | Servidor decide · cliente obedece | | **Por documento** | C1/C2 sin grant → 404 en `view_open`. El doc no aparece en listados (`list_skills`, `search`, `get_sync_map` filtran por clearance además de tenant). No se oculta "en el cliente": los endpoints no lo emiten. | 100% servidor | | **Por máquina** | Revocación de token de dispositivo (EXP-09 `user_tokens.revoked`) mata esa instalación: no sync, no vistas, no nada. Cubre la laptop perdida y al ex-colaborador. | 100% servidor | | **Residuo local C0** | Lo único que un revocado conserva es lo que el sync C0 ya había aterrizado antes (texto plano en su disco). El bloqueo dispara una orden de retiro best-effort; el residuo C0 es riesgo residual R-4 (§7) con mitigación de roadmap (cifrado at-rest del vault local). C1/C2 por diseño **nunca estuvieron ahí**. | Declarado, no resuelto en v01 | **Sync map con clearance:** `get_sync_map.php` (ya per-tenant en EXP-09) excluye ahora todo C1/C2 — esos documentos **no existen para el daemon de sync**. Aparecen en el árbol de navegación del OS solo para usuarios con grant (metadatos: nombre y clase, servidos por `list_skills` con filtro de clearance) con ícono de candado; abrirlos usa el canal §3, nunca el sync. Así el enforcement no depende de que el cliente "sepa no descargar": el canal de descarga simplemente no los contiene. --- ## 5. Confidencialidad end-to-end: clasificación → acceso → kill-switch → log **Ciclo de vida de un documento confidencial:** 1. **Nace clasificado** — por regla de ruta (Capa 0) o por acción explícita del owner al crearlo. La tripleta del activo (Owner + Sherpa + Ratificador) queda en el header del doc como siempre; el ratificador del activo es quien firma los grants C2 sobre él (§2). 2. **Acceso otorgado** — grant con tripleta de gobernanza: quién otorga (`granted_by`), quién ratifica (`ratified_by`, solo C2), hasta cuándo (`expires_at`). 3. **Se consulta** — solo in-place; cada vista C2 genera entrada de log individual. 4. **Kill-switch** (tres niveles, todos server-side, efecto ≤ TTL del ticket = 5 min sobre vistas abiertas, inmediato sobre toda petición nueva): - **Doc:** revocar grants / subir a C2 / `sealed=true` (nadie salvo `admin_global` lo abre). Como no existen copias locales, cortar el servidor ES cortar el acceso — esta es la recompensa estructural de consulta-in-place. - **Usuario:** revocar sus tokens (EXP-09) → fuera del sistema completo. - **Empresa:** freeze de tenant (bloquea vistas y sync de toda la empresa; para incidentes). 5. **Log admin (Capa 5 extendida, mismo `skill_activity_logs` de EXP-09):** eventos nuevos `view_open`, `view_denied`, `view_save`, `grant_created`, `grant_ratified`, `grant_revoked`, `class_changed`, `killswitch_*` — siempre con usuario, doc, clase, veredicto y `rule_id`. El tablero de salud del RFI (Capa 5) gana una vista de confidencialidad: vistas C2 por doc, denials por usuario (un usuario acumulando `view_denied` es una señal de reconocimiento hostil), grants por expirar. --- ## 6. Criterios de aceptación (a escala, con adversarios) Simulación con **≥20 usuarios concurrentes**, incluyendo **1 no-autorizado** (sin cuenta o revocado) y **1 exfiltrador** (autorizado a ver C2, intentando sacarlo): - [ ] **No-autorizado:** 0 endpoints responden útil (401/404 en todo); el OS no monta vault ni lista un solo documento; 0 bytes de contenido de cualquier clase entregados. Sus intentos quedan atribuidos en el log. - [ ] **Revocación en caliente:** usuario con viewer C2 abierto es revocado → toda petición nueva muere al instante y el viewer muere al siguiente heartbeat (≤60s) o expiración de ticket (≤5 min). Verificado con cronómetro. - [ ] **Cero aterrizaje:** durante toda la corrida, watch del filesystem (incluyendo `~/Library`/AppData, particiones y cache de Electron, /tmp) en la máquina del exfiltrador = **0 artefactos** con contenido C1/C2, incluso tras crash forzado del app a mitad de una vista. - [ ] **Cero descarga:** `get_sync_map` de los 20 clientes no contiene ninguna entrada C1/C2; will-download cancelado; export/print/copy bloqueados según clase (y sus intentos logueados). - [ ] **Ticket robado:** un `view_ticket` capturado y reproducido desde otra máquina/token → 403, 0 bytes. - [ ] **Bypass de listados:** `search.php`, `list_skills.php`, `get_skill_content.php`, `render`, `restaurar` — consultados con usuario sin grant → el doc C1/C2 no aparece ni por ID directo (404). Ningún endpoint "legacy" lo sirve. - [ ] **CDN:** 0 respuestas de `view_*` cacheadas (verificado con el parámetro anti-caché, lección EXP-09). - [ ] **Atribución forense:** un render C2 filtrado (simulado) se atribuye a usuario+vista exacta por su watermark, cruzado con el log. - [ ] **Grants:** grant C2 sin ratificar NO autoriza; grant expirado NO autoriza; ambos verificados por intento real. - [ ] **Escala:** los 18 usuarios legítimos operan C0 (sync normal EXP-09) sin degradación perceptible mientras corren las vistas confidenciales. **Victoria del sistema:** cero descarga no autorizada, cero fuga *no atribuible*, cero falso bloqueo de trabajo legítimo C0. --- ## 7. Riesgos residuales y supuestos **Riesgos residuales (declarados para el wargame, no escondidos):** | # | Riesgo | Postura del diseño | |---|---|---| | R-1 | **Analógico:** foto a la pantalla, transcripción manual | Imprevenible por cualquier sistema. Respuesta: watermark forense + log por vista = atribución. Es política + consecuencias, no software. | | R-2 | **Cliente recompilado** que ignora §3.2 (guarda el stream que recibió) | El stream ya viene watermarkeado por-usuario y logueado; lo que puede robar es su propia vista atribuible. La prevención dura sigue siendo §2/§3.1: nunca recibe lo que no le corresponde. | | R-3 | **Memoria del proceso:** un usuario técnico con debugger extrae el render de la RAM | Equivalente a R-2. Aceptado y atribuible. | | R-4 | **Residuo C0 histórico** en máquinas revocadas o docs reclasificados | Retiro best-effort + roadmap: cifrado at-rest del vault local con llave atada a sesión (decisión separada — rompe interop con Obsidian/Finder, la trae EXP-09 como trade-off abierto). | | R-5 | Conversión server-side (§3.3) agrega carga y superficie al servidor | Cache por versión; la conversión corre en zona privada; dimensionamiento pendiente del dato F del brief EXP-09 (SUPUESTO-5 de aquel ARQ). | **Supuestos a confirmar por Alex (⚠️):** 1. **SUPUESTO-1:** la tabla `skills` admite columna `class` + `sealed` sin fricción con el esquema real, y la clase puede resolverse en `gate.php` sin costo extra de query (JOIN ya presente). 2. **SUPUESTO-2:** el modelo de roles v01 (`admin_global`, `company_admin`, `member`, `guest`) basta para el roster actual; no existe hoy tabla de roles — se crea con `doc_grants`. 3. **SUPUESTO-3:** la versión de Electron del app soporta particiones de sesión no persistentes en memoria y la cancelación de `will-download` por partición (Electron ≥ moderno lo hace; confirmar versión real). 4. **SUPUESTO-4:** el previewer actual de `.docx/.pdf` en el cliente ¿escribe temporales a disco? Si sí, ese camino queda **prohibido** para C1/C2 (solo canal §3.3); si no, aun así el render server-side se mantiene por el watermark. 5. **SUPUESTO-5:** el servidor puede correr conversión de documentos (LibreOffice headless o servicio equivalente) o se contrata un microservicio aparte; PHP 5.6 solo orquesta, no convierte. 6. **SUPUESTO-6:** el heartbeat de 60s y TTL de ticket de 5 min son tolerables en UX para Victor (ajustables en Capa 0 sin deploy). --- ## 8. Encaje explícito con EXP-09 (qué se reutiliza, qué se agrega) | Pieza | EXP-09 (existente) | Esta capa (nuevo) | |---|---|---| | Identidad | `user_tokens`, `require_identity()` | — (se usa tal cual) | | Tenant | `company_id`, scoping en todo endpoint | — (se usa tal cual) | | Pipeline `gate.php` | pasos 1–10 | **paso 3b: clearance** (clase × grant) en lecturas y `view_*` | | Capa 0 | `CP-XX-IntelliBanks-Governance-v01.json` | sección `confidentiality` en el MISMO archivo | | Audit (Capa 5) | `skill_activity_logs` + tablero | eventos `view_*`/`grant_*`/`killswitch_*` + vista de confidencialidad | | Cuarentena/atribución | per-tenant | — (se usa tal cual) | | Canal de datos | sync bidireccional (C0) | **consulta-in-place** (C1/C2): `view_open/stream/save` + viewer efímero | | Kill-switch | revocación de token | + revocación por doc/grant y freeze por empresa | | Nada duplicado | ✔ | ✔ | **Secuencia de implementación (encadena con las fases EXP-09):** la columna `class`, `doc_grants` y el filtro de clearance en lecturas entran con la **Fase 1b** (keystone) de EXP-09 — es el mismo deploy de enforcement. El canal `view_*` + viewer efímero + conversión server-side entran con el **cliente 1.1.0 (Fase 2)**. El watermark forense y el tablero de confidencialidad, en **Fase 3**. Nada de esto bloquea las fases ya planeadas; se suben al mismo tren. --- ## NEXTs - [ ] **Opus — WARGAME de seguridad (obligatorio, antes de construir):** actores rojos del SP — no-autorizado que entra, exfiltración, suplantación, bypass de consulta-in-place — con foco extra en R-2/R-3 (cliente comprometido) y en el ticket/heartbeat como superficie. Output en `PB-EL-IALab/WG-EL-WarRoom/Wargames/`. - [ ] **Alex** — confirmar SUPUESTOS 1–6 (los 3 primeros con el código a la vista). - [ ] **Victor (L3)** — ratificar decisiones de negocio: 3 clases (¿bastan?), política `class_policy` por clase, roles v01, TTLs, y el trade-off R-4 (cifrado local sí/no). - [ ] **Equipo WORX** — extender `CP-XX-IntelliBanks-Governance-v01.json` con la sección `confidentiality` (Capa 0, junto con la Fase 0 de EXP-09). - [ ] Tras wargame + ratificación → handoff a Alex/Codex y actualizar **EXP-2026-13 a Corriendo**. --- ## CHANGELOG | Versión | Fecha | Cambio | |---|---|---| | v01 | 2026-07-11 | Creación con Fable 5 (EXP-2026-13, pack SP-EL-IntelliBanks-ConfidencialidadAcceso-Fable-v01). Clasificación C0/C1/C2 como extensión de la Capa 0; RBAC deny-by-default con doc_grants ratificados (C2 doble firma); canal de consulta-in-place con render server-side, viewer efímero en memoria y watermark forense; gating a nivel-OS por-request (el bloqueo de UI es cortesía, el real es 401/404 del servidor); kill-switch en 3 niveles con efecto ≤5 min; 10 criterios de aceptación adversariales; 5 riesgos residuales declarados y 6 supuestos para Alex. Reuso total del enforcement/identidad/audit de EXP-09 — cero duplicación. Pendiente wargame Opus (mandatorio). |