datekeys-ts 0.1.0 inspects and opens DateKeys capsules of both formats
of spec v0.9 (tag spec-v0.9 of datekeys-go), in memory or streaming,
with the /inspect page. The writer of phase 3 comes with 0.2.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Syncs testdata with datekeys-go at spec-v0.9 (7e2d83c) and moves the
reader to the DateKeys Protocol Specification v0.9. Both capsule formats
are read; a format 1 capsule keeps the verdict v0.8.2 gave it.
- framing: the VERSION of the prelude is the capsule format, 1 or 2
(Prelude.format, FORMAT_1, FORMAT_2, isFormat).
- control: decodeControl and encodeControl take the format; schema
version 2 adds payload_length (8 bytes, at most L_MAX) and padding
(1 or 2).
- padding.ts: the rules bloque256 and reforzado of spec §29.1, exact up
to L_MAX with BigInt bit lengths and ceil roundings, and the length of
PAYLOAD_AGE.
- open: exactly 16 stanzas in INNER_ACCESS_AGE of format 2 (step 12), P
at step 16, and at step 17 a plaintext of exactly P bytes whose
padding is zero; only the first L bytes are delivered, never the
padding. Step 17 is recorded when it passes, and step 18 gives the
bytes of content, as the reference does. Opened reports the format, L
and, in format 2, the rule and P.
- inspect: the JSON view carries format, as datekeys inspect -json.
- The page shows the format, warns about format 1, and gives the
padding rule and P once a format 2 capsule opens.
Tests: the twelve fixtures, the 125 mutation cases through open from
memory and from a Blob, the 4380 differential cases, padding.json, the
format 2 CBOR vectors, and padding.test.ts against a BigInt statement of
§29.1. The error texts of the 125 corpus cases were compared with
capsule.Open at spec-v0.9. ibe-vectors.json gains the seven format 2
fixtures from scripts/ibe-go-vectors.go; its frozen values are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The handoff, the plans and the protocol review describe the whole project,
not this implementation, so they move unchanged to the private docs
repository next to this one (../docs, commit 6e8d6c6).
The folder is being renamed from App to datekeys-ts, to match datekeys-go.
The package name, the README title and the site's licence notice follow.
The README points to the prototype's new place, ../archive/prototype.
npm run verify is green: 2,611 tests, build and build checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
- ibe.ts gains encryptOnG2RFC9380, EncryptCCAonG2 of kyber with the
suite of tlock for Quicknet. Qid is H(id) on G1 with the RFC 9380 DST,
sigma comes from crypto.getRandomValues, U = r·G2, V = sigma XOR
H2(e(Qid, key)^r) and W = msg XOR H4(sigma). The key passes the
canonical gate, and sigma and the masks are wiped. encryptOnG2WithSigma
takes a given sigma, for the vectors only; index.ts exports neither.
- tlock.ts adds timeRecipient, the age-encryption Recipient of
OUTER_TIME_AGE, as Go's agewrap.TimeRecipient. It writes the stanza
"tlock <round> <chain hash>" with the checks and texts of
NewTimeRecipient: the scheme and the pinned key, then the round range.
age-encryption has no labels, so the writer of phase 3 adds it alone.
Vectors, in src/lib/dkc/testing/tlock-vectors.json from
scripts/tlock-go-vectors.go:
- Fixed-sigma encryptions of 1, 16 and 32 bytes for rounds 1000 and
1001. Go restates EncryptCCAonG2, since kyber draws sigma itself, and
checks the restatement with ibe.DecryptCCAonG2 and tlock.TimeUnlock.
encryptOnG2WithSigma reproduces them byte for byte.
- The samples of scripts/tlock-ts-samples.mjs, which Node runs on the
TypeScript sources: IBE bodies and age files that this library made
for rounds 1000 and 1001. Go opened every one: the bodies with
tlock.TimeUnlock and the age files with age.Decrypt and
agewrap.NewTimeIdentity, the identity of step 11. It got the same
file keys and plaintexts, and the samples are frozen with those
verdicts.
tlock.test.ts replays both blocks, the random round trip, the
rejections with their texts, and an age file sealed with timeRecipient
and opened with the step-11 identity of open.ts. Coverage of ibe.ts and
tlock.ts is 100 %, now a threshold for tlock.ts too. Step 6 of the plan
is recorded as done: the canonicality amendment is in spec-v0.8.2.
npm run verify is green: 2,567 tests. The site does not change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
open(dkc, opts) now takes a Uint8Array or a Blob, such as a File.
- Of a Blob it reads only the prefix that steps 1 to 8 need.
inspectedLength and readCapsule move from src/lib/inspector/load.ts to
src/lib/dkc/prefix.ts, and the page imports them from the library.
- The .dkk capsule_digest is computed over the Blob's stream with the
new src/lib/dkc/digest.ts, an incremental SHA-256 on @noble/hashes,
since Web Crypto hashes whole buffers only. digest.ts joins the noble
allowlist of the guards.
- PAYLOAD_AGE is decrypted in streaming.
The plaintext goes to memory, as before, or to opts.output, a
WritableStream. The output is written as age authenticates each chunk,
closed only after step 18, and aborted after any failure at any step,
even before step 17 (spec §56). A failure of the output is ERR_INTEGRITY
with its text, as Go keeps the error of the writer of the plaintext.
Tests:
- the fixtures from Blobs, to memory and to an output;
- a truncated two-chunk payload whose first chunk reached the output
before the abort;
- early failures that never write;
- write and close failures, and an abort that fails;
- a .dkk without capsule_digest;
- the whole mutation corpus again as Blobs into an output, aborted in
every case;
- digest.ts against Web Crypto.
Coverage of digest.ts is 100 % and a threshold.
Checked in the browser (dev server, real OPFS). time_only.dkc, two
STREAM chunks, opened from a Blob into FileSystemFileHandle.createWritable
gives 78,000 bytes with the SHA-256 of its sidecar. The same file
truncated fails at step 17, and the OPFS file keeps its previous
content. The quota check, the temporary file and the download belong to
the page, in step 8.
npm run verify is green: 2,560 tests. The site does not change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/lib/dkc/open.ts runs steps 9 to 18 of spec §63 on top of the steps
1 to 8 of inspectWith. It follows capsule.Open of the Go reference, with
its checks, codes and texts:
- step 9: the access credentials (the .dkk as an object, then its
capsule_id and capsule_digest), then the release, never before the
round time. Any failure of the source is ERR_RELEASE_UNAVAILABLE
alone, keeping its text and its cause (correction 6);
- step 10: verifyRelease;
- steps 11 to 13: OUTER_TIME_AGE, the structure against access_policy
and INNER_ACCESS_AGE;
- steps 14 to 18: CONTROL_CBOR, header_binding, I_PAYLOAD, PAYLOAD_AGE
and the commit.
The three age files open with the Decrypter of age-encryption and
identities that apply the rules of Go's agewrap: the tlock identity on
ibe.ts, and the access and payload identities on x25519.ts. A failure of
age that no identity reports is ERR_INTEGRITY with the fixed reason of
its phase, header or STREAM, never the text of age-encryption. The
plaintext is decrypted in memory and returned only after step 18.
Streaming to OPFS is step 5b.
src/lib/dkc/x25519.ts opens one age X25519 stanza at a time, in the
order of age's X25519Identity. Step 13 must try every identity on every
stanza (spec §36), and age-encryption's Decrypter stops at the first.
Its primitives are the ones age-encryption uses: X25519 and HKDF from
noble curves and hashes, and ChaCha20-Poly1305 from @noble/ciphers
2.4.0. The author approved declaring that package as a direct
dependency on 2026-09-28; it is the copy already installed and bundled.
The guards now allow ciphers, and x25519.ts in the noble allowlist.
src/lib/dkc/bech32.ts ports age's internal/bech32, with its MIT notice,
to read AGE-SECRET-KEY-1 identities.
Tests:
- vectors.test.ts runs all 65 cases of the mutation corpus through open.
Each gives the code and the step of Go, and no case that fails without
the network requests a release. This includes the 34 cases of steps 9
to 18 that were skipped, so the suite no longer skips any test.
- open.test.ts:
- the five official fixtures open to their plaintext, with each
credential, with the checks and details of the reference;
- the unusable noncritical extensions are reported;
- the source failures and the clock;
- age failures by phase;
- CONTROL_CBOR that does not decode, and a low-order share in
INNER_ACCESS_AGE, through an OUTER_TIME_AGE resealed with the FK_TIME
of the Go vectors;
- the texts of the identities.
- x25519.test.ts checks against age-encryption both ways and against the
Go-written stanzas of the fixtures, and covers every low-order share.
- bech32.test.ts has the vectors of the reference.
x25519.ts and bech32.ts are at 100 % coverage, now thresholds. open.ts
is at 100 % of lines; the one branch left is the one for an error that
is not a DateKeysError.
index.ts does not re-export the opening yet. The page imports index.ts,
and re-exporting would pull noble into /inspect (58.7 to 84.9 KB gzip)
even unused; step 8 will load it on demand. The site does not change.
npm run verify is green: 2,490 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- src/lib/dkc/version.ts exports VERSION (0.1.0-dev, which becomes
0.1.0 once phase 2 adds the opening of capsules) and SPEC_VERSION
(0.8.2, the tag spec-v0.8.2 of datekeys-go), from index.ts too.
- package.json and its lockfile move to 0.1.0-dev. version.test.ts ties
VERSION to both and checks it is semantic versioning. It also ties
SPEC_VERSION to the spec field of the shared vectors and fixtures.
testing/vectors.ts now takes SPEC_VERSION from version.ts, so every
vector file is checked against the version the library declares.
- The footer of the page shows both, instead of a fixed 0.8.2.
- README.md gains a "Versiones" section: the three versions (format,
specification, library) and what 0.1.0-dev covers. CHANGELOG.md is
new.
The Go reference gained datekeys.SpecVersion, datekeys.Version() and
`datekeys version` in 5b342d3.
npm run verify is green: 2,406 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/lib/dkc/release.ts is provider.Verify of the Go reference (spec §17,
§51, §63 step 10), in its order and with its texts:
- the round within the profile (ERR_DATEKEY_INVALID);
- the round of the release before the signature (ERR_ROUND_MISMATCH);
- the length of the signature;
- the pinned public key (ERR_UNKNOWN_PROFILE when it does not decode;
the point at infinity fails the verification, as with kyber);
- the signature (ERR_RELEASE_INVALID): the canonical encoding of a point
of G1 other than the point at infinity, gated by bls12381.ts, that
verifies on @noble/curves 2.4.0 as the BLS signature of
SHA-256(uint64be(round)), hashed with the RFC 9380 DST of G1.
Nothing noble throws becomes anything but ERR_RELEASE_INVALID, and no
text of noble is copied. Only Quicknet's scheme is verified (plan
decision 3): another scheme fails with ERR_UNKNOWN_PROFILE after the
round checks. It also defines ReleaseSource, with the contract for
network sources and correction 6, and suppliedRelease, the release that
the caller hands over (unverified, so step 10 checks it). index.ts does
not re-export it yet.
release.test.ts:
- replays TestVerifyRejects of the reference, with exact texts;
- accepts the published releases of rounds 1000, 1001, 2000 and 1004
(the last one recovered from the x + p encoding of the corpus, which
shows that encoding is the published signature re-encoded);
- shows the DST of G2, the one bls-unchained-on-g1 uses, fails;
- gives the code of each of the 7 corpus cases that fail at step 10,
with the round that inspect reads from the capsule.
ibe.failure.test.ts becomes noble.failure.test.ts and also fails noble's
BLS verification. Coverage of release.ts is 100 % and is now a
threshold. The site does not change.
npm run verify is green: 2,404 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/lib/dkc/ibe.ts is DecryptCCAonG2 of drand/kyber encrypt/ibe, the
decryption of tlock.TimeUnlock for Quicknet, on @noble/curves 2.4.0
(plan of phase 2, section 4). It adds nothing that kyber would reject:
- the signature and U pass the canonical-encoding gate of bls12381.ts,
which rejects the point at infinity too;
- H2 hashes GT in the order of kilic, never with noble's Fp12.toBytes;
- H3 and H4 follow kyber, including the rejection sampling of r, and
r = 0 never proves;
- roundIdentity is drand's DigestBeacon;
- the stanza body is exactly U || V || W, 128 bytes, as in tlock.
Errors are IbeError with a fixed reason (length, encoding, identity,
proof) and message: none carries sigma, the message, r or input bytes.
Anything noble throws past the gate is a proof failure. sigma and the
hashes derived from it are wiped on every path. The file keeps the MIT
notice of tlock-js, whose structure it follows. index.ts does not
re-export it yet; the opening of step 5 will use it.
scripts/ibe-go-vectors.go writes src/lib/dkc/testing/ibe-vectors.json
with kyber, tlock and age:
- the GT of e(G1, G2) and of its square, with H2;
- H3, including inputs accepted at the second and third iteration, and
H4;
- round identities;
- for the tlock stanza of every official fixture, the pairing, sigma, r
and the file key. tlock.TimeUnlock unwraps that file key, and age
opens OUTER_TIME_AGE with it;
- messages of 0, 1, 16 and 32 bytes encrypted by EncryptCCAonG2;
- kyber's verdict on eleven edited copies of the time_only stanza.
It restates the unexported H2, H3 and H4 and checks them on every
fixture against tlock and U = r·G2.
ibe.test.ts replays every vector and opens OUTER_TIME_AGE of each
fixture through age-encryption with a custom Identity: the header MAC
and STREAM verify, and a time_only fixture yields its control_cbor. It
also gates all 157 BLS edge encodings as the Go reference decodes them
and checks the fixed error texts. ibe.failure.test.ts mocks a noble
failure. Coverage of ibe.ts is 100 % and is now a threshold. The site
does not change.
npm run verify is green: 2,397 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- age-encryption 0.3.1, @noble/curves 2.4.0 (moved from dev) and
@noble/hashes 2.4.0 become exact runtime dependencies (plan section 3,
decision 5). The lockfile gains six packages: age-encryption,
@noble/ciphers 2.4.0, @scure/base 2.4.0, @noble/post-quantum 0.5.4 and
its own @noble/curves and @noble/hashes 2.0.1. No file of src/ imports
them yet, so the site does not change.
- src/lib/dependencies.test.ts guards them. package.json declares exactly
these three, pinned. The lockfile has no tlock-js, drand-client or noble
1.x, and no noble 2.x copy other than 2.4.0 at the root and 2.0.1 under
@noble/post-quantum. No file of src/ imports tlock-js or drand-client.
Only ibe.ts, release.ts and the tests name @noble/, always subpaths of
@noble/curves or @noble/hashes that resolve to the root 2.4.0 copy.
Every check also runs on bad inputs. It replaces the "only tests import
@noble/curves" test of bls12381.contrast.test.ts.
- vite.config.ts records the modules of each client chunk in
.svelte-kit/output/client-modules.json. check-build.mjs fails if the
bundle holds tlock-js, drand-client or @babel/*, or a nested copy
other than noble under @noble/post-quantum. It also reports the
JavaScript each page loads: /inspect today loads 157 KB, 58.7 KB gzip.
- Measured with a probe build (Vite 8, minified, gzip 9): the Decrypter
is 48 KB gzip, with the Encrypter 56 KB, noble BLS12-381 plus SHA-256
28 KB, and all of them 73 KB. age-encryption imports its hybrid ML-KEM
recipients statically, so post-quantum and its nested noble copy are
about 99 KB of the Decrypter's 212 KB of rendered code.
- npm audit --omit=dev: no vulnerabilities. The full audit finds two low
ones in the tooling: cookie < 0.7.0 through @sveltejs/kit 2.70.3,
which is the latest version and affects only SvelteKit's server.
npm run verify is green: 2,384 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
testdata is synced from datekeys-go c57ed48, which adds
vectors/tlock_ibe.json, the frozen H2 of the tlock IBE (spec §63 step
11). Until phase 2 brings the IBE, the harness recomputes it with the
audited @noble/curves 2.4.0 (development only): the points are canonical
for bls12381.ts, noble's pairing serialized in the order of kilic is the
vector's GT, its H2 matches, and noble's own Fp12.toBytes order gives
another hash.
The extension registry gains the optional registeredIn: a known
extension out of the objects and arrays of its registration counts as
unknown there (spec §54, §72), as Go's extension.Placement; step 4 of
inspect checks PUBLIC_HEADER with it. A test copies a CONTROL_CBOR-only
critical extension into PUBLIC_HEADER and fails if the check is removed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
testdata is synced from datekeys-go f6f2e9f: mutations.json has 65
cases, the ten new §64 ones at steps 10 and 11 (phase 2, skipped and
counted: 31 run, 34 skipped).
The reference no longer copies library error text into errors, so the
TypeScript uses its fixed texts too: every unreadable age header is
"agewrap: not an age v1 header: malformed, truncated or beyond the
parser limits", with the parser's reason in the error's cause, which the
tests still compare with age's own texts; the profile messages for an
unknown drand scheme and a non-canonical public key follow Go. Against
Go at f6f2e9f, 407,196 differential inputs agree on verdict, code, step,
error text, check details and the full inspect -json output.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The author confirmed the decisions of PLAN_fase2_ibe_noble2.md (v2),
with the adjustments of two reviews verified against the code, the spec
and npm: the ReleaseSource contract (a source that obtains no verified
release fails at step 9 with ERR_RELEASE_UNAVAILABLE, a caller-supplied
release at step 10 with ERR_RELEASE_INVALID, as Go's drand client);
age-encryption 0.3.1 without npm overrides, accepting the nested noble
2.0.x of @noble/post-quantum (~2.0.0) under guards that keep the BLS
code on the exact 2.4.0; streaming decryption of PAYLOAD_AGE into OPFS,
released only after age succeeds (§56), with the storage quota as the
limit; IBE test vectors generated from the Go reference; verifyRelease
in the order of provider.Verify; the canonicality spec change as an
amendment of v0.8.2.
App is now Apache-2.0, like the Go reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
testdata is synced from datekeys-go 692cf87, where the reference stopped
replacing invalid UTF-8 in the dk1_ JSON with U+FFFD and fails step 2
with ERR_DATEKEY_INVALID, as §19 requires. parseJSON checks the bytes
first in the same way, the test that pinned the old reference behaviour
now pins the spec's, and the README drops the Go-vs-spec conflict note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finishes the interrupted alignment of 3305bbb. The TypeScript now
follows the reference at 3820066 on steps 1 to 8, decoding and profile
validation:
- §69.1 precedence: the schema head is read strictly (a null or simple
version is ERR_NON_CANONICAL_CBOR, version 2 is ERR_UNSUPPORTED_VERSION
whatever follows) and every CDDL rule, including access_policy,
extension_version and the 64-extension cap read from the array head,
is checked before the DateKey; the wideUint path that imitated the
old Go order is gone.
- §19: CR or LF in dk1_ is ERR_DATEKEY_INVALID; §15: a round at exactly
9999-12-31T23:59:59Z is valid; §12.1: period above one day is
rejected on decode and pin; BODY_LEN 0 is ERR_INTEGRITY.
- Inspector texts no longer call header data authenticated at step 15
(§55.1: binding, never authorship or date); the fixture list ignores
the new *.inspect.json goldens.
The harness runs every shared Go vector: dk1.json 30, quicknet_rounds
17, profile 1, cbor.json 103 generic and 135 schema vectors,
mutations.json 31 through inspect with 24 phase-2 cases skipped and
counted, inspect_differential.json 1,825, and the five inspect goldens
byte-identical. A differential of 483,527 inputs against the Go
reference found no disagreement on verdict, code or step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Our checkCompressedPoint stays: it matches the Go reference on every edge
case, is 1.3 KB gzip and adds no runtime dependency. Its assurance now
comes from a contrast test run on every test pass:
- 41 frozen edge-case encodings with the Go reference verdict (drand crypto
KeyGroup over kyber-bls12381 and kilic/bls12-381, as profile.Validate
uses it), reproducible with scripts/bls12381-go-verdicts.go;
- the audited @noble/curves 2.4.0 on the same edge cases and on a
fixed-seed corpus of valid points, negations, bit flips, random x and G1
points on the curve outside the subgroup.
Breaking the G1 or the G2 subgroup check makes the test fail.
@noble/curves 2.4.0 is a development dependency only; a test fails if
anything that is not a test imports it, and the build contains none of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A prerendered static site (adapter-static) with a landing page and
/inspect, which runs spec §63 steps 1 to 8 on a .dkc chosen with the file
picker, dropped anywhere on the page, or taken from the official fixtures
bundled at build time. It shows every step, the decoded header, the unlock
date in UTC and local time, and each extension's id, version, criticality,
length and hex, with a text view and an informative CBOR diagnostic view,
all escaped and labelled as unauthenticated before step 15. Copiar JSON
copies the exact "datekeys inspect -json" view.
No network: a hash-mode Content-Security-Policy with connect-src 'self'
is the first element of every page, and scripts/check-build.mjs verifies
it, the fixtures and the absence of external URLs after every build.
Large files are read only up to what steps 1 to 8 need.
Reviewed for design and accessibility (WCAG AA contrast, keyboard,
focus, live status, 360 px), security and correctness; 262 tests pass,
svelte-check has no warnings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Canonical CBOR codec of the spec §58 profile, hand-written schema codecs
(profile, PUBLIC_HEADER, CONTROL_CBOR, .dkk, extensions), DKC1/DKK1
framing, a strict age header parser, dk1_ parsing and nanosecond date to
round resolution, and inspect (spec §63 steps 1 to 8) with the same checks
and view as "datekeys inspect -json". No runtime dependencies.
216 tests, cbor.ts at 100 % coverage, typecheck of the library without
Node types. A differential comparison with the Go reference at afb44a3
found no verdict, code or step disagreement in about 336,000 inputs. The
harness for the future Go vector files runs them when they appear.
vite 8.3.0 is declared explicitly: it is a required peer of vitest 5.0.1
that legacy-peer-deps does not install.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Skeleton for the TypeScript implementation of DateKeys v0.8.x: the plans
in docs/, testdata/ vendored from g.activething.com/go/DateKeys at 5719f6a
with a zero-dependency sync and check script, and the TypeScript and vitest
tooling already used by the prototype, which now lives in ../AppOld.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>