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/README.md

370 lines
61 KiB

# datekeys-ts
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Implementación en TypeScript del protocolo DateKeys v0.9 y página de prueba en el navegador. Sustituye al prototipo, archivado en `../archive/prototype` (API Quicknet en Go, CLI tlock y cliente Svelte, commit `4d2b0a1`).
La implementación de referencia es la librería Go `g.activething.com/go/DateKeys`, en `../datekeys-go`. Los planes y el estado del trabajo están en `../docs`, el repositorio privado de documentación del proyecto.
## Contenido
| Parte | Ubicación | Paso del plan | Estado |
|---|---|---|---|
| Codec CBOR del subconjunto, parsers de schema, DateKey, `inspect` | `src/lib/dkc/` | 4 | hecho |
| Página inspector, sin red | SvelteKit estático: `src/routes/`, `src/lib/inspector/`, `src/lib/components/` | 5 | hecho |
| Apertura de cápsulas en el navegador, y la acción "abrir" de la página | fase 2 (`PLAN_fase2_ibe_noble2.md`, en `../docs`) | 6 | hecho (pasos 2 a 8 de la fase 2) |
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| Escritura de cápsulas en TypeScript | fase 3 (`PLAN_fase3_escritura.md`, en `../docs`) | 7 | hecho: la librería, su interoperabilidad con Go y la página `/create` (pasos 0 a 7 del plan) |
## Versiones
Hay tres números de versión, cada uno con su significado, como en la referencia Go:
| Versión | Dónde | Cambia cuando |
|---|---|---|
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| Formato | Dentro de los objetos: el formato de la cápsula, el `VERSION` del prelude de DKC1, 1 o 2 al leer, que fija también la versión de schema de CONTROL_CBOR; y 1 en la trama DKK1 y en el schema de los demás objetos | Cambia el formato. Un lector rechaza una versión que no conoce (§22, §70) |
| Especificación | `SPEC_VERSION` de `src/lib/dkc/version.ts`, hoy `0.9`: la del tag `spec-v0.9` de `datekeys-go` | Cambia el texto normativo |
| Librería | `VERSION` de `src/lib/dkc/version.ts`, igual al campo `version` de `package.json` | Cambia la API o el comportamiento. Versionado semántico, sin promesa de estabilidad antes de 1.0.0 |
`version.test.ts` comprueba que `VERSION` coincide con `package.json` y con su lockfile, y que `SPEC_VERSION` es la versión que nombran los vectores y fixtures compartidos; `vectors.test.ts` exige esa versión a cada fichero. El pie de la página muestra las dos.
La versión actual es `0.2.0-dev`: la fase 3 añade la escritura de cápsulas de formato 2. La última publicada es `0.1.0`, del 29 de septiembre de 2026, con el tag `v0.1.0`, que cubre:
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- la especificación 0.9: lee los formatos de cápsula 1 y 2 (escribir, que solo escribirá el formato 2, llega en la fase 3);
- solo el scheme de Quicknet (`bls-unchained-g1-rfc9380`): un perfil de otro scheme se inspecciona, pero su release no se verifica (decisión 3 del plan de la fase 2);
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- la inspección de los pasos 1 a 8 y la apertura de los pasos 9 a 18 (`open.ts`), desde un `Uint8Array` o un `Blob` y hacia memoria o hacia un stream de salida, como el de un fichero OPFS, también desde la página `/inspect`. Escribir cápsulas llega en la fase 3;
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- todos los vectores y fixtures compartidos de `datekeys-go` en `7e2d83c` (`spec-v0.9`);
- navegadores con Web Crypto y Node 20 o posterior.
[CHANGELOG.md](CHANGELOG.md) recoge los cambios de cada versión.
## `src/lib/dkc`
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
La inspección (pasos 1 a 8) no importa ninguna dependencia. Funciona en navegadores y en Node 20+: solo usa `Uint8Array`, `DataView`, `TextEncoder`/`TextDecoder`, `BigInt` y `crypto.subtle` (SHA-256). La apertura (fase 2) usa las dependencias de ejecución de su sección: noble solo lo importan `digest.ts`, `ibe.ts`, `release.ts` y `x25519.ts`, y `age-encryption` solo `open.ts` y `tlock.ts`.
`crypto.subtle` solo existe en contextos seguros: `https`, o `http` en `localhost`. La página del paso 5 servida por `http` desde una IP de la red local (por ejemplo `vite --host` para probar en un móvil) no lo tiene, y `inspect` rechaza entonces con un `Error` que lo dice (`SHA-256 needs Web Crypto (crypto.subtle), …`) en vez de dar un veredicto. El registro por defecto no memoriza ese fallo: la siguiente llamada lo vuelve a intentar.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| Fichero | Contenido | Equivale en Go (`spec-v0.9`) |
|---|---|---|
| `errors.ts` | `DateKeysError` con el código normativo de §69 (`ERR_*`); mensajes con la forma `contexto: CÓDIGO` de Go | `errors.go` |
| `bytes.ts` | Hex, UTF-8 estricto, `goQuote` (el `%q` de Go, con la tabla de `strconv.IsPrint` de Go 1.26 fijada en el código) y `sha256` (Web Crypto) | `strconv`, `unicode/utf8` |
| `cbor.ts` | El codec propio de la referencia, con las mismas lecturas, las mismas comprobaciones en el mismo orden y los mismos textos de error: `Encoder` con error persistente; `Decoder`, cursor estricto (`map`/`key`/`endMap`, `array`, `uint`/`uint64`, `bstr`, `text`, `done`); `unmarshal` (decodifica, reencodifica y compara; `onReject` para borrar secretos); `peek`/`checkSchema` (capa 2 de §69.1: tipo y versión antes que nada) y `walk` (lector genérico acotado en profundidad y longitud) | `codec` |
| `schema.ts` | Lo que comparten los decodificadores de PUBLIC_HEADER, CONTROL_CBOR y el cuerpo de la `.dkk`: `key N: ` en los errores, claves obligatorias y arrays de extensiones | `capsule/framing.go`, `accesskey` |
| `extension.ts` | Arrays de extensiones, leídos con su objeto (capa 3 de §69.1). Registros con ubicación opcional (`registeredIn`): una extensión conocida fuera de los objetos y arrays de su registro cuenta allí como desconocida (§54, §72), como `extension.Placement` en Go. Reglas del array: de 1 a 64, en orden estrictamente ascendente de los bytes UTF-8 de `extension_id` (nunca por unidades UTF-16), `extension_version` hasta 2³² − 1, `data` ausente o `bstr` no vacío, ningún id en los dos arrays; registros, críticas y no críticas (capa 4) | `extension` |
| `profile.ts` | Provider Profile: CBOR exacto, `profile_hash`, reglas 1 a 4 de §12.1 en su orden (el límite de `period` de §74 en la capa del esquema; alfabetos, clave pública del grupo del scheme y fórmula de `chain_hash`), registro pinneado; Quicknet fijado por su CBOR y su hash | `profile` |
| `bls12381.ts` | Pertenencia de claves públicas BLS12-381 comprimidas (G1 y G2) al subgrupo, como `FromCompressed` de kilic | `kyber-bls12381` |
Phase 2, step 7: tlock encryption, checked against Go both ways - ibe.ts gains encryptOnG2RFC9380, EncryptCCAonG2 of kyber with the suite of tlock for Quicknet. Qid is H(id) on G1 with the RFC 9380 DST, sigma comes from crypto.getRandomValues, U = r·G2, V = sigma XOR H2(e(Qid, key)^r) and W = msg XOR H4(sigma). The key passes the canonical gate, and sigma and the masks are wiped. encryptOnG2WithSigma takes a given sigma, for the vectors only; index.ts exports neither. - tlock.ts adds timeRecipient, the age-encryption Recipient of OUTER_TIME_AGE, as Go's agewrap.TimeRecipient. It writes the stanza "tlock <round> <chain hash>" with the checks and texts of NewTimeRecipient: the scheme and the pinned key, then the round range. age-encryption has no labels, so the writer of phase 3 adds it alone. Vectors, in src/lib/dkc/testing/tlock-vectors.json from scripts/tlock-go-vectors.go: - Fixed-sigma encryptions of 1, 16 and 32 bytes for rounds 1000 and 1001. Go restates EncryptCCAonG2, since kyber draws sigma itself, and checks the restatement with ibe.DecryptCCAonG2 and tlock.TimeUnlock. encryptOnG2WithSigma reproduces them byte for byte. - The samples of scripts/tlock-ts-samples.mjs, which Node runs on the TypeScript sources: IBE bodies and age files that this library made for rounds 1000 and 1001. Go opened every one: the bodies with tlock.TimeUnlock and the age files with age.Decrypt and agewrap.NewTimeIdentity, the identity of step 11. It got the same file keys and plaintexts, and the samples are frozen with those verdicts. tlock.test.ts replays both blocks, the random round trip, the rejections with their texts, and an age file sealed with timeRecipient and opened with the step-11 identity of open.ts. Coverage of ibe.ts and tlock.ts is 100 %, now a threshold for tlock.ts too. Step 6 of the plan is recorded as done: the canonicality amendment is in spec-v0.8.2. npm run verify is green: 2,567 tests. The site does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `ibe.ts` | IBE-CCA de tlock sobre G2 para Quicknet (§63 paso 11): `decryptOnG2` y `encryptOnG2RFC9380` (Qid = H(id) en G1 con el DST de RFC 9380, sigma aleatorio, U = r·G2), con la puerta de codificación canónica de `bls12381.ts` sobre la firma y U; H2 sobre GT serializado en el orden de kilic (nunca `Fp12.toBytes` de noble), H3 y H4; `roundIdentity`; el cuerpo `U ‖ V ‖ W` de 128 bytes del stanza. Errores `IbeError` con motivo (`length`, `encoding`, `identity`, `proof`) y texto fijos, sin ningún valor del cálculo; borra sigma y los hashes derivados. Sobre `@noble/curves` 2.4.0; lleva el aviso MIT de `tlock-js`, cuya estructura sigue. Lo usa la apertura (`open.ts`) | `encrypt/ibe` de drand/kyber (`DecryptCCAonG2`), `tlock.BytesToCiphertext` y `TimeUnlock` |
Phase 2, step 5a: open capsules, steps 9 to 18, in memory src/lib/dkc/open.ts runs steps 9 to 18 of spec §63 on top of the steps 1 to 8 of inspectWith. It follows capsule.Open of the Go reference, with its checks, codes and texts: - step 9: the access credentials (the .dkk as an object, then its capsule_id and capsule_digest), then the release, never before the round time. Any failure of the source is ERR_RELEASE_UNAVAILABLE alone, keeping its text and its cause (correction 6); - step 10: verifyRelease; - steps 11 to 13: OUTER_TIME_AGE, the structure against access_policy and INNER_ACCESS_AGE; - steps 14 to 18: CONTROL_CBOR, header_binding, I_PAYLOAD, PAYLOAD_AGE and the commit. The three age files open with the Decrypter of age-encryption and identities that apply the rules of Go's agewrap: the tlock identity on ibe.ts, and the access and payload identities on x25519.ts. A failure of age that no identity reports is ERR_INTEGRITY with the fixed reason of its phase, header or STREAM, never the text of age-encryption. The plaintext is decrypted in memory and returned only after step 18. Streaming to OPFS is step 5b. src/lib/dkc/x25519.ts opens one age X25519 stanza at a time, in the order of age's X25519Identity. Step 13 must try every identity on every stanza (spec §36), and age-encryption's Decrypter stops at the first. Its primitives are the ones age-encryption uses: X25519 and HKDF from noble curves and hashes, and ChaCha20-Poly1305 from @noble/ciphers 2.4.0. The author approved declaring that package as a direct dependency on 2026-09-28; it is the copy already installed and bundled. The guards now allow ciphers, and x25519.ts in the noble allowlist. src/lib/dkc/bech32.ts ports age's internal/bech32, with its MIT notice, to read AGE-SECRET-KEY-1 identities. Tests: - vectors.test.ts runs all 65 cases of the mutation corpus through open. Each gives the code and the step of Go, and no case that fails without the network requests a release. This includes the 34 cases of steps 9 to 18 that were skipped, so the suite no longer skips any test. - open.test.ts: - the five official fixtures open to their plaintext, with each credential, with the checks and details of the reference; - the unusable noncritical extensions are reported; - the source failures and the clock; - age failures by phase; - CONTROL_CBOR that does not decode, and a low-order share in INNER_ACCESS_AGE, through an OUTER_TIME_AGE resealed with the FK_TIME of the Go vectors; - the texts of the identities. - x25519.test.ts checks against age-encryption both ways and against the Go-written stanzas of the fixtures, and covers every low-order share. - bech32.test.ts has the vectors of the reference. x25519.ts and bech32.ts are at 100 % coverage, now thresholds. open.ts is at 100 % of lines; the one branch left is the one for an error that is not a DateKeysError. index.ts does not re-export the opening yet. The page imports index.ts, and re-exporting would pull noble into /inspect (58.7 to 84.9 KB gzip) even unused; step 8 will load it on demand. The site does not change. npm run verify is green: 2,490 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `release.ts` | Verificación local del release (§17, §51, §63 paso 10), en el orden y con los textos de `provider.Verify`:<br>1. el rango de la ronda (`ERR_DATEKEY_INVALID`);<br>2. la ronda del release antes que la firma (`ERR_ROUND_MISMATCH`);<br>3. la longitud de la firma;<br>4. la clave pinneada (`ERR_UNKNOWN_PROFILE`);<br>5. la firma: codificación canónica de un punto de G1 que no sea el infinito, y firma BLS válida de la ronda sobre `@noble/curves` 2.4.0, con el DST de RFC 9380 para G1 (`ERR_RELEASE_INVALID`).<br>Nada de noble se copia a los errores. Solo verifica el scheme de Quicknet: un perfil de otro scheme falla con `ERR_UNKNOWN_PROFILE` tras las comprobaciones de ronda, donde la referencia sí lo verificaría (decisión 3 del plan de la fase 2). También define `ReleaseSource`, con su contrato de fuentes de red y de la corrección 6, y `suppliedRelease`, el release que entrega quien llama | `provider` (`Verify`, `ReleaseSource`) |
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `open.ts` | Los pasos 9 a 18 de §63 sobre los pasos 1 a 8 de `inspectWith`, con los checks, códigos y textos de `capsule.Open`:<br>- las credenciales y el release (paso 9), que cualquier fallo de la fuente convierte en `ERR_RELEASE_UNAVAILABLE` (corrección 6);<br>- la verificación del release (10);<br>- `OUTER_TIME_AGE` (11), la estructura frente a `access_policy` (12) e `INNER_ACCESS_AGE` (13);<br>- `CONTROL_CBOR` (14), `header_binding` (15), `I_PAYLOAD` (16), `PAYLOAD_AGE` (17) y el commit (18).<br>Lee los dos formatos (§22, §70). En el formato 2, `INNER_ACCESS_AGE` tiene exactamente 16 stanzas (paso 12); `CONTROL_CBOR` es de la versión de schema 2, con L y la regla de relleno (14); el paso 16 calcula P, y el 17 exige un texto en claro de exactamente P bytes con ceros tras el contenido, `ERR_INTEGRITY` en otro caso. Solo se entregan los L primeros bytes, nunca el relleno (§29.1, §56). `Opened` da el formato y, en el formato 2, L, la regla y P.<br>Abre los tres ficheros `age` con el `Decrypter` de `age-encryption` y con identidades propias que aplican las reglas de `agewrap`: la de tiempo, sobre `ibe.ts`; las de acceso y payload, sobre `x25519.ts`, stanza a stanza. Los fallos de `age` que no informa una identidad son `ERR_INTEGRITY` con el motivo fijo de su fase, cabecera o STREAM, sin copiar el texto de `age-encryption`.<br>La entrada puede ser un `Uint8Array` o un `Blob`, como un `File`. De un `Blob` solo se lee el prefijo de los pasos 1 a 8 (`prefix.ts`), el `capsule_digest` de la `.dkk` se calcula sobre su stream (`digest.ts`) y `PAYLOAD_AGE` se descifra en streaming.<br>El texto en claro va a memoria o a `output`, un `WritableStream`. Se escribe a medida que `age` autentica cada chunk, se cierra solo tras el paso 18 y se aborta ante cualquier fallo, en cualquier paso (§56). Un fallo del stream de salida es `ERR_INTEGRITY` con su texto, como en Go. El `WritableStream` de un fichero OPFS guarda lo escrito en un fichero de intercambio hasta el cierre: comprobado en el navegador, un fallo de STREAM deja intacto el contenido anterior | `capsule.Open`, `agewrap` (`TimeIdentity`, `AccessIdentity`, `PayloadIdentity`) |
Phase 3, steps 3 and 4: the writer of capsule format 2 encrypt(src, opts) writes a .dkc of format 2 and, when asked, a portable .dkk (spec §61, §62, §62.1), in the order and with the texts and codes of capsule.Encrypt: - L known in advance: the size of a Uint8Array or a Blob, or the length declared with a ReadableStream; a source of another length fails with Go's texts; - reforzado padding by default, or bloque256; - 1 to 16 credentials, canonical and not of low order, a dummy in each slot left, whose scalar is wiped once its public key is derived, and a uniform order of the 16; - SEALED_CONTROL_LEN from the formula of §62.1, checked against the real seal; - the self-checks of rule 11, plus OUTER_TIME_AGE under the reader's rules and the header of PAYLOAD_AGE opened by I_PAYLOAD before anything is written. The content is streamed in pieces of 64 KiB, then the zeros of the padding, into memory (up to MAX_MEMORY_DKC) or an output that is closed only once the capsule is complete and checked and aborted on any failure. The core in writer.ts takes its random values from the caller: encrypt.ts passes crypto.getRandomValues, and only testing/encrypt.ts fixes them. Tests: the deterministic sections of the seven format 2 fixtures of Go byte for byte; round trips with open for both policies, 1 to 16 credentials and every padding boundary; the invalid options; streaming and failures of the source and the output; the internal errors with age-encryption replaced by a spy; a property loop (50 seeds per run, 500 by hand). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `encrypt.ts`, `writer.ts` | El writer de la fase 3: `encrypt(src, opts)` escribe un `.dkc` de formato 2 y, si se pide, una `.dkk` portable (§61, §62, §62.1), en el orden y con los textos y códigos de `capsule.Encrypt`:<br>- el formato 2 siempre; L conocida de antemano (el tamaño de un `Uint8Array` o un `Blob`, o `length` con un `ReadableStream`), y una fuente que da más o menos bytes falla con los textos de Go;<br>- el relleno `reforzado` por defecto, o `bloque256`;<br>- de 1 a 16 credenciales, canónicas y no de orden bajo, un señuelo en cada hueco libre, cuyo escalar se borra al derivar su clave pública, y un orden uniforme de los 16 (`random.ts`);<br>- `SEALED_CONTROL_LEN` con la fórmula del §62.1, comprobada con el sellado real;<br>- las autocomprobaciones de la regla 11 y dos más: `OUTER_TIME_AGE` con las reglas del lector, y la cabecera de `PAYLOAD_AGE`, que `I_PAYLOAD` abre antes de escribir nada.<br>Nada se escribe hasta que todo lo anterior al contenido está comprobado. El contenido va en trozos de 64 KiB, seguido de los ceros del relleno, con presión inversa, hacia memoria (hasta `MAX_MEMORY_DKC`, 1 GiB) o hacia `output`, que se cierra solo con la cápsula completa y comprobada y se aborta ante cualquier fallo. Los errores de la fuente y de la salida se relanzan tal cual.<br>El núcleo, `writer.ts`, recibe la aleatoriedad de quien lo llama: `encrypt.ts` le da la de `crypto.getRandomValues`, y solo `testing/encrypt.ts` la fija, para reproducir los fixtures de Go | `capsule.Encrypt`, `accesskey.Encode` |
Phase 2, step 7: tlock encryption, checked against Go both ways - ibe.ts gains encryptOnG2RFC9380, EncryptCCAonG2 of kyber with the suite of tlock for Quicknet. Qid is H(id) on G1 with the RFC 9380 DST, sigma comes from crypto.getRandomValues, U = r·G2, V = sigma XOR H2(e(Qid, key)^r) and W = msg XOR H4(sigma). The key passes the canonical gate, and sigma and the masks are wiped. encryptOnG2WithSigma takes a given sigma, for the vectors only; index.ts exports neither. - tlock.ts adds timeRecipient, the age-encryption Recipient of OUTER_TIME_AGE, as Go's agewrap.TimeRecipient. It writes the stanza "tlock <round> <chain hash>" with the checks and texts of NewTimeRecipient: the scheme and the pinned key, then the round range. age-encryption has no labels, so the writer of phase 3 adds it alone. Vectors, in src/lib/dkc/testing/tlock-vectors.json from scripts/tlock-go-vectors.go: - Fixed-sigma encryptions of 1, 16 and 32 bytes for rounds 1000 and 1001. Go restates EncryptCCAonG2, since kyber draws sigma itself, and checks the restatement with ibe.DecryptCCAonG2 and tlock.TimeUnlock. encryptOnG2WithSigma reproduces them byte for byte. - The samples of scripts/tlock-ts-samples.mjs, which Node runs on the TypeScript sources: IBE bodies and age files that this library made for rounds 1000 and 1001. Go opened every one: the bodies with tlock.TimeUnlock and the age files with age.Decrypt and agewrap.NewTimeIdentity, the identity of step 11. It got the same file keys and plaintexts, and the samples are frozen with those verdicts. tlock.test.ts replays both blocks, the random round trip, the rejections with their texts, and an age file sealed with timeRecipient and opened with the step-11 identity of open.ts. Coverage of ibe.ts and tlock.ts is 100 %, now a threshold for tlock.ts too. Step 6 of the plan is recorded as done: the canonicality amendment is in spec-v0.8.2. npm run verify is green: 2,567 tests. The site does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `tlock.ts` | `timeRecipient`, el `Recipient` de `age-encryption` para `OUTER_TIME_AGE` (§32, §35), como `agewrap.TimeRecipient`: cifra la file key con `ibe.ts` para una ronda de un perfil pinneado y escribe el stanza `tlock <ronda> <chain hash>` de tlock. Comprueba el perfil y luego el rango de la ronda, con los textos de `NewTimeRecipient`. `age-encryption` no tiene etiquetas, así que quien escriba `OUTER_TIME_AGE` (fase 3) lo añade como único recipient | `agewrap.TimeRecipient` |
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `lengths.ts` | El tamaño de un `.dkc` de formato 2 antes de escribirlo: `sealedControlLength`, la fórmula de `SEALED_CONTROL_LEN` del §62.1 con la que el writer comprueba su sellado, y `capsuleLength`, el tamaño exacto que escribe `encrypt` para una ronda, una política, L, el relleno y las extensiones, que la página muestra antes de cifrar porque cualquiera con el fichero lo ve (§55.2). Sin noble ni `age-encryption` | `capsule.Encrypt`, que mide un borrador sellado |
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `padding.ts` | El relleno del formato 2 (§29.1): los códigos 1 (`bloque256`) y 2 (`reforzado`), `paddedLength`, exacta hasta L_MAX = 2⁵³ − 2⁴⁶ (`bitlen` con `BigInt` y los redondeos con `ceil`, exactos en doubles; nunca operaciones de 32 bits, `Math.clz32` ni `Math.log2`), y la longitud de `PAYLOAD_AGE` | `capsule/padding.go` |
| `digest.ts` | SHA-256 incremental con `@noble/hashes` (`sha256Hasher`, `sha256Stream`), para el `capsule_digest` de un `.dkc` que no está en memoria o que se está escribiendo (Web Crypto solo calcula el hash de buffers enteros) | |
| `agefile.ts` | Ficheros `age` enteros con el `Decrypter` de `age-encryption`, compartidos por la apertura y las autocomprobaciones del writer: los errores de una identidad conservan su código, y cualquier otro fallo de `age` es `ERR_INTEGRITY` con el motivo fijo de su fase, cabecera o STREAM. Lo que se lee en memoria se borra trozo a trozo | `capsule.Open` |
| `recipient.ts` | Recipients `age1…` como los lee y escribe `age` 1.3.2, con sus textos, y las reglas de §37 y §62.1 (regla 3) con los de `agewrap.CheckX25519Recipient`: se rechazan los no canónicos (bit 255, u ≥ p) y los de orden bajo, comprobados con la lista de sus cinco coordenadas u, sin aritmética de curva; un punto del twist se acepta, como en Go. También lee la lista de recipients que escribe una persona, como un fichero `-R` de `age` algo más tolerante, con errores por número de línea que nunca citan su contenido. Sin noble, para que una página valide las líneas sin cargar el writer | `age` (`ParseX25519Recipient`), `agewrap.CheckX25519Recipient` |
| `random.ts` | Un índice uniforme sin sesgo (rechaza las palabras de 32 bits desde ⌊2³²/n⌋·n) y la permutación de Fisher–Yates, para el orden de los 16 huecos de `INNER_ACCESS_AGE` (§39) | `capsule.permute` |
| `x25519.ts` | El stanza X25519 de `age`, abierto de uno en uno como `X25519Identity.Unwrap` de `age`: argumentos, share, acuerdo de claves, longitud del cuerpo y autenticación, en ese orden, con las primitivas que usa `age-encryption` (X25519 de `@noble/curves`, HKDF-SHA-256 de `@noble/hashes` y ChaCha20-Poly1305 de `@noble/ciphers`). También lee identidades `AGE-SECRET-KEY-1…`, genera identidades nuevas en bytes crudos (`newX25519Identity`) y deriva su clave pública (`x25519PublicKey`) | `filippo.io/age` (`X25519Identity`), `agewrap` |
Phase 2, step 5a: open capsules, steps 9 to 18, in memory src/lib/dkc/open.ts runs steps 9 to 18 of spec §63 on top of the steps 1 to 8 of inspectWith. It follows capsule.Open of the Go reference, with its checks, codes and texts: - step 9: the access credentials (the .dkk as an object, then its capsule_id and capsule_digest), then the release, never before the round time. Any failure of the source is ERR_RELEASE_UNAVAILABLE alone, keeping its text and its cause (correction 6); - step 10: verifyRelease; - steps 11 to 13: OUTER_TIME_AGE, the structure against access_policy and INNER_ACCESS_AGE; - steps 14 to 18: CONTROL_CBOR, header_binding, I_PAYLOAD, PAYLOAD_AGE and the commit. The three age files open with the Decrypter of age-encryption and identities that apply the rules of Go's agewrap: the tlock identity on ibe.ts, and the access and payload identities on x25519.ts. A failure of age that no identity reports is ERR_INTEGRITY with the fixed reason of its phase, header or STREAM, never the text of age-encryption. The plaintext is decrypted in memory and returned only after step 18. Streaming to OPFS is step 5b. src/lib/dkc/x25519.ts opens one age X25519 stanza at a time, in the order of age's X25519Identity. Step 13 must try every identity on every stanza (spec §36), and age-encryption's Decrypter stops at the first. Its primitives are the ones age-encryption uses: X25519 and HKDF from noble curves and hashes, and ChaCha20-Poly1305 from @noble/ciphers 2.4.0. The author approved declaring that package as a direct dependency on 2026-09-28; it is the copy already installed and bundled. The guards now allow ciphers, and x25519.ts in the noble allowlist. src/lib/dkc/bech32.ts ports age's internal/bech32, with its MIT notice, to read AGE-SECRET-KEY-1 identities. Tests: - vectors.test.ts runs all 65 cases of the mutation corpus through open. Each gives the code and the step of Go, and no case that fails without the network requests a release. This includes the 34 cases of steps 9 to 18 that were skipped, so the suite no longer skips any test. - open.test.ts: - the five official fixtures open to their plaintext, with each credential, with the checks and details of the reference; - the unusable noncritical extensions are reported; - the source failures and the clock; - age failures by phase; - CONTROL_CBOR that does not decode, and a low-order share in INNER_ACCESS_AGE, through an OUTER_TIME_AGE resealed with the FK_TIME of the Go vectors; - the texts of the identities. - x25519.test.ts checks against age-encryption both ways and against the Go-written stanzas of the fixtures, and covers every low-order share. - bech32.test.ts has the vectors of the reference. x25519.ts and bech32.ts are at 100 % coverage, now thresholds. open.ts is at 100 % of lines; the one branch left is the one for an error that is not a DateKeysError. index.ts does not re-export the opening yet. The page imports index.ts, and re-exporting would pull noble into /inspect (58.7 to 84.9 KB gzip) even unused; step 8 will load it on demand. The site does not change. npm run verify is green: 2,490 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `bech32.ts` | Bech32 (BIP 173) tal como `internal/bech32` de `age`, que la referencia copia como `codec/bech32`; conserva su aviso MIT | `codec/bech32` |
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `datekey.ts` | `dk1_` canónico con las reglas de lectura de §19 (CR, LF y todo carácter fuera del alfabeto fallan el paso 1; números JSON por su valor decimal exacto), ronda desde una fecha con precisión de nanosegundos y cota de 9999-12-31T23:59:59Z (§15), parser RFC 3339 equivalente a `time.Parse(time.RFC3339Nano, …)`; `compareInstants` e `isInstant`; `LONG_HORIZON_SECONDS` e `isLongHorizon`, el umbral de 365 días de los avisos de §53 y §50, una política de producto | `datekey` |
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `header.ts`, `control.ts`, `accesskey.ts` | PUBLIC_HEADER, CONTROL_CBOR y `.dkk` (cuerpo y trama), decodificar y codificar, con las capas de §69.1. CONTROL_CBOR se lee y se escribe para un formato: versión de schema 1 sin las claves 6 y 7, o 2 con `payload_length` (8 bytes, hasta L_MAX) y `padding` (1 o 2); 103 bytes sin extensiones sea cual sea L | `capsule`, `accesskey` |
| `framing.ts` | Prelude DKC1 (16 bytes), con el formato de la cápsula, 1 o 2, y DKK1 (12 bytes) en el orden de §23 y §40, longitudes de 1 byte hasta los límites de §57, y troceo de secciones | `capsule/framing.go` |
| `age.ts` | Parser estricto de la cabecera `age` v1 (§28.1) sobre los ficheros binarios, con los textos de error de `age`; reglas de stanzas, con los 16 de `INNER_ACCESS_AGE` en el formato 2 (`ACCESS_SLOTS`); `MAX_AGE_HEADER_LEN` (2 MiB), el límite que usa la página para leer solo el prefijo de un `.dkc` grande | `agewrap`, `filippo.io/age/internal/format` |
| `inspect.ts` | Pasos 1 a 8 de §63 y la vista JSON de `datekeys inspect -json` (`inspectView`, `inspectJSON`) | `capsule/inspect.go`, `internal/inspectview` |
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `prefix.ts` | Lecturas acotadas de un `Blob`: el prefijo de un `.dkc` que necesitan los pasos 1 a 8 (`readCapsule`) y, de una `.dkk`, como mucho 12 bytes + 16 MiB + 1 (`readAccessKey`), que dan el mismo resultado que el fichero entero | |
| `index.ts` | Reexporta todo salvo la fase 2 (`ibe.ts`, `release.ts`, `open.ts`, `tlock.ts`, `x25519.ts`, `bech32.ts`, `digest.ts` y `agefile.ts`) y la fase 3 (`encrypt.ts`, `writer.ts`, `recipient.ts` y `random.ts`), y una guarda lo comprueba. La página importa `index.ts`, y reexportarlos metería noble o `age-encryption` en la primera carga de `/inspect` aunque no los use, porque noble ejecuta código al cargarse. La página carga la apertura bajo demanda (`src/lib/inspector/opener.ts`) | |
| `testing/` | Solo para tests: lectura de `testdata/` y de sus formatos (`vectors.ts`: ediciones, vectores), constructores de CBOR en hex, cirugía de cápsulas | |
Los tests (`*.test.ts`) están junto a cada fichero.
### Equivalencia con la referencia Go
El comportamiento se contrastó con la librería Go en `3820066` (`692cf87` solo cambia la regla de UTF-8 de `dk1_` descrita abajo) mediante un oráculo diferencial fuera del repositorio: 483 527 entradas de tres semillas. Son mutaciones de los cinco `.dkc` oficiales de todas las clases (bits, bytes, truncados, inserciones, borrados, longitudes y campos del prelude, cabeceras reescritas con cambios de CBOR, DateKeys, arrays de extensiones con y sin registro de extensiones, cabeceras `age` de SEALED_CONTROL y de PAYLOAD_AGE, argumentos del stanza tlock, rondas, secciones cambiadas de sitio y combinaciones de varios defectos); CBOR de cada esquema (PUBLIC_HEADER, CONTROL_CBOR, cuerpo y fichero `.dkk`, Provider Profile, arrays de extensiones); `walk`, `peek` y `checkSchema`; cabeceras `age`, preludes, cadenas `dk1_`, rondas y fechas. En todas coinciden el veredicto, el código y el paso, y también el texto del error, el valor decodificado y la salida entera de `datekeys inspect -json`, byte a byte. Un segundo diferencial con otro generador, de 407 196 entradas, se repitió contra `f6f2e9f` (la enmienda de canonicidad de puntos) con el mismo resultado, textos incluidos.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Con la v0.9 se contrastaron contra `spec-v0.9` los 125 casos de `mutations.json`, en memoria y desde un `Blob` con salida, y coinciden el código, el paso y el texto del error de `capsule.Open` en todos. Los tres de extensiones conocidas con datos inválidos solo difieren en el texto que da el validador de cada harness, que no es de la librería.
Como la referencia desde `f6f2e9f`, los errores no copian el texto de una librería: una cabecera `age` que no se puede leer da siempre `agewrap: not an age v1 header: malformed, truncated or beyond the parser limits` (`ERR_INTEGRITY`). El motivo del parser, con las palabras de `age`, queda en `cause` del error, fuera del mensaje, y los tests lo comparan con los textos de `age` para comprobar que se rechaza por la misma razón.
Precedencia de errores (§69.1): primero la trama; después el tipo y la versión de esquema (`checkSchema`); después el perfil CBOR y el CDDL, con los límites de implementación de §74, en una sola decodificación (`unmarshal` con el decodificador del esquema, que ya comprueba tipos, tamaños, rangos, `access_policy`, `extension_version`, el máximo de 64 extensiones y su orden); y solo entonces los campos con código propio, en orden de clave: DateKey, perfil fijado y extensiones críticas en PUBLIC_HEADER; `access_type` y `access_material` en la `.dkk`; los campos y la autocomprobación de `chain_hash` en el Provider Profile. Entre pasos decide el orden de §63. Un PUBLIC_HEADER que rompe a la vez el CDDL y la DateKey da `ERR_NON_CANONICAL_CBOR`, como en la referencia de `3820066`; la de `afb44a3` leía `access_policy` y `extension_version` como `uint64` y los acotaba después de la DateKey, y la vía `wideUint` que lo imitaba ya no existe.
Enteros: el decodificador lee cada entero hasta 2⁶⁴ − 1 (un `bigint` por encima de 2⁵³ − 1) y lo compara con el máximo de su campo, con el texto de la referencia (`unsigned integer 9007199254740992 above 9007199254740991`). Ningún campo de un objeto del protocolo admite más de 2⁵³ − 1; `walk` admite cualquier `uint64`, como `codec.Walk`.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`peek` lee la capa 2 de §69.1 como `codec.Peek`: una cabecera de mapa de longitud definida que anuncia al menos dos entradas y no más de la mitad de los bytes que la siguen, la clave 0 con un texto de hasta 64 bytes y la clave 1 con un entero de hasta 2⁵³ − 1, todo en su forma más corta. No lee nada más: una versión que no es la esperada es `ERR_UNSUPPORTED_VERSION` sea lo que sea lo que venga detrás (en CONTROL_CBOR, la esperada es el formato de la cápsula), y cualquier otra cosa en esas posiciones, `ERR_NON_CANONICAL_CBOR`.
UTF-8 inválido en el JSON de un `dk1_` hace fallar el paso 2 de §19 (`ERR_DATEKEY_INVALID`) en las dos implementaciones, también en un miembro que un nombre repetido reemplaza después. Hasta `692cf87` la referencia Go lo sustituía por U+FFFD (`encoding/json`) y en ese caso daba `ERR_DATEKEY_NON_CANONICAL`; el diferencial de esta implementación lo encontró y la referencia se corrigió para seguir al spec. `datekey.test.ts` y el vector *invalid UTF-8 in a member a repeated name overwrites* de `dk1.json` lo fijan.
`goQuote` escribe como Go 1.26 (Unicode 15.0.0) los textos entre comillas de los detalles de fallo: usa una tabla de `strconv.IsPrint` generada con Go y no `\p{…}`, porque cada motor JavaScript trae su propia versión de Unicode (V8 en Node 24 ya imprime runas de Unicode 16 que Go escapa, como U+31E4). `bytes.test.ts` fija el SHA-256 del conjunto completo de runas imprimibles. Si el toolchain de la referencia cambia de versión de Unicode, se regenera con `go run scripts/go-isprint-table.go` y se actualizan la tabla y el hash.
Secretos: `access_material` de un `.dkk` e `I_PAYLOAD` de CONTROL_CBOR se borran en todos los caminos, también cuando la decodificación falla a medias o `unmarshal` rechaza el valor, como el `clear` diferido de Go.
## Página inspector
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Sitio SvelteKit estático (`@sveltejs/adapter-static`, `strict`): las tres páginas se prerenderizan a HTML y no hay código de servidor.
| Ruta | Contenido |
|---|---|
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `/` | Qué es el inspector y qué garantiza; enlaza con `/inspect` y `/create` |
| `/inspect` | El inspector |
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `/create` | Crear una cápsula (fase 3, paso 7) |
`/inspect` carga un `.dkc` con el selector de ficheros, soltándolo en cualquier parte de la página o desde la lista de fixtures oficiales, y ejecuta `inspect` (pasos 1 a 8 de §63) sin registro de extensiones, como `datekeys inspect`. Muestra:
- cada paso con su número, su nombre de la CLI, `superado` o el código normativo, y el detalle (los caracteres invisibles o de control se escriben como `\uXXXX`);
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- el veredicto, `capsule_id`, la DateKey compacta y decodificada (red y ronda), el perfil fijado, la fecha de apertura en UTC y en la hora local del navegador, `access_policy`, los campos del prelude con el formato de la cápsula (con un aviso en el veredicto si es el 1, que no oculta el número de credenciales ni la longitud exacta del contenido, §55.2), los argumentos del stanza `tlock` frente al perfil fijado y el número y tipo de stanzas de OUTER_TIME_AGE y PAYLOAD_AGE;
- las extensiones de PUBLIC_HEADER según el contrato del plan §8: id (entre comillas y escapado si tiene caracteres no imprimibles), versión, crítica o no, conocida o no, longitud y hex (plegado si pasa de 64 bytes); texto si los bytes son UTF-8 imprimible; vista CBOR con `walk` si son un ítem del perfil de §58, marcada "informativo, no validado por el protocolo"; y el aviso de que son públicas, de que nada las vincula al resto de la cápsula antes del paso 15 y de que ni entonces prueban autoría (§55.1);
- **Copiar JSON**, que copia exactamente la salida de `datekeys inspect -json` (`cliJSON`: el `json.Encoder` de Go con sangría de dos espacios, `<`, `>`, `&`, U+2028 y U+2029 escapados y salto de línea final). `file` es el nombre del fichero.
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Todo el texto leído de la cápsula pasa por interpolación de texto de Svelte (nunca `{@html}`), y ni las entradas de mapas CBOR ni los identificadores de extensión se usan nunca como claves de objetos o `Map` de JavaScript.
### Abrir
Tras los pasos 1 a 8, si la cápsula es válida y su fecha de apertura ya pasó según el reloj del dispositivo, la página ofrece abrirla: pasos 9 a 18 de §63 con `open` (fase 2, paso 8). Antes de la fecha dice que nadie puede abrirla todavía y no pide nada.
- **El release lo da quien abre** (decisión 4 del plan de la fase 2, confirmada el 28-09-2026): la página nunca lo pide a la red. Se pega la respuesta JSON de drand o la firma sola en hexadecimal (`release-input.ts`), y solo se leen `round` y `signature`: cualquier otro campo, como una clave pública, se ignora, porque la raíz de confianza es el perfil fijado (§11, §13). La página enlaza la URL de drand de esa ronda (`https://api.drand.sh/<chain hash>/public/<ronda>`, con `rel="noopener noreferrer"`), que abre la persona en otra pestaña. Es un release que suministra quien llama: el paso 10 lo verifica y da sus códigos (`ERR_ROUND_MISMATCH`, `ERR_RELEASE_INVALID`). En los fixtures oficiales el campo viene relleno con el release de su registro, que es el que publicó drand.
- **Credenciales**, solo en `time_and_key`: una `.dkk` (se leen como mucho 16 MiB + 13 bytes, `readAccessKey`) o identidades `AGE-SECRET-KEY-1…`, una por línea, como un fichero de identidades de `age`. Un error de una línea se da por su número, nunca por su contenido, y el foco va al campo; las identidades se borran tras usarlas. En `time_only` la página no las pide y `open` no las usa.
- **El código de la apertura se carga bajo demanda**: la página importa `opener.ts` con `import()` al pulsar "Abrir", y con él `open.ts`, noble y `age-encryption`. `opening.ts`, que construye lo que se muestra, solo importa tipos de `open.ts`. `check-build.mjs` comprueba que ninguna página carga noble, `@scure/base` ni `age-encryption` en la primera carga.
- **El texto en claro** de un fixture se abre en memoria y se muestra, con su SHA-256 comparado con el del registro. El de un fichero propio va a un fichero temporal del almacenamiento privado del navegador (OPFS, `tempfile.ts`), escrito con `createWritable`. El navegador guarda lo escrito en un fichero de intercambio que solo se confirma al cerrarlo, y `open` lo cierra tras el paso 18 y lo aborta ante cualquier fallo (§56). Después se ofrece para descargar, con el nombre del `.dkc` sin la extensión, como hace `age`, y cada carácter no imprimible cambiado por `_`, para que un carácter de control bidireccional no disfrace la extensión. Antes de empezar se compara el tamaño de `PAYLOAD_AGE` con la cuota libre (`navigator.storage.estimate()`).
- **El fichero temporal se borra** al pulsar "Borrar", al abrir o cargar otra cápsula y al salir de la página (`pagehide`). Si el navegador terminó antes, se borra en la siguiente visita. Cada pestaña escribe en su propio directorio, `datekeys-open/<id>`, y tiene un Web Lock con ese nombre mientras existe. Así la limpieza de otra pestaña nunca borra un fichero en uso; sin Web Locks, solo borra lo que tiene más de un día. Si el navegador no tiene OPFS o `createWritable`, o los rechaza (una ventana privada, datos del sitio bloqueados), o la cuota no alcanza, la página abre en memoria hasta 64 MiB. Una apertura en curso se detiene si se carga otra cápsula: su salida deja de aceptar datos, `open` falla en el paso 17 y su fichero se borra. Si la página vuelve de la caché de atrás y adelante tras borrar el fichero, ya no lo ofrece.
- **La fecha** se vuelve a mirar cuando llega y cuando la pestaña vuelve a verse, así que una cápsula inspeccionada antes de su fecha se puede abrir sin cargarla otra vez. `open` comprueba de nuevo el reloj en el paso 9.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- **El resultado** muestra cada paso de 9 a 18 como muestra los de 1 a 8, con los checks que registra la referencia (desde la v0.9, también el 17 cuando se supera), el release verificado, las extensiones de CONTROL_CBOR, el tamaño y el SHA-256 del texto en claro y, en el formato 2, la regla de relleno y P. También avisa de que el texto está autenticado, pero no prueba quién lo escribió ni que sea el original si otros abrieron la cápsula antes (§55.1).
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Medido en Chromium (el navegador de la app de escritorio) sobre la compilación de producción: los pasos 9 a 18 tardan 0,34 s en `time_only`, la primera apertura de la página, y 0,11 s en `time_and_key_portable`, la siguiente, sin contar la descarga del código y en el hilo principal (la política no permite workers).
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
### Crear
`/create` (plan de la fase 3, sección 9, con las decisiones del paso 6) cifra un fichero propio en un `.dkc` de formato 2 y, si se pide, en una `.dkk` portable, todo en el navegador y sin red.
- **El formulario** (`create-input.ts`) pide el fichero, elegido o soltado en la página; su nombre no entra en la cápsula. Después, el día y la hora, en la zona del dispositivo, en otra de las que conoce el navegador o en UTC. Y la política: «solo con la fecha» (`time_only`, la de por defecto) o «con la fecha y una clave» (`time_and_key`). Esta lleva destinatarios `age1…`, uno por línea y comprobados mientras se escriben (`recipient.ts`, sin noble), y la casilla de la clave portable, marcada por defecto; como mucho 16 credenciales. Los campos se comprueban en su orden, y el foco va al campo del primer problema.
- **La hora local** (`localtime.ts`) se convierte al instante UTC con `Intl` y las reglas de zona que conoce hoy el navegador. Una hora que la zona se salta al adelantar los relojes se rechaza. De una que repite al atrasarlos se toma la más tardía, para no abrir nunca antes de lo querido. La cápsula no guarda la zona.
- **Antes de cifrar**, la página muestra el instante efectivo, que es el de la ronda, en la zona elegida y en UTC, y cuánto cae después del pedido. También la ronda, la `dk1_`, la hora del dispositivo junto a la UTC, el tamaño exacto del `.dkc` (`capsuleLength`) y lo que la cápsula deja ver hasta la fecha (§55.2). Y los avisos: el de protocolo preliminar (§74), siempre; los de §53 y §50, con sus textos, si el instante efectivo está a más de 365 días (`LONG_HORIZON_SECONDS`); y uno informativo si está a menos de una hora. La hora del dispositivo se lee cada segundo mientras la pestaña se ve y al crear la cápsula; la página no la pregunta a ningún servidor. Si el navegador no conoce la zona del dispositivo (V8 da entonces `Etc/Unknown`), la página empieza en UTC.
- **El writer se carga bajo demanda**: la página importa `creator.ts` con `import()` al pulsar «Crear la cápsula», y con él `encrypt.ts`, noble y `age-encryption`. El relleno es siempre `reforzado`, y no hay extensiones. Lo escrito no se ofrece si no mide lo que se mostró o si los pasos 1 a 8 lo rechazan, y ante cualquier fallo se borra la `.dkk`. Si la fecha llega mientras la página se prepara para escribir, el writer la rechaza con su propio reloj (§62.1, regla 2), y la página lo dice en el campo de la fecha.
- **El `.dkc`** se escribe en un fichero temporal de OPFS, `datekeys-create/<id>/capsule` (`tempfile.ts`), que el navegador solo confirma cuando el writer lo cierra con la cápsula completa y comprobada. Si el navegador no tiene OPFS o lo rechaza, o la cuota no alcanza, se escribe en memoria, hasta 64 MiB. La cuota se comprueba con el tamaño exacto antes del primer byte. «Cancelar» detiene la escritura tras el trozo en curso: la salida se aborta y el fichero se borra. El fichero temporal se borra al pulsar «Borrar el fichero temporal», al crear otra cápsula y al salir de la página. Si el navegador terminó antes, se borra en la siguiente visita a `/create` o a `/inspect`, que limpian las dos zonas.
- **La `.dkk`** (152 bytes sin extensiones) vive solo en la memoria de la página: nunca en OPFS, y nunca se muestra como `AGE-SECRET-KEY-1…`. Sus bytes se borran al pulsar «Olvidar la clave», al crear otra cápsula y al salir, y con ellos las URL de sus descargas. Junto a ella va el aviso de §7.4. Si la cápsula solo se abre con su `.dkk` y no se ha descargado, la página pide confirmación antes de borrarla, al olvidarla o al crear otra, y avisa antes de salir: el navegador pregunta al cerrar o recargar, y la página, al seguir un enlace del sitio. Salir de la página también cancela una escritura en curso.
- **Las descargas** van por separado, con nombres que se pueden editar. Por defecto son `capsula-<apertura en UTC>.dkc` y `.dkk`, que no dicen cuándo se creó la cápsula. La URL `blob:` de cada descarga se revoca un minuto después del clic.
- **El resultado** muestra el `capsule_id`, la política, el tamaño y el relleno, y el informe de los pasos 1 a 8 del `.dkc` escrito, con el componente del inspector.
Comprobado el 29-09-2026 en Chromium (el navegador de la app de escritorio), sobre la compilación de producción:
- un fichero de 77 bytes se cifró en `time_and_key`, con clave portable, para dentro de cuatro minutos, sin ninguna petición fuera del origen, y el informe pasó los pasos 1 a 8 con el formato 2;
- pasada la fecha, la cápsula se abrió en `/inspect`, con el release pegado de drand y la `.dkk`, y con `datekeys decrypt` de Go, con red y la `.dkk`: el mismo contenido, con el mismo SHA-256;
- los avisos de §53 y §50 salen solo a partir de 365 días;
- cancelar a mitad de 200 MB no deja fichero, y sin `createWritable` la cápsula se escribe en memoria;
- la página avisa al salir sin la `.dkk`;
- a 375 px ningún elemento desborda el ancho, ni con un nombre de fichero largo sin espacios.
Una revisión adversarial del 29-09-2026 encontró un fallo mayor: la `.dkk` que era la única credencial se podía borrar sin confirmación. Encontró también ocho menores, entre ellos una zona del dispositivo desconocida, el reloj de la página parado mientras la pestaña se veía, un mensaje equivocado con el reloj del dispositivo anterior a Quicknet y el foco durante la escritura. Todos se contrastaron y se corrigieron, y los del navegador se volvieron a comprobar en él. La revisión comparó además `localToEpochMs` con una búsqueda exhaustiva en las 418 zonas, sin ninguna diferencia en 26 114 horas locales. `localtime.test.ts` repite esa comparación en cada ejecución, alrededor de los 260 cambios de hora de 2030.
Rendimiento, informativo, en ese navegador con la ventana en segundo plano: una cápsula pequeña tarda 0,14 s en `time_and_key` y 0,2 s en `time_only`. 64 MiB tardan 5,4 s hacia OPFS (unos 12 MiB/s), y 60 MiB, 3,2 s en memoria; 1 GiB, 74,5 s hacia OPFS. Escribir 64 MiB en OPFS cuesta 1 s en trozos de 64 KiB, los del writer, y 0,55 s en trozos de 1 MiB.
| Fichero | Contenido |
|---|---|
| `src/lib/inspector/report.ts` | `buildReport`: el modelo de la página a partir de `Inspection`, sin DOM ni reloj |
| `src/lib/inspector/format.ts` | Nombres y glosas de pasos, códigos y políticas; texto imprimible y escapado; números y fechas en español; `cliJSON` |
| `src/lib/inspector/diagnostic.ts` | Notación de diagnóstico CBOR (RFC 8949 §8) de `walk`, acotada a 16 384 caracteres |
Phase 2, step 5b: open from a Blob, streaming to an output open(dkc, opts) now takes a Uint8Array or a Blob, such as a File. - Of a Blob it reads only the prefix that steps 1 to 8 need. inspectedLength and readCapsule move from src/lib/inspector/load.ts to src/lib/dkc/prefix.ts, and the page imports them from the library. - The .dkk capsule_digest is computed over the Blob's stream with the new src/lib/dkc/digest.ts, an incremental SHA-256 on @noble/hashes, since Web Crypto hashes whole buffers only. digest.ts joins the noble allowlist of the guards. - PAYLOAD_AGE is decrypted in streaming. The plaintext goes to memory, as before, or to opts.output, a WritableStream. The output is written as age authenticates each chunk, closed only after step 18, and aborted after any failure at any step, even before step 17 (spec §56). A failure of the output is ERR_INTEGRITY with its text, as Go keeps the error of the writer of the plaintext. Tests: - the fixtures from Blobs, to memory and to an output; - a truncated two-chunk payload whose first chunk reached the output before the abort; - early failures that never write; - write and close failures, and an abort that fails; - a .dkk without capsule_digest; - the whole mutation corpus again as Blobs into an output, aborted in every case; - digest.ts against Web Crypto. Coverage of digest.ts is 100 % and a threshold. Checked in the browser (dev server, real OPFS). time_only.dkc, two STREAM chunks, opened from a Blob into FileSystemFileHandle.createWritable gives 78,000 bytes with the SHA-256 of its sidecar. The same file truncated fails at step 17, and the OPFS file keeps its previous content. The quota check, the temporary file and the download belong to the page, in step 8. npm run verify is green: 2,560 tests. The site does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `src/lib/dkc/prefix.ts` | Lectura por prefijo, que también usa la apertura: de un `.dkc` grande solo se leen 16 + PUBLIC_HEADER_LEN + SEALED_CONTROL_LEN + 2 MiB + 1 bytes, y solo 16 si los pasos 1 y 2 rechazan el prelude (otro tipo de fichero, un `.dkk`, longitudes fuera de §57), siempre con el mismo resultado que el fichero entero (lo comprueba `prefix.test.ts`) |
| `src/lib/inspector/fixtures.ts` | Los fixtures oficiales, empaquetados desde `testdata/fixtures` |
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `src/lib/inspector/release-input.ts` | Lee el release pegado (respuesta de drand o firma sola) y construye la URL de drand de la ronda |
| `src/lib/inspector/opener.ts` | La apertura, cargada bajo demanda: identidades, `.dkk`, `open` con el release suministrado, SHA-256 del texto en claro |
| `src/lib/inspector/opening.ts` | `buildOpenReport`: el modelo de la apertura (pasos 9 a 18, release, extensiones de CONTROL_CBOR, texto en claro), sin DOM ni reloj; nombre del fichero descifrado |
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `src/lib/inspector/tempfile.ts` | El fichero temporal de OPFS, con un directorio y un Web Lock por pestaña y por zona (la apertura y crear), la cuota libre y la limpieza de lo que quedó |
| `src/lib/inspector/localtime.ts` | Una fecha y una hora locales de una zona como instante UTC, con `Intl`: las horas que no existen y las repetidas; la lista de zonas |
| `src/lib/inspector/create-input.ts` | El formulario de `/create`, sin DOM, reloj ni writer: `planCapsule` comprueba los campos en su orden y da lo que se muestra antes de cifrar; los destinatarios y los nombres de los ficheros |
| `src/lib/inspector/creator.ts` | La escritura, cargada bajo demanda: `encrypt` hacia el fichero temporal o la memoria, con la cancelación y la cuota, y los pasos 1 a 8 de lo escrito |
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `src/lib/components/` | `InspectionReport`, `OpenPanel`, `StepList`, `ExtensionList`, `DataView`, `Mark` |
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `src/routes/` | Layout, portada, inspector y crear |
### Fixtures
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`fixtures.ts` importa con `import.meta.glob` los `.dkc` de `testdata/fixtures` como URL (`?url`) y, de cada registro JSON, solo tres campos públicos: `description`, `release` (la ronda y la firma que publicó drand, con las que la página abre el fixture) y `plaintext_sha256` (para comparar con lo descifrado). `testdata/` sigue siendo la única fuente: Vite copia cada `.dkc` como fichero con hash en `_app/immutable/assets/` y nunca lo incrusta como `data:` (`assetsInlineLimit: 0`), y no se copia nada más. Los `.dkk`, los textos en claro y los demás campos de los registros (`payload_identity`, `control_cbor`…) no llegan al sitio; `check-build.mjs` lo comprueba. En desarrollo, `server.fs.allow` deja que Vite sirva `testdata/fixtures`.
### Sin red: la Content-Security-Policy
`kit.csp` (`svelte.config.js`, modo `hash`) pone en cada página prerenderizada, como primer elemento que carga algo, un `<meta http-equiv="content-security-policy">`:
```
default-src 'self'; frame-src 'none'; worker-src 'none'; connect-src 'self'; font-src 'self';
img-src 'self'; manifest-src 'self'; object-src 'none'; script-src 'self' 'sha256-…';
style-src 'self'; style-src-attr 'unsafe-hashes' 'sha256-…'; base-uri 'none'; form-action 'none'
```
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `connect-src 'self'`: `fetch` solo llega al propio origen, y solo se usa para los fixtures. Tampoco llega a URL `blob:`. La descarga del texto en claro es una navegación a una URL `blob:` y el enlace a drand lo abre la persona en otra pestaña: ninguno es una conexión de la página.
- `script-src`: los módulos del sitio y el hash SHA-256 del único script en línea, el arranque de SvelteKit (los nonces no sirven en HTML prerenderizado).
- `style-src 'self'`: solo hojas de estilo del sitio; sin fuentes web ni CDN, con las fuentes del sistema.
- `style-src-attr`: solo el atributo `style` del anunciador de rutas de SvelteKit, por su hash (`ANNOUNCER_STYLE_HASH`, válido para `@sveltejs/kit` 2.70.3; `app.css` lo oculta también si el navegador bloquea el atributo).
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`npm run build` ejecuta después `scripts/check-build.mjs` (`postbuild`; también `npm run build:check`), que falla si una ruta no tiene su HTML prerenderizado; si una página no tiene exactamente esa política, con la etiqueta antes de cualquier elemento que cargue recursos; si un script en línea no está en `script-src` o sobra un hash; si `style-src-attr` no coincide con los atributos `style` del bundle; si hay estilos en línea, manejadores de eventos en atributos, `@import` o URL a otro origen; si algún `.dkc` oficial no está byte a byte; si aparece en el sitio algún secreto de los fixtures (`.dkk`, textos en claro, identidades, `payload_identity`, `access_material`, `control_cbor`); si el bundle del cliente contiene `tlock-js`, `drand-client` o helpers de Babel, o una copia anidada de un paquete que no sea la de noble bajo `@noble/post-quantum`; si una página carga noble, `@scure/base` o `age-encryption` en su primera carga, o si `/inspect` y `/create` no pueden cargar bajo demanda `age-encryption`, `@noble/curves` y `@noble/ciphers`, y `/create` también `@noble/hashes`; o si a `licenses.txt` le falta el aviso de un paquete del bundle, las líneas de copyright de un módulo de `src/` derivado de otro proyecto o la licencia del sitio. `vite.config.ts` registra los módulos de cada chunk en `.svelte-kit/output/client-modules.json`, fuera del sitio. Al terminar informa del JavaScript que carga cada página, en bytes y con gzip, en la primera carga y bajo demanda, y de los paquetes npm que lleva el bundle.
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
### Avisos de licencia: `licenses.txt`
El JavaScript minimizado no conserva comentarios, así que el sitio publica `licenses.txt`, enlazado desde el pie de cada página. Lo escribe un plugin de `vite.config.ts` al compilar el cliente, con tres partes:
- los módulos de `src/` derivados de otros proyectos, con el aviso de su cabecera: `ibe.ts` (de `tlock-js`, MIT) y `bech32.ts` (de `age`, MIT);
- el fichero de licencia de cada paquete npm con algún módulo en el bundle;
- la licencia Apache-2.0 del sitio.
En un hosting estático basta con servir `build/`. Las directivas que solo funcionan como cabecera HTTP (`frame-ancestors`, `sandbox`, `report-to`) quedan para el servidor que la aloje. `crypto.subtle` exige contexto seguro: `https`, o `http` en `localhost`.
## Reglas
- Los fixtures y vectores del Go son la verdad. Este proyecto nunca genera fixtures propios: `testdata/` es una copia exacta de un commit de la librería Go.
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- Dependencias de ejecución: solo `age`, `drand`, `tlock` y lo que ellas arrastran; hoy, las de la sección «Dependencias de ejecución». El sitio lleva compilado además el runtime de cliente de Svelte y SvelteKit, el tooling que el plan elige para la página (sección 13).
- Tooling de desarrollo: solo el de la lista siguiente. Cualquier otra dependencia se propone por escrito y no se instala sin aprobación.
## Comandos
```bash
npm test # vitest, todos los tests
npm run coverage # tests con cobertura v8; falla por debajo de los umbrales
npm run typecheck # svelte-kit sync y tsc sobre todo y sobre la librería sin tipos de Node
npm run check # svelte-kit sync y svelte-check (componentes y rutas), falla con avisos
npm run dev # servidor de desarrollo: http://localhost:5173/inspect
npm run build # sitio estático en build/ y, después, scripts/check-build.mjs
npm run preview # sirve build/: http://localhost:4173/inspect
npm run build:check # solo la comprobación del sitio ya construido
npm run verify # check, typecheck, coverage y build (con su comprobación)
```
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Umbrales de cobertura (`vitest.config.ts`): `cbor.ts`, `ibe.ts`, `release.ts`, `tlock.ts`, `x25519.ts`, `bech32.ts`, `digest.ts`, `padding.ts`, `agefile.ts`, `recipient.ts`, `random.ts`, `writer.ts`, `encrypt.ts` y `lengths.ts`, y `localtime.ts`, `create-input.ts` y `creator.ts` de la página, al 100 % en líneas, ramas, funciones y sentencias; el conjunto de `src/lib/dkc` al 95/90/95/95, y el de `src/lib/inspector` también.
`vitest.config.ts` es la configuración de los tests; `vite.config.ts`, la del sitio con el plugin de SvelteKit. Vitest prefiere la primera, así que los tests de `src/lib` corren sin SvelteKit, y `src/lib/inspector` importa la librería por rutas relativas, sin el alias `$lib`. `tsconfig.json` extiende el que genera `svelte-kit sync` (por eso `typecheck` y `check` lo ejecutan antes, y `npm install` también, con `prepare`).
`npm run typecheck` pasa dos veces: `tsconfig.json` (todo, con tipos de Node para los tests) y `tsconfig.lib.json` (solo la librería y sin tipos de Node, para que no se cuele ninguna API que no exista en el navegador).
### Vectores y fixtures
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `testdata/fixtures/*.json`: cada `.dkc`, de los dos formatos, pasa los pasos 1 a 8 con los valores registrados (formato, prelude, PUBLIC_HEADER, DateKey, `capsule_id`, política, `unlock_at`, extensiones con sus bytes exactos, stanzas, `header_binding`, CONTROL_CBOR) y abre con `open` a su contenido; L, y en el formato 2 la regla y P, cuadran con la longitud de `PAYLOAD_AGE`, y cada credencial abre justo el stanza que el registro le asigna. Cada `.dkk` se decodifica campo a campo y se reencodifica byte a byte. Los fixtures nuevos se recogen solos.
- `testdata/fixtures/*.inspect.json`: la vista de `inspect` de cada `.dkc`, escrita con `inspectJSON` como la imprime la CLI (`file` incluido), es idéntica byte a byte al fichero, también el texto de cada paso.
- `testdata/vectors/dk1.json` (con los tres vectores de los refinamientos de §19: LF dentro del Base64, CR y LF después, y la versión `1.0000000000000001`), `quicknet_rounds.json` y `profile_quicknet.json`: se ejecutan todos.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `testdata/vectors/cbor.json`: cada vector genérico (`accept` y `reject`) pasa por `walk` con los `max_depth` y `max_len` del fichero; los enteros aceptados comparan su `value` (número o, por encima de 2⁵³ − 1, `bigint`), y los rechazados «above max_len» o «above max_depth» se aceptan sin ese límite. Cada vector de `schemas` pasa por el decodificador de su esquema (`decodeProfile`, `decodeHeader`, `decodeControl` con el formato del vector, 1 si no lo trae, `decodeAccessKeyBody`) con el código exacto, y un objeto aceptado se reescribe a los mismos bytes.
- `testdata/vectors/padding.json`: P con las dos reglas y la longitud de `PAYLOAD_AGE` de cada L, incluidas las fronteras en que fallan las operaciones de 32 bits y un logaritmo en coma flotante, y las longitudes por encima de L_MAX, que se rechazan. `padding.test.ts` contrasta además `paddedLength` con el §29.1 escrito en `BigInt` sobre 20 000 longitudes de todo el rango.
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `testdata/vectors/tlock_ibe.json`: el vector de H2 del IBE de tlock (§63 paso 11). Hasta que llegue `ibe.ts` (fase 2), el test lo recalcula con `@noble/curves` 2.4.0: los puntos son canónicos para `bls12381.ts`, el pairing serializado en el orden de kilic es el GT del vector y su H2 coincide; el orden propio de noble (`Fp12.toBytes`) da otro hash.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `testdata/vectors/mutations.json`: se leen los 125 casos enteros (ediciones sobre un fixture o hex congelado, release, reloj, registro, extensiones, `.dkk` e identidades). Los 125 pasan por `open` con su release, su reloj, su registro, sus extensiones, su `.dkk` y sus identidades, y dan el mismo código en el mismo paso que Go. También pasan como `Blob` con un stream de salida, que termina abortado en los 125. Son las 33 mutaciones de las dos primeras listas de §64 en cada formato, las 22 de la tercera, la del formato 2, y 37 más. Entre ellos están los 81 de los pasos 9 a 18, con las 10 mutaciones de la enmienda de canonicidad de puntos en los pasos 10 y 11 de cada formato. Ninguno de los que fallan sin red pide un release. Los 44 de los pasos 1 a 8 pasan además por `inspect`, y un test fija los recuentos y el orden. Las `.dkk` ofrecidas se decodifican.
- `testdata/vectors/inspect_differential.json`: las 4 380 mutaciones de los doce fixtures dan el mismo veredicto, código y paso que Go; los `bases` se comprueban por su SHA-256.
Phase 2, step 3: the tlock IBE on noble 2, checked against Go src/lib/dkc/ibe.ts is DecryptCCAonG2 of drand/kyber encrypt/ibe, the decryption of tlock.TimeUnlock for Quicknet, on @noble/curves 2.4.0 (plan of phase 2, section 4). It adds nothing that kyber would reject: - the signature and U pass the canonical-encoding gate of bls12381.ts, which rejects the point at infinity too; - H2 hashes GT in the order of kilic, never with noble's Fp12.toBytes; - H3 and H4 follow kyber, including the rejection sampling of r, and r = 0 never proves; - roundIdentity is drand's DigestBeacon; - the stanza body is exactly U || V || W, 128 bytes, as in tlock. Errors are IbeError with a fixed reason (length, encoding, identity, proof) and message: none carries sigma, the message, r or input bytes. Anything noble throws past the gate is a proof failure. sigma and the hashes derived from it are wiped on every path. The file keeps the MIT notice of tlock-js, whose structure it follows. index.ts does not re-export it yet; the opening of step 5 will use it. scripts/ibe-go-vectors.go writes src/lib/dkc/testing/ibe-vectors.json with kyber, tlock and age: - the GT of e(G1, G2) and of its square, with H2; - H3, including inputs accepted at the second and third iteration, and H4; - round identities; - for the tlock stanza of every official fixture, the pairing, sigma, r and the file key. tlock.TimeUnlock unwraps that file key, and age opens OUTER_TIME_AGE with it; - messages of 0, 1, 16 and 32 bytes encrypted by EncryptCCAonG2; - kyber's verdict on eleven edited copies of the time_only stanza. It restates the unexported H2, H3 and H4 and checks them on every fixture against tlock and U = r·G2. ibe.test.ts replays every vector and opens OUTER_TIME_AGE of each fixture through age-encryption with a custom Identity: the header MAC and STREAM verify, and a time_only fixture yields its control_cbor. It also gates all 157 BLS edge encodings as the Go reference decodes them and checks the fixed error texts. ibe.failure.test.ts mocks a noble failure. Coverage of ibe.ts is 100 % and is now a threshold. The site does not change. npm run verify is green: 2,397 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `src/lib/dkc/testing/ibe-vectors.json`: los valores de referencia de `ibe.ts`. Los escribe `scripts/ibe-go-vectors.go` con kyber, tlock y `age`, las librerías de la referencia Go, y `ibe.test.ts` los comprueba todos:
- el GT de e(G1, G2) y de su cuadrado, con H2 de 16 y 32 bytes;
- H3 y H4 sobre entradas fijas, entre ellas una H3 aceptada en la segunda iteración y otra en la tercera;
- la identidad de varias rondas;
- para el stanza tlock de cada fixture oficial, el pairing, sigma, r y la file key. La file key es la que devuelve `tlock.TimeUnlock`, y con ella `age` abre el `OUTER_TIME_AGE`. El test lo repite con `age-encryption`: el MAC de la cabecera y STREAM verifican, y el contenido es el `control_cbor` del registro o un `INNER_ACCESS_AGE`;
- mensajes de 0, 1, 16 y 32 bytes cifrados por `EncryptCCAonG2` de kyber;
- el veredicto de `DecryptCCAonG2` sobre copias editadas del stanza de `time_only`: U con c0 + p, en el infinito o negado, V o W alterados, la firma de otra ronda, negada o en el infinito, y longitudes erróneas.
Phase 2, step 7: tlock encryption, checked against Go both ways - ibe.ts gains encryptOnG2RFC9380, EncryptCCAonG2 of kyber with the suite of tlock for Quicknet. Qid is H(id) on G1 with the RFC 9380 DST, sigma comes from crypto.getRandomValues, U = r·G2, V = sigma XOR H2(e(Qid, key)^r) and W = msg XOR H4(sigma). The key passes the canonical gate, and sigma and the masks are wiped. encryptOnG2WithSigma takes a given sigma, for the vectors only; index.ts exports neither. - tlock.ts adds timeRecipient, the age-encryption Recipient of OUTER_TIME_AGE, as Go's agewrap.TimeRecipient. It writes the stanza "tlock <round> <chain hash>" with the checks and texts of NewTimeRecipient: the scheme and the pinned key, then the round range. age-encryption has no labels, so the writer of phase 3 adds it alone. Vectors, in src/lib/dkc/testing/tlock-vectors.json from scripts/tlock-go-vectors.go: - Fixed-sigma encryptions of 1, 16 and 32 bytes for rounds 1000 and 1001. Go restates EncryptCCAonG2, since kyber draws sigma itself, and checks the restatement with ibe.DecryptCCAonG2 and tlock.TimeUnlock. encryptOnG2WithSigma reproduces them byte for byte. - The samples of scripts/tlock-ts-samples.mjs, which Node runs on the TypeScript sources: IBE bodies and age files that this library made for rounds 1000 and 1001. Go opened every one: the bodies with tlock.TimeUnlock and the age files with age.Decrypt and agewrap.NewTimeIdentity, the identity of step 11. It got the same file keys and plaintexts, and the samples are frozen with those verdicts. tlock.test.ts replays both blocks, the random round trip, the rejections with their texts, and an age file sealed with timeRecipient and opened with the step-11 identity of open.ts. Coverage of ibe.ts and tlock.ts is 100 %, now a threshold for tlock.ts too. Step 6 of the plan is recorded as done: the canonicality amendment is in spec-v0.8.2. npm run verify is green: 2,567 tests. The site does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
El mismo tratamiento tiene `src/lib/dkc/testing/tlock-vectors.json`, los valores de referencia del cifrado (paso 7 de la fase 2), que escribe `scripts/tlock-go-vectors.go` y comprueba `tlock.test.ts`:
- cifrados con sigma fijo de mensajes de 1, 16 y 32 bytes para las rondas 1000 y 1001. Go reescribe `EncryptCCAonG2` porque kyber toma sigma de `crypto/rand`, y comprueba su reescritura descifrando con `ibe.DecryptCCAonG2` y, en los de 16 bytes, con `tlock.TimeUnlock`. `encryptOnG2WithSigma` los reproduce byte a byte;
- la interoperabilidad de TypeScript a Go. `scripts/tlock-ts-samples.mjs` cifra con esta librería un cuerpo IBE y un fichero `age` escrito con `timeRecipient`, para las rondas 1000 y 1001. Go abre los cuerpos con `tlock.TimeUnlock` y los ficheros con `age.Decrypt` y `agewrap.NewTimeIdentity`, la identidad del paso 11, y obtiene la misma file key y el mismo texto. Esas muestras son aleatorias, así que se congelan con el veredicto de Go.
Para regenerarlo: `node scripts/tlock-ts-samples.mjs > ts-samples.json`, y desde el mismo módulo Go temporal, `go run tlock-go-vectors.go ts-samples.json > tlock-vectors.json`.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
En `ibe-vectors.json`, H2, H3 y H4 no son públicas en kyber: el script las reescribe con sus etiquetas y las comprueba en cada fixture contra la file key de tlock y contra U = r·G2. Los cifrados de kyber usan un sigma aleatorio, así que el fichero se genera una vez y se congela. Para regenerarlo, desde un módulo Go temporal que requiera la referencia (`replace g.activething.com/go/DateKeys => ../datekeys-go`, `GOFLAGS=-mod=mod`, y la directiva `go` de la referencia, para que se use su toolchain): `go run ibe-go-vectors.go ../datekeys-ts/testdata/fixtures > ibe-vectors.json`. Los siete fixtures del formato 2 se añadieron el 29-09-2026 así, sobre `spec-v0.9`, tomando solo el bloque `fixtures`: los valores de los cinco anteriores salieron idénticos, y el resto del fichero no cambió.
Phase 3, steps 3 and 4: the writer of capsule format 2 encrypt(src, opts) writes a .dkc of format 2 and, when asked, a portable .dkk (spec §61, §62, §62.1), in the order and with the texts and codes of capsule.Encrypt: - L known in advance: the size of a Uint8Array or a Blob, or the length declared with a ReadableStream; a source of another length fails with Go's texts; - reforzado padding by default, or bloque256; - 1 to 16 credentials, canonical and not of low order, a dummy in each slot left, whose scalar is wiped once its public key is derived, and a uniform order of the 16; - SEALED_CONTROL_LEN from the formula of §62.1, checked against the real seal; - the self-checks of rule 11, plus OUTER_TIME_AGE under the reader's rules and the header of PAYLOAD_AGE opened by I_PAYLOAD before anything is written. The content is streamed in pieces of 64 KiB, then the zeros of the padding, into memory (up to MAX_MEMORY_DKC) or an output that is closed only once the capsule is complete and checked and aborted on any failure. The core in writer.ts takes its random values from the caller: encrypt.ts passes crypto.getRandomValues, and only testing/encrypt.ts fixes them. Tests: the deterministic sections of the seven format 2 fixtures of Go byte for byte; round trips with open for both policies, 1 to 16 credentials and every padding boundary; the invalid options; streaming and failures of the source and the output; the internal errors with age-encryption replaced by a spy; a property loop (50 seeds per run, 500 by hand). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- El writer (`encrypt.test.ts`, `encrypt.stream.test.ts`, `encrypt.internal.test.ts`, `encrypt.property.test.ts`):
- con los valores de su registro, reproduce byte a byte el PRELUDE, PUBLIC_HEADER, `header_binding` y CONTROL_CBOR de los siete fixtures de formato 2 de Go, con las mismas longitudes; cada credencial cae en el hueco del registro y la `.dkk` sale igual, salvo su `capsule_digest`;
- lo que escribe se abre con `open`: las dos políticas, de 1 a 16 credenciales, cada una sola y todas juntas; contenidos en todos los bordes de trozo y de relleno, hasta 5 000 000 de bytes, con las dos reglas; rondas 1000, 1001 y 2000;
- las opciones inválidas dan los textos y códigos de `capsule.Encrypt` sin escribir nada y con la salida abortada;
- streaming desde `Uint8Array`, `Blob` y `ReadableStream` con cualquier troceado; una fuente de otra longitud, una fuente o una salida que fallan y un `progress` que lanza dejan la salida abortada;
- las autocomprobaciones, con entradas malas y con un `age-encryption` sustituido que falla, alarga, cambia o corta lo que sella;
- un bucle de propiedades con opciones aleatorias, a veces inválidas: 50 semillas en cada ejecución y 500 con `DATEKEYS_PROPERTY_SEEDS=500` (pasó el 29-09-2026, en 203 s).
Rendimiento en Node 24.9, informativo: `time_only`, unos 120 ms por cápsula en caliente (470 ms la primera, con la carga del código); `time_and_key` con clave portable, unos 200 ms; 64 MiB desde un `Blob` hacia una salida, 50 MiB/s con el SHA-256 en la misma pasada.
- `src/lib/dkc/testing/capsule-vectors.json`: la interoperabilidad del writer con Go a nivel de cápsula (plan de la fase 3, sección 8, punto 9), que comprueba `interop.test.ts`. `scripts/capsule-ts-samples.mjs` escribe con `encrypt` trece cápsulas de formato 2 para las rondas 1000, 1001 y 2000:
- `time_only` de 0, 46, 65 536 y 78 000 bytes, con las dos reglas de relleno;
- `time_and_key` con una clave portable, con tres recipients y una clave portable, y con dieciséis recipients;
- una con extensiones en PUBLIC_HEADER, CONTROL_CBOR y la `.dkk`;
- una para un instante un nanosegundo posterior a la ronda 1000.
`scripts/capsule-go-verdicts.go` las pasa por `capsule.Inspect` y las abre con `capsule.Open` con cada credencial sola y con todas juntas: todas abren al mismo contenido, con el formato, L, la regla y P pedidos. Además abre `SEALED_CONTROL` capa a capa con las identidades de `agewrap`, cuenta los 16 stanzas de `INNER_ACCESS_AGE`, y reencodifica PUBLIC_HEADER, CONTROL_CBOR y la `.dkk` a los mismos bytes. El test comprueba esos veredictos y repite cada apertura con `open` sobre los bytes congelados, con el mismo resultado. El fichero lleva también:
- cuatro mezclas de dos cápsulas de la ronda 1000, que Go y `open` rechazan con el mismo código en el mismo paso: `ERR_HEADER_BINDING` en el 15 (dos de ellas), `ERR_INTEGRITY` en el 17 y `ERR_POLICY_STRUCTURE_MISMATCH` en el 12;
- el diferencial de los codificadores: 500 entradas válidas sacadas de una semilla (`testing/interop.ts`), que `capsule.EncodeHeader`, `capsule.EncodeControl` y `AccessKey.MarshalBody` codifican a los mismos bytes que esta librería. Se congelan la semilla, el número de entradas y el SHA-256 de todas las codificaciones, que el test recalcula;
- 22 cadenas de recipient, que `parseX25519Recipient` y `checkX25519Recipient` leen con los textos de `age.ParseX25519Recipient` y `agewrap.CheckX25519Recipient`;
- las 21 opciones inválidas de `encrypt` que tienen equivalente en Go, con el texto de `capsule.Encrypt`.
Las cápsulas son aleatorias, así que se generan una vez y se congelan con los veredictos de Go. Para regenerarlo: `node scripts/capsule-ts-samples.mjs > ts-samples.json`, y desde el mismo módulo Go temporal, `go run capsule-go-verdicts.go ts-samples.json > capsule-vectors.json`.
- Todo se lee con los formatos de `testdata/README.md` (`testing/vectors.ts`): una clave desconocida o que falta, un valor de otro tipo, un código que no es de §69 o una edición fuera de su base hacen fallar el fichero con su motivo; nada se salta en silencio.
- Todo fichero de `testdata/` tiene que ejecutarlo algún test: un nombre nuevo exportado por Go (otro `vectors/*.json`, un fichero de fixture que ningún JSON nombra) hace fallar `testdata/ holds no file that no test runs` hasta que se le añade su bloque.
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
## Dependencias de ejecución
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Aprobadas en el plan de la fase 2 (sección 3 y decisión 5) e instaladas con su versión exacta. La página solo las carga al abrir una cápsula (ver [Abrir](#abrir)).
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| Paquete | Versión | Licencia | Uso |
|---|---|---|---|
| `age-encryption` | 0.3.1 | BSD-3-Clause | las tres envolturas `age` (§28), con `Identity` y `Recipient` propios para el stanza `tlock` |
| `@noble/curves` | 2.4.0 | MIT | BLS12-381 del núcleo IBE y de la verificación de releases; también el oráculo de `bls12381.contrast.test.ts` |
Phase 2, step 5a: open capsules, steps 9 to 18, in memory src/lib/dkc/open.ts runs steps 9 to 18 of spec §63 on top of the steps 1 to 8 of inspectWith. It follows capsule.Open of the Go reference, with its checks, codes and texts: - step 9: the access credentials (the .dkk as an object, then its capsule_id and capsule_digest), then the release, never before the round time. Any failure of the source is ERR_RELEASE_UNAVAILABLE alone, keeping its text and its cause (correction 6); - step 10: verifyRelease; - steps 11 to 13: OUTER_TIME_AGE, the structure against access_policy and INNER_ACCESS_AGE; - steps 14 to 18: CONTROL_CBOR, header_binding, I_PAYLOAD, PAYLOAD_AGE and the commit. The three age files open with the Decrypter of age-encryption and identities that apply the rules of Go's agewrap: the tlock identity on ibe.ts, and the access and payload identities on x25519.ts. A failure of age that no identity reports is ERR_INTEGRITY with the fixed reason of its phase, header or STREAM, never the text of age-encryption. The plaintext is decrypted in memory and returned only after step 18. Streaming to OPFS is step 5b. src/lib/dkc/x25519.ts opens one age X25519 stanza at a time, in the order of age's X25519Identity. Step 13 must try every identity on every stanza (spec §36), and age-encryption's Decrypter stops at the first. Its primitives are the ones age-encryption uses: X25519 and HKDF from noble curves and hashes, and ChaCha20-Poly1305 from @noble/ciphers 2.4.0. The author approved declaring that package as a direct dependency on 2026-09-28; it is the copy already installed and bundled. The guards now allow ciphers, and x25519.ts in the noble allowlist. src/lib/dkc/bech32.ts ports age's internal/bech32, with its MIT notice, to read AGE-SECRET-KEY-1 identities. Tests: - vectors.test.ts runs all 65 cases of the mutation corpus through open. Each gives the code and the step of Go, and no case that fails without the network requests a release. This includes the 34 cases of steps 9 to 18 that were skipped, so the suite no longer skips any test. - open.test.ts: - the five official fixtures open to their plaintext, with each credential, with the checks and details of the reference; - the unusable noncritical extensions are reported; - the source failures and the clock; - age failures by phase; - CONTROL_CBOR that does not decode, and a low-order share in INNER_ACCESS_AGE, through an OUTER_TIME_AGE resealed with the FK_TIME of the Go vectors; - the texts of the identities. - x25519.test.ts checks against age-encryption both ways and against the Go-written stanzas of the fixtures, and covers every low-order share. - bech32.test.ts has the vectors of the reference. x25519.ts and bech32.ts are at 100 % coverage, now thresholds. open.ts is at 100 % of lines; the one branch left is the one for an error that is not a DateKeysError. index.ts does not re-export the opening yet. The page imports index.ts, and re-exporting would pull noble into /inspect (58.7 to 84.9 KB gzip) even unused; step 8 will load it on demand. The site does not change. npm run verify is green: 2,490 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
| `@noble/hashes` | 2.4.0 | MIT | los hashes del IBE y el HKDF de los stanzas X25519; se declara porque se importa directamente |
| `@noble/ciphers` | 2.4.0 | MIT | el ChaCha20-Poly1305 de los stanzas X25519 (`x25519.ts`): la misma copia que usa `age-encryption`, así que no añade nada al bundle. Aprobada el 28-09-2026 para abrir los stanzas de uno en uno, como exige §36 |
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`age-encryption` arrastra `@noble/ciphers` 2.4.0, `@scure/base` 2.4.0 y `@noble/post-quantum` 0.5.4, todos con licencia MIT. `@noble/post-quantum` fija `@noble/curves` y `@noble/hashes` a `~2.0.0` y trae su propia copia 2.0.1, que usa para el ML-KEM híbrido. La decisión 5 la acepta, sin overrides de npm.
Guardas de `src/lib/dependencies.test.ts`, en cada `npm test`:
Phase 2, step 5a: open capsules, steps 9 to 18, in memory src/lib/dkc/open.ts runs steps 9 to 18 of spec §63 on top of the steps 1 to 8 of inspectWith. It follows capsule.Open of the Go reference, with its checks, codes and texts: - step 9: the access credentials (the .dkk as an object, then its capsule_id and capsule_digest), then the release, never before the round time. Any failure of the source is ERR_RELEASE_UNAVAILABLE alone, keeping its text and its cause (correction 6); - step 10: verifyRelease; - steps 11 to 13: OUTER_TIME_AGE, the structure against access_policy and INNER_ACCESS_AGE; - steps 14 to 18: CONTROL_CBOR, header_binding, I_PAYLOAD, PAYLOAD_AGE and the commit. The three age files open with the Decrypter of age-encryption and identities that apply the rules of Go's agewrap: the tlock identity on ibe.ts, and the access and payload identities on x25519.ts. A failure of age that no identity reports is ERR_INTEGRITY with the fixed reason of its phase, header or STREAM, never the text of age-encryption. The plaintext is decrypted in memory and returned only after step 18. Streaming to OPFS is step 5b. src/lib/dkc/x25519.ts opens one age X25519 stanza at a time, in the order of age's X25519Identity. Step 13 must try every identity on every stanza (spec §36), and age-encryption's Decrypter stops at the first. Its primitives are the ones age-encryption uses: X25519 and HKDF from noble curves and hashes, and ChaCha20-Poly1305 from @noble/ciphers 2.4.0. The author approved declaring that package as a direct dependency on 2026-09-28; it is the copy already installed and bundled. The guards now allow ciphers, and x25519.ts in the noble allowlist. src/lib/dkc/bech32.ts ports age's internal/bech32, with its MIT notice, to read AGE-SECRET-KEY-1 identities. Tests: - vectors.test.ts runs all 65 cases of the mutation corpus through open. Each gives the code and the step of Go, and no case that fails without the network requests a release. This includes the 34 cases of steps 9 to 18 that were skipped, so the suite no longer skips any test. - open.test.ts: - the five official fixtures open to their plaintext, with each credential, with the checks and details of the reference; - the unusable noncritical extensions are reported; - the source failures and the clock; - age failures by phase; - CONTROL_CBOR that does not decode, and a low-order share in INNER_ACCESS_AGE, through an OUTER_TIME_AGE resealed with the FK_TIME of the Go vectors; - the texts of the identities. - x25519.test.ts checks against age-encryption both ways and against the Go-written stanzas of the fixtures, and covers every low-order share. - bech32.test.ts has the vectors of the reference. x25519.ts and bech32.ts are at 100 % coverage, now thresholds. open.ts is at 100 % of lines; the one branch left is the one for an error that is not a DateKeysError. index.ts does not re-export the opening yet. The page imports index.ts, and re-exporting would pull noble into /inspect (58.7 to 84.9 KB gzip) even unused; step 8 will load it on demand. The site does not change. npm run verify is green: 2,490 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `package.json` declara exactamente estas cuatro dependencias, con versión exacta;
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- `package-lock.json` no contiene `tlock-js` ni `drand-client`, ningún noble 1.x, ni más copias 2.x de `@noble/curves` o `@noble/hashes` que la 2.4.0 de la raíz y la 2.0.1 bajo `@noble/post-quantum`;
- ningún fichero de `src/` importa `tlock-js` ni `drand-client`;
Phase 2, step 5b: open from a Blob, streaming to an output open(dkc, opts) now takes a Uint8Array or a Blob, such as a File. - Of a Blob it reads only the prefix that steps 1 to 8 need. inspectedLength and readCapsule move from src/lib/inspector/load.ts to src/lib/dkc/prefix.ts, and the page imports them from the library. - The .dkk capsule_digest is computed over the Blob's stream with the new src/lib/dkc/digest.ts, an incremental SHA-256 on @noble/hashes, since Web Crypto hashes whole buffers only. digest.ts joins the noble allowlist of the guards. - PAYLOAD_AGE is decrypted in streaming. The plaintext goes to memory, as before, or to opts.output, a WritableStream. The output is written as age authenticates each chunk, closed only after step 18, and aborted after any failure at any step, even before step 17 (spec §56). A failure of the output is ERR_INTEGRITY with its text, as Go keeps the error of the writer of the plaintext. Tests: - the fixtures from Blobs, to memory and to an output; - a truncated two-chunk payload whose first chunk reached the output before the abort; - early failures that never write; - write and close failures, and an abort that fails; - a .dkk without capsule_digest; - the whole mutation corpus again as Blobs into an output, aborted in every case; - digest.ts against Web Crypto. Coverage of digest.ts is 100 % and a threshold. Checked in the browser (dev server, real OPFS). time_only.dkc, two STREAM chunks, opened from a Blob into FileSystemFileHandle.createWritable gives 78,000 bytes with the SHA-256 of its sidecar. The same file truncated fails at step 17, and the OPFS file keeps its previous content. The quota check, the temporary file and the download belong to the page, in step 8. npm run verify is green: 2,560 tests. The site does not change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- solo `digest.ts`, `ibe.ts`, `release.ts`, `x25519.ts` y los tests nombran `@noble/`, siempre con subrutas de `@noble/curves`, `@noble/hashes` y `@noble/ciphers` que resuelven a la copia 2.4.0 de la raíz;
- solo `agefile.ts`, `open.ts`, `tlock.ts`, `writer.ts` y los tests importan `age-encryption`; solo `encrypt.ts`, `testing/` y los tests importan `writer.ts`, cuyo núcleo recibe la aleatoriedad de quien lo llama (plan de la fase 3, decisiones 4 y 13); e `index.ts` no reexporta la apertura ni el writer;
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
- cada comprobación se ejecuta también sobre entradas malas, así que una guarda que dejara de detectar algo fallaría.
`check-build.mjs` hace la misma comprobación sobre el bundle del cliente.
Coste medido en el bundle el 28-09-2026, con una compilación de prueba de Vite 8 minificada (gzip de nivel 9):
| Qué se importa | Minificado | gzip |
|---|---|---|
| `Decrypter` de `age-encryption` | 153 645 B | 48 032 B |
| `Decrypter` y `Encrypter` | 183 650 B | 55 915 B |
| `bls12_381` y `sha256` de noble | 92 637 B | 28 090 B |
| Todo lo anterior | 239 454 B | 72 783 B |
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`age-encryption` importa de forma estática sus recipients ML-KEM híbridos, P-256 y scrypt. Por eso el `Decrypter` arrastra `@noble/post-quantum` y la copia 2.0.1 de noble, aunque DateKeys no los use: son unos 99 KB de los 212 KB de código antes de minificar.
En el sitio, según `check-build.mjs` el 28-09-2026:
| `/inspect` | JavaScript | gzip |
|---|---|---|
| Primera carga, antes del paso 8 | unos 157 KB | 58,7 KB |
| Primera carga, con la acción "abrir" | 187 700 B | 68 197 B |
| Bajo demanda, al abrir: `opener.ts`, `open.ts`, noble y `age-encryption` | 183 747 B | 66 882 B |
Phase 3, steps 6 and 7: the create page /create seals a person's file into a .dkc of format 2 and, when asked, a portable .dkk, in the browser and without network, with the decisions the author confirmed in step 6: time_only by default, the zone of the device with a selector, the warnings of §53 and §50 beyond 365 days, a notice of preliminary protocol, and files named capsula-<opening time, UTC>. - lengths.ts: sealedControlLength moves out of writer.ts, and capsuleLength gives the size of the .dkc before writing it; the property loop checks it on every capsule (500 seeds pass). - datekey.ts: LONG_HORIZON_SECONDS and isLongHorizon. - src/lib/inspector: localtime.ts (local times of a zone as UTC instants, a skipped time refused, a repeated one taken at its later instant), create-input.ts (the form, checked in its order) and creator.ts (the writing, loaded on demand), all at 100 %. - The .dkc goes to an OPFS temporary file, committed only when complete and checked, or to memory up to 64 MiB; the writing can be cancelled. The .dkk stays in memory only; losing it when it is the only credential asks for confirmation. - /inspect also cleans the temporary files of /create, and check-build checks the code loaded on demand of both pages. Checked in the browser on the production build: a capsule made for four minutes later opened afterwards in /inspect with the pasted release and with datekeys decrypt of Go over the network, to the same content. An adversarial review found one major and eight minor issues, all fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Y según `check-build.mjs` el 29-09-2026, con la página de crear:
| `/create` | JavaScript | gzip |
|---|---|---|
| Primera carga, con el formulario y el informe de los pasos 1 a 8 | 207 348 B | 76 525 B |
| Bajo demanda, al crear: `creator.ts`, `encrypt.ts`, noble y `age-encryption` | 212 515 B | 76 534 B |
Los 9,5 KB con gzip que crece la primera carga de `/inspect` son la interfaz de la apertura (`OpenPanel.svelte`) y sus módulos sin noble. Las cifras exactas cambian unos bytes en cada compilación, por la versión que SvelteKit incrusta.
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`npm audit --omit=dev` no encuentra vulnerabilidades. El `npm audit` completo encuentra 2 de gravedad baja en el tooling: `cookie` < 0.7.0 (GHSA-pxg6-pf52-xh8x), que llega a través de `@sveltejs/kit` 2.70.3. Esa es la última versión y sigue pidiendo `cookie` ^0.6.0. Solo afecta a la gestión de cookies del servidor de SvelteKit, que un sitio estático no usa.
## Tooling de desarrollo
| Paquete | Versión | Estado |
|---|---|---|
| `typescript` | 5.9.3 | en `package.json`; ya usado en el prototipo |
| `vitest` | 5.0.1 | en `package.json`; ya usado en el prototipo |
| `@vitest/coverage-v8` | 5.0.1 | en `package.json`; cobertura del 100 % del codec |
| `@types/node` | 24.13.6 | en `package.json`; tests que leen `testdata/` desde disco |
| `vite` | 8.3.0 | en `package.json`; dependencia peer obligatoria de `vitest` 5.0.1 y base del paso 5; la versión del prototipo |
| `svelte` | 5.57.1 | en `package.json`; paso 5 |
| `@sveltejs/kit` | 2.70.3 | en `package.json`; paso 5 |
| `@sveltejs/vite-plugin-svelte` | 7.3.0 | en `package.json`; paso 5 |
| `svelte-check` | 4.7.6 | en `package.json`; paso 5, `npm run check` |
| `@sveltejs/adapter-static` | 3.0.10 | en `package.json`; paso 5: la página es estática y no necesita servidor |
Phase 2, step 2: runtime dependencies and their guards - age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and @noble/hashes 2.4.0 become exact runtime dependencies (plan section 3, decision 5). The lockfile gains six packages: age-encryption, @noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports them yet, so the site does not change. - src/lib/dependencies.test.ts guards them. package.json declares exactly these three, pinned. The lockfile has no tlock-js, drand-client or noble 1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under @noble/post-quantum. No file of src/ imports tlock-js or drand-client. Only ibe.ts, release.ts and the tests name @noble/, always subpaths of @noble/curves or @noble/hashes that resolve to the root 2.4.0 copy. Every check also runs on bad inputs. It replaces the "only tests import @noble/curves" test of bls12381.contrast.test.ts. - vite.config.ts records the modules of each client chunk in .svelte-kit/output/client-modules.json. check-build.mjs fails if the bundle holds tlock-js, drand-client or @babel/*, or a nested copy other than noble under @noble/post-quantum. It also reports the JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip. - Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256 28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM recipients statically, so post-quantum and its nested noble copy are about 99 KB of the Decrypter's 212 KB of rendered code. - npm audit --omit=dev: no vulnerabilities. The full audit finds two low ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3, which is the latest version and affects only SvelteKit's server. npm run verify is green: 2,384 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
`@noble/curves` 2.4.0 estaba en esta lista como oráculo de `bls12381.contrast.test.ts` y ha pasado a dependencia de ejecución en la fase 2.
Todas las versiones se fijan exactas y `package-lock.json` se versiona. `.npmrc` activa `legacy-peer-deps` porque npm 11.5.2 falla al resolver los peers opcionales de `vitest` 5.0.1 (`Cannot read properties of null (reading 'edgesOut')`); con esa opción npm no instala peers, así que el peer obligatorio `vite` está declarado explícitamente.
## testdata
```bash
npm run testdata:sync
```
Copia `testdata/` de `../datekeys-go` en `HEAD`, leyendo los blobs con git para no arrastrar cambios sin commit, y escribe `testdata/SOURCE.json` con el commit completo y el SHA-256 de cada fichero. Para fijar otro commit: `node scripts/sync-testdata.mjs sync --commit <rev>`.
```bash
npm run testdata:check
```
Comprueba que los ficheros coinciden con `SOURCE.json`, sin faltantes ni sobrantes, y vuelve a leerlos del repositorio Go en el commit registrado. Sin la opción `--against`, `node scripts/sync-testdata.mjs check` verifica solo la copia local. Ambos comandos usan solo Node y git.
`.gitattributes` marca `testdata/**` como binario para que git no altere ningún byte.
Read capsule format 2 of spec v0.9 Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the reader to the DateKeys Protocol Specification v0.9. Both capsule formats are read; a format 1 capsule keeps the verdict v0.8.2 gave it. - framing: the VERSION of the prelude is the capsule format, 1 or 2 (Prelude.format, FORMAT_1, FORMAT_2, isFormat). - control: decodeControl and encodeControl take the format; schema version 2 adds payload_length (8 bytes, at most L_MAX) and padding (1 or 2). - padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up to L_MAX with BigInt bit lengths and ceil roundings, and the length of PAYLOAD_AGE. - open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P at step 16, and at step 17 a plaintext of exactly P bytes whose padding is zero; only the first L bytes are delivered, never the padding. Step 17 is recorded when it passes, and step 18 gives the bytes of content, as the reference does. Opened reports the format, L and, in format 2, the rule and P. - inspect: the JSON view carries format, as datekeys inspect -json. - The page shows the format, warns about format 1, and gives the padding rule and P once a format 2 capsule opens. Tests: the twelve fixtures, the 125 mutation cases through open from memory and from a Blob, the 4380 differential cases, padding.json, the format 2 CBOR vectors, and padding.test.ts against a BigInt statement of §29.1. The error texts of the 125 corpus cases were compared with capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2 fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Copia actual: la de `testdata/SOURCE.json` (tag `spec-v0.9`, `7e2d83c`).
## Licencia
Phase 2, step 8: the open action of /inspect After steps 1 to 8, a valid capsule whose date has passed on the device clock can be opened in the page: steps 9 to 18 of spec section 63 with open, loaded on demand with a dynamic import (opener.ts), so noble and age-encryption stay out of the first load of every page. - The release is supplied directly by the person (spec 63, step 10): drand's JSON answer or the bare signature, pasted after opening the drand URL the page links to, or the release in the record of an official fixture. The page never fetches it and reads only its round and signature (spec 11, 13). The CSP is unchanged. - time_and_key credentials: a .dkk (readAccessKey reads at most 12 bytes + 16 MiB + 1) or age identities, one per line. - The plaintext of the person's own file goes to a temporary OPFS file (tempfile.ts), committed only after step 18 (spec 56), offered for download and deleted on request, with another capsule, on pagehide and, if left over, on the next visit. One directory and one Web Lock per tab keep other tabs' clean-up away from files in use. Without OPFS, or when the browser refuses it, capsules up to 64 MiB open in memory. An opening in progress stops when another capsule is loaded. - opening.ts builds the page model of steps 9 to 18 as the reference records them; fixtures show their plaintext and compare its SHA-256 with their record. - licenses.txt: the notices of tlock-js (ibe.ts) and age (bech32.ts), the license of every package in the client bundle, Vite's and rolldown's runtime code, and the site's own license. check-build now fails if a notice is missing, or if a page loads noble, @scure/base or age-encryption with its first load. - The home page no longer says that the page never asks for keys. Checked in the browser on the production build: the time_only, time_and_key_portable (with its .dkk) and time_and_key_recipients (with a pasted identity) fixtures open with the SHA-256 of their records; a tampered signature fails at step 10 and a tampered STREAM chunk at step 17, with no download and no file left; an own file opens to OPFS, downloads without a CSP violation and is deleted with its lock; a left over directory goes on the next visit; no request leaves the origin. An adversarial review (four dimensions, each finding checked by a refuter) confirmed 15 findings, all fixed here. 2611 tests; coverage 100 % of the new modules, now a threshold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
Apache-2.0 ([LICENSE](LICENSE)), como la librería Go de referencia. El código que se derive de terceros conserva su aviso de copyright y licencia en el propio fichero: el núcleo IBE (`ibe.ts`), derivado de `tlock-js` (Apache-2.0 OR MIT, usado bajo MIT), y `bech32.ts`, portado de `age` (MIT). El sitio publica esos avisos y los de sus paquetes npm en `licenses.txt`.

Powered by TurnKey Linux.