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/OpenPanel.svelte

883 lines
32 KiB

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
<script lang="ts">
// The "abrir" action of the inspector (plan of phase 2, section 9): steps 9
// to 18 of spec §63 on a capsule that passed steps 1 to 8, with the release
// the person supplies directly, pasted from drand or taken from the record
// of an official fixture (decision 4: the page never fetches it), and the
// credentials that time_and_key asks for. The opening code, with noble and
// age-encryption, is imported on demand, so the page's first load does not
// carry it.
//
// The plaintext of a fixture is opened in memory. The plaintext of the
// person's own file goes to a private temporary file of the browser (OPFS,
// tempfile.ts), committed only after step 18 (spec §56), offered for
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
// download and deleted on request, when another capsule is opened or
// loaded, and when the page is left; in memory, up to MEMORY_LIMIT, when
// the browser has no such file or refuses it. Once the capsule opened, the
// start of the plaintext is shown when it is text (plaintextPreview). An
// opening in progress stops when the panel is destroyed, and its file is
// removed.
//
// The files of a format 3 capsule go to that same temporary file through a
// ZipSink: the file itself when there is one of one segment, and a ZIP
// otherwise, of which each file is a slice (design of format 3, section
// 5). The page shows the verdicts of the security area first, then the
// declared author and the comment, then each path as text with its
// warnings (spec §29.7), and offers the downloads.
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 { onDestroy, tick } from 'svelte';
import { toHex } from '$lib/dkc/index.ts';
import type { Fixture } from '$lib/inspector/fixtures.ts';
import { errorGloss, escapeInvisible, formatByteCount, formatDateTime, formatInteger } from '$lib/inspector/format.ts';
import type { OpenedFiles } from '$lib/inspector/opener.ts';
import { buildOpenReport, contentExtension, type OpenReport, plaintextFileName, plaintextPreview, SHOWN_TEXT } from '$lib/inspector/opening.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 { drandReleaseURL, parseReleaseText, releaseText } from '$lib/inspector/release-input.ts';
import type { Report } from '$lib/inspector/report.ts';
import { browserPlatform, cancellable, createTempFile, freeSpace, type TempFile } from '$lib/inspector/tempfile.ts';
import ExtensionList from './ExtensionList.svelte';
import StepList from './StepList.svelte';
let {
report,
capsule,
fixture,
nowMs,
}: {
/** The inspection of the capsule: valid, with its round and profile. */
report: Report;
/** What was inspected: the bytes of a fixture or the person's file. */
capsule: Uint8Array | Blob;
/** The official fixture, when it is one. */
fixture: Fixture | undefined;
/** When the report was made. */
nowMs: number;
} = $props();
/** The largest plaintext opened in memory when the browser has no OPFS. */
const MEMORY_LIMIT = 64 << 20;
/** The files of a format 3 capsule that opened, and their downloads. */
interface FilesView {
readonly files: OpenedFiles;
/** The main download and the ZIP, as object URLs, for the person's own capsule. */
readonly primary?: { readonly url: string; readonly name: string; readonly size: number };
readonly zip?: { readonly url: string; readonly name: string; readonly size: number };
/** Whether each file has its own download. */
readonly each: boolean;
/** Whether the files are in the temporary file, or in memory. */
readonly temporary: boolean;
/** The start of the one file, when there is one and it is text. */
readonly shown?: { readonly text: string; readonly cut: boolean };
}
/** How many files the list shows; the ZIP holds them all. */
const LISTED = 500;
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
interface Result {
readonly report: OpenReport;
readonly ms: number;
readonly files?: FilesView;
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 plaintext offered for download: a temporary file, or memory. */
readonly download?: { readonly url: string; readonly name: string; readonly size: number; readonly temporary: boolean };
}
// Read once: the panel is made again for each inspected capsule.
const initial = (): { round: number; release: string } => ({
round: report.capsule!.round,
release: fixture?.release === undefined ? '' : releaseText(fixture.release.round, toHex(fixture.release.signature)),
});
const { round, release: initialRelease } = initial();
/** The input a problem is about. */
type ProblemField = 'release' | 'identities' | 'accessKey';
const FIELD_IDS: Readonly<Record<ProblemField, string>> = {
release: 'release-input',
identities: 'ids-input',
accessKey: 'dkk-input',
};
let releaseInput = $state(initialRelease);
let identities = $state('');
let accessKey: File | undefined = $state();
let problem: string | undefined = $state();
let problemField: ProblemField | undefined = $state();
let busy = $state(false);
let result: Result | undefined = $state();
let deleted = $state(false);
let announcement = $state('');
const timeAndKey = $derived(report.capsule?.accessPolicy === 'time_and_key');
const due = $derived(report.unlock?.epochMs !== undefined && report.unlock.epochMs <= nowMs);
const drandURL = $derived(drandReleaseURL(report.profile!.chainHash, round));
const payloadLength = $derived(report.prelude?.payloadLength ?? 0);
// The temporary file and the object URLs of the last opening.
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
let temp: TempFile | undefined;
let objectURLs: string[] = [];
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 temporary file of the opening in progress, until it becomes `temp`
// or is removed: discard() removes it too, so a panel destroyed or a page
// left in the middle of an opening leaves nothing behind.
let pending: TempFile | undefined;
// Only the latest opening may show its result; destroying the panel
// invalidates the one in progress, which then stops writing.
let openId = 0;
async function discard(): Promise<void> {
for (const url of objectURLs) URL.revokeObjectURL(url);
objectURLs = [];
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
const files = [temp, pending];
temp = undefined;
pending = undefined;
await Promise.all(files.map((f) => f?.remove()));
}
onDestroy(() => {
openId++;
void discard();
});
// pagehide also fires when the page goes into the back/forward cache: if
// it comes back, it must not offer what was deleted here.
function leave(): void {
if (result?.download !== undefined || result?.files?.primary !== undefined) deleted = true;
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
void discard();
}
// An object URL of this opening, revoked by discard.
function objectURL(blob: Blob): string {
const url = URL.createObjectURL(blob);
objectURLs.push(url);
return url;
}
// Saves file i of a format 3 capsule on its own: an object URL of its
// slice, revoked a minute after the click.
function saveFile(files: OpenedFiles, i: number, name: string): void {
const url = URL.createObjectURL(files.file(i));
const a = document.createElement('a');
a.href = url;
a.download = name;
a.click();
setTimeout(() => URL.revokeObjectURL(url), 60_000);
}
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
async function deleteNow(): Promise<void> {
await discard();
deleted = true;
announcement = 'Fichero temporal borrado.';
}
function onAccessKey(event: Event & { currentTarget: HTMLInputElement }): void {
accessKey = event.currentTarget.files?.[0];
}
async function submit(event: SubmitEvent): Promise<void> {
event.preventDefault();
if (busy) return;
problem = undefined;
problemField = undefined;
const parsed = parseReleaseText(releaseInput, round);
if (!parsed.ok) {
await fail(parsed.problem, 'release');
return;
}
const id = ++openId;
const stale = (): boolean => id !== openId;
busy = true;
result = undefined;
deleted = false;
announcement = 'Abriendo la cápsula.';
await discard();
// This opening's temporary file, until it becomes `temp`: the finally
// block removes it on every other path.
let t: TempFile | undefined;
try {
const opener = await import('$lib/inspector/opener.ts');
if (stale()) return;
let free: number | 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
if (fixture === undefined) {
const platform = browserPlatform();
if (platform !== undefined) {
try {
free = await freeSpace(platform);
if (free === undefined || free >= payloadLength) t = pending = await createTempFile(platform);
} catch {
// The OPFS is there but the browser refuses it (a private window,
// site data blocked): the capsule opens in memory instead.
}
if (stale()) return;
}
if (t === undefined && payloadLength > MEMORY_LIMIT) {
await fail(
free !== undefined && free < payloadLength
? `El navegador deja ${formatByteCount(free)} libres para esta página y el contenido cifrado ocupa ${formatByteCount(payloadLength)}, más de los ${formatByteCount(MEMORY_LIMIT)} que la página abre en memoria. Libera espacio o usa la CLI (datekeys decrypt).`
: `Este navegador no deja a la página un fichero temporal privado (OPFS) y el contenido cifrado ocupa ${formatByteCount(payloadLength)}, más de los ${formatByteCount(MEMORY_LIMIT)} que la página abre en memoria. Usa otro navegador o la CLI (datekeys decrypt).`,
);
return;
}
}
const out = t;
const attempt = await opener.openCapsule({
capsule,
release: parsed.release,
...(timeAndKey ? { identities } : {}),
...(timeAndKey && accessKey !== undefined ? { accessKey } : {}),
// A stale opening stops writing, so open aborts the file and stops.
...(out === undefined ? {} : { output: { writable: cancellable(out.writable, stale), file: () => out.file(), remove: () => out.remove() } }),
// Format 3: the files without an mtime take the time of the round in
// a ZIP, which must fit in the room the browser leaves.
roundTime: Math.floor((report.unlock?.epochMs ?? 0) / 1000),
...(free === undefined ? {} : { room: free }),
capsuleName: report.fileName,
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
});
if (stale()) {
if (attempt.ok) attempt.plaintext?.fill(0);
return;
}
if (!attempt.ok) {
await fail(attempt.problem, attempt.field);
return;
}
if (attempt.noRoom !== undefined) {
await fail(
`Los ficheros de la cápsula ocupan ${formatByteCount(attempt.noRoom.needed)} y el navegador deja ${formatByteCount(attempt.noRoom.room)} libres para esta página. Libera espacio o usa la CLI (datekeys decrypt).`,
);
return;
}
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
const opened = attempt.opened.error === undefined;
let shown: { text: string; cut: boolean } | undefined;
// The extension of the download, from the content: the capsule does
// not keep the name of the file it seals.
let ext = '';
if (opened && attempt.preview !== undefined) {
shown = plaintextPreview(attempt.preview.bytes, attempt.preview.whole);
ext = contentExtension(attempt.preview.bytes, shown !== undefined);
attempt.preview.bytes.fill(0);
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
}
const r = buildOpenReport(
attempt.opened,
attempt.digest === undefined
? undefined
: {
...attempt.digest,
...(fixture?.plaintextSHA256 === undefined ? {} : { expectedSHA256: fixture.plaintextSHA256 }),
...(shown === undefined ? {} : { text: shown.text, textCut: shown.cut }),
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
},
);
let download: Result['download'];
let files: FilesView | undefined;
if (attempt.files !== undefined) {
const f = attempt.files;
const d = f.downloads;
const offer = fixture === undefined;
const primary =
offer && d.primary !== undefined
? d.primary.kind === 'zip'
? { url: objectURL(f.zip!), name: d.primary.name, size: f.zip!.size }
: { url: objectURL(f.file(d.primary.index)), name: d.primary.name, size: f.file(d.primary.index).size }
: undefined;
const zip = offer && d.zip !== undefined ? { url: objectURL(f.zip!), name: d.zip, size: f.zip!.size } : undefined;
files = {
files: f,
...(primary === undefined ? {} : { primary }),
...(zip === undefined ? {} : { zip }),
each: offer && d.each,
temporary: t !== undefined,
...(shown === undefined ? {} : { shown }),
};
if (t !== undefined) {
temp = t;
pending = undefined;
t = undefined;
}
} else if (opened && fixture === undefined) {
const name = plaintextFileName(report.fileName, ext);
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
if (t !== undefined) {
const file = await t.file();
if (stale()) return;
temp = t;
pending = undefined;
t = undefined;
download = { url: objectURL(file), name, size: file.size, temporary: true };
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
} else {
const blob = new Blob([attempt.plaintext! as Uint8Array<ArrayBuffer>]);
download = { url: objectURL(blob), name, size: blob.size, temporary: false };
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
}
}
attempt.plaintext?.fill(0);
result = { report: r, ms: attempt.ms, ...(download === undefined ? {} : { download }), ...(files === undefined ? {} : { files }) };
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
announcement = r.opened
? 'Cápsula abierta: pasos 9 a 18 superados.'
: `Apertura rechazada en el paso ${r.failure!.step}, ${r.failure!.code}.`;
await tick();
document.getElementById('open-result-title')?.focus();
} catch (err) {
if (!stale()) await fail(`No se pudo abrir: ${escapeInvisible(err instanceof Error ? err.message : String(err))}`);
} finally {
// A file that did not become `temp`: a failure, or a stale opening.
if (t !== undefined) {
if (pending === t) pending = undefined;
await t.remove();
}
if (!stale()) busy = false;
}
}
// Shows why the opening could not run, announces it and moves the focus to
// the field at fault, or to the message: the submit button that had it is
// disabled while the opening runs.
async function fail(message: string, field?: ProblemField): Promise<void> {
problem = message;
problemField = field;
announcement = '';
await tick();
announcement = message;
document.getElementById(field === undefined ? 'open-problem' : FIELD_IDS[field])?.focus();
}
function seconds(ms: number): string {
return new Intl.NumberFormat('es-ES', { maximumFractionDigits: 2 }).format(ms / 1000);
}
</script>
<svelte:window onpagehide={leave} />
<section class="open" aria-labelledby="open-title">
<h3 id="open-title">Abrir la cápsula</h3>
{#if !due}
<p class="prose">
La fecha de apertura todavía no ha llegado según el reloj de este dispositivo. Hasta entonces drand no publica la
firma de la ronda {round} y nadie puede abrir la cápsula, tampoco esta página (paso 9, ERR_RELEASE_UNAVAILABLE).
</p>
{:else}
<form class="form" onsubmit={submit} novalidate>
{#if timeAndKey}
<fieldset>
<legend>Credencial de acceso</legend>
<p class="hint">
La política time_and_key pide, además de la firma de la ronda, una clave .dkk de esta cápsula o la identidad
X25519 de uno de sus destinatarios. No salen de este navegador.
</p>
<div class="field">
<label for="dkk-input">Clave .dkk</label>
<input
id="dkk-input"
type="file"
accept=".dkk"
onchange={onAccessKey}
aria-invalid={problemField === 'accessKey' ? 'true' : undefined}
aria-describedby={problemField === 'accessKey' ? 'open-problem' : undefined}
/>
</div>
<div class="field">
<label for="ids-input">Identidades X25519 de age</label>
<textarea
id="ids-input"
rows="2"
bind:value={identities}
autocomplete="off"
spellcheck="false"
placeholder="AGE-SECRET-KEY-1…"
aria-invalid={problemField === 'identities' ? 'true' : undefined}
aria-describedby={problemField === 'identities' ? 'open-problem ids-hint' : 'ids-hint'}
></textarea>
<p id="ids-hint" class="hint">
Si te enviaron la cápsula como destinatario de age, pega aquí tu identidad secreta: la línea
<code>AGE-SECRET-KEY-1…</code> del fichero que creaste con <code>age-keygen</code>. Sirve pegar el fichero entero,
porque las líneas que empiezan por <code>#</code> se ignoran. Una por línea. Solo se usa en este navegador, para
probar cuál de los stanzas de la cápsula abre, y se borra después.
</p>
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
</div>
</fieldset>
{/if}
<div class="field">
<label for="release-input">Firma de la ronda {round}, el release que publica drand</label>
<textarea
id="release-input"
rows="3"
bind:value={releaseInput}
autocomplete="off"
spellcheck="false"
aria-invalid={problemField === 'release' ? 'true' : undefined}
aria-describedby={problemField === 'release' ? 'open-problem release-hint' : 'release-hint'}
></textarea>
<p id="release-hint" class="hint">
{#if fixture?.release !== undefined}
Viene del registro del fixture: es la que publicó drand para la ronda {round}, como muestra
<a href={drandURL} target="_blank" rel="noopener noreferrer">su página en drand</a>. Cámbiala para ver cómo la
rechaza el paso 10.
{:else}
Abre <a href={drandURL} target="_blank" rel="noopener noreferrer">la firma de la ronda {round} en drand</a> en
otra pestaña, copia todo lo que muestra y pégalo aquí; vale también la firma sola, en hexadecimal. La página no
se conecta a drand: la abres tú.
{/if}
Solo se leen la ronda y la firma, que se verifican aquí con la clave pública del perfil fijado (§51).
</p>
</div>
{#if problem}
<p id="open-problem" class="problem" tabindex="-1">{problem}</p>
{/if}
<div class="actions">
<button class="button" type="submit" disabled={busy}>{busy ? 'Abriendo…' : 'Abrir la cápsula'}</button>
{#if fixture === undefined}
<p class="hint">
El texto descifrado se escribe en un fichero temporal privado de este navegador y solo se muestra y se ofrece
si age lo autentica entero (§56); si es texto, la página muestra el principio. Si la cápsula guarda varios
ficheros, ese fichero temporal es un ZIP con todos ellos. Se borra cuando lo pides, al abrir o cargar otra
cápsula y al salir de la página; sin ese fichero, se abre en la memoria de la página hasta 64 MiB.
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
</p>
{/if}
</div>
</form>
{/if}
<p class="visually-hidden" role="status">{announcement}</p>
{#if result}
{@const r = result.report}
<div class={['verdict', r.opened ? 'valid' : 'invalid']}>
<h4 id="open-result-title" class="state-word" tabindex="-1">
{r.opened ? 'Abierta' : r.failure ? `Rechazada en el paso ${r.failure.step}` : 'Rechazada'}
</h4>
{#if r.opened}
<p>
Supera los pasos 9 a 18 en {seconds(result.ms)} s. age ha autenticado el texto entero con la clave de
PAYLOAD_AGE, pero eso no prueba quién lo escribió, ni que sea el original si otros abrieron la cápsula antes
(§55.1).
</p>
{:else if r.failure}
<p class="code">{r.failure.code}</p>
<p>{errorGloss(r.failure.code)}</p>
{#if r.steps.length === 0}
<p class="detail">{escapeInvisible(r.failure.message)}</p>
{/if}
{/if}
</div>
{#if r.plaintext}
{@const p = r.plaintext}
<section class="block" aria-labelledby="plaintext-title">
<h3 id="plaintext-title">Contenido</h3>
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
{#if p.text !== undefined}
{#if p.text === ''}
<p class="muted">El texto en claro está vacío.</p>
{:else}
<pre class="plaintext">{p.textCut ? `${p.text}\n…` : p.text}</pre>
{#if p.textCut}
<p class="muted">
Se muestra el principio, como mucho {formatInteger(SHOWN_TEXT)} caracteres.{fixture === undefined
? ' Descárgalo para verlo entero.'
: ''}
</p>
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
{/if}
{/if}
{:else}
<p class="muted">
No es texto UTF-8 imprimible, así que la página no lo muestra{fixture === undefined
? ': descárgalo y ábrelo con el programa que corresponda.'
: '.'}
</p>
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
{/if}
{#if result.download}
{@const d = result.download}
{#if deleted}
<p class="muted">
{d.temporary
? 'Fichero temporal borrado. Para descargarlo otra vez, abre de nuevo la cápsula.'
: 'El texto descifrado ya no está en la página. Para descargarlo otra vez, abre de nuevo la cápsula.'}
</p>
{:else}
<div class="actions">
<a class="button" href={d.url} download={d.name}>Descargar {d.name}</a>
{#if d.temporary}
<button class="button quiet" type="button" onclick={deleteNow}>Borrar el fichero temporal</button>
{/if}
</div>
<p class="hint">
{#if d.temporary}
Está en un fichero temporal privado de este navegador ({formatByteCount(d.size)}). Se borra cuando lo pides, al
abrir o cargar otra cápsula y al salir de la página.
{:else}
Está en la memoria de esta página ({formatByteCount(d.size)}), porque el navegador no le deja un fichero
temporal privado. Se libera al abrir o cargar otra cápsula y al salir de la página.
{/if}
</p>
{/if}
{/if}
<dl>
<div>
<dt>Tamaño</dt>
<dd>{formatByteCount(p.length)}</dd>
</div>
{#if r.padding}
<div>
<dt>Relleno</dt>
<dd>
regla {r.padding.rule}: la cápsula sellaba {formatByteCount(r.padding.paddedLength)}, el contenido seguido de ceros,
que se comprobaron y no se entregan
</dd>
</div>
{/if}
<div>
<dt>SHA-256</dt>
<dd class="mono">{p.sha256}</dd>
</div>
{#if p.expectedSHA256 !== undefined}
<div>
<dt>Registro del fixture</dt>
<dd class={p.expectedSHA256 === p.sha256 ? 'match' : 'mismatch'}>
{p.expectedSHA256 === p.sha256 ? 'coincide' : 'no coincide'}: <span class="mono">{p.expectedSHA256}</span>
</dd>
</div>
{/if}
</dl>
</section>
{/if}
{#if result.files}
{@const v = result.files}
{@const f = v.files}
<section class="block" aria-labelledby="files-title">
<h3 id="files-title">Contenido</h3>
<ul class="verdict-lines">
{#each f.verdicts as line, i (i)}
<li>{line}</li>
{/each}
</ul>
{#if f.head.author !== ''}
<p class="creator-label">autor declarado (texto del creador, sin comprobar):</p>
<p class="creator"><bdi dir="auto">{escapeInvisible(f.head.author, true)}</bdi></p>
{/if}
{#if f.head.comment !== ''}
<figure class="comment">
<figcaption>Comentario del creador (sin comprobar)</figcaption>
<pre><bdi dir="auto">{escapeInvisible(f.head.comment, true)}</bdi></pre>
</figure>
{/if}
{#if v.shown}
<figure class="comment">
<figcaption>Principio de <bdi dir="auto">{f.facts[0]?.shown}</bdi> (contenido del creador, sin comprobar)</figcaption>
<pre class="plaintext">{v.shown.cut ? `${v.shown.text}\n…` : v.shown.text}</pre>
</figure>
{#if v.shown.cut}
<p class="muted">Se muestra el principio del fichero, como mucho {formatInteger(SHOWN_TEXT)} caracteres.</p>
{/if}
{/if}
{#if v.primary}
{#if deleted}
<p class="muted">
{v.temporary
? 'Fichero temporal borrado. Para descargar los ficheros otra vez, abre de nuevo la cápsula.'
: 'Los ficheros ya no están en la página. Para descargarlos otra vez, abre de nuevo la cápsula.'}
</p>
{:else}
<div class="actions">
<a class="button" href={v.primary.url} download={v.primary.name}>Descargar {v.primary.name}</a>
{#if v.zip}
<a class="button quiet" href={v.zip.url} download={v.zip.name}>Descargar {v.zip.name}, con su carpeta</a>
{/if}
{#if v.temporary}
<button class="button quiet" type="button" onclick={deleteNow}>Borrar el fichero temporal</button>
{/if}
</div>
<p class="hint">
{#if v.temporary}
Están en un fichero temporal privado de este navegador. Se borra cuando lo pides, al abrir o cargar otra
cápsula y al salir de la página.
{:else}
Están en la memoria de esta página: el navegador no le deja un fichero temporal privado, o no tiene sitio
para ellos. Se liberan al abrir o cargar otra cápsula y al salir de la página.
{/if}
</p>
{/if}
{/if}
{#if f.facts.length === 0}
<p class="muted">{f.head.comment === '' ? 'La cápsula no guarda ficheros ni comentario.' : 'La cápsula no guarda ficheros, solo el comentario.'}</p>
{:else}
<h4 class="files-title">Ficheros ({formatInteger(f.facts.length)})</h4>
<ul class="files">
{#each f.facts.slice(0, LISTED) as file, i (i)}
<li>
<bdi dir="auto" class="mono path">{file.shown}</bdi>
<span class="muted"
>{formatByteCount(file.size)}{file.mtimeMs === undefined ? '' : `, fecha del fichero ${formatDateTime(file.mtimeMs)}`}</span
>
{#if v.each && !deleted}
<button class="button quiet small" type="button" onclick={() => saveFile(f, i, file.name)}
>Descargar</button
>
{/if}
{#each file.warnings as warning, k (k)}
<p class="warning">aviso: {warning}</p>
{/each}
</li>
{/each}
</ul>
{#if f.facts.length > LISTED}
<p class="muted">Y {formatInteger(f.facts.length - LISTED)} ficheros más, que están en el ZIP.</p>
{/if}
<p class="hint">
Las rutas son texto del creador: se muestran tal cual, con los caracteres invisibles escritos como
<code>\u…</code>. La fecha de cada fichero es la que declaró el creador y no prueba nada.
</p>
{/if}
</section>
{/if}
{#if r.steps.length > 0}
<section class="block" aria-labelledby="open-steps-title">
<h3 id="open-steps-title">Pasos 9 a 18</h3>
<StepList steps={r.steps} />
</section>
{/if}
{#if r.release}
<section class="block" aria-labelledby="release-title">
<h3 id="release-title">Release verificado</h3>
<dl>
<div>
<dt>Ronda</dt>
<dd class="mono">{r.release.round}</dd>
</div>
<div>
<dt>Firma</dt>
<dd class="mono">{r.release.signature}</dd>
</div>
</dl>
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
</section>
{/if}
{#if r.controlExtensions}
<section class="block" aria-labelledby="control-ext-title">
<h3 id="control-ext-title">Extensiones de CONTROL_CBOR</h3>
<ExtensionList extensions={r.controlExtensions} object="CONTROL_CBOR" />
</section>
{/if}
{/if}
</section>
<style>
.verdict-lines {
margin: 0;
padding-left: 1.2rem;
font-weight: 600;
}
.creator-label {
margin: 0;
color: var(--ink-2, inherit);
}
.creator {
margin: 0;
overflow-wrap: anywhere;
}
.comment {
margin: 0;
padding: 0.75rem 1rem;
border: 1px solid var(--rule);
border-radius: var(--radius);
}
.comment figcaption {
font-weight: 600;
margin-bottom: 0.4rem;
}
.comment pre {
margin: 0;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.files {
display: grid;
gap: 0.5rem;
margin: 0;
padding-left: 1.2rem;
}
.files .path {
overflow-wrap: anywhere;
margin-right: 0.5rem;
}
.files .warning {
margin: 0.2rem 0 0;
color: var(--warn, #9a5b00);
}
.files-title {
margin: 0;
}
.small {
padding: 0.1rem 0.5rem;
font-size: 0.85em;
}
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
.open {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1.5rem;
align-content: start;
}
/* minmax(0, 1fr) and min-width 0: a fieldset is as wide as its content
by default, which widens the page on a phone. */
.form {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1.1rem;
max-width: var(--measure);
padding: 1.1rem 1.25rem 1.25rem;
border: 1px solid var(--rule);
border-radius: var(--radius);
background: var(--paper-2);
}
fieldset {
display: grid;
grid-template-columns: minmax(0, 1fr);
min-width: 0;
gap: 0.9rem;
margin: 0;
padding: 0.75rem 1rem 1rem;
border: 1px solid var(--rule);
border-radius: var(--radius);
}
legend {
padding-inline: 0.35rem;
font-weight: 650;
}
.field {
display: grid;
gap: 0.35rem;
}
label {
font-weight: 600;
}
textarea {
width: 100%;
font-family: var(--mono);
font-size: 0.82rem;
line-height: 1.45;
padding: 0.5rem 0.6rem;
border: 1px solid var(--rule-strong);
border-radius: var(--radius);
background: var(--paper);
color: var(--ink);
resize: vertical;
overflow-wrap: anywhere;
}
input[type='file'] {
max-width: 100%;
font: inherit;
font-size: var(--t-small);
}
.hint {
font-size: var(--t-small);
color: var(--ink-muted);
max-width: var(--measure);
}
.actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem 1rem;
}
/* The download link quotes the file name, which may have no break points;
break-word would not let the flex item shrink below it on a phone. */
.actions a.button {
overflow-wrap: anywhere;
}
.button[disabled] {
opacity: 0.6;
cursor: progress;
}
.problem {
border-left: 4px solid var(--fail);
background: var(--fail-bg);
padding: 0.65rem 0.9rem;
border-radius: 0 var(--radius) var(--radius) 0;
overflow-wrap: anywhere;
}
.verdict {
display: grid;
gap: 0.5rem;
padding: 1.1rem 1.25rem 1.25rem;
border: 1px solid var(--rule);
border-top: 6px solid var(--state);
border-radius: var(--radius);
background: var(--paper-2);
max-width: var(--measure);
}
.verdict.valid {
--state: var(--pass);
}
.verdict.invalid {
--state: var(--fail);
}
.state-word {
font-size: var(--t-h2);
font-weight: 700;
letter-spacing: -0.015em;
line-height: 1.2;
color: var(--state);
}
.code,
.detail {
font-family: var(--mono);
font-weight: 600;
overflow-wrap: anywhere;
}
.detail {
font-size: 0.82rem;
font-weight: 500;
}
.block {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 1rem;
align-content: start;
}
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);
}
dd {
margin: 0;
overflow-wrap: anywhere;
}
dd.mono,
dd .mono {
font-size: 0.85rem;
}
dd.match {
color: var(--pass);
}
dd.mismatch {
color: var(--fail);
}
.plaintext {
max-height: 24rem;
overflow: auto;
padding: 0.85rem 1rem;
background: var(--paper-2);
border: 1px solid var(--rule);
border-radius: var(--radius);
font-size: 0.82rem;
line-height: 1.5;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
</style>

Powered by TurnKey Linux.