@kehto/shell 0.17.2 → 0.19.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 +94 -9
- package/dist/index.d.ts +110 -182
- package/dist/index.js +519 -177
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
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.31.0 <0.32.0`. The installed core/nap 0.31.0 contracts are from source
|
|
19
|
+
`7b675622e13870628ce174833d7b2a33cf32a0ab` and release
|
|
20
|
+
`03ad65b66413e5798536ef48695ffc4c2508f2c3`.
|
|
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,62 @@ 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
|
|
26
|
-
- Five optional per-domain proxies — `createIdentityProxy`, `createThemeProxy`, `createKeysProxy`, `createMediaProxy`, `createNotifyProxy` — can be composed between napplet and runtime to intercept
|
|
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.31.0 and shim 0.29.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
|
|
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/5ac0490461ca6fec2f0d2e45b4835cf9bc08de24/naps/NAP-INC.md)
|
|
41
|
+
on `napplet/naps` master
|
|
42
|
+
`5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`. 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 and released `@napplet/nap@0.31.0` both deliver one `IncEvent`
|
|
56
|
+
to `on(topic, callback)`.
|
|
57
|
+
|
|
58
|
+
For channels, runtime ACL checks are open-only rather than per-message. The
|
|
59
|
+
target `inc.channel.opened` before the opener result creates an equivalent target
|
|
60
|
+
handle; `channel.onOpened` exposes it, while symmetric handles expose `on` and
|
|
61
|
+
`onClosed`. The binding retains inbound, early, and terminal lifecycle state in
|
|
62
|
+
order, closes on bounded overflow, and treats teardown as deterministic.
|
|
63
|
+
`channel.list()` is informational only. The downstream tracker is
|
|
64
|
+
[`kehto/web#203`](https://github.com/kehto/web/issues/203), with its [upstream
|
|
65
|
+
resolution reply](https://github.com/kehto/web/issues/203#issuecomment-5060904495);
|
|
66
|
+
the prior opener-only interpretation is obsolete.
|
|
67
|
+
|
|
68
|
+
### NAP-INTENT binding and delivery
|
|
69
|
+
|
|
70
|
+
Kehto implements merged [NAP-INTENT at
|
|
71
|
+
`5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`](https://github.com/napplet/naps/blob/5ac0490461ca6fec2f0d2e45b4835cf9bc08de24/naps/NAP-INTENT.md).
|
|
72
|
+
The protected `window.napplet.intent` binding accepts structured
|
|
73
|
+
`invoke(request)` calls and `open(archetype, payload?, opts?)`. It defaults
|
|
74
|
+
`action` to `open`, preserves an optional queryless convention, rejects
|
|
75
|
+
unsupported fields and caller-supplied sender data, and resolves only
|
|
76
|
+
parent-originated correlated results.
|
|
77
|
+
|
|
78
|
+
Successful results include `handled`, `handler`, `windowId`, and `convention`
|
|
79
|
+
after target dispatch completes. There is no `intent.deliver` or `onDelivery`;
|
|
80
|
+
selected targets consume their convention through the ordinary
|
|
81
|
+
runtime-attested INC binding.
|
|
82
|
+
|
|
83
|
+
Phase 105 completed released `@napplet/*` package adoption and persistent live
|
|
84
|
+
Paja/playground catalogs and target controllers. The Phase 104 Paja simulator
|
|
85
|
+
and playground catalog builder are historical exact-contract consumers; preserve
|
|
86
|
+
archived planning rather than presenting it as active guidance.
|
|
29
87
|
|
|
30
88
|
## Quick Start
|
|
31
89
|
|
|
@@ -41,7 +99,7 @@ const bridge = createShellBridge({
|
|
|
41
99
|
|
|
42
100
|
// Register a reference service against the underlying runtime.
|
|
43
101
|
bridge.runtime.registerService(
|
|
44
|
-
'
|
|
102
|
+
'notify',
|
|
45
103
|
createNotificationService({ onChange: updateBadge }),
|
|
46
104
|
);
|
|
47
105
|
```
|
|
@@ -68,15 +126,36 @@ const bridge = createShellBridge({
|
|
|
68
126
|
```
|
|
69
127
|
|
|
70
128
|
### Shell init
|
|
71
|
-
- `buildShellCapabilities` — construct the
|
|
129
|
+
- `buildShellCapabilities` — construct the immutable domain-only `ShellCapabilities` payload emitted during the `shell.ready` / `shell.init` handshake
|
|
130
|
+
- `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
131
|
- `injectNappletNamespacePrelude` — insert a host-owned NIP-5D `window.napplet` callable-domain prelude into verified HTML before authored scripts
|
|
73
132
|
- `renderNappletNamespacePrelude` — render only the bootstrap `<script>` for hosts that already own HTML insertion
|
|
74
133
|
|
|
75
134
|
### Domain proxies (NIP-5D composition seams)
|
|
76
|
-
- `createIdentityProxy` — intercept
|
|
77
|
-
- `createThemeProxy` — intercept `theme.get
|
|
135
|
+
- `createIdentityProxy` — intercept identity read requests; direct `emit()` is prohibited
|
|
136
|
+
- `createThemeProxy` — intercept `theme.get`; direct `emit()` is prohibited
|
|
78
137
|
- `createKeysProxy` — intercept `keys.bind/unbind/bindings`
|
|
79
138
|
- `createMediaProxy` — intercept `media.*` playback control
|
|
139
|
+
|
|
140
|
+
### Protected identity and theme delivery
|
|
141
|
+
|
|
142
|
+
The injected identity/theme bindings follow NAP-IDENTITY and NAP-THEME at
|
|
143
|
+
`napplet/naps` master `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`.
|
|
144
|
+
They are readonly protected objects: identity exposes supported reads and
|
|
145
|
+
`onChanged`, while theme exposes `get` and `onChanged` only. Results and
|
|
146
|
+
automatic changes are accepted only when their `MessageEvent.source` is
|
|
147
|
+
`window.parent`; direct-domain and whole-namespace assignment cannot replace
|
|
148
|
+
the canonical operations.
|
|
149
|
+
|
|
150
|
+
`ShellBridge` delivers `identity.changed` and `theme.changed` exactly once per
|
|
151
|
+
eligible recipient: a live authenticated `shell.ready` session, its frozen
|
|
152
|
+
environment includes the relevant domain, and its current recipient read
|
|
153
|
+
capability is still granted. Identity sign-out is `pubkey: ""`. Theme updates
|
|
154
|
+
arrive only after the service has stored the complete color state. These are
|
|
155
|
+
automatic change messages, not NAP-INC/intent delivery and not a subscription
|
|
156
|
+
protocol. Kehto's denied/unavailable theme read policy is a complete fixed
|
|
157
|
+
normal result without `error`, deliberately avoiding a mixed theme/error
|
|
158
|
+
payload.
|
|
80
159
|
- `createNotifyProxy` — intercept `notify.send/list/read/dismiss`
|
|
81
160
|
|
|
82
161
|
### Session / origin registry
|
|
@@ -98,7 +177,13 @@ const bridge = createShellBridge({
|
|
|
98
177
|
- `TopicKey`, `TopicValue` — typed topic lookup helpers
|
|
99
178
|
|
|
100
179
|
### 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.
|
|
180
|
+
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.
|
|
181
|
+
|
|
182
|
+
`RelayPoolLike.publish()` may return `Promise<void>` when transport acceptance
|
|
183
|
+
is asynchronous. The shell adapter forwards that promise so the runtime does
|
|
184
|
+
not acknowledge or buffer an event before the relay operation settles.
|
|
185
|
+
`RelayPoolHooks.publishToScopedRelay()` may likewise return
|
|
186
|
+
`Promise<boolean>` so asynchronous hosts report the settled outcome.
|
|
102
187
|
|
|
103
188
|
### Enforcement re-exports (from @kehto/runtime)
|
|
104
189
|
`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
|
-
/**
|
|
46
|
-
|
|
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
|
|
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
|
-
*
|
|
270
|
-
*
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
540
|
+
* Publish a theme update to each eligible napplet.
|
|
536
541
|
*
|
|
537
542
|
* Posts a `theme.changed` envelope (shell → napplet push) to every
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
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
|
|
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
|
|
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','
|
|
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
|
|
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
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
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
|
-
*
|
|
967
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
988
|
-
*
|
|
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
|
-
*
|
|
1026
|
-
*
|
|
1027
|
-
*
|
|
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
|
-
*
|
|
1058
|
-
*
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
1077
|
-
*
|
|
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
|
|
1021
|
+
* import { createThemeProxy, originRegistry } from '@kehto/shell';
|
|
1089
1022
|
*
|
|
1090
|
-
* const bridge = createShellBridge(hooks);
|
|
1091
1023
|
* const themeProxy = createThemeProxy({
|
|
1092
|
-
* 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 };
|