41 KiB
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 explica qué cubre cada número.
Especificación 0.10, en la rama v0.10 — sin versión
La nota pública en /inspect (06-10-2026)
/inspectmuestra la nota pública justo debajo del veredicto: el texto del creador, en la letra serif de la página y sobre el fondo de aviso, con dos líneas que dicen que nadie la ha comprobado, que la lee cualquiera que tenga el fichero y que antes de la fecha nadie puede comprobar quién creó la cápsula ni si va firmada, comoshowNotede la CLI de Go. Una nota que no cumple las reglas de texto no se muestra, y la página lo dice.buildReportlleva la nota enReport.note, con su prueba. Comprobado en el navegador con el fixtureformat3_note.- Errores que no se esperaban. Las páginas ya no enseñan el mensaje de una excepción, que podía venir en inglés y con una URL interna, como «Failed to fetch dynamically imported module: http://localhost:5188/src/lib/inspector/opener.ts».
unexpectedProblemdeformat.tslo dice en español y con lo que hay que hacer: recargar si no llegó una parte de la página, volver a elegir un fichero que el navegador ya no deja leer, liberar espacio, o, en otro caso, recargar y usar la herramienta de línea de órdenes. La excepción va a la consola. Lo usan la apertura, la lectura de una cápsula en/inspecty la creación en/create. - El servidor de desarrollo ya no recarga la página al abrir.
vite.config.tshace que Vite prepare desde el arranque las dependencias que las páginas cargan bajo demanda (optimizeDeps.include). Sin eso, la primera apertura le hacía preparar dos módulos de noble y recargar la página, y la carga en curso fallaba con ese mensaje.dependencies.test.tscomprueba que la lista está completa. La versión compilada no tenía el problema.
NAT64, del borrador v0.13 (06-10-2026)
testdatase sincroniza condatekeys-goena83b44d, el borrador v0.13, sin aprobar: añadevectors/resolved_ip.json, y ningún otro fichero cambia.SPEC_VERSIONsigue en0.12.checkResolvedIpcuenta una dirección de NAT64 a la que resuelve un nombre, de64:ff9b::/96o del prefijo de la red (el parámetronat64), por la IPv4 que lleva dentro, con los textos delocator.CheckResolvedIPde Go.ipaddr.tsexportaisIpv4In6. Un bloque nuevo devectors.test.tscorre los 42 casos.
Las claves de autor y la firma al escribir (06-10-2026)
Las claves de autor de alg 1 y los enganches de firma y de sello del escritor, como authorkey y capsule.EncryptFiles de Go en spec-v0.12, byte a byte. Ningún paquete ni módulo nuevo: el SHA-512, el HKDF, ChaCha20-Poly1305 y el scrypt de age-encryption ya estaban.
-
ed25519sign.ts, la firma Ed25519 en código propio:crypto_signde TweetNaCl, como el port de Dart, con el SHA-512 de@noble/hashes. Aritmética exacta enFloat64Array, y los secretos nunca enBigInt. JavaScript no promete tiempo constante ni borrar la memoria, y el código lo dice. -
authorkey.ts, el paqueteauthorkeyde Go:AuthorKey(generatecon una fuente de azar inyectable,fromSeed,publicKey,sign,clear,secrety untoStringque oculta el secreto),authorPublicString,parseAuthorPublic,parseAuthorSecret,marshalAuthorKey,encryptAuthorKeyyreadAuthorKey, con los textos de error de Go byte a byte, también los deage, también los números al revés del dePublicString. Las cadenas se leen como bytes, con las mayúsculas, las minúsculas y los espacios del paqueteunicodede Go, quegounicode.tstrae en tablas generadas con Go (scripts/go-unicode-tables.go). El fichero cifrado lleva scrypt con logN 16, y se lee con un máximo de 16, 64 KiB y las líneas debufio.Scanner. -
El escritor.
encryptFilesganaauthorKey(alg1),cmsSigner(alg2) ysealer(seal_type2), que pueden ser asíncronos, ylargeArea. Se comprueban en el orden de Go y con sus textos; la firma y el sello se piden con el control y el head finales y antes de escribir nada, y el área se evalúa con el lector antes de escribirla, comosecurityde Go. Un área que crece a 64 KiB cuenta en el límite de memoria.security.tsescribe el área con firma y sello, ysecuritycms.ts,SIGNERS. -
Interoperabilidad con Go, en dos ficheros congelados de
testing/:authorkey-vectors.json, descripts/authorkey-go-vectors.go: 234 firmas, la reducción de escalares, 24 claves,Generatey el fichero cifrado con los valores al azar de Go, queencryptAuthorKeyreproduce byte a byte, 1 288 cadenas, 3 240 runas y 130 ficheros que leer, cada uno con el resultado o el texto de Go;signing-vectors.json, descripts/signing-go-vectors_test.goen una exportación despec-v0.12: con los valores al azar y las firmas de Go,encryptFilesescribe sus ocho cápsulas firmadas y selladas byte a byte, y falla con su texto en las otras 15. Y Go abre las cinco cápsulas que escribescripts/signing-ts-samples.mjscon esta librería, sus certificados y su azar, con los mismos veredictos y las mismas líneas.
-
Guardas.
dependencies.test.tsdeja importar noble aauthorkey.tsyed25519sign.ts, yage-encryptionaauthorkey.ts, y no deja queindex.tslos reexporte.check-build.mjsfalla si una página carga las claves de autor con la página, o si/inspectpuede cargarlas.authorkey.ts,ed25519sign.tsygounicode.tsquedan al 100 % de cobertura. -
Pruebas.
npm run verifypasa con 7 904; una más, la de un área que crece y ya no cabe en memoria, lee 1 GiB y corre conDATEKEYS_LARGE=1. Con 27 fallos inyectados uno a uno en la firma, las claves, el escritor y los codificadores, las pruebas detectan 26; el otro es equivalente: el escritor compara la clave del veredicto con la del enganche, que no puede ser otra si la firma da F4. Ycheck-build.mjsdetecta las claves de autor en/inspect. -
Integración. Se hizo en la rama
signing, en paralelo con el localizador, y se puso encima de él y de NAT64 conrebase, con conflictos solo en las guardas, envitest.config.ts, en el README y aquí. Con las tres partes,npm run verifypasa con 8 017 pruebas y una aplazada.
El localizador de datekeys.capsule (06-10-2026)
El paquete locator de datekeys-go en spec-v0.12 (§43 a §44.1), con los mismos checks en el mismo orden, los mismos códigos y los mismos textos de error, byte a byte, como lo portó datekeys-dart en sus partes 7a y 7b.
-
Lo que no necesita criptografía (
locator.ts,ipaddr.ts): los datos de la extensión (parseInfoeinfoExtension);standardExtensions, el registro de las extensiones de la especificación, que por defecto comprueba los datos dedatekeys.capsulecomolocator.Standard; las direcciones, con cada regla del §44.1 de la v0.12 (checkURI,addressHost,usableAddresses), los bloques de IANA comparados byte a byte sobre los 16 bytes de una IPv6 y los CID v1 en base32;checkResolvedIp, la IP a la que resuelve un nombre, como la dedatekeys-dart, que hoy rechaza64:ff9b::/96; el texto en claro con su relleno (marshalLocator,unmarshalLocator,plaintextLength); y el resto en su host (restIn,hide). -
La criptografía (
ageio.ts,envelope.ts): abrir un localizador sellado con el release de su ronda (openSealed,openInfoLocator), que lee como mucho 1 MiB como Go; abrir el sobre (openEnvelope); sellar (seal) y crear el sobre (newEnvelope), con una fuente de lo aleatorio inyectable que se lee en el orden de Go.ageio.tslee y escribe ficherosagecomofilippo.io/age1.3.2, con sus textos, porquelocator.OpenyOpenEnvelopelos copian. Sin dependencias nuevas: usa los módulos de noble que ya usax25519.ts, y HMAC es el HKDF-Extract de@noble/hashes/hkdf.js. -
Nada entra en
/inspect.index.tsno reexporta el localizador: la nota trae las tablas de Unicode, y el sobre, noble.dependencies.test.tsañadeageio.tsa los que pueden importar noble y los cuatro módulos a los queindex.tsno reexporta, ycheck-build.mjsfalla si una página carga el localizador con su primera carga. -
Contra Go, todo con el resultado y el texto de Go:
vectors.test.tscorre enterotestdata/vectors/locator.json, en vez de mirar solo su campospec;testing/locator-uris.jsonytesting/locator-vectors.json, descripts/locator-go-vectors.go: 6 531 casos de direcciones, IP, textos en claro, localizadores sellados, sobres, datos de la extensión, el registro y la apertura de una cápsula cuya.dkkllevadatekeys.capsule, y las 20 585 bases del relleno de −4 100 a 16 484;testing/locator-seal.json, descripts/locator-seal-go-vectors.go: con la misma semilla,sealynewEnvelopesacan los mismos valores que Go en el mismo orden y escriben los mismos bytes;testing/locator-interop.json: Go abre los localizadores y los sobres que escribe esta librería (scripts/locator-ts-samples.mjsyscripts/locator-go-verdicts.go), de 0 bytes a 16 MiB y un byte.
Los generadores son los de
datekeys-dartcon las semillas de este repositorio, y corren en una exportación dedatekeys-goenspec-v0.12. -
Lo que el autor tiene pendiente se queda como en Go: un CID no canónico pasa,
https://[[2000::]/pasa,parseInfocomprueba menos de lo que podría yopenSealedlee 1 MiB. -
Pruebas.
locator.test.ts,envelope.test.tsylocator.interop.test.ts, con los cuatro módulos al 100 % de cobertura: 7 901 pruebas en total.
La especificación 0.12, aprobada (06-10-2026)
- El autor aprobó el 6 de octubre de 2026 el borrador v0.12, tal como estaba:
datekeys-golo cierra con el tagspec-v0.12(fe405e2).SPEC_VERSIONpasa a0.12, ytestdatase sincroniza con ese tag: solo cambia el campospecde cada fichero. testing/mutation-texts.jsonse regenera conscripts/mutation-go-texts.go: solo cambia su campospec.- La librería ya seguía el borrador: no cambia nada más.
npm run verifypasa con 7 832 pruebas.
Los vectores compartidos de la llave de palabras (06-10-2026)
testdatase sincroniza condatekeys-goen084728d, que añadevectors/wordkey.json: los casos de la llave de palabras que pide el §64 de la v0.11, que hasta ahora solo estaban en las pruebas de Go. Ningún otro fichero cambia.- Un bloque nuevo de
vectors.test.tslos corre: las palabras de 45 textos, entre ellos cada espacio del §38.1 y tres que no lo son; lo que hace un escritor con 20 textos, con el texto del error de Go; y 6 identidades con su recipient, el vector del §38.1 el primero. La librería no cambia: todos coinciden.
El borrador v0.12: testdata, la regla del §72 y la nota de inspect (05-10-2026)
testdatase sincroniza con la cabeza de la ramav0.12dedatekeys-go(601e6d2);SPEC_VERSIONsigue en0.11hasta que el autor apruebe el borrador. Trae:- los fixtures
format3_unsigned, el contenido deformat3_signedsin firma y con la misma P, yformat3_note, con nota pública; format3_seal_unsupportedconseal_type4294967295;note.json,security.jsoncon su contexto y sus líneas, los 135 casos desecurity_cms.jsony los 218 demutations.json, 178 de ellos del §64.
- los fixtures
- Los vectores de Go.
mutation-texts.jsonse regenera conscripts/mutation-go-texts.go: cambian solo los ocho casos nuevos y el nombre del deseal_type.ibe-vectors.jsongana las entradas deformat3_note,format3_unsignedy elformat3_seal_unsupportednuevo, sin tocar sus valores congelados. - La regla de los codificadores del §72,
checkWriteenextension.ts, conNOTE_IDyCAPSULE_ID, comoextension.CheckWritede Go:- el escritor de cápsulas rechaza
datekeys.notefuera del array no crítico de la cabecera o con datos que incumplen sus reglas, ydatekeys.capsuleen una cápsula; - el de
.dkkrechaza una nota, ydatekeys.capsulefuera de su array no crítico o sin datos; - los textos de error son los de Go.
- el escritor de cápsulas rechaza
- La nota pública.
checkNoteDatacomprueba los bytes de una nota en el orden de Go, yunusableNotedistingue una nota inservible de ninguna.inspectlee la nota bajo demanda, solo si la cabecera lleva una, y la vista deinspect -jsondapublic_noteypublic_note_unusable, como la CLI de Go. Por eso/inspectsigue sin cargar las tablas de Unicode. - Pruebas.
note.json, y el bloque demutations.jsoncon sus recuentos nuevos: 7 759 pruebas en total.
El lector de certificados y los textos del borrador v0.12 (05-10-2026)
Los fallos T2, T6, T7 y T8, y lo que toca a TypeScript de E2, E3, E7, E8 y E9, de spec_v0.11/revision_sesion_1_2_octubre.md (en ../docs), como los arregla datekeys-go en la rama v0.12 (601e6d2), cuyos security.json y security_cms.json leen las pruebas.
- El certificado, campo a campo, con el perfil del §29.10 del borrador v0.12: versión 3, los campos en orden, nombres de SET no vacíos, la validez en DER y sin fracción, las extensiones sin repetir y un
subjectKeyIdentifierno vacío. Uno que lo incumple no decide nada salvo que lo nombre unSignerInfo, y dos copias de uno cuentan como una. El texto de un nombre sale solo de los cinco tipos de cadena, en su alfabeto, sin quitar nada y nunca de un atributo repetido. El titular es sugivenNamey susurnameantes que sucommonName, que puede llevar el NIF, y el emisor, sucommonNameo suorganizationName, ya no el texto de todos sus atributos. - Identificadores, claves y sellos. Los OID se comparan por los bytes de su DER: un arco de 2³¹ o más es solo uno que la tabla no tiene. Un SET OF puede repetir un elemento, así que una TSA que manda dos veces su certificado ya no da S2. La clave RSA lleva parámetros NULL, exactamente un módulo y un exponente, y un módulo impar, y una clave de otro esquema que su algoritmo da F2, no F5. Un
messageImprintde otra longitud da S3, loscrlsde un token no deciden nada, y los milisegundos y microsegundos deaccuracyson INTEGER mínimos.der.tscomprueba las horas en sus formas de X.690 y admite los tipos de cadena restringidos, NumericString entre ellos. - Los textos de los veredictos. Cada nombre de un certificado va entre « y », y se muestra si cumple las reglas del autor declarado, tiene como mucho 64 puntos de código y no lleva dos espacios seguidos; si no, su SHA-256. La línea de cada firmante de F6 nombra la autoridad de su sello, y si alguna dice «antes de la fecha de apertura», la sigue «DateKeys no comprueba quién emitió los sellos.». El resultado de un firmante ajeno va en español, y una hora lleva la fracción de su sello.
- Pruebas.
cms.test.ts,der.test.tsysecuritycms.test.tsportan las deinternal/cms,internal/derysignature2_test.go, y la del emisor sincommonNameya no lo compara consigo mismo.vectors.test.tsleesecurity.jsoncon su contexto y sus líneas, y compara en cada caso desecurity_cms.jsonlos veredictos, los firmantes exigidos y los ajenos, el sello y las líneas. - Diferencial con Go.
capsule.EvaluateSecurityInyVerdicts.Linesde601e6d2dan lo mismo queevaluateSecurityyverdictLines, campo a campo, en 63 623 áreas: las de los vectores, sus mutaciones byte a byte y elemento a elemento, y áreas firmadas y selladas de verdad cuyos certificados, firmas y tokens varían campo a campo. Antes de estos cambios diferían en 13 296 de 42 986.
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,0x04y las dos coordenadas, como enx509.ParsePKIXPublicKey: con el punto comprimido, un firmante es «no verificable» (F5) y un sello da S1. UnUTF8Stringy 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.
oidOfeintOfleen 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. evaluateSecuritynunca 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.tsde 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 deder.test.tsfallaba por la longitud, y ahora prueba la frontera de 32 niveles. - La nota pública.
checkNoterechaza 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.publicNoteconserva un U+FEFF inicial, que la deja inservible como en Go. También lo conservanauthorCodey 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.
testVectorsyareaLensalen 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
TestVectorsdel núcleo, que solo pasan los ayudantes detesting/encrypt.ts:encryptVectors,encryptWithyencryptFilesWith. encryptconserva la forma decapsule.Encrypt: sin generador, falla con su texto.dependencies.test.tsycheck-build.mjsdejantesting/fuera de la librería y de las páginas.
- Lo que solo pide un generador de vectores es el argumento
- Las opciones, como en Go. Como generador,
encryptrechaza la nota y el área con el texto decapsule.Encrypt. Los errores de la nota llevancapsule:, y las opciones se comprueban en el orden denewSealer. - El tamaño con nota.
capsuleLengthacepta la nota pública y predice exactamente el tamaño del.dkccon ella. - Interoperabilidad.
capsule-vectors.jsonse 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 conHeader.PublicNote. El fichero gana el texto decapsule.Encryptpara una nota en el formato 2, y los decapsule.EncryptFilespara nueve notas inválidas. - Documentación. El README completa la tabla de módulos (
author.ts,ed25519strict.ts,der.ts,cms.ts,securitycms.tsynote.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 sinalg. - 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.tsque lo compara consigo mismo.
testdata en spec-v0.11 (01-10-2026)
testdatase sincroniza con el tagspec-v0.11dedatekeys-go(ae33434), ySPEC_VERSIONpasa a0.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.jsonylocator.json). El corpus de mutaciones pasa a 210 casos, con uno fuera del §64: una firma dealg1 que no verifica, F2.ibe-vectors.jsonañade los tres fixtures y rehace los deformat3_signature_unsupportedyformat3_seal_unsupported;mutation-texts.jsonse rehace con elcapsule.Opende esa referencia.- La firma de clave propia,
alg1 (§29.8, §29.9), portada:ed25519strict.tscomprueba 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 deed25519_strict.json;author.tscalculapayload_commit,control_commit,head_digest,signers_digest,AUTHOR_MESSAGEy su código, y los registros deformat3_signedlos confirman.evaluateSecurity(área, contexto)da F2, F3 y F4,openpasa el contexto del control y del head, yOpenOptions.authorKeysson las claves que la persona guardó. Los dos módulos usan solo@noble/curvesy@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,
alg2, y el sello RFC 3161,seal_type2 (§29.10, §29.11), portados sin dependencias nuevas:der.tscomprueba el DER byte a byte,cms.tslee la firma CMS y el token con la tabla cerrada de algoritmos (RSA PKCS #1 y PSS enBigInt, síncrono, y ECDSA con la aritmética de@noble/curves) ysecuritycms.tsda F1, F2, F5 y F6 con los firmantes nombrados, y S1 a S5 con la autoridad del sello.evaluateSecuritylos devuelve con sudetail,verdictLinesescribe las líneas de F6 y S4, yopenpasa la hora de la ronda. Reproducen los 22 casos desecurity_cms.json, con los resultados de cada firmante, y los fixturesformat3_signed_cmsyformat3_sealed.testing/cmsbuild.tsconstruye firmas y tokens de prueba con WebCrypto, ycms.test.tsporta los casos hostiles de Go. - El escritor de la v0.11 (§29.2, §24.1, §62.1 regla 13):
encryptFilesescribe el área de seguridad de 32 KiB (AREA_LENpasa de 512 a 32768) y aceptapublicNote, la extensióndatekeys.notede 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, contestVectorsyareaLen, y así los tests reproducen byte a byte los fixtures que escribió un escritor de la v0.10, de 512 bytes.lengths.tsy 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 (
alg1,alg2 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óndatekeys.capsulede la.dkk, su sobre y su relleno) y la página de firma.locator.jsonsolo 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 depathrule.ts, no con la de la plataforma; cuentan solo las distintas de 3 letras o más; y se rechazan los caracteres invisibles.encryptFilesrecibe las palabras (words) y deriva la llave al sortear elcapsule_id./createpide 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 asrc/lib/dkc/wordkey.ts. /inspectpide 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 deagemá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
ageen 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 unTypeError. - El sitio es siempre claro.
/createse 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. /inspectmarca 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
.dkcantes de escribir, con los ficheros medidos una vez (measureFiles,headLengthOfybodyLengthOfenlengths.ts), y el progreso de las dos lecturas deencryptFiles. create-files.ts(la lista, sin tablas) ycreate-check.ts(las reglas en español, bajo demanda), al 100 %. Go abre una cápsula de la página, y/inspecttambié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) yopener.ts(OpenedFiles, la falta de espacio), cargados bajo demanda;ZipSinkcomprueba el espacio al empezar.check-build.mjsexige 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.zipOfhace el mismo ZIP en memoria.tempfile.ts: elwritablede un fichero temporal acepta trozos con posición (TempChunk), comoFileSystemWritableFileStream.- Interoperabilidad:
archive/zipde 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, descripts/zip-ts-samples.mjsyscripts/zip-go-read.go).
Paso 4: la escritura del formato 3
writer.tsse parte como el writer de Go:newSealercomprueba las opciones que no dependen del contenido, ysealescribe la cápsula de un formato alrededor de un contenido dado en trozos. El formato 2 no cambia.encryptFiles(files, opts)escribe un.dkcde formato 3 con sus ficheros, el comentario y el autor declarado, y las extensiones del head, comocapsule.EncryptFiles: lee cada fichero dos veces y falla, con el texto de Go, si cambió entre las dos lecturas.fileSourcehace la fuente de unFile. Los sorteos ganan la sal del head.lengths.ts:bodyLength,headLength,mtimeSecondsyheadComment, para dar el tamaño exacto del.dkcantes de escribirlo.encryptescribe el formato 2 solo contestVectors, y sin comentario, autor ni extensiones del head, con los textos decapsule.Encrypt; las pruebas,encryptWithy los scripts de muestras lo piden./createescribe el formato 3 conencryptFiles: el fichero va bajo su nombre y con su fecha de modificación, y el plan da el tamaño exacto conbodyLength.- Interoperabilidad:
capsule-vectors.jsonse regenera con seis cápsulas de formato 3 escritas porencryptFiles, que Go abre en unSinkcon 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 aencodeHead; y las entradas inválidas, con los textos que dacapsule.EncryptFilesa las mismas entradas.
Paso 3: la lectura del formato 3, con testdata en spec-v0.10
testdatase sincroniza con el tagspec-v0.10dedatekeys-go(cc35d2c), ySPEC_VERSIONpasa a0.10.framing.ts:FORMAT_3, eisPaddedpara los formatos 2 y 3.control.ts: la versión de schema 3, con las claves 6 y 7 de la 2.open.tsyopen3.ts: el paso 17 del formato 3 en sus subpasos, con la precedencia y los textos decapsule.Open. Los ficheros van aOpenOptions.sink(sink.ts:SinkyMemorySink), que solo los publica en el paso 18; sin sumidero,openrechaza con unTypeErrorjusto tras el paso 2.Openedganahead,verdicts,areaLenyunusableHeadExtensions.- La página:
opener.tsabre 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 conscripts/mutation-go-texts.goentesting/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.jsongana los nueve fixtures nuevos. check-build.mjsimpide 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.tsyhead.ts, comocapsule/format3.go: la trama deBODY, los veredictos del área de seguridad con sus textos, y el head con las capas de §69.1.ERR_HEAD_INVALIDes 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 pordatekeys-gocon Unicode 18.0.0 y WindowsBestFit, ypathrule.ts, las reglas de las rutas y de los textos del head con los textos de error de Go.crc32.tsyzip.ts, ensrc/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
/inspectmuestra 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.tsda esos primeros bytes (PREVIEW_BYTES), yopening.tsdecide qué se muestra (plaintextPreview).- Ayuda de las claves de
age. En/create, un bloque plegable explica qué es un destinatarioage1…y cómo se consigue conage-keygen. En/inspect, el campo de identidades dice qué pegar: la líneaAGE-SECRET-KEY-1…del fichero deage-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,.jpgy otras por sus primeros bytes. La cápsula no guarda el nombre del fichero (§6, §55.2). vite previewsirve las páginas conCache-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_onlypor 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 nombrescapsula-<apertura en UTC>. /createcifra un fichero propio en un.dkcde formato 2 y, si se pide, en una.dkkportable, en el navegador y sin red. Antes de cifrar muestra el instante efectivo, la ronda, ladk1_, el tamaño exacto del.dkcy lo que deja ver hasta la fecha. El.dkcva a un fichero temporal de OPFS, o a memoria hasta 64 MiB, y la escritura se puede cancelar. La.dkkvive 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:sealedControlLengthsale dewriter.ts, ycapsuleLengthda el tamaño del.dkcantes de escribirlo; el bucle de propiedades lo comprueba en cada cápsula.datekey.ts:LONG_HORIZON_SECONDSeisLongHorizon.localtime.ts,create-input.tsycreator.ts, al 100 %./inspectlimpia también la zona de crear.check-build.mjscomprueba 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
/inspectcon el release pegado y condatekeys decryptde Go, con el mismo contenido. - Una revisión adversarial encontró un fallo mayor y ocho menores, todos corregidos. El mayor: la
.dkkque 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.tscompara 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.tsytesting/capsule-vectors.json: Go abre concapsule.Openlas trece cápsulas de muestra que escribeencrypt, 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.mjsyscripts/capsule-go-verdicts.go, y se congela; el test comprueba en cada ejecución los veredictos de Go y queopenda lo mismo sobre los bytes congelados.
Pasos 3 y 4: el writer
encrypt.tsywriter.ts:encrypt(src, opts)escribe un.dkcde formato 2 y, si se pide, una.dkkportable, comocapsule.Encrypt: L conocida de antemano, rellenoreforzadopor defecto, de 1 a 16 credenciales con señuelos en un orden uniforme,SEALED_CONTROL_LENcon la fórmula del §62.1 y las autocomprobaciones de la regla 11 y dos más. Streaming desdeUint8Array,BloboReadableStream, hacia memoria o hacia unWritableStreamque 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-encryptionsustituido y un bucle de propiedades (50 semillas en cada ejecución; 500 pasaron a mano).
Paso 2: piezas de apoyo
recipient.ts: recipientsage1…comoage1.3.2, las reglas de §37 con los textos deagewrap.CheckX25519Recipienty 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 ficherosageenteros que antes eran privados de la apertura, para compartirlos con el writer.x25519.ts:newX25519Identityyx25519PublicKey, con los vectores de RFC 7748.digest.ts:sha256Hasher.datekey.ts:compareInstants, que usaopen.ts, eisInstant.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.tsy los tests importanage-encryption; soloencrypt.tsytesting/importanwriter.ts;index.tsno 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); otroVERSIONesERR_UNSUPPORTED_VERSIONen el paso 2.DKC_FRAMING_VERSIONdesaparece:FORMAT_1,FORMAT_2eisFormat. CONTROL_CBORse lee y se escribe para un formato,decodeControl(b, format)yencodeControl(c, format): su versión de schema es la del formato, y en el formato 2 llevapayload_length(8 bytes, hasta L_MAX) ypadding(claves 6 y 7).padding.ts: las reglasbloque256yreforzadode §29.1, exactas hasta L_MAX = 2⁵³ − 2⁴⁶, y la longitud dePAYLOAD_AGE.open: exactamente 16 stanzas enINNER_ACCESS_AGEdel 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.Openeddaformat,payloadLengthy, en el formato 2,paddingypaddedLength.- La vista JSON de
inspectllevaformat, comodatekeys 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.
testdatasincronizado conspec-v0.9: doce fixtures, siete de ellos del formato 2,padding.json, 125 casos de mutación y 4 380 del diferencial.ibe-vectors.jsonañ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_CBORy.dkk. - Tramas DKC1 y DKK1, cabeceras
agey 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-encryption0.3.1,@noble/curvesy@noble/hashes2.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 deagewrapse apoyan enx25519.ts, que abre cada stanza X25519 por separado, y enbech32.ts. Los 65 casos del corpus de mutaciones pasan poropencon el código y el paso de Go, y los cinco fixtures oficiales se abren a su texto en claro; @noble/ciphers2.4.0 como dependencia directa, aprobada el 28-09-2026: la copia que ya traeage-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 descifraPAYLOAD_AGEen streaming. La salida puede ser unWritableStream, 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 elRecipientdeOUTER_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 contlock.TimeUnlocky el ficheroageconagewrap.NewTimeIdentity; - la acción "abrir" de
/inspect(paso 8), cuyo código, con noble yage-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_keyson una.dkko identidadesAGE-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;
readAccessKeyenprefix.ts.
- dependencias de ejecución (
VERSIONySPEC_VERSION, también en el pie de la página.licenses.txten el sitio, con los avisos detlock-jsyagey los de cada paquete del bundle.