@kehto/shell 0.17.2 → 0.18.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
@@ -12,6 +12,13 @@ Browser adapter over @kehto/runtime — ShellBridge and domain proxies.
12
12
  pnpm add @kehto/shell
13
13
  ```
14
14
 
15
+ ## Published Napplet Compatibility
16
+
17
+ `@kehto/shell` publishes against `@napplet/core` and `@napplet/nap`
18
+ `>=0.29.0 <0.30.0`. The installed core/nap 0.29.0 contracts are from source
19
+ `dd7b3a728eb9c838b7218fcec7bb7bb00e7cc88b` and release
20
+ `60889f1c2476e063500c7ab6624af6abe0dbcbe5`.
21
+
15
22
  ## Overview
16
23
 
17
24
  `@kehto/shell` is the browser-specific integration layer for kehto. It wraps `@kehto/runtime` with the window/postMessage transport, localStorage persistence hooks, an audio manager, and the five canonical per-domain proxies defined by NIP-5D.
@@ -21,11 +28,64 @@ The primary entry point is `createShellBridge()` — it owns the postMessage lis
21
28
  Current draft behaviors this package enforces:
22
29
 
23
30
  - The shell does not inject a host-provided nostr object into napplets — NIP-5D explicitly forbids napplet-visible signing. Napplets call `relay.publish` / `relay.publishEncrypted` and the shell mediates the signing flow internally (NIP-44 default, NIP-04 opt-in for encrypted envelopes).
24
- - `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.
25
- - `window.napplet.shell.supports()` answers synchronously from the cached NAP-SHELL environment. Optional-domain presence remains the NIP-5D binding, while method semantics and numbered protocols remain owned by their matching NAP specs.
26
- - Five optional per-domain proxies — `createIdentityProxy`, `createThemeProxy`, `createKeysProxy`, `createMediaProxy`, `createNotifyProxy` — can be composed between napplet and runtime to intercept or augment traffic per NAP. They are NOT wired by default (Kehto's runtime already owns dispatch for the currently supported domains); they exist as host-app composition seams.
31
+ - `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.29.0 and shim 0.27.0 omit generic mandatory shell, so Kehto retains this host-owned prelude under NAP-SHELL `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24` until an upstream correction is reviewed.
32
+ - `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.
33
+ - 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.
27
34
  - `keys.forward` is napplet-to-shell only. Active napplets suppress locally-bound keys from `keys.bindings` before forwarding; shell-initiated action triggers use `keys.action`.
28
- - `buildShellCapabilities()` advertises `dm` when the host adapter provides `hooks.dm`, and disabled-domain overrides remove it from both `domains` and `naps`.
35
+ - `buildShellCapabilities()` advertises only live domains (including `dm` when the host adapter provides `hooks.dm`); disabled domains are absent from the delivered `domains` and named services snapshots.
36
+
37
+ ### NAP-INC binding and channel contract
38
+
39
+ The injected INC binding follows merged
40
+ [`naps/NAP-INC.md`](https://github.com/napplet/naps/blob/6461e4b37c29dc09a20dff35d9515889c4433874/naps/NAP-INC.md)
41
+ on `napplet/naps` master
42
+ `6461e4b37c29dc09a20dff35d9515889c4433874`. The specification remains marked
43
+ draft, but its merged path is the protocol authority.
44
+
45
+ The released package projection and `window.napplet.inc` binding own
46
+ query-to-text-payload
47
+ transposition: it converts a convention URI query before emitting a stable,
48
+ exact queryless topic identity. Subscriptions reject query-bearing identities.
49
+ The binding never creates a normalized query-bearing wire/discovery identity,
50
+ does not do prefix/wildcard/query-aware matching or service-over-INC prefix
51
+ dispatch, and does not infer payload kinds. Runtime delivery supplies the
52
+ **runtime-attested dTag**; no caller sender is accepted, topic source exclusion
53
+ is runtime-owned, and IDs and payloads are opaque.
54
+
55
+ Merged NAP-INC delivers one `IncEvent` to `on(topic, callback)`. Released
56
+ `@napplet/nap@0.29.0` instead declares and implements a
57
+ `(payload, NostrEvent)` callback. The protected binding follows the NAP;
58
+ package-based consumers must bridge that upstream callback drift.
59
+
60
+ For channels, runtime ACL checks are open-only rather than per-message. The
61
+ target `inc.channel.opened` before the opener result creates an equivalent target
62
+ handle; `channel.onOpened` exposes it, while symmetric handles expose `on` and
63
+ `onClosed`. The binding retains inbound, early, and terminal lifecycle state in
64
+ order, closes on bounded overflow, and treats teardown as deterministic.
65
+ `channel.list()` is informational only. The downstream tracker is
66
+ [`kehto/web#203`](https://github.com/kehto/web/issues/203), with its [upstream
67
+ resolution reply](https://github.com/kehto/web/issues/203#issuecomment-5060904495);
68
+ the prior opener-only interpretation is obsolete.
69
+
70
+ ### NAP-INTENT binding and delivery
71
+
72
+ Phase 104 implements the draft [NAP-INTENT PR #91 at
73
+ `a718915ddefa2f03a0126579601f59d8bd86f7c4`](https://github.com/napplet/naps/pull/91).
74
+ The protected `window.napplet.intent` binding normalizes URI invocation into
75
+ exact `archetype`, `action`, queryless `convention`, and optional payload
76
+ fields. It rejects fragments, malformed/repeated queries, query plus explicit
77
+ payload, mismatched normalized fields, and caller-supplied sender data.
78
+
79
+ Only parent-originated result, change, and delivery envelopes settle the
80
+ binding. Incoming no-ID `intent.deliver` values are retained until an
81
+ `onDelivery` handler is registered. The runtime, not the source, attests the
82
+ sender dTag; an accepted result records retained responsibility and does not
83
+ expose target window, handled, protocol, retry, or lifecycle state.
84
+
85
+ Phase 105 completed released `@napplet/*` package adoption and persistent live
86
+ Paja/playground catalogs and target controllers. The Phase 104 Paja simulator
87
+ and playground catalog builder are historical exact-contract consumers; preserve
88
+ archived planning rather than presenting it as active guidance.
29
89
 
30
90
  ## Quick Start
31
91
 
@@ -41,7 +101,7 @@ const bridge = createShellBridge({
41
101
 
42
102
  // Register a reference service against the underlying runtime.
43
103
  bridge.runtime.registerService(
44
- 'notifications',
104
+ 'notify',
45
105
  createNotificationService({ onChange: updateBadge }),
46
106
  );
47
107
  ```
@@ -68,15 +128,36 @@ const bridge = createShellBridge({
68
128
  ```
69
129
 
70
130
  ### Shell init
71
- - `buildShellCapabilities` — construct the current draft `ShellCapabilities` payload emitted during the `shell.ready` / `shell.init` handshake
131
+ - `buildShellCapabilities` — construct the immutable domain-only `ShellCapabilities` payload emitted during the `shell.ready` / `shell.init` handshake
132
+ - `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
72
133
  - `injectNappletNamespacePrelude` — insert a host-owned NIP-5D `window.napplet` callable-domain prelude into verified HTML before authored scripts
73
134
  - `renderNappletNamespacePrelude` — render only the bootstrap `<script>` for hosts that already own HTML insertion
74
135
 
75
136
  ### Domain proxies (NIP-5D composition seams)
76
- - `createIdentityProxy` — intercept `identity.getProfile/getFollows/...` traffic
77
- - `createThemeProxy` — intercept `theme.get/theme.changed`
137
+ - `createIdentityProxy` — intercept identity read requests; direct `emit()` is prohibited
138
+ - `createThemeProxy` — intercept `theme.get`; direct `emit()` is prohibited
78
139
  - `createKeysProxy` — intercept `keys.bind/unbind/bindings`
79
140
  - `createMediaProxy` — intercept `media.*` playback control
141
+
142
+ ### Protected identity and theme delivery
143
+
144
+ The injected identity/theme bindings follow NAP-IDENTITY and NAP-THEME at
145
+ `napplet/naps` master `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`.
146
+ They are readonly protected objects: identity exposes supported reads and
147
+ `onChanged`, while theme exposes `get` and `onChanged` only. Results and
148
+ automatic changes are accepted only when their `MessageEvent.source` is
149
+ `window.parent`; direct-domain and whole-namespace assignment cannot replace
150
+ the canonical operations.
151
+
152
+ `ShellBridge` delivers `identity.changed` and `theme.changed` exactly once per
153
+ eligible recipient: a live authenticated `shell.ready` session, its frozen
154
+ environment includes the relevant domain, and its current recipient read
155
+ capability is still granted. Identity sign-out is `pubkey: ""`. Theme updates
156
+ arrive only after the service has stored the complete color state. These are
157
+ automatic change messages, not NAP-INC/intent delivery and not a subscription
158
+ protocol. Kehto's denied/unavailable theme read policy is a complete fixed
159
+ normal result without `error`, deliberately avoiding a mixed theme/error
160
+ payload.
80
161
  - `createNotifyProxy` — intercept `notify.send/list/read/dismiss`
81
162
 
82
163
  ### Session / origin registry
@@ -98,7 +179,13 @@ const bridge = createShellBridge({
98
179
  - `TopicKey`, `TopicValue` — typed topic lookup helpers
99
180
 
100
181
  ### Types
101
- Exported for host-app integration: `ShellAdapter`, `ShellCapabilities`, `RelayPoolHooks`, `RelayPoolLike`, `RelayConfigHooks`, `WindowManagerHooks`, `AuthHooks`, `ConfigHooks`, `HotkeyHooks`, `WorkerRelayHooks`, `WorkerRelayLike`, `CryptoHooks`, `DmHooks`, `UploadHooks`, `IntentHooks`, `LinkHooks`, `CommonHooks`, `ListsHooks`, `SerialHooks`, `BleHooks`, `WebrtcHooks`, `SessionEntry`, `NappKeyEntry` (deprecated), `AclEntry`, `AclCheckEvent`, `UnroutedMessageInfo`, `ServiceDescriptor`, `ServiceHandler`, `ServiceRegistry`, `NostrEvent`, `NostrFilter`, `NappletMessage`, `ConsentRequest`, and per-proxy `*Deps`/`*Proxy` interfaces.
182
+ Exported for host-app integration: `ShellAdapter`, `ShellCapabilities`, `CapabilityHooks`, `OriginIdentity`, `RelayPoolHooks`, `RelayPoolLike`, `RelayConfigHooks`, `WindowManagerHooks`, `AuthHooks`, `ConfigHooks`, `HotkeyHooks`, `WorkerRelayHooks`, `WorkerRelayLike`, `CryptoHooks`, `DmHooks`, `UploadHooks`, `IntentHooks`, `LinkHooks`, `CommonHooks`, `ListsHooks`, `SerialHooks`, `BleHooks`, `WebrtcHooks`, `SessionEntry`, `NappKeyEntry` (deprecated), `AclEntry`, `AclCheckEvent`, `UnroutedMessageInfo`, `ServiceDescriptor`, `ServiceHandler`, `ServiceRegistry`, `NostrEvent`, `NostrFilter`, `NappletMessage`, `ConsentRequest`, and per-proxy `*Deps`/`*Proxy` interfaces.
183
+
184
+ `RelayPoolLike.publish()` may return `Promise<void>` when transport acceptance
185
+ is asynchronous. The shell adapter forwards that promise so the runtime does
186
+ not acknowledge or buffer an event before the relay operation settles.
187
+ `RelayPoolHooks.publishToScopedRelay()` may likewise return
188
+ `Promise<boolean>` so asynchronous hosts report the settled outcome.
102
189
 
103
190
  ### Enforcement re-exports (from @kehto/runtime)
104
191
  `createEnforceGate`, `createNapEnforceGate`, `formatDenialReason`, plus `EnforceResult`, `EnforceConfig`, `NapEnforceConfig`, `IdentityResolver`, `AclChecker`, `NapMessage`.
package/dist/index.d.ts CHANGED
@@ -4,6 +4,38 @@ import { NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';
4
4
  export { NappletMessage, NostrEvent, NostrFilter, TOPICS, TopicKey, TopicValue } from '@napplet/core';
5
5
  import { Theme } from '@napplet/nap/theme/types';
6
6
 
7
+ /** NIP-5D identity metadata associated with a registered iframe window. */
8
+ interface OriginIdentity {
9
+ readonly dTag: string;
10
+ readonly aggregateHash: string;
11
+ }
12
+ /**
13
+ * Bidirectional registry mapping Window references to windowId strings.
14
+ * Optionally stores NIP-5D identity metadata (dTag and aggregateHash) per window.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { originRegistry } from '@kehto/shell';
19
+ *
20
+ * originRegistry.register(iframe.contentWindow, 'napp-1');
21
+ * const id = originRegistry.getWindowId(iframe.contentWindow); // 'napp-1'
22
+ * ```
23
+ */
24
+ interface OriginRegistry {
25
+ register(win: Window, windowId: string, identity?: OriginIdentity): void;
26
+ unregister(windowId: string): void;
27
+ getWindowId(win: Window): string | undefined;
28
+ getIframeWindow(windowId: string): Window | null;
29
+ getAllWindowIds(): string[];
30
+ getIdentity(win: Window): OriginIdentity | undefined;
31
+ setEnvironment(win: Window, environment: ShellEnvironment): void;
32
+ getEnvironment(win: Window): ShellEnvironment | undefined;
33
+ getRegistrationId(win: Window): number | undefined;
34
+ clear(): void;
35
+ }
36
+ /** Shell-wide iframe window registry singleton. */
37
+ declare const originRegistry: OriginRegistry;
38
+
7
39
  /**
8
40
  * ACL entry controlling what a napplet pubkey is permitted to do.
9
41
  * @example
@@ -42,8 +74,13 @@ interface RelayPoolHooks {
42
74
  openScopedRelay(windowId: string, relayUrl: string, subId: string, filters: NostrFilter[], sourceWindow: Window): void;
43
75
  /** Close a scoped relay connection. */
44
76
  closeScopedRelay(windowId: string): void;
45
- /** Publish to a scoped relay. Returns false if no active scoped relay. */
46
- publishToScopedRelay(windowId: string, event: NostrEvent): boolean;
77
+ /**
78
+ * Publish to a scoped relay.
79
+ *
80
+ * Async hosts settle the returned promise after transport acceptance.
81
+ * Returns false if no active scoped relay or publication fails.
82
+ */
83
+ publishToScopedRelay(windowId: string, event: NostrEvent): boolean | Promise<boolean>;
47
84
  /** Select relay URLs for a given set of filters. */
48
85
  selectRelayTier(filters: NostrFilter[]): string[];
49
86
  }
@@ -54,7 +91,7 @@ interface RelayPoolLike {
54
91
  unsubscribe(): void;
55
92
  };
56
93
  };
57
- publish(relayUrls: string[], event: any): void;
94
+ publish(relayUrls: string[], event: any): void | Promise<void>;
58
95
  request(relayUrls: string[], filters: any): {
59
96
  subscribe(observer: {
60
97
  next: (event: unknown) => void;
@@ -205,8 +242,26 @@ interface WebrtcHooks {
205
242
  * environments while still using the production shell-ready path.
206
243
  */
207
244
  interface CapabilityHooks {
208
- /** Bare capability domains to remove from domains, naps, and protocols. */
245
+ /** Bare capability domains to remove from the delivered environment. */
209
246
  readonly disabledDomains?: readonly string[];
247
+ /**
248
+ * Narrow the live environment for one trusted creation-time identity.
249
+ *
250
+ * The shell supplies copied, frozen live domains and named services. Returned
251
+ * names are intersected with that exact availability set, so this hook cannot
252
+ * add aliases, disabled entries, or unwired capabilities.
253
+ *
254
+ * @param identity - Identity assigned by the host when the iframe was created.
255
+ * @param available - Immutable live domains and services before host policy.
256
+ * @returns The requested subset for this identity.
257
+ */
258
+ readonly resolveEnvironment?: (identity: OriginIdentity, available: Readonly<{
259
+ domains: readonly string[];
260
+ services: readonly string[];
261
+ }>) => Readonly<{
262
+ domains: readonly string[];
263
+ services: readonly string[];
264
+ }>;
210
265
  }
211
266
  /**
212
267
  * Event emitted on every ACL enforcement check.
@@ -266,67 +321,17 @@ interface UnroutedMessageInfo {
266
321
  * authored code, while mandatory NAP-SHELL caches this environment for local
267
322
  * capability queries.
268
323
  *
269
- * Current availability is the injected `window.napplet.<domain>` namespace.
270
- * NAP-SHELL supports() treats only NAP domains and protocols as advertised
271
- * capabilities:
272
- *
273
- * - Bare names for NAP-capability lookups, resolved against the `naps`
274
- * array — e.g. `supports('relay')`, `supports('identity')`.
275
- *
276
- * The sandbox array remains for older payload readers, but Kehto emits it empty.
277
- * NIP-5D no longer blesses additional browser sandbox tokens as interoperability
278
- * requirements or napplet-visible capabilities.
279
- *
280
- * ## NAP-SHELL shape
281
- *
282
- * The shell shim reads the structured
283
- * `capabilities.{ domains: string[], protocols: Record<string, string[]> }`
284
- * alongside the flat `naps` array:
285
- *
286
- * - `supports('relay')` → true iff `'relay' ∈ capabilities.domains`
287
- * - `supports('inc','NAP-01')`→ true iff `capabilities.protocols['inc']`
288
- * includes `'NAP-01'`
289
- *
290
- * `domains` and `protocols` are emitted as a superset alongside the
291
- * `naps`/`sandbox` fields for back-compat.
324
+ * NAP-SHELL needs only a truthful set of bare domain names. The shim answers
325
+ * `shell.supports(domain)` locally from this snapshot after `shell.init`.
292
326
  */
293
327
  interface ShellCapabilities {
294
- /**
295
- * NAP-SHELL domain list.
296
- * Bare NAP domain names the shell offers — `'identity'`, `'storage'`,
297
- * `'inc'`, `'theme'`, etc. — with the same conditional entries as `naps`
298
- * (`relay`/`outbox` when a relay pool is wired, `upload`/`intent` under their
299
- * hooks). Carries NO `inc:NAP-NN` protocol strings (those live in
300
- * `protocols`).
301
- */
302
- domains: string[];
303
- /**
304
- * NAP-SHELL per-domain numbered protocols. Maps a domain to the NAP-NN protocol IDs it
305
- * speaks — `{ inc: ['NAP-01','NAP-02','NAP-03','NAP-04','NAP-05','NAP-06'] }`
306
- * (derived from `NAP_INC_PROTOCOLS` by stripping the `inc:` prefix).
307
- *
308
- * Local supports helpers resolve `supports('<domain>','NAP-NN')` against this map.
309
- */
310
- protocols: Record<string, string[]>;
311
- /**
312
- * NAP-vocabulary domain entries the shell handles (PRIMARY — consumed by
313
- * `@napplet/shim >=0.9.0`). Bare domain `inc` (the NAP rename of `inc`)
314
- * plus protocol IDs `inc:NAP-01..inc:NAP-06`. Conditional entries:
315
- * `relay`, `outbox` prepended when a relay pool is wired; `upload` appended
316
- * when an upload backend is wired; `intent` appended when an intent
317
- * dispatcher is available.
318
- *
319
- * Contains NO `NAP-NN` protocol strings (those live in `protocols`).
320
- */
321
- naps: string[];
322
- /**
323
- * Empty compatibility field for older `shell.init` payload consumers.
324
- *
325
- * Current NIP-5D keeps the web sandbox baseline narrow: hosted napplet iframes
326
- * use `allow-scripts`, never `allow-same-origin`, and do not receive optional
327
- * browser sandbox relaxations as advertised capabilities.
328
- */
329
- sandbox: string[];
328
+ /** Immutable bare domain names the shell offers to this napplet. */
329
+ readonly domains: readonly string[];
330
+ }
331
+ /** Immutable NAP-SHELL environment delivered to one trusted iframe session. */
332
+ interface ShellEnvironment {
333
+ readonly capabilities: ShellCapabilities;
334
+ readonly services: readonly string[];
330
335
  }
331
336
  /**
332
337
  * All adapters that the shell requires from the host application.
@@ -532,18 +537,13 @@ interface ShellBridge {
532
537
  */
533
538
  registerConsentHandler(handler: (request: ConsentRequest) => void): void;
534
539
  /**
535
- * Publish a theme update to every registered napplet.
540
+ * Publish a theme update to each eligible napplet.
536
541
  *
537
542
  * Posts a `theme.changed` envelope (shell → napplet push) to every
538
- * window currently tracked by the runtime's sessionRegistry, using
539
- * the browser-adapter originRegistry to resolve windowId → iframe.
540
- * Napplets whose window cannot be resolved (stale sessions) are
541
- * silently skipped; this method never throws.
542
- *
543
- * ACL is enforced BY THE RECIPIENT NAPPLET — the runtime's
544
- * `themeMap` in @kehto/acl assigns `recipientCap: 'theme:read'` for
545
- * `theme.changed`, and @napplet/shim drops pushes the napplet lacks
546
- * the capability for. Hosts should not self-filter here.
543
+ * live authenticated session whose frozen environment grants `theme` and
544
+ * whose current ACL grants `theme:read`. Stale or ineligible sessions are
545
+ * skipped before origin-registry delivery; recipient code is not trusted
546
+ * to enforce host authorization.
547
547
  *
548
548
  * @param theme - The new theme payload to broadcast.
549
549
  * @example
@@ -630,36 +630,6 @@ declare function renderNappletNamespacePrelude(options: NappletNamespacePreludeO
630
630
  */
631
631
  declare function injectNappletNamespacePrelude(html: string, options: NappletNamespacePreludeOptions): string;
632
632
 
633
- /** NIP-5D identity metadata associated with a registered iframe window. */
634
- interface OriginIdentity {
635
- readonly dTag: string;
636
- readonly aggregateHash: string;
637
- }
638
- /**
639
- * Bidirectional registry mapping Window references to windowId strings.
640
- * Optionally stores NIP-5D identity metadata (dTag and aggregateHash) per window.
641
- *
642
- * @example
643
- * ```ts
644
- * import { originRegistry } from '@kehto/shell';
645
- *
646
- * originRegistry.register(iframe.contentWindow, 'napp-1');
647
- * const id = originRegistry.getWindowId(iframe.contentWindow); // 'napp-1'
648
- * ```
649
- */
650
- interface OriginRegistry {
651
- register(win: Window, windowId: string, identity?: OriginIdentity): void;
652
- unregister(windowId: string): void;
653
- getWindowId(win: Window): string | undefined;
654
- getIframeWindow(windowId: string): Window | null;
655
- getAllWindowIds(): string[];
656
- getIdentity(win: Window): OriginIdentity | undefined;
657
- getRegistrationId(win: Window): number | undefined;
658
- clear(): void;
659
- }
660
- /** Shell-wide iframe window registry singleton. */
661
- declare const originRegistry: OriginRegistry;
662
-
663
633
  /**
664
634
  * Manifest cache — persists verified NIP-5A aggregate hashes per napplet identity.
665
635
  */
@@ -864,61 +834,36 @@ interface BrowserDeps {
864
834
  declare function adaptHooks(shellHooks: ShellAdapter, deps: BrowserDeps): RuntimeAdapter;
865
835
 
866
836
  /**
867
- * Build the shell's static capability set from adapter configuration.
868
- *
869
- * Returns the NAP-SHELL `domains`/`protocols` environment used by local
870
- * `window.napplet.shell.supports()` queries. Flat `naps` and empty `sandbox`
871
- * fields remain alongside it for older payload readers.
872
- *
873
- * ### naps array (NAP vocabulary)
874
- * Bare domain `inc` + `inc:NAP-01..inc:NAP-06`.
875
- * Conditional: `relay`+`outbox` prepended when hooks.relayPool;
876
- * `upload` appended when hooks.upload;
877
- * `intent` appended when hooks.intent.isAvailable();
878
- * `link` appended when hooks.link.isAvailable();
879
- * `common` appended when hooks.common.isAvailable().
880
- *
881
- * The sandbox array is retained as an always-empty compatibility field. Current
882
- * NIP-5D defines only the `allow-scripts` baseline and does not make additional
883
- * browser sandbox tokens a napplet capability surface.
884
- *
885
- * ### domains array + protocols map (NAP-SHELL)
886
- * The structured environment delivered to the mandatory shell shim:
887
- *
888
- * - `domains` — bare NAP domain names (the `naps` set MINUS the `inc:NAP-NN`
889
- * protocol strings) with the same conditional entries (relay/outbox under
890
- * `hooks.relayPool`, upload/intent/link/common under their hooks).
891
- * - `protocols` — `{ inc: ['NAP-01'..'NAP-06'] }`, derived from
892
- * `NAP_INC_PROTOCOLS` by stripping the `inc:` prefix.
893
- *
894
- * `naps`/`sandbox` are emitted as compatibility fields alongside this shape.
837
+ * Build the shell's immutable live capability snapshot from adapter wiring.
895
838
  *
896
- * @param hooks - The ShellAdapter provided by the host app
897
- * @returns ShellCapabilities with normative local-query data plus legacy fields
839
+ * @param hooks - The ShellAdapter provided by the host app.
840
+ * @returns Domain-only ShellCapabilities for the current live wiring.
898
841
  * @example
899
842
  * ```ts
900
843
  * const caps = buildShellCapabilities(hooks);
901
- * // caps.domains => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','cvm']
902
- * // (relay + outbox present when hooks.relayPool is provided; 'upload'/'intent'
903
- * // appended under their hooks)
904
- * // caps.protocols => { inc: ['NAP-01','NAP-02','NAP-03','NAP-04','NAP-05','NAP-06'] }
905
- * // caps.naps => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','cvm',
906
- * // 'inc:NAP-01','inc:NAP-02','inc:NAP-03','inc:NAP-04','inc:NAP-05','inc:NAP-06']
907
- * // (relay + outbox present when hooks.relayPool is provided; 'upload'
908
- * // appended when hooks.upload is provided; 'intent' appended when
909
- * // hooks.intent.isAvailable() is true)
910
- * // caps.sandbox => [] // retained for compatibility; not a NAP capability surface
844
+ * // caps.domains => ['relay', 'identity', 'storage', 'inc', 'theme', 'keys', 'media', 'notify']
911
845
  * ```
912
846
  */
913
847
  declare function buildShellCapabilities(hooks: ShellAdapter): ShellCapabilities;
848
+ /**
849
+ * Resolve a trusted creation identity's immutable NAP-SHELL environment.
850
+ *
851
+ * This host-adapter API is intentionally not part of `window.napplet`: it
852
+ * bounds host policy to current runtime wiring before `shell.init` crosses into
853
+ * an untrusted iframe.
854
+ *
855
+ * @param hooks - Shell host wiring and optional per-identity grant policy.
856
+ * @param identity - The source's creation-time identity.
857
+ * @returns A fresh immutable environment whose entries are exact live subsets.
858
+ */
859
+ declare function resolveShellEnvironment(hooks: ShellAdapter, identity: OriginIdentity): ShellEnvironment;
914
860
 
915
861
  /**
916
862
  * identity-proxy.ts — Shell-side per-domain proxy for identity.* envelopes.
917
863
  *
918
864
  * Establishes the canonical proxy shape for @kehto/shell (Plan 12-11): each
919
865
  * per-domain proxy exposes a `dispatch` method that delegates napplet→shell
920
- * requests to the runtime and an `emit` method that posts shell→napplet
921
- * push envelopes through the origin registry.
866
+ * requests to the runtime.
922
867
  *
923
868
  * By default, `createShellBridge()` does NOT compose this proxy into its
924
869
  * dispatch path — the runtime already owns identity.* dispatch per Plan
@@ -927,10 +872,9 @@ declare function buildShellCapabilities(hooks: ShellAdapter): ShellCapabilities;
927
872
  * augment identity dispatch (e.g. custom logging, sandboxed rewrites, test
928
873
  * doubles).
929
874
  *
930
- * The canonical proxy shape — dispatch + emit — is mirrored verbatim by
931
- * theme-proxy, keys-proxy, media-proxy, and notify-proxy. Storage today is
932
- * served by `@kehto/runtime` state-handler directly; a storage-proxy using
933
- * this shape can be added later if host apps need a composition seam.
875
+ * Identity changes are deliberately not emitted through this proxy. Hosts
876
+ * must use `ShellBridge.publishIdentityChanged()`, which enforces the live
877
+ * session, granted-domain, and current recipient-capability checks.
934
878
  */
935
879
 
936
880
  /**
@@ -963,9 +907,8 @@ interface IdentityProxyDeps {
963
907
  /**
964
908
  * Per-domain proxy for `identity.*` envelopes.
965
909
  *
966
- * The canonical proxy shape: `dispatch` routes napplet→shell requests into
967
- * the runtime; `emit` pushes shell→napplet envelopes through the iframe's
968
- * Window.
910
+ * `dispatch` routes napplet→shell requests into the runtime. The deprecated
911
+ * `emit` member remains only as a fail-closed compatibility trap.
969
912
  */
970
913
  interface IdentityProxy {
971
914
  /**
@@ -979,13 +922,10 @@ interface IdentityProxy {
979
922
  */
980
923
  dispatch(windowId: string, envelope: NappletMessage): void;
981
924
  /**
982
- * Push a shell-initiated identity-domain envelope into a napplet iframe.
983
- *
984
- * No-op when the originRegistry cannot resolve the windowId (unknown or
985
- * unregistered napplet). Never throws.
925
+ * Direct identity delivery is prohibited.
986
926
  *
987
- * @param windowId - The target napplet's windowId
988
- * @param envelope - The NIP-5D NappletMessage envelope to deliver
927
+ * @deprecated Use `ShellBridge.publishIdentityChanged()` so delivery is
928
+ * filtered by live session, granted domain, and current ACL.
989
929
  */
990
930
  emit(windowId: string, envelope: NappletMessage): void;
991
931
  }
@@ -1022,12 +962,9 @@ declare function createIdentityProxy(deps: IdentityProxyDeps): IdentityProxy;
1022
962
  * API that emits `theme.changed` push envelopes to registered napplets;
1023
963
  * this proxy is the canonical seam those pieces plug into.
1024
964
  *
1025
- * Shape mirrors identity-proxy (Plan 12-11):
1026
- *
1027
- * - `dispatch(windowId, envelope)` routes napplet→shell `theme.get` into
1028
- * the runtime (where Phase 13's theme-service will answer).
1029
- * - `emit(windowId, envelope)` posts shell→napplet `theme.changed`
1030
- * envelopes through the origin registry.
965
+ * `dispatch(windowId, envelope)` routes napplet→shell `theme.get` into the
966
+ * runtime. Theme changes must use `ShellBridge.publishTheme()` so the host
967
+ * projection enforces live-session, granted-domain, and current-ACL checks.
1031
968
  *
1032
969
  * By default `createShellBridge()` does NOT compose this proxy into its
1033
970
  * dispatch path — the runtime owns theme.* dispatch. This module is an
@@ -1054,8 +991,8 @@ interface ThemeProxyDeps {
1054
991
  /**
1055
992
  * Per-domain proxy for `theme.*` envelopes.
1056
993
  *
1057
- * Shape: `dispatch` routes napplet→shell requests into the runtime; `emit`
1058
- * pushes shell→napplet envelopes through the iframe's Window.
994
+ * `dispatch` routes napplet→shell requests into the runtime. The deprecated
995
+ * `emit` member remains only as a fail-closed compatibility trap.
1059
996
  */
1060
997
  interface ThemeProxy {
1061
998
  /**
@@ -1067,14 +1004,10 @@ interface ThemeProxy {
1067
1004
  */
1068
1005
  dispatch(windowId: string, envelope: NappletMessage): void;
1069
1006
  /**
1070
- * Push a shell-initiated theme-domain envelope (e.g. `theme.changed`)
1071
- * into a napplet iframe.
1072
- *
1073
- * No-op when the originRegistry cannot resolve the windowId (unknown or
1074
- * unregistered napplet). Never throws.
1007
+ * Direct theme delivery is prohibited.
1075
1008
  *
1076
- * @param windowId - The target napplet's windowId
1077
- * @param envelope - The NIP-5D NappletMessage envelope to deliver
1009
+ * @deprecated Use `ShellBridge.publishTheme()` so delivery is filtered by
1010
+ * live session, granted domain, and current ACL.
1078
1011
  */
1079
1012
  emit(windowId: string, envelope: NappletMessage): void;
1080
1013
  }
@@ -1085,18 +1018,13 @@ interface ThemeProxy {
1085
1018
  * @returns A {@link ThemeProxy} ready to route theme.* envelopes
1086
1019
  * @example
1087
1020
  * ```ts
1088
- * import { createThemeProxy, originRegistry, createShellBridge } from '@kehto/shell';
1021
+ * import { createThemeProxy, originRegistry } from '@kehto/shell';
1089
1022
  *
1090
- * const bridge = createShellBridge(hooks);
1091
1023
  * const themeProxy = createThemeProxy({
1092
- * runtime: bridge.runtime,
1024
+ * runtime,
1093
1025
  * originRegistry,
1094
1026
  * });
1095
- *
1096
- * // Phase 13: broadcast theme.changed to every registered napplet
1097
- * for (const entry of bridge.runtime.sessionRegistry.getAllEntries()) {
1098
- * themeProxy.emit(entry.windowId, { type: 'theme.changed', theme: newTheme });
1099
- * }
1027
+ * themeProxy.dispatch(windowId, { type: 'theme.get', id: requestId });
1100
1028
  * ```
1101
1029
  */
1102
1030
  declare function createThemeProxy(deps: ThemeProxyDeps): ThemeProxy;
@@ -1535,4 +1463,4 @@ type ResourceInbound = ResourceInfoRequest | ResourceBytesRequest | ResourceByte
1535
1463
  */
1536
1464
  type ResourceOutbound = ResourceInfoResult | ResourceInfoError | ResourceBytesResult | ResourceBytesError | ResourceBytesManyResult | ResourceBytesManyError;
1537
1465
 
1538
- 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 ResourceInfo, type ResourceInfoError, type ResourceInfoRequest, type ResourceInfoResult, type ResourceOutbound, type ResourceRequestId, type SerialHooks, type SessionRegistry, type ShellAdapter, type ShellBridge, type ShellCapabilities, 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, sessionRegistry };
1466
+ 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 ResourceInfo, 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 };