@kehto/shell 0.11.1 → 0.12.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';
@@ -188,36 +188,51 @@ interface AclCheckEvent {
188
188
  /** The triggering message, if available. Accepts NIP-01 arrays or NIP-5D NappletMessage envelopes. */
189
189
  message?: NappletMessage | unknown[];
190
190
  }
191
+ /**
192
+ * Diagnostic payload reported when {@link ShellAdapter.onUnroutedMessage} fires —
193
+ * i.e. when `ShellBridge.handleMessage` drops an incoming postMessage because it
194
+ * cannot be routed to a registered napplet window.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * hooks.onUnroutedMessage = (info: UnroutedMessageInfo) => {
199
+ * console.warn(`[shell] dropped ${info.type ?? '<unknown>'} from ${info.origin}: ${info.reason}`);
200
+ * };
201
+ * ```
202
+ */
203
+ interface UnroutedMessageInfo {
204
+ /**
205
+ * The dropped message's `type` field when `event.data` is an object with a
206
+ * string `type`; `undefined` for malformed/non-envelope payloads.
207
+ */
208
+ type?: string;
209
+ /** The `MessageEvent.origin` of the dropped message. */
210
+ origin: string;
211
+ /**
212
+ * Why the message was dropped:
213
+ * - `no-source-window` — the `MessageEvent` had no `source` window to identify the sender.
214
+ * - `unregistered-window` — the source `Window` is not in `originRegistry` (the
215
+ * sending iframe was never registered, or a `srcdoc` reload swapped its
216
+ * `contentWindow` to a new object that no longer matches the registry key).
217
+ */
218
+ reason: 'no-source-window' | 'unregistered-window';
219
+ }
191
220
  /**
192
221
  * Static capability set sent to napplet iframes through the shell.ready /
193
222
  * shell.init handshake. Used by hosted `window.napplet.shell.supports()` for
194
223
  * synchronous capability queries after the shim consumes shell.init.
195
224
  *
196
- * Per canonical NIP-5D (specs/NIP-5D.md lines 81-94), supports() distinguishes
197
- * two namespaces:
225
+ * Per canonical NIP-5D (https://github.com/nostr-protocol/nips/pull/2303/),
226
+ * supports() distinguishes two namespaces:
198
227
  *
199
- * - Bare names (or optional `nub:` prefix) for NUB-capability lookups,
200
- * resolved against the `nubs` array — e.g. `supports('relay')`,
201
- * `supports('identity')`.
228
+ * - Bare names for NAP-capability lookups, resolved against the `naps`
229
+ * array — e.g. `supports('relay')`, `supports('identity')`.
202
230
  * - The `perm:<permission>` prefix for sandbox-permission lookups, resolved
203
231
  * against the `sandbox` array — e.g. `supports('perm:popups')`,
204
232
  * `supports('perm:modals')`.
205
233
  *
206
234
  * 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.
235
+ * entry and a `perm:`-prefixed lookup never matches a NAP entry.
221
236
  *
222
237
  * ## Conformant NAP-SHELL shape (TERM-03 — @napplet/core@0.12 / @napplet/shim@0.13)
223
238
  *
@@ -230,8 +245,8 @@ interface AclCheckEvent {
230
245
  * - `supports('inc','NAP-01')`→ true iff `capabilities.protocols['inc']`
231
246
  * includes `'NAP-01'`
232
247
  *
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
248
+ * `domains` and `protocols` are emitted as a SUPERSET ALONGSIDE the
249
+ * `naps`/`sandbox` fields (TERM-05 back-compat). The 0.13 shim has NO
235
250
  * special `perm:` logic — `supports('perm:popups')` is an ordinary bare-domain
236
251
  * membership check, so kehto's `perm:`-prefixed sandbox entries are folded into
237
252
  * `domains` (empty by default, preserving the default-empty sandbox behavior).
@@ -262,38 +277,25 @@ interface ShellCapabilities {
262
277
  protocols: Record<string, string[]>;
263
278
  /**
264
279
  * NAP-vocabulary domain entries the shell handles (PRIMARY — consumed by
265
- * `@napplet/shim >=0.9.0`). Bare domain `inc` (the NAP rename of `ifc`)
280
+ * `@napplet/shim >=0.9.0`). Bare domain `inc` (the NAP rename of `inc`)
266
281
  * plus protocol IDs `inc:NAP-01..inc:NAP-06`. Conditional entries:
267
282
  * `relay`, `outbox` prepended when a relay pool is wired; `upload` appended
268
283
  * when an upload backend is wired; `intent` appended when an intent
269
284
  * dispatcher is available.
270
285
  *
271
- * Contains NO `ifc`, `NUB-NN`, or unaliased legacy identifiers.
286
+ * Contains NO `NAP-NN` protocol strings (those live in `protocols`).
272
287
  */
273
288
  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
289
  /**
288
290
  * Sandbox permissions under the `perm:<permission>` namespace. Each entry
289
291
  * MUST begin with the literal prefix `'perm:'` — e.g. `'perm:popups'`,
290
292
  * `'perm:modals'`, `'perm:downloads'`. Napplets call
291
293
  * `shell.supports('perm:<permission>')` to check sandbox entitlements.
292
294
  *
293
- * The `perm:` prefix is what separates sandbox permissions from NUB
295
+ * The `perm:` prefix is what separates sandbox permissions from NAP
294
296
  * 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
297
+ * be unreachable through `supports()` (see the living NIP-5D at
298
+ * https://github.com/nostr-protocol/nips/pull/2303/). NAP-capability lookups (on `naps`) retain the bare-name
297
299
  * convention and do NOT use the `perm:` prefix.
298
300
  */
299
301
  sandbox: string[];
@@ -339,22 +341,24 @@ interface ShellAdapter {
339
341
  intent?: IntentHooks;
340
342
  /** Called on every ACL enforcement check. Both allows and denials are reported. */
341
343
  onAclCheck?: (event: AclCheckEvent) => void;
344
+ /**
345
+ * Called when `ShellBridge.handleMessage` drops an incoming postMessage because
346
+ * it cannot be routed to a registered napplet window (no source window, or the
347
+ * source `Window` is not in `originRegistry`). Observe-only — the message is
348
+ * still dropped; this hook exists so otherwise-silent drops are diagnosable
349
+ * (the FEED-02 / hyprgate#21 class of "a napplet's messages vanish" bug).
350
+ */
351
+ onUnroutedMessage?: (info: UnroutedMessageInfo) => void;
342
352
  /** Called when aggregate hash verification fails (computed != declared). */
343
353
  onHashMismatch?: (dTag: string, claimed: string, computed: string) => void;
344
354
  /**
345
355
  * 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.
356
+ * Returns identity metadata for originRegistry.register(). Returning null
357
+ * means "not NIP-5D / skip registration".
353
358
  */
354
359
  onNip5dIframeCreate?: (windowId: string) => {
355
360
  dTag: string;
356
361
  aggregateHash: string;
357
- class: NappletClass;
358
362
  } | null;
359
363
  /**
360
364
  * Optional service extensions. Each key is a service name (e.g., 'audio',
@@ -381,146 +385,6 @@ interface ShellAdapter {
381
385
  getConfigOverrides?(): RuntimeConfigOverrides;
382
386
  }
383
387
 
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
388
  /**
525
389
  * Shell-side message bridge that handles NIP-5D communication with napplet iframes.
526
390
  *
@@ -548,7 +412,7 @@ interface ShellBridge {
548
412
  *
549
413
  * shell.ready messages are handled locally: the bridge responds with shell.init
550
414
  * containing the capability set and registered service list. All other envelopes
551
- * are delegated to the runtime's NUB domain dispatch.
415
+ * are delegated to the runtime's NAP domain dispatch.
552
416
  *
553
417
  * @param event - The raw MessageEvent from window.addEventListener('message', ...)
554
418
  * @example
@@ -559,7 +423,7 @@ interface ShellBridge {
559
423
  handleMessage(event: MessageEvent): void;
560
424
  /**
561
425
  * Inject a shell-originated event into subscription delivery. Under NIP-5D,
562
- * shell-originated events are forwarded to napplets as ifc.event envelope
426
+ * shell-originated events are forwarded to napplets as inc.event envelope
563
427
  * messages. The runtime's injectEvent() handles the per-session routing.
564
428
  *
565
429
  * v1.10 hard-removed the v1.8 soft-rename compatibility branch for the
@@ -640,45 +504,7 @@ interface ShellBridge {
640
504
  * and manifestCache.
641
505
  */
642
506
  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
507
  }
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
508
  declare function createShellBridge(hooks: ShellAdapter): ShellBridge;
683
509
 
684
510
  /**
@@ -1221,27 +1047,23 @@ declare function adaptHooks(shellHooks: ShellAdapter, deps: BrowserDeps): Runtim
1221
1047
  /**
1222
1048
  * Build the shell's static capability set from adapter configuration.
1223
1049
  *
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.
1050
+ * Returns the conformant NAP-SHELL `domains`/`protocols` shape (consumed by
1051
+ * `@napplet/shim >=0.13` via `@napplet/nap@0.12`'s `createShellEnvironment` +
1052
+ * `makeSupports`) alongside the flat `naps` array (consumed by
1053
+ * `@napplet/shim >=0.9.0`) and the `sandbox` array.
1228
1054
  *
1229
1055
  * ### naps array (NAP vocabulary)
1230
- * Bare domain `inc` (rename of `ifc`) + `inc:NAP-01..inc:NAP-06`.
1056
+ * Bare domain `inc` + `inc:NAP-01..inc:NAP-06`.
1231
1057
  * Conditional: `relay`+`outbox` prepended when hooks.relayPool;
1232
1058
  * `upload` appended when hooks.upload;
1233
1059
  * `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
1060
  *
1240
1061
  * Sandbox permissions are left empty by default — host apps may extend after
1241
1062
  * construction. Sandbox entries (and any host-app extensions) MUST use the
1242
1063
  * canonical `perm:<permission>` form — e.g. `'perm:popups'`, `'perm:modals'`,
1243
1064
  * `'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.
1065
+ * sandbox permissions from NAP-capability lookups; see the living NIP-5D at
1066
+ * https://github.com/nostr-protocol/nips/pull/2303/
1245
1067
  *
1246
1068
  * ### domains array + protocols map (conformant NAP-SHELL — @napplet/core@0.12)
1247
1069
  * The structured shape the released `@napplet/shim@0.13` actually reads via
@@ -1255,26 +1077,23 @@ declare function adaptHooks(shellHooks: ShellAdapter, deps: BrowserDeps): Runtim
1255
1077
  * - `protocols` — `{ inc: ['NAP-01'..'NAP-06'] }`, derived from
1256
1078
  * `NAP_INC_PROTOCOLS` by stripping the `inc:` prefix.
1257
1079
  *
1258
- * Emitted as a SUPERSET alongside `naps`/`nubs`/`sandbox` (TERM-05 back-compat).
1080
+ * Emitted as a SUPERSET alongside `naps`/`sandbox` (TERM-05 back-compat).
1259
1081
  *
1260
1082
  * @param hooks - The ShellAdapter provided by the host app
1261
1083
  * @returns ShellCapabilities with domains/protocols (conformant 0.13 shape) plus
1262
- * naps (NAP vocab), nubs (legacy vocab), and sandbox (perm:-prefixed) arrays
1084
+ * naps (NAP vocab) and sandbox (perm:-prefixed) arrays
1263
1085
  * @example
1264
1086
  * ```ts
1265
1087
  * const caps = buildShellCapabilities(hooks);
1266
- * // caps.domains => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','connect','class','cvm']
1088
+ * // caps.domains => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','cvm']
1267
1089
  * // (relay + outbox present when hooks.relayPool is provided; 'upload'/'intent'
1268
1090
  * // appended under their hooks; perm:<x> sandbox entries appended when extended)
1269
1091
  * // 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',
1092
+ * // caps.naps => ['relay','outbox','identity','storage','inc','theme','keys','media','notify','config','resource','cvm',
1271
1093
  * // 'inc:NAP-01','inc:NAP-02','inc:NAP-03','inc:NAP-04','inc:NAP-05','inc:NAP-06']
1272
1094
  * // (relay + outbox present when hooks.relayPool is provided; 'upload'
1273
1095
  * // 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)
1096
+ * // hooks.intent.isAvailable() is true)
1278
1097
  * // caps.sandbox => [] // host app may extend with 'perm:popups', etc.
1279
1098
  * ```
1280
1099
  */
@@ -1738,15 +1557,15 @@ declare function createNotifyProxy(deps: NotifyProxyDeps): NotifyProxy;
1738
1557
 
1739
1558
  /**
1740
1559
  * keys-forwarder.ts — Shell-side host keydown listener that forwards events
1741
- * to registered napplets as `keys.forward` envelopes (Plan 12-11, NUB-05
1560
+ * to registered napplets as `keys.forward` envelopes (Plan 12-11, NAP-05
1742
1561
  * shell-side half).
1743
1562
  *
1744
1563
  * Per `@napplet/nap/keys`, `keys.forward` is fire-and-forget (no result
1745
- * envelope, no correlation id). Field names follow the nub convention:
1564
+ * envelope, no correlation id). Field names follow the nap convention:
1746
1565
  * `{ ctrl, alt, shift, meta }` — NOT the DOM-style `ctrlKey`/etc.
1747
1566
  *
1748
1567
  * Capability gate: only forwards to napplets whose ACL grants the
1749
- * `keys:forward` capability (per Plan 12-10 `resolveCapabilitiesNub` 'keys'
1568
+ * `keys:forward` capability (per Plan 12-10 `resolveCapabilitiesNap` 'keys'
1750
1569
  * case). The caller wires the cap-lookup via the `hasKeysForwardCap` dep so
1751
1570
  * this module stays free of any direct ACL-store dependency.
1752
1571
  *
@@ -1831,63 +1650,6 @@ interface KeysForwarder {
1831
1650
  */
1832
1651
  declare function createKeysForwarder(deps: KeysForwarderDeps): KeysForwarder;
1833
1652
 
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
1653
  /**
1892
1654
  * @file internal-resource.ts
1893
1655
  *
@@ -1897,8 +1659,8 @@ interface ConnectConsentRequest {
1897
1659
  * field names (kehto's `requestId` vs upstream `id`; kehto's `bodyBase64` vs
1898
1660
  * upstream `blob` + `mime`), different message-type names
1899
1661
  * (`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,
1662
+ * vocabularies (kehto: 4 codes `{denied, canceled, network-error, invalid-url}`;
1663
+ * upstream: 8 codes `{not-found, blocked-by-policy,
1902
1664
  * timeout, too-large, unsupported-scheme, decode-failed, network-error,
1903
1665
  * quota-exceeded}`).
1904
1666
  *
@@ -1954,7 +1716,7 @@ interface ResourceBytesResult {
1954
1716
  * Canonical typed-error codes for RESOURCE-03 cancel correlation + H-03
1955
1717
  * ungranted-origin refusal.
1956
1718
  */
1957
- type ResourceErrorCode = 'denied' | 'canceled' | 'network-error' | 'invalid-url' | 'class-forbidden';
1719
+ type ResourceErrorCode = 'denied' | 'canceled' | 'network-error' | 'invalid-url';
1958
1720
  /**
1959
1721
  * Outbound: error result — used for both grant-refusal (RESOURCE-01) and
1960
1722
  * cancel-correlation (RESOURCE-03) cases.
@@ -1974,4 +1736,4 @@ type ResourceInbound = ResourceBytesRequest | ResourceCancelRequest;
1974
1736
  */
1975
1737
  type ResourceOutbound = ResourceBytesResult | ResourceBytesError;
1976
1738
 
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 };
1739
+ export { type AclCheckEvent, type AclEntry, type AudioSource, type AuthHooks, type BrowserDeps, 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 };