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/src/lib/components/InspectionReport.svelte

623 lines
18 KiB

<script lang="ts">
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
import type { Snippet } from 'svelte';
import type { Report, Comparison } from '$lib/inspector/report.ts';
import {
displayText,
errorGloss,
escapeInvisible,
formatByteCount,
formatDateTime,
formatInteger,
formatRelative,
policyGloss,
} from '$lib/inspector/format.ts';
import StepList from './StepList.svelte';
import ExtensionList from './ExtensionList.svelte';
let {
report,
nowMs,
timeZone,
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
opening,
}: {
report: Report;
/** When the report was made, for the distance to the unlock date. */
nowMs: number;
/** The viewer's time zone, for example "Europe/Madrid". */
timeZone: string | undefined;
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
/** The opening of the capsule, after steps 1 to 8. */
opening?: Snippet;
} = $props();
let copyStatus = $state('');
let jsonOpen = $state(false);
let copyButton: HTMLButtonElement | undefined = $state();
const unlockMs = $derived(report.unlock?.epochMs);
const fileName = $derived(displayText(report.fileName));
const shownJSON = $derived(escapeInvisible(report.json));
async function copyJSON(): Promise<void> {
copyStatus = '';
try {
await navigator.clipboard.writeText(report.json);
copyStatus = 'JSON copiado.';
return;
} catch {
// No Clipboard API (an insecure context, a denied permission): fall
// back to a selected textarea and the legacy copy command.
}
if (legacyCopy(report.json)) {
copyStatus = 'JSON copiado.';
} else {
jsonOpen = true;
copyStatus = 'El navegador no deja copiar desde la página: selecciona el JSON de abajo y cópialo.';
}
copyButton?.focus();
}
function legacyCopy(text: string): boolean {
const area = document.createElement('textarea');
area.value = text;
area.readOnly = true;
area.className = 'visually-hidden';
document.body.append(area);
area.select();
let ok = false;
try {
ok = document.execCommand('copy');
} catch {
ok = false;
}
area.remove();
return ok;
}
function stanzaTypes(list: readonly { type: string }[]): string {
return list.length === 0 ? 'ninguno' : list.map((s) => s.type).join(', ');
}
function comparisonState(c: Comparison): string {
return c.match ? 'coincide' : 'no coincide';
}
</script>
<section class="report" aria-labelledby="report-title">
<header class="report-head">
<div class="file">
<h2 id="report-title" tabindex="-1">{fileName.text}</h2>
<p class="muted">
{formatByteCount(report.size)}{#if report.readLength < report.size}. Se leyeron los primeros {formatByteCount(
report.readLength,
)}: el resto {report.prelude ? 'de PAYLOAD_AGE' : 'del fichero'} no interviene en los pasos 1 a 8{/if}.
</p>
</div>
<div class="copy">
<button class="button quiet" type="button" onclick={copyJSON} bind:this={copyButton}>Copiar JSON</button>
<p class="copy-status" role="status">{copyStatus}</p>
</div>
</header>
<div class={['verdict', report.valid ? 'valid' : 'invalid']}>
<div class="state">
{#if report.valid}
<p class="state-word">Estructura válida</p>
<p>
Supera los pasos 1 a 8 de §63 sin red y sin secretos. Es una comprobación de estructura: la cabecera pública solo
queda vinculada al control al abrir la cápsula (paso 15), y ni siquiera entonces prueba quién la escribió ni
cuándo (§55.1).
</p>
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
{#if report.prelude?.format === 1}
<p class="format-note">
Formato 1, el de la v0.8.2: hasta la fecha, esta cápsula deja ver cuántas credenciales tiene y la longitud exacta
Format 3, step 3: read capsule format 3 of spec v0.10 Syncs testdata with datekeys-go at the tag spec-v0.10 (cc35d2c) and moves the reader to the DateKeys Protocol Specification v0.10. The three capsule formats are read. - framing: FORMAT_3, and isPadded for formats 2 and 3. control: schema version 3, with the keys 6 and 7 of version 2. SPEC_VERSION is 0.10. - open: OpenOptions.sink receives the files of a format 3 capsule (sink.ts: Sink with begin, create, commit and abort, as capsule.Sink, and MemorySink). Without one, open rejects with a TypeError right after step 2, before any request, as ErrSinkRequired. Opened gains head, verdicts, areaLen and unusableHeadExtensions. - open3.ts: step 17 of format 3 in its substeps 17.2 to 17.8, as openBody of the reference: a failure of age or a plaintext whose length is not P prevails, the first failing substep decides, and the codes other than ERR_INTEGRITY are reported only after reading PAYLOAD_AGE to its end. Reads grow with the bytes received, never with the lengths BODY declares. A failure of the sink is ERR_INTEGRITY with its text, and the sink is aborted once after begin. - The page: opener.ts opens the fixtures of format 3 into a MemorySink; the open panel says that it does not deliver their files yet, and the glosses of the steps name format 3. check-build.mjs refuses to ship the heads, salts, comments and paths of the format 3 fixtures. Tests: the 21 fixtures, format 3 laid out byte by byte from its record and opened into a sink with its files and verdicts; the 209 cases of the corpus from memory and from a Blob, with the code, the step and, new, the exact text of capsule.Open, frozen by scripts/mutation-go-texts.go in testing/mutation-texts.json, which replays the corpus as internal/testkit does (its extension validator texts included); the 5110 differential cases over 14 bases; paths, path_fold, head_schema and security vectors; the control of schema version 3 in cbor.json; and step 17 on crafted plaintexts sealed again to I_PAYLOAD, whose texts capsule.Open gives on the same plaintexts. ibe-vectors.json gains the nine format 3 fixtures from scripts/ibe-go-vectors.go; the twelve before are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 week ago
de su contenido. Los formatos 2 y 3 ocultan las dos cosas (§55.2).
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
</p>
{/if}
{:else if report.failure}
<p class="state-word">Rechazada en el paso {report.failure.step}</p>
<p class="code">{report.failure.code}</p>
<p>{errorGloss(report.failure.code)}</p>
{/if}
</div>
<div class="when">
<p class="when-label">Fecha de apertura</p>
{#if report.unlock && unlockMs !== undefined}
<p class="date"><time datetime={report.unlock.rfc3339}>{formatDateTime(unlockMs, 'UTC')}</time></p>
<p>
En tu hora local{timeZone ? ` (${timeZone})` : ''}: {formatDateTime(unlockMs)}
</p>
<p class="muted">
Ronda {report.capsule?.round} de {report.profile?.network ?? report.capsule?.network}; {unlockMs <= nowMs
? `la fecha ya pasó, ${formatRelative(unlockMs, nowMs)}`
: `la fecha todavía no ha llegado: ${formatRelative(unlockMs, nowMs)}`}.
</p>
{:else if report.unlock}
<p class="date"><time datetime={report.unlock.rfc3339}>{report.unlock.rfc3339}</time></p>
{:else}
<p class="muted">
Sin calcular: sale de la DateKey en el paso 7, {report.steps[6]?.state === 'failed' ? 'que falló' : 'que no llegó a ejecutarse'}.
</p>
{/if}
</div>
</div>
{#if report.profileStatus === 'compromised'}
<p class="compromised">
La red de esta cápsula, {report.profile?.network ?? report.capsule?.network}, figura como comprometida en el registro de
perfiles de DateKeys: su contenido pudo leerse antes de la fecha (§71).
</p>
{/if}
{#if report.note && 'text' in report.note}
<figure class="note">
<blockquote>{report.note.text}</blockquote>
<figcaption>
<p>Nota pública del creador, sin comprobar: la puede leer cualquiera que tenga el fichero.</p>
<p>Nadie puede comprobar antes de la fecha quién creó la cápsula ni si va firmada.</p>
</figcaption>
</figure>
{:else if report.note}
<p class="note-unusable">La cápsula lleva una nota pública que no cumple las reglas de texto: no se muestra.</p>
{/if}
<section class="block" aria-labelledby="steps-title">
<h3 id="steps-title">Pasos 1 a 8</h3>
<StepList steps={report.steps} />
</section>
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
{@render opening?.()}
<div class="grid">
<section class="block" aria-labelledby="header-title">
<h3 id="header-title">Cabecera pública</h3>
{#if report.capsule}
{@const c = report.capsule}
<dl>
<div>
<dt>capsule_id</dt>
<dd class="mono">{c.capsuleId}</dd>
</div>
<div>
<dt>access_policy</dt>
<dd><span class="mono">{c.accessPolicy}</span> <span class="muted">{policyGloss(c.accessPolicy)}</span></dd>
</div>
<div>
<dt>DateKey</dt>
<dd class="mono">{c.dateKey}</dd>
</div>
<div>
<dt>DateKey decodificada</dt>
<dd class="mono">{c.dateKeyJSON}</dd>
</div>
<div>
<dt>Red (network)</dt>
<dd class="mono">{c.network}</dd>
</div>
<div>
<dt>Ronda</dt>
<dd class="mono">{c.round}</dd>
</div>
<div>
<dt>unlock_at</dt>
<dd class="mono">{report.unlock?.rfc3339 ?? 'sin calcular'}</dd>
</div>
</dl>
{:else}
<p class="muted">PUBLIC_HEADER no llegó a decodificarse.</p>
{/if}
</section>
<section class="block" aria-labelledby="profile-title">
<h3 id="profile-title">Perfil fijado</h3>
{#if report.profile}
{@const p = report.profile}
<dl>
<div>
<dt>profile_id</dt>
<dd class="mono">{p.id}</dd>
</div>
<div>
<dt>Proveedor y red</dt>
<dd><span class="mono">{p.provider}</span>, <span class="mono">{p.network}</span></dd>
</div>
<div>
<dt>Cadena (chain hash)</dt>
<dd class="mono">{p.chainHash}</dd>
</div>
<div>
<dt>Periodo</dt>
<dd>{p.period} s por ronda</dd>
</div>
<div>
<dt>Génesis</dt>
<dd class="mono">{p.genesis}</dd>
</div>
<div>
<dt>Esquema</dt>
<dd class="mono">{p.scheme}</dd>
</div>
</dl>
{:else if report.capsule}
<p class="muted">
La DateKey nombra el perfil <span class="mono">{report.capsule.network}</span>, que este lector no tiene fijado.
</p>
{:else}
<p class="muted">Se elige con la DateKey de PUBLIC_HEADER, que no llegó a decodificarse.</p>
{/if}
</section>
<section class="block" aria-labelledby="prelude-title">
<h3 id="prelude-title">Prelude</h3>
{#if report.prelude}
{@const p = report.prelude}
<dl>
<div>
<dt>Bytes</dt>
<dd class="mono">{p.hex}</dd>
</div>
<div>
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
<dt>Marca y formato</dt>
<dd><span class="mono">{p.magic}</span>, formato {p.format}{p.format === 1 ? ', el de la v0.8.2' : ''}</dd>
</div>
<div>
<dt>FLAGS y RESERVED</dt>
<dd><span class="mono">{p.flags}</span>, <span class="mono">{p.reserved}</span></dd>
</div>
<div>
<dt>PUBLIC_HEADER_LEN</dt>
<dd>{formatByteCount(p.publicHeaderLen)}</dd>
</div>
<div>
<dt>SEALED_CONTROL_LEN</dt>
<dd>{formatByteCount(p.sealedControlLen)}</dd>
</div>
<div>
<dt>PAYLOAD_AGE</dt>
<dd>
desde el byte {formatInteger(p.payloadOffset)}{#if p.payloadLength !== undefined}, {formatByteCount(
p.payloadLength,
)}{:else}, fuera del fichero{/if}
</dd>
</div>
</dl>
{:else}
<p class="muted">El prelude no se superó.</p>
{/if}
</section>
<section class="block" aria-labelledby="stanzas-title">
<h3 id="stanzas-title">Stanzas age</h3>
<dl>
<div>
<dt>OUTER_TIME_AGE</dt>
<dd>
{#if report.outerStanzas}
{report.outerStanzas.length}
{report.outerStanzas.length === 1 ? 'stanza' : 'stanzas'}: <span class="mono">{stanzaTypes(report.outerStanzas)}</span>
<span class="muted">(se exige exactamente uno, tlock)</span>
{:else}
<span class="muted">sin leer</span>
{/if}
</dd>
</div>
<div>
<dt>PAYLOAD_AGE</dt>
<dd>
{#if report.payloadStanzas}
{report.payloadStanzas.length}
{report.payloadStanzas.length === 1 ? 'stanza' : 'stanzas'}: <span class="mono">{stanzaTypes(report.payloadStanzas)}</span>
<span class="muted">(se exige exactamente uno, X25519)</span>
{:else}
<span class="muted">sin leer</span>
{/if}
</dd>
</div>
</dl>
<h4 class="sub">Stanza tlock frente al perfil fijado</h4>
{#if report.tlock}
<ul class="compare">
{#each report.tlock as c (c.label)}
<li class={c.match ? 'match' : 'mismatch'}>
<p class="compare-head">
<span>{c.label}</span>
<span class="verdict-word">{comparisonState(c)}</span>
</p>
<p><span class="muted">En el stanza:</span> <span class="mono">{c.found ?? 'falta'}</span></p>
<p><span class="muted">Esperado:</span> <span class="mono">{c.expected}</span></p>
</li>
{/each}
</ul>
{:else if report.outerStanzas}
<p class="muted">OUTER_TIME_AGE no trae ningún stanza tlock.</p>
{:else}
<p class="muted">Se compara cuando se leen la cabecera, el perfil y OUTER_TIME_AGE.</p>
{/if}
</section>
</div>
<section class="block" aria-labelledby="ext-title">
<h3 id="ext-title">Extensiones de PUBLIC_HEADER</h3>
<p class="prose muted">
Este inspector implementa el protocolo base V1 y no conoce ninguna extensión: una crítica hace fallar el paso 4 y
las no críticas se ignoran. Sus datos son bytes opacos para el protocolo.
</p>
{#if report.extensions}
<ExtensionList extensions={report.extensions} />
{:else}
<p class="muted">PUBLIC_HEADER no llegó a decodificarse.</p>
{/if}
</section>
<section class="block" aria-labelledby="json-title">
<h3 id="json-title">Salida de la CLI</h3>
<details bind:open={jsonOpen}>
<summary>JSON de <code>datekeys inspect -json</code></summary>
{#if shownJSON !== report.json}
<p class="json-note">
Los caracteres invisibles o de control se muestran escapados (<code>&#92;uXXXX</code>, el mismo JSON).
<strong>Copiar JSON</strong> copia la salida exacta de la CLI.
</p>
{/if}
<pre class="json">{shownJSON}</pre>
</details>
</section>
</section>
<style>
/* minmax(0, 1fr): a child's unbreakable content must not widen the track
beyond the screen. */
.report {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 2.5rem;
}
.report-head {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
gap: 1rem 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--rule);
}
.file h2 {
font-family: var(--mono);
font-size: var(--t-h2);
font-weight: 650;
letter-spacing: 0;
overflow-wrap: anywhere;
}
.file p {
margin-top: 0.25rem;
font-size: var(--t-small);
}
.copy {
display: grid;
justify-items: end;
gap: 0.25rem;
}
.copy-status {
font-size: var(--t-small);
color: var(--ink-muted);
min-height: 1.3em;
text-align: right;
max-width: 28rem;
}
/* The verdict: the one loud moment of the page. */
.verdict {
display: grid;
border: 1px solid var(--rule);
border-top: 6px solid var(--state);
border-radius: var(--radius);
background: var(--paper-2);
}
.verdict.valid {
--state: var(--pass);
}
.verdict.invalid {
--state: var(--fail);
}
@media (min-width: 820px) {
.verdict {
grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
}
.when {
border-top: 0;
border-left: 1px solid var(--rule);
}
}
.state,
.when {
padding: 1.25rem 1.25rem 1.5rem;
display: grid;
align-content: start;
gap: 0.5rem;
}
.when {
border-top: 1px solid var(--rule);
}
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
.format-note {
padding: 0.5rem 0.75rem;
border-radius: var(--radius);
background: var(--caution-bg);
font-size: var(--t-small);
}
/* The note is the creator's voice, not the page's: the serif of the
letter, on the caution of what nobody has checked. */
.note {
margin: -1.25rem 0 0;
padding: 1rem 1.25rem;
border-left: 4px solid var(--orange);
border-radius: 0 var(--radius) var(--radius) 0;
background: var(--caution-bg);
}
.note blockquote {
margin: 0;
max-width: var(--measure);
font-family: var(--serif);
font-size: var(--t-lead);
line-height: 1.5;
overflow-wrap: anywhere;
}
.note figcaption {
margin-top: 0.6rem;
font-size: var(--t-small);
color: var(--orange-text);
}
.note figcaption p {
margin: 0;
}
.compromised {
margin: -1.25rem 0 0;
padding: 0.5rem 0.75rem;
border-radius: var(--radius);
background: var(--fail-bg);
color: var(--fail);
}
.note-unusable {
margin: -1.25rem 0 0;
padding: 0.5rem 0.75rem;
border-radius: var(--radius);
background: var(--caution-bg);
font-size: var(--t-small);
}
.state-word {
font-size: var(--t-h2);
font-weight: 700;
letter-spacing: -0.015em;
line-height: 1.2;
color: var(--state);
}
.code {
font-family: var(--mono);
font-weight: 600;
overflow-wrap: anywhere;
}
.when-label {
font-size: var(--t-small);
font-weight: 600;
color: var(--ink-muted);
}
.date {
font-size: var(--t-date);
font-weight: 650;
line-height: 1.15;
letter-spacing: -0.02em;
font-variant-numeric: tabular-nums;
text-wrap: balance;
}
.block {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1rem;
align-content: start;
}
.grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 2.5rem 3rem;
}
@media (min-width: 900px) {
.grid {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
}
dl {
display: grid;
border-top: 1px solid var(--rule);
}
dl > div {
display: grid;
gap: 0.1rem 1rem;
padding-block: 0.55rem;
border-bottom: 1px solid var(--rule);
}
@media (min-width: 560px) {
dl > div {
grid-template-columns: 11rem minmax(0, 1fr);
}
}
dt {
font-size: var(--t-small);
font-weight: 600;
color: var(--ink-muted);
overflow-wrap: anywhere;
}
dd {
margin: 0;
overflow-wrap: anywhere;
}
dd.mono,
dd .mono {
font-size: 0.85rem;
}
.sub {
font-size: var(--t-body);
margin-top: 0.5rem;
}
.compare {
list-style: none;
padding: 0;
display: grid;
gap: 0.6rem;
}
.compare li {
padding: 0.6rem 0.75rem;
border-left: 3px solid var(--pass);
background: var(--paper-2);
border-radius: 0 var(--radius) var(--radius) 0;
overflow-wrap: anywhere;
font-size: var(--t-small);
}
.compare li.mismatch {
border-left-color: var(--fail);
background: var(--fail-bg);
}
.compare-head {
display: flex;
justify-content: space-between;
gap: 1rem;
font-weight: 600;
font-size: var(--t-body);
}
.compare .verdict-word {
font-size: var(--t-small);
color: var(--pass);
}
.compare li.mismatch .verdict-word {
color: var(--fail);
}
.compare .mono {
font-size: 0.8rem;
}
details summary {
cursor: pointer;
font-weight: 600;
width: fit-content;
padding-block: 0.25rem;
}
.json-note {
margin-top: 0.75rem;
font-size: var(--t-small);
color: var(--ink-muted);
max-width: var(--measure);
}
.json {
margin-top: 0.75rem;
padding: 0.85rem 1rem;
background: var(--paper-2);
border: 1px solid var(--rule);
border-radius: var(--radius);
font-size: 0.8rem;
line-height: 1.5;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
</style>

Powered by TurnKey Linux.