From eb07ef82b4277454d90548a3be9aa44130901e26 Mon Sep 17 00:00:00 2001 From: Enrico Piovesan Date: Fri, 2 Oct 2026 14:48:25 -0600 Subject: [PATCH] feat(discover): migrate the generate panel to traverse-embedder-web 0.14 registerPackage (#139) - pin traverse-embedder-web ^0.14.0 (npm 0.14.0) - discover-generate admits fixture.responder@1.0.0 through registerPackage with its real signed manifest bytes and detached Ed25519 signature, trusting only Traverse's labelled test-only fixture key; the pin digest is now the SHA-256 of the signed manifest (wasm digest unchanged) - tests assert both digests against the traverse fixture and that insertVerified is gone - embedder-pin Q&A and llms.txt updated: the ^0.13.0 sentence is no longer true Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 9 ++-- package.json | 2 +- public/llms.txt | 2 +- ...ite-use-the-latest-traverse-embedder.astro | 12 ++--- src/scripts/discover-generate.js | 49 ++++++++++++------- tests/discover-generate.test.mjs | 17 +++++-- 6 files changed, 57 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index ad657b5..21a7df8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "astro": "7.3.4", - "traverse-embedder-web": "^0.13.0" + "traverse-embedder-web": "^0.14.0" }, "devDependencies": { "@playwright/test": "^1.63.0" @@ -3721,9 +3721,10 @@ } }, "node_modules/traverse-embedder-web": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/traverse-embedder-web/-/traverse-embedder-web-0.13.0.tgz", - "integrity": "sha512-JhwgFxabLzbQEA2rqjB/u3sukwUAoTFUw+JHsged94T8H0WDjmkJQNoesJg2gzioZSYIK/YADfJO5TD2XgKjnQ==" + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/traverse-embedder-web/-/traverse-embedder-web-0.14.0.tgz", + "integrity": "sha512-GpqQLapHM0xVdAyR2A3OIzDKfsR3Ncfg8wsu42wyiCluhLsRKJ3LqQujt/ffLEiRMc3BUx0Wnv6VmQ96oNwPAw==", + "license": "Apache-2.0" }, "node_modules/trim-lines": { "version": "3.0.1", diff --git a/package.json b/package.json index 2c7b1dd..ad37603 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "astro": "7.3.4", - "traverse-embedder-web": "^0.13.0" + "traverse-embedder-web": "^0.14.0" }, "devDependencies": { "@playwright/test": "^1.63.0" diff --git a/public/llms.txt b/public/llms.txt index 000fc96..3a79545 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -64,7 +64,7 @@ Current packages: crates.io at 0.14.0 (pin `traverse-registry` at `=0.25.0` if y - [How do hosts trust signed models?](https://traverse-framework.com/questions/how-do-hosts-trust-signed-models.html): host-owned trust roots gate `register_package`; pin by digest + `model.sig.json`; test-only digits-mlp key; exact-ref execute native+web+Swift ExactModelHost; Kotlin/.NET not yet — no claimed ONNX generic runner. - [Which hosts run signed exact-ref models?](https://traverse-framework.com/questions/which-hosts-run-signed-exact-ref-models.html) · [Is production model signing ready?](https://traverse-framework.com/questions/is-production-model-signing-ready.html) · [Can Kotlin or .NET run exact-ref yet?](https://traverse-framework.com/questions/can-kotlin-or-dotnet-embedders-run-exact-ref-yet.html): honest matrix (native+web+Swift ExactModelHost; Kotlin/.NET #1580/#1602); test-only key ≠ prod (#1567). - [What is digits-mlp?](https://traverse-framework.com/questions/what-is-digits-mlp.html): first trained exact-ref package in v0.14.0 (UCI digits MLP, 96.10% held-out, bit-identical); test-only signing; not a general LLM. -- [Does the website use the latest traverse-embedder-web?](https://traverse-framework.com/questions/does-the-website-use-the-latest-traverse-embedder.html): site currently pins `^0.13.0` while npm ships `0.14.0`; `/discover` still uses `insertVerified`; product apps pin published packages, not the website demo. +- [Does the website use the latest traverse-embedder-web?](https://traverse-framework.com/questions/does-the-website-use-the-latest-traverse-embedder.html): site pins `^0.14.0` (matching npm) and `/discover` admits its signed demo fixture through `registerPackage`; the site can lag future releases, so product apps pin published packages, not the website demo. - [How does Traverse complement Hugging Face?](https://traverse-framework.com/questions/how-does-traverse-complement-hugging-face.html): Hub = provenance; Traverse = pinned signed client-first capability; not Transformers.js/Hub replacement; no defer promise until a second executor exists. ## Optional diff --git a/src/pages/questions/does-the-website-use-the-latest-traverse-embedder.astro b/src/pages/questions/does-the-website-use-the-latest-traverse-embedder.astro index bf76701..c6737a6 100644 --- a/src/pages/questions/does-the-website-use-the-latest-traverse-embedder.astro +++ b/src/pages/questions/does-the-website-use-the-latest-traverse-embedder.astro @@ -9,7 +9,7 @@ const jsonLd = JSON.stringify({ name: 'Does the Traverse website use the latest traverse-embedder-web?', acceptedAnswer: { '@type': 'Answer', - text: 'Not always. The public website currently pins traverse-embedder-web at ^0.13.0 while npm publishes 0.14.0 with Traverse v0.14.0. The /discover generate panel still calls ExactModelBrowserHost.insertVerified (the 0.13 API); 0.14 replaced that with registerPackage(manifestBytes, wasm, signatureBytes) for signed Spec 138 packages. Treat published npm/crates versions and what-is-real-today as the product line — do not assume the website pin equals the latest shipped embedder.', + text: 'Today, yes: the website pins traverse-embedder-web at ^0.14.0, the same line npm publishes with Traverse v0.14.0, and the /discover generate panel admits its demo fixture through registerPackage(manifestBytes, wasm, signatureBytes), the 0.14 signed Spec 138 path. The demo fixture is signed with Traverse\'s published test-only key, which it trusts only for that demo. The site can still lag a future release, so treat published npm/crates versions and what-is-real-today as the product line, not the website pin.', }, }], }); @@ -24,19 +24,19 @@ const relatedLinks = [ --- -

Short answer: not always. The marketing site can lag the published package line. That is honesty, not a secret second runtime.

+

Short answer: today, yes, but it can lag. The marketing site follows the published package line after each release. That is honesty, not a secret second runtime.

What is pinned today

-

On traverse-framework/website main, package.json depends on traverse-embedder-web at ^0.13.0. npm’s current published line for that package is 0.14.0, lockstep with Traverse v0.14.0 crates. Cite what is real today for the consumer list apps should build against.

+

On traverse-framework/website main, package.json depends on traverse-embedder-web at ^0.14.0. npm’s current published line for that package is also 0.14.0, lockstep with Traverse v0.14.0 crates. Cite what is real today for the consumer list apps should build against.

-

Why /discover can lag

-

The public /discover generate panel (discover-generate.js) still constructs an ExactModelBrowserHost and calls insertVerified(...) — the 0.13 admission path. In 0.14, that host admits packages through registerPackage(manifestBytes, wasm, signatureBytes) so signed Spec 138 manifests and model.sig.json are verified before execute. Migrating the demo is separate eng work; until it lands, the site pin stays on 0.13 so the demo keeps working.

+

How /discover admits its demo model

+

The public /discover generate panel (discover-generate.js) constructs an ExactModelBrowserHost and admits the fixture.responder@1.0.0 conformance fixture through registerPackage(manifestBytes, wasm, signatureBytes), the 0.14 path. The Ed25519 signature over the exact model.manifest.json bytes, the pin digest, and the WASM digest are all verified before execute. The fixture is signed with Traverse's published test-only key, which the demo trusts only for that fixture. Product hosts configure their own trust roots. Earlier, the 0.13 pin used insertVerified, which 0.14 removed.

What you should pin

If you are building a product host, depend on the published npm / crates.io versions (today traverse-embedder-web@0.14.0 / traverse-embedder@0.14.0), not on whatever the website happens to ship in its demo bundle. The site is a citeable demo surface; it is not the version authority for your app.

diff --git a/src/scripts/discover-generate.js b/src/scripts/discover-generate.js index d14099f..f8089ef 100644 --- a/src/scripts/discover-generate.js +++ b/src/scripts/discover-generate.js @@ -32,6 +32,17 @@ import { // Small enough to embed and audit directly rather than fetch. const FIXTURE_WASM_B64 = 'AGFzbQEAAAABCQFgBH9/f38BfwMCAQAFAwEAAgcaAgZtZW1vcnkCAA1tb2RlbF9leGVjdXRlAAAK9AEB8QEBB38gAUEMSQRAQX8PCyAAKAIIIQQgAEEMaiEFQQwgBGogAUsEQEF/DwtBACEIQQAhBgJAA0AgBkECaiAESw0BIAUgBmotAABB6ABGIAUgBkEBamotAABB6QBGcQRAQQEhCAwCCyAGQQFqIQYMAAsLIAhBAUYEQEHKuAIhCUEIIQoFQd64AiEJQQMhCgtBDCAKaiADSwRAQX8PCyACQQE7AQAgAkEEOgACIAJBAToAAyACIAo2AgQgAiAKNgIIQQAhBwJAA0AgByAKTw0BIAJBDGogB2ogCSAHai0AADoAACAHQQFqIQcMAAsLQQwgCmoLCyMDAEHAuAILAmhpAEHKuAILCGhpIHRoZXJlAEHeuAILA2htbQCUAQRuYW1lAmIBAAsABmluX3B0cgEGaW5fbGVuAgdvdXRfcHRyAwdvdXRfY2FwBAtwYXlsb2FkX2xlbgULcGF5bG9hZF9wdHIGAWkHAWoIB21hdGNoZWQJCHJlc3BfcHRyCghyZXNwX2xlbgMpAQAEAgtzZWFyY2hfZG9uZQMGc2VhcmNoBwljb3B5X2RvbmUIBGNvcHk='; +// The same fixture's signed Spec 138 package files (traverse-embedder-web +// 0.14 admits packages only through registerPackage): the exact bytes of +// fixtures/models/fixture-responder-1.0.0/model.manifest.json (its SHA-256 +// is the pin digest) and its detached Ed25519 model.sig.json. +const FIXTURE_MANIFEST_B64 = 'ewogICJzY2hlbWFfdmVyc2lvbiI6ICIyLjAuMCIsCiAgIm1vZGVsX2lkIjogImZpeHR1cmUucmVzcG9uZGVyIiwKICAidmVyc2lvbiI6ICIxLjAuMCIsCiAgIndhc21fZGlnZXN0IjogImJmMDQ3NjBiMTkzN2MyZjJiODEzYjZlMjhmMmZkYzZlMzM0ODMzYzJkZmFkYzFmN2M4NzJmM2YyOGMzNDEyOTQiLAogICJyZWdpc3RyeV9yZWYiOiAicmVnaXN0cnk6Zml4dHVyZS5yZXNwb25kZXJAMS4wLjAiLAogICJleGVjdXRhYmxlX2Zvcm1hdCI6ICJ0cmF2ZXJzZS1tb2RlbC13YXNtIiwKICAiYWJpX3ZlcnNpb24iOiAxLAogICJpbnB1dF9zY2hlbWFfcmVmIjogInNjaGVtYTpicmlkZ2VkLWdlbmVyYXRlLWluIiwKICAiaW5wdXRfc2NoZW1hX3ZlcnNpb24iOiAiMS4wLjAiLAogICJvdXRwdXRfc2NoZW1hX3JlZiI6ICJzY2hlbWE6YnJpZGdlZC1nZW5lcmF0ZS1vdXQiLAogICJvdXRwdXRfc2NoZW1hX3ZlcnNpb24iOiAiMS4wLjAiLAogICJyaWdodHMiOiB7CiAgICAibGljZW5zZV9pZCI6ICJBcGFjaGUtMi4wIiwKICAgICJhdHRyaWJ1dGlvbiI6ICJUcmF2ZXJzZSBTcGVjIDA0NS8xMzggYnJpZGdlIGNvbmZvcm1hbmNlIGZpeHR1cmUiLAogICAgInJlZGlzdHJpYnV0aW9uIjogInRlc3Qtb25seTsgbm90IGZvciBwcm9kdWN0aW9uIHJlZGlzdHJpYnV0aW9uIGNsYWltcyIsCiAgICAiY29tbWVyY2lhbF91c2UiOiAiYWxsb3dlZCIsCiAgICAic291cmNlX3VybCI6ICJodHRwczovL2dpdGh1Yi5jb20vdHJhdmVyc2UtZnJhbWV3b3JrL1RyYXZlcnNlL3RyZWUvbWFpbi9maXh0dXJlcy9tb2RlbHMvZml4dHVyZS1yZXNwb25kZXItMS4wLjAiCiAgfSwKICAic3VwcG9ydGVkX3Byb2ZpbGVzIjogWwogICAgIndhc20tY3B1IgogIF0sCiAgIm1heF9tZW1vcnlfYnl0ZXMiOiAxMzEwNzIsCiAgIm1heF9mdWVsIjogMTAwMDAwMCwKICAibWF4X2lucHV0X2J5dGVzIjogNDA5NiwKICAibWF4X291dHB1dF9ieXRlcyI6IDQwOTYsCiAgIm1heF9leGVjdXRpb25fbXMiOiA1MDAwLAogICJvZmZsaW5lX2FsbG93ZWQiOiB0cnVlCn0K'; +const FIXTURE_SIGNATURE_B64 = 'ewogICJhbGciOiAiZWQyNTUxOSIsCiAgImtleV9pZCI6ICJlZDI1NTE5OjQ1NWRmZmIwOTMxNjNkODQzOWI1NDRjYzRmMGVhNzllMzNhMDdiMjA1OTRjNDczOWE4YmJiNGMwODE5ZDJkNjciLAogICJzaWduYXR1cmUiOiAiOTkzZDA0M2QzZTM5NmE5OTkxMjUzZmY1NWZiZDgwNjU4MWZlYjU5MTYxNDRjNTc5ZTAzMTMxZGZjOTVmMmNlOTBlZTE2ODQ1ZGE0YjY5ZmIzZWNkMzc2YjYzNmVhZTBjYTQzM2RlNTA1OWY1MmNkNWMzNjQ4Yjc0YmY2NThkMDQiCn0K'; +// TEST-ONLY public key (fixtures/models/test-signing-key.json in +// traverse-framework/traverse) that signed the conformance fixtures. It is +// trusted here only to run this demo fixture; production hosts never trust it. +const FIXTURE_TEST_KEY_HEX = '97002eba1e28b582a739eb48750a5f0822b2bbcb61e6bcedd2fada36b51af355'; + const MODEL_ID = 'fixture.responder'; const MODEL_VERSION = '1.0.0'; const GUEST_TEXT_DTYPE = 4; // matches the fixture's own conformance test (Spec 138 guest ABI v1) @@ -54,22 +65,22 @@ async function sha256Hex(bytes) { the WASM guest decides, and this function only reports what it did. */ export async function runResponderDemo(promptText) { const wasm = b64ToBytes(FIXTURE_WASM_B64); - const digest = await sha256Hex(wasm); - const host = new ExactModelBrowserHost([ - { model_id: MODEL_ID, version: MODEL_VERSION, digest, offline_allowed: true }, - ]); - await host.insertVerified( - { - model_id: MODEL_ID, version: MODEL_VERSION, - wasm_digest: digest, package_digest: digest, - input_schema_ref: 'schema:bridged-generate-in', input_schema_version: '1.0.0', - max_memory_bytes: 131072, max_fuel: 1_000_000, - max_input_bytes: MAX_BYTES, max_output_bytes: MAX_BYTES, - offline_allowed: true, supported_profiles: [PLACEMENT_WASM_CPU], - license_id: 'Apache-2.0', abi_version: 1, - }, - wasm, + const manifestBytes = b64ToBytes(FIXTURE_MANIFEST_B64); + const wasmDigest = await sha256Hex(wasm); + // Spec 138: the pin digest is the SHA-256 of the exact signed manifest + // bytes, which pin wasm_digest in turn. + const digest = await sha256Hex(manifestBytes); + const host = new ExactModelBrowserHost( + [{ + model_id: MODEL_ID, version: MODEL_VERSION, digest, offline_allowed: true, + target: PLACEMENT_WASM_CPU, rights: { license_id: 'Apache-2.0', commercial_use: 'allowed' }, + }], + { trustedPublicKeysHex: [FIXTURE_TEST_KEY_HEX] }, ); + // Real verification: Ed25519 signature by a trusted key over the exact + // manifest bytes, pin digest, WASM digest, rights, target, and limits. + // Any mismatch throws; nothing here can fabricate an admitted package. + await host.registerPackage(manifestBytes, wasm, b64ToBytes(FIXTURE_SIGNATURE_B64)); const promptBytes = new TextEncoder().encode(promptText); const frame = encodeGuestFrame(GUEST_TEXT_DTYPE, [promptBytes.length], promptBytes); @@ -97,7 +108,7 @@ export async function runResponderDemo(promptText) { output_ref: result.output_ref, }); - return { digest, response, placement: result.placement, evidence }; + return { digest, wasmDigest, response, placement: result.placement, evidence }; } function el(id) { return document.getElementById(id); } @@ -121,11 +132,11 @@ async function runDemo() { log.innerHTML = ''; if (out) out.hidden = true; btn.disabled = true; - logLine(log, '$ sha256(model.wasm) — verify against the fixture.responder@1.0.0 pin', 'cmd'); + logLine(log, '$ registerPackage — verify the Ed25519 signature (test-only fixture key), manifest pin, and sha256(model.wasm)', 'cmd'); try { - const { digest, response, placement, evidence } = await runResponderDemo(promptText); - logLine(log, '✓ digest verified: sha256:' + digest.slice(0, 23) + '…', 'ok'); + const { digest, wasmDigest, response, placement, evidence } = await runResponderDemo(promptText); + logLine(log, '✓ signed package verified: pin sha256:' + digest.slice(0, 16) + '…, wasm sha256:' + wasmDigest.slice(0, 16) + '…', 'ok'); logLine(log, '$ encode prompt as a Spec 138 guest frame (ABI v1) and stage it', 'cmd'); logLine(log, '$ execute — wasm-cpu, offline, no network, no daemon', 'cmd'); logLine(log, '✓ response: "' + response + '" (placement=' + placement + ')', 'ok'); diff --git a/tests/discover-generate.test.mjs b/tests/discover-generate.test.mjs index bf9b789..ac4053d 100644 --- a/tests/discover-generate.test.mjs +++ b/tests/discover-generate.test.mjs @@ -21,12 +21,23 @@ test('runResponderDemo genuinely executes the checked-in bridge fixture, not a c assert.equal(miss.digest, hit.digest); }); -test('the fixture digest matches the real checked-in traverse-framework/traverse conformance fixture', async () => { +test('the fixture digests match the real checked-in traverse-framework/traverse conformance fixture', async () => { // fixtures/models/fixture-responder-1.0.0/model.manifest.json in // traverse-framework/traverse pins this exact wasm_digest for the same // 491-byte file — this embeds the real bytes, not a stripped variant. - const { digest } = await runResponderDemo('hi'); - assert.equal(digest, 'bf04760b1937c2f2b813b6e28f2fdc6e334833c2dfadc1f7c872f3f28c341294'); + // Since traverse-embedder-web 0.14 the pin digest is the SHA-256 of that + // exact signed manifest file. + const { digest, wasmDigest } = await runResponderDemo('hi'); + assert.equal(wasmDigest, 'bf04760b1937c2f2b813b6e28f2fdc6e334833c2dfadc1f7c872f3f28c341294'); + assert.equal(digest, '50b74bd39bc81da3639d74f2df6e31c243d1a496fde0127486630624b2983763'); +}); + +test('admission goes through registerPackage with a real signature, not the removed insertVerified', async () => { + const gen = await read('src/scripts/discover-generate.js'); + assert.match(gen, /registerPackage\(/); + assert.doesNotMatch(gen, /insertVerified/); + // The test-only fixture key is labelled as such, never presented as production trust. + assert.match(gen, /TEST-ONLY public key/); }); test('/discover states the generation panel honestly and does not overclaim', async () => {