@kehto/shell 0.11.1 → 0.13.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
@@ -53,6 +53,18 @@ bridge.runtime.registerService(
53
53
  ### Hooks adapter
54
54
  - `adaptHooks` — convert a `ShellAdapter` + `BrowserDeps` into the canonical `RuntimeAdapter` hook bag consumed by `@kehto/runtime`
55
55
 
56
+ ### Diagnostics
57
+ - `ShellAdapter.onUnroutedMessage?(info)` — optional observe-only hook fired when `ShellBridge.handleMessage` drops an incoming postMessage it can't route to a registered napplet window. `info` is an `UnroutedMessageInfo` (`{ type?, origin, reason }`) where `reason` is `'no-source-window'` or `'unregistered-window'`. The message is still dropped — this exists so otherwise-silent drops (e.g. an intent/`srcdoc` iframe whose `contentWindow` was never registered in `originRegistry`, or was swapped by a reload) are diagnosable instead of vanishing. Host can `console.warn` inside its own hook; the bridge adds no console output and swallows hook errors so routing is never broken.
58
+
59
+ ```ts
60
+ const bridge = createShellBridge({
61
+ ...myShellAdapter,
62
+ onUnroutedMessage: ({ type, origin, reason }) => {
63
+ console.warn(`[shell] dropped ${type ?? '<unknown>'} from ${origin}: ${reason}`);
64
+ },
65
+ });
66
+ ```
67
+
56
68
  ### Shell init
57
69
  - `buildShellCapabilities` — construct the current draft `ShellCapabilities` payload emitted during the `shell.ready` / `shell.init` handshake
58
70
 
@@ -85,10 +97,10 @@ bridge.runtime.registerService(
85
97
  - `TopicKey`, `TopicValue` — typed topic lookup helpers
86
98
 
87
99
  ### Types
88
- Exported for host-app integration: `ShellAdapter`, `ShellCapabilities`, `RelayPoolHooks`, `RelayPoolLike`, `RelayConfigHooks`, `WindowManagerHooks`, `AuthHooks`, `ConfigHooks`, `HotkeyHooks`, `WorkerRelayHooks`, `WorkerRelayLike`, `CryptoHooks`, `DmHooks`, `SessionEntry`, `NappKeyEntry` (deprecated), `AclEntry`, `AclCheckEvent`, `ServiceDescriptor`, `ServiceHandler`, `ServiceRegistry`, `NostrEvent`, `NostrFilter`, `NappletMessage`, `ConsentRequest`, and per-proxy `*Deps`/`*Proxy` interfaces.
100
+ Exported for host-app integration: `ShellAdapter`, `ShellCapabilities`, `RelayPoolHooks`, `RelayPoolLike`, `RelayConfigHooks`, `WindowManagerHooks`, `AuthHooks`, `ConfigHooks`, `HotkeyHooks`, `WorkerRelayHooks`, `WorkerRelayLike`, `CryptoHooks`, `DmHooks`, `SessionEntry`, `NappKeyEntry` (deprecated), `AclEntry`, `AclCheckEvent`, `UnroutedMessageInfo`, `ServiceDescriptor`, `ServiceHandler`, `ServiceRegistry`, `NostrEvent`, `NostrFilter`, `NappletMessage`, `ConsentRequest`, and per-proxy `*Deps`/`*Proxy` interfaces.
89
101
 
90
102
  ### Enforcement re-exports (from @kehto/runtime)
91
- `createEnforceGate`, `createNubEnforceGate`, `formatDenialReason`, plus `EnforceResult`, `EnforceConfig`, `NubEnforceConfig`, `IdentityResolver`, `AclChecker`, `NubMessage`.
103
+ `createEnforceGate`, `createNapEnforceGate`, `formatDenialReason`, plus `EnforceResult`, `EnforceConfig`, `NapEnforceConfig`, `IdentityResolver`, `AclChecker`, `NapMessage`.
92
104
 
93
105
  ### Compat re-exports (DRIFT-CORE-06)
94
106
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Capability, NappletClass, ServiceRegistry, RuntimeConfigOverrides, ConsentRequest, Runtime, SessionEntry, PendingUpdate, RuntimeAdapter } from '@kehto/runtime';
2
- export { ALL_CAPABILITIES, AclChecker, Capability, ConsentRequest, EnforceConfig, EnforceResult, IdentityResolver, NappKeyEntry, NappletClass, NubEnforceConfig, NubMessage, PendingUpdate, ServiceDescriptor, ServiceHandler, ServiceRegistry, SessionEntry, createEnforceGate, createNubEnforceGate, formatDenialReason } from '@kehto/runtime';
1
+ import { Capability, ServiceRegistry, RuntimeConfigOverrides, ConsentRequest, Runtime, SessionEntry, PendingUpdate, RuntimeAdapter } from '@kehto/runtime';
2
+ export { ALL_CAPABILITIES, AclChecker, Capability, ConsentRequest, EnforceConfig, EnforceResult, IdentityResolver, NapEnforceConfig, NapMessage, NappKeyEntry, PendingUpdate, ServiceDescriptor, ServiceHandler, ServiceRegistry, SessionEntry, createEnforceGate, createNapEnforceGate, formatDenialReason } from '@kehto/runtime';
3
3
  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';
@@ -165,6 +165,17 @@ interface IntentHooks {
165
165
  /** True when the host has an intent resolver wired and ready to dispatch. */
166
166
  isAvailable(): boolean;
167
167
  }
168
+ /**
169
+ * Optional host override for the static shell.init capability handshake.
170
+ *
171
+ * Hosts normally advertise the default Kehto NAP surface. Development hosts may
172
+ * temporarily suppress domains to simulate smaller or policy-constrained shell
173
+ * environments while still using the production shell-ready path.
174
+ */
175
+ interface CapabilityHooks {
176
+ /** Bare capability domains to remove from domains, naps, and protocols. */
177
+ readonly disabledDomains?: readonly string[];
178
+ }
168
179
  /**
169
180
  * Event emitted on every ACL enforcement check.
170
181
  * @example
@@ -188,36 +199,51 @@ interface AclCheckEvent {
188
199
  /** The triggering message, if available. Accepts NIP-01 arrays or NIP-5D NappletMessage envelopes. */
189
200
  message?: NappletMessage | unknown[];
190
201
  }
202
+ /**
203
+ * Diagnostic payload reported when {@link ShellAdapter.onUnroutedMessage} fires —
204
+ * i.e. when `ShellBridge.handleMessage` drops an incoming postMessage because it
205
+ * cannot be routed to a registered napplet window.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * hooks.onUnroutedMessage = (info: UnroutedMessageInfo) => {
210
+ * console.warn(`[shell] dropped ${info.type ?? '<unknown>'} from ${info.origin}: ${info.reason}`);
211
+ * };
212
+ * ```
213
+ */
214
+ interface UnroutedMessageInfo {
215
+ /**
216
+ * The dropped message's `type` field when `event.data` is an object with a
217
+ * string `type`; `undefined` for malformed/non-envelope payloads.
218
+ */
219
+ type?: string;
220
+ /** The `MessageEvent.origin` of the dropped message. */
221
+ origin: string;
222
+ /**
223
+ * Why the message was dropped:
224
+ * - `no-source-window` — the `MessageEvent` had no `source` window to identify the sender.
225
+ * - `unregistered-window` — the source `Window` is not in `originRegistry` (the
226
+ * sending iframe was never registered, or a `srcdoc` reload swapped its
227
+ * `contentWindow` to a new object that no longer matches the registry key).
228
+ */
229
+ reason: 'no-source-window' | 'unregistered-window';
230
+ }
191
231
  /**
192
232
  * Static capability set sent to napplet iframes through the shell.ready /
193
233
  * shell.init handshake. Used by hosted `window.napplet.shell.supports()` for
194
234
  * synchronous capability queries after the shim consumes shell.init.
195
235
  *
196
- * Per canonical NIP-5D (specs/NIP-5D.md lines 81-94), supports() distinguishes
197
- * two namespaces:
236
+ * Per canonical NIP-5D (https://github.com/nostr-protocol/nips/pull/2303/),
237
+ * supports() distinguishes two namespaces:
198
238
  *
199
- * - Bare names (or optional `nub:` prefix) for NUB-capability lookups,
200
- * resolved against the `nubs` array — e.g. `supports('relay')`,
201
- * `supports('identity')`.
239
+ * - Bare names for NAP-capability lookups, resolved against the `naps`
240
+ * array — e.g. `supports('relay')`, `supports('identity')`.
202
241
  * - The `perm:<permission>` prefix for sandbox-permission lookups, resolved
203
242
  * against the `sandbox` array — e.g. `supports('perm:popups')`,
204
243
  * `supports('perm:modals')`.
205
244
  *
206
245
  * The two namespaces do not cross: a bare-name lookup never matches a sandbox
207
- * entry and a `perm:`-prefixed lookup never matches a NUB entry.
208
- *
209
- * ## Dual-emit (back-compat window — D2/D3, ALIGN-01..04)
210
- *
211
- * Both `naps` and `nubs` are emitted in the shell.init payload for one
212
- * back-compat release:
213
- *
214
- * - `naps` (PRIMARY) — NAP vocabulary: bare domain `inc`, protocol IDs
215
- * `inc:NAP-01..inc:NAP-06`. Consumed by `@napplet/shim >=0.9.0`.
216
- * Contains NO unaliased `ifc` or `NUB-NN` identifiers.
217
- * - `nubs` (LEGACY) — retains the original `ifc` / `ifc:NUB-01..06` /
218
- * `ifc:NAP-01` vocabulary for `@napplet/nub` and `<=0.8.x` shims.
219
- *
220
- * The dual-emit is intentional; removal is tracked as CLEANUP-01.
246
+ * entry and a `perm:`-prefixed lookup never matches a NAP entry.
221
247
  *
222
248
  * ## Conformant NAP-SHELL shape (TERM-03 — @napplet/core@0.12 / @napplet/shim@0.13)
223
249
  *
@@ -230,8 +256,8 @@ interface AclCheckEvent {
230
256
  * - `supports('inc','NAP-01')`→ true iff `capabilities.protocols['inc']`
231
257
  * includes `'NAP-01'`
232
258
  *
233
- * `domains` and `protocols` are emitted as a SUPERSET ALONGSIDE the legacy
234
- * `naps`/`nubs`/`sandbox` fields (TERM-05 back-compat). The 0.13 shim has NO
259
+ * `domains` and `protocols` are emitted as a SUPERSET ALONGSIDE the
260
+ * `naps`/`sandbox` fields (TERM-05 back-compat). The 0.13 shim has NO
235
261
  * special `perm:` logic — `supports('perm:popups')` is an ordinary bare-domain
236
262
  * membership check, so kehto's `perm:`-prefixed sandbox entries are folded into
237
263
  * `domains` (empty by default, preserving the default-empty sandbox behavior).
@@ -262,38 +288,25 @@ interface ShellCapabilities {
262
288
  protocols: Record<string, string[]>;
263
289
  /**
264
290
  * NAP-vocabulary domain entries the shell handles (PRIMARY — consumed by
265
- * `@napplet/shim >=0.9.0`). Bare domain `inc` (the NAP rename of `ifc`)
291
+ * `@napplet/shim >=0.9.0`). Bare domain `inc` (the NAP rename of `inc`)
266
292
  * plus protocol IDs `inc:NAP-01..inc:NAP-06`. Conditional entries:
267
293
  * `relay`, `outbox` prepended when a relay pool is wired; `upload` appended
268
294
  * when an upload backend is wired; `intent` appended when an intent
269
295
  * dispatcher is available.
270
296
  *
271
- * Contains NO `ifc`, `NUB-NN`, or unaliased legacy identifiers.
297
+ * Contains NO `NAP-NN` protocol strings (those live in `protocols`).
272
298
  */
273
299
  naps: string[];
274
- /**
275
- * Legacy NUB domain entries the shell handles (retained for one back-compat
276
- * release — consumed by `@napplet/nub` and `@napplet/shim <=0.8.x`).
277
- * Vocabulary: `ifc`, `ifc:NUB-01..06`, `ifc:NAP-01`, bare domain names.
278
- * Contents are unchanged from the pre-0.9.0 release; `naps` is the primary
279
- * field for new shims. Removal tracked as CLEANUP-01.
280
- *
281
- * Entries are bare domain names — `'relay'`, `'identity'`, etc. They MUST NOT
282
- * carry the `perm:` prefix; that prefix is reserved for the `sandbox` array.
283
- * Napplets query NUB support via `supports('<domain>')` (or, equivalently,
284
- * `supports('nub:<domain>')`).
285
- */
286
- nubs: string[];
287
300
  /**
288
301
  * Sandbox permissions under the `perm:<permission>` namespace. Each entry
289
302
  * MUST begin with the literal prefix `'perm:'` — e.g. `'perm:popups'`,
290
303
  * `'perm:modals'`, `'perm:downloads'`. Napplets call
291
304
  * `shell.supports('perm:<permission>')` to check sandbox entitlements.
292
305
  *
293
- * The `perm:` prefix is what separates sandbox permissions from NUB
306
+ * The `perm:` prefix is what separates sandbox permissions from NAP
294
307
  * capabilities; bare-name entries here violate the NIP-5D contract and will
295
- * be unreachable through `supports()` (see specs/NIP-5D.md §6 and lines
296
- * 81-94). NUB-capability lookups (on `nubs`) retain the bare-name
308
+ * be unreachable through `supports()` (see the living NIP-5D at
309
+ * https://github.com/nostr-protocol/nips/pull/2303/). NAP-capability lookups (on `naps`) retain the bare-name
297
310
  * convention and do NOT use the `perm:` prefix.
298
311
  */
299
312
  sandbox: string[];
@@ -337,24 +350,30 @@ interface ShellAdapter {
337
350
  * concrete service via `registerService('intent')`.
338
351
  */
339
352
  intent?: IntentHooks;
353
+ /**
354
+ * Optional capability advertisement override. Omitted by production hosts.
355
+ */
356
+ capabilities?: CapabilityHooks;
340
357
  /** Called on every ACL enforcement check. Both allows and denials are reported. */
341
358
  onAclCheck?: (event: AclCheckEvent) => void;
359
+ /**
360
+ * Called when `ShellBridge.handleMessage` drops an incoming postMessage because
361
+ * it cannot be routed to a registered napplet window (no source window, or the
362
+ * source `Window` is not in `originRegistry`). Observe-only — the message is
363
+ * still dropped; this hook exists so otherwise-silent drops are diagnosable
364
+ * (the FEED-02 / hyprgate#21 class of "a napplet's messages vanish" bug).
365
+ */
366
+ onUnroutedMessage?: (info: UnroutedMessageInfo) => void;
342
367
  /** Called when aggregate hash verification fails (computed != declared). */
343
368
  onHashMismatch?: (dTag: string, claimed: string, computed: string) => void;
344
369
  /**
345
370
  * Called at iframe creation for NIP-5D napplets.
346
- * Returns identity metadata for originRegistry.register(), INCLUDING the
347
- * class posture (CLASS-01, breaking v1.7). Returning `class: null` selects
348
- * the permissive default (D2). Returning null overall means "not NIP-5D /
349
- * skip registration" (unchanged semantics from v1.6).
350
- *
351
- * BREAKING v1.7: previously `{ dTag, aggregateHash } | null`; now requires
352
- * `class` in the non-null branch. See .changeset/class-01-breaking-hook.md.
371
+ * Returns identity metadata for originRegistry.register(). Returning null
372
+ * means "not NIP-5D / skip registration".
353
373
  */
354
374
  onNip5dIframeCreate?: (windowId: string) => {
355
375
  dTag: string;
356
376
  aggregateHash: string;
357
- class: NappletClass;
358
377
  } | null;
359
378
  /**
360
379
  * Optional service extensions. Each key is a service name (e.g., 'audio',
@@ -381,146 +400,6 @@ interface ShellAdapter {
381
400
  getConfigOverrides?(): RuntimeConfigOverrides;
382
401
  }
383
402
 
384
- /**
385
- * connect-store.ts — NUB-CONNECT grant registry keyed on (dTag, aggregateHash).
386
- *
387
- * Mirrors acl-store.ts pattern: module-level singleton, localStorage persistence,
388
- * composite-key Map. Grants are per-napplet-build: changing the aggregateHash
389
- * (a rebuild) invalidates prior grants (CONNECT-06) — the composite key makes
390
- * this structural, not policy-driven.
391
- *
392
- * Default policy is RESTRICTIVE (opposite of aclStore): unknown (dTag, hash, origin)
393
- * combinations return `false` from check() — napplets must be explicitly granted
394
- * origins via grant(). This is the NUB-CONNECT security invariant.
395
- *
396
- * @see packages/shell/src/types/internal-connect.ts for the wire types.
397
- * @see docs/policies/SHELL-CONNECT-POLICY.md (Plan 39-05) for the full policy.
398
- */
399
- /**
400
- * Public interface for the NUB-CONNECT grant store singleton.
401
- *
402
- * All methods are keyed on (dTag, aggregateHash). A rebuild with a new
403
- * aggregateHash is a distinct identity — CONNECT-06 hash-upgrade semantics
404
- * are structurally guaranteed: check(dTag, newHash, origin) returns false
405
- * because newHash has no entry in the store.
406
- */
407
- interface ConnectStore {
408
- /**
409
- * Check whether an origin has been granted for a specific (dTag, aggregateHash).
410
- *
411
- * Returns false for unknown (dTag, aggregateHash) pairs — RESTRICTIVE default.
412
- * Returns false for unknown origins even if the (dTag, hash) pair exists.
413
- *
414
- * @param dTag - The napplet's dTag identifier
415
- * @param aggregateHash - The napplet's build aggregate hash
416
- * @param origin - The origin to check (e.g., 'https://relay.example.com')
417
- * @returns true if origin is in the grant set; false otherwise
418
- */
419
- check(dTag: string, aggregateHash: string, origin: string): boolean;
420
- /**
421
- * Get all granted origins for a (dTag, aggregateHash) pair.
422
- *
423
- * Returns an empty array for unknown (dTag, aggregateHash) pairs.
424
- *
425
- * @param dTag - The napplet's dTag identifier
426
- * @param aggregateHash - The napplet's build aggregate hash
427
- * @returns Readonly array of granted origin strings; empty if not granted
428
- */
429
- getOrigins(dTag: string, aggregateHash: string): readonly string[];
430
- /**
431
- * Grant a set of origins to a (dTag, aggregateHash) pair.
432
- *
433
- * Deduplicates and sorts origins for idempotent CSP header output (Plan 39-03).
434
- * Replaces any existing grant for the same (dTag, aggregateHash) pair.
435
- * Persists to localStorage automatically.
436
- *
437
- * @param dTag - The napplet's dTag identifier
438
- * @param aggregateHash - The napplet's build aggregate hash
439
- * @param origins - Origins to grant (e.g., ['https://relay.example.com', 'wss://relay2.example.com'])
440
- */
441
- grant(dTag: string, aggregateHash: string, origins: readonly string[]): void;
442
- /**
443
- * Revoke all granted origins for a (dTag, aggregateHash) pair.
444
- *
445
- * After revocation, check() returns false and getOrigins() returns [].
446
- * Persists to localStorage automatically.
447
- *
448
- * @param dTag - The napplet's dTag identifier
449
- * @param aggregateHash - The napplet's build aggregate hash
450
- */
451
- revoke(dTag: string, aggregateHash: string): void;
452
- /**
453
- * Get all current grants across all (dTag, aggregateHash) pairs.
454
- *
455
- * Used by the Vite CSP plugin (Plan 39-03) to build the connect-src
456
- * header for each napplet on dev-server startup.
457
- *
458
- * @returns Readonly array of all grants
459
- */
460
- getAllGrants(): ReadonlyArray<{
461
- dTag: string;
462
- aggregateHash: string;
463
- origins: readonly string[];
464
- }>;
465
- /**
466
- * Persist the current store state to localStorage under 'napplet:connect'.
467
- *
468
- * Tolerates unavailable localStorage (e.g., SSR, private browsing with
469
- * storage disabled). Called automatically by grant() and revoke().
470
- */
471
- persist(): void;
472
- /**
473
- * Load the store state from localStorage.
474
- *
475
- * Validates shape before populating; clears store on corrupt data.
476
- * Should be called once on shell startup before handling any napplet messages.
477
- */
478
- load(): void;
479
- /**
480
- * Clear all grants and remove the localStorage entry.
481
- *
482
- * Best-effort localStorage removal (tolerates unavailability).
483
- */
484
- clear(): void;
485
- }
486
- /**
487
- * NUB-CONNECT grant store singleton.
488
- *
489
- * Module-level instance — import and use directly. Persists under localStorage
490
- * key 'napplet:connect'. Call `connectStore.load()` on shell startup to restore
491
- * persisted grants from a previous session.
492
- *
493
- * @example
494
- * ```ts
495
- * import { connectStore } from '@kehto/shell';
496
- *
497
- * // On shell startup:
498
- * connectStore.load();
499
- *
500
- * // On consent approval:
501
- * connectStore.grant(dTag, aggregateHash, ['https://relay.example.com']);
502
- *
503
- * // In Vite CSP plugin (Plan 39-03):
504
- * const origins = connectStore.getOrigins(dTag, aggregateHash);
505
- * ```
506
- */
507
- declare const connectStore: ConnectStore;
508
- /**
509
- * Compose the canonical `<dTag>:<aggregateHash>` composite key used by the
510
- * connect-store. Exported for consumers that need to key their own maps by
511
- * the same identity (e.g., Vite CSP plugin — Plan 39-03).
512
- *
513
- * @param dTag - The napplet's dTag identifier
514
- * @param aggregateHash - The napplet's build aggregate hash
515
- * @returns Composite key string in `<dTag>:<aggregateHash>` format
516
- *
517
- * @example
518
- * ```ts
519
- * const key = connectGrantKey('chat', 'abc123'); // 'chat:abc123'
520
- * ```
521
- */
522
- declare function connectGrantKey(dTag: string, aggregateHash: string): string;
523
-
524
403
  /**
525
404
  * Shell-side message bridge that handles NIP-5D communication with napplet iframes.
526
405
  *
@@ -548,7 +427,7 @@ interface ShellBridge {
548
427
  *
549
428
  * shell.ready messages are handled locally: the bridge responds with shell.init
550
429
  * containing the capability set and registered service list. All other envelopes
551
- * are delegated to the runtime's NUB domain dispatch.
430
+ * are delegated to the runtime's NAP domain dispatch.
552
431
  *
553
432
  * @param event - The raw MessageEvent from window.addEventListener('message', ...)
554
433
  * @example
@@ -559,7 +438,7 @@ interface ShellBridge {
559
438
  handleMessage(event: MessageEvent): void;
560
439
  /**
561
440
  * Inject a shell-originated event into subscription delivery. Under NIP-5D,
562
- * shell-originated events are forwarded to napplets as ifc.event envelope
441
+ * shell-originated events are forwarded to napplets as inc.event envelope
563
442
  * messages. The runtime's injectEvent() handles the per-session routing.
564
443
  *
565
444
  * v1.10 hard-removed the v1.8 soft-rename compatibility branch for the
@@ -640,45 +519,7 @@ interface ShellBridge {
640
519
  * and manifestCache.
641
520
  */
642
521
  readonly runtime: Runtime;
643
- /**
644
- * Access the NUB-CONNECT grant store. Per-napplet connect grants are
645
- * keyed on (dTag, aggregateHash) and persisted under localStorage key
646
- * 'napplet:connect'. Public API surface for the Vite CSP middleware
647
- * (Plan 39-03) and the consent flow (Plan 39-04).
648
- *
649
- * Default policy is RESTRICTIVE: check() returns false for any origin
650
- * not explicitly granted. Call load() on shell startup to restore
651
- * persisted grants from a previous session.
652
- *
653
- * @see SHELL-CONNECT-POLICY.md for the full policy.
654
- */
655
- readonly connectStore: ConnectStore;
656
522
  }
657
- /**
658
- * Create a ShellBridge instance with dependency injection via hooks.
659
- *
660
- * Internally creates a Runtime from @kehto/runtime and adapts the
661
- * browser-oriented ShellAdapter into environment-agnostic RuntimeAdapter.
662
- *
663
- * @param hooks - Host application provides relay pool, auth, config, etc.
664
- * @returns A ShellBridge instance ready to handle napplet messages
665
- * @example
666
- * ```ts
667
- * import { createShellBridge, type ShellAdapter } from '@kehto/shell';
668
- *
669
- * const hooks: ShellAdapter = {
670
- * relayPool: myRelayPoolHooks,
671
- * relayConfig: myRelayConfigHooks,
672
- * windowManager: myWindowManagerHooks,
673
- * auth: myAuthHooks,
674
- * config: myConfigHooks,
675
- * hotkeys: myHotkeyHooks,
676
- * workerRelay: myWorkerRelayHooks,
677
- * crypto: myCryptoHooks,
678
- * };
679
- * const bridge = createShellBridge(hooks);
680
- * ```
681
- */
682
523
  declare function createShellBridge(hooks: ShellAdapter): ShellBridge;
683
524
 
684
525
  /**
@@ -1221,27 +1062,23 @@ declare function adaptHooks(shellHooks: ShellAdapter, deps: BrowserDeps): Runtim
1221
1062
  /**
1222
1063
  * Build the shell's static capability set from adapter configuration.
1223
1064
  *
1224
- * Returns BOTH `naps` (NAP vocabulary, primary — consumed by @napplet/shim >=0.9.0)
1225
- * and `nubs` (legacy vocabulary — consumed by @napplet/nub and <=0.8.x shims)
1226
- * for one back-compat release (D2/D3, ALIGN-01..04). Dual-emit is intentional;
1227
- * removal is tracked as CLEANUP-01.
1065
+ * Returns the conformant NAP-SHELL `domains`/`protocols` shape (consumed by
1066
+ * `@napplet/shim >=0.13` via `@napplet/nap@0.12`'s `createShellEnvironment` +
1067
+ * `makeSupports`) alongside the flat `naps` array (consumed by
1068
+ * `@napplet/shim >=0.9.0`) and the `sandbox` array.
1228
1069
  *
1229
1070
  * ### naps array (NAP vocabulary)
1230
- * Bare domain `inc` (rename of `ifc`) + `inc:NAP-01..inc:NAP-06`.
1071
+ * Bare domain `inc` + `inc:NAP-01..inc:NAP-06`.
1231
1072
  * Conditional: `relay`+`outbox` prepended when hooks.relayPool;
1232
1073
  * `upload` appended when hooks.upload;
1233
1074
  * `intent` appended when hooks.intent.isAvailable().
1234
- * Contains NO `ifc` or `NUB-NN` identifiers.
1235
- *
1236
- * ### nubs array (legacy vocabulary)
1237
- * Bare domain `ifc`, `ifc:NUB-01..06`, `ifc:NAP-01`.
1238
- * Same conditional appends as naps, using legacy vocabulary.
1239
1075
  *
1240
1076
  * Sandbox permissions are left empty by default — host apps may extend after
1241
1077
  * construction. Sandbox entries (and any host-app extensions) MUST use the
1242
1078
  * canonical `perm:<permission>` form — e.g. `'perm:popups'`, `'perm:modals'`,
1243
1079
  * `'perm:downloads'`. Napplets rely on the `perm:` prefix to distinguish
1244
- * sandbox permissions from NUB-capability lookups; see specs/NIP-5D.md lines 81-94.
1080
+ * sandbox permissions from NAP-capability lookups; see the living NIP-5D at
1081
+ * https://github.com/nostr-protocol/nips/pull/2303/
1245
1082
  *
1246
1083
  * ### domains array + protocols map (conformant NAP-SHELL — @napplet/core@0.12)
1247
1084
  * The structured shape the released `@napplet/shim@0.13` actually reads via
@@ -1255,26 +1092,23 @@ declare function adaptHooks(shellHooks: ShellAdapter, deps: BrowserDeps): Runtim
1255
1092
  * - `protocols` — `{ inc: ['NAP-01'..'NAP-06'] }`, derived from
1256
1093
  * `NAP_INC_PROTOCOLS` by stripping the `inc:` prefix.
1257
1094
  *
1258
- * Emitted as a SUPERSET alongside `naps`/`nubs`/`sandbox` (TERM-05 back-compat).
1095
+ * Emitted as a SUPERSET alongside `naps`/`sandbox` (TERM-05 back-compat).
1259
1096
  *
1260
1097
  * @param hooks - The ShellAdapter provided by the host app
1261
1098
  * @returns ShellCapabilities with domains/protocols (conformant 0.13 shape) plus
1262
- * naps (NAP vocab), nubs (legacy vocab), and sandbox (perm:-prefixed) arrays
1099
+ * naps (NAP vocab) and sandbox (perm:-prefixed) arrays
1263
1100
  * @example
1264
1101
  * ```ts
1265
1102
  * const caps = buildShellCapabilities(hooks);
1266
- * // caps.domains => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','connect','class','cvm']
1103
+ * // caps.domains => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','cvm']
1267
1104
  * // (relay + outbox present when hooks.relayPool is provided; 'upload'/'intent'
1268
1105
  * // appended under their hooks; perm:<x> sandbox entries appended when extended)
1269
1106
  * // caps.protocols => { inc: ['NAP-01','NAP-02','NAP-03','NAP-04','NAP-05','NAP-06'] }
1270
- * // caps.naps => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','connect','class','cvm',
1107
+ * // caps.naps => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','cvm',
1271
1108
  * // 'inc:NAP-01','inc:NAP-02','inc:NAP-03','inc:NAP-04','inc:NAP-05','inc:NAP-06']
1272
1109
  * // (relay + outbox present when hooks.relayPool is provided; 'upload'
1273
1110
  * // appended when hooks.upload is provided; 'intent' appended when
1274
- * // hooks.intent.isAvailable() is true; no ifc or NUB-NN identifiers)
1275
- * // caps.nubs => ['relay','outbox','identity','storage','ifc','theme','keys','media','notify','config','resource','connect','class','cvm',
1276
- * // 'ifc:NAP-01','ifc:NUB-01','ifc:NUB-02','ifc:NUB-03','ifc:NUB-04','ifc:NUB-05','ifc:NUB-06']
1277
- * // (legacy vocabulary, unchanged for back-compat)
1111
+ * // hooks.intent.isAvailable() is true)
1278
1112
  * // caps.sandbox => [] // host app may extend with 'perm:popups', etc.
1279
1113
  * ```
1280
1114
  */
@@ -1738,15 +1572,15 @@ declare function createNotifyProxy(deps: NotifyProxyDeps): NotifyProxy;
1738
1572
 
1739
1573
  /**
1740
1574
  * keys-forwarder.ts — Shell-side host keydown listener that forwards events
1741
- * to registered napplets as `keys.forward` envelopes (Plan 12-11, NUB-05
1575
+ * to registered napplets as `keys.forward` envelopes (Plan 12-11, NAP-05
1742
1576
  * shell-side half).
1743
1577
  *
1744
1578
  * Per `@napplet/nap/keys`, `keys.forward` is fire-and-forget (no result
1745
- * envelope, no correlation id). Field names follow the nub convention:
1579
+ * envelope, no correlation id). Field names follow the nap convention:
1746
1580
  * `{ ctrl, alt, shift, meta }` — NOT the DOM-style `ctrlKey`/etc.
1747
1581
  *
1748
1582
  * Capability gate: only forwards to napplets whose ACL grants the
1749
- * `keys:forward` capability (per Plan 12-10 `resolveCapabilitiesNub` 'keys'
1583
+ * `keys:forward` capability (per Plan 12-10 `resolveCapabilitiesNap` 'keys'
1750
1584
  * case). The caller wires the cap-lookup via the `hasKeysForwardCap` dep so
1751
1585
  * this module stays free of any direct ACL-store dependency.
1752
1586
  *
@@ -1831,63 +1665,6 @@ interface KeysForwarder {
1831
1665
  */
1832
1666
  declare function createKeysForwarder(deps: KeysForwarderDeps): KeysForwarder;
1833
1667
 
1834
- /**
1835
- * @file internal-connect.ts
1836
- *
1837
- * Kehto-internal shell-side connect-store + consent-flow types. Per
1838
- * PROJECT.md Decision #31, this is NOT a staging-ground duplicate of upstream
1839
- * `@napplet/nap/connect`: upstream exports a napplet-side accessor interface
1840
- * (`NappletConnect = { granted, origins }`) plus the shared
1841
- * `normalizeConnectOrigin` pure validator. Kehto's types here describe the
1842
- * shell's grant-store records (`ConnectGrant`, `ConnectGrantKey`,
1843
- * `ConsentResult`), used by the connect-store singleton and the Vite CSP
1844
- * plugin's consent flow.
1845
- *
1846
- * The two surfaces are complementary: upstream's accessor type ships at the
1847
- * napplet boundary; kehto's grant-store types live shell-side. No retirement
1848
- * planned. For canonical origin validation (kehto has no local impl;
1849
- * Decision #32), consume `@napplet/nap/connect`'s `normalizeConnectOrigin`
1850
- * directly.
1851
- *
1852
- * Downstream consumers (Phase 39 `connect-store.ts`, Vite CSP middleware,
1853
- * consent flow UI) import from this module.
1854
- */
1855
- /**
1856
- * Grant key composed from the napplet's dTag and build aggregateHash. A hash
1857
- * upgrade invalidates prior grants (CONNECT-06).
1858
- */
1859
- interface ConnectGrantKey {
1860
- dTag: string;
1861
- aggregateHash: string;
1862
- }
1863
- /**
1864
- * A persisted connect grant. Stored in the shell connect-store singleton,
1865
- * surfaced as the CSP `connect-src` origin list for the corresponding napplet.
1866
- */
1867
- interface ConnectGrant {
1868
- /** Composite key `"<dTag>:<aggregateHash>"` under which this grant is stored. */
1869
- key: string;
1870
- /** The set of origins (WebSocket and HTTPS) the napplet is granted to connect to. */
1871
- origins: readonly string[];
1872
- /** Epoch millis when the grant was approved by the user. */
1873
- grantedAt: number;
1874
- }
1875
- /**
1876
- * Consent flow outcome. `'dismiss'` resolves to deny (MUST NOT default to allow).
1877
- * `'timeout'` also resolves to deny.
1878
- */
1879
- type ConsentResult = 'approve' | 'deny' | 'dismiss' | 'timeout';
1880
- /**
1881
- * Inbound consent request from a napplet requesting connect access to one or
1882
- * more origins.
1883
- */
1884
- interface ConnectConsentRequest {
1885
- dTag: string;
1886
- aggregateHash: string;
1887
- /** Origins the napplet is asking to be granted access to. */
1888
- requestedOrigins: readonly string[];
1889
- }
1890
-
1891
1668
  /**
1892
1669
  * @file internal-resource.ts
1893
1670
  *
@@ -1897,8 +1674,8 @@ interface ConnectConsentRequest {
1897
1674
  * field names (kehto's `requestId` vs upstream `id`; kehto's `bodyBase64` vs
1898
1675
  * upstream `blob` + `mime`), different message-type names
1899
1676
  * (`ResourceBytesRequest` vs `ResourceBytesMessage`), and disjoint error
1900
- * vocabularies (kehto: 5 codes `{denied, canceled, network-error, invalid-url,
1901
- * class-forbidden}`; upstream: 8 codes `{not-found, blocked-by-policy,
1677
+ * vocabularies (kehto: 4 codes `{denied, canceled, network-error, invalid-url}`;
1678
+ * upstream: 8 codes `{not-found, blocked-by-policy,
1902
1679
  * timeout, too-large, unsupported-scheme, decode-failed, network-error,
1903
1680
  * quota-exceeded}`).
1904
1681
  *
@@ -1954,7 +1731,7 @@ interface ResourceBytesResult {
1954
1731
  * Canonical typed-error codes for RESOURCE-03 cancel correlation + H-03
1955
1732
  * ungranted-origin refusal.
1956
1733
  */
1957
- type ResourceErrorCode = 'denied' | 'canceled' | 'network-error' | 'invalid-url' | 'class-forbidden';
1734
+ type ResourceErrorCode = 'denied' | 'canceled' | 'network-error' | 'invalid-url';
1958
1735
  /**
1959
1736
  * Outbound: error result — used for both grant-refusal (RESOURCE-01) and
1960
1737
  * cancel-correlation (RESOURCE-03) cases.
@@ -1974,4 +1751,4 @@ type ResourceInbound = ResourceBytesRequest | ResourceCancelRequest;
1974
1751
  */
1975
1752
  type ResourceOutbound = ResourceBytesResult | ResourceBytesError;
1976
1753
 
1977
- export { type AclCheckEvent, type AclEntry, type AudioSource, type AuthHooks, type BrowserDeps, type ConfigHooks, type ConnectConsentRequest, type ConnectGrant, type ConnectGrantKey, type ConnectStore, type ConsentResult, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysForwarder, type KeysForwarderDeps, type KeysForwarderOriginRegistry, type KeysForwarderSessionRegistry, type KeysProxy, type KeysProxyDeps, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NotifyProxy, type NotifyProxyDeps, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceOutbound, type ResourceRequestId, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ThemeProxy, type ThemeProxyDeps, type UploadBackendLike, type UploadHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildShellCapabilities, connectGrantKey, connectStore, createIdentityProxy, createKeysForwarder, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, manifestCache, nappKeyRegistry, originRegistry, sessionRegistry };
1754
+ export { type AclCheckEvent, type AclEntry, type AudioSource, type AuthHooks, type BrowserDeps, type CapabilityHooks, type ConfigHooks, type CryptoHooks, type DmHooks, type HotkeyHooks, type IdentityProxy, type IdentityProxyDeps, type IntentHooks, type KeysForwarder, type KeysForwarderDeps, type KeysForwarderOriginRegistry, type KeysForwarderSessionRegistry, type KeysProxy, type KeysProxyDeps, type ManifestCacheEntry, type MediaProxy, type MediaProxyDeps, type NotifyProxy, type NotifyProxyDeps, type ProxyOriginRegistry, type RelayConfigHooks, type RelayPoolHooks, type RelayPoolLike, type ResourceBytesError, type ResourceBytesRequest, type ResourceBytesResult, type ResourceCancelRequest, type ResourceErrorCode, type ResourceInbound, type ResourceOutbound, type ResourceRequestId, type ShellAdapter, type ShellBridge, type ShellCapabilities, type ThemeProxy, type ThemeProxyDeps, type UnroutedMessageInfo, type UploadBackendLike, type UploadHooks, type WindowManagerHooks, type WorkerRelayHooks, type WorkerRelayLike, adaptHooks, audioManager, buildShellCapabilities, createIdentityProxy, createKeysForwarder, createKeysProxy, createMediaProxy, createNotifyProxy, createShellBridge, createThemeProxy, manifestCache, nappKeyRegistry, originRegistry, sessionRegistry };