M4.md
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
SubstrateCapabilities { durable_per_device, identity_as_devices }
AdapterError { NoDurability, NoDeviceIdentity, InvalidSplit }
SovereignAccount::open(caps, inv, weights, edges) -> Result<Self, AdapterError>
// 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 − Σ spentinvariante a las transferencias. - Durabilidad —
durable::WriteAheadLog(T11b): elspendpersiste al WAL antes del ack;recoverrestaura≥ último ack. Sin ella,openrehúsa. - Ruteo edge-local —
nerve+ transferenciasgrant(T9b/T10b):spendrebalancea 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 enfencing.rs.
Lo que M4 NO promete (las fronteras, dichas)
- No previene el doble gasto por clon. Lo acota (una celda) y lo delata.
CloneDetectedes 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.