26 KiB
Plan: codec CBOR propio y página de prueba en Svelte
Estado: v2, 25 de septiembre de 2026. Sustituye a la v1 (conservada en AppOld/docs) con la revisión, el panel sobre data y las decisiones 2, 3 y 4. La decisión 1 queda pendiente de confirmar el límite N (sección 3).
Alcance: sustituir la dependencia CBOR externa de datekeys-go por un codec propio, escribir el mismo codec en TypeScript en el proyecto nuevo App, y construir sobre él la página de prueba: primero un inspector de cápsulas, después cifrado y descifrado en el navegador.
Regla de dependencias del proyecto: en ejecución, solo age, drand, tlock y lo que ellas arrastran, en cualquier lenguaje. El tooling de desarrollo queda fuera de la regla, pero solo se admite el de la lista de la sección 13. Toda dependencia nueva, de ejecución o de tooling, se propone por escrito y no se instala sin aprobación.
1. Situación de partida
| Elemento | Estado verificado el 25-09-2026 |
|---|---|
datekeys-go, paquete codec |
Usa github.com/fxamacker/cbor/v2 v2.9.4, que arrastra github.com/x448/float16. Es la única dependencia directa fuera de la regla. |
| Uso de esa librería | codec (Marshal, Unmarshal, Peek, CheckSchema, Valid) y structs con etiquetas cbor:"N,keyasint" en profile, capsule, accesskey, extension e internal/testkit. extension.Wire.Data es cbor.RawMessage. |
| Fixtures y vectores | Cinco cápsulas .dkc, dos .dkk, vectores de ronda, dk1_ y profile_hash. Los vectores se regeneran byte a byte. Los .dkc se generaron una vez y están congelados: la aleatoriedad de age no se puede inyectar, así que regenerar una cápsula cambia capsule_id, las claves y todos sus bytes. |
| Proyecto TypeScript | App, nuevo y separado. Tiene un esqueleto sin dependencias instaladas, los planes en docs/ y testdata/ copiado del commit 5719f6a de datekeys-go con scripts/sync-testdata.mjs, que verifica cada fichero contra testdata/SOURCE.json. |
| Prototipo | AppOld: API Quicknet en Go, CLI tlock y cliente Svelte (web-client), commit 4d2b0a1. No se modifica. |
web |
Landing estática de datekeys.com. Fuera de este plan. |
Fallos de la librería actual que este plan corrige, todos reproducidos el 25-09-2026:
| Fallo | Reproducción | Se corrige en |
|---|---|---|
CheckDisjoint es cuadrático y DecodeHeader lo ejecuta antes de comprobar las extensiones críticas |
una PUBLIC_HEADER de 880 KB con 40 000 + 40 000 extensiones se decodifica sin error en 8,3 s, sin red ni secretos | paso 1 (máximo de 64 por array) y paso 2a (fusión lineal) |
| Asimetría entre sellar y abrir | Encrypt sella data de control con 14 o 15 arrays anidados; al llegar la fecha, Open la rechaza en el paso 14 (exceeded max nested level 16) y la cápsula ya no se puede abrir |
paso 2a (data pasa a ser una hoja) y autocomprobación del encoder |
| Veredicto no determinista | la misma cápsula de 810 bytes, con data de cabecera {NaN:0, NaN:1}, la acepta Inspect 437 veces de 500 y Open 82 de 100 |
paso 2a (fxamacker sale del camino de data) |
extension.New(id, v, nil) emite f6 |
contradice §58.1 | paso 2a (New recibe []byte y rechaza vacío) |
2. Subconjunto CBOR del protocolo
Del CDDL spec/datekeys.cddl se deduce que el protocolo solo usa:
| Tipo CBOR | Tipo mayor | Uso |
|---|---|---|
| Entero sin signo | 0 | versiones, claves de mapa, política, periodo, genesis, versión de extensión |
| Cadena de bytes | 2 | identificadores, hashes, claves, capsule_digest, data de extensión |
| Cadena de texto | 3 | etiquetas de tipo, ids, DateKey compacta, access_type |
| Array | 4 | listas de extensiones |
| Mapa con claves enteras | 5 | todos los objetos |
El spec lo nombra en §58 como perfil del protocolo: RFC 8949 §4.2.1 restringido a los tipos mayores 0, 2, 3, 4 y 5, con claves enteras sin signo. Con claves enteras coinciden el orden por bytes, el orden por longitud y el orden numérico, así que no hay ambigüedad de perfil.
Reglas que el codec impone al decodificar y al codificar:
- enteros y longitudes en su forma más corta;
- longitudes definidas;
- claves de mapa en orden ascendente estricto, sin duplicados;
- mapas cerrados: toda clave no prevista por el schema se rechaza;
- texto UTF-8 válido;
- tamaños acotados por el schema y por los límites de §57, que pasan a ser MUST (sección 9);
- como mucho tres niveles de contenedores (objeto, array de extensiones y extensión).
dataes una hoja y el codec nunca desciende dentro.
Se rechazan con ERR_NON_CANONICAL_CBOR: enteros negativos, flotantes, tags, valores simples, longitudes indefinidas y claves no enteras. Los límites de tamaño usan el código que decida la sección 9.
Tras decodificar, el decodificador reencodifica y compara byte a byte con la entrada. Es una defensa independiente de la decodificación estricta, y ahora cubre también data.
3. Decisión normativa: data de las extensiones
Tipo: bstr no vacío. Bytes opacos para el protocolo base. Un panel comparó cuatro opciones con tres jueces independientes (seguridad, interoperabilidad y gobernanza del spec) y un red team contra la ganadora:
| Opción | Puntos (máx. 30) |
|---|---|
A. bstr opaco no vacío |
25,5 (ganadora para los tres jueces) |
| B. Subconjunto estructurado recursivo | 19 |
C. any fijado a un perfil preciso (CDE o dCBOR) |
11 |
D. any como hoy, fijando el comportamiento de fxamacker |
6,5 |
A gana porque:
- el protocolo base no analiza nada de
data, sin superficie de parser ni recursión; - un lector nunca analiza la
datade extensiones que no conoce; - la validez base es una comprobación de longitud, idéntica en todas las implementaciones.
Es el diseño de X.509 (extnValue OCTET STRING) y TLS (extension_data opaco).
B es la rival seria, pero:
- obliga a cada lector a validar datos que §54 dice que no interpreta;
- congela el espacio de valores;
- sus ventajas (canonicidad interna, vista estructurada, comprobación al sellar) se recuperan en A mediante la regla de registro de la sección 9.
El red team no refutó el tipo.
Justificación §76. Se basa en casos reproducibles, no en el tamaño del código:
- El CDDL se contradice: las líneas 12 a 14 dicen que
nully los vacíos nunca representan ausencia, y la línea 82 declara? 2 => any. extension.New(id, v, nil)emitef6.- El veredicto no determinista con claves NaN.
- El fixture oficial
time_only_extensionsusa claves de texto ytrue. - La asimetría de profundidad: una cápsula sellada que no se abre tras la espera. Es el caso decisivo.
Límite de tamaño: pendiente de confirmar. El panel propuso N = 65 535 (el límite de TLS). El red team mostró que ninguna evidencia sostiene un N concreto, y que un N uniforme de 64 KiB bloquearía el modo opaque_onion del Dead Man Switch de los borradores v0.1 y v0.2, si ese servicio llevara cada capa anterior en una extensión de control. Según una simulación del red team con la librería actual, crece unos 477 bytes por renovación y se bloquea en la renovación 139. El diseño del Dead Man Switch sigue abierto, así que el argumento es condicional.
Opciones:
- Recomendada: sin N propio.
dataqueda acotada por la trama de su contenedor (§57, que pasa a MUST), y cada extensión registrada declara su máximo en §72. - Límites por contenedor: 65 535 en PUBLIC_HEADER (pública y leída antes del desbloqueo) y la trama en CONTROL_CBOR y
.dkk. - N uniforme de 65 535, registrando en §76 que limita el
opaque_onion.
4. Diseño del codec en Go
Paquete codec reescrito sin reflexión y sin etiquetas de struct.
// Codificación: se acumula en un []byte. El primer error queda fijado y
// Err lo devuelve; las llamadas posteriores no hacen nada.
type Encoder struct{ buf []byte; err error }
func (e *Encoder) Map(pairs int)
func (e *Encoder) Array(items int)
func (e *Encoder) Uint(v uint64)
func (e *Encoder) Bstr(b []byte)
func (e *Encoder) Text(s string) // rechaza UTF-8 inválido
func (e *Encoder) Out() ([]byte, error)
// Decodificación: cursor estricto sobre la entrada. El Decoder guarda la
// última clave de cada mapa abierto y exige orden ascendente estricto.
type Decoder struct{ /* entrada, posición, pila de últimas claves */ }
func (d *Decoder) Map(max int) (pairs int, err error)
func (d *Decoder) Key() (uint64, error)
func (d *Decoder) EndMap() error
func (d *Decoder) Array(max int) (items int, err error)
func (d *Decoder) Uint(max uint64) (uint64, error)
func (d *Decoder) Bstr(min, max int) ([]byte, error) // longitud comprobada contra lo que queda antes de copiar
func (d *Decoder) Text(max int) (string, error)
func (d *Decoder) Done() error // rechaza bytes sobrantes
// Canonicidad: decodifica con decode, reencodifica con encode y compara.
func Unmarshal(in []byte, decode func(*Decoder) error, encode func(*Encoder)) error
// Lee las claves 0 y 1 de un mapa (tipo y versión) antes de la
// decodificación estricta (spec §70).
func Peek(in []byte) (typeTag string, version uint64, err error)
// Recorredor genérico del subconjunto, con profundidad y tamaño acotados.
// Es auxiliar: vectores compartidos, fuzzing, vista informativa del
// inspector y extensiones registradas cuyo `data` sea CBOR. Nunca decide
// la validez base de un objeto.
func Walk(in []byte, maxDepth, maxLen int) error
Cada schema escribe su codificación y decodificación a mano, en su paquete, en sustitución de los structs wire:
| Schema | Paquete | Claves |
|---|---|---|
| Provider Profile | profile |
0 a 10 |
| PUBLIC_HEADER | capsule |
0 a 4, opcionales 5 y 6 |
| CONTROL_CBOR | capsule |
0 a 3, opcionales 4 y 5 |
.dkk body |
accesskey |
0 a 5, opcionales 6, 7 y 8 |
verification_metadata |
accesskey |
0 |
| extensión | extension |
0, 1, opcional 2 (bstr no vacío) |
Reglas comunes:
- una clave opcional ausente se omite;
- un array vacío, un mapa vacío o un
h''en una clave opcional es un error explícito, no un efecto deomitempty; - la etiqueta de tipo y la versión se comprueban antes de decodificar el resto;
- los errores envuelven el sentinel de §69 que corresponda.
Cambios en extension:
Newrecibe[]bytey rechaza vacío o fuera de límite;- el array se rechaza por su cabecera si supera 64 elementos, antes de iterar;
CheckDisjointse sustituye por una fusión lineal de los dos arrays ordenados.
Autocomprobación del encoder: Encrypt, EncodeHeader, EncodeControl y el encoder de .dkk decodifican lo que producen con el decodificador del lector antes de sellar o escribir. Hay un fuzz target que exige que todo encode correcto implique un decode correcto.
go.mod pierde github.com/fxamacker/cbor/v2 y github.com/x448/float16. No entra nada a cambio.
5. Diseño del codec en TypeScript
Mismo diseño y misma API, en App/src/lib/dkc/cbor.ts, sobre Uint8Array, sin dependencias:
- texto con
TextDecoder('utf-8', { fatal: true, ignoreBOM: true }); sinignoreBOM, un BOM inicial desaparece y TS rechaza lo que Go acepta; Textdel encoder compruebaisWellFormed()antes de codificar, porqueTextEncodersustituye en silencio un surrogate suelto porefbfbd;- los
extension_idse comparan por sus bytes UTF-8, nunca como cadenas JavaScript, que comparan en orden UTF-16 (el par U+FF61 y U+10000 sale al revés); un port ingenuo divergió en 782 de 200 000 entradas; - enteros siempre como
number: todos losuintdel CDDL quedan acotados por debajo de 2⁵³ (sección 9), y el decodificador rechaza lo que supereNumber.MAX_SAFE_INTEGER; datase devuelve comoUint8Array;undefinedsignifica clave ausente.
Los parsers de schema van en el mismo directorio: profile.ts, header.ts, control.ts, accesskey.ts, extension.ts, datekey.ts e inspect.ts, que cubre los pasos 1 a 8 de §63.
datekey.ts no porta el roundForTime del prototipo. Ese código usa Date.parse, que trunca a milisegundos, y falla el vector "genesis + 1ns" de quicknet_rounds.json (da la ronda 1 en vez de la 2). Hace falta un parser RFC 3339 con precisión de nanosegundos.
La cabecera age de OUTER_TIME_AGE y PAYLOAD_AGE (pasos 5, 6 y 8) se analiza con un parser estricto según la gramática de age: stanzas, cuerpo base64 canónico, líneas de 64 columnas y línea MAC, sobre los ficheros binarios que localizan las longitudes del prelude. El filtro de líneas -> del prototipo es demasiado laxo.
6. Vectores compartidos entre lenguajes
Fichero testdata/vectors/cbor.json en datekeys-go, generado desde Go y congelado. App lo recibe con sync-testdata. Los casos genéricos se ejecutan con Walk; los de schema, con el decodificador de cada schema. Los enteros por encima de 2⁵³ van como cadena decimal.
{
"accept": [
{ "name": "uint 23 inline", "hex": "17", "value": 23 },
{ "name": "uint 24 one byte", "hex": "1818", "value": 24 },
{ "name": "uint 256 two bytes", "hex": "190100", "value": 256 },
{ "name": "uint 2^32 eight bytes", "hex": "1b0000000100000000", "value": 4294967296 },
{ "name": "empty bstr", "hex": "40" },
{ "name": "map two sorted keys", "hex": "a200010101" },
{ "name": "text with leading BOM", "hex": "64efbbbf61" }
],
"reject": [
{ "name": "uint 23 with one extra byte", "hex": "1817", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "bstr length not shortest", "hex": "5800", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "keys out of order", "hex": "a201000001", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "duplicate key", "hex": "a200000001", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "text key", "hex": "a1616100", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "indefinite array", "hex": "9f01ff", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "tag", "hex": "c101", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "float", "hex": "f97e00", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "true", "hex": "f5", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "null", "hex": "f6", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "negative int", "hex": "20", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "truncated uint", "hex": "1901", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "length beyond input", "hex": "5affffffff", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "trailing byte", "hex": "0100", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "invalid UTF-8", "hex": "61ff", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "overlong UTF-8", "hex": "62c080", "error": "ERR_NON_CANONICAL_CBOR" },
{ "name": "UTF-8 surrogate", "hex": "63eda080", "error": "ERR_NON_CANONICAL_CBOR" }
]
}
Además, un bloque por schema: objeto válido mínimo, clave desconocida, clave obligatoria ausente, tipo incorrecto y tamaño fuera de límite.
Casos de extensión:
datacon40, con5801xxy en el límite de la sección 3 y uno más;datacon tipo distinto debstr: texto,null,uint, mapa, tag y longitud indefinida;- 64 y 65 extensiones en un array;
- un
extension_idque empieza por BOM; - el par de orden U+FF61 y U+10000;
extension_versionen 2⁵³ − 1 y 2⁵³.
7. Tests
- Regresión byte a byte. Tras el paso 2b,
profile_hashde Quicknet, los hex de prelude, cabecera y control de las cinco cápsulas, y los dos.dkkcompletos tienen que ser idénticos a los fixtures congelados, sin ninguna excepción: el cambio dedatay la regeneración de su fixture ocurren antes, en el paso 2a. - Asertos sobre los bytes exactos de
dataen los tests de conformidad, no solo sobre su número (conformance_test.go:165y205,accesskey_test.go:54). - Vectores compartidos de la sección 6, en Go y en TypeScript.
- Corpus de mutaciones exportado. Los 45 casos de
capsule/mutation_test.go(23 de ellos en los pasos 1 a 8 y sin red) se exportan como vectores congelados: bytes, código de error y paso. TypeScript tiene que reproducir el código y el paso de cada uno. Además, un corpus diferencial de mutaciones aleatorias con semilla fija y el veredicto de Go. - Fuzzing en Go de
Decoder, deWalk, de cada decodificador de schema y de la propiedad "encode correcto implica decode correcto". El corpus de mutación existente sigue pasando. - TypeScript con
vitest. Carga los fixtures deApp/testdatay comparainspectcon la salida congelada dedatekeys inspect -jsonde cada fixture, generada en Go y guardada entestdata; los tests no ejecutan Go. Pasaquicknet_rounds.json,dk1.jsonyprofile_quicknet.json. - Cobertura del codec al 100 % en ambos lenguajes. En TypeScript necesita
@vitest/coverage-v8(sección 13).
8. La página de prueba en Svelte
En el proyecto App, como sitio estático, sin servidor.
Fase 1, inspector. Ruta /inspect. Carga un .dkc desde disco o uno de los fixtures, ejecuta los pasos 1 a 8 y muestra, como la CLI Go, el resultado de cada paso: prelude, cabecera, política, DateKey, fecha de apertura, argumentos del stanza tlock frente al perfil pinneado y recuento de stanzas de OUTER_TIME_AGE y PAYLOAD_AGE. Sin secretos. Una CSP con connect-src 'self' garantiza que la página no habla con ningún otro origen.
Contrato de las extensiones:
- id, versión, crítica o no, conocida o no, longitud y hex siempre;
- una vista de texto si los bytes son UTF-8 imprimible;
- una vista decodificada con
Walksi los bytes son CBOR del subconjunto, marcada como informativa y no validada por el protocolo; - todo el texto escapado;
- nunca se construyen objetos ni
Mapde JavaScript a partir de claves (__proto__, colisiones por encima de 2⁵³); - la
datade PUBLIC_HEADER y.dkkse marca como pública y no autenticada hasta el paso 15.
Fase 2, cifrar y descifrar. Solo cuando la fase 1 pase con todos los fixtures y el corpus exportado.
Necesita age-encryption 0.3.1, la librería age oficial en TypeScript, que admite Recipient e Identity propios. Arrastra @noble/ciphers, @noble/curves 2, @noble/hashes 2, @noble/post-quantum y @scure/base. Como tlock-js 0.9 sigue en @noble/curves 1.9.7 y @noble/hashes 1.8.0, el bundle tendrá dos versiones mayores de noble. La aprobación se decide al empezar la fase 2 (decisión 4).
Piezas de tlock-js y drand-client: sustituido el 26-09-2026 por PLAN_fase2_ibe_noble2.md. El bloque original nombraba decryptOnG1 (para Quicknet es decryptOnG2) y daba a drand-client la verificación de releases; ninguno de los dos paquetes entra ya en el bundle: el núcleo IBE se escribe en el proyecto sobre @noble/curves 2.x y los releases se verifican con noble y el perfil pinneado.
Las identidades tlock y X25519 aplican la cardinalidad de §63 (pasos 11, 13 y 17) dentro de unwrapFileKey, como en Go. La prueba de aceptación descifra un fixture time_and_key en el navegador con la firma embebida, sin red.
9. Cambios en la especificación y el CDDL
Todos en un único cambio normativo, con la justificación §76 de la sección 3:
- Extensiones.
- CDDL:
extensions = [1*64 extension]yextension = { 0 => extension-id, 1 => extension-version, ? 2 => bstr .size (1..MAX) }, con MAX según la opción elegida en la sección 3. - §31 y §54:
dataes una cadena de bytes opaca. El protocolo base nunca la decodifica ni la valida, y la validez de PUBLIC_HEADER, CONTROL_CBOR y.dkknunca depende de su contenido. Una extensión sin datos omite la clave 2, yh''es inválido. - §31 y §54: se elimina la excepción de multiplicidad ("salvo que el schema registrado permita multiplicidad"), que contradice al CDDL y a la implementación de referencia. La multiplicidad va dentro de
data. - §54: una extensión crítica conocida con
datainválida se rechaza con un código definido (nuevo en §69, o uno existente nombrado expresamente). Una no crítica conocida condatainválida se marca como inutilizable y el objeto sigue siendo válido.
- CDDL:
- Enteros.
extension_version,periodygenesis_timeacotados, como mucho a 2⁵³ − 1; paraextension_versionbasta con 2³² − 1. En TypeScript, 2⁵³ + 1 se confunde con 2⁵³, y una versión crítica desconocida podría pasar por conocida. - §57. Los límites pasan de "V1 recomienda" a MUST para encoders y decoders. Un único código de error para toda violación de límites; la referencia usa hoy
ERR_INTEGRITYpara las tramas. - §58. Nombra el perfil de la sección 2, aplica también a la cabecera del
bstrdedatay nunca a su contenido. - §72.
- Cada extensión registrada declara la codificación de su
data, su forma canónica, su longitud máxima y sus vectores. - Si la codificación es CBOR, usa el perfil de §58 de forma recursiva, con contenedores no vacíos y sin
null. - Sus lectores rechazan codificaciones no canónicas, y su encoder decodifica lo que produce antes de sellar.
- Las extensiones de firma firman los bytes exactos de
data.
- Cada extensión registrada declara la codificación de su
- §64. Mutaciones nuevas:
datacon tipo incorrecto,datavacía,datapor encima del límite y 65 extensiones. - §68. Un fixture
.dkkcon una extensión. - §74. Se retira "formato exacto de extensiones" de lo provisional.
- Documentación. Se actualizan
docs/traceability.md(decisión 6) yCHANGELOG.md, que recoge la rotura de API:extension.Newrecibe[]byte, ydatadistinta debstro vacía deja de ser válida. El fixture se regenera en el mismo commit.
10. Orden de trabajo y criterios de aceptación
| Paso | Contenido | Hecho cuando |
|---|---|---|
| 1 | Cambios de spec y CDDL de la sección 9 | texto aprobado |
| 2a | Con fxamacker todavía dentro: data como bstr, New([]byte), máximo de 64, fusión lineal, autocomprobación del encoder. Se regenera time_only_extensions con -force (cabecera: los bytes de "public label", que no son CBOR; control: un ejemplo CBOR del subconjunto) y se añade el .dkk con extensión. |
tests verdes; los tres fallos de la sección 1 tienen test de regresión; commit en Gitea |
| 2b | Codec propio en Go y baja de fxamacker | cero cambios en testdata/; regresión completa de la sección 7; govulncheck sin cambios; una noche de fuzzing limpia; commit en Gitea |
| 3 | Vectores CBOR, corpus de mutaciones exportado y salidas de inspect -json congeladas |
Go los genera y los pasa; ficheros congelados |
| 4 | En App: sync-testdata, codec y parsers TypeScript, inspect.ts |
vitest reproduce fixtures, vectores y corpus exportado; cobertura del codec al 100 % |
| 5 | Ruta /inspect |
abre los fixtures y un .dkc arrastrado; la CSP bloquea cualquier otro origen. npm run verify: svelte-check, tsc, cobertura de src/lib/dkc y src/lib/inspector, y build con scripts/check-build.mjs (CSP de cada página prerenderizada, fixtures byte a byte, ningún secreto en el sitio) |
| 6 | Fase 2 | age-encryption aprobada; un fixture time_and_key se descifra en el navegador |
11. Riesgos
- Dos codecs propios en vez de una librería auditada por muchos. Mitigación: subconjunto mínimo,
dataopaca, vectores compartidos, regresión byte a byte sin excepciones, corpus de mutaciones exportado y fuzzing. - Desviación entre Go y TypeScript. Los fixtures y vectores del Go son la verdad; TypeScript nunca genera fixtures propios, y
testdata/SOURCE.jsonfija el commit de origen y el hash de cada fichero. - Parser estricto de cabecera
ageen TypeScript. Cubierto por las mutaciones de los pasos 5, 6 y 8. dataopaca aplaza la validación a cada extensión. Es el tipo de fallo de CVE-2020-1971. Mitigación: la regla de §72, el perfil con nombre y el recorredor compartido.- Límite de
data. Si se elige un N uniforme, puede limitar servicios futuros, como elopaque_oniondel Dead Man Switch.
12. Decisiones
data:bstrno vacío (recomendado, sección 3). Pendiente de confirmar el límite: la trama del contenedor (recomendado), límites por contenedor o un N uniforme de 65 535.- Proyecto TypeScript: aparte, en
App. El prototipo queda enAppOld. Decidido. - Regla de dependencias: afecta a las de ejecución. El tooling de desarrollo se admite solo desde la lista explícita de la sección 13. Decidido.
age-encryption0.3.1: se decide al empezar la fase 2. Decidido.
13. Tooling de desarrollo de App
| 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 (sección 7) |
@types/node |
24.13.6 | en package.json; tests que leen testdata/ |
svelte, @sveltejs/kit, @sveltejs/vite-plugin-svelte, vite, svelte-check |
5.57.1, 2.70.3, 7.3.0, 8.3.0, 4.7.6 | en package.json; paso 5 |
@sveltejs/adapter-static |
3.0.10 | en package.json; paso 5, sitio estático |
@noble/curves |
2.4.0 | solo desarrollo; oráculo auditado del test de contraste de bls12381.ts (decidido el 26-09-2026: se mantiene nuestra implementación y se contrasta en cada ejecución). En la fase 2 pasará a ser la dependencia BLS de ejecución, fijada a 2.3.0 o posterior |
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')).