@kehto/shell 0.21.3 → 0.23.0

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
@@ -34,7 +34,7 @@ Current draft behaviors this package enforces:
34
34
  when the runtime can identify it. The shell forwards this optional context
35
35
  without imposing a consent policy; host runtimes may ignore it or use it to
36
36
  resolve their own identity-scoped signer decisions.
37
- - `injectNappletNamespacePrelude()` implements the NIP-5D injected-domain bootstrap and mandatory NAP-SHELL shim: hosts prepend it to `srcdoc` outside verified artifact bytes, install the parent-bound `shell.init` receiver, emit one `shell.ready`, and expose callable NAP interfaces before authored scripts run. Optional namespaces are filtered to the bare-domain allowlist; `shell` is always retained. Published core 0.32.0 and shim 0.30.0 omit generic mandatory shell, so Kehto retains this host-owned prelude under NAP-SHELL `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24` until an upstream correction is reviewed.
37
+ - `prepareNappletSrcdoc()` inserts validated CSP first, then the NIP-5D bootstrap and mandatory NAP-SHELL shim, outside verified artifact bytes. The shim installs the parent-bound `shell.init` receiver, emits one `shell.ready`, and exposes callable NAP interfaces before authored scripts run. Optional namespaces are filtered to the bare-domain allowlist; `shell` is always retained. Published core 0.32.0 and shim 0.30.0 omit generic mandatory shell, so Kehto retains this host-owned prelude under NAP-SHELL `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24` until an upstream correction is reviewed.
38
38
  - The injected resource projection follows draft NAP-RESOURCE `9511232f69313aa7953d110e35d32cc28d506f66`: `bytes(url, { servers? })` carries advisory Blossom locations and `bytesMany(requests)` keeps each `{ url, servers? }` entry intact.
39
39
  - `window.napplet.shell.supports(domain)` answers synchronously and locally from the cached first `shell.init` environment. It returns `false` before `shell.init`, for unknown values, and for domains that are not live and granted to that napplet; it never sends a support-query message.
40
40
  - Five optional per-domain proxies — `createIdentityProxy`, `createThemeProxy`, `createKeysProxy`, `createMediaProxy`, `createNotifyProxy` — can be composed between napplet and runtime to intercept request traffic per NAP. They are NOT wired by default. Identity/theme proxy `emit()` compatibility members fail closed; hosts must deliver automatic changes through `ShellBridge.publishIdentityChanged()` / `publishTheme()`, which enforce live session, granted domain, and current ACL.
@@ -76,24 +76,23 @@ the prior opener-only interpretation is obsolete.
76
76
 
77
77
  ### NAP-INTENT binding and delivery
78
78
 
79
- Kehto implements merged [NAP-INTENT at
80
- `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`](https://github.com/napplet/naps/blob/5ac0490461ca6fec2f0d2e45b4835cf9bc08de24/naps/NAP-INTENT.md).
81
- The protected `window.napplet.intent` binding accepts structured
82
- `invoke(request)` calls and `open(archetype, payload?, opts?)`. It defaults
83
- `action` to `open`, preserves an optional queryless convention, rejects
84
- unsupported fields and caller-supplied sender data, and resolves only
85
- parent-originated correlated results.
86
-
87
- Successful results include `handled`, `handler`, `windowId`, and `convention`
88
- after target dispatch completes. Target delivery now follows NAP-INTENT at
89
- `25b29ee49e5bff8ebfe031f4b76dce98705c8b7e`: one `intent.deliver` envelope
90
- feeds the buffered `onDelivery` binding described below. Broader invocation and
91
- result changes from that newer spec remain deferred.
92
-
93
- Phase 105 completed released `@napplet/*` package adoption and persistent live
94
- Paja/playground catalogs and target controllers. The Phase 104 Paja simulator
95
- and playground catalog builder are historical exact-contract consumers; preserve
96
- archived planning rather than presenting it as active guidance.
79
+ Kehto follows [NAP-INTENT PR #106](https://github.com/napplet/naps/blob/fc121fc264615482143eda86125863d2e1f741a2/naps/NAP-INTENT.md) at `fc121fc264615482143eda86125863d2e1f741a2`. The protected binding supports
80
+ `window.napplet.intent.invoke('napplet:profile/open?pubkey=abc%2B123')` and
81
+ `invoke(uri, { payload, handler, handlerHint, behavior })`. Query fields decode
82
+ once into text; `+` remains literal. An explicit payload cannot accompany an
83
+ inner query, and a `#naddr` recommendation cannot accompany `handlerHint`.
84
+ Unsupported options and caller-supplied sender data are rejected. Only a
85
+ parent-originated correlated result can settle the call.
86
+
87
+ Successful results identify retained delivery responsibility with `ok`,
88
+ `archetype`, `action`, `convention`, and an opaque `handler`. A target receives
89
+ one parent-attested `intent.deliver`, buffered by `onDelivery` until registration.
90
+ Acceptance is separate from later target completion.
91
+
92
+ Legacy object-form `invoke(request)` and `open(archetype, payload?, opts?)`
93
+ remain bounded compatibility adapters and warn once per iframe with
94
+ `KEHTO_COMPAT_INTENT_OBJECT_INVOKE`. Their removal conditions are tracked in
95
+ [compatibility](https://github.com/kehto/web/blob/main/docs/compatibility.md).
97
96
 
98
97
  ## Quick Start
99
98
 
@@ -136,11 +135,52 @@ const bridge = createShellBridge({
136
135
  ```
137
136
 
138
137
  ### Shell init
138
+ - `prepareNappletSrcdoc` — recommended verified-document entry point; applies CSP and namespace together
139
+ - `buildNappletCsp`, `renderNappletCspMeta`, `injectNappletCsp` — shared policy construction and CSP-only composition utilities
139
140
  - `buildShellCapabilities` — construct the immutable domain-only `ShellCapabilities` payload emitted during the `shell.ready` / `shell.init` handshake
140
141
  - `resolveShellEnvironment(hooks, identity)` — host-integrator-only utility that narrows the live environment for a trusted creation-time identity before `shell.init`; it is not installed on `window.napplet` and is not a shim-facing napplet API
141
- - `injectNappletNamespacePrelude` — insert a host-owned NIP-5D `window.napplet` callable-domain prelude into verified HTML before authored scripts
142
+ - `injectNappletNamespacePrelude` — low-level bootstrap-only insertion for development wrappers; does not apply CSP
142
143
  - `renderNappletNamespacePrelude` — render only the bootstrap `<script>` for hosts that already own HTML insertion
143
144
 
145
+ ### Host CSP overrides
146
+
147
+ Resolve and verify the signed manifest, aggregate and artifact bytes before
148
+ calling `prepareNappletSrcdoc`. Keep the verified bytes for identity and caching;
149
+ only the rendered copy receives policy and bootstrap. Bind the iframe window to
150
+ the verified identity before assigning `srcdoc`, with `sandbox="allow-scripts"`.
151
+
152
+ ```ts
153
+ import { prepareNappletSrcdoc } from '@kehto/shell';
154
+
155
+ const srcdoc = prepareNappletSrcdoc(verifiedHtml, {
156
+ domains: ['theme'],
157
+ csp: {
158
+ connectOrigins: ['https://api.example'],
159
+ directives: { 'media-src': ['blob:'], 'img-src': ['data:'] },
160
+ },
161
+ });
162
+ ```
163
+
164
+ Omitting `csp` applies NIP-5D's conservative baseline, including WASM compilation.
165
+ Directive overrides replace individual source lists. They cannot disable CSP
166
+ insertion, allow `'unsafe-eval'`, or suppress the inline namespace bootstrap.
167
+ `connect-src` defaults to the exact `connectOrigins` grants and can be narrowed;
168
+ wildcards, scheme-wide grants, credentials, paths, query strings and fragments
169
+ are rejected. Empty source lists are rejected; use `["'none'"]` to deny a
170
+ directive. Hosts may remove WASM permission by setting
171
+ `'script-src': ["'unsafe-inline'"]`.
172
+
173
+ `script-src` and an explicit `script-src-elem` must permit the inline bootstrap.
174
+ Nonce/hash/`strict-dynamic` combinations that suppress that allowance are rejected;
175
+ nonce-based bootstrap authorization is not supported by this API. Existing
176
+ artifact CSP and inherited response policies still apply cumulatively and may
177
+ block execution; this helper never strips them to make a napplet run.
178
+
179
+ Meta-unsupported directives (`frame-ancestors`, `sandbox`, `report-uri`) and
180
+ unsupported directive names are rejected. Configure embedding restrictions on
181
+ the host HTTP response. See the [CSP enforcement contract](https://github.com/kehto/web/blob/main/docs/policies/NIP-5D-CONFORMANCE.md#shell-csp-enforcement)
182
+ for normative requirements, enforced recommendations and configurable defaults.
183
+
144
184
  ### Domain proxies (NIP-5D composition seams)
145
185
  - `createIdentityProxy` — intercept identity read requests; direct `emit()` is prohibited
146
186
  - `createThemeProxy` — intercept `theme.get`; direct `emit()` is prohibited
@@ -215,8 +255,8 @@ MIT
215
255
 
216
256
  ## NIP-5D event compatibility
217
257
 
218
- Current manifests use a direct artifact `x` hash, plain-text `content`, independent
219
- `z`/`i` routing declarations, and required `R` / optional `O` domains. Kehto also
258
+ Current manifests use a direct artifact `x` hash, plain-text `content`, role-matched
259
+ `z`/`i` intent declarations, and required `R` / optional `O` domains. Kehto also
220
260
  accepts legacy aggregate events through an isolated compatibility adapter.
221
261
  Existing `aggregateHash` host/cache/ACL fields carry the verified artifact hash
222
262
  for current events; legacy identities keep their original aggregate. Both paths
@@ -234,5 +274,12 @@ A throwing listener does not prevent delivery to other listeners.
234
274
  When no canonical listener is registered, matching legacy INC listeners can
235
275
  consume the delivery locally. This emits `KEHTO_COMPAT_INTENT_INC` once per
236
276
  iframe; a delivery is never replayed to both APIs. See [compatibility tracking](https://github.com/kehto/web/blob/main/docs/compatibility.md).
237
- This aligns delivery with NAP-INTENT at `25b29ee`; broader invocation/result
238
- changes and the packaged SDK upgrade remain deferred.
277
+ Delivery and URI invocation follow NAP-INTENT PR #106 at
278
+ `fc121fc264615482143eda86125863d2e1f741a2`; the packaged SDK still trails that
279
+ contract, as documented in the conformance policy.
280
+
281
+ ## Paja URL integration
282
+
283
+ The shell normalizer also powers Paja intent links. See the
284
+ [Paja guide](https://kehto.github.io/web/docs/packages/paja.html#nap-intent-links-and-lifecycle)
285
+ for outer URL encoding, review, routing, and verified launcher behavior.
package/dist/index.d.ts CHANGED
@@ -639,6 +639,114 @@ declare function renderNappletNamespacePrelude(options: NappletNamespacePreludeO
639
639
  */
640
640
  declare function injectNappletNamespacePrelude(html: string, options: NappletNamespacePreludeOptions): string;
641
641
 
642
+ /** Canonical request fields produced from a NAP-INTENT convention URI. */
643
+ interface NormalizedIntentUri {
644
+ archetype: string;
645
+ action: string;
646
+ convention: string;
647
+ payload?: unknown;
648
+ handlerHint?: {
649
+ address: string;
650
+ relays?: string[];
651
+ };
652
+ handler?: string;
653
+ behavior?: {
654
+ focus?: boolean;
655
+ reuse?: boolean;
656
+ };
657
+ }
658
+ /**
659
+ * Normalize a NAP-INTENT convention URI.
660
+ *
661
+ * The host and injected NIP-5D binding use the same normalizer factory.
662
+ *
663
+ * @param uri - Complete NAP-INTENT convention URI.
664
+ * @param options - Optional payload, handler, recommendation, and behavior hints.
665
+ * @returns The canonical request fields for `intent.invoke`.
666
+ * @example
667
+ * ```ts
668
+ * normalizeIntentUri('napplet:profile/open?pubkey=abc%2B123');
669
+ * // { archetype: 'profile', action: 'open', convention: 'napplet:profile/open', payload: { pubkey: 'abc+123' } }
670
+ * ```
671
+ */
672
+ declare const normalizeIntentUri: (uri: unknown, options?: unknown) => NormalizedIntentUri;
673
+
674
+ /** Source-list directives supported by the shell's meta-delivered CSP. */
675
+ type NappletCspDirective = 'default-src' | 'script-src' | 'script-src-elem' | 'script-src-attr' | 'style-src' | 'style-src-elem' | 'style-src-attr' | 'img-src' | 'font-src' | 'connect-src' | 'worker-src' | 'child-src' | 'frame-src' | 'media-src' | 'object-src' | 'manifest-src' | 'base-uri' | 'form-action';
676
+ /** Host replacements for individual CSP source lists; omitted directives retain defaults. */
677
+ type NappletCspDirectives = Readonly<Partial<Record<NappletCspDirective, readonly string[]>>>;
678
+ /** Host policy for a verified napplet's CSP, separate from its signed manifest. */
679
+ interface NappletCspOptions {
680
+ /** Explicit HTTP(S)/WS(S) origins granted direct connections; defaults to none. */
681
+ readonly connectOrigins?: readonly string[];
682
+ /** Source-list replacements. connect-src may only narrow the explicit grants. */
683
+ readonly directives?: NappletCspDirectives;
684
+ }
685
+ /**
686
+ * Build the default NIP-5D CSP with validated host source-list replacements.
687
+ * Meta-unsupported directives must be configured on the host HTTP response.
688
+ *
689
+ * @param options - Explicit connection grants and directive replacements.
690
+ * @returns A serialized policy. Invalid or conflicting overrides throw.
691
+ * @example
692
+ * ```ts
693
+ * const policy = buildNappletCsp({
694
+ * connectOrigins: ['https://api.example'],
695
+ * directives: { 'media-src': ['blob:'] },
696
+ * });
697
+ * ```
698
+ */
699
+ declare function buildNappletCsp(options?: NappletCspOptions): string;
700
+ /**
701
+ * Render the shell's validated CSP meta element.
702
+ *
703
+ * @param options - Explicit connection grants and directive replacements.
704
+ * @returns An attribute-escaped CSP meta element for the first position in head.
705
+ * @example
706
+ * ```ts
707
+ * const meta = renderNappletCspMeta({ directives: { 'img-src': ['data:'] } });
708
+ * ```
709
+ */
710
+ declare function renderNappletCspMeta(options?: NappletCspOptions): string;
711
+ /**
712
+ * Insert CSP before authored content. The caller must verify the artifact first.
713
+ * Existing authored CSP remains in place and can only further restrict execution.
714
+ *
715
+ * @param html - Already-verified artifact HTML; never use the result for its hash.
716
+ * @param options - Explicit connection grants and directive replacements.
717
+ * @returns A srcdoc copy with the CSP first in head.
718
+ * @example
719
+ * ```ts
720
+ * const protectedHtml = injectNappletCsp(verifiedHtml);
721
+ * ```
722
+ */
723
+ declare function injectNappletCsp(html: string, options?: NappletCspOptions): string;
724
+
725
+ /** Shell bootstrap availability and host CSP for a verified napplet document. */
726
+ interface NappletSrcdocOptions extends NappletNamespacePreludeOptions {
727
+ /** Validated host policy changes. Omission uses the NIP-5D baseline. */
728
+ readonly csp?: NappletCspOptions;
729
+ }
730
+ /**
731
+ * Prepare verified artifact HTML with CSP first, then the mandatory namespace.
732
+ * There is no CSP opt-out. Resolve and verify the artifact before calling this;
733
+ * bind its identity to the sandboxed iframe before assigning the returned srcdoc.
734
+ * Keep the original verified bytes for hashing and caching.
735
+ *
736
+ * @param html - Already-verified napplet artifact HTML.
737
+ * @param options - Exposed domains and validated host CSP changes.
738
+ * @returns Srcdoc HTML with policy and bootstrap preceding authored content.
739
+ * @example
740
+ * ```ts
741
+ * iframe.sandbox.value = 'allow-scripts';
742
+ * iframe.srcdoc = prepareNappletSrcdoc(verifiedHtml, {
743
+ * domains: ['shell', 'theme'],
744
+ * csp: { directives: { 'media-src': ['blob:'] } },
745
+ * });
746
+ * ```
747
+ */
748
+ declare function prepareNappletSrcdoc(html: string, options: NappletSrcdocOptions): string;
749
+
642
750
  /**
643
751
  * Manifest cache — persists verified NIP-5A aggregate hashes per napplet identity.
644
752
  */
@@ -1452,4 +1560,4 @@ type ResourceInbound = ResourceInfoRequest | ResourceBytesRequest | ResourceByte
1452
1560
  */
1453
1561
  type ResourceOutbound = ResourceInfoResult | ResourceInfoError | ResourceBytesResult | ResourceBytesError | ResourceBytesManyResult | ResourceBytesManyError;
1454
1562
 
1455
- export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletNamespacePreludeOptions, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, originRegistry, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };
1563
+ export { type AclCheckEvent, type AclEntry, type AclStore, type AudioManager, type AudioSource, type AuthHooks, type BleHooks, type BrowserDeps, type CapabilityHooks, type CommonHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysProxy, type KeysProxyDeps, type LinkHooks, type ListsHooks, type ManifestCache, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NappletCspDirective, type NappletCspDirectives, type NappletCspOptions, type NappletNamespacePreludeOptions, type NappletSrcdocOptions, type NormalizedIntentUri, type NotifyProxy, type NotifyProxyDeps, type OriginIdentity, type OriginRegistry, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesManyError, type ResourceBytesManyItem, type ResourceBytesManyRequest, type ResourceBytesManyResult, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ShellEnvironment, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WebrtcHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildNappletCsp, buildShellCapabilities, createIdentityProxy, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, injectNappletCsp, injectNappletNamespacePrelude, manifestCache, nappKeyRegistry, normalizeIntentUri, originRegistry, prepareNappletSrcdoc, renderNappletCspMeta, renderNappletNamespacePrelude, resolveShellEnvironment, sessionRegistry };