Pilar 1 — Fideicomiso de Datos
La tesis. Construir el fideicomiso de datos de la región: una infraestructura de contribución donde instituciones, empresas y personas aportan datos bajo consentimiento granular y revocable, reciben un recibo de procedencia verificable, y se convierten en partes interesadas de primera clase. Derechos, no propiedad. Compensación inmediata en reputación, no promesas diferidas.
Este es el primer pilar del ciclo de los cuatro pilares: el corpus con procedencia es el único activo que hace posible un modelo soberano que nadie más puede replicar.
Por qué un fideicomiso y no una compra de datos
Comprar o raspar datos produce un corpus sin consentimiento, sin licencia clara y sin nadie invertido en su calidad. Un fideicomiso invierte la relación: el aportante es parte interesada, no proveedor. Recibe un recibo verificable en el acto, ve exactamente por qué su contribución valió lo que valió, y conserva el derecho a revocar. El activo que crece no es una tabla — es una red de contribuyentes con incentivo a que los datos sean buenos.
Cómo funciona /contribuir, de punta a punta
La superficie en vivo es /contribuir. El formulario del cliente (src/routes/contribuir.tsx) envía la promesa a la función POST /api/contribute, un transporte delgado sobre un núcleo puro y probado con tests (server/trust/ledger.ts). El flujo, de punta a punta:
1. Validación con puerta de consentimiento
validateContribution() normaliza el cuerpo no confiable — nunca lanza excepción — y rechaza toda promesa sin consentimiento explícito: si consent !== true, la contribución no se crea. Valida el email por regex, exige título (3–160 caracteres) y descripción (10–1.200), y filtra las geografías contra el conjunto canónico de 27 ISO3 de la región (LATAM_ISO3), deduplicadas y con tope — un ledger de procedencia no admite códigos de país basura. Los pilares pasan por regex de slug ([a-z0-9-], tope 12); un idioma no reconocido cae a es.
2. Puntuación con una rúbrica transparente
scoreContribution() asigna puntos con una rúbrica determinista y explicada — no un Shapley opaco (su costo combinatorio se descartó al lanzar). Los puntos se descomponen en: base (20; +8 de bono si la promesa es explícitamente regional, con geografías vacías), cobertura geográfica (3 por ISO3, tope 30), amplitud de pilares (3 por pilar, tope 12), apertura de la licencia (open 15 · cc-by 12 · share-alike 9 · non-commercial 5 · cite-only 2) y modo de privacidad (abierto, sala limpia y cómputo privado 10 por igual; solo-agregados 7). La descomposición (points_breakdown) viaja en el recibo, así el contribuyente ve exactamente por qué ganó lo que ganó.
La privacidad se premia, no se penaliza
Una contribución que preserva la privacidad (sala limpia o cómputo privado) desbloquea datos que de otro modo no podrían compartirse — por eso se recompensa a la par de los datos crudos abiertos, no por debajo. Es el principio de "computar sin poseer": el valor sin la exposición.
3. Recibo de procedencia verificable por hash, listo para cadena
makeReceipt() emite un Receipt cuyo nivel de garantía depende de un interruptor de entorno, y el propio recibo lo declara en su campo version:
- v1 — verificable por hash (por defecto). El
receipt_hashprueba integridad del contenido, no autenticidad del emisor; la prueba de emisión es la presencia del recibo en el ledger del servidor. - v2 — firmado por el emisor. Con
TRUST_RECEIPT_HMAC_KEYconfigurada, el recibo pasa aversion: 2y añadesig_alg: "hmac-sha256"mássignature= HMAC-SHA256(clave,receipt_hash). Eso autentica al emisor, pero no es verificable por terceros: verificar exige la clave secreta.
La verificabilidad pública por terceros necesita una firma Ed25519 sobre el cuerpo canónico, y sigue en hoja de ruta — ver Procedencia avanzada. Sus propiedades verificables:
- El email nunca se almacena. El registro público lleva un
contributor_idseudónimo — un hash salado del email. El mismo email produce el mismo id, pero el email crudo no es recuperable ni entra jamás al ledger persistente. Un requisito operativo sostiene esa irreversibilidad:TRUST_ID_SALTdebe configurarse con un valor secreto, porque la sal de respaldo incluida en el código es pública, y con una sal pública el id deja de ser irreversible frente a quien pruebe emails candidatos. El recibo estampa además unsalt_versionpúblico, para que una rotación de sal nunca rompa la revocabilidad (ver abajo). - El recibo es reproducible. El
receipt_hashes un SHA-256 sobre el cuerpo canónico (claves ordenadas recursivamente constableStringify), así que el mismo valor lógico siempre produce el mismo digest. Cualquiera puede recomputar el hash desde las entradas y verificarlo. - Listo para cadena por construcción. El esquema mapea 1:1 a una atestación on-chain futura:
contributor_id,contribution_idyreceipt_hashson las columnas de esa atestación, ychain_anchores la ranura reservada para el hash de transacción cuando llegue la liquidación on-chain (fase posterior). Hoy:chain_ready: true,chain_anchor: null— listo fuera de cadena.
Anatomía exacta del Receipt (interfaz en server/trust/ledger.ts):
| Campo | Derivación |
|---|---|
receipt_hash | SHA-256 hex sobre el cuerpo canónico — todos los demás campos, con claves ordenadas. |
contribution_id | d_ + 24 hex: hash de (contributor_id, título, descripción, geografías, pilares, licencia, privacidad, issuedAt, nonce UUID). Único por promesa. |
contributor_id | c_ + 24 hex: sha256(sal + ":" + email). Estable, seudónimo, irreversible. |
points / points_breakdown | Total + descomposición {base, coverage, breadth, license, privacy}. |
corpus_posture | Lo que la licencia permite en el corpus (la puerta de licencia, estampada de antemano). |
salt_version | s_ + 12 hex: etiqueta pública de qué sal produjo el id — un hash unidireccional que no revela nada de la sal. |
issued_at / version | ISO-8601 de emisión · versión de esquema: 1 (solo hash) o 2 (firmado por el emisor con HMAC). |
chain_ready / chain_anchor | true · null (ranura reservada al tx hash). |
Los campos declarativos (tier, dataset_title, geographies, pillars, license, privacy, indigenous_data) viajan tal cual se validaron. makeReceipt() es determinista dadas (promesa, issuedAt, nonce, sal): el mismo insumo siempre produce el mismo recibo — la base de la verificación independiente.
4. Registro append-only y honestidad cuando no está configurado
El ledger es append-only: server/trust/store.ts hace RPUSH a Upstash Redis vía REST (comandos como arrays JSON con fetch plano, sin dependencia npm). Un recibo nunca se muta. Si Upstash no está configurado, la función es honesta: devuelve el recibo con persisted: false en vez de fingir persistencia. El append es resiliente sin dejar de ser honesto: se reintenta con backoff acotado (3 intentos, 100·n ms) antes de degradar a persisted: false, y las lecturas saltan filas corruptas en vez de tumbar el registro.
La disposición de claves en Redis separa fuente de verdad de optimizaciones:
| Clave | Estructura | Rol |
|---|---|---|
trust:ledger:v1 | LIST | Fuente de verdad append-only (RPUSH / LRANGE). |
trust:revocations:v1 | LIST | Tombstones, en lista hermana — cada lista es mono-tipo, la lectura tipada nunca confunde una fila. |
trust:ledger:index:v1 | HASH id→registro | Búsqueda O(1) para revocar (HGET); best-effort — si falla, el append no falla. |
trust:revocations:index:v1 | SET | Revocación idempotente: SADD = 0 → ya revocada, no-op. |
trust:ledger:dedupe:v1 | SET | Claves de dedupe de promesas. |
El ledger también se defiende solo:
- Dedupe de promesas idénticas. La clave es contenido-direccionada:
dk_+ 32 hex del hash de{contributor_id salado, título, descripción}— nunca contiene el email crudo. Dos promesas idénticas colapsan a una fila víaSADD; el contribuyente igual recibe un recibo válido, con la nota de que no se duplicó. Y el dedupe falla abierto: un error del store devuelvefresh: true— un traspié de Redis jamás bloquea una contribución genuina. - Límite de tasa y de cuerpo.
POST /api/contributeacepta 10 solicitudes/minuto por IP y un cuerpo de máximo 64 KB con guardia en dos etapas (rechazo inmediato porContent-Length, y conteo de bytes durante la lectura contra unContent-Lengthmentiroso); los índices y sets de Redis tienen techo de crecimiento (500.000 entradas) — pasado el tope se deja de indexar, pero la lista y el escaneo siguen funcionando. - Registro público con ventana honesta.
GET /api/contributepublica agregados sobre una ventana acotada de 500 registros, etiquetada como tal (stats.window {limit, complete}), y la lista pública omite deliberadamente elcontribution_id— así una revocación no se puede falsificar desde afuera ni confirmar el mapeo email→registro.
5. Revocación por tombstone
El consentimiento se prometió revocable; api/revoke.ts es el mecanismo. makeRevocation() escribe un tombstone append-only que computeStats resta al momento de leer — el recibo original permanece en el ledger para siempre (listo para cadena, nunca mutado), pero la contribución queda excluida de los agregados y de la lista pública. La propiedad se prueba recomputando el contributor_id salado desde el email (matchingSalt, del que verifyRevocation es la envoltura): como el email crudo nunca se guardó, solo quien controla ese email puede probar control de la contribución.
Dos garantías de ingeniería sostienen la promesa:
- La rotación de sal no rompe la revocación. El orden de resolución es preciso (
saltCandidates+matchingSaltenledger.ts): las sales candidatas sonTRUST_ID_SALTvigente, luego cada entrada separada por comas deTRUST_ID_SALT_PREVIOUS, con la sal por defecto del código como respaldo. Si el registro traesalt_versionestampado, se busca la candidata cuya etiqueta coincide y solo esa debe reproducir elcontributor_id— una versión desconocida falla seguro (no se puede probar propiedad). Un registro legado sin estampa prueba cada candidata directamente. - La revocación es O(1) e idempotente. Un índice
HSET(contribution_id → registro) resuelve la búsqueda con unHGETsin escanear el ledger completo — con un escaneo completo único como respaldo para registros legados que precedieron al índice — y un índice de revocaciones (SADD) convierte revocar dos veces en un no-op barato. El endpoint valida la forma del id (d_+ 24 hex) antes de tocar el store, y responde 404 si la contribución no existe y 403 si el email no prueba propiedad.
La puerta de licencia: solo-cita nunca se vuelve valor
Este es el gate estructural que deja fluir datos aportados al corpus sin violar su licencia (server/trust/license-gate.ts). Espeja la puerta de LicensePosture del registro de fuentes: una contribución cuya licencia prohíbe la redistribución solo puede volverse una referencia de cita, jamás un valor de indicador horneado.
| Licencia | Acción en el corpus | Qué permite |
|---|---|---|
open / cc-by / share-alike | indicator | Los valores pueden hornearse en el corpus y la API pública. |
non-commercial | aggregates_only | Solo agregados derivados; nunca republicación fila a fila. |
cite-only | citation_only | Solo referencia de cita; jamás un valor horneado. |
La puerta se aplica en tiempo de horneado: scripts/bake-contributions.ts lee el export del ledger (public/data/contributions.json) y publica la proyección con licencia aplicada (gateContribution()) a public/api/v1/contributions.json — la superficie de contribuciones de la API pública. Al proyectar una contribución solo-cita a su forma pública, sus geografías y pilares se vacían a [] — no se republica cobertura alguna (gateViolation() es el invariante probado por tests detrás de esa regla). El recibo estampa la postura (corpus_posture) para que el contribuyente vea de antemano en qué puede convertirse su dato.
El artefacto declara su propio contrato: lleva la nota license_gate en texto plano y los contadores redistributable / citation_only, así un consumidor de la API ve cuántos registros están gateados sin leer el código. El script solo reescribe ese artefacto y dos campos del manifiesto — nunca regenera el árbol completo de la API (bake-api.ts también lo produce en un rebake total).
Derechos, no propiedad — y el anclaje CARE / LGPD
El marco es derechos, no propiedad: el contribuyente no "vende" un activo, retiene derechos sobre su uso — consentimiento con propósito acotado, revocable. Los datos etiquetados como indígenas llevan una bandera explícita de autoridad-para-controlar, siguiendo los principios CARE (Collective Benefit, Authority to Control, Responsibility, Ethics). El manejo del email — usado solo para una notificación operativa fuera de banda (Mailgun, best-effort), nunca persistido — sigue la lógica de minimización de datos de la LGPD brasileña y marcos afines de la región, no una plantilla importada. La misma disciplina alcanza a la analítica: el cliente identifica con un SHA-256 del email calculado en el navegador; la dirección cruda nunca sale de la página.
Qué está en vivo hoy vs. por fases
| Estado | |
|---|---|
| Contribución con puerta de consentimiento | En vivo — /contribuir |
| Recibo verificable por hash (SHA-256 reproducible) | En vivo |
| Ledger append-only + revocación por tombstone | En vivo (Upstash; honesto si no está configurado) |
| Puerta de licencia (solo-cita nunca es valor) | En vivo (regla de build) |
| Rúbrica de puntos transparente | En vivo |
| Firma de emisor HMAC-SHA256 (recibo v2) | En el aire, por entorno — se activa con TRUST_RECEIPT_HMAC_KEY; autentica al emisor, no verificable por terceros |
| Firma Ed25519 verificable por terceros | En hoja de ruta — sin ella, la emisión se prueba por presencia en el ledger |
| Custodia por un fideicomisario independiente | Por fase — a largo plazo la administración sale del operador |
| Niveles de privacidad / salas limpias operativas | Por fase — el esquema ya los modela; la ejecución llega después |
| Liquidación on-chain | Por fase — esquema listo (chain_anchor reservado); sin cadena aún |
| Niveles de sensibilidad + cifrado de registros | Por fase: hoy el modo de privacidad (PrivacyMode) es metadato declarado, no aplicado; los registros se guardan en texto plano |
| Prueba de registro de conocimiento cero (ZK proof-of-record) | Por fase: sin código aún; probaría la presencia de un registro en el ledger sin revelarlo |
| Notas comunitarias sobre contribuciones | Por fase: sin código aún |
| Carta de incentivos del contribuyente | Por fase: la rúbrica transparente de puntos ya está en vivo; lo que queda por fases es la carta que la gobierna y la liquidación en token |
Superficies relacionadas
- /contribuir — el formulario de contribución y el registro público de recibos.
- /datos-abiertos — el corpus abierto con su licencia declarada.
- /linaje — el linaje de procedencia de las cifras.
- /confianza — las insignias de confianza y la trazabilidad.
Sigue el ciclo hacia el Pilar 2 — Modelo Soberano, el modelo que este corpus hace posible.