# M4 — el adaptador soberano (API congelada) El puente a los sistemas soberanos (Tawasuyu/Hammer). Unifica todo el proyecto tras una API estable, `adapter::SovereignAccount`, con precondiciones honestas. No se declara completo hasta que el contrato de abajo se sostiene bajo los tests que lo respaldan (`adapter::tests`). ## El contrato (§H de T9/T10/T11) — escrito en piedra | Cláusula | Cómo se cumple | Respaldo | |---|---|---| | **Requiere** WAL durable de `spent` por dispositivo | `open` rehúsa con `NoDurability` si `caps.durable_per_device == false` | `rehusa_sin_durabilidad` | | **Requiere** identidad = conjunto de dispositivos | `open` rehúsa con `NoDeviceIdentity` si `caps.identity_as_devices == false` | `rehusa_sin_identidad_por_dispositivo` | | **Garantiza** seguridad coordination-free del invariante | `value() ≥ floor` en toda ejecución (Σ b0 conservado, gastos guardados) | `certificado_gastando_todo_el_slack` | | **Acota y detecta** (no previene) el clon | `sync` devuelve `CloneDetected`; el daño ya ocurrió, es **alarma** (`BOUNDARY.md`) | `clon_detectado_en_sync` | | **Limita** la disponibilidad bajo partición | `spend` devuelve `Rejected { spendable }`; nunca sobregira ni cuelga | `particion_sin_ruta_es_fail_safe` | | **Rehúsa** en vez de prometer de más | `open` devuelve `Err(AdapterError)`, no una cuenta insegura | ambos `rehusa_*` | ## La API pública congelada ```rust SubstrateCapabilities { durable_per_device, identity_as_devices } AdapterError { NoDurability, NoDeviceIdentity, InvalidSplit } SovereignAccount::open(caps, inv, weights, edges) -> Result // rehúsa si el sustrato no cumple; reparte el slack en celdas-dispositivo. .spend(device, amount) -> SpendOutcome // Local | rebalanceo | Rejected{spendable} .available(device) -> i64 // presupuesto local .total_available() -> i64 // Σ b0 − Σ spent .value() -> i64 // inicial − Σ spent (≥ floor: certificado) .invariant_holds() -> bool .recover(device) // restaura spent desde el WAL tras crash .reboot(device) -> u64 // incrementa la época device-local (fencing) .sync(&other) -> MergeOutcome // Ok | CloneDetected (alarma) ``` ## Qué compone (y de dónde sale cada garantía) - **Presupuesto con procedencia** — `escrow::EscrowState` (T10a): crash-safe y merge-safe; `Σ avail = Σ b0 − Σ spent` invariante a las transferencias. - **Durabilidad** — `durable::WriteAheadLog` (T11b): el `spend` persiste al WAL **antes** del ack; `recover` restaura `≥ último ack`. Sin ella, `open` rehúsa. - **Ruteo edge-local** — `nerve` + transferencias `grant` (T9b/T10b): `spend` rebalancea de vecinas con excedente, transaccional (o rechaza, no cuelga). - **Réplica-por-dispositivo** — `escrow::Identity` (T11a): la cuenta ES la identidad; cada dispositivo, una celda single-writer. - **Fencing / clon** — época device-local + `sync` → `MergeOutcome::CloneDetected` (T11c). Detalle del daño acotado a una celda en `fencing.rs`. ## Lo que M4 NO promete (las fronteras, dichas) - **No previene el doble gasto por clon.** Lo acota (una celda) y lo delata. `CloneDetected` es una **alarma**, no una recuperación: el dinero ya salió, la compensación vive en la capa de aplicación y puede fracasar (`BOUNDARY.md`). - **No hay disponibilidad ilimitada bajo partición.** Cada dispositivo se limita a su presupuesto local; `diffuse` (ruteo proactivo, `router.rs`) lo mitiga, no lo elimina (`AVAILABILITY.md`). CAP: safety > availability. - **No es un motor de replicación.** No hay red real ni wire protocol: es la capa de decisión coordination-free que el sustrato soberano invoca. La persistencia y el transporte los provee Tawasuyu/Hammer; el adaptador **exige** que la persistencia sea durable-por-dispositivo, o rehúsa. ## El enunciado, una última vez **Seguridad coordination-free módulo un supuesto físico nombrado:** un escritor durable por celda. Bajo él, el certificado dinámico de T10 aplica sin coordinación. Roto (clon), el daño está acotado a una celda por clon no detectado y se detecta al reconectar. No "seguro pase lo que pase" — seguro salvo esta condición física, con el costo exacto de romperla escrito en `BOUNDARY.md` y `AVAILABILITY.md`.