23 KiB
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.
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 elCLAUDE.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-Byde los commits dicen qué modelo escribió cada uno. - Comprueba cada repo con
git statusygit log: todo lo de abajo estaba subido al cerrar.
Estado
| Repo | Rama y commit | Tags | Qué es |
|---|---|---|---|
datekeys-go |
v0.15 en 6158be2; 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, 92e7154 los dados y aefc8f6 lo que dice el SDK al sellar |
App (datekeys-ts) |
v0.10 en e8b5d35; 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 e2296b0 |
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.dkcni de.dkk. No hay ningún borrador abierto. Las versiones aprobadas y sus SHA-256 están endatekeys-go/spec/README.md. - El fichero
.dkrse 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/testdataenfe50885, 142 ficheros, los mismos que enaefc8f6, de donde TypeScript y Dart copian hoytestdata,wordlistsyannex. Gates el 7-10:- Go:
scripts/check.shentero enfe50885, con el anexo de recuperación abriendo fixtures sin código de DateKeys, yscripts/fuzz.sh 20senfe50885con sus 27 objetivos, sin fallos;scripts/check.shentero otra vez en27a75ee,96d8992,e671032,92e7154,aefc8f6y6158be2; - TypeScript:
npm run verifyen728c3bb, con 8 132 pruebas; - Dart:
tool/check.shene2296b0, con 2 250 pruebas en la VM y 716 en Node.
- Go:
- 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:
c49c67cañadewordkey.Generate,ListyCheckList, ydatekeys encrypt -new-words FICHERO [-dic es] [-word-count 7].27a75eehace queCheckList(lang, words)compruebe cada palabra contra el alfabeto de su idioma, que da el código y no la lista (paraes, de laaa laz,á,é,í,ó,ú,ü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ñadeBits, la fuerza de lo sorteado, queencryptmuestra (90 bits para 7 de 7 776).scripts/check.shpasa entero en los dos. - TypeScript (
0.5.0-dev, de012cd74a4057607):scripts/sync-testdata.mjscopia del mismo commit de Gowordkey/listsenwordlists/, con suSOURCE.json(testdataywordlistsen27a75ee, cuyotestdataes el del tag).wordlist.tshace lo de Go, con sus textos:readWordListsolo acepta una lista con el SHA-256 fijado para su idioma,checkWordList,generateWordsywordBits./createofrece 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.txtlleva la licencia de la lista.npm run verifypasa con 8 119 pruebas, y el flujo se comprobó en el navegador sobre el build de producción. - En
/createapareció 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. wordBitsde TypeScript usaMath.log2, que puede diferir delmath.Log2de Go en el último bit; la página muestra los bits truncados, como la CLI, así que no se nota.- Dart (
a770d4dafcc7780, de un agente Opus en un worktree, revisado contra Go):tool/sync_testdata.dartcopia tambiénwordlists/, ylib/src/wordlist.darthace lo mismo que Go, exportado desdelib/datekeys.dart. Sus vectores (test/vectors/wordlist_vectors.json) los genera Go contool/wordlist_go_vectors.go:List,CheckListen 83 listas, el veredicto de cada punto de código de los planos 0, 1 y 14,Generateen 51 casos con los mismos bytes que Go, yBitsbit a bit (porta elmath.Log2de Go). - El agente de Dart vio que
TestGeneratede Go no probaba nada: su semilla solo daba dos índices, las dos llamadas acababan enEOFy el test comparaba dos resultados vacíos. Está arreglado en96d8992. - La lista inglesa (7-10 por la tarde, con permiso del autor):
wordkey/lists/en.txtes 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.-dicla toma por defecto, como decidió el autor. El alfabeto deenes de laaa lazy el guion ASCII de sus cuatro palabras compuestas (drop-down,felt-tip,t-shirtyyo-yo), para no quitar ninguna ni romper su numeración. Goe671032, TypeScript13df99b(la página sigue ofreciendo solo la española, ycheck-build.mjsexige que publique solo esa) y Dartbf0f3d3, 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) yDiceList, la lista numerada como la publica la EFF (la deenes su fichero byte a byte, SHA-256addd3553…903e; la dees,611f779a…fddb, enwordkey/lists/README.md);encrypt -dice TEXTy-dice-file FILE, que muestran las palabras (las que abren la cápsula, no los números), ydatekeys wordlist [-dic LISTA], que escribe la lista numerada y su SHA-256. TypeScriptf5044e3: lo mismo enwordlist.ts, y en/createla 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. Dart00f4ad4, de un agente Opus revisado contra Go: lo mismo enwordlist.dart, con vectores de Go de los dados (DiceWordscon 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-256ff77b487…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á enwordkey/lists/README.mdy el script en wordlists/build_es.py. Cumple el alfabeto tal cual.
- Go:
- 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.mdendatekeys-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.RecoveryAnnexlo incrusta yTestRecoveryAnnexexige que sea el §79 del spec deSpecVersion: al aprobar una versión nueva se regenera conDATEKEYS_WRITE_ANNEX=1 go test -run TestRecoveryAnnex ..encryptlo escribe comoFICHERO.dkc.recuperacion.txt(-no-recoverylo evita), y/createlo ofrece para descargar con ese nombre. TypeScript y Dart lo copian de Go enannex/, como las listas. - Qué hará falta para abrirla (regla 26):
encrypty/createlo dicen al sellar: el.dkc, una credencial si estime_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 pararound_time(MAY) no se ha hecho. time_and_keypara horizontes largos (§7.6): a más de un año y contime_only, la CLI y/createla recomiendan.- Las palabras propias (§38.1):
/createy 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/StatusOfen Go,profileStatusOfen 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, ydecrypt,inspecte/inspectavisan si el de una cápsula está comprometido. - Go
aefc8f6y6158be2, TypeScript728c3bbye8b5d35, Dart8eed96fye2296b0(de un agente Opus revisado contra Go: el anexo es una constante generada,lib/src/recovery_annex.g.dart, que escribetool/recovery_annex_copy.darttras cada sincronización que cambieannex/). - Pendiente menor: el anexo es texto del spec, con licencia CC BY 4.0 (
spec/README.md), pero ni su cabecera ni el README de Go lo dicen.
- El anexo junto a la cápsula (§62.1, regla 27):
- La revisión externa: el paquete de revision_externa/ 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 cuando su web vuelva (el 7-10 por la tarde seguía caída). Sin eso, lo siguiente de «Librerías y clientes» es la página web cuando se publique (§59) o el localizador en las páginas.
Protocolo
- La revisión de Astra de la v0.15 (7-10), para la v0.16. Cinco hallazgos, todos comprobados y aceptados; la v0.15 no cambia. Tras su segunda respuesta, la dirección es:
- Sello sin
accuracy(§29.11): S4 solo con una cota de precisión conocida, la del token o la de una política de TSA que el lector tenga identificada (p. ej. una tabla de OID de políticas con su cota, que habría que fijar y mantener en el spec); sin ella el sello verifica pero no acredita anterioridad. Se descarta el margen fijo que propuso Claude: ningún margen finito es conservador para toda TSA. S5 pasa a «No acredita que se sellara antes de la fecha de apertura». - Anexo y llave de palabras (§79.6): el anexo incluirá la derivación de §38.1 (normalización, PBKDF2 con su sal y sus rondas, vector), con una receta exacta para un subconjunto explícito y comprobado (los alfabetos de las listas de DateKeys) y, para el resto, las tablas de Unicode 18.0.0 conservadas junto a la documentación (quizá un paquete de recuperación publicado una vez, con su SHA-256).
scripts/recoveryabrirá con palabras, con casos que ejerciten la normalización. - Último bloque de
age(§79.5): «el último puede ser más corto», y un vector con un último bloque completo.scripts/recoveryya lo hace bien. - Evidencias de la firma con certificado (§29.10, regla 21): guardar la cadena sin la raíz y las respuestas OCSP cuando quepan; si no caben, el escritor dice qué deja fuera, también de la TSA y de los intermedios, y permite exportarlo; §29.10 promete solo lo conservado. Ligado a la medida del área (punto 3).
- JSON de drand (§47.1): regla estricta, con vectores: nombres exactos tras decodificar sus escapes (
"\u0072ound"esround), un nombre repetido esERR_RELEASE_INVALID, la ronda un entero sin fracción ni exponente. Hoy Go y TypeScript leen comoencoding/json: el último gana y los nombres no distinguen mayúsculas. - Pendiente del autor: aprobar esta dirección y cuándo abrir la v0.16 (antes o después del informe de la revisión externa).
- Sello sin
- 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.
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-bls12381cuandoscripts/recoveryusa también las interfaces dedrand/kyber. - 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.
- 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.
- 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. - El registro de perfiles firmado (§71), que no existe.
- 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,
/createy 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.txtdelante; - los dados protegen de un generador trucado, no de una app que se quede con las palabras: eso solo lo cubre una app auditable (§59);
- 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 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
- Lo que queda de las recomendaciones al SDK: el aviso local para
round_time(§62.1, regla 26, MAY). - 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
/createno crea sobres e/inspectno 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
Appadatekeys-tsy borrarenquiry.phpde la raíz, pendientes desde el 29-09. websigue 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.comsolo 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.Opensigue 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-designsobre la página real y no gastar en maquetas. Ver la memoria.
Para trabajar
-
Subir al Gitea. El nombre
g.activething.coma veces resuelve a 89.46.247.16, que no responde. Entonces se sube a la IP de la red local, sin tocar el remoto:git -C <repo> push https://192.168.18.112/go/<repo-remoto>.git <rama>y después
git -C <repo> update-ref refs/remotes/origin/<rama> <rama>. Los remotos songo/DateKeys,go/DateKeys-App,go/dateKeys-dartygo/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:
- en Go, la cabecera del spec con «aprobado por su autor ese día» y nada más del texto;
SpecVersion;go run ./internal/testkit/genfixtures -out testdata, y el campospeca mano en los dos congelados,security_cms.jsonylocator.json;- el SHA-256 en
spec/README.md, y los README,SECURITY.md, la trazabilidad,testdata/README.mdy el CHANGELOG; scripts/check.sh, con el árbol limpio tras el commit;- el tag anotado
spec-vX.Yymainavanzado sin fusión (git fetch . <rama>:main); - en TypeScript y Dart, la versión del spec,
testdatasincronizado con sus scripts,mutation-texts.jsonregenerado con Go en un módulo temporal (G:\tmp\mutgo), y el campospecde 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-goresuelva; - 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.
- con
-
Herramientas de esta máquina:
- los heredocs de Bash rompen las barras invertidas,
\nincluido; - la herramienta Write quita los espacios de final de línea (los saltos de Markdown del §77) y convierte
\uXXXXen 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_textde Python escribe CRLF: abre connewline=''o escribe bytes, ogofmty los hashes fallan; cden Bash mueve el directorio de la sesión: usa( cd … )ogit -C.
- los heredocs de Bash rompen las barras invertidas,
-
Vista previa de la página: la configuración
inspector-dev-appde.claude/launch.jsonarrancaApp, mientras la carpeta no se renombre, einspector-preview-appsirve su build de producción (npm run buildantes, 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.tsprepara 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 los lista. Para retomar, lo principal es:
- revision_externa/: el paquete y su nota;
- REVISION_completitud_v0.13.md: qué falta antes de la v1.0;
- spec_v0.15/decisiones.md: las decisiones de la última versión, y diseno_recuperacion.md, su diseño.