@kehto/shell 0.16.7 → 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
@@ -1,6 +1,6 @@
1
1
  # @kehto/shell
2
2
 
3
- Browser adapter over @kehto/runtime — ShellBridge, domain proxies, keys-forwarder.
3
+ Browser adapter over @kehto/runtime — ShellBridge and domain proxies.
4
4
 
5
5
  > **Alpha status:** Kehto is an early runtime implementation for a draft NIP-5D
6
6
  > protocol. NAP contracts and injected-domain behavior are still draft; treat
@@ -12,6 +12,13 @@ Browser adapter over @kehto/runtime — ShellBridge, domain proxies, keys-forwar
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 current draft NIP-5D injected-domain bootstrap: hosts prepend a `window.napplet` prelude to `srcdoc` outside the verified artifact bytes, so callable NAP domain interfaces exist before napplet-authored scripts run. Assigned namespaces are filtered back to the explicit bare-domain allowlist; the playground does not inject `shell`.
25
- - Legacy `window.napplet.shell.supports()` compatibility is not the current NIP-5D availability primitive. New host and napplet availability checks should use injected `window.napplet.<domain>` presence and leave per-domain semantic checks to the matching NAP.
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.
27
- - The keys-forwarder pumps host keydown events into `keys.forward` envelopes for napplets that hold the `keys:forward` capability.
28
- - `buildShellCapabilities()` advertises `dm` when the host adapter provides `hooks.dm`, and disabled-domain overrides remove it from both `domains` and `naps`.
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.
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`.
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,24 +128,42 @@ 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
80
- - `createNotifyProxy` — intercept `notify.send/list/read/dismiss`
81
141
 
82
- ### Keys forwarder
83
- - `createKeysForwarder` — host-keydown pump into `keys.forward` envelopes; auto-attached by `createShellBridge`, also exported for hosts that manage their own forwarder instance
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.
161
+ - `createNotifyProxy` — intercept `notify.send/list/read/dismiss`
84
162
 
85
163
  ### Session / origin registry
86
164
  - `sessionRegistry` — canonical windowId ↔ verified-napplet registry singleton
87
165
  - `nappKeyRegistry` — deprecated alias for `sessionRegistry`
88
- - `originRegistry` — origin-to-windowId map used by proxies and the keys-forwarder
166
+ - `originRegistry` — origin-to-windowId map used by proxies and bridge broadcasts
89
167
  - `PendingUpdate` — type for pending aggregate-hash change prompts
90
168
 
91
169
  ### Manifest cache
@@ -101,7 +179,13 @@ const bridge = createShellBridge({
101
179
  - `TopicKey`, `TopicValue` — typed topic lookup helpers
102
180
 
103
181
  ### Types
104
- 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.
105
189
 
106
190
  ### Enforcement re-exports (from @kehto/runtime)
107
191
  `createEnforceGate`, `createNapEnforceGate`, `formatDenialReason`, plus `EnforceResult`, `EnforceConfig`, `NapEnforceConfig`, `IdentityResolver`, `AclChecker`, `NapMessage`.