CONCEPTOS
El modelo Event-Sourcing
ChainMemory sigue una arquitectura event-sourcing. Entender este patrón es clave para entender todo el sistema.
En sistemas tradicionales, guardás el estado actual y lo sobreescribís en cada cambio. En event-sourcing, guardás cada cambio como un evento inmutable, y el estado actual se deriva reproduciendo esos eventos.
En ChainMemory:
- Las memorias son eventos — Cada memoria es un registro inmutable de algo que pasó: se tomó una decisión, se identificó un riesgo, se eligió una tecnología, se alcanzó un hito.
- El Project State es una vista construida a partir de esos eventos — Un cliente lee las memorias y propone operaciones que las citan; el servidor las valida, las aplica y guarda cada versión resultante. El estado no se recalcula solo desde el log: cambia únicamente cuando un cliente consolida.
- El anclaje es la prueba de existencia — Cuando un Project State se ancla on-chain, la blockchain certifica que esta vista materializada específica existía en ese block height exacto.
Patrón
Memoria #1 (evento) ─┐
Memoria #2 (evento) ─┤
Memoria #3 (evento) ─┼──→ ops de un cliente ──→ Project State v1 ──→ Anchor (bloque 120000)
Memoria #4 (evento) ─┤
Memoria #5 (evento) ─┘
Memoria #6 (evento) ─┐
Memoria #7 (evento) ─┼──→ ops de un cliente ──→ Project State v2 ──→ Anchor (bloque 123539)
Memoria #8 (evento) ─┘
Las memorias son la fuente de verdad y no cambian. Cada versión del estado registra en qué memorias se apoyó, así que cualquier ítem se puede rastrear hasta su evidencia. El anchor prueba qué estado existía en qué bloque.
Qué es una memoria
Una memoria es la unidad fundamental de ChainMemory. Es un fragmento de información extraído de una conversación con IA que se considera valioso para el futuro del proyecto.
Cada memoria contiene:
- Contenido — El texto de la conversación o nota
- Hash — SHA-256 del contenido, inmutable
- Proyecto — A qué proyecto pertenece
- Tags — Etiquetas libres para organización (decisión, bug, arquitectura, idea, etc.). Los tags son para tu uso — el Project State tiene sus propios campos fijos (ver Project State)
- Número — Secuencial dentro de tu cuenta (#1, #2...)
- Fuente — Desde dónde se guardó (extensión, MCP, API)
- Timestamp — Momento exacto de creación
Proyectos
Un proyecto agrupa memorias relacionadas. Cada proyecto tiene su propia línea de tiempo, estado consolidado, y ancla on-chain independiente.
Ejemplos de proyectos: mi-saas, tesis-ml, chainmemory, app-mobile.
Project State
El Project State (también llamado Project Brain) es la vista consolidada y versionada de un proyecto. No lo genera un motor en el servidor: un cliente — un modelo de IA que trabaja con update_project_state (MCP) o POST /v1/project/:name/state/ops (API) — propone operaciones, el servidor las valida contra una gramática cerrada, las aplica con un constructor determinístico y calcula el nuevo state_hash. Cada ítem registra las memorias que lo justifican.
Qué contiene el estado (versión de esquema 2)
| Campo | Qué guarda | Valores cerrados |
|---|---|---|
| vision, phase, current_focus | Hacia dónde va el proyecto, en qué etapa está y qué importa ahora | — |
| decisions | Decisiones con título, enunciado y estado | status: proposed, confirmed, rejected, superseded |
| milestones | Entregables | status: planned, in_progress, done |
| risks | Amenazas identificadas | severity: low, med, high — no medium ni critical; status: open, mitigated, closed |
| priorities | Trabajo ordenado con un priority_score, vinculado a los riesgos que mitiga (mitigates) | status: active, done, dropped |
| open_questions | Preguntas y, cuando se resuelven, su respuesta | — |
| assumptions, constraints | Lo que el proyecto da por supuesto y las reglas que debe respetar | — |
| vocabulary, metrics | Términos compartidos y valores con nombre | — |
| environment | Hosts, servicios, repositorios y reglas de operación | — |
| state_meta | Versión, marca consolidated_until_event y previous_state_hash | — |
"severity": "medium": el motor solo acepta low, med o high.
get_project_state (MCP) o GET /v1/project/:name/state, y es lo que lee un cliente antes de proponer operaciones. El resumen (brief) es el mismo estado escrito como texto por relevancia y dentro de un presupuesto de caracteres, para orientarse o para trabajar en una prioridad: get_project_brief (MCP), GET /v1/project/:name/inject (API) y el botón Project Brain de la extensión. El resumen es público por defecto: las direcciones, puertos, rutas de servidor y reglas de seguridad se reservan.
Ciclo de vida del estado
El estado es incremental y append-only: cada consolidación parte de la versión anterior y aplica solo las operaciones nuevas. Cada versión lleva su state_hash (SHA3-256 del estado canónico, con separador de dominio CM_PROJECT_STATE_V<schema_version>) y, en state_meta, el hash de la versión anterior. Una vez escrita, el hash de la versión se ancla on-chain en el contrato ProjectStateAnchor, normalmente en menos de diez minutos.
Cadena de versiones
v85 ──previous_state_hash──▶ v86 ──previous_state_hash──▶ v87
│ │ │
└─ anchorId 89 └─ anchorId 90 └─ anchorId 94
Ejemplo: lo que devuelve GET /v1/project/:name/state (recortado)
JSON
{
"project": "sistema-pagos",
"schema_version": 2,
"version": 4,
"state_hash": "0x3f8c2a…e91d",
"generated_at": "2026-09-13T22:02:35.000Z",
"anchor": { "status": "anchored", "onchain_anchor_id": 12, "tx_hash": "0x…", "block_number": 681919 },
"state": {
"phase": "beta",
"current_focus": "Revisión PCI antes de abrir a los primeros tenants",
"decisions": [
{ "id": "dec_0002", "title": "PostgreSQL con RLS para multi-tenancy",
"statement": "La seguridad por fila saca el filtrado por tenant de la capa de aplicación",
"status": "confirmed", "superseded_by": null,
"evidence_root": "0x9a41…c07e", "created_version": 2, "updated_version": 3 }
],
"milestones": [
{ "id": "mil_0001", "title": "Schema de BD", "status": "done",
"evidence_root": "0x5b2d…81fa", "created_version": 1, "updated_version": 2 }
],
"risks": [
{ "id": "risk_0001", "title": "Performance de RLS con 100K tenants", "severity": "med", "status": "open",
"evidence_root": "0xd7e0…2a19", "created_version": 3, "updated_version": 3 }
],
"state_meta": { "version": 4, "consolidated_until_event": 150, "previous_state_hash": "0x7b2e…f4c0" }
}
}
GET /v1/project/:name/state requiere la API key del dueño y siempre devuelve la versión más reciente. Las versiones anteriores se pueden verificar por hash (ver Hash y verificación), no volver a leer completas con este endpoint.
Buenas prácticas
- Primero guardá la memoria, después consolidá — el estado cita memorias; una decisión que solo existe en la operación no tiene respaldo
- Esperá el anclaje antes de citar — una memoria en texto plano recibe su
event_hashal sincronizarse con la cadena, unos 30 segundos después de escribirla - Avanzá la marca — enviá
consolidated_until_eventcon el número de memoria más alto que realmente revisaste - Reemplazá, no reescribas — una decisión reemplazada queda en el estado con su estado cambiado, así se conserva cómo evolucionó el proyecto
Cadena de evidencia
Cuando un cliente propone una operación, cita las memorias que la respaldan en evidence_memory_ids — un array de números de memoria. El servidor resuelve cada una a su event_hash y guarda en el ítem resultante la raíz Merkle de esos hashes como evidence_root.
Operación que envía el cliente
{
"op": "add_decision",
"title": "Usar consenso Clique PoA",
"statement": "Proof of Authority da un anclaje rápido y económico para una cadena soberana",
"evidence_memory_ids": [12, 45, 67]
}
Ítem guardado en el estado
{
"id": "dec_0001",
"title": "Usar consenso Clique PoA",
"statement": "Proof of Authority da un anclaje rápido y económico para una cadena soberana",
"status": "proposed",
"evidence_root": "0x028218d0…f082"
}
Esto crea una cadena de procedencia:
- La decisión
dec_0001existe porque las memorias 12, 45 y 67 la respaldan; la respuesta de la consolidación lista sus event hashes - El
event_hashde cada memoria se escribe on-chain cuando la memoria se sincroniza - El
evidence_rootcompromete exactamente ese conjunto de hashes - El estado que contiene el ítem tiene un
state_hashanclado enProjectStateAnchor
422 evidence_unresolved, y no se escribe ni se cobra nada. Una operación que no cita nada se acepta, pero se guarda con evidence_root 0x000…0: estado sin procedencia. La cadena lo sella igual, porque verifica hashes, no que el contenido sea correcto.
Resolución de conflictos
Nada resuelve contradicciones automáticamente. No hay un motor que lea las memorias y decida cuál gana: decide el cliente que consolida, y la gramática hace que esa decisión quede explícita y auditable.
Reemplazar una decisión
Si una memoria más nueva cambia el rumbo, el cliente agrega la decisión nueva y marca la anterior con supersede_decision (id de la anterior, by_id de la nueva). Las dos quedan en el estado.
Estado de una decisión
Una decisión se crea como proposed. El cliente la pasa a confirmed o rejected con set_decision_status, y a superseded con supersede_decision.
Varios agentes en el mismo proyecto
Las operaciones se aplican sobre la versión vigente en el momento en que llega la llamada; /state/ops no comprueba una versión esperada. No se pierde nada — cada llamada crea una versión nueva — pero un agente que trabaja con una lectura vieja puede volver a agregar o contradecir lo que otro acaba de escribir. Leé el estado justo antes de consolidar. Cada llamada acepta hasta 100 operaciones.
Gobernanza del estado
El ciclo de vida del Project State está gobernado por reglas claras:
Quién puede consolidar?
Solo el dueño del proyecto (la cuenta que lo creó) puede disparar una consolidación. Esto asegura que la extracción de conocimiento estructurado siempre esté controlada por el dueño de los datos.
Cuándo ocurre la consolidación?
- La dispara el cliente — un modelo de IA propone operaciones vía
update_project_state(MCP) oPOST /v1/project/:name/state/ops(API). El servidor las valida y recién entonces se vuelven estado - No hay disparador automático — la consolidación nunca ocurre sola. Nada se escribe en tu estado si un cliente no lo propone
Se puede revertir?
Cada consolidación crea una nueva versión (v1, v2, v3...). Las versiones anteriores siguen accesibles. No podés borrar una versión, pero siempre podés consolidar de nuevo para producir un estado corregido. La cadena de versiones es append-only.
Snapshots
Cada versión del Project State es un snapshot. La combinación de número de versión + state_hash + ancla on-chain crea un checkpoint verificable. El dueño lee la versión más reciente con GET /v1/project/:name/state; el hash y el ancla on-chain de cualquier versión anterior son públicos en GET /v1/project/:name/state/anchor?version=2 y GET /v1/verify/:name.
| Acción | Quién | Cuándo | Reversible |
|---|---|---|---|
| Consolidar | Dueño del proyecto | Solo cuando un cliente propone operaciones | Se crea nueva versión (append-only) |
| Anclar on-chain | Automático (wallet del operador) | En ~10 minutos tras cada consolidación | Inmutable una vez anclado |
| Archivar memoria | Dueño del proyecto | Cualquier momento | Se puede desarchivar |
| Leer el estado más reciente | Dueño del proyecto (API key) | Cualquier momento | N/A (solo lectura) |
| Verificar el hash de cualquier versión | Cualquiera (endpoint público) | Cualquier momento | N/A (solo lectura) |
Hash y verificación
Cada memoria genera un hash SHA-256 de su contenido. Este hash es la huella digital única e inmutable de esa memoria. El contenido no se puede modificar sin cambiar el hash.
El state hash es un hash del estado consolidado completo (todas las decisiones, hitos, riesgos, etc.). Este state hash se ancla en la blockchain mediante una transacción en el contrato ProjectStateAnchor.
Verificación pública
La verificación en ChainMemory es pública y sin permisos. Cualquier persona puede:
- Llamar a
GET /v1/project/:name/state/anchor(no requiere API key) - Obtener el
state_hashy eltx_hashde la transacción - Verificar en el explorer que la transacción existe
- Leer el contrato directamente en la blockchain para confirmar que el hash coincide
Esto demuestra que el estado del proyecto existía exactamente así en el momento del anclaje. No se puede falsificar retroactivamente.
Modelo de privacidad
Una preocupación común con sistemas basados en blockchain es la exposición de datos. ChainMemory aborda esto con una separación estricta:
Qué se guarda dónde
| Dato | Ubicación | Acceso |
|---|---|---|
| Contenido de memoria | Base de datos encriptada (off-chain) y texto cifrado AES-256-GCM on-chain | Texto plano: solo el dueño. Texto cifrado: público pero ilegible sin la llave |
| Hash de memoria (SHA-256) | Base de datos + on-chain cuando la memoria se sincroniza (~30 s) | El hash es público pero no revela nada del contenido |
| Project State (estructurado) | Base de datos (off-chain) | Solo el dueño |
| State hash | Blockchain (on-chain) | Público — esta es la prueba verificable |
| Metadata del anchor (tx, bloque) | Blockchain (on-chain) | Público |
Un ancla de estado contiene solo el identificador del proyecto hasheado, el número de versión, el hash del estado y un timestamp. Una transacción de memoria lleva además el contenido cifrado más su categoría, importancia y largo del texto plano. Desde cualquiera de las dos es imposible reconstruir el contenido sin la llave del dueño.
Este diseño significa que ChainMemory puede proveer verificación criptográfica sin comprometer la privacidad. La blockchain prueba que un estado existió, no qué contenía.