You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
DateKeys-App/CHANGELOG.md

171 lines
26 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Changelog
Cambios notables de la librería TypeScript y de la página. El proyecto usa versionado semántico; mientras sea 0.x, no hay promesa de estabilidad. La sección «Versiones» del [README](README.md) explica qué cubre cada número.
## Especificación 0.10, en la rama `v0.10` — sin versión
### Arreglos de la revisión de la sesión del 1 y 2 de octubre (02-10-2026)
Los fallos T1, T3, T4, T5, T9 a T12, T14 y parte de T13 de `spec_v0.11/revision_sesion_1_2_octubre.md` (en `../docs`). Cada texto y cada veredicto nuevo se contrastó con un oráculo de Go sobre el tag `spec-v0.11`.
- **CMS como lo lee Go.** Un emisor que incumple las reglas del autor declarado se muestra con el SHA-256 de su `Name` (`RawIssuer`), y no con el del certificado. Una clave ECDSA solo cuenta sin comprimir, `0x04` y las dos coordenadas, como en `x509.ParsePKIXPublicKey`: con el punto comprimido, un firmante es «no verificable» (F5) y un sello da S1. Un `UTF8String` y las horas del certificado y del token conservan un U+FEFF inicial, así que ese nombre se muestra con el hash y esa hora rompe el perfil (F1, S2).
- **Lectura lineal.** `oidOf` e `intOf` leen en tiempo lineal, sin un desplazamiento por byte, que tardaba unos 700 ms con 60 KB; los atributos de un tipo se añaden sin copiarse.
- **`evaluateSecurity` nunca lanza.** Una excepción al evaluar la firma da F1, y una al evaluar el sello, S2, cada una sin tocar el otro veredicto; una al decodificar el área da X. La referencia hará lo mismo desde la v0.12.
- **Pruebas que no probaban lo que decían.** Las de `cms.test.ts` de un segundo content-type y dos signature-time-stamp fallaban por un SET OF desordenado, y ahora fallan por la regla de recuento. La de profundidad de `der.test.ts` fallaba por la longitud, y ahora prueba la frontera de 32 niveles.
- **La nota pública.** `checkNote` rechaza un texto con UTF-16 mal formado, con el texto de Go para el UTF-8 inválido, así que ninguna nota se escribe alterada. `publicNote` conserva un U+FEFF inicial, que la deja inservible como en Go. También lo conservan `authorCode` y la lectura de la respuesta de los relays de drand; el head y las rutas ya lo hacían, y sus pruebas lo fijan con los textos de Go.
- **El escritor.** `testVectors` y `areaLen` salen de las opciones públicas: con ellas, cualquiera podía escribir el formato 2 o un área de 512 bytes, que delata la falta de firma (§55.2).
- Lo que solo pide un generador de vectores es el argumento `TestVectors` del núcleo, que solo pasan los ayudantes de `testing/encrypt.ts`: `encryptVectors`, `encryptWith` y `encryptFilesWith`.
- `encrypt` conserva la forma de `capsule.Encrypt`: sin generador, falla con su texto.
- `dependencies.test.ts` y `check-build.mjs` dejan `testing/` fuera de la librería y de las páginas.
- **Las opciones, como en Go.** Como generador, `encrypt` rechaza la nota y el área con el texto de `capsule.Encrypt`. Los errores de la nota llevan `capsule: `, y las opciones se comprueban en el orden de `newSealer`.
- **El tamaño con nota.** `capsuleLength` acepta la nota pública y predice exactamente el tamaño del `.dkc` con ella.
- **Interoperabilidad.** `capsule-vectors.json` se regenera con el escritor actual: las seis cápsulas de formato 3 llevan el área de 32 KiB, y hay dos más con nota pública, una de 1024 bytes. Go las abre con cada credencial, encuentra el área de 32 KiB y lee la nota con `Header.PublicNote`. El fichero gana el texto de `capsule.Encrypt` para una nota en el formato 2, y los de `capsule.EncryptFiles` para nueve notas inválidas.
- **Documentación.** El README completa la tabla de módulos (`author.ts`, `ed25519strict.ts`, `der.ts`, `cms.ts`, `securitycms.ts` y `note.ts`), los veredictos de la v0.11, las dependencias y sus guardas, y los recuentos del corpus de mutaciones; los comentarios ya no hablan del área de 512 bytes ni de una versión sin `alg`.
- **Pendiente, con la v0.12:** el lector de certificados, cuyo perfil se fija ahora en Go (T2, T6, T7 y T8), el texto del emisor sin CN y la prueba de `cms.test.ts` que lo compara consigo mismo.
### `testdata` en `spec-v0.11` (01-10-2026)
- `testdata` se sincroniza con el tag `spec-v0.11` de `datekeys-go` (`ae33434`), y `SPEC_VERSION` pasa a `0.11`. Trae tres fixtures (`format3_signed`, con firma de clave propia; `format3_signed_cms`, con dos certificados sellados; `format3_sealed`, con firma y sello RFC 3161) y tres ficheros de vectores (`ed25519_strict.json`, `security_cms.json` y `locator.json`). El corpus de mutaciones pasa a 210 casos, con uno fuera del §64: una firma de `alg` 1 que no verifica, F2. `ibe-vectors.json` añade los tres fixtures y rehace los de `format3_signature_unsupported` y `format3_seal_unsupported`; `mutation-texts.json` se rehace con el `capsule.Open` de esa referencia.
- **La firma de clave propia, `alg` 1** (§29.8, §29.9), portada: `ed25519strict.ts` comprueba las cuatro condiciones del perfil estricto con la aritmética de `@noble/curves` (que solo ofrece la ecuación con cofactor) y da la respuesta de Go en los 18 vectores de `ed25519_strict.json`; `author.ts` calcula `payload_commit`, `control_commit`, `head_digest`, `signers_digest`, `AUTHOR_MESSAGE` y su código, y los registros de `format3_signed` los confirman. `evaluateSecurity(área, contexto)` da F2, F3 y F4, `open` pasa el contexto del control y del head, y `OpenOptions.authorKeys` son las claves que la persona guardó. Los dos módulos usan solo `@noble/curves` y `@noble/hashes`, que ya iban en el bundle: ningún paquete nuevo, y entran en la lista de quien puede importar noble.
- **La firma con certificados, `alg` 2, y el sello RFC 3161, `seal_type` 2** (§29.10, §29.11), portados sin dependencias nuevas: `der.ts` comprueba el DER byte a byte, `cms.ts` lee la firma CMS y el token con la tabla cerrada de algoritmos (RSA PKCS #1 y PSS en `BigInt`, síncrono, y ECDSA con la aritmética de `@noble/curves`) y `securitycms.ts` da F1, F2, F5 y F6 con los firmantes nombrados, y S1 a S5 con la autoridad del sello. `evaluateSecurity` los devuelve con su `detail`, `verdictLines` escribe las líneas de F6 y S4, y `open` pasa la hora de la ronda. Reproducen los 22 casos de `security_cms.json`, con los resultados de cada firmante, y los fixtures `format3_signed_cms` y `format3_sealed`. `testing/cmsbuild.ts` construye firmas y tokens de prueba con WebCrypto, y `cms.test.ts` porta los casos hostiles de Go.
- **El escritor de la v0.11** (§29.2, §24.1, §62.1 regla 13): `encryptFiles` escribe el área de seguridad de 32 KiB (`AREA_LEN` pasa de 512 a 32768) y acepta `publicNote`, la extensión `datekeys.note` de la cabecera pública (`note.ts`: `checkNote`, `newNote`, `publicNote`, con las reglas de texto del autor declarado). Otra área solo la escribe un generador de vectores, con `testVectors` y `areaLen`, y así los tests reproducen byte a byte los fixtures que escribió un escritor de la v0.10, de 512 bytes. `lengths.ts` y la página calculan L con el área nueva. Una nota cambiada después de escribir la cápsula falla en el paso 15.
- **Lo que esta biblioteca no hace todavía:** el escritor no firma ni pide sellos (`alg` 1, `alg` 2 y RFC 3161 solo se leen y verifican), la página no pide ni muestra la nota, y el localizador del §44.1 (la extensión `datekeys.capsule` de la `.dkk`, su sobre y su relleno) y la página de firma. `locator.json` solo se comprueba en su estructura.
El formato 3 de la especificación 0.10, según `PLAN_formato3_ts.md` (en `../docs`). La versión que lo publique la decide el autor.
### Llave de palabras y firma de drand
- Llave de palabras v2, como decide el borrador de la especificación 0.11 (§38.1): la sal lleva además el `capsule_id`, así que las mismas palabras dan otra llave en cada cápsula; las palabras se pasan a minúsculas con la tabla de Unicode 18.0.0 de `pathrule.ts`, no con la de la plataforma; cuentan solo las distintas de 3 letras o más; y se rechazan los caracteres invisibles. `encryptFiles` recibe las palabras (`words`) y deriva la llave al sortear el `capsule_id`. `/create` pide escribirlas dos veces y muestra cómo se guardan. Las cápsulas hechas antes con palabras ya no se abren con ellas. El módulo pasa a `src/lib/dkc/wordkey.ts`.
- `/inspect` pide la firma de la ronda a los relays de drand con un botón (`drand.ts`), además de poder pegarla.
- Una cápsula «solo con una llave» puede abrirse con palabras que elige quien la crea, al menos 6, en vez del fichero `.dkk` (`wordkey.ts`): PBKDF2-SHA256 de 600.000 vueltas, con la red y la ronda como sal, da una clave X25519 que entra como una persona de `age` más. El formato no cambia; dan igual mayúsculas, acentos y espacios.
### Paso 8: revisión adversarial y rediseño de `/create`
- Correcciones de la revisión: el payload llega a `age` en trozos de un chunk (`chunked`), el sumidero nunca se aborta tras el commit y recibe copias, el escritor copia cada trozo que lee y acepta fuentes escritas como clase, y las extensiones de tipos incorrectos son un `TypeError`.
- El sitio es siempre claro. `/create` se rediseña como una carta al futuro: tres preguntas, fechas rápidas, la fecha de apertura en un sobre de correo aéreo con el botón, y los detalles técnicos plegados; los mensajes ya no citan R4 ni §29.6.
- `/inspect` marca el principio de un fichero como contenido del creador, sin comprobar, y conserva ZWNJ y ZWJ en el comentario y el autor.
### Paso 7: `/create` con ficheros y carpetas
- La página cifra ficheros y carpetas, elegidos o soltados, con un comentario y un autor declarado. Las rutas se pueden editar y se comprueban mientras se escriben, cada problema en su fila y en español; los ficheros de los sistemas quedan fuera, tachados, salvo que se marquen; y una casilla, marcada por defecto, guarda la fecha de cada fichero.
- El tamaño exacto del `.dkc` antes de escribir, con los ficheros medidos una vez (`measureFiles`, `headLengthOf` y `bodyLengthOf` en `lengths.ts`), y el progreso de las dos lecturas de `encryptFiles`.
- `create-files.ts` (la lista, sin tablas) y `create-check.ts` (las reglas en español, bajo demanda), al 100 %. Go abre una cápsula de la página, y `/inspect` también.
### Paso 6: `/inspect` abre el formato 3
- La página abre las cápsulas de formato 3: los ficheros van al fichero temporal por un `ZipSink`, o a la memoria, y se muestran primero los veredictos, después el autor declarado y el comentario, y luego cada ruta como texto, con su tamaño, su fecha y los avisos de la CLI de la referencia. Se descarga el fichero, si es el único, con el ZIP de su carpeta como segunda opción, o el ZIP de todos y cada fichero por separado.
- `files.ts` (avisos, rutas y descargas) y `opener.ts` (`OpenedFiles`, la falta de espacio), cargados bajo demanda; `ZipSink` comprueba el espacio al empezar.
- `check-build.mjs` exige que las tablas de las rutas no entren en la primera carga de ninguna página.
### Paso 5: el ZIP de la página
- `zipsink.ts`: `ZipSink`, el sumidero de la página sobre el fichero temporal de OPFS. Un fichero de un segmento va tal cual, y los demás casos a un ZIP propio, con el CRC-32 de cada entrada parcheado con una escritura posicionada y el directorio central al hacer commit; nada se publica antes del paso 18. `zipOf` hace el mismo ZIP en memoria.
- `tempfile.ts`: el `writable` de un fichero temporal acepta trozos con posición (`TempChunk`), como `FileSystemWritableFileStream`.
- Interoperabilidad: `archive/zip` de Go lee las muestras de la página, 65 535 entradas con ZIP64 incluidas, con sus nombres, CRC-32, tamaños, fechas y contenidos (`testing/zip-vectors.json`, de `scripts/zip-ts-samples.mjs` y `scripts/zip-go-read.go`).
### Paso 4: la escritura del formato 3
- `writer.ts` se parte como el writer de Go: `newSealer` comprueba las opciones que no dependen del contenido, y `seal` escribe la cápsula de un formato alrededor de un contenido dado en trozos. El formato 2 no cambia.
- `encryptFiles(files, opts)` escribe un `.dkc` de formato 3 con sus ficheros, el comentario y el autor declarado, y las extensiones del head, como `capsule.EncryptFiles`: lee cada fichero dos veces y falla, con el texto de Go, si cambió entre las dos lecturas. `fileSource` hace la fuente de un `File`. Los sorteos ganan la sal del head.
- `lengths.ts`: `bodyLength`, `headLength`, `mtimeSeconds` y `headComment`, para dar el tamaño exacto del `.dkc` antes de escribirlo.
- `encrypt` escribe el formato 2 solo con `testVectors`, y sin comentario, autor ni extensiones del head, con los textos de `capsule.Encrypt`; las pruebas, `encryptWith` y los scripts de muestras lo piden.
- `/create` escribe el formato 3 con `encryptFiles`: el fichero va bajo su nombre y con su fecha de modificación, y el plan da el tamaño exacto con `bodyLength`.
- Interoperabilidad: `capsule-vectors.json` se regenera con seis cápsulas de formato 3 escritas por `encryptFiles`, que Go abre en un `Sink` con cada credencial, con los mismos ficheros, head y veredictos; las trece de formato 2 y los demás bloques se regeneran igual.
- Pruebas: los cinco fixtures que escribió `EncryptFiles`, byte a byte; los tamaños frente a lo escrito y 300 heads aleatorios frente a `encodeHead`; y las entradas inválidas, con los textos que da `capsule.EncryptFiles` a las mismas entradas.
### Paso 3: la lectura del formato 3, con `testdata` en `spec-v0.10`
- `testdata` se sincroniza con el tag `spec-v0.10` de `datekeys-go` (`cc35d2c`), y `SPEC_VERSION` pasa a `0.10`.
- `framing.ts`: `FORMAT_3`, e `isPadded` para los formatos 2 y 3. `control.ts`: la versión de schema 3, con las claves 6 y 7 de la 2.
- `open.ts` y `open3.ts`: el paso 17 del formato 3 en sus subpasos, con la precedencia y los textos de `capsule.Open`. Los ficheros van a `OpenOptions.sink` (`sink.ts`: `Sink` y `MemorySink`), que solo los publica en el paso 18; sin sumidero, `open` rechaza con un `TypeError` justo tras el paso 2. `Opened` gana `head`, `verdicts`, `areaLen` y `unusableHeadExtensions`.
- La página: `opener.ts` abre en memoria los fixtures del formato 3; el panel de apertura dice que aún no entrega sus ficheros, y los textos de los pasos nombran el formato 3.
- Pruebas: los 21 fixtures; los 209 casos del corpus, con el texto exacto de `capsule.Open`, congelado con `scripts/mutation-go-texts.go` en `testing/mutation-texts.json`; las 5 110 mutaciones del diferencial; los vectores de rutas, pliegue, head y seguridad; y los textos del paso 17 con textos en claro preparados, contrastados con Go. `ibe-vectors.json` gana los nueve fixtures nuevos.
- `check-build.mjs` impide publicar el head, la sal, el comentario y las rutas de los fixtures del formato 3.
### Paso 2: el códec del formato 3
- `body.ts`, `security.ts` y `head.ts`, como `capsule/format3.go`: la trama de `BODY`, los veredictos del área de seguridad con sus textos, y el head con las capas de §69.1. `ERR_HEAD_INVALID` es el código 19.
### Pasos 0, 1 y parte del 5: las reglas de las rutas y el ZIP de la página
- `pathrule-tables.ts`, generado por `datekeys-go` con Unicode 18.0.0 y WindowsBestFit, y `pathrule.ts`, las reglas de las rutas y de los textos del head con los textos de error de Go.
- `crc32.ts` y `zip.ts`, en `src/lib/inspector`: la disposición del ZIP en que la página entregará los ficheros, con entradas almacenadas, nombres UTF-8, las fechas en DOS, NTFS y el sello extendido, y ZIP64.
## 0.2.0 — sin publicar
Fase 3: la escritura de cápsulas de formato 2, según `PLAN_fase3_escritura.md` (v3, en `../docs`). Hecha, pasos 0 a 7; la versión sigue sin publicar hasta que el autor la cierre.
### Después del paso 7: ayuda de `age` y el texto en claro a la vista
- `/inspect` muestra el principio del texto en claro de cualquier cápsula que se abre, no solo de los fixtures, si es texto: UTF-8 imprimible, hasta 100 000 caracteres de sus primeros 128 KiB. Se ve también un texto escrito en Windows, con CR LF o BOM; la descarga conserva los bytes exactos. `opener.ts` da esos primeros bytes (`PREVIEW_BYTES`), y `opening.ts` decide qué se muestra (`plaintextPreview`).
- Ayuda de las claves de `age`. En `/create`, un bloque plegable explica qué es un destinatario `age1…` y cómo se consigue con `age-keygen`. En `/inspect`, el campo de identidades dice qué pegar: la línea `AGE-SECRET-KEY-1…` del fichero de `age-keygen`, o el fichero entero.
- Al abrir, el contenido va justo debajo del veredicto. La descarga de una cápsula sin extensión propia, como `capsula-<fecha>.dkc`, toma la del contenido (`contentExtension`): `.txt`, `.pdf`, `.png`, `.jpg` y otras por sus primeros bytes. La cápsula no guarda el nombre del fichero (§6, §55.2).
- `vite preview` sirve las páginas con `Cache-Control: no-cache`, para que una pestaña recargada tras compilar no se quede con la página anterior, cuyos ficheros ya no existen.
### Pasos 6 y 7: la página `/create`
- El autor confirma las decisiones de la página con cada recomendación: `time_only` por defecto, la zona del dispositivo con un selector, los avisos de §53 y §50 desde 365 días, un aviso de protocolo preliminar y los nombres `capsula-<apertura en UTC>`.
- `/create` cifra un fichero propio en un `.dkc` de formato 2 y, si se pide, en una `.dkk` portable, en el navegador y sin red. Antes de cifrar muestra el instante efectivo, la ronda, la `dk1_`, el tamaño exacto del `.dkc` y lo que deja ver hasta la fecha. El `.dkc` va a un fichero temporal de OPFS, o a memoria hasta 64 MiB, y la escritura se puede cancelar. La `.dkk` vive solo en memoria, y la página avisa si se sale sin descargarla. El resultado lleva el informe de los pasos 1 a 8 de lo escrito.
- `lengths.ts`: `sealedControlLength` sale de `writer.ts`, y `capsuleLength` da el tamaño del `.dkc` antes de escribirlo; el bucle de propiedades lo comprueba en cada cápsula. `datekey.ts`: `LONG_HORIZON_SECONDS` e `isLongHorizon`.
- `localtime.ts`, `create-input.ts` y `creator.ts`, al 100 %. `/inspect` limpia también la zona de crear. `check-build.mjs` comprueba la carga bajo demanda de las dos páginas que la tienen.
- Comprobado en el navegador: una cápsula creada para dentro de cuatro minutos se abrió después en `/inspect` con el release pegado y con `datekeys decrypt` de Go, con el 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. Ahora la página la pide al olvidarla y al crear otra cápsula. Entre los menores: la zona desconocida del dispositivo pasa a UTC; el reloj de la página se lee cada segundo; hay un mensaje propio para un reloj anterior a Quicknet; lo escrito no se ofrece si los pasos 1 a 8 lo rechazan; y el foco va a «Cancelar» durante la escritura. `localtime.test.ts` compara la conversión con una búsqueda exhaustiva alrededor de todos los cambios de hora de 2030.
### Paso 5: interoperabilidad con Go a nivel de cápsula
- `interop.test.ts` y `testing/capsule-vectors.json`: Go abre con `capsule.Open` las trece cápsulas de muestra que escribe `encrypt`, con cada credencial, y reencodifica sus objetos a los mismos bytes. Cubren las dos políticas y las dos reglas, de 0 a 16 credenciales, los bordes de trozo y las extensiones.
- Go rechaza cuatro mezclas de dos cápsulas con el mismo código y paso que `open`, codifica igual 500 entradas aleatorias de los codificadores, y da los mismos textos que esta librería con 22 recipients y 21 opciones inválidas.
- Lo generan `scripts/capsule-ts-samples.mjs` y `scripts/capsule-go-verdicts.go`, y se congela; el test comprueba en cada ejecución los veredictos de Go y que `open` da lo mismo sobre los bytes congelados.
### Pasos 3 y 4: el writer
- `encrypt.ts` y `writer.ts`: `encrypt(src, opts)` escribe un `.dkc` de formato 2 y, si se pide, una `.dkk` portable, como `capsule.Encrypt`: L conocida de antemano, relleno `reforzado` por defecto, de 1 a 16 credenciales con señuelos en un orden uniforme, `SEALED_CONTROL_LEN` con la fórmula del §62.1 y las autocomprobaciones de la regla 11 y dos más. Streaming desde `Uint8Array`, `Blob` o `ReadableStream`, hacia memoria o hacia un `WritableStream` que solo se cierra con la cápsula completa y comprobada.
- Reproduce byte a byte las secciones deterministas de los siete fixtures de formato 2 de Go, y todo lo que escribe se abre con `open`.
- Tests de streaming, de errores internos con `age-encryption` sustituido y un bucle de propiedades (50 semillas en cada ejecución; 500 pasaron a mano).
### Paso 2: piezas de apoyo
- `recipient.ts`: recipients `age1…` como `age` 1.3.2, las reglas de §37 con los textos de `agewrap.CheckX25519Recipient` y la lista de recipients de una persona, con errores por número de línea. Sin noble.
- `random.ts`: el índice sin sesgo y la permutación de Fisher–Yates del orden de los 16 huecos, con la prueba de uniformidad de Go.
- `agefile.ts`: los ficheros `age` enteros que antes eran privados de la apertura, para compartirlos con el writer.
- `x25519.ts`: `newX25519Identity` y `x25519PublicKey`, con los vectores de RFC 7748. `digest.ts`: `sha256Hasher`. `datekey.ts`: `compareInstants`, que usa `open.ts`, e `isInstant`.
- `tempfile.ts`: la zona de la apertura y la de crear (`OPEN_AREA`, `CREATE_AREA`), con su limpieza por separado.
- Guardas: solo `agefile.ts`, `open.ts`, `tlock.ts`, `writer.ts` y los tests importan `age-encryption`; solo `encrypt.ts` y `testing/` importan `writer.ts`; `index.ts` no reexporta la apertura ni el writer.
## 0.1.0 — 29 de septiembre de 2026
Implementa la especificación DateKeys 0.9 (tag `spec-v0.9` de `datekeys-go`) para el perfil Quicknet, y pasa todos los vectores y fixtures compartidos de `datekeys-go` en `7e2d83c`. Hasta el 29-09-2026 implementaba la 0.8.2 (`9ac9cd9`).
### Especificación 0.9: el formato 2
- El prelude lee el formato de la cápsula, 1 o 2 (`Prelude.format`); otro `VERSION` es `ERR_UNSUPPORTED_VERSION` en el paso 2. `DKC_FRAMING_VERSION` desaparece: `FORMAT_1`, `FORMAT_2` e `isFormat`.
- `CONTROL_CBOR` se lee y se escribe para un formato, `decodeControl(b, format)` y `encodeControl(c, format)`: su versión de schema es la del formato, y en el formato 2 lleva `payload_length` (8 bytes, hasta L_MAX) y `padding` (claves 6 y 7).
- `padding.ts`: las reglas `bloque256` y `reforzado` de §29.1, exactas hasta L_MAX = 2⁵³ − 2⁴⁶, y la longitud de `PAYLOAD_AGE`.
- `open`: exactamente 16 stanzas en `INNER_ACCESS_AGE` del formato 2 (paso 12, `ACCESS_SLOTS`), P en el paso 16, y en el 17 un texto en claro de P bytes con ceros tras el contenido, del que solo se entregan los L primeros bytes, nunca el relleno. El paso 17 se registra también cuando se supera, y el 18 da los bytes de contenido, como la referencia. `Opened` da `format`, `payloadLength` y, en el formato 2, `padding` y `paddedLength`.
- La vista JSON de `inspect` lleva `format`, como `datekeys inspect -json`.
- La página muestra el formato de la cápsula, avisa cuando es el 1, que no oculta el número de credenciales ni la longitud exacta del contenido, y al abrir una del formato 2 da la regla de relleno y P.
- `testdata` sincronizado con `spec-v0.9`: doce fixtures, siete de ellos del formato 2, `padding.json`, 125 casos de mutación y 4 380 del diferencial. `ibe-vectors.json` añade los siete fixtures nuevos.
### Hecho
- Codec CBOR del perfil de §58 con los textos de error de la referencia Go.
- Schemas del Provider Profile, `PUBLIC_HEADER`, `CONTROL_CBOR` y `.dkk`.
- Tramas DKC1 y DKK1, cabeceras `age` y DateKey (`dk1_`).
- Extensiones, con los objetos y arrays de su registro.
- La inspección de los pasos 1 a 8 de §63.
- La página estática `/inspect`, sin red. Tras cada compilación se comprueban su política de seguridad y los paquetes de su bundle.
- Fase 2:
- dependencias de ejecución (`age-encryption` 0.3.1, `@noble/curves` y `@noble/hashes` 2.4.0) con sus guardas;
- el IBE de tlock (`ibe.ts`) y la verificación local de releases (`release.ts`), contrastados con la referencia Go;
- la apertura, pasos 9 a 18 de §63 (`open.ts`), con el texto en claro en memoria. Las identidades estrictas de `agewrap` se apoyan en `x25519.ts`, que abre cada stanza X25519 por separado, y en `bech32.ts`. Los 65 casos del corpus de mutaciones pasan por `open` con el código y el paso de Go, y los cinco fixtures oficiales se abren a su texto en claro;
- `@noble/ciphers` 2.4.0 como dependencia directa, aprobada el 28-09-2026: la copia que ya trae `age-encryption`;
- la apertura en streaming. La entrada puede ser un `Blob`, del que se lee solo el prefijo de los pasos 1 a 8 (`prefix.ts`, antes en la página) y se descifra `PAYLOAD_AGE` en streaming. La salida puede ser un `WritableStream`, que se cierra solo tras el paso 18 y se aborta ante cualquier fallo. En el navegador, con un fichero OPFS, un fallo de STREAM deja intacto su contenido anterior.
- el cifrado del stanza tlock (`encryptOnG2RFC9380`) y el `Recipient` de `OUTER_TIME_AGE` (`tlock.ts`). Con sigma fijo, el cifrado reproduce byte a byte los vectores de Go. Además Go abre lo que cifra esta librería: el cuerpo IBE con `tlock.TimeUnlock` y el fichero `age` con `agewrap.NewTimeIdentity`;
- la acción "abrir" de `/inspect` (paso 8), cuyo código, con noble y `age-encryption`, se carga bajo demanda:
- el release lo pega quien abre (la respuesta de drand o la firma sola), o sale del registro de un fixture. La página nunca lo pide a la red y solo lee su ronda y su firma;
- las credenciales de `time_and_key` son una `.dkk` o identidades `AGE-SECRET-KEY-1…`;
- el texto en claro de un fichero propio va a un fichero temporal de OPFS que solo se confirma tras el paso 18. Se ofrece para descargar y se borra al pedirlo, con otra cápsula, al salir o en la visita siguiente;
- `readAccessKey` en `prefix.ts`.
- `VERSION` y `SPEC_VERSION`, también en el pie de la página.
- `licenses.txt` en el sitio, con los avisos de `tlock-js` y `age` y los de cada paquete del bundle.

Powered by TurnKey Linux.