Dos precisiones para que el registro diga exactamente lo que el código hace: - durable: documentar que `real` es un oráculo INDEPENDIENTE del desembolso FÍSICO al usuario — se incrementa en el ack (punto de entrega externo e irreversible), una vez por retiro entregado, y NUNCA lee la contabilidad interna (value/committed). Cuenta eventos de entrega, no lo que el sistema cree que gastó. El guard usa la contabilidad para decidir; si se corrompe (rollback) se entrega de más y `real` lo atrapa. Ahí vive detección vs prevención. - BOUNDARY.md: CloneDetected es una ALARMA, no una recuperación. El daño de una celda ya ocurrió en el mundo (dinero real entregado); aislar evita que el doble conteo se propague, pero no recupera nada. La compensación vive fuera del sistema, es coordinación, y puede fracasar. Acotado y delatado, sí; reversible, no necesariamente. Para que M4 no lo lea como auto-reparación. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.6 KiB
BOUNDARY — la frontera del clon (T11c, §D)
El límite último del proyecto, dicho sin sobre-reclamo. Respaldado por
fencing::tests.
El teorema de imposibilidad
Ningún mecanismo local puede detectar un clon silencioso antes de que los clones se comuniquen.
Un dispositivo restaurado a dos copias vivas (VM snapshot, proceso duplicado) que
comparten device_id y celda y no se comunican: cada clon cree tener la celda
entera y puede gastarla. Sin comunicación, no hay forma de saber que el
presupuesto ya se gastó en la otra copia. Es el mismo CAP que el sobregiro
particionado (AVAILABILITY.md), ahora dentro de una celda: bajo partición, o
sacrificas consistencia (doble gasto) o disponibilidad (no gastas). No hay tercera
opción, y este proyecto no finge tenerla.
Lo mejor posible: fencing por época, enforced en el merge
No un servidor central (no existe en la malla), sino el propio merge:
- Cada arranque de un dispositivo incrementa una época monótona, device-local
y persistida en el WAL (§B/
durable.rs). Device-local a propósito: épocas globalmente monótonas pedirían consenso, que es justo lo que evitamos. - El gasto se indexa por
(device_id, epoch). - Al fusionar, dos linajes con el mismo
device_idy épocas distintas delatan un clon: la época menor es la obsoleta (MergeOutcome::CloneDetected).
La garantía real: bounded + detect, no prevent
Honestidad frente al fencing token clásico (Kleppmann): aquél asume un recurso
linealizable que rechaza tokens viejos. La malla no lo tiene, por eso el
enforcement vive en el merge y la garantía es más débil y honesta:
| Sin fencing | Con fencing (T11c) | |
|---|---|---|
| Doble gasto | silencioso e ilimitado | acotado a una celda por clon no detectado |
| Detección | nunca | al reconectar (en el merge) |
- No previene el doble gasto mientras los clones están aislados — es imposible.
- Lo acota: cada clon gasta ≤ su presupuesto de celda ⟹ el sobregiro es ≤ una
celda (
FencedCell::overspend() ≤ budget, verificado porproptest). - Lo delata:
CloneDetecteden el merge. El linaje vivo (época mayor) es lo único que se propaga como legítimo; los gastos del clon obsoleto quedan aislados.
CloneDetected es una ALARMA, no una recuperación. El daño de una celda ya
ocurrió en el mundo: el clon obsoleto entregó dinero real que no se puede
des-entregar desde aquí. Aislar sus gastos evita que el doble conteo se propague
como presupuesto legítimo por la malla, pero no recupera nada. La compensación
(clawback, reverso, alarma humana) es un problema de la capa de aplicación,
vive fuera de este sistema, es coordinación, y puede fracasar (el
dinero se fue). Acotado y delatado, sí; reversible, no necesariamente. M4 no
debe leer esto como "el sistema se auto-repara".
Convierte "silencioso e ilimitado" en "acotado y delatado". Eso es todo lo que un sistema coordination-free puede ofrecer aquí — y decirlo así es el resultado.
El terminus honesto (§E)
Seguridad coordination-free módulo un supuesto físico nombrado: cada celda de presupuesto tiene un único escritor durable. Bajo ese supuesto (durabilidad §B + réplica-por-dispositivo §C) el certificado de T10 aplica sin coordinación. Cuando el supuesto se rompe (clon, §D), el daño está acotado a una celda por clon no detectado y se detecta al reconectar. No "seguro pase lo que pase", sino "seguro salvo esta condición física, y aquí está exactamente qué pasa cuando se rompe y cuánto cuesta".