# Handoff DateKeys, 26 de septiembre de 2026 (actualizado al cerrar la sesión del 5 de octubre: Go y TypeScript al día con el borrador v0.12, y las etapas 0 y 1 de Dart)
Sirve para retomar sin contexto previo, sea una persona o una sesión de Claude. Las secciones 0 a 4, al final, son el historial; el estado actual es este apartado.
**Antes de empezar:** comprueba que la sesión corre en Opus. La del 1-10 por la tarde y noche 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.
Corrió en Opus 5.5. Se abrió en `App` y a los pocos minutos pasó a la raíz con la herramienta de carpetas de la app, sin cerrarse. Hizo los puntos 1 a 4 de la lista de abajo:
- **Dart, etapa 2:** hecha y revisada, commits `0c21f55` a `82fa714`.
- **Dart, etapa 3:** el autor pidió adelantarla, y se hizo en paralelo con la 2, en un worktree aparte. Está revisada e integrada encima de la 2, commits `cdd1c89` a `b81bdfa`. El worktree y su rama ya están borrados.
Las revisiones de las dos etapas están en [PLAN_dart.md](PLAN_dart.md), al final; ninguna encontró fallos.
**Estado al cerrar:**
-`datekeys-dart`: rama `v0.11` en `b81bdfa`, limpia, solo en local. El gate pasa, con 367 pruebas en la VM, más una aplazada, y 113 en Node.
-`datekeys-go` y `App` no se tocaron.
- G: tiene 26 GB libres desde el 5-10, porque el autor liberó unos 24 GB.
La etapa 4 está completa: 4a, 4b y 4c hechas, revisadas e integradas en `v0.11`, hasta `8e0e993`. La salida `-dart` del generador de tablas está en `c531e93` de `datekeys-go`, subido.
La etapa 5 se reparte en 5a, 5b y 5c, descritas en [PLAN_dart.md](PLAN_dart.md), al final. La 5a trabaja en el worktree `datekeys-dart-stage5a`, rama `stage5a`, y la 5b en la copia principal. Si la sesión se cierra a medias:
1. mira qué commits hay en `v0.11` después de `8e0e993` y en `stage5a`;
**Para empezar la sesión siguiente.** La del 5-10 se cerró porque tenía el contexto casi lleno.
1. Abre la sesión en `G:\bussines\datekeys`, la raíz, no en `App`, y comprueba que corre en Opus.
2. Comprueba que los repos están como dice este apartado (`git status` y `git log` en cada uno). Todos estaban limpios al cerrar:
-`datekeys-go`: rama `v0.12` en `424cdf3`, subida;
-`App`: rama `v0.10` en `289fe71`, subida;
-`datekeys-dart`: rama `v0.11` en `c8bf3c0`, solo en local;
-`docs`: en el commit de este handoff, solo en local.
3.**Dart, etapa 2.** Se encargó a un agente y se detuvo antes de que escribiera nada en el repo. Hay que relanzarla con el encargo de [PLAN_dart.md](PLAN_dart.md), apartado «Cómo se hacen las etapas», revisar su resultado y pasar `tool/check.sh`.
4.**Dart, etapa 3**, BLS12-381 y tlock: el autor la pidió el 5-10 para después de la 2.
**Qué pasó.** Una sesión en Opus, abierta otra vez en `App`, hizo los puntos 2, 3 y 4 de la lista del 2-10: lo que faltaba de Go, la segunda parte del TypeScript y las etapas 0 y 1 de Dart. El autor decidió además una ampliación del borrador v0.12. El trabajo grande se repartió entre agentes Opus en worktrees y repos aparte, y cada resultado se revisó e integró en la sesión.
-`da9fd2c`: arreglo editorial del borrador. Faltaba una línea en blanco antes de cinco `---`, y dos párrafos, el final del §70 y el del §72, se veían como títulos. La v0.11 etiquetada tiene el mismo fallo y no se toca.
-`ea5c6eb`: `locator.json` con los casos del §64 de la v0.11 y la v0.12:
- 217 direcciones: la primera y la última de cada bloque IPv4, con sus vecinas públicas; los bloques IPv6; los nombres locales; los caracteres fuera de RFC 3986; los segmentos `..`; y los CID que no decodifican;
-`mixed`: un localizador con una dirección `http` y otra de NAT64, que se rechazan, y una tercera que se usa;
-`rest_cases`, `extension_cases`, con el localizador de otra ronda, y `plaintext_cases`, con el caso del cambio 7;
- las bases de relleno 3837, 4070, 4094 y 4095 y las del múltiplo siguiente: la spec pide ocho, y la lista del 2-10 daba seis.
El generador pasa a `internal/testkit/genfixtures/locatorvectors.go` y comprueba cada caso contra Go.
-`e22d358`: `testdata/README.md` al día, y sin marcas *pending* en `docs/traceability.md` ni en el CHANGELOG. El README decía que el release de la ronda 1000 estaba en `quicknet_rounds.json`, y no estaba.
-`601e6d2`, con la **decisión del autor del 5-10** («sí, aplícalo así»): el §44.1 escribe lo que Go ya rechazaba y el texto no decía, y el cambio 6 del §76 y el §64 lo recogen:
- segmentos de 1 a 63 caracteres, sin guion en los extremos;
- el esquema en minúsculas, y `0x` y los nombres locales sin distinguir mayúsculas;
- IPv4 sin ceros a la izquierda y CID en su forma canónica;
- el puerto sin ceros a la izquierda: Go aceptaba `:0443` y `:00443`, y `checkPort` ahora los rechaza.
`locator.json` pasa a 247 direcciones.
- **Verificación:** `scripts/check.sh 60s` completo sobre `e22d358`, y sobre `601e6d2``check.sh` sin fuzzing y `fuzz.sh 60s`, los 25 objetivos: todo limpio. Cobertura de `capsule`, 92,2 %. govulncheck da los dos avisos de siempre, sin llamada alcanzable.
-`997f331`: la regla de los codificadores del §72 (`checkWrite` en `extension.ts`, con `NOTE_ID` y `CAPSULE_ID`), que aplican el escritor de cápsulas y el de `.dkk` con los textos de Go; `checkNoteData` y `unusableNote`. Comprobado también contra el `testdata` de `spec-v0.11`.
-`95329ee`, el borrador v0.12, en un solo commit porque nada pasaba por separado:
-`testdata` en `601e6d2`; `SPEC_VERSION` sigue en 0.11;
- el lector de certificados campo a campo, los OID por bytes, los SET OF con repetidos y los casos límite: T2, T6, T7, T8 y lo de TypeScript de E7, E8 y E9;
- los textos de los veredictos de la v0.12: E2, E3 y E4;
-`security.json` y los 135 casos de `security_cms.json`, comparados campo a campo, con sus líneas;
- los textos de las 218 mutaciones y los vectores IBE de los fixtures nuevos;
-`note.json`, y la nota pública en `inspect`, que la lee bajo demanda.
El port del CMS lo hizo un agente: un diferencial suyo de 63 623 áreas no dio ninguna diferencia con Go, y el código anterior difería en 13 296 de 42 986.
-`289fe71`: README y CHANGELOG.
- **Verificación:** `npm run verify` (7 759 pruebas, cobertura y build con sus guardas) y `testdata:check` contra Go.
**`datekeys-dart`, rama `v0.11`, solo local (no tiene remoto):**
-`5aa5eed`, etapa 0:
- el paquete `datekeys`, Dart 3.13, sin publicar;
-`testdata` en `spec-v0.11`, 124 ficheros, con el mismo `SOURCE.json` que escribe el TypeScript;
-`tool/sync_testdata.dart`, pruebas de integridad y de versión, y `tool/check.sh` como gate;
- licencia Apache-2.0;
- dependencias: `crypto` en ejecución y `test` en desarrollo, aprobado por el autor el 5-10.
-`da63bc3` a `10b0895`, etapa 1, también de un agente:
- errores del §69;
- bytes: UTF-8 estricto propio, porque el `Utf8Decoder` de Dart quita un U+FEFF inicial; y el `%q` de Go con su tabla `IsPrint`;
- el códec CBOR de `codec.go`;
- el DER de la v0.12.
Los enteros son exactos en la VM y en la web: un `int` hasta 2^53 − 1 y un `BigInt` por encima. Hay 169 pruebas en la VM y 56 también en Node, y una comparación con Go de 263 611 casos no dio ninguna diferencia.
1.**El autor aprueba el borrador v0.12:** §76, cambios 1 a 9, con el 6 ampliado y el 3 precisado el 5-10. Sigue abierto si se mide antes el área de 32 KiB con firmas reales. El 5-10 el autor dejó las dos cosas para más adelante («la aprobación y la medida, luego»). Con la aprobación: el SHA-256 en `spec/README.md`, `SpecVersion` 0.12, el tag `spec-v0.12` y `main` avanzado sin fusión, siempre con permiso del autor.
Decidido el 5-10 («sí a las dos»):
- **«Con texto» en el §29.7:** el titular toma `givenName` y `surname` solo si los dos tienen texto no vacío, y el emisor toma el `organizationName` si no tiene un `commonName` con texto. Es lo que ya hacían Go y el TypeScript. Está en `424cdf3`, subido.
- **`dart test -p node` en el gate de Dart:** `c8bf3c0`.
2.**Dart, etapa 2:** las primitivas y `age`, según [PLAN_dart.md](PLAN_dart.md). Se encargó el 5-10 y se detuvo sin escribir nada al cerrar la sesión: hay que relanzarla.
3.**Dart, etapa 3:** BLS12-381 y tlock, pedida por el autor para después de la 2.
4.**TypeScript, sin urgencia:** el localizador, que aún no está portado; que la página muestre la nota pública, que ya lee `inspect`; y lo pendiente del §2.5.
- El error de una nota pública con un tabulador dice «in the declared author», porque la nota usa las reglas del autor declarado. Está en `note.json` y el TypeScript lo copia: cambiarlo exige tocar los dos lados.
-`testdata` no tiene vectores compartidos de la llave de palabras. El §64 de la v0.11 los pide, y solo están en las pruebas de Go (`wordkey.TestKeyVector`). TypeScript y Dart los van a necesitar.
**Qué pasó.** La sesión que cerró la v0.11 (1-10, de 19:42 a 01:05) corrió con Sonnet 5.5. El 2-10 se revisó todo lo que hizo con seis revisores de Opus: [spec_v0.11/revision_sesion_1_2_octubre.md](spec_v0.11/revision_sesion_1_2_octubre.md).
- **Resultado:** nada bloqueante. El núcleo criptográfico estaba bien.
- **Fallos encontrados:**
- cómo se puso el tag;
- la paridad entre Go y TypeScript con los certificados;
- unos vectores que cubrían mucho menos de lo que decían;
- la presentación (un nombre de certificado podía imitar una línea de veredicto);
- comprobaciones del escritor que faltaban;
- documentación desfasada.
- **Arreglos:** casi todos hechos el mismo 2-10. Lo pendiente está abajo.
**Decisión aplicada (E1).** El tag `spec-v0.11` no se toca: es una versión cerrada. Los cambios de texto normativo van a un **borrador v0.12 sin aprobar**: `datekeys-go/spec/DateKeys_Protocol_Specification_v0.12.md`, cuyo §76 «Cambios normativos de la v0.12» lista los nueve cambios con su caso.
**Repositorios**
- **`datekeys-go`:** la rama **`v0.12`**, subida, implementa el borrador v0.12. `main` y el tag `spec-v0.11` siguen en `ae33434`, sin tocar, y `SpecVersion` sigue en 0.11 hasta la aprobación.
- **`App` (`datekeys-ts`):** la rama `v0.10`, subida, con cinco commits nuevos (`06e992f` a `fc5fb24`). Su `testdata` sigue sincronizado con `ae33434` (`spec-v0.11`).
- **`docs`:** local. La revisión está en `d60b856`, los planes de Dart y de la firma en `ac47bf6`, y este handoff en el commit siguiente.
**Hecho el 2-10 en Go (rama `v0.12`)**
- **Claves de autor, escritor, CLI, extensiones y localizador** (`fee531b`):
-`Key.String()` oculta la clave secreta, y `ParsePublic` exige un punto de la curva;
- un nil con tipo da error;
- un pánico al evaluar la firma o el sello solo afecta a su parte (F1 o S2);
-`OpenOptions.Accept`;
-`extension.CheckWrite`, la regla del encoder del §72;
- en la CLI, `encrypt -sign` muestra el código; `-expect-author` no escribe nada si la clave no coincide; las líneas de veredicto se parten con ` ↳ `; y se avisa de una nota inutilizable;
- en el localizador, las direcciones no públicas de IANA y los nombres locales se rechazan, se descarta una dirección mala sin perder las demás, y `ParseInfo` ata el localizador a su ronda.
- **Lector CMS** (`839e117`, `b06ab4f`):
- un perfil de certificado propio, campo a campo, sin `encoding/asn1` ni `crypto/x509`;
- los OID se comparan por sus bytes, y un SET OF puede repetir elementos;
- una clave que no casa con su algoritmo es F2, y un imprint de otra longitud es S3;
- los tiempos DER se comprueban, y NumericString y los demás tipos de cadena son DER válido;
- el titular sale de `givenName` y `surname`, delante del CN que lleva el NIF.
- **Borrador v0.12 y textos de los veredictos** (`840c87e`):
- los nombres de certificado van entre «», con 64 puntos de código como máximo y sin espacios dobles;
- la línea de cada firmante de F6 nombra su autoridad de sellado, seguida de «DateKeys no comprueba quién emitió los sellos.»;
- el resultado de un firmante ajeno sale en español.
- **Datos de prueba:**
- el generador vuelve a escribir los fixtures de la v0.10 con 512 bytes (`EncryptOptions.TestAreaLen`, solo con `TestVectors`), y `-force` ya no rompe (`e597760`);
-`format3_seal_unsupported` usa `seal_type` 4294967295, y hay `format3_unsigned` (32 KiB y la misma P que la firmada), `format3_note`, `security.json` con contexto y líneas (`aaf0f41`), y 8 mutaciones nuevas de `alg` 1, del área y de la nota (`6c78c94`, 218 casos);
-`note.json` (`88ccbfd`);
-`security_cms.json`, que pasa de 22 a 135 casos con sus `lines` (`edff5d1`).
- **Pruebas y fuzzing:**
- pruebas negativas de cada regla del lector CMS: de 949 mutantes, los 91 que sobreviven son equivalentes (`4ce4d59`);
- siete objetivos de fuzzing nuevos, del localizador, de DER y de CMS (`ec08ec9`, `ad40329`);
- pruebas de la regla 25, de la nota inutilizable y de la recuperación ante un pánico (`9b1123d`).
- **Documentación** (`858c5f1`, `2aff4a0`): los README, `SECURITY.md`, `docs/traceability.md` y el CHANGELOG, puestos en la v0.11 y el borrador v0.12.
- **Verificación:** `go test ./...` en verde en `ad40329`. `scripts/check.sh` pasó entero sobre `2aff4a0` (`-race`, cobertura de `capsule` 91,6 %, govulncheck y vectores reproducibles). Su fuzzing se paró tras tres objetivos limpios, para cerrar la sesión. Los objetivos nuevos se pasaron aparte, de 40 a 60 s cada uno, limpios.
**Hecho el 2-10 en TypeScript (rama `v0.10` de `App`)**, con `npm run verify` en verde (7 432 tests): lo que no dependía del borrador v0.12.
- T1: el emisor que incumple las reglas se muestra con el hash de su nombre.
- T3: solo claves EC sin comprimir.
- T4: la nota con UTF-16 mal formado se rechaza.
- T5: ningún decodificador quita un BOM.
- T9: `testVectors` y `areaLen` salen del API público y pasan a `testing/`.
- T10: `capsule-vectors.json` regenerado con 32 KiB y nota; Go lo abre.
- T11 y T12: la longitud con nota, y el formato 2 sin nota ni área.
- T13, en parte: `evaluateSecurity` no lanza nunca, y los lectores de OID son lineales.
1.**El autor revisa y aprueba el borrador v0.12** (§76, cambios 1 a 9). Las decisiones que contiene, todas con la recomendación de la revisión:
- las líneas de F6 con la autoridad y el aviso;
- las reglas de los nombres de certificado y la marca ` ↳ `;
-`givenName` y `surname` delante del CN;
- la lista de bloques no públicos de IANA y de nombres locales;
- el perfil del certificado;
-`accuracy` hasta 2³¹ − 1;
- los tamaños del localizador en el CDDL.
Queda también abierto si se quiere medir el área de 32 KiB con firmas reales antes de cerrar (pregunta 6 de la sesión anterior). Con la aprobación: el SHA-256 en `spec/README.md`, `SpecVersion` 0.12, el tag `spec-v0.12` y `main` avanzado sin fusión, siempre con permiso del autor.
2.**Go, lo que falta:**
- **`locator.json` ampliado:**
- en `uri_cases`, una dirección de cada bloque no público, los nombres locales, los caracteres fuera de RFC 3986, los segmentos `..` y un CID que no decodifica;
- un localizador con una dirección rechazada y otra válida;
- otra ronda, un resto con otro SHA-256 y un recurso que sigue tras el resto;
- las bases de relleno 3837, 4094, 4095, 7933, 8166 y 8191.
El generador está en `internal/testkit/genfixtures/cmsvectors.go`. Es un vector congelado: se borra y se regenera.
- **`testdata/README.md`,** que sigue en la v0.10. Debe recoger:
-`security.json` con contexto y líneas;
-`note.json`;
-`format3_unsigned` y `format3_note`;
- las 218 mutaciones;
-`security_cms.json`, con el texto que dejó su agente: está en el informe de su commit `edff5d1` y se puede rehacer leyendo el generador;
-`locator.json`, cuando se amplíe.
- Las marcas *pending* de `docs/traceability.md` y el apartado «Pending in this branch» del CHANGELOG.
- Un `scripts/check.sh 60s` completo.
3.**TypeScript, segunda parte:**
- **Datos:** sincronizar `testdata` con la cabeza de `v0.12`.
- **Lector CMS:** portar el perfil de certificado de la v0.12 (T2, T6, T7, T8), los OID por bytes, los SET OF con repetidos, NumericString y los demás tipos de cadena como DER, y los casos límite (E9).
- **Textos:** los nuevos de los veredictos (comillas, autoridad de cada firmante, aviso, resultados en español, `t` en RFC 3339 con fracción), el titular por `givenName` y `surname`, y las reglas de 64 puntos de código y espacios dobles.
- **Escritor:** la regla de `CheckWrite`.
- **Vectores y fixtures:** `security.json` con su estructura nueva; `note.json`; los fixtures `format3_unsigned` y `format3_note`, y los `ibe-vectors` de los fixtures nuevos o cambiados; las 8 mutaciones; los 135 casos de `security_cms.json` con sus líneas.
- **Pruebas:** la tautología de `cms.test.ts:106`.
El TypeScript no tiene todavía el localizador. Es una función pendiente, no un fallo.
4.**Dart:** el plan está en su v2, [PLAN_dart.md](PLAN_dart.md), con las tres decisiones del autor del 2-10: solo `package:crypto`, primero la lectura y paquete `datekeys`.
- Se empieza por la etapa 0, el esqueleto con `testdata` sincronizado. Para la etapa 4, el generador `pathrule/gen` necesita una salida `-dart`.
- La etapa 5, la de firmas y sello, se sincroniza con `v0.12`, no con `spec-v0.11`.
-`scripts/check.sh` en `datekeys-go`: los tests con `-race` de `capsule` tardan minutos. Si lo paras, mata también `fuzz.sh` y los procesos `*.test.exe`, que quedan huérfanos.
-`npm run verify` y `npm run testdata:check` en `datekeys-ts`.
- **El generador:**
-`go run ./internal/testkit/genfixtures -out testdata` no toca los fixtures que ya existen, ni sus registros.
- **No uses `-only mutations`:** regenera los 33 casos congelados del corpus. Para añadir mutaciones basta una ejecución normal.
- Los vectores congelados (`security_cms.json`, `locator.json`) se rehacen borrándolos.
- **Herramientas de esta máquina:**
- Los heredocs de Bash rompen las barras invertidas y los apóstrofos.
- La herramienta Write convierte `\uXXXX` en caracteres literales; en Go, escribe esos escapes con un script.
-`go test … | tail` esconde el código de salida: mira `$?` antes de hacer commit. Una vez se hizo commit con una prueba rota (`e597760`, arreglada en `0f51af7`).
- **Agentes:** el trabajo en paralelo en Go se hizo con agentes en clones propios del scratchpad y ficheros repartidos, integrados con `git fetch` y `cherry-pick`. Los subagentes, con `model: "opus"`.
## 0. Reorganización del espacio de trabajo (29-09, mediodía)
`G:\bussines\datekeys` se reorganizó el 29-09. El mapa de carpetas y las reglas del proyecto están ahora en el [README](README.md) de este repo. Los cambios:
- **Documentos:** los del proyecto, este incluido, pasaron de `App/docs` a este repo, `docs`, que es privado y no tiene remoto. Su primer commit es `6e8d6c6`. Los papeles de trabajo de la v0.9, que solo estaban en una carpeta temporal, están en [spec_v0.9/](spec_v0.9/README.md).
- **`App`:** quitó `docs/`, y el paquete, el README y el aviso de licencias del sitio pasan a llamarse `datekeys-ts` (`5ee8813`, subido). `npm run verify` sigue en verde, con 2 611 tests.
- **Archivo y logos:**
-`AppOld` pasó a `archive/prototype`, sin cambios;
- los borradores v0.1 a v0.8.1, que estaban en Descargas, pasaron a `archive/spec-drafts`, con los hashes comprobados;
- los logos pasaron a `brand/`.
- **Claude:**
- la memoria de Claude es ahora la de la raíz. La de `App` queda congelada, con una nota que remite a la nueva;
- en la raíz hay un `CLAUDE.md`, que carga el README, y un `.claude/launch.json` para las vistas previas.
- **Nombres:** en las secciones siguientes, `App` es el repo que ahora se llama `datekeys-ts`. Su remoto sigue siendo `go/DateKeys-App`.
Pendiente del autor, antes de seguir:
1. Cerrar la sesión abierta en `App` y renombrar la carpeta a `datekeys-ts`. Windows no deja renombrar una carpeta en uso.
2. Abrir las sesiones siguientes en `G:\bussines\datekeys`. La primera comprueba `npm run verify` y `npm run testdata:check` en `datekeys-ts`.
3. Borrar `G:\bussines\datekeys\enquiry.php`, que es una copia idéntica de `web/static/api/enquiry.php`. Claude no tiene permiso para borrarlo.
4.`docs` y `web` solo existen en esta máquina. Para tener una copia fuera de ella, hay que darles un remoto.
Los repos con remoto están en el Gitea privado `g.activething.com` (solo LAN, certificado autofirmado para `git.activething.com`; git funciona, `curl` necesita `-k`). **Ese servidor no es del autor**: no se proponen cambios en él. La integración continua es el gate local.
| `datekeys-go` (`go/DateKeys`) | `main` | `cc35d2c` | Spec v0.10, entrega 1 del formato 3, aprobada por el autor el 30-09 y cerrada con el tag anotado **`spec-v0.10`** en `cc35d2c`; SHA-256 del spec `7f26419a444aa3e89a3aa8afbbba9d952af69e048aee1e93cd70732c2d1d99d1`. Implementa el formato 3: lo escribe y lee los formatos 1 a 3, con `SpecVersion` 0.10. `scripts/check.sh 60s` limpio. La v0.9 sigue en su tag, `spec-v0.9` (`7e2d83c`), y la v0.8.2 en el suyo, `spec-v0.8.2` (`9ac9cd9`). |
| `datekeys-ts`, antes `App` (`go/DateKeys-App`) | `main` | `da26862` | Desde `5ee8813`, sin `docs/` y con el paquete `datekeys-ts` (§0). Versión `0.2.0-dev` desde `8c08c97`, con `v0.1.0` cerrada en `d577283`; implementa el spec 0.9 desde `0118890` (`VERSION` y `SPEC_VERSION`, también en el pie de la página). Escribe el formato 2 y tiene la página `/create` (fase 3, §2.5). Librería TypeScript con la inspección (pasos 1 a 8) y la apertura (pasos 9 a 18) de los formatos 1 y 2, en memoria o desde un `Blob` hacia un stream de salida, como un fichero OPFS. Página `/inspect` con la acción "abrir", que muestra el formato y avisa del 1. `testdata` sincronizado a `7e2d83c` (`spec-v0.9`). Fase 2 completa (pasos 2 a 8), incluidos el cifrado tlock y la interoperabilidad de TypeScript a Go. Los 125 casos del corpus pasan por `open` con el código, el paso y el texto de Go. En `0118890`, 5 425 tests, ninguno saltado; la fase 3 añadió los suyos, con `npm run verify` en verde en cada paso (§2.5). |
-`scripts/check.sh 60s` sobre `9ac9cd9`, el commit del tag, limpio (28-09), y sobre el estado de `6fa2b54`, con tres objetivos de fuzzing nuevos del formato 3, limpio (30-09);
- govulncheck no encuentra nada alcanzable. Avisa, sin llamada alcanzable, de dos problemas:
- GO-2026-6443, en `google.golang.org/grpc` 1.84.0, arreglado solo en una versión `-dev`. Hay que subir grpc cuando salga la 1.85.0.
- GO-2026-5932, en `golang.org/x/crypto/openpgp`, sin arreglo. No lo importamos.
---
## 2. Qué queda, en orden
### 2.1 Hecho el 28-09: cierre de la v0.8.2
- Los 9 retoques de la 2.ª revisión están en `9ac9cd9`, un solo commit sobre `c57ed48` que absorbe el WIP `69a3342`. La rama WIP está borrada.
- §76 los recoge como correcciones 4 a 6:
- un encoder MUST NOT escribir una extensión fuera de su registro;
- §17 y §51 dan los códigos del paso 10 solo para un release suministrado directamente;
- en el paso 9, cualquier fallo de la fuente es `ERR_RELEASE_UNAVAILABLE` y ningún otro código.
- La regla del encoder no se comprueba en la librería: `capsule.Encrypt` y `accesskey.Encode` no reciben `Registry`, y la aplica la aplicación. Está documentado en `extension.Placement`.
- El autor aprobó el texto el 28-09. El spec lleva esa fecha, y el SHA-256 de sus bytes LF, `5cfa3203…`, está en `spec/README.md` y en el mensaje del tag.
-`main` avanzó por fast-forward hasta `9ac9cd9` y lleva el tag `spec-v0.8.2`. Los dos están subidos.
-`App` sincronizó `testdata` con `9ac9cd9` en `71ab8fb`. Solo cambia `testdata/README.md`.
Plan: `docs/PLAN_fase2_ibe_noble2.md` v2, con las decisiones confirmadas por el autor. Los pasos 2 a 8 de su sección 10:
- hecho el 28-09 (`74e1215`): `age-encryption` 0.3.1 y noble 2.4.0 como dependencias de ejecución, con sus guardas en `src/lib/dependencies.test.ts` y `check-build.mjs`. El README de `App` recoge el coste medido en el bundle, 73 KB con gzip para todo, y `npm audit`;
- hecho el 28-09 (`35be27c`): `ibe.ts` sobre noble 2, con vectores de Go en `src/lib/dkc/testing/ibe-vectors.json` (`scripts/ibe-go-vectors.go`) y cobertura del 100 % fijada como umbral. Los argumentos del stanza y su paso al `Stanza` de `age-encryption`, que guarda el tipo en `args[0]`, van al paso 7;
- hecho el 28-09 (`076f3db`): `release.ts`, con `verifyRelease` en el orden y con los textos de `provider.Verify`, `ReleaseSource` y `suppliedRelease`. Solo verifica el scheme de Quicknet: otro da `ERR_UNKNOWN_PROFILE`. Reproduce los 7 casos del corpus que fallan en el paso 10;
- hecho el 28-09 (`97827ae`), paso 5a: `open.ts`, los pasos 9 a 18 con el texto en claro en memoria. Las identidades estrictas de `agewrap` se apoyan en `x25519.ts`, que abre los stanzas de uno en uno. Usa `@noble/ciphers` 2.4.0, dependencia directa aprobada ese día: es la copia que ya usa `age-encryption`. Las identidades `AGE-SECRET-KEY-1…` se leen con `bech32.ts`. `index.ts` no reexporta aún la apertura, que metería noble en `/inspect`;
- hecho el 28-09 (`a89bee5`), paso 5b. `open` acepta un `Blob` del que solo lee el prefijo de los pasos 1 a 8 (`prefix.ts`, movido de la página a la librería), calcula el `capsule_digest` en streaming (`digest.ts`) y descifra `PAYLOAD_AGE` en streaming. La salida puede ser un `WritableStream`, que se cierra tras el paso 18 y se aborta ante cualquier fallo. Comprobado en el navegador con OPFS real: un fallo de STREAM deja intacto el fichero;
- el paso 6 está cubierto: la enmienda de canonicidad entró en la v0.8.2;
- hecho el 28-09 (`66970cf`), paso 7: `encryptOnG2RFC9380` y `timeRecipient`. Con sigma fijo, el cifrado reproduce byte a byte los vectores de Go. Go abrió los cuerpos IBE y los ficheros `age` que cifró esta librería, con `tlock.TimeUnlock` y con `agewrap.NewTimeIdentity`. Todo está en `src/lib/dkc/testing/tlock-vectors.json`;
- hecho el 28 y 29-09 (`48d6704`), paso 8: la acción "abrir" en `/inspect`, cargada bajo demanda, con la política aprobada por el autor el 28-09 tras revisar lo que dice el protocolo:
- el release lo pega quien abre, o sale del registro del fixture, y la página nunca lo pide a la red;
- el texto en claro de un fichero propio va a un fichero temporal de OPFS (§56), que se borra;
- los avisos de licencia se publican en `licenses.txt`.
Una revisión adversarial confirmó 15 hallazgos, todos corregidos. Los detalles y las comprobaciones en el navegador están en la fila 8 del plan.
La lectura del protocolo del 28-09 deja dos SHOULD para más adelante: §48 (varios relays) y §49 (obtener el release directamente del proveedor). Los cubrirá una fuente drand opcional del SDK, nunca activa por defecto en la página (sección 12 del plan).
El vector de GT (`vectors/tlock_ibe.json`) ya está cubierto en `App`. El paso 9 de `open.ts` debe seguir la corrección 6: cualquier fallo de una fuente es `ERR_RELEASE_UNAVAILABLE` y ningún otro código.
**Estado al parar (30-09 por la noche). Retomar por el formato 3, al final de esta sección:** la spec v0.10, entrega 1 del formato 3, está aprobada, implementada en la referencia Go y cerrada con el tag `spec-v0.10`, todo subido en `main` de `datekeys-go` (`cc35d2c`). Lo siguiente es el formato 3 en `datekeys-ts`.
- Pasos 1 a 5 hechos. El spec v0.9 está cerrado con el tag `spec-v0.9` (`7e2d83c`, en `main` de `datekeys-go`), y las dos implementaciones leen el formato 2: Go desde `f24a280`, que también lo escribe, y TypeScript desde `0118890`.
- Paso 6 hecho: la fase 3 está completa en `datekeys-ts` (pasos 0 a 7 del plan, hasta `3a9d2b1`, subido). La librería escribe el formato 2, Go abre lo que escribe y la página `/create` cifra en el navegador. La versión `0.2.0` sigue sin publicar: cerrarla es decisión del autor (§2.3). Después, a petición del autor, `/inspect` muestra el contenido al abrir, debajo del veredicto, y nombra la descarga por su tipo. Las páginas explican las claves de `age`, y la vista previa sirve las páginas sin caché (`da2000a`, `da26862`, subidos).
- Las 22 correcciones del repaso final ([spec_v0.9/review.md](spec_v0.9/review.md)) se aplicaron en `4a025d1`, más cuatro sitios que repetían los mismos problemas: las reglas 1 y 4 del §62.1 y los cambios 4 y 10 del §76. Los números de la corrección 1 se recalcularon con un Padmé en BigInt.
- En este repo están [PLAN_fase3_escritura.md](PLAN_fase3_escritura.md), el plan del writer TypeScript, y [REVISION_completitud_protocolo.md](REVISION_completitud_protocolo.md), la revisión del protocolo. El plan está en su v3: el autor confirmó sus 17 decisiones el 29-09.
**Aprobación del 29-09.** El autor aprobó el texto de la v0.9 con las respuestas recomendadas a las preguntas abiertas:
1. L es un `bstr` de 8 bytes, no un `uint`.
2. El lector SHOULD informar del formato.
3. Se mantienen el objetivo 8 de §4 y el no objetivo nuevo de §5.
4. Solo un generador de vectores de prueba MAY escribir el formato 1 (§62.1, regla 1, y §70).
5. Los fixtures nuevos llevan el prefijo `format2_`.
6. Se mantiene D1: los señuelos son identidades generadas cuya clave privada se descarta en el acto.
7. Rechazar puntos del twist sigue siendo MAY.
8. Se confirman las reglas añadidas: L_MAX = 2⁵³ − 2⁴⁶, el orden uniforme de los stanzas, que el SDK SHOULD mostrar el instante efectivo de la ronda y que el lector MAY comprobar la longitud de PAYLOAD_AGE.
- D1: `time_and_key` lleva siempre exactamente 16 stanzas X25519 en `INNER_ACCESS_AGE`. Los que sobran son señuelos, y todos van en orden aleatorio. Como mucho hay 16 credenciales, contando la `.dkk`.
- D2: el contenido se rellena siempre. El código 1 redondea al siguiente múltiplo de 256, con un mínimo de 256. El código 2, "reforzado", es max(bloque256, Padmé) y es el de por defecto. No hay opción sin relleno.
- D3: la longitud real y el código van en `CONTROL_CBOR` versión 2 (claves 6 y 7). El lector entrega solo L bytes y comprueba que el relleno sea todo ceros y que siga la regla.
- D4: el formato pasa a 2 (`VERSION` 2 del PRELUDE), así que un lector v0.8.2 lo rechaza en el paso 2, sin red. Un lector v0.9 sigue abriendo el formato 1.
- D5: `access_policy` sigue visible.
- D6: nueva sección de privacidad, §55.2.
- D7: reglas normativas del escritor, §62.1:
-`I_PAYLOAD` sale de un CSPRNG y no se reutiliza;
- límites;
- la fecha pedida tiene que ser futura;
- el escritor se autocomprueba;
- rechaza claves X25519 no canónicas o de orden bajo.
-`capsule`: `Format`, `Padding`, `PaddedLength`, `PayloadAgeLength`; `EncodeControl` y `DecodeControl` reciben el formato; `Open` aplica los pasos 12 a 18 del formato 2.
-`agewrap`: `AccessSlots` y `CheckX25519Recipient`.
- **Escritura:**
-`Encrypt` escribe solo el formato 2.
-`EncryptOptions.Length` es obligatorio; `Padding` es reforzado por defecto.
- Acepta de 1 a 16 credenciales, con señuelos, orden aleatorio y autocomprobaciones.
- La CLI mide la entrada, acepta `-padding` e informa del formato.
- **`testdata`:**
- 7 fixtures `format2_*`, dos de ellos con `.dkk`;
-`vectors/padding.json`;
- los vectores CBOR del formato 2;
- un corpus de mutaciones de 125 casos: 88 del §64 (33 en cada formato y los 22 de la tercera lista) y 37 más;
- un diferencial de 4 380 casos, en el que los 1 825 anteriores no cambian.
- Los fixtures del formato 1 se conservan byte a byte.
- **Mutaciones sin aleatoriedad:** se vuelven a sellar los fixtures con sus claves y nonces conocidos (`internal/testkit/reseal.go`), así que se regeneran byte a byte.
- 60 s de fuzzing en cada uno de los 15 objetivos.
- **Documentación:** la copia del spec del repo pierde las marcas "(por implementar)". README, README.es, CHANGELOG, `docs/traceability.md` y `testdata/README.md` están al día.
4. Se pone el tag `spec-v0.9`. Hecho el 29-09, con la autorización del autor:
-`7e2d83c` anota el SHA-256 del spec en `spec/README.md`, que también va en el mensaje del tag;
-`main` avanzó hasta ese commit sin merge;
-`main`, `v0.9` y el tag están subidos.
5.`datekeys-ts` sincroniza `testdata` y el lector TypeScript abre el formato 2. Hecho el 29-09 (`0118890`, subido):
-`testdata` a `7e2d83c`;
- el prelude lee el formato; `decodeControl` y `encodeControl` reciben el formato; `padding.ts` con las dos reglas, exactas hasta L_MAX;
-`open` exige 16 stanzas en el paso 12, calcula P en el 16 y comprueba el relleno en el 17, entregando solo los L primeros bytes; el paso 17 se registra también cuando se supera;
- la página muestra el formato, avisa del 1 y, al abrir una cápsula del formato 2, da la regla y P;
-`ibe-vectors.json` añade los siete fixtures del formato 2, calculados con `scripts/ibe-go-vectors.go`; los valores congelados no cambian;
- comprobado: los 125 casos del corpus dan en TypeScript el código, el paso y el texto de error de `capsule.Open`, en memoria y desde un `Blob`.
6. Se replantea el plan de la fase 3 y se escribe el writer TypeScript del formato 2. **Hecho el 29-09.** El 29-09 por la noche, [PLAN_fase3_escritura.md](PLAN_fase3_escritura.md) pasó a su v2, sobre las reglas del §62.1:
Una revisión crítica con sondas no encontró nada bloqueante ni grave, y sus 12 correcciones menores están incorporadas.
Hecho el 29-09 en `datekeys-ts`, todo subido y con `npm run verify` en verde en cada paso:
- paso 0: el autor confirma las 17 decisiones con su recomendación, entre ellas cerrar `0.1.0` ya (D14), la fórmula del §62.1 comprobada con el sellado real (D3) y aceptar los puntos del twist, como Go (D6). El plan pasa a v3 (`b97dee2` en este repo);
- paso 1: `0.1.0` cerrada (`d577283`, tag `v0.1.0`) y `0.2.0-dev` abierta (`8c08c97`);
- paso 2 (`8838c7d`): `recipient.ts`, `random.ts` y `agefile.ts`, y las guardas nuevas;
- pasos 3 y 4 (`8473fdf`): `encrypt.ts` y `writer.ts`. Reproducen byte a byte las secciones deterministas de los siete fixtures del formato 2 de Go, escriben en streaming y abortan la salida ante cualquier fallo. El bucle de propiedades pasa con 500 semillas;
- paso 5 (`da0b39f`): Go abre con cada credencial las trece cápsulas de muestra que escribe TypeScript, y las reencodifica a los mismos bytes. Rechaza cuatro mezclas con el mismo código y paso, codifica igual 500 entradas aleatorias y da los mismos textos en 22 recipients y 21 opciones inválidas. Todo queda congelado en `src/lib/dkc/testing/capsule-vectors.json`.
- paso 6: el autor confirma las decisiones de la página con cada recomendación (sección 9 del plan, «Decidido en el paso 6»): `time_only` por defecto, la zona del dispositivo con un selector, los avisos de §53 y §50 desde 365 días y un aviso de protocolo preliminar;
- paso 7 (`3a9d2b1`): la página `/create`, con `lengths.ts` (el tamaño del `.dkc` antes de escribirlo), `localtime.ts`, `create-input.ts` y `creator.ts`. En la compilación de producción, una cápsula creada para dentro de cuatro minutos se abrió después en `/inspect`, con el release pegado de drand, y con `datekeys decrypt` de Go, con red y su `.dkk`, al mismo contenido. Una revisión adversarial encontró un fallo mayor y ocho menores, todos corregidos. El mayor: la `.dkk` que es la única credencial se podía borrar sin confirmación.
- después, a petición del autor (`da2000a`): `/inspect` muestra el texto en claro de cualquier cápsula que se abre, si es texto, y no solo el de los fixtures. Las dos páginas explican las claves de `age`: qué es un destinatario `age1…`, cómo se consigue con `age-keygen` y qué se pega al abrir.
**Formato de cápsula 3, en diseño (30-09).** El autor quiere que una cápsula guarde varios ficheros y carpetas con sus nombres, tamaños, hashes y fechas, más una firma de autor y un sello de tiempo, ambos opcionales. Hoy el nombre del fichero se pierde: §6 y §55.2 lo dejan fuera del protocolo.
- Decisiones del autor:
- todo va cifrado dentro de `PAYLOAD_AGE`, como `security | head | content`;
- hash SHA-256 por fichero, sin hash global;
- con carpetas;
- la fecha de modificación se toma al cargar, marcada por defecto;
- un comentario y un autor declarado, opcionales;
- sin tipo de contenido;
- firma Ed25519 y sello de DateKeys, con registro anclado en OpenTimestamps.
- Un flujo de diseño con revisión adversarial produjo [spec_v0.10/formato3_diseno.md](spec_v0.10/formato3_diseno.md): 28 objeciones, que resultaron ser 24 distintas, corregidas o aceptadas.
- El paquete para la revisión de seguridad externa, con el contexto del protocolo, el modelo de amenazas y las preguntas, era `spec_v0.10/formato3_para_revision.md` (`56851a9`). Se borró al unificar el diseño, pero sigue en git y publicado como artefacto privado, en la versión que revisó Fable: https://claude.ai/artifact/5NY9YxBjZA6DCadZ1aT5Ln.
- **Revisión de Fable (30-09),** guardada en [spec_v0.10/revision_fable.md](spec_v0.10/revision_fable.md).
- **Veredicto:** la parte criptográfica aguanta, y confirma la aritmética de tamaños y la necesidad del verificador Ed25519 propio. Los problemas están en los bordes.
- **Diez hallazgos**, cuatro mayores y seis menores; también hay observaciones sobre el documento.
- **Valoración:** coincido con todos. Comprobados de memoria: el 2 (la lista de git en `core.protectHFS` incluye ZWJ, ZWNJ y U+206A–206F) y el 6 (JavaScript compara en UTF-16: U+FF5E y un emoji quedan al revés que en Go).
- **Arreglos que no necesitan decisión:**
- **2.** R4 por la propiedad `Default_Ignorable_Code_Point`, con lista blanca corta (ZWJ, ZWNJ, VS15, VS16); la clave de R7 descarta lo permitido; tags U+E0000 a U+E007F prohibidos.
- **5.** El tamaño del área de `security` va en la trama, fijo por versión y reservado siempre.
- **6.** TypeScript compara bytes UTF-8, con un vector en `paths.json`.
- **7.** En la CLI, prefijo en cada línea visual del comentario, y los veredictos repetidos al final.
- **8.** Se declara como fuga aceptada que el POST al servicio delata el sello.
- **9.** Tabla de verdad firma/sello, expresión regular de R6b y regla para una mtime negativa.
- **10.** Prefijos de dominio en `control_commit` y `head_digest`, y el nombre `.datekeys-*` reservado.
- **El documento:** 24 objeciones en vez de 28, marcadas «corregida» o «aceptada»; un glosario (capas 2 a 4, L_MAX, la sal); un texto para un sello que el lector no sabe verificar; una sola fuente en vez de tres ficheros que repiten el texto.
- **Decisiones del autor del 30-09 sobre la revisión:**
1.**Tres entregas,** con el formato congelado desde la primera y `security` presente pero vacío:
- 1: contenedor, varios ficheros y rutas;
- 2: firma y claves de autor;
- 3: el sello, cuando exista el documento «Servicio de sellado v1».
Las entregas 2 y 3 no cambian el formato: un lector anterior muestra «no soportado» y abre igual.
2.**Origen del servicio (hallazgo 1):** el autor no tiene preferencia, así que se toma la recomendación.
- Un subdominio aparte, `seal.datekeys.com`, que solo sirve la API y nunca HTML, con cabeceras `nosniff` y `CSP: sandbox` de todos modos.
- La CSP de `/create` añade ese origen a `connect-src`, solo en esa página.
- Es de la entrega 3 y se puede revisar entonces.
3.**Vida del sello (hallazgo 3):** la prueba va dentro de la cápsula.
- El token lleva la prueba de inclusión y el checkpoint firmado, y una raíz offline, fijada desde ya, certifica las claves anuales. Así el sello se verifica sin red para siempre, y la auditoría del anclaje queda como comprobación online opcional.
- Esto agranda el área de `security`: con el arreglo del hallazgo 5, la entrega 3 define una versión de `security` con un área fija mayor.
4.**Abuso (hallazgo 4):** prueba de trabajo SHA-256 en cada petición, de un segundo en el navegador, sin guardar estado sobre quién pide.
- **Hecho el 30-09 a mediodía (`57dc659`),** sin flujo y con un único revisor adversarial:
- [formato3_diseno.md](spec_v0.10/formato3_diseno.md) es la única fuente del diseño. Se borraron `formato3_revision_intro.md` y `formato3_para_revision.md`, que repetían su texto.
- **Estructura:** una parte 0 de contexto, con glosario, decisiones y modelo de amenazas; una parte por entrega, cada una con sus pruebas y sus preguntas abiertas; y tres anexos con las revisiones. El de Fable dice dónde se resuelve cada hallazgo, el de la revisión interna marca sus 24 objeciones como corregidas o aceptadas, y el tercero recoge la revisión adversarial de esta versión.
- **Aplicados** los arreglos 2 y 5 a 10, los del documento y las cuatro decisiones.
- **La revisión adversarial** no encontró nada bloqueante ni mayor. Sus 15 hallazgos menores están resueltos en el diseño (anexo C), y una segunda pasada comprobó los arreglos. Los que cambian el diseño:
- el head va siempre en su versión 1, porque §22 exige un formato nuevo para una versión nueva;
- las claves 2 y 3 de `security` van codificadas aparte, así que una firma o un sello mal formados no arrastran al otro;
- el área depende solo de la versión del spec, nunca de `-seal`, y hay dos fugas nuevas declaradas: un área de 512 delata que no hay sello, y el registro publica la t de cada sello;
- R3 limita el NFD de cada segmento a 255 unidades UTF-16, por HFS+, y R4 prohíbe U+F000–U+F0FF;
- un sello que no verifica se descarta y ya no aborta la cápsula.
- **Interpretación que confirmar:** la decisión 3 hablaba de «una versión de `security` con un área fija mayor». El diseño hace que el área la fije la versión del spec y deja el mapa `security` en su versión 1, para que un lector de la entrega 2 siga comprobando la firma en una cápsula de la entrega 3. Es la pregunta 3 de la entrega 1.
- El autor cerró las diez preguntas de la entrega 1, todas con la recomendación (`afd9f76`). Los límites quedan como se propusieron y congelados con el formato. Un único fichero dentro de una carpeta se descarga directamente, con el ZIP como opción, y la página ofrece cada fichero del ZIP como un corte suyo.
- El borrador del spec v0.10 de la entrega 1 está en `datekeys-go`, rama `v0.10`, sin subir: `fa7f95e` lo redacta y `9d1cd2e` aplica la revisión final. Lleva el spec, el CDDL y `spec/README.md`. La referencia sigue implementando la v0.9, y su `SpecVersion` no cambia.
- La revisión final, como la de la v0.9, encontró un problema bloqueante, tres mayores y once menores, todos corregidos ([spec_v0.10/revision_borrador.md](spec_v0.10/revision_borrador.md)). El bloqueante: R6c rechazaba «¿», porque bestfit1250 lo lleva a '?'. Ahora solo rechaza '/', '\', ':' y U+0000 en una proyección. El diseño recoge los cambios que le afectan en su anexo D.
- Pregunta del autor: la firma de autor con su clave privada está prevista, en la entrega 2. Varias firmas no hace falta decidirlas ahora: un `alg` futuro puede definir una lista de firmas sin cambiar el formato (pregunta 3 de la entrega 2 del diseño).
1. Hecho: el autor eligió Unicode 18.0.0 («no vamos a trabajar con unicodes ya pasados») y dio el visto bueno al texto. El borrador lo recoge en su rama, y el diseño, en su decisión 4.
4. Hecho: la referencia Go implementa el formato 3, con el [plan](PLAN_formato3_go.md) y el detalle de abajo. Con la autorización del autor («sí, sube la rama y pon el tag»), la rama `v0.10` y el tag anotado `spec-v0.10` están en el remoto, en `cc35d2c`, que solo cambia textos para nombrar el tag. Después, a petición suya («sí, avanza main hasta la v0.10»), `main` avanzó hasta `cc35d2c` sin fusión y está subida.
5.**En curso:**`datekeys-ts` lee y escribe el formato 3 con su [plan](PLAN_formato3_ts.md). En la rama `v0.10` de `App`, sin subir, están hechos los pasos 0 y 1 (`fbb90b2`, las tablas y las reglas de rutas y de texto), del paso 5 el CRC-32 y la disposición del ZIP (`36096a9`), el paso 2 (`d9a9cf5`, el códec) el paso 3 (`b176ad2`, la lectura del formato 3 con `testdata` en `spec-v0.10`) y el paso 4 (de `3daa1f7` a `2efc8bc`: `encryptFiles`, `encrypt` solo con `testVectors`, `/create` en formato 3 con un fichero, y la interoperabilidad con Go). El paso 5, el ZIP y el sumidero de la página, está en `ee82f43`, el paso 6, la página `/inspect` con los ficheros del formato 3, en `651178a`, y el paso 7, la página `/create` con ficheros, carpetas, rutas editables, comentario y autor, en `7dc88ef`. El paso 8 está hecho (`7527c35`, `57e0a8a`, `e6cca0b` y el CHANGELOG): README, `testdata:check` contra `cc35d2c`, tres revisiones adversariales con subagentes y sus correcciones, y, a petición del autor, `/create` rediseñado (una carta al futuro, sobre de correo aéreo, fuentes del sistema) y el sitio siempre claro. Pendiente, sin urgencia:
-`/inspect` solo cambió de aspecto con los estilos globales: falta poner el resultado delante y plegar los pasos 1 a 18, como en `/create`.
- El texto del paso 17 aún difiere del de Go cuando la cápsula se corta justo tras un chunk completo: `age-encryption` retiene ese chunk hasta ver un byte más. Igualarlo exige descifrar el STREAM como Go (código propio con `@noble/ciphers`).
- Un head de 16 MiB tarda de 10 a 40 s en la página, en el hilo principal (Go tarda la mitad): `open()` en un worker.
- Menores: el `TypeError` sin sumidero llega tras los pasos 3 a 8 con un `Blob`; `tempfile.remove()` marca borrado antes de lograrlo; `measureFiles` en cada tecla con 65 535 ficheros; un bundle `.app` no avisa (igual que la CLI).
- Subido el 01-10 por la noche, con permiso del autor: la rama `v0.10` de `App`, y `main` y `v0.11` de `datekeys-go`.
6. La entrega 2 abre la versión siguiente del spec, y la 3 espera al documento «Servicio de sellado DateKeys v1». En esa versión entra también la **llave de palabras** que pidió el autor el 01-10: palabras que elige la persona y abren la cápsula en lugar de la `.dkk`, ya hechas en la página y en la CLI (apartado «Retomar aquí»). Además, el 01-10 el autor pidió que `/inspect` pueda pedir la firma a drand: ya lo hace, con un botón (`2ec7102`), lo que cambia la decisión 4 del plan de la fase 2. Para otra revisión externa, el artefacto hay que volver a publicarlo desde el diseño nuevo.
**Hecho el 30-09 por la noche: el formato 3 en Go.** Rama `v0.10` de `datekeys-go`, subida con el tag `spec-v0.10` en `cc35d2c`, un commit o varios por paso del plan:
-`631d09c`, el spec con la regla de invisibles; `6fba1dd`, paso 1, `internal/pathrule` con las tablas de Unicode 18.0.0 y best-fit generadas y su `TablesDigest`; `72e86b8`, paso 2, el códec de `BODY`, `security` y el head.
-`7249ef4`, paso 3, el lector: `capsule.Sink`, `ErrSinkRequired` justo tras el paso 2, el paso 17 en subpasos con su precedencia, y lecturas de `BODY` que crecen con los bytes recibidos (§57).
-`8955c0f`, paso 4, el escritor `capsule.EncryptFiles`, que lee cada fichero dos veces; `Encrypt` solo escribe el formato 2 con `EncryptOptions.TestVectors`.
-`a0de80f`, paso 5, el CLI: `encrypt -in` con ficheros y carpetas, `-comment`, `-author` y `-no-mtime`; `decrypt -out CARPETA` con `os.Root`; la presentación de §29.7, con el ancho del terminal pedido por `syscall`, sin dependencias nuevas.
- Paso 6, datos de prueba: `3c39336` (`SpecVersion` 0.10 y `ERR_HEAD_INVALID`), `e070f89` (las nueve fixtures), `db862d5` (`paths.json`, `path_fold.json`, `head_schema.json`, `security.json` y el control de versión 3 en `cbor.json`) y `18eb3c3` (las mutaciones: 209 casos, 169 del §64, tres de los cuales se abren con su veredicto).
-`20f9959`, paso 7, la documentación; `735885c`, una corrección del CLI: una carpeta de salida sin su carpeta padre falla antes de pedir el release; `6fa2b54`, paso 8, tres objetivos de fuzzing nuevos (`FuzzDecodeHead`, `FuzzEvaluateSecurity`, `FuzzCheckPath`) y el SHA-256 del spec en `spec/README.md`.
- Verificación: `go test ./...` y `scripts/check.sh 60s` limpios, cobertura de `capsule` 93,1 %; la prueba en vivo contra Quicknet abrió cápsulas de formato 3 de las dos políticas; los tres objetivos nuevos, cerca de un millón de ejecuciones cada uno, sin fallos.
- Para el autor, sin urgencia:
- un cambio editorial en el texto aprobado: §67 decía «Serán … (por implementar)» de las fixtures del formato 3, y ahora dice «Son …». El SHA-256 de `spec/README.md` es el del texto con ese cambio;
- el corpus de mutaciones pasa de 168 KB a 706 KB. 476 KB son el caso de 65 536 carpetas implícitas que pide el §64, cuyo head mide 235 KB;
- los textos del CLI que el spec no fija son de la referencia: la cabecera «Ficheros escritos en CARPETA (N):», los avisos de nombres peligrosos y los mensajes del escritor. Los que fija, los veredictos y las dos etiquetas, van literales.
- hecho: el generador escribe el módulo TypeScript con la bandera `-ts` (`13910b3`, en `main` de `datekeys-go`, un commit por delante de `origin/main` y sin subir), y el digest recalculado en TypeScript coincide con `TablesDigest`;
-`testdata` cambia entero de `"spec"`, y añade nueve fixtures, cuatro ficheros de vectores, `mutations.json` con 209 casos (`version changed` pone ahora `VERSION` 4, y hay casos con `verdicts`) y el diferencial con 5 110;
- los textos de las reglas de rutas y de texto deben coincidir byte a byte: `paths.json` y `head_schema.json` los traen en `result` y `detail`.
- Herramientas: en esta máquina los heredocs de Bash rompen apóstrofos y barras invertidas, y la herramienta Write convierte `\uXXXX` en caracteres literales. Funciona escribir los cambios como scripts de Python en el scratchpad.
**Conclusiones de la conversación, sin decisión pendiente:**
- El protocolo no contempla un sello de tiempo de creación (§5, §55.1). Si se quiere, lo recomendado es un sello RFC 3161 u OpenTimestamps sobre el SHA-256 del `.dkc` completo, guardado aparte.
- Un fichero único `.dk` con `.dkc` y `.dkk` juntos no conviene: con `time_and_key` equivaldría a `time_only`. Ya se envía un solo fichero con `time_only` o con destinatarios `age1…`.
- Crear `security@datekeys.com` y, si se quiere, un `security.txt` en la web. Después, actualizar `SECURITY.md`, que hoy dice `info@activething.com`.
- Elegir la ruta pública del módulo Go (propuesta: `datekeys.com/go/datekeys`, en minúsculas) y el espejo público, que no puede ser GitHub (por ejemplo Codeberg). Publicar la página `go-import` en `datekeys.com`. Después: renombrar el módulo y poner el tag `v0.1.0` cuando `go get` funcione desde una máquina limpia.
Release API sobre la librería, traducción del spec al inglés y revisión externa antes de la v1.0. Opcional: escribir en OPFS en trozos de 1 MiB, que ahorra un 10 % al cifrar ficheros grandes en la página (README de `datekeys-ts`, «Crear»).
-`spec/DateKeys_Protocol_Specification_v0.10.md`, cerrado con el tag `spec-v0.10`; `spec/DateKeys_Protocol_Specification_v0.9.md`, cerrado con el tag `spec-v0.9`; `spec/DateKeys_Protocol_Specification_v0.8.2.md`, `spec/datekeys.cddl` y `spec/README.md`;
- Las herramientas de verificación de las sesiones del 26 al 28-09 estaban en carpetas temporales y pueden haber desaparecido: el diferencial Go/TypeScript de `tsreview/` y las copias congeladas `dkgo-ref-<commit>`. Su descripción está en los mensajes de los commits de `datekeys-ts` y en su README.