@kehto/paja 0.17.1 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -57,6 +57,15 @@ Any dev server works as long as it answers `Origin: null` with
57
57
  and logs a `paja.target.cors.error` entry in the message log, plus a console
58
58
  warning, when the target would block the sandboxed frame.
59
59
 
60
+ Every drawer section is a flush, independent native accordion: **Pointer** (in
61
+ runtime-pointer mode), **Interfaces**, **ACL**, **Signer**, **Resource servers**,
62
+ then **Messages** last. Headers support Enter/Space, visible keyboard focus and
63
+ state chevrons; bodies keep their padding and live controls when hidden. Sections
64
+ start expanded unless a validated origin-local boolean preference says otherwise.
65
+ The collapsed map uses `kehto:paja:sidebar-sections:v1`, separately from whole-drawer
66
+ visibility and resource server values. Storage failures leave session-only toggles
67
+ usable; collapsing never reloads the running app or discards unsaved drafts.
68
+
60
69
  The console shows supported interfaces with per-domain injection toggles,
61
70
  runtime ACL controls, signer controls, and a filterable message log with visible
62
71
  error details. It starts expanded and collapses to the left with the chevron
@@ -193,6 +202,34 @@ and hinted-author/publisher server lists first, then the active shell user's
193
202
  published list, the current window's verified pointer-manifest servers, and
194
203
  finally configured runtime fallbacks.
195
204
 
205
+ ## Local `index.html` files
206
+
207
+ In runtime-pointer mode, Paja can also open a napplet straight from disk. Use
208
+ **Open file…** next to **Load**, or drop an `index.html` anywhere on the Paja
209
+ page (not onto a running napplet frame, which receives its own drag events).
210
+ The file opens in a new runtime tab named after the file.
211
+
212
+ - **Identity comes from the bytes.** Paja hashes the exact file bytes (`sha256`)
213
+ and derives `aggregateHash` as the NIP-5A aggregate over the single
214
+ `/index.html` path entry. That is the same derivation the resolver checks for a
215
+ published single-file napplet, so editing the file gives it a new identity. The
216
+ `dTag` comes from the NIP-5D publishing metadata
217
+ `<meta name="napplet-id" content="…">`. Without it, Paja uses
218
+ `local-<file-stem>`.
219
+ - **Same loading path as verified pointers.** Paja registers the identity before
220
+ the frame runs. It injects the Class-1 CSP (with `connect-src 'none'`, since a
221
+ local file has no relay or Blossom hints) and then the runtime-owned
222
+ `window.napplet` prelude, including mandatory `shell`. The bytes go in through
223
+ `srcdoc` under the same `allow-scripts` sandbox.
224
+ - **Development only and unverified.** A local file has no signed manifest. It
225
+ never enters the installed napplet catalog, never becomes an intent delivery
226
+ target, has no share link, and is not restored after a page reload.
227
+ - **Self-contained single-file HTML only.** A `srcdoc` document has no base URL,
228
+ and the CSP denies network loads, so relative `<script src>`, stylesheets, and
229
+ images do not load. Paja lists relative references it finds in the status line
230
+ and the message log. Build with an inlining bundler (for example
231
+ `vite-plugin-singlefile`). Opening a folder or zip of assets is not supported.
232
+
196
233
  ## Installed intent handlers and delivery
197
234
 
198
235
  Paja keeps resolver-verified pointer and manifest facts in an installed catalog,
@@ -201,7 +238,10 @@ the catalog record; an explicit artifact removal removes it. Closing, reloading,
201
238
  or replacing a frame never makes an installed handler unavailable, so a cold
202
239
  target can still be selected and started later.
203
240
 
204
- Intent selection considers only exact compatible contracts from that catalog.
241
+ Intent selection considers only exact compatible contracts from that catalog. Required or optional INC declarations are eligible only when
242
+ INC is available in the target's host-resolved environment. Missing optional INC
243
+ still permits frame loading. Nameless root/snapshot artifacts may also run, but
244
+ are excluded from the dTag-keyed intent catalog.
205
245
  Paja can use a compatible user default, ask its host chooser when more than one
206
246
  candidate is available, or reject an ambiguity. An explicit handler d-tag is
207
247
  accepted only when it is an installed compatible handler and the invoking sender
@@ -211,7 +251,7 @@ arbitrary running frame.
211
251
  When Paja receives an invocation, it selects and opens or reuses a verified
212
252
  target. The controller waits for the target generation's
213
253
  registered `MessageEvent.source` to establish its real `shell.ready` session;
214
- it checks that generation is still current, sends one target-only `inc.event`
254
+ it checks that generation is still current, sends one target-only `intent.deliver`
215
255
  with the selected queryless convention, and returns the final handled target
216
256
  identity. A superseded target/source, failed open/readiness, or terminal send is
217
257
  handled by the controller's replacement/retry/terminal policy and produces a
@@ -318,6 +358,25 @@ pubkey without `signEvent` is read-only. This implements the draft
318
358
 
319
359
  ## NAP-RESOURCE schemes
320
360
 
361
+ The development sidebar's **Resource servers** textarea adds read-only Blossom
362
+ lookup origins in both target-URL and runtime-pointer modes. Enter one public
363
+ HTTPS origin per line (`cdn.example` is shorthand for `https://cdn.example`)
364
+ and press **Save**. Blanks are ignored and equivalent origins are deduplicated
365
+ in first-seen order. A bad line rejects the whole save without changing the
366
+ active or saved list; credentials, paths, queries, fragments, HTTP and obvious
367
+ local/private hosts are rejected. Saving blank clears only these extras.
368
+
369
+ Valid saves apply to subsequent Blossom requests across all running tabs,
370
+ without reloading their frames. The list persists in the host origin's
371
+ `localStorage`; if storage fails, it remains usable **session-only**, and the
372
+ status warns that an older saved list may return after reload. Extras are
373
+ appended after existing request/event/user/window/configured defaults. The
374
+ combined eight-server cap remains: extras may not be reached if earlier
375
+ candidates fill it. This setting does not change upload destinations,
376
+ per-napplet NAP-CONFIG, initial pointer/artifact loading, direct HTTP(S) resource
377
+ URLs, or napplet CSP/network grants. Browser CORS and the DNS limitation below
378
+ still apply.
379
+
321
380
  Paja's developer-runtime policy accepts arbitrary `http:` and `https:` resource
322
381
  URLs so a normal remote image does not look broken merely because its origin was
323
382
  not pre-granted. Paja resolves those URLs with browser `fetch`, omits credentials
@@ -325,33 +384,68 @@ and referrer data, caps responses at 10 MiB, and classifies MIME from returned
325
384
  bytes. Its byte classifier recognizes checksum-valid Game Boy ROM headers as
326
385
  `application/vnd.nintendo.gb-rom`; it never trusts a server-supplied media type.
327
386
  Browser network and CORS rules still apply: an unreadable response is the
328
- canonical `network-error`, while any CORS-readable response is returned as NAP
329
- bytes.
387
+ canonical `network-error`. HTTP(S) and locally decoded `data:` bytes must have a
388
+ recognized type; opaque invalid-UTF-8 or NUL-bearing binary remains rejected.
389
+ Only capped, SHA-256-verified canonical Blossom resources may return unknown
390
+ opaque bytes as `application/octet-stream`. Recognized formats retain their
391
+ sniffed MIME. SVG, HTML, XML and script prefixes remain blocked, even with NUL or
392
+ invalid-UTF-8 suffixes; upstream headers never override classification.
393
+ Byte-identifiable UTF-16 markup is checked by decoding the complete capped
394
+ buffer; identifiable UTF-32 document signatures are conservatively rejected.
395
+ This encoding guard is not a general markup parser.
330
396
 
331
397
  `data:` remains locally decoded. `blossom:` is a separate, content-addressed
332
398
  boundary and is advertised because each request may provide server locations
333
- without a host default. Paja accepts only public-looking HTTPS origin hints,
334
- discards invalid/private literals, and deduplicates them. For a canonical URL
399
+ without a host default. For example, reads accept
400
+ `blossom:<hash>.gbc?xs=cdn.example&as=<pubkey>&sz=32768` and
401
+ `blossom:sha256:<hash>.gbc?xs=cdn.example&sz=32768`.
402
+ Repeated `xs` and `as` are supported without an observed event. Paja accepts only
403
+ public-looking HTTPS origin hints (scheme-less domains use HTTPS), discards
404
+ invalid/private literals, and deduplicates them. Unlike BUD-10's suggested HTTP
405
+ retry, URI hints never enable public HTTP. For a Blossom URL
335
406
  previously returned to the same napplet window by `outbox.getEvent`,
336
407
  `outbox.query`, or `outbox.subscribe`, Paja retains bounded event context
337
- without prefetching bytes. Resolution tries request and
338
- event-local server hints first, then lazily queries hinted authors' and the
408
+ without prefetching bytes. Resolution tries explicit request servers, URI `xs`,
409
+ and event-local server hints first, then lazily queries URI `as`, event-hinted authors' and the
339
410
  event publisher's newest BUD-03 kind `10063` lists through the verified
340
411
  NIP-65-aware OUTBOX router, then queries the active shell user's BUD-03 list
341
412
  through that same router, then uses the current window's verified
342
- pointer-manifest servers, followed by upload-runtime fallbacks. The user-list
413
+ pointer-manifest servers, followed by upload-runtime fallbacks and saved extra
414
+ resource servers. The user-list
343
415
  lookup works independently of upload mode; an upload runtime may reuse the same
344
416
  servers when present. ROM-specific event and publisher locations retain
345
- priority over the user/runtime fallbacks. The combined list is capped at eight
346
- candidates. The only accepted identifier is `blossom:sha256:<hex>`;
347
- Paja refuses redirects, verifies the requested SHA-256, and permits plain-HTTP
417
+ priority over the user/runtime fallbacks. URI length is capped at 8192 characters;
418
+ retained unique `xs`, the complete author lookup list, and combined server
419
+ candidates are each capped at eight. Both prefixes require exactly 64 hex
420
+ characters, with an optional single ASCII-alphanumeric extension preserved in
421
+ `GET /<hash>.<ext>`; no query hints reach the blob server. Extensions and upstream
422
+ headers never establish MIME. Fragments, path injection, malformed escapes/authors,
423
+ and invalid or repeated `sz` fail with `invalid-request` before discovery.
424
+ `sz` must be a positive decimal safe integer; sizes above 10 MiB fail `too-large`.
425
+ Paja checks a numeric Content-Length before reading and always checks actual
426
+ bytes against `sz`; a size or hash mismatch is `decode-failed` unless fallback succeeds.
427
+ Hash-only event context stays window-scoped, without transferring extension/size
428
+ or retaining request hints. Byte caches retain the original URL and identity scope;
429
+ public author lists reuse five-minute cache/single-flight lookups, with incomplete misses retryable.
430
+
431
+ [BUD-10 at `b5bd280`](https://github.com/hzrd149/blossom/blob/b5bd2801d1763aa635fc8fea7a76597e0eb18990/buds/10.md)
432
+ requires lowercase hashes and an extension (default `.bin`) for emitted references.
433
+ Extensionless reads, mixed-case hashes, and the `sha256:` alias are deliberately
434
+ retained local compatibility, not a full BUD-10 conformance claim. Upload URI
435
+ generation is unchanged. Paja refuses redirects, verifies SHA-256 and byte MIME,
436
+ and permits plain-HTTP
348
437
  transport only for configured loopback development defaults. Browser-only Paja
349
438
  cannot pin DNS results, so production runtimes must add the draft's DNS-time
350
439
  private-address checks. The current
351
440
  [NAP-RESOURCE draft at `fa6bcc6`](https://github.com/napplet/naps/blob/fa6bcc6935aa19e7b70ab2a2c721dafca77c78e1/naps/NAP-RESOURCE.md)
352
- assigns fetching and policy to the runtime and defines no wire-level Blossom
353
- server-hint field. Retaining verified manifest servers per window is therefore
354
- Paja host policy, informed by the current
441
+ was rechecked for this MIME policy against PR #80's exact head. The opaque
442
+ fallback preserves byte sniffing, hash verification, complete Blob delivery,
443
+ ordered independent bulk results, and the prohibition on raw SVG delivery.
444
+ This is bounded conformance, not full draft compliance: existing developer HTTP
445
+ policy, DNS enforcement, SVG rejection instead of rasterization, and request
446
+ server-hint differences remain outside this change. The draft defines no
447
+ wire-level Blossom server-hint field. Retaining verified manifest servers per
448
+ window is therefore Paja host policy, informed by the current
355
449
  [NIP-5D draft at `24711d9`](https://github.com/nostr-protocol/nips/blob/24711d9c47bbdd07908bf1d52bf677d9cbc530f0/5D.md),
356
450
  which defines manifest `server` tags. The existing request-server compatibility
357
451
  path comes from the earlier NAP-RESOURCE draft at `9511232`, with the merged package implementation
@@ -364,3 +458,13 @@ Full package docs: [`docs/packages/paja.md`](../../docs/packages/paja.md).
364
458
  Getting started: [`docs/how-tos/paja-getting-started.md`](../../docs/how-tos/paja-getting-started.md).
365
459
  Local authoring how-to: [`docs/how-tos/paja-local-authoring.md`](../../docs/how-tos/paja-local-authoring.md).
366
460
  Generated API module: `docs/api/modules/_kehto_paja.html` (run `pnpm docs:api`).
461
+
462
+ ## NIP-5D event compatibility
463
+
464
+ Current manifests use a direct artifact `x` hash, plain-text `content`, independent
465
+ `z`/`i` routing declarations, and required `R` / optional `O` domains. Kehto also
466
+ accepts legacy aggregate events through an isolated compatibility adapter.
467
+ Existing `aggregateHash` host/cache/ACL fields carry the verified artifact hash
468
+ for current events; legacy identities keep their original aggregate. Both paths
469
+ verify signatures and bytes before runtime injection and `srcdoc` execution.
470
+ For the schema and removal boundary, see [event migration](https://kehto.github.io/web/docs/migrations/NIP-5D-EVENT-SCHEMA.html).