From 48782cd0ee008674ee5d65af560ade0b0db829ec Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Wed, 14 Oct 2026 11:29:25 -0500 Subject: [PATCH 1/3] docs(resource): use BUD-10 blossom:. URIs Replace every blossom:sha256: reference with the BUD-10 Blossom URI form (blossom:., .bin when the extension is unknown) and link the BUD-10 spec from scheme descriptions and resource JSDoc. Co-Authored-By: Claude Opus 5.5 --- apps/docs/naps/index.md | 8 ++++---- apps/docs/packages/nap.md | 2 +- apps/docs/packages/sdk.md | 6 +++--- packages/conformance/src/run/boot.test.ts | 4 ++-- packages/conformance/src/validators/envelope.test.ts | 4 ++-- packages/core/src/types/global.ts | 12 +++++++----- packages/core/src/types/global/runtime-api.ts | 12 +++++++----- packages/nap/README.md | 8 ++++---- packages/nap/src/resource/sdk.ts | 8 +++++--- packages/nap/src/resource/shim.test.ts | 8 ++++---- packages/nap/src/resource/shim.ts | 6 +++--- packages/nap/src/resource/types.ts | 4 +++- packages/sdk/README.md | 10 +++++----- packages/sdk/src/config.ts | 6 ++++-- packages/shim/README.md | 8 ++++---- skills/napplet-sdk/SKILL.md | 4 ++-- specs/SHELL-RESOURCE-POLICY.md | 2 +- 17 files changed, 61 insertions(+), 51 deletions(-) diff --git a/apps/docs/naps/index.md b/apps/docs/naps/index.md index 51cc80c3..cb5c8201 100644 --- a/apps/docs/naps/index.md +++ b/apps/docs/naps/index.md @@ -117,20 +117,20 @@ window.napplet.config.openSettings({ section: 'appearance' }); ### resource -Sandboxed byte fetching (`info() -> ResourceInfo`, `bytes(url, options?) -> Blob`, `bytesMany(requests) -> ResourceBytesItem[]`) over https / blossom / nostr / data schemes — the only network-fetch primitive available inside the iframe sandbox. +Sandboxed byte fetching (`info() -> ResourceInfo`, `bytes(url, options?) -> Blob`, `bytesMany(requests) -> ResourceBytesItem[]`) over https / blossom ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) `blossom:.` URIs) / nostr / data schemes — the only network-fetch primitive available inside the iframe sandbox. ```ts const info = await window.napplet.resource.info(); -const blob = await window.napplet.resource.bytes('blossom:sha256:abc123…', { +const blob = await window.napplet.resource.bytes('blossom:.png', { servers: ['https://cdn.hzrd149.com'], }); const items = await window.napplet.resource.bytesMany([ { url: 'https://example.com/avatar.png' }, - { url: 'blossom:sha256:abc123…', servers: ['https://cdn.hzrd149.com'] }, + { url: 'blossom:.png', servers: ['https://cdn.hzrd149.com'] }, ]); // Managed object URL — revoke when done to free memory -const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:sha256:abc123…'); +const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:.png'); imgEl.src = url; imgEl.onload = () => revoke(); ``` diff --git a/apps/docs/packages/nap.md b/apps/docs/packages/nap.md index f7e94a61..60bcf82d 100644 --- a/apps/docs/packages/nap.md +++ b/apps/docs/packages/nap.md @@ -56,7 +56,7 @@ import { notifySend } from '@napplet/nap/notify/sdk'; - **resource** — a single scheme-pluggable byte-fetching primitive, `bytes(url) → Blob`, over four canonical schemes (`data:`, `https:`, - `blossom:sha256:`, `nostr:`). + `blossom:.` per [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), `nostr:`). - **identity** — strictly **read-only**: it exposes the shell-user pubkey and public identity data but never signs, encrypts, or decrypts. Take one snapshot with `getPublicKey()`, then subscribe to shell-pushed `identity.changed`. diff --git a/apps/docs/packages/sdk.md b/apps/docs/packages/sdk.md index a830b3ea..a9f6dcc2 100644 --- a/apps/docs/packages/sdk.md +++ b/apps/docs/packages/sdk.md @@ -68,13 +68,13 @@ const intentResult = await intent.open('napplet:profile/open', { payload: { pubk // Live per-napplet config const configSub = config.subscribe((values) => applyTheme(values.theme)); -// Fetch external bytes through the shell -const avatarBlob = await resource.bytes('blossom:sha256:abc123...', { +// Fetch external bytes through the shell (`blossom:.` URIs follow BUD-10: https://github.com/hzrd149/blossom/blob/master/buds/10.md) +const avatarBlob = await resource.bytes('blossom:.png', { servers: ['https://cdn.hzrd149.com'], }); const avatarItems = await resource.bytesMany([ { url: 'https://example.com/avatar.png' }, - { url: 'blossom:sha256:abc123...', servers: ['https://cdn.hzrd149.com'] }, + { url: 'blossom:.png', servers: ['https://cdn.hzrd149.com'] }, ]); ``` diff --git a/packages/conformance/src/run/boot.test.ts b/packages/conformance/src/run/boot.test.ts index ae8fcdbe..7f860787 100644 --- a/packages/conformance/src/run/boot.test.ts +++ b/packages/conformance/src/run/boot.test.ts @@ -70,14 +70,14 @@ describe('bootAndCollect — DOM wiring', () => { expect(await (await promise).text()).toBe('hi'); const many = napplet.resource.bytesMany([{ - url: 'blossom:sha256:abc', + url: 'blossom:abc.bin', servers: ['https://cdn.example'], }]); expect(posted[1]).toEqual({ type: 'resource.bytesMany', id: expect.any(String), requests: [{ - url: 'blossom:sha256:abc', + url: 'blossom:abc.bin', servers: ['https://cdn.example'], }], }); diff --git a/packages/conformance/src/validators/envelope.test.ts b/packages/conformance/src/validators/envelope.test.ts index 423c7618..82790423 100644 --- a/packages/conformance/src/validators/envelope.test.ts +++ b/packages/conformance/src/validators/envelope.test.ts @@ -166,13 +166,13 @@ describe('validateEnvelope — outbound field checks', () => { expect(validateEnvelope({ type: 'resource.bytes', id: 'single', - url: 'blossom:sha256:abc', + url: 'blossom:abc.bin', servers: ['https://cdn.example'], }).ok).toBe(true); expect(validateEnvelope({ type: 'resource.bytesMany', id: 'a', - requests: [{ url: 'https://x/y' }, { url: 'blossom:sha256:abc', servers: ['https://cdn.example'] }], + requests: [{ url: 'https://x/y' }, { url: 'blossom:abc.bin', servers: ['https://cdn.example'] }], }).ok).toBe(true); expect(validateEnvelope({ type: 'resource.bytesMany', id: 'a' }).errors[0].code).toBe('missing-field'); expect(validateEnvelope({ type: 'resource.bytesMany', id: 'a', requests: 'https://x/y' }).errors[0].code).toBe('wrong-type'); diff --git a/packages/core/src/types/global.ts b/packages/core/src/types/global.ts index 4293ae73..1c10432a 100644 --- a/packages/core/src/types/global.ts +++ b/packages/core/src/types/global.ts @@ -201,26 +201,28 @@ export interface NappletGlobal { * * URL space is scheme-pluggable: shells register handlers per scheme. * Canonical schemes include `data:` (decoded in-shim, no round-trip), - * `https:` (shell-side network with policy), `blossom:` (Blossom hash to - * bytes), `htree:` (Hashtree-verified bytes), and `nostr:` (NIP-19 + * `https:` (shell-side network with policy), `blossom:.` + * (BUD-10 Blossom URI to bytes), `htree:` (Hashtree-verified bytes), and `nostr:` (NIP-19 * single-hop resolution). * + * @see https://github.com/hzrd149/blossom/blob/master/buds/10.md BUD-10 Blossom URI schema + * * @example * ```ts * // Fetch raw bytes with advisory Blossom server locations: - * const blob = await window.napplet.resource.bytes('blossom:sha256:abc123...', { + * const blob = await window.napplet.resource.bytes('blossom:.png', { * servers: ['https://cdn.hzrd149.com'], * }); * * // Fetch many resources in one envelope: * const items = await window.napplet.resource.bytesMany([ * { url: 'https://example.com/avatar.png' }, - * { url: 'blossom:sha256:abc123...', servers: ['https://cdn.hzrd149.com'] }, + * { url: 'blossom:.png', servers: ['https://cdn.hzrd149.com'] }, * { url: 'htree://example-root/path' }, * ]); * * // Get a managed object URL (revoke when done to free memory): - * const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:abc123...'); + * const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:.png'); * imgEl.src = url; * imgEl.onload = () => revoke(); * ``` diff --git a/packages/core/src/types/global/runtime-api.ts b/packages/core/src/types/global/runtime-api.ts index 855adc6f..27f38cc5 100644 --- a/packages/core/src/types/global/runtime-api.ts +++ b/packages/core/src/types/global/runtime-api.ts @@ -252,26 +252,28 @@ export interface ResourceInfo { * * URL space is scheme-pluggable: shells register handlers per scheme. * Canonical schemes include `data:` (decoded in-shim, no round-trip), - * `https:` (shell-side network with policy), `blossom:` (Blossom hash to - * bytes), `htree:` (Hashtree-verified bytes), and `nostr:` (NIP-19 single-hop + * `https:` (shell-side network with policy), `blossom:.` + * (BUD-10 Blossom URI to bytes), `htree:` (Hashtree-verified bytes), and `nostr:` (NIP-19 single-hop * resolution). * + * @see https://github.com/hzrd149/blossom/blob/master/buds/10.md BUD-10 Blossom URI schema + * * @example * ```ts * // Fetch raw bytes: - * const blob = await window.napplet.resource.bytes('blossom:sha256:abc123...', { + * const blob = await window.napplet.resource.bytes('blossom:.png', { * servers: ['https://cdn.hzrd149.com'], * }); * * // Fetch many resources in one envelope: * const items = await window.napplet.resource.bytesMany([ * { url: 'https://example.com/avatar.png' }, - * { url: 'blossom:sha256:abc123...', servers: ['https://cdn.hzrd149.com'] }, + * { url: 'blossom:.png', servers: ['https://cdn.hzrd149.com'] }, * { url: 'htree://example-root/path' }, * ]); * * // Get a managed object URL (revoke when done to free memory): - * const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:abc123...'); + * const { url, revoke } = window.napplet.resource.bytesAsObjectURL('blossom:.png'); * imgEl.src = url; * imgEl.onload = () => revoke(); * ``` diff --git a/packages/nap/README.md b/packages/nap/README.md index 9ffd1e04..492d8d37 100644 --- a/packages/nap/README.md +++ b/packages/nap/README.md @@ -169,18 +169,18 @@ import { info, bytes, bytesMany, bytesAsObjectURL } from '@napplet/nap/resource/ const resourceInfo = await info(); // Fetch any URL the shell accepts under its policy. URL space is scheme-pluggable. -const blob: Blob = await bytes('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', { +const blob: Blob = await bytes('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin', { servers: ['https://cdn.hzrd149.com'], }); // Fetch many URLs in one envelope. Items preserve input order and length. const items = await bytesMany([ { url: 'https://example.com/avatar.png' }, - { url: 'blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', servers: ['https://cdn.hzrd149.com'] }, + { url: 'blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin', servers: ['https://cdn.hzrd149.com'] }, ]); // Synchronous handle for use; revoke when done. -const handle = bytesAsObjectURL('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); +const handle = bytesAsObjectURL('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin'); imgEl.src = handle.url; // later handle.revoke(); @@ -190,7 +190,7 @@ Canonical schemes are defined in the spec: - `data:` — RFC 2397, decoded inside the napplet shim with zero shell round-trip - `https:` — shell-side network fetch under the default resource policy (private-IP block list at DNS time, MIME byte-sniffing, size cap, timeout, rate limit, redirect cap) -- `blossom:sha256:` — Blossom hash → bytes; shell verifies hash before delivery +- `blossom:.` — [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) Blossom URI → bytes; shell verifies hash before delivery - `htree:` — Hashtree reference; shell verifies every Hashtree hash before delivery - `nostr:` — single-hop NIP-19 resolution against the shell's relay pool diff --git a/packages/nap/src/resource/sdk.ts b/packages/nap/src/resource/sdk.ts index b1869ccb..145b2433 100644 --- a/packages/nap/src/resource/sdk.ts +++ b/packages/nap/src/resource/sdk.ts @@ -41,11 +41,13 @@ export const info = resourceInfo; * `data:`, `https:`, `blossom:`, `htree:`, `nostr:`, ...) * @returns Promise resolving to the fetched bytes as a Blob * + * @see https://github.com/hzrd149/blossom/blob/master/buds/10.md BUD-10 Blossom URI schema for `blossom:` URLs + * * @example * ```ts * import { resourceBytes } from '@napplet/nap/resource'; * - * const blob = await resourceBytes('blossom:sha256:abc123...', { + * const blob = await resourceBytes('blossom:.png', { * servers: ['https://cdn.hzrd149.com'], * }); * const url = URL.createObjectURL(blob); @@ -74,7 +76,7 @@ export const bytes = resourceBytes; * * const items = await resourceBytesMany([ * { url: 'https://example.com/avatar.png' }, - * { url: 'blossom:sha256:...', servers: ['https://cdn.hzrd149.com'] }, + * { url: 'blossom:.png', servers: ['https://cdn.hzrd149.com'] }, * { url: 'htree://example-root/path' }, * ]); * ``` @@ -102,7 +104,7 @@ export const bytesMany = resourceBytesMany; * ```ts * import { resourceBytesAsObjectURL } from '@napplet/nap/resource'; * - * const handle = resourceBytesAsObjectURL('blossom:abc123...'); + * const handle = resourceBytesAsObjectURL('blossom:.png'); * imgEl.src = handle.url; * imgEl.onload = () => handle.revoke(); * ``` diff --git a/packages/nap/src/resource/shim.test.ts b/packages/nap/src/resource/shim.test.ts index 86548cc6..8c2affae 100644 --- a/packages/nap/src/resource/shim.test.ts +++ b/packages/nap/src/resource/shim.test.ts @@ -117,14 +117,14 @@ describe('@napplet/nap/resource shim', () => { it('posts resource.bytes with advisory Blossom server hints', async () => { const { bytes, handleResourceMessage } = await import('./shim.js'); - const promise = bytes('blossom:sha256:abc123', { + const promise = bytes('blossom:abc123.bin', { servers: ['https://cdn.example', 'https://mirror.example'], }); const sent = lastPosted('resource.bytes'); expect(sent).toEqual({ type: 'resource.bytes', id: 'resource-test-1', - url: 'blossom:sha256:abc123', + url: 'blossom:abc123.bin', servers: ['https://cdn.example', 'https://mirror.example'], }); @@ -142,7 +142,7 @@ describe('@napplet/nap/resource shim', () => { const { bytesMany, handleResourceMessage } = await import('./shim.js'); const requests = [ - { url: 'blossom:sha256:abc123', servers: ['https://cdn.example'] }, + { url: 'blossom:abc123.bin', servers: ['https://cdn.example'] }, { url: 'https://example.com/missing.png' }, ]; const promise = bytesMany(requests); @@ -155,7 +155,7 @@ describe('@napplet/nap/resource shim', () => { const okBlob = new Blob(['a'], { type: 'image/png' }); const items: ResourceBytesItem[] = [ - { url: 'blossom:sha256:abc123', ok: true, blob: okBlob, mime: 'image/png' }, + { url: 'blossom:abc123.bin', ok: true, blob: okBlob, mime: 'image/png' }, { url: 'https://example.com/missing.png', ok: false, diff --git a/packages/nap/src/resource/shim.ts b/packages/nap/src/resource/shim.ts index 67ad638d..8403409f 100644 --- a/packages/nap/src/resource/shim.ts +++ b/packages/nap/src/resource/shim.ts @@ -53,7 +53,7 @@ export function info(): Promise { * * // With cancellation: * const ac = new AbortController(); - * const promise = bytes('blossom:abc...', { signal: ac.signal }); + * const promise = bytes('blossom:.bin', { signal: ac.signal }); * ac.abort(); // -> rejects with AbortError, sends resource.cancel envelope * ``` */ @@ -150,7 +150,7 @@ export function bytesMany( * * @example * ```ts - * const handle = bytesAsObjectURL('blossom:abc123...'); + * const handle = bytesAsObjectURL('blossom:.png'); * await (handle as { ready: Promise }).ready; * imgEl.src = handle.url; * imgEl.onload = () => handle.revoke(); @@ -203,7 +203,7 @@ export function bytesAsObjectURL(url: string): { url: string; revoke: () => void * ```ts * hydrateResourceCache([ * { url: 'https://example.com/a.png', blob: aBlob, mime: 'image/png' }, - * { url: 'blossom:def456', blob: bBlob, mime: 'image/jpeg' }, + * { url: 'blossom:def456.jpg', blob: bBlob, mime: 'image/jpeg' }, * ]); * // Subsequent bytes('https://example.com/a.png') resolves from cache. * ``` diff --git a/packages/nap/src/resource/types.ts b/packages/nap/src/resource/types.ts index af25fb33..d7c5914f 100644 --- a/packages/nap/src/resource/types.ts +++ b/packages/nap/src/resource/types.ts @@ -86,12 +86,14 @@ export interface ResourceInfoErrorMessage extends ResourceMessage { * NOTE: AbortSignal is napplet-side only and never crosses the wire. * In-flight cancellation flows via the separate `ResourceCancelMessage`. * + * @see https://github.com/hzrd149/blossom/blob/master/buds/10.md BUD-10 Blossom URI schema for `blossom:` URLs + * * @example * ```ts * const msg: ResourceBytesMessage = { * type: 'resource.bytes', * id: crypto.randomUUID(), - * url: 'blossom:sha256:abc123...', + * url: 'blossom:.png', * servers: ['https://cdn.hzrd149.com'], * }; * ``` diff --git a/packages/sdk/README.md b/packages/sdk/README.md index b55d020e..ab851bb1 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -95,14 +95,14 @@ const configSub = config.subscribe((v) => { config.openSettings({ section: 'appearance' }); // Fetch external bytes through the runtime's NAP-RESOURCE surface -const avatarBlob = await resource.bytes('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', { +const avatarBlob = await resource.bytes('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin', { servers: ['https://cdn.hzrd149.com'], }); const resourceItems = await resource.bytesMany([ { url: 'https://example.com/avatar.png' }, - { url: 'blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', servers: ['https://cdn.hzrd149.com'] }, + { url: 'blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin', servers: ['https://cdn.hzrd149.com'] }, ]); -const handle = resource.bytesAsObjectURL('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); +const handle = resource.bytesAsObjectURL('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin'); imgEl.src = handle.url; // handle.revoke() when done @@ -295,7 +295,7 @@ Sandboxed byte fetching (NAP-RESOURCE). Mirrors `window.napplet.resource`; loadi | `bytesMany(requests, opts?)` | `Promise` | Fetch many per-resource requests through one envelope. Each request may carry advisory Blossom `servers`; items preserve input order and length. | | `bytesAsObjectURL(url)` | `{ url: string; revoke: () => void }` | Synchronous handle whose `url` resolves to a blob URL once the fetch completes. | -Canonical schemes: `data:` (in-shim), `https:` (shell-side under policy), `blossom:sha256:` (hash-verified), `htree:` (Hashtree-verified), `nostr:` (single-hop NIP-19). +Canonical schemes: `data:` (in-shim), `https:` (shell-side under policy), `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), hash-verified), `htree:` (Hashtree-verified), `nostr:` (single-hop NIP-19). Bare helper aliases are also re-exported for consumers that prefer functional imports: @@ -305,7 +305,7 @@ import { resourceInfo, resourceBytes, resourceBytesMany, resourceBytesAsObjectUR const info = await resourceInfo(); const blob = await resourceBytes('https://example.com/avatar.png'); const items = await resourceBytesMany(['https://example.com/a.png']); -const handle = resourceBytesAsObjectURL('blossom:sha256:...'); +const handle = resourceBytesAsObjectURL('blossom:.png'); ``` ### `keys` diff --git a/packages/sdk/src/config.ts b/packages/sdk/src/config.ts index 77e1e9ce..cd0b8653 100644 --- a/packages/sdk/src/config.ts +++ b/packages/sdk/src/config.ts @@ -102,18 +102,20 @@ export const config: SdkDomain<'config'> = { * shell fetches and returns a Blob. URL space is scheme-pluggable * (`data:`, `https:`, `blossom:`, `htree:`, `nostr:`). * + * @see https://github.com/hzrd149/blossom/blob/master/buds/10.md BUD-10 Blossom URI schema for `blossom:` URLs + * * @example * ```ts * import { resource } from '@napplet/sdk'; * - * const blob = await resource.bytes('blossom:sha256:abc123...', { + * const blob = await resource.bytes('blossom:.png', { * servers: ['https://cdn.hzrd149.com'], * }); * const items = await resource.bytesMany([ * { url: 'https://example.com/a.png' }, * { url: 'htree://example-root/path' }, * ]); - * const handle = resource.bytesAsObjectURL('blossom:abc123...'); + * const handle = resource.bytesAsObjectURL('blossom:.png'); * imgEl.src = handle.url; * imgEl.onload = () => handle.revoke(); * ``` diff --git a/packages/shim/README.md b/packages/shim/README.md index 7a33f562..b053ccea 100644 --- a/packages/shim/README.md +++ b/packages/shim/README.md @@ -137,14 +137,14 @@ const configSub = window.napplet.config.subscribe((values) => { window.napplet.config.openSettings({ section: 'appearance' }); // Fetch external bytes through the runtime's NAP-RESOURCE surface -const avatarBlob = await window.napplet.resource.bytes('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', { +const avatarBlob = await window.napplet.resource.bytes('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin', { servers: ['https://cdn.hzrd149.com'], }); const resourceItems = await window.napplet.resource.bytesMany([ { url: 'https://example.com/avatar.png' }, - { url: 'blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', servers: ['https://cdn.hzrd149.com'] }, + { url: 'blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin', servers: ['https://cdn.hzrd149.com'] }, ]); -const handle = window.napplet.resource.bytesAsObjectURL('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); +const handle = window.napplet.resource.bytesAsObjectURL('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin'); imgEl.src = handle.url; // later: handle.revoke(); @@ -565,7 +565,7 @@ Sandboxed byte fetching through the runtime-provided NAP-RESOURCE surface. Use ` | `bytesMany(requests, opts?)` | `Promise` | Fetch many per-resource requests through one envelope. Each request may carry advisory Blossom `servers`; items preserve input order and length. | | `bytesAsObjectURL(url)` | `{ url: string; revoke: () => void }` | Synchronous handle whose `url` resolves to a blob URL once the underlying fetch completes. Caller MUST `revoke()` when done. | -Canonical schemes: `data:` (decoded in-shim), `https:` (shell-side network with policy), `blossom:sha256:` (hash-verified), `htree:` (Hashtree-verified), `nostr:` (single-hop NIP-19 resolution). +Canonical schemes: `data:` (decoded in-shim), `https:` (shell-side network with policy), `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), hash-verified), `htree:` (Hashtree-verified), `nostr:` (single-hop NIP-19 resolution). Errors use the NAP-RESOURCE vocabulary: `invalid-request`, `not-found`, `blocked-by-policy`, `timeout`, `too-large`, `unsupported-scheme`, `decode-failed`, `network-error`, or `quota-exceeded`. diff --git a/skills/napplet-sdk/SKILL.md b/skills/napplet-sdk/SKILL.md index e6f8f2a4..a7c133b9 100644 --- a/skills/napplet-sdk/SKILL.md +++ b/skills/napplet-sdk/SKILL.md @@ -144,12 +144,12 @@ import { resource } from '@napplet/sdk'; const blob = await resource.bytes('https://example.com/avatar.png'); img.src = URL.createObjectURL(blob); // revokeObjectURL when done -const handle = resource.bytesAsObjectURL('blossom:sha256:'); +const handle = resource.bytesAsObjectURL('blossom:.'); img.src = handle.url; // resolves once fetched handle.revoke(); ``` -Schemes: `data:` (decoded in-shim), `https:` (shell network under policy), `blossom:sha256:` (hash-verified), `htree:` (Hashtree-verified), `nostr:` (single-hop NIP-19). Cancel with `bytes(url, { signal })`. Rejections carry a `code` — branch on it, not the message: `not-found`, `blocked-by-policy`, `timeout`, `too-large`, `unsupported-scheme`, `decode-failed`, `network-error`, `quota-exceeded`. The shell byte-sniffs the MIME; never trust upstream `Content-Type`. SVG is rasterized before delivery. `resource.bytesMany` batches. +Schemes: `data:` (decoded in-shim), `https:` (shell network under policy), `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), hash-verified), `htree:` (Hashtree-verified), `nostr:` (single-hop NIP-19). Cancel with `bytes(url, { signal })`. Rejections carry a `code` — branch on it, not the message: `not-found`, `blocked-by-policy`, `timeout`, `too-large`, `unsupported-scheme`, `decode-failed`, `network-error`, `quota-exceeded`. The shell byte-sniffs the MIME; never trust upstream `Content-Type`. SVG is rasterized before delivery. `resource.bytesMany` batches. ## Config and theme diff --git a/specs/SHELL-RESOURCE-POLICY.md b/specs/SHELL-RESOURCE-POLICY.md index 02a06e82..5d08d651 100644 --- a/specs/SHELL-RESOURCE-POLICY.md +++ b/specs/SHELL-RESOURCE-POLICY.md @@ -166,7 +166,7 @@ Only the canonical schemes plus shell-administrator opt-ins are dispatched. Smug - [ ] `data:` (RFC 2397) — decoded in-shim with zero shell round-trip; size cap still applies on the decoded `Blob` - [ ] `https:` — full Default Resource Policy applies -- [ ] `blossom:sha256:` — shell verifies hash against the URL's declared digest **before** delivery; mismatch → `code: "decode-failed"` +- [ ] `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md)) — shell verifies hash against the URL's declared digest **before** delivery; mismatch → `code: "decode-failed"` - [ ] `htree:` — shell resolves Hashtree references to verified bytes without forwarding fragment keys to relays, storage servers, or peers - [ ] `nostr:` — single-hop NIP-19 resolution; recursive resolution is **not** the shell's job From ff59db583e55a12fa622021cc0a5e43ab1193a7f Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Wed, 14 Oct 2026 11:29:25 -0500 Subject: [PATCH 2/3] docs(planning): use BUD-10 blossom URIs in archived v0.28.0 notes Co-Authored-By: Claude Opus 5.5 --- .../132-cross-repo-nubs-prs/132-01-PLAN.md | 4 ++-- .../132-cross-repo-nubs-prs/132-01-SUMMARY.md | 4 ++-- .../drafts/NUB-RESOURCE.md | 6 +++--- .../133-01-PLAN.md | 20 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-PLAN.md b/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-PLAN.md index 1d922a6d..ea627414 100644 --- a/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-PLAN.md +++ b/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-PLAN.md @@ -295,7 +295,7 @@ These drafts will be copy-pasted into PRs against napplet/nubs. Treat them as if `### https:` — Shell performs the network fetch. Subject to the full Default Resource Policy below: private-IP block list MUST at DNS-resolution time, MIME byte-sniffing MUST, redirect chain cap SHOULD, response size cap SHOULD, fetch timeout SHOULD, per-napplet rate limit SHOULD. The `mime` returned to the napplet is the shell's byte-sniffed classification — never the upstream `Content-Type`. - `### blossom:` — Blossom hash → bytes resolution. Canonical URL form: `blossom:sha256:` where `` is the lowercase 64-character SHA-256 hex digest. Shells MAY consult any number of Blossom servers per their server-selection policy. The shell MUST verify the returned bytes hash against the URL's declared digest before delivering to the napplet; mismatch MUST result in `code: "decode-failed"`. The `mime` is byte-sniffed by the shell after hash verification. + `### blossom:` — Blossom hash → bytes resolution. Canonical URL form: `blossom:.` as defined by [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), where `` is the lowercase 64-character SHA-256 hex digest and `` is a file extension (`.bin` when unknown), optionally followed by BUD-10 discovery hints (`xs`, `as`, `sz`). Shells MAY consult any number of Blossom servers per their server-selection policy. The shell MUST verify the returned bytes hash against the URL's declared digest before delivering to the napplet; mismatch MUST result in `code: "decode-failed"`. The `mime` is byte-sniffed by the shell after hash verification. `### nostr:` — NIP-19 bech32 input form (e.g., `nostr:nprofile1...`, `nostr:naddr1...`). **Single-hop resolution semantics:** the shell resolves the bech32 to its referenced Nostr resource (profile metadata, addressable event content) and returns those bytes as a Blob with `mime: "application/json"` (or another shell-classified MIME if the resource is byte-typed). Shells MUST NOT recursively follow URLs *within* the resolved bytes — that is the napplet's job via subsequent `resource.bytes()` calls. Single-hop semantics prevent shell-side fan-out attacks. @@ -378,7 +378,7 @@ These drafts will be copy-pasted into PRs against napplet/nubs. Treat them as if | Byte-sniff MIME | Never honor upstream `Content-Type`. | | Rasterize SVG in a sandboxed Worker with no network | Per the SVG Handling section. | | Single-Blob delivery | Result envelope contains exactly one Blob; no streaming, no chunked, no range. | - | Verify Blossom hash before delivery | For `blossom:sha256:` URLs, the returned bytes MUST hash to the declared digest. | + | Verify Blossom hash before delivery | For [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) `blossom:.` URLs, the returned bytes MUST hash to the declared digest. | | Single-hop `nostr:` resolution | Do NOT recursively follow URLs within resolved Nostr resources. | | Scope cache per `(dTag, aggregateHash)` | Per NIP-5D identity binding; napplets MUST NOT see another napplet's cached resources. | diff --git a/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-SUMMARY.md b/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-SUMMARY.md index 3924caff..578f4e13 100644 --- a/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-SUMMARY.md +++ b/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/132-01-SUMMARY.md @@ -88,7 +88,7 @@ completed: 2026-04-20 - Scheme Protocol Surfaces (4 subsections — covers SCH-02, SCH-03, SCH-04 plus references SCH-01 done in Phase 126): - `data:` per RFC 2397, in-shim or shell-handled, no network, no SSRF policy - `https:` with full Default Resource Policy applied; explicit ban on `http:` cleartext as default - - `blossom:` with canonical `blossom:sha256:` form, hash verification before delivery, same `https:` policy on upstream Blossom-host fetches + - `blossom:` with canonical [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) `blossom:.` form, hash verification before delivery, same `https:` policy on upstream Blossom-host fetches - `nostr:` with NIP-19 bech32 input and **single-hop resolution semantics** (anti-fan-out) - Default Resource Policy section with grep-anchor MUST language for Pitfall 6 mitigation: - Private-IP block list as MUST at DNS-resolution time covering all 6 categories (10/8, 172.16/12, 192.168/16, 127/8, ::1, 169.254/16, fe80::/10, fc00::/7, 169.254.169.254) @@ -277,7 +277,7 @@ Exit 0 across all 14 workspace packages. DEF-125-01 stays closed. | SPEC-05 | NUB-MEDIA-AMENDMENT.md (entire file) | Documentation Clarification with artwork.url -> resource.bytes() prose | | SPEC-06 | All 4 drafts | grep -c '@napplet/' returns 0 across all 4 files | | SCH-02 | NUB-RESOURCE.md § Scheme Protocol Surfaces / `https:` | Shell-side network fetch with full Default Resource Policy; `http:` not default | -| SCH-03 | NUB-RESOURCE.md § Scheme Protocol Surfaces / `blossom:` | Canonical `blossom:sha256:` form; hash verification before delivery | +| SCH-03 | NUB-RESOURCE.md § Scheme Protocol Surfaces / `blossom:` | Canonical [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) `blossom:.` form; hash verification before delivery | | SCH-04 | NUB-RESOURCE.md § Scheme Protocol Surfaces / `nostr:` | NIP-19 bech32 input; single-hop resolution semantics | | POL-01 | NUB-RESOURCE.md § Default Resource Policy / Private-IP block list (MUST, at DNS-resolution time) | Full 6-category IP block list; DNS-pinning per redirect hop | | POL-02 | NUB-RESOURCE.md § Default Resource Policy / Response size cap (SHOULD, ~10 MiB) | Excess -> `code: "too-large"` | diff --git a/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/drafts/NUB-RESOURCE.md b/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/drafts/NUB-RESOURCE.md index faa92140..b865f041 100644 --- a/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/drafts/NUB-RESOURCE.md +++ b/.planning/milestones/v0.28.0-phases/132-cross-repo-nubs-prs/drafts/NUB-RESOURCE.md @@ -77,7 +77,7 @@ napplet: napplet.resource.bytes('data:image/png;base64,iVBORw0KGgo...') **Successful `blossom:` fetch:** ``` --> { "type": "resource.bytes", "id": "r2", "url": "blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" } +-> { "type": "resource.bytes", "id": "r2", "url": "blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin" } <- { "type": "resource.bytes.result", "id": "r2", "blob": , "mime": "image/webp" } ``` @@ -110,7 +110,7 @@ The shell performs the network fetch. This scheme is subject to the **full Defau ### `blossom:` -Blossom hash → bytes resolution. Canonical URL form: `blossom:sha256:` where `` is the lowercase 64-character SHA-256 hex digest of the resource bytes. Shells MAY consult any number of Blossom servers per their server-selection policy (typically a user-configured list of preferred Blossom hosts). The shell MUST verify the returned bytes hash against the URL's declared digest **before** delivering them to the napplet; mismatch MUST result in `error: "decode-failed"`. The `mime` field is byte-sniffed by the shell after hash verification, not before. +Blossom hash → bytes resolution. Canonical URL form: `blossom:.` as defined by [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), where `` is the lowercase 64-character SHA-256 hex digest of the resource bytes and `` is a file extension (`.bin` when unknown), optionally followed by BUD-10 discovery hints (`xs`, `as`, `sz`). Shells MAY consult any number of Blossom servers per their server-selection policy (typically a user-configured list of preferred Blossom hosts). The shell MUST verify the returned bytes hash against the URL's declared digest **before** delivering them to the napplet; mismatch MUST result in `error: "decode-failed"`. The `mime` field is byte-sniffed by the shell after hash verification, not before. When fetching from upstream Blossom hosts, shells MUST apply the same `https:` policy (private-IP block list at DNS-resolution time, etc.) to each candidate host. Blossom-host selection MUST NOT bypass DNS-pinning. @@ -221,7 +221,7 @@ The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are t | Byte-sniff MIME | Never honor upstream `Content-Type` for the value delivered to the napplet. Sniffed MIME drives scheme-appropriate allowlist enforcement. | | Rasterize SVG in a sandboxed Worker with no network | Per the SVG Handling section. `image/svg+xml` MUST NOT be delivered to napplets. | | Single-Blob delivery | Result envelope contains exactly one `Blob`; no streaming, no chunked, no range, no progress. | -| Verify Blossom hash before delivery | For `blossom:sha256:` URLs, the returned bytes MUST hash to the declared digest. Mismatch MUST result in `error: "decode-failed"`. | +| Verify Blossom hash before delivery | For [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) `blossom:.` URLs, the returned bytes MUST hash to the declared digest. Mismatch MUST result in `error: "decode-failed"`. | | Single-hop `nostr:` resolution | Do NOT recursively follow URLs found within resolved Nostr resources. | | Scope cache per `(dTag, aggregateHash)` | Per NIP-5D identity binding. Napplets MUST NOT see another napplet's cached resources. | | Drop late envelopes after cancel | After receiving `resource.cancel` for an `id`, shells SHOULD NOT emit a terminal envelope for that `id`; if one is emitted (race), conformant napplet shims MUST silently drop it. | diff --git a/.planning/milestones/v0.28.0-phases/133-documentation-demo-coordination/133-01-PLAN.md b/.planning/milestones/v0.28.0-phases/133-documentation-demo-coordination/133-01-PLAN.md index ef4824c5..b353f4fc 100644 --- a/.planning/milestones/v0.28.0-phases/133-documentation-demo-coordination/133-01-PLAN.md +++ b/.planning/milestones/v0.28.0-phases/133-documentation-demo-coordination/133-01-PLAN.md @@ -277,7 +277,7 @@ e. Add a new section after the Theme Exception section, before Migration, titled const blob: Blob = await bytes('https://example.com/avatar.png'); // Synchronous handle for use; revoke when done. - const handle = bytesAsObjectURL('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); + const handle = bytesAsObjectURL('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin'); imgEl.src = handle.url; // later handle.revoke(); @@ -286,7 +286,7 @@ e. Add a new section after the Theme Exception section, before Migration, titled Four canonical schemes are defined in the spec: - `data:` — RFC 2397, decoded inside the napplet shim with zero shell round-trip - `https:` — shell-side network fetch under the default resource policy (private-IP block list at DNS time, MIME byte-sniffing, size cap, timeout, rate limit, redirect cap) - - `blossom:sha256:` — Blossom hash → bytes; shell verifies hash before delivery + - `blossom:.` — [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) Blossom URI → bytes; shell verifies hash before delivery - `nostr:` — single-hop NIP-19 resolution against the shell's relay pool Errors arrive as one of 8 typed codes: `not-found`, `blocked-by-policy`, `timeout`, @@ -310,7 +310,7 @@ b. In the Quick Start code block, ADD a new pair of lines before `// Clean up`: ```ts // Fetch external bytes via the shell (CSP blocks direct / fetch()) const avatarBlob = await window.napplet.resource.bytes('https://example.com/avatar.png'); - const handle = window.napplet.resource.bytesAsObjectURL('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); + const handle = window.napplet.resource.bytesAsObjectURL('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin'); imgEl.src = handle.url; // later: handle.revoke(); ``` @@ -346,7 +346,7 @@ f. Add a new subsection after `### window.napplet.config` and before `### window | `bytes(url, opts?)` | `Promise` | Fetch bytes for a URL via the shell. `opts.signal` accepts an `AbortSignal` for cancellation. | | `bytesAsObjectURL(url)` | `{ url: string; revoke: () => void }` | Synchronous handle whose `url` resolves to a blob URL once the underlying fetch completes. Caller MUST `revoke()` when done. | - Four canonical schemes: `data:` (decoded in-shim), `https:` (shell-side network with policy), `blossom:sha256:` (hash-verified), `nostr:` (single-hop NIP-19 resolution). + Four canonical schemes: `data:` (decoded in-shim), `https:` (shell-side network with policy), `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), hash-verified), `nostr:` (single-hop NIP-19 resolution). Errors reject the Promise with one of 8 codes: `not-found`, `blocked-by-policy`, `timeout`, `too-large`, `unsupported-scheme`, `decode-failed`, `network-error`, `quota-exceeded`. @@ -371,7 +371,7 @@ b. ADD a usage block in Quick Start after the `// Deep-link settings UI` block, ```ts // Fetch external bytes via the shell (the iframe sandbox + strict CSP block direct fetch) const avatarBlob = await resource.bytes('https://example.com/avatar.png'); - const handle = resource.bytesAsObjectURL('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); + const handle = resource.bytesAsObjectURL('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin'); imgEl.src = handle.url; // handle.revoke() when done ``` @@ -388,7 +388,7 @@ c. ADD a new section after `### config` and before `### keys` titled `### resour | `bytes(url, opts?)` | `Promise` | Fetch bytes through the shell. `opts.signal` accepts an `AbortSignal`. | | `bytesAsObjectURL(url)` | `{ url: string; revoke: () => void }` | Synchronous handle whose `url` resolves to a blob URL once the fetch completes. | - Four canonical schemes: `data:` (in-shim), `https:` (shell-side under policy), `blossom:sha256:` (hash-verified), `nostr:` (single-hop NIP-19). + Four canonical schemes: `data:` (in-shim), `https:` (shell-side under policy), `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), hash-verified), `nostr:` (single-hop NIP-19). ``` d. In the NUB Domain Constants section, update the import line to include `RESOURCE_DOMAIN`: @@ -500,7 +500,7 @@ a. Add a new section after the existing `## Architecture` block and before `## O v0.28.0 converts napplet iframe security from ambient trust ("napplets shouldn't fetch directly") to browser-enforced isolation ("napplets cannot fetch directly — the browser blocks it"): - - **New NUB:** `@napplet/nub/resource` ships a single scheme-pluggable primitive — `napplet.resource.bytes(url) → Blob`. URL space is scheme-pluggable: `https:` (shell-side network under policy), `blossom:sha256:` (hash-verified), `nostr:` (single-hop NIP-19), `data:` (RFC 2397, decoded in-shim). + - **New NUB:** `@napplet/nub/resource` ships a single scheme-pluggable primitive — `napplet.resource.bytes(url) → Blob`. URL space is scheme-pluggable: `https:` (shell-side network under policy), `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md), hash-verified), `nostr:` (single-hop NIP-19), `data:` (RFC 2397, decoded in-shim). - **Strict CSP:** `@napplet/vite-plugin` ships a `strictCsp` option that emits a 10-directive baseline (`default-src 'none'` + `connect-src 'none'` in prod) as the first child of ``. The browser blocks every `fetch()`, ``, `XMLHttpRequest`, and direct WebSocket; everything network-sourced flows through the resource NUB. - **Optional sidecar:** Shells MAY pre-resolve resources referenced by `relay.event` envelopes via an opt-in (default-OFF) `resources?` field; napplet-side `resource.bytes(sidecarUrl)` calls then resolve from cache without a postMessage round-trip. - **SVG rasterization:** Shells MUST rasterize SVG to PNG/WebP in a sandboxed Worker with no network — napplets never receive parseable XML. @@ -606,7 +606,7 @@ imgEl.src = objectUrl; // remember to URL.revokeObjectURL(objectUrl) when done // Or use the synchronous handle helper: -const handle = window.napplet.resource.bytesAsObjectURL('blossom:sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'); +const handle = window.napplet.resource.bytesAsObjectURL('blossom:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.bin'); imgEl.src = handle.url; // populated once the underlying fetch resolves // later handle.revoke(); @@ -618,7 +618,7 @@ Four canonical schemes are supported: |--------|---------|------------| | `data:` | `data:image/png;base64,iVBORw0KGgo...` | Decoded in the napplet shim — zero shell round-trip | | `https:` | `https://example.com/avatar.png` | Shell-side network fetch under policy (private-IP block at DNS time, MIME byte-sniffing, size cap, timeout, rate limit, redirect cap) | -| `blossom:sha256:` | `blossom:sha256:e3b0c44...` | Blossom hash → bytes; shell verifies hash before delivery | +| `blossom:.` | `blossom:e3b0c44…b855.bin` | [BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md) Blossom URI → bytes; shell verifies hash before delivery | | `nostr:` | `nostr:nprofile1...` | Single-hop NIP-19 resolution against the shell's relay pool | **Cancellation:** Pass an `AbortSignal` to cancel an in-flight fetch: @@ -858,7 +858,7 @@ Only the canonical schemes plus shell-administrator opt-ins are dispatched. Smug - [ ] `data:` (RFC 2397) — decoded in-shim with zero shell round-trip; size cap still applies on the decoded `Blob` - [ ] `https:` — full Default Resource Policy applies -- [ ] `blossom:sha256:` — shell verifies hash against the URL's declared digest **before** delivery; mismatch → `code: "decode-failed"` +- [ ] `blossom:.` ([BUD-10](https://github.com/hzrd149/blossom/blob/master/buds/10.md)) — shell verifies hash against the URL's declared digest **before** delivery; mismatch → `code: "decode-failed"` - [ ] `nostr:` — single-hop NIP-19 resolution; recursive resolution is **not** the shell's job ### Never enable by default From 1179d7849a58a680b448889491f4d8ced6d06777 Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Wed, 14 Oct 2026 11:29:36 -0500 Subject: [PATCH 3/3] docs(quick-261014-fuc): replace blossom:sha256 URIs with BUD-10 form Co-Authored-By: Claude Opus 5.5 --- .planning/STATE.md | 3 ++- .../261014-fuc-PLAN.md | 10 ++++++++++ .../261014-fuc-SUMMARY.md | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-PLAN.md create mode 100644 .planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-SUMMARY.md diff --git a/.planning/STATE.md b/.planning/STATE.md index 783d0768..d169e1a0 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -32,7 +32,7 @@ See: .planning/PROJECT.md (updated 2026-05-24 after v0.31.0 archive) Phase: 162 (nip-5d-event-schema-migration) — COMPLETE Plan: 3 of 3 Status: Verified; [draft PR #224](https://github.com/napplet/web/pull/224) open, hzrd149 requested -Last activity: 2026-10-13 - Completed quick task 261013-vo3: Add icon design and generation skill +Last activity: 2026-10-14 - Completed quick task 261014-fuc: Replace blossom:sha256 URIs with BUD-10 form See phase 162 VERIFICATION.md for evidence and disclosed compatibility/tooling findings. Earlier phase and quick-task records below are historical. @@ -537,6 +537,7 @@ Full decision log in PROJECT.md Key Decisions table. Recent decisions affecting | Quick ID | Task | Date | Commit | Artifacts | |----------|------|------|--------|-----------| +| 261014-fuc | Replace blossom:sha256 URIs with BUD-10 blossom:. | 2026-10-14 | 48782cd0 | [261014-fuc-replace-blossom-sha256-uris-with-bud-10-](./quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/) | | 261013-vo3 | Add a skill for icon design, generation, refinement, and NIP-5D integration | 2026-10-13 | b5757eec | [261013-vo3-add-a-napplet-skill-for-icon-design-imag](./quick/261013-vo3-add-a-napplet-skill-for-icon-design-imag/) | | 261009-ur6 | Remove commit-title gating from JSR publication | 2026-10-09 | d798a91f | [261009-ur6-remove-commit-title-gating-from-jsr-publ](./quick/261009-ur6-remove-commit-title-gating-from-jsr-publ/) | | 261006-ij3 | Fix verified PR #224 review findings (conformance x-hash panel, deploy/plugin dropped-asset guards, CLI R/O overlap, debug crash) | 2026-10-06 | a4f1c8b4 | [261006-ij3-fix-verified-pr-224-review-findings-conf](./quick/261006-ij3-fix-verified-pr-224-review-findings-conf/) | diff --git a/.planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-PLAN.md b/.planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-PLAN.md new file mode 100644 index 00000000..6d4321ea --- /dev/null +++ b/.planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-PLAN.md @@ -0,0 +1,10 @@ +--- +quick_id: 261014-fuc +type: quick +--- + +# Replace blossom:sha256 URIs with BUD-10 form + +1. Replace every `blossom:sha256:` occurrence (packages, docs, specs, skills, tests, archived planning) with the BUD-10 `blossom:.` form (`.bin` when the extension is unknown). +2. Link BUD-10 (https://github.com/hzrd149/blossom/blob/master/buds/10.md) from scheme descriptions and add `@see` tags to resource JSDoc. +3. Verify with type-check, unit tests, build; flag that canonical NAP-RESOURCE (napplet/naps#80) still names `blossom:sha256:`. diff --git a/.planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-SUMMARY.md b/.planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-SUMMARY.md new file mode 100644 index 00000000..54221a05 --- /dev/null +++ b/.planning/quick/261014-fuc-replace-blossom-sha256-uris-with-bud-10-/261014-fuc-SUMMARY.md @@ -0,0 +1,11 @@ +--- +quick_id: 261014-fuc +status: complete +--- + +# Summary + +- Rewrote all `blossom:sha256:` references across 21 files to BUD-10 `blossom:.` URIs, with BUD-10 links in scheme lists and `@see` tags in resource JSDoc. +- Commits: 48782cd0 (packages/docs/specs/skills), ff59db58 (archived planning). +- Verification: `pnpm type-check` green, `pnpm -r test:unit` green, non-docs build green; the docs (vitepress) build fails identically on unmodified main (local `vue` symlink env issue). +- Upstream conflict: canonical NAP-RESOURCE at napplet/naps#80 head 2529209 still names `blossom:sha256:` as canonical; it needs a matching revision.