- 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.16` y `main` en `b6ff17a`; `v0.15` en `70d907b` | `spec-v0.16` en `b6ff17a`, y los de las versiones anteriores | Spec v0.16 aprobada, implementación de referencia, `testdata` compartido. En `v0.15`, tras su tag: `c49c67c` las palabras al azar, `27a75ee` el alfabeto de cada lista y la fuerza de las palabras, `96d8992` arregla `TestGenerate`, `e671032` la lista inglesa de la EFF, `92e7154` los dados, `aefc8f6` lo que dice el SDK al sellar y `70d907b` la licencia CC BY-ND 4.0. En `v0.16`: el borrador (`a2b71c2`, `2c29625`), su implementación (`7e3b810` a `3fd0e93`) y el cierre (`b6ff17a`) |
| `App` (`datekeys-ts`) | `v0.10` y `main` en `2c305cf` | `v0.5.0` en `2c305cf`, `v0.4.0`, `v0.3.0`, `v0.2.0`, `v0.1.0` | Librería TypeScript `0.5.0`, publicada el 7-10, de la spec 0.16, y páginas `/inspect` y `/create`, con las palabras al azar |
| `datekeys-dart` | `v0.16` en `b53afdc`; `v0.15` en `e2296b0` | ninguno | Librería Dart completa, de la spec 0.16, con las palabras al azar; las ramas `v0.11` a `v0.15` se quedan atrás |
- **Spec:** la v0.16 está aprobada desde el 7-10, con SHA-256 `807d4fe85ac09ad6f97abc75ab3e2156bb2f3fb0dc589777f4420627fad545e1` y el tag `spec-v0.16` (`b6ff17a`, también `main`). Recoge la revisión de Astra de la v0.15: un sello sin `accuracy` no acredita nada antes de la fecha (S5, con su motivo); el anexo de recuperación deriva una llave de palabras y admite un último bloque de `age` completo; la firma con certificados guarda las cadenas y las respuestas OCSP que caben y dice lo que deja fuera; y el JSON de drand se lee estricto. No cambia ningún formato. La v0.15, la recuperación a largo plazo, sigue en `spec-v0.15` (`fe50885`). 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 `spec-v0.16` (`b6ff17a`), 150 ficheros, de donde TypeScript y Dart copian hoy `testdata`, `wordlists` y `annex`. Gates el 7-10 por la noche:
- Go: `scripts/check.sh` entero en `4f78854`, en `3fd0e93` y en `b6ff17a`, con el anexo abriendo con palabras y con un último bloque completo; en la v0.15, `scripts/fuzz.sh 20s` en `fe50885` con sus 27 objetivos, sin fallos;
- **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.
- **Lo que dice el SDK oficial al sellar (7-10 por la tarde):** los SHOULD de la v0.14 y la v0.15 que no cumplían ni la CLI ni `/create`.
- **El anexo junto a la cápsula** (§62.1, regla 27): `annex/recovery.md` en `datekeys-go`, el §79 del spec bajo un título con su versión y el SHA-256 de su texto, igual para toda cápsula. `datekeys.RecoveryAnnex` lo incrusta y `TestRecoveryAnnex` exige que sea el §79 del spec de `SpecVersion`: al aprobar una versión nueva se regenera con `DATEKEYS_WRITE_ANNEX=1 go test -run TestRecoveryAnnex .`. `encrypt` lo escribe como `FICHERO.dkc.recuperacion.txt` (`-no-recovery` lo evita), y `/create` lo ofrece para descargar con ese nombre. TypeScript y Dart lo copian de Go en `annex/`, como las listas.
- **Qué hará falta para abrirla** (regla 26): `encrypt` y `/create` lo dicen al sellar: el `.dkc`, una credencial si es `time_and_key`, y el release de su ronda, que tendrá que guardar un archivo o un servicio de caché si drand ya no lo sirve (§50). El aviso local opcional para `round_time` (MAY) no se ha hecho.
- **`time_and_key` para horizontes largos** (§7.6): a más de un año y con `time_only`, la CLI y `/create` la recomiendan.
- **Las palabras propias** (§38.1): `/create` y la ayuda de la CLI dicen que no bastan para algo valioso y que no se use una contraseña de otro sitio, porque pasada la fecha la cápsula permite probarla.
- **El estado del perfil** (§71): `profile.Status`/`StatusOf` en Go, `profileStatusOf` en TypeScript, con la tabla de los perfiles fijados que trae cada versión, porque el registro firmado no existe: Quicknet, activo. Con un perfil que no esté activo no se escribe ninguna cápsula, y `decrypt`, `inspect` e `/inspect` avisan si el de una cápsula está comprometido.
- Go `aefc8f6` y `6158be2`, TypeScript `728c3bb` y `e8b5d35`, Dart `8eed96f` y `e2296b0` (de un agente Opus revisado contra Go: el anexo es una constante generada, `lib/src/recovery_annex.g.dart`, que escribe `tool/recovery_annex_copy.dart` tras cada sincronización que cambie `annex/`).
- **La licencia** (7-10, decisión del autor): el spec y su anexo son CC BY-ND 4.0, no CC BY 4.0, que no le gustaba. Se pueden copiar y compartir sin cambios, citando su origen; una versión modificada o una traducción necesita su permiso por escrito. El anexo lo dice en su cabecera (`70d907b`). Las listas de palabras conservan las suyas.
- **La revisión externa:** el paquete de [revision_externa/](revision_externa/NOTA_PARA_EL_AUTOR.md) está al día con la v0.16. 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, las palabras al azar en las tres librerías y en `/create`, y la revisión de Astra con la v0.16, publicada en TypeScript como la `0.5.0`; el paquete de la revisión externa está al día (7-10).
**Decisiones que esperan al autor**, por orden de lo que desbloquean:
1.**La revisión externa** ([nota](revision_externa/NOTA_PARA_EL_AUTOR.md), «Orden», 4 a 6): elegir revisor, primero uno que lea español; el nivel, 1 o 1 y 2; pedir presupuesto; el NDA, los bundles y el envío. Antes, si se enseñan Q1 y Q10 al equipo de drand (gratis, pero enseña el proyecto fuera). Si el revisor no lee español, traducir el núcleo, unas 31 800 palabras, y decidir si con §47.1, §49 y §50.
2.**Medir el área de 32 KiB** (§74; §75, punto 13; K2 del paquete): firmas reales con un certificado del autor (DNIe, FNMT u otro) y permiso para pedir sellos a una autoridad pública. Cierra el perfil CMS y da código a las reglas 21 y 22 de la v0.16, que hoy no tienen ningún escritor.
3.**La lista española de palabras**: que la revise alguien que hable español, o rehacerla con el corpus de Leipzig cuando su web vuelva (la descarga el autor: bloquea a los agentes).
4.**Un archivo o servicio de caché de releases** (§50): dónde, quién lo mantiene y si DateKeys ofrece el suyo.
5.**Dónde se publica**: el código (ni GitHub ni el Gitea), la página web (§59) y con ello `words.datekeys.com`, el paquete de recuperación (decisión 5 de la v0.16) y la primera release del módulo de Go.
6.**Menores**: renombrar la carpeta `App` y borrar `enquiry.php` de la raíz (desde el 29-09); un remoto para `web`; el permiso para reportar el posible fallo de dart2js.
**Lo que sigue sin decisiones** («Librerías y clientes»): el aviso local para `round_time` (§62.1, regla 26, MAY), un objetivo de fuzzing para el lector estricto del JSON de drand, y el localizador en las páginas.
0.**La v0.16, cerrada (7-10).** Es la revisión de Astra de la v0.15, con las ocho [decisiones](spec_v0.16/decisiones.md) y su recomendación; el autor pidió implementarla («ponte con la implementación de la 0.16») y después cerrarla («sí, cierra la v0.16»).
- Go (`7e3b810` a `3fd0e93`, cierre en `b6ff17a`): el sello sin `accuracy` da S5 con su motivo (`capsule.SealReason`), y el escritor devuelve el veredicto del área que escribió (`Result.Security`, regla 19); el JSON de drand se lee estricto con un solo lector, también el de los relays; `scripts/recovery` abre con palabras (`-words`, y `-unicodedata` para otro texto que el de las listas), y sus dos normalizaciones dan las palabras de todos los casos de `wordkey.json`; dos fixtures nuevos, `format3_time_and_key_words` y `format3_full_chunk`; `security_cms.json` hecho de nuevo, 143 casos.
- TypeScript (`f79d8e2`, `59d7607`, `c74a37e`) y Dart (`290d97e`, `0609e9e`, `b53afdc`), de dos agentes Opus revisados contra Go. TypeScript usa también el lector estricto para un release pegado en la página. Dart regeneró sus vectores propios desde un `git archive` de Go.
- Un tropiezo: las herramientas de edición convierten un escape `\u0072` en el carácter. Los casos «escaped as» de `release.json` llevaban un `round` llano en `b570338`; lo vio el agente de TypeScript y se arregló en `3fd0e93`. Ver la memoria.
- No hay paquete de recuperación (decisión 5): el anexo nombra `UnicodeData.txt` por su SHA-256.
- **TypeScript 0.5.0** (7-10, «sí» del autor): tag `v0.5.0` en `2c305cf`, `main` movido; la v0.16, las palabras al azar con dados y lo que dice el SDK al sellar.
- **El paquete de la revisión externa** está al día con la v0.16: congela `spec-v0.16` (`b6ff17a`), TypeScript `v0.5.0` y Dart `b53afdc`; gana Q13, sobre los cambios de la v0.16, y cierra K17. Ver su [nota](revision_externa/NOTA_PARA_EL_AUTOR.md).
- Las reglas 21 y 22 de la v0.16 (guardar las cadenas y las respuestas OCSP, decir lo que queda fuera) no tienen código: ningún escritor de las librerías pide sellos ni firmas con certificado todavía.
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).
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.
- **La licencia del spec es CC BY-ND 4.0** (7-10): copiar y compartir sin cambios, citando su origen; una versión modificada o una traducción, solo con su permiso. El código sigue siendo Apache-2.0.
- **`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.