# Handoff DateKeys *Estado al 7 de octubre de 2026 por la tarde. Lo que pasó hasta aquí, sesión a sesión, está en [HANDOFF_historial.md](HANDOFF_historial.md).* Sirve para retomar sin contexto previo, sea una persona o una sesión de Claude. **Antes de empezar:** - La sesión se abre en `G:\bussines\datekeys`, la raíz, que es la que tiene la memoria y el `CLAUDE.md`. - Comprueba que corre en Opus. Una sesión del 1-10 corrió por descuido con Sonnet 5.5 y hubo que revisarla entera. Los trailers `Co-Authored-By` de los commits dicen qué modelo escribió cada uno. - Comprueba cada repo con `git status` y `git log`: todo lo de abajo estaba subido al cerrar. --- ## Estado | Repo | Rama y commit | Tags | Qué es | |---|---|---|---| | `datekeys-go` | `v0.15` en `96d8992`; `main` en `fe50885` | `spec-v0.15` en `fe50885`, y los de las versiones anteriores | Spec v0.15 aprobada, implementación de referencia, `testdata` compartido; `c49c67c` añade las palabras al azar, `27a75ee` el alfabeto de cada lista y la fuerza de las palabras, y `96d8992` arregla `TestGenerate` | | `App` (`datekeys-ts`) | `v0.10` en `4057607`; `main` en `7650418` | `v0.4.0` en `7650418`, `v0.3.0`, `v0.2.0`, `v0.1.0` | Librería TypeScript `0.5.0-dev`, de la spec 0.15, y páginas `/inspect` y `/create`, con las palabras al azar; la última publicada es la 0.4.0 | | `datekeys-dart` | `v0.15` en `fcc7780` | ninguno | Librería Dart completa, de la spec 0.15, con las palabras al azar; las ramas `v0.11` a `v0.14` se quedan atrás | | `docs` | `main` | — | Este repo | | `web` | `main` en `f2b8a38` | — | Landing de datekeys.com, sin remoto | - **Spec:** la v0.15 está aprobada desde el 7-10, con SHA-256 `45105e693be4187af4dd30f4d254402612587b6427c746f5d29f07a541c1e3f3`. Es la recuperación a largo plazo: el objeto release, los archivos y servicios de caché que guardan todas las rondas, el release en la mano en el paso 9.c (opción B: no se compara con el reloj) y el anexo de recuperación del §79. No cambia ningún formato de `.dkc` ni de `.dkk`. No hay ningún borrador abierto. Las versiones aprobadas y sus SHA-256 están en `datekeys-go/spec/README.md`. - **El fichero `.dkr` se descartó** antes de aprobar (7-10). El autor no lo había aprobado conscientemente, y no tiene sentido: al crear la cápsula no hay release que guardar, y cuando llega la ronda la cápsula ya se abre. El objeto release no tiene extensión propia; sale de un archivo o de un servicio de caché. El §76 lo registra. - **Las tres implementaciones** coinciden en todos los vectores compartidos de `datekeys-go/testdata` en `fe50885`, 142 ficheros, los mismos que en `27a75ee`, de donde TypeScript y Dart copian hoy `testdata` y `wordlists`. Gates el 7-10: - Go: `scripts/check.sh` entero en `fe50885`, con el anexo de recuperación abriendo fixtures sin código de DateKeys, y `scripts/fuzz.sh 20s` en `fe50885` con sus 27 objetivos, sin fallos; `scripts/check.sh` entero otra vez en `27a75ee` y en `96d8992`; - TypeScript: `npm run verify` en `323d920`, con 8 119 pruebas, y `npm run check` y el build en `4057607`; - Dart: `tool/check.sh` en `fcc7780`, con 2 230 pruebas en la VM y 705 en Node. - **Palabras al azar (7-10, después de cerrar la v0.15):** es el SHOULD de §38.1 de ofrecer palabras generadas; no cambia ningún formato ni la derivación. - Go: `c49c67c` añade `wordkey.Generate`, `List` y `CheckList`, y `datekeys encrypt -new-words FICHERO [-dic es] [-word-count 7]`. `27a75ee` hace que `CheckList(lang, words)` compruebe cada palabra contra el alfabeto de su idioma, que da el código y no la lista (para `es`, de la `a` a la `z`, `á`, `é`, `í`, `ó`, `ú`, `ü` y `ñ`, en minúscula y NFC): una letra cirílica que parece latina, una mayúscula, una cifra o el retorno de carro de un fichero CRLF dejarían la cápsula sin abrir. También añade `Bits`, la fuerza de lo sorteado, que `encrypt` muestra (90 bits para 7 de 7 776). `scripts/check.sh` pasa entero en los dos. - TypeScript (`0.5.0-dev`, de `012cd74` a `4057607`): `scripts/sync-testdata.mjs` copia del mismo commit de Go `wordkey/lists` en `wordlists/`, con su `SOURCE.json` (`testdata` y `wordlists` en `27a75ee`, cuyo `testdata` es el del tag). `wordlist.ts` hace lo de Go, con sus textos: `readWordList` solo acepta una lista con el SHA-256 fijado para su idioma, `checkWordList`, `generateWords` y `wordBits`. `/create` ofrece palabras al azar por defecto tras una casilla, con su fuerza y «Otras palabras», y deja escribir las propias con el aviso de que son más débiles; `licenses.txt` lleva la licencia de la lista. `npm run verify` pasa con 8 119 pruebas, y el flujo se comprobó en el navegador sobre el build de producción. - En `/create` apareció y se corrigió un fallo de la 0.4.0 (`4057607`): el efecto que comprueba las rutas se relanzaba sin fin (`effect_update_depth_exceeded`) en cuanto había un fichero, un comentario o un autor. - `wordBits` de TypeScript usa `Math.log2`, que puede diferir del `math.Log2` de Go en el último bit; la página muestra los bits truncados, como la CLI, así que no se nota. - Dart (`a770d4d` a `fcc7780`, de un agente Opus en un worktree, revisado contra Go): `tool/sync_testdata.dart` copia también `wordlists/`, y `lib/src/wordlist.dart` hace lo mismo que Go, exportado desde `lib/datekeys.dart`. Sus vectores (`test/vectors/wordlist_vectors.json`) los genera Go con `tool/wordlist_go_vectors.go`: `List`, `CheckList` en 83 listas, el veredicto de cada punto de código de los planos 0, 1 y 14, `Generate` en 51 casos con los mismos bytes que Go, y `Bits` bit a bit (porta el `math.Log2` de Go). - El agente de Dart vio que `TestGenerate` de Go no probaba nada: su semilla solo daba dos índices, las dos llamadas acababan en `EOF` y el test comparaba dos resultados vacíos. Está arreglado en `96d8992`. - La lista española, `wordkey/lists/es.txt` (7 776 palabras, SHA-256 `ff77b487…34fe`), es un **borrador sin revisar**, CC BY-SA 4.0, sacado de las frecuencias de subtítulos de FrequencyWords filtradas con el diccionario de LibreOffice; el método está en `wordkey/lists/README.md` y el script en [wordlists/build_es.py](wordlists/build_es.py). Cumple el alfabeto tal cual. - **La revisión externa:** el paquete de [revision_externa/](revision_externa/NOTA_PARA_EL_AUTOR.md) está al día con la v0.15. Su nota dice qué decidió el autor y qué falta: elegir revisor, alcance y presupuesto, el NDA, los bundles y el envío. --- ## Qué queda La recuperación a largo plazo quedó hecha con la v0.15, y las palabras al azar en las tres librerías y en `/create` (7-10). **Para empezar mañana**, lo que depende del autor en ellas: la revisión de la lista española por alguien que hable español, o rehacerla con el corpus de Leipzig si su web ha vuelto, y el permiso para descargar la lista inglesa de la EFF. Sin eso, lo siguiente es el segundo punto de «Librerías y clientes»: las recomendaciones de la v0.14 y la v0.15 al SDK oficial. ### Protocolo 1. **La revisión externa.** El paquete está listo; lo que falta es del autor. Al recibir el informe, se abre la versión siguiente con sus hallazgos, el párrafo de idioma y precedencia del final del §1, las etiquetas del §76 que hoy llaman independientes a revisiones de IA, y el registro de la revisión (§75, punto 10). Todo eso lo aprobó el autor el 6-10; ver [revision_externa/precedence_and_language.md](revision_externa/precedence_and_language.md). Con ellos, dos retoques editoriales que el texto no puede llevar tras su tag: los de K17 del paquete (la cabecera «prevista», el §1 con la Release API y la Release Cache, el punto y coma del §74), y el §79, que nombra solo `drand/kyber-bls12381` cuando `scripts/recovery` usa también las interfaces de `drand/kyber`. 2. **Un archivo o servicio de caché de releases** (§50). El protocolo ya lo admite y las librerías leen un archivo local, pero nadie aloja uno todavía. Es decisión del autor: dónde, quién lo mantiene y si DateKeys ofrece el suyo. 3. **Medir el área de 32 KiB y cerrar el perfil CMS** (§74; §75, punto 13), con firmas reales con certificado de varios países y sellos de autoridades reales. Necesita firmas del autor o permiso para pedir sellos a una autoridad pública. 4. **Lo que el §74 aún llama provisional:** los esquemas de bytes de la cabecera, el control y la `.dkk`, los límites de los campos y los vectores definitivos del perfil. Conviene congelarlo con el informe de la revisión delante. 5. **El registro de perfiles firmado** (§71), que no existe. 6. **Más adelante:** un tipo de acceso post-cuántico, y quizá una derivación más dura que PBKDF2 para la llave de palabras. ### Librerías y clientes - **Las palabras al azar** (§38.1) están en Go, TypeScript, `/create` y Dart, con el SHA-256 fijado de cada lista, el alfabeto de su idioma y la fuerza calculada con la lista cargada (ver «Estado»). Ninguna lista se da por buena, tampoco las de DateKeys (el autor, 7-10: un empleado malicioso podría servir una lista con las mismas 6 palabras). Falta: - la lista inglesa de la EFF (7 776 palabras, CC BY): descargarla necesita permiso del autor. Con ella, `-dic` pasa a `en` por defecto, como decidió el autor, y hay que fijar el alfabeto de `en` en las tres librerías; - la lista española definitiva: el autor espera a que vuelva la web de la Universidad de Leipzig (el 7-10 daba un error de servidor), cuyos corpus de unos 256 idiomas se descargan con CC BY. El script sirve cambiando la fuente de frecuencias. Su web bloquea a los agentes (Anubis): descarga el autor. Cada lista la revisa alguien que hable el idioma antes de fijar su hash. En el borrador hay nombres propios, préstamos y palabras malsonantes, como «claudia», «green», «sport» o «cojonudo»: que los mire quien la revise, con `descartes.txt` delante; - los dados de quien desconfíe (decisión del 7-10): cinco tiradas, de 11111 a 66666, son una posición de la lista ordenada, porque 7 776 = 6⁵. No están en ninguna librería ni en `/create`; - el servicio `words.datekeys.com`, cuando haya sitio propio: ver las decisiones; - quizá el alfabeto por idioma como regla del SDK en la versión siguiente del spec (§38.1). El riesgo de fondo es la app misma, que puede cambiar el generador: §59, builds reproducibles y releases firmadas. - **Las recomendaciones de la v0.14 y la v0.15 al SDK oficial**, que no aplican ni `/create` ni la CLI de Go: - recomendar `time_and_key` para horizontes largos (§7.6); - avisar del estado de un perfil (§71); - explicar al crear cómo se abrirá la cápsula dentro de veinte años: el anexo de recuperación (§79) y los archivos de releases (§50). - **La página web, cuando se publique** (§59): builds reproducibles con sus hashes, Subresource Integrity y un cliente sin conexión. - **El localizador en las páginas:** las tres librerías lo tienen, pero `/create` no crea sobres e `/inspect` no descarga el resto. - **Go no tiene ninguna release del módulo**; la primera, cuando haya un repo accesible desde fuera para `go get`. govulncheck avisa de GO-2026-6443 en grpc, que el código no alcanza: subir grpc cuando salga la 1.85.0. - **Dart:** sin versión ni tag. Medir los tiempos en un móvil espera a la app. ### Espacio de trabajo - **Del autor:** renombrar la carpeta `App` a `datekeys-ts` y borrar `enquiry.php` de la raíz, pendientes desde el 29-09. - **`web` sigue sin remoto.** --- ## Decisiones del autor que siguen vigentes - **La app Flutter está archivada** «para un futuro» (6-10). No se empieza sin que lo pida. - **La revisión externa** (6-10): - primero, buscar un revisor que lea español; si no lo hay, traducir solo el núcleo normativo; - dos niveles de alcance, con presupuesto cerrado por nivel; - código entero en bundles de git cifrados con `age`; - un NDA mutuo; - la versión siguiente, después del informe. La v0.15 se adelantó a petición del autor (7-10). - **Las palabras al azar** (7-10): - listas de 7 776 palabras por idioma y 7 palabras por defecto; BIP-39 (2 048) se le queda pequeña al autor; - la app descarga la lista del idioma del usuario; la CLI las lleva dentro, inglés por defecto, y otra con `-dic es`, `-dic fr`; - **las palabras se sortean siempre en el dispositivo y nunca viajan**: el servidor no las genera ni las ve, ni cifradas. El autor propuso un servicio que las mandara cifradas con la clave de DateKeys y aceptó esto en su lugar; - `words.datekeys.com` solo servirá las listas públicas: ficheros estáticos con el hash en la URL, el SHA-256 fijado en la app o un índice firmado por DateKeys. La clave de DateKeys firma; no cifra nada. - **La app será auditable** (7-10): código publicado de todo lo que toca las llaves, builds reproducibles, releases firmadas y una auditoría externa (§59). Con eso, las palabras generadas por la app son la opción por defecto; los dados (diceware, lista numerada del 11111 al 66666 y publicada con su hash) quedan como opción para quien desconfíe, y las palabras propias con su aviso. Límite: en iOS la App Store vuelve a firmar y cifrar, y no se puede comprobar el binario; en Android sí (F-Droid). Dónde se publica el código está por decidir: ni GitHub ni el Gitea. - **Sin fichero `.dkr`** (7-10): el objeto release no se guarda junto a la cápsula ni tiene extensión propia. - **`locator.Open` sigue leyendo como mucho 1 MiB** del localizador (6-10). - **El cifrado tlock queda en `BigInt`, sin tiempo constante**, documentado en Dart y TypeScript («continúa», 6-10). - **El reporte del posible fallo de dart2js** al equipo de Dart, solo con permiso del autor. Está documentado en el README de `datekeys-dart`. - **Mostrar Q1 y Q10 de la revisión al equipo de drand** antes de pagar una revisión es decisión del autor, porque supone enseñar el proyecto fuera. - **El diseño de las páginas:** nunca un fondo oscuro, nada del estilo de la landing, usar la skill `frontend-design` sobre la página real y no gastar en maquetas. Ver la memoria. --- ## Para trabajar - **Subir al Gitea.** El nombre `g.activething.com` a veces resuelve a 89.46.247.16, que no responde. Entonces se sube a la IP de la red local, sin tocar el remoto: ```bash git -C push https://192.168.18.112/go/.git ``` y después `git -C update-ref refs/remotes/origin/ `. Los remotos son `go/DateKeys`, `go/DateKeys-App`, `go/dateKeys-dart` y `go/datekeys-doc`. El servidor no es del autor: no se propone ningún cambio en él. - **Cerrar una versión del spec**, como la v0.12 a la v0.15: 1. en Go, la cabecera del spec con «aprobado por su autor ese día» y nada más del texto; 2. `SpecVersion`; 3. `go run ./internal/testkit/genfixtures -out testdata`, y el campo `spec` a mano en los dos congelados, `security_cms.json` y `locator.json`; 4. el SHA-256 en `spec/README.md`, y los README, `SECURITY.md`, la trazabilidad, `testdata/README.md` y el CHANGELOG; 5. `scripts/check.sh`, con el árbol limpio tras el commit; 6. el tag anotado `spec-vX.Y` y `main` avanzado sin fusión (`git fetch . :main`); 7. en TypeScript y Dart, la versión del spec, `testdata` sincronizado con sus scripts, `mutation-texts.json` regenerado con Go en un módulo temporal (`G:\tmp\mutgo`), y el campo `spec` de los vectores propios que salen de Go. Tras aprobar no se añade texto normativo sin enseñarlo: fue el fallo E1 de la v0.11. - **Encargos a agentes:** - con `model: "opus"`; - en worktrees junto a los repos, para que `../datekeys-go` resuelva; - nunca dos agentes en los mismos ficheros; - solo ficheros nuevos, y los comunes en un último commit; - la sesión revisa contra Go antes de integrar. - **Herramientas de esta máquina:** - los heredocs de Bash rompen las barras invertidas, `\n` incluido; - la herramienta Write quita los espacios de final de línea (los saltos de Markdown del §77) y convierte `\uXXXX` en caracteres; - para cambios con barras o espacios exactos, un script de Python escrito con Write que use `chr(92)`, o la herramienta Edit; - en Windows, `write_text` de Python escribe CRLF: abre con `newline=''` o escribe bytes, o `gofmt` y los hashes fallan; - `cd` en Bash mueve el directorio de la sesión: usa `( cd … )` o `git -C`. - **Vista previa de la página:** la configuración `inspector-dev-app` de `.claude/launch.json` arranca `App`, mientras la carpeta no se renombre, e `inspector-preview-app` sirve su build de producción (`npm run build` antes, y reiniciarla tras cada build: si no, los chunks nuevos dan 404 y la página no se hidrata). No pares el servidor mientras el autor lo usa. `vite.config.ts` prepara las dependencias que se cargan bajo demanda (`optimizeDeps.include`), para que la primera apertura no recargue la página. - **Fuzzing:** usa `FUZZ_PARALLEL=4`. Un «context deadline exceeded» sin entrada que falle es del motor de Go, no del código; se vuelve a pasar. - **Disco:** C: está casi lleno con las cachés de Go y Dart; los temporales van en `G:\tmp`, y nunca se borra con comodines. --- ## Documentos El [README](README.md#documentos) los lista. Para retomar, lo principal es: - [revision_externa/](revision_externa/NOTA_PARA_EL_AUTOR.md): el paquete y su nota; - [REVISION_completitud_v0.13.md](REVISION_completitud_v0.13.md): qué falta antes de la v1.0; - [spec_v0.15/decisiones.md](spec_v0.15/decisiones.md): las decisiones de la última versión, y [diseno_recuperacion.md](diseno_recuperacion.md), su diseño.