Skip to content

NUB-RESOURCE: Sandboxed resource fetching (https / blossom / nostr / data)#13

Open
dskvr wants to merge 2 commits intomasterfrom
nub-resource
Open

NUB-RESOURCE: Sandboxed resource fetching (https / blossom / nostr / data)#13
dskvr wants to merge 2 commits intomasterfrom
nub-resource

Conversation

@dskvr
Copy link
Copy Markdown
Contributor

@dskvr dskvr commented Apr 21, 2026

Status: Draft
Namespace: `window.napplet.resource`
Discovery: `window.napplet.shell.supports("resource")`

Summary

NUB-RESOURCE defines a single scheme-pluggable primitive for sandboxed napplets to fetch byte-addressable resources through the host shell: `resource.bytes(url) → Blob`. Napplets address resources by URL only — they never see raw `fetch`, never open a socket, and never observe the upstream `Content-Type`. The shell mediates under a documented policy and returns a single `Blob` plus a shell-classified `mime` string.

This NUB is the canonical fetch path that complements the browser-enforced isolation defined by NIP-5D (`sandbox="allow-scripts"`, no `allow-same-origin`): napplets cannot fetch directly because the sandbox blocks it; everything network-sourced flows through the shell over `postMessage`.

Scope

  • 4 canonical URL schemes: `data:` (RFC 2397, in-shim decode), `https:` (shell-side network fetch with policy enforcement), `blossom:` (hash → bytes, canonical `blossom:sha256:`), `nostr:` (NIP-19 bech32, single-hop resolution).
  • Wire protocol: 4 envelope types (`resource.bytes`, `resource.cancel`, `resource.bytes.result`, `resource.bytes.error`) + AbortSignal contract.
  • Default Resource Policy: private-IP block list at DNS-resolution time (full RFC1918 + link-local + loopback + ULA + `169.254.169.254` cloud metadata), MIME byte-sniffing (upstream `Content-Type` never passed through), response size cap, fetch timeout, per-napplet rate limit, redirect chain cap with per-hop re-validation.
  • SVG handling: shell-side rasterization MUST run in a sandboxed Worker with no network; raw `image/svg+xml` bytes MUST NOT be delivered to napplets.
  • Shell Guarantees tables: 8 MUSTs, 7 SHOULDs, 5 MAYs.
  • 8 Error Codes: `unsupported-scheme`, `policy-denied`, `network-error`, `timeout`, `too-large`, `rate-limited`, `cancelled`, `internal`.
  • 6 Security Considerations subsections covering source-identity scope binding, SSRF as primary attack surface (with DNS-rebinding rationale), SVG attack surface, upstream Content-Type passthrough, sidecar pre-resolution privacy, and cleartext bytes over postMessage.

Coexistence

Referenced by (to be amended separately):

  • NUB-RELAY: additive optional `resources?: ResourceSidecarEntry[]` field on `relay.event` for pre-resolution (default OFF; privacy-gated per-event-kind allowlist).
  • NUB-IDENTITY: `ProfileData.picture` / `.banner` URLs flow through `resource.bytes(url)`.
  • NUB-MEDIA: `MediaMetadata.artwork.url` flows through `resource.bytes(url)`.

Implementations

`(none yet)`

Next steps

Follow-up amendment PRs against NUB-RELAY / NUB-IDENTITY / NUB-MEDIA will reference this spec once merged.

dskvr added 2 commits April 21, 2026 13:02
Defines window.napplet.resource.bytes(url) -> Blob — the canonical
fetch path for sandboxed napplets. Four schemes (data:, https:,
blossom:, nostr:), 8-code error vocabulary, Default Resource Policy
with SSRF block list at DNS-resolution time, SVG rasterization in
sandboxed Worker with no network, Shell Guarantees MUST/SHOULD/MAY
tables, and six Security Considerations subsections.

Complements NIP-5D browser-enforced iframe isolation: napplets cannot
fetch directly because the sandbox blocks it; network-sourced bytes
flow through the host shell over postMessage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant