- 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.
| `datekeys-go` | `v0.15` en `92e7154`; `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, `96d8992` arregla `TestGenerate`, `e671032` añade la lista inglesa de la EFF y `92e7154` los dados |
| `App` (`datekeys-ts`) | `v0.10` en `f5044e3`; `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 `00f4ad4` | 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 |
- **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 `92e7154`, 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`, `96d8992`, `e671032` y `92e7154`;
- TypeScript: `npm run verify` en `f5044e3`, con 8 127 pruebas;
- Dart: `tool/check.sh` en `00f4ad4`, con 2 243 pruebas en la VM y 712 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 inglesa** (7-10 por la tarde, con permiso del autor): `wordkey/lists/en.txt` es la lista grande de la EFF (Joseph Bonneau, 2016), 7 776 palabras, CC BY 4.0 según la política de copyright de eff.org, en su orden y sin los números de los dados, así que la posición de cada palabra sigue dando su número. `-dic` la toma por defecto, como decidió el autor. El alfabeto de `en` es de la `a` a la `z` y el guion ASCII de sus cuatro palabras compuestas (`drop-down`, `felt-tip`, `t-shirt` y `yo-yo`), para no quitar ninguna ni romper su numeración. Go `e671032`, TypeScript `13df99b` (la página sigue ofreciendo solo la española, y `check-build.mjs` exige que publique solo esa) y Dart `bf0f3d3`, con sus vectores regenerados con Go.
- **Los dados** (7-10 por la tarde, decisión del autor «sí, hazlo así»): para quien no se fía del azar del ordenador, cinco dados por palabra dan un número del 11111 al 66666, la posición de la palabra en la lista (7 776 = 6⁵), con el primer dado como cifra más significativa. Go `92e7154`: `wordkey.DiceNumber`, `DiceWord`, `DiceWords` (al menos 6 números y ninguna palabra repetida, que se vuelve a tirar) y `DiceList`, la lista numerada como la publica la EFF (la de `en` es su fichero byte a byte, SHA-256 `addd3553…903e`; la de `es`, `611f779a…fddb`, en `wordkey/lists/README.md`); `encrypt -dice TEXT` y `-dice-file FILE`, que muestran las palabras (las que abren la cápsula, no los números), y `datekeys wordlist [-dic LISTA]`, que escribe la lista numerada y su SHA-256. TypeScript `f5044e3`: lo mismo en `wordlist.ts`, y en `/create` la opción «Con dados» entre «Al azar» y «Las elijo yo», que convierte los números en palabras a medida que se escriben, avisa de un número que no vale o de una palabra repetida y ofrece la lista numerada para imprimirla, con su SHA-256. Comprobado en el navegador sobre el build de producción: la lista descargada tiene el SHA-256 de Go y la cápsula se crea con las palabras de los dados. Dart `00f4ad4`, de un agente Opus revisado contra Go: lo mismo en `wordlist.dart`, con vectores de Go de los dados (`DiceWords` con cada punto de código de los planos 0, 1 y 14 entre dos números, entre otros).
- 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.
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 cuando su web vuelva (el 7-10 por la tarde seguía caída). 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.
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.
- **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 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;
- 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.
- **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.
- 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.
- **`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.
- **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:
y después `git -C <repo> update-ref refs/remotes/origin/<rama> <rama>`. 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.
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 . <rama>: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.
- **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.
- [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.