@kehto/runtime 0.20.1 → 0.22.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
@@ -15,9 +15,11 @@ pnpm add @kehto/runtime
15
15
  ## Published Napplet Compatibility
16
16
 
17
17
  `@kehto/runtime` publishes against `@napplet/core` and `@napplet/nap`
18
- `>=0.31.0 <0.32.0`. The exact installed convention contracts are core/nap
19
- 0.31.0 from source `7b675622e13870628ce174833d7b2a33cf32a0ab` and release
20
- `03ad65b66413e5798536ef48695ffc4c2508f2c3`.
18
+ `>=0.31.0 <0.32.0`. The exact installed convention contracts are core 0.31.1 /
19
+ nap 0.31.2 from NAP-INTENT authority `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`,
20
+ napplet/web#199 source `3037200c932488f14f7f369b8583c39c9c16510a` / merge
21
+ `b3f0007867eac109fa4917fac9c285d3b7cc6155`, and Version Packages #198 release
22
+ source `dc1d24153c759152b6ba31a6ec9bea967798f2df`.
21
23
 
22
24
  ## Overview
23
25
 
@@ -32,6 +34,7 @@ The runtime is built around the current draft dispatch contract from `@napplet/c
32
34
  - **notify** — `notify.send`, `notify.channel.register`, badge/permission flows
33
35
  - **relay** — `relay.publish`, `relay.publishEncrypted`, `relay.subscribe`
34
36
  - **dm** — `dm.status`, `dm.conversations`, `dm.messages`, `dm.send`, runtime service dispatch
37
+ - **fs** — `fs.info`, pickers, metadata, byte I/O, mutations, watches, runtime service dispatch
35
38
  - **storage** — `storage.get/set/remove/keys` with quota enforcement
36
39
  - **theme** — `theme.get` and automatic recipient-authorized `theme.changed`
37
40
 
@@ -48,7 +51,7 @@ the relay backend. Success returns exactly one correlated
48
51
  Signing, replay, relay, and service failures return `ok: false` plus `error`
49
52
  and are never buffered as successful local publications.
50
53
 
51
- The released `@napplet/nap@0.31.0` SDK accepts `EventTemplate`, while its
54
+ The released `@napplet/nap@0.31.2` SDK accepts `EventTemplate`, while its
52
55
  `RelayPublishMessage.event` declaration still says `NostrEvent`; Kehto records
53
56
  that mismatch as upstream package drift and follows the NAP wire direction.
54
57
 
@@ -197,7 +200,7 @@ runtime.handleMessage('window-1', {
197
200
  ### Service dispatch
198
201
  - `routeServiceMessage` — domain-prefix router into the service registry
199
202
  - `notifyServiceWindowDestroyed` — lifecycle fan-out to every service handler
200
- - Service-only NAP domains, including `dm`, route through handlers registered with `runtime.registerService('<domain>', handler)`.
203
+ - Service-only NAP domains, including `dm` and `fs`, route through handlers registered with `runtime.registerService('<domain>', handler)`.
201
204
 
202
205
  ### Types
203
206
  40+ interfaces — including `Runtime`, `RuntimeAdapter`, `SendToNapplet`, `RelayPoolAdapter`, `ServiceHandler`, `ServiceRegistry`, `NappletMessage`, `SessionEntry`, `AclEntryExternal`, `AclCheckEvent`, and the per-adapter hook types — are exported from `./types.js` for host-app integration.
package/dist/index.d.ts CHANGED
@@ -548,6 +548,14 @@ interface ServiceRuntimeContext {
548
548
  * @returns A fresh frozen snapshot of live window identifiers.
549
549
  */
550
550
  listWindowIds(): readonly string[];
551
+ /**
552
+ * Check a capability against the current source-bound ACL grant.
553
+ *
554
+ * @param windowId - Requesting live window.
555
+ * @param capability - Capability to check for that source.
556
+ * @returns `true` when the live source currently holds the grant.
557
+ */
558
+ hasCapability(windowId: string, capability: Capability): boolean;
551
559
  /**
552
560
  * Send a runtime-originated message after current recipient policy checks.
553
561
  *
package/dist/index.js CHANGED
@@ -147,6 +147,8 @@ var CAP_INTENT_READ = 1 << 21;
147
147
  var CAP_INTENT_WRITE = 1 << 22;
148
148
  var CAP_DM_READ = 1 << 23;
149
149
  var CAP_DM_WRITE = 1 << 24;
150
+ var CAP_FS_READ = 1 << 25;
151
+ var CAP_FS_WRITE = 1 << 26;
150
152
  var CAP_MAP = {
151
153
  "relay:read": CAP_RELAY_READ,
152
154
  "relay:write": CAP_RELAY_WRITE,
@@ -171,7 +173,9 @@ var CAP_MAP = {
171
173
  "intent:read": CAP_INTENT_READ,
172
174
  "intent:write": CAP_INTENT_WRITE,
173
175
  "dm:read": CAP_DM_READ,
174
- "dm:write": CAP_DM_WRITE
176
+ "dm:write": CAP_DM_WRITE,
177
+ "fs:read": CAP_FS_READ,
178
+ "fs:write": CAP_FS_WRITE
175
179
  };
176
180
  var RUNTIME_CAP_ALL = Object.values(CAP_MAP).reduce((bits, bit) => bits | bit, 0);
177
181
  function capToBit(cap) {
@@ -1625,7 +1629,8 @@ function createRuntimeDomainHandlers(context) {
1625
1629
  serial: (windowId, msg) => handleServiceOnlyMessage(context, "serial", windowId, msg),
1626
1630
  ble: (windowId, msg) => handleServiceOnlyMessage(context, "ble", windowId, msg),
1627
1631
  webrtc: (windowId, msg) => handleServiceOnlyMessage(context, "webrtc", windowId, msg),
1628
- dm: (windowId, msg) => handleServiceOnlyMessage(context, "dm", windowId, msg)
1632
+ dm: (windowId, msg) => handleServiceOnlyMessage(context, "dm", windowId, msg),
1633
+ fs: (windowId, msg) => handleServiceOnlyMessage(context, "fs", windowId, msg)
1629
1634
  };
1630
1635
  }
1631
1636
  function handleStorageMessage(context, windowId, msg) {
@@ -1779,6 +1784,7 @@ function createNapEnvelopeDispatcher(handlers) {
1779
1784
  napDispatch.registerNap("ble", adapt(handlers.ble));
1780
1785
  napDispatch.registerNap("webrtc", adapt(handlers.webrtc));
1781
1786
  napDispatch.registerNap("dm", adapt(handlers.dm));
1787
+ napDispatch.registerNap("fs", adapt(handlers.fs));
1782
1788
  return (windowId, envelope) => {
1783
1789
  currentWindowId = windowId;
1784
1790
  try {
@@ -1963,6 +1969,10 @@ function createRuntimeInstance(context) {
1963
1969
  sessionRegistry.getAllEntries().map((entry) => entry.windowId)
1964
1970
  );
1965
1971
  },
1972
+ hasCapability(windowId, capability) {
1973
+ const entry = sessionRegistry.getEntryByWindowId(windowId);
1974
+ return !!entry && aclState.check("", entry.dTag, entry.aggregateHash, capability);
1975
+ },
1966
1976
  sendToEligibleNapplet(windowId, message) {
1967
1977
  const entry = sessionRegistry.getEntryByWindowId(windowId);
1968
1978
  if (!entry || typeof message.type !== "string") return false;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/enforce.ts","../src/session-registry.ts","../src/acl-state.ts","../src/firewall-state.ts","../src/manifest-cache.ts","../src/replay.ts","../src/event-buffer.ts","../src/relay-result.ts","../src/runtime.ts","../src/service-dispatch.ts","../src/relay-handler.ts","../src/domain-results.ts","../src/identity-handler.ts","../src/count-handler.ts","../src/inc-handler.ts","../src/state-handler.ts","../src/domain-handlers.ts","../src/index.ts"],"sourcesContent":["\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { NapMessage } from '@kehto/acl';\nimport type { AclCheckEvent } from './types.js';\n\n// Re-export NAP capability resolution for consumers who import through enforce.ts\nexport { resolveCapabilitiesNap } from '@kehto/acl';\nexport type { NapMessage } from '@kehto/acl';\n\n/**\n * Result of an enforcement check.\n *\n * @param allowed - Whether the capability check passed\n * @param capability - The capability that was checked (human-readable string)\n * @param reason - Why the decision was reached (v1.7 CLASS-03 / D7). Always set on return.\n */\nexport interface EnforceResult {\n allowed: boolean;\n capability: Capability;\n /**\n * Why the decision was reached. Always set on the return path.\n * Distinct from AclCheckEvent.reason (which is optional for backwards compat).\n */\n reason: 'allowed' | 'capability-missing';\n}\n\n/**\n * Identity lookup function type — resolves a pubkey to its full identity.\n * Provided by sessionRegistry at runtime.\n */\nexport type IdentityResolver = (pubkey: string) => { dTag: string; aggregateHash: string } | undefined;\n\n/**\n * ACL check function type — performs the actual capability check.\n * Provided by @kehto/acl's check() at runtime, or by the legacy aclStore.check().\n */\nexport type AclChecker = (pubkey: string, dTag: string, aggregateHash: string, capability: Capability) => boolean;\n\n/**\n * Enforcement gate configuration.\n *\n * @param checkAcl - The ACL check function (wraps @kehto/acl or legacy aclStore)\n * @param resolveIdentity - Maps pubkey to full identity (dTag, aggregateHash)\n * @param onAclCheck - Optional audit callback. Called on every enforce() check\n * with the identity, capability, and decision.\n */\nexport interface EnforceConfig {\n checkAcl: AclChecker;\n resolveIdentity: IdentityResolver;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate with the given configuration.\n *\n * Returns a function that checks a single capability for a given pubkey.\n * Every call is logged to the audit callback.\n *\n * @param config - Enforcement configuration with ACL checker, identity resolver, and audit hooks\n * @returns An enforce function that checks capabilities and logs decisions\n *\n * @example\n * ```ts\n * const gate = createEnforceGate({\n * checkAcl: aclStore.check,\n * resolveIdentity: (pk) => nappKeyRegistry.getEntry(pk),\n * onAclCheck: hooks.onAclCheck,\n * });\n * const result = gate('abc123...', 'relay:write');\n * // result.allowed === true | false\n * ```\n */\nexport function createEnforceGate(config: EnforceConfig): (pubkey: string, capability: Capability, message?: unknown[]) => EnforceResult {\n const { checkAcl, resolveIdentity, onAclCheck } = config;\n\n return function enforce(pubkey: string, capability: Capability, message?: unknown[]): EnforceResult {\n const entry = resolveIdentity(pubkey);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n\n const allowed = checkAcl(pubkey, dTag, aggregateHash, capability);\n\n // Audit logging — every check, both allows and denials\n const identity = { pubkey, dTag, hash: aggregateHash };\n const decision = allowed ? 'allow' as const : 'deny' as const;\n const reason = allowed ? 'allowed' as const : 'capability-missing' as const;\n\n if (onAclCheck) {\n onAclCheck({ identity, capability, decision, message, reason });\n }\n\n return { allowed, capability, reason };\n };\n}\n\n/**\n * Enforcement gate configuration for NIP-5D NAP handlers.\n * Uses windowId for identity resolution instead of pubkey (which is '' in NIP-5D sessions).\n *\n * @param checkAcl - The ACL check function\n * @param resolveIdentityByWindowId - Maps windowId to identity (dTag, aggregateHash).\n * @param onAclCheck - Optional audit callback, called on every enforceNap() check\n */\nexport interface NapEnforceConfig {\n checkAcl: AclChecker;\n resolveIdentityByWindowId: (windowId: string) => { dTag: string; aggregateHash: string } | undefined;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate for NIP-5D NAP message handlers.\n *\n * Unlike createEnforceGate (which resolves identity by pubkey), this factory\n * resolves identity by windowId — necessary for NIP-5D sessions where pubkey is ''.\n *\n * @param config - NAP enforcement configuration\n * @returns An enforceNap function that resolves identity by windowId and\n * delegates to the ACL check.\n *\n * @example\n * ```ts\n * const gate = createNapEnforceGate({\n * checkAcl: aclStore.check,\n * resolveIdentityByWindowId: (wid) => sessionRegistry.getEntryByWindowId(wid),\n * onAclCheck: hooks.onAclCheck,\n * });\n * const result = gate('win-1', 'relay:write', { type: 'relay.publish' });\n * // result.allowed === true | false\n * ```\n */\nexport function createNapEnforceGate(config: NapEnforceConfig): (windowId: string, capability: Capability, message?: NapMessage) => EnforceResult {\n const { checkAcl, resolveIdentityByWindowId, onAclCheck } = config;\n\n return function enforceNap(windowId: string, capability: Capability, message?: NapMessage): EnforceResult {\n const entry = resolveIdentityByWindowId(windowId);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n\n // NIP-5D: pass empty string for pubkey - toKey() ignores it (uses dTag:hash).\n const allowed = checkAcl('', dTag, aggregateHash, capability);\n\n const identity = { pubkey: '', dTag, hash: aggregateHash };\n const decision = allowed ? 'allow' as const : 'deny' as const;\n const reason = allowed ? 'allowed' as const : 'capability-missing' as const;\n\n if (onAclCheck) {\n onAclCheck({ identity, capability, decision, message, reason });\n }\n\n return { allowed, capability, reason };\n };\n}\n\n/**\n * Format a denial reason string with the standard 'denied:' prefix.\n *\n * @param capability - The denied capability name\n * @returns Formatted denial string, e.g., 'denied: relay:write'\n *\n * @example\n * ```ts\n * formatDenialReason('relay:write')\n * // => 'denied: relay:write'\n * ```\n */\nexport function formatDenialReason(capability: Capability): string {\n return `denied: ${capability}`;\n}\n","\nimport type { SessionEntry, PendingUpdate, PendingUpdateNotifier } from './types.js';\n\n/**\n * Bidirectional registry mapping windowIds to verified napplet pubkeys.\n * Maintained by the runtime after successful AUTH handshakes.\n *\n * @example\n * ```ts\n * const registry = createSessionRegistry();\n * registry.register('win-1', entry);\n * const pubkey = registry.getPubkey('win-1');\n * ```\n */\nexport interface SessionRegistry {\n /** Register a napplet entry, mapping windowId to pubkey and vice versa. */\n register(windowId: string, entry: SessionEntry): void;\n /** Unregister a napplet by windowId, removing both mappings. */\n unregister(windowId: string): void;\n /** Get the pubkey associated with a windowId. */\n getPubkey(windowId: string): string | undefined;\n /** Get the full entry for a napplet pubkey. */\n getEntry(pubkey: string): SessionEntry | undefined;\n /** Get the windowId for a napplet pubkey. */\n getWindowId(pubkey: string): string | undefined;\n /**\n * Resolve an unambiguous live napplet dTag to its internal window ID.\n *\n * A duplicate dTag fails closed rather than selecting an arbitrary session.\n * Window IDs and pubkeys remain internal transport identities and never\n * resolve through this lookup.\n *\n * @param dTag - Runtime-attested napplet dTag\n * @returns The sole owning window ID, or undefined when missing or ambiguous\n */\n getWindowIdByDTag(dTag: string): string | undefined;\n /** Check if a windowId has a registered napplet. */\n isRegistered(windowId: string): boolean;\n /** Get all registered napplet entries. */\n getAllEntries(): SessionEntry[];\n /** Get the instance GUID for a window. */\n getInstanceId(windowId: string): string | undefined;\n /** Set a pending update for a window (napplet reconnected with different hash). */\n setPendingUpdate(windowId: string, update: PendingUpdate): void;\n /** Get a pending update for a window. */\n getPendingUpdate(windowId: string): PendingUpdate | undefined;\n /** Clear a pending update for a window. */\n clearPendingUpdate(windowId: string): void;\n /**\n * Get the full entry for a napplet by windowId directly.\n * NIP-5D: Required for sessions where pubkey is '' (identity established via originRegistry).\n * Unlike getEntry(pubkey), this works when pubkey is empty.\n */\n getEntryByWindowId(windowId: string): SessionEntry | undefined;\n /** Clear all registrations and pending updates. */\n clear(): void;\n}\n\n/** @deprecated Use SessionRegistry. Will be removed in v0.9.0. */\nexport type NappKeyRegistry = SessionRegistry;\n\n/**\n * Create a new SessionRegistry instance.\n *\n * @param notifier - Optional callback invoked when pending updates change\n * @returns A SessionRegistry instance\n *\n * @example\n * ```ts\n * const registry = createSessionRegistry((windowId) => {\n * console.log('Pending update changed for', windowId);\n * });\n * ```\n */\nexport function createSessionRegistry(notifier?: PendingUpdateNotifier): SessionRegistry {\n const byWindowId = new Map<string, string>();\n const byPubkey = new Map<string, SessionEntry>();\n const byWindowIdEntry = new Map<string, SessionEntry>();\n const pendingUpdates = new Map<string, PendingUpdate>();\n\n return {\n register(windowId: string, entry: SessionEntry): void {\n byWindowId.set(windowId, entry.pubkey);\n byPubkey.set(entry.pubkey, entry);\n byWindowIdEntry.set(windowId, entry);\n },\n\n unregister(windowId: string): void {\n const pubkey = byWindowId.get(windowId);\n if (pubkey) {\n byPubkey.delete(pubkey);\n byWindowId.delete(windowId);\n }\n byWindowIdEntry.delete(windowId);\n pendingUpdates.delete(windowId);\n },\n\n getPubkey(windowId: string): string | undefined {\n return byWindowId.get(windowId);\n },\n\n getEntry(pubkey: string): SessionEntry | undefined {\n return byPubkey.get(pubkey);\n },\n\n getWindowId(pubkey: string): string | undefined {\n return byPubkey.get(pubkey)?.windowId;\n },\n\n getWindowIdByDTag(dTag: string): string | undefined {\n let resolvedWindowId: string | undefined;\n for (const [windowId, entry] of byWindowIdEntry) {\n if (entry.dTag !== dTag) continue;\n if (resolvedWindowId !== undefined) return undefined;\n resolvedWindowId = windowId;\n }\n return resolvedWindowId;\n },\n\n isRegistered(windowId: string): boolean {\n return byWindowId.has(windowId);\n },\n\n getAllEntries(): SessionEntry[] {\n return Array.from(byWindowIdEntry.values());\n },\n\n getInstanceId(windowId: string): string | undefined {\n const pubkey = byWindowId.get(windowId);\n if (!pubkey) return undefined;\n return byPubkey.get(pubkey)?.instanceId;\n },\n\n getEntryByWindowId(windowId: string): SessionEntry | undefined {\n return byWindowIdEntry.get(windowId);\n },\n\n setPendingUpdate(windowId: string, update: PendingUpdate): void {\n pendingUpdates.set(windowId, update);\n notifier?.(windowId);\n },\n\n getPendingUpdate(windowId: string): PendingUpdate | undefined {\n return pendingUpdates.get(windowId);\n },\n\n clearPendingUpdate(windowId: string): void {\n pendingUpdates.delete(windowId);\n notifier?.(windowId);\n },\n\n clear(): void {\n byWindowId.clear();\n byPubkey.clear();\n byWindowIdEntry.clear();\n pendingUpdates.clear();\n },\n };\n}\n\n/** @deprecated Use createSessionRegistry. Will be removed in v0.9.0. */\nexport const createNappKeyRegistry = createSessionRegistry;\n","/**\n * acl-state.ts — ACL state container with persistence hooks.\n *\n * Wraps @kehto/acl's pure functions with persistence via\n * AclPersistence. No localStorage or DOM references.\n */\n\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { AclState, Identity } from '@kehto/acl';\nimport {\n createState, check, grant, revoke, block, unblock,\n serialize, deserialize, getQuota,\n CAP_RELAY_READ, CAP_RELAY_WRITE, CAP_CACHE_READ, CAP_CACHE_WRITE,\n CAP_HOTKEY_FORWARD,\n CAP_STATE_READ, CAP_STATE_WRITE,\n toKey,\n} from '@kehto/acl';\nimport type { AclPersistence, AclEntryExternal } from './types.js';\n\nconst CAP_IDENTITY_READ = 1 << 5; // 32 (reclaimed from CAP_SIGN_EVENT)\nconst CAP_KEYS_BIND = 1 << 6; // 64 (reclaimed from CAP_SIGN_NIP04)\nconst CAP_KEYS_FORWARD = 1 << 7; // 128 (reclaimed from CAP_SIGN_NIP44)\nconst CAP_MEDIA_CONTROL = 1 << 10; // 1024\nconst CAP_NOTIFY_SEND = 1 << 11; // 2048\nconst CAP_NOTIFY_CHANNEL = 1 << 12; // 4096\nconst CAP_THEME_READ = 1 << 13; // 8192\nconst CAP_CONFIG_READ = 1 << 14; // 16384 (v1.7 Phase 39 NAP-CONFIG)\nconst CAP_RESOURCE_FETCH = 1 << 15; // 32768 (v1.7 Phase 40 NAP-RESOURCE)\n// 1 << 16 (65536) RETIRED — was identity:decrypt (v1.8); removed as a spec\n// violation. Left as a permanent gap; do NOT reuse this bit (persisted ACL\n// grants are bitfields — reassigning it would silently re-grant old state).\nconst CAP_CVM_CALL = 1 << 17; // 131072 (NAP-CVM ContextVM bridge)\nconst CAP_OUTBOX_READ = 1 << 18; // 262144 (NAP-OUTBOX read-side routing)\nconst CAP_OUTBOX_WRITE = 1 << 19; // 524288 (NAP-OUTBOX shell-signed publish)\nconst CAP_UPLOAD_WRITE = 1 << 20; // 1048576 (NAP-UPLOAD shell-mediated upload)\nconst CAP_INTENT_READ = 1 << 21; // 2097152 (NAP-INTENT archetype introspection)\nconst CAP_INTENT_WRITE = 1 << 22; // 4194304 (NAP-INTENT cross-napplet dispatch)\nconst CAP_DM_READ = 1 << 23; // 8388608 (NAP-DM read/history/subscription)\nconst CAP_DM_WRITE = 1 << 24; // 16777216 (NAP-DM send)\n\nconst CAP_MAP: Record<Capability, number> = {\n 'relay:read': CAP_RELAY_READ,\n 'relay:write': CAP_RELAY_WRITE,\n 'cache:read': CAP_CACHE_READ,\n 'cache:write': CAP_CACHE_WRITE,\n 'hotkey:forward': CAP_HOTKEY_FORWARD,\n 'state:read': CAP_STATE_READ,\n 'state:write': CAP_STATE_WRITE,\n 'identity:read': CAP_IDENTITY_READ,\n 'keys:bind': CAP_KEYS_BIND,\n 'keys:forward': CAP_KEYS_FORWARD,\n 'media:control': CAP_MEDIA_CONTROL,\n 'notify:send': CAP_NOTIFY_SEND,\n 'notify:channel': CAP_NOTIFY_CHANNEL,\n 'theme:read': CAP_THEME_READ,\n 'config:read': CAP_CONFIG_READ,\n 'resource:fetch': CAP_RESOURCE_FETCH,\n 'cvm:call': CAP_CVM_CALL,\n 'outbox:read': CAP_OUTBOX_READ,\n 'outbox:write': CAP_OUTBOX_WRITE,\n 'upload:write': CAP_UPLOAD_WRITE,\n 'intent:read': CAP_INTENT_READ,\n 'intent:write': CAP_INTENT_WRITE,\n 'dm:read': CAP_DM_READ,\n 'dm:write': CAP_DM_WRITE,\n};\n\nconst RUNTIME_CAP_ALL = Object.values(CAP_MAP).reduce((bits, bit) => bits | bit, 0);\n\nfunction capToBit(cap: Capability): number {\n return CAP_MAP[cap] ?? 0;\n}\n\n/** Convert a bitfield to an array of capability strings. */\nfunction bitsToCapabilities(bits: number): Capability[] {\n const result: Capability[] = [];\n for (const [cap, bit] of Object.entries(CAP_MAP)) {\n if (bits & bit) result.push(cap as Capability);\n }\n return result;\n}\n\nfunction toIdentity(pubkey: string, dTag: string, hash: string): Identity {\n return { pubkey, dTag, hash };\n}\n\n/** An ACL authority change that can invalidate runtime-owned routes. */\nexport type AclMutation =\n | { readonly type: 'revoke'; readonly dTag: string; readonly aggregateHash: string; readonly capability: Capability }\n | { readonly type: 'block'; readonly dTag: string; readonly aggregateHash: string };\n\n/** Receives synchronous ACL authority changes after persistence succeeds. */\nexport type AclMutationObserver = (mutation: AclMutation) => void;\n\n/**\n * ACL state container — wraps @kehto/acl's pure functions with\n * persistence and a convenient imperative API.\n *\n * @example\n * ```ts\n * const aclState = createAclState(persistence);\n * aclState.load();\n * const allowed = aclState.check(pubkey, dTag, hash, 'relay:read');\n * ```\n */\nexport interface AclStateContainer {\n check(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): boolean;\n grant(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void;\n revoke(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void;\n block(pubkey: string, dTag: string, aggregateHash: string): void;\n unblock(pubkey: string, dTag: string, aggregateHash: string): void;\n isBlocked(pubkey: string, dTag: string, aggregateHash: string): boolean;\n getEntry(pubkey: string, dTag: string, aggregateHash: string): AclEntryExternal | undefined;\n getAllEntries(): AclEntryExternal[];\n getStateQuota(pubkey: string, dTag: string, aggregateHash: string): number;\n persist(): void;\n load(): void;\n clear(): void;\n}\n\n/**\n * Create an ACL state container backed by @kehto/acl and persisted\n * via the given persistence hooks.\n *\n * @param persistence - Storage backend for ACL state\n * @param defaultPolicy - Default ACL policy for unknown identities\n * @param onMutation - Internal runtime observer for revoke and block facts\n * @returns An AclStateContainer instance\n *\n * @example\n * ```ts\n * const aclState = createAclState(persistence, 'permissive');\n * aclState.load();\n * ```\n */\nexport function createAclState(\n persistence: AclPersistence,\n defaultPolicy: 'permissive' | 'restrictive' = 'permissive',\n onMutation?: AclMutationObserver,\n): AclStateContainer {\n let state: AclState = createState(defaultPolicy);\n\n function ensureRuntimeDefaultEntry(id: Identity): void {\n if (state.defaultPolicy !== 'permissive') return;\n if (state.entries[toKey(id)]) return;\n state = grant(state, id, RUNTIME_CAP_ALL);\n }\n\n return {\n check(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): boolean {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n return check(state, id, capToBit(capability));\n },\n\n grant(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = grant(state, id, capToBit(capability));\n },\n\n revoke(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = revoke(state, id, capToBit(capability));\n onMutation?.({ type: 'revoke', dTag, aggregateHash, capability });\n },\n\n block(pubkey: string, dTag: string, aggregateHash: string): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = block(state, id);\n onMutation?.({ type: 'block', dTag, aggregateHash });\n },\n\n unblock(pubkey: string, dTag: string, aggregateHash: string): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = unblock(state, id);\n },\n\n isBlocked(pubkey: string, dTag: string, aggregateHash: string): boolean {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n // A blocked identity fails all checks — check with all runtime caps.\n // If blocked, check returns false even for all caps\n return !check(state, id, RUNTIME_CAP_ALL) && this.getEntry(pubkey, dTag, aggregateHash)?.blocked === true;\n },\n\n getEntry(pubkey: string, dTag: string, aggregateHash: string): AclEntryExternal | undefined {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n const key = `${id.dTag}:${id.hash}`;\n const entry = state.entries[key];\n if (!entry) return undefined;\n return {\n pubkey: pubkey || '',\n capabilities: bitsToCapabilities(entry.caps),\n blocked: entry.blocked,\n stateQuota: entry.quota,\n };\n },\n\n getAllEntries(): AclEntryExternal[] {\n return Object.entries(state.entries).map(([, entry]) => {\n return {\n pubkey: '',\n capabilities: bitsToCapabilities(entry.caps),\n blocked: entry.blocked,\n stateQuota: entry.quota,\n };\n });\n },\n\n getStateQuota(pubkey: string, dTag: string, aggregateHash: string): number {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n return getQuota(state, id);\n },\n\n persist(): void {\n try {\n persistence.persist(serialize(state));\n } catch { /* persistence is best-effort */ }\n },\n\n load(): void {\n try {\n const raw = persistence.load();\n if (!raw) return;\n state = deserialize(raw);\n } catch {\n state = createState(defaultPolicy);\n }\n },\n\n clear(): void {\n state = createState(defaultPolicy);\n try { persistence.persist(''); } catch { /* best-effort */ }\n },\n };\n}\n","/**\n * firewall-state.ts — Firewall state container with persistence hooks.\n *\n * Wraps @kehto/firewall's pure functions with persistence via\n * FirewallPersistence. No localStorage or DOM references.\n *\n * Two independent `let`-bound cells:\n * - `config` — immutable FirewallConfig; persisted on each mutation.\n * - `counters` — ephemeral FirewallState (token-buckets + burst counters);\n * in-memory only, reset on reload (RUNTIME-03).\n *\n * CRITICAL: `evaluate` reassigns `counters = result.newState` on every call.\n * Without this, token buckets never advance and flood events never escalate\n * from 'flag' to 'block'.\n */\n\nimport type {\n Observation,\n FirewallConfig,\n FirewallState,\n NappletPolicy,\n RateLimit,\n ContentMatcher,\n EvaluateResult,\n} from '@kehto/firewall';\nimport {\n evaluate,\n defaultConfig,\n createState,\n serialize,\n deserialize,\n setPolicy,\n setRateLimit,\n setGlobalRate,\n addMatcher,\n} from '@kehto/firewall';\nimport type { FirewallPersistence } from './types.js';\n\n/**\n * Stateful firewall container — wraps @kehto/firewall's pure functions\n * with persistence and a convenient imperative API.\n *\n * Mirrors AclStateContainer from acl-state.ts in structure and naming.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * const result = firewall.evaluate({ napplet: 'chat', opClass: 'relay:write', focused: true, now: Date.now() });\n * ```\n */\nexport interface FirewallStateContainer {\n /**\n * Evaluate an observation against the current firewall config and counters.\n * CRITICAL: advances the in-memory counter state on each call.\n *\n * @param observation - Normalized observation extracted from the napplet message envelope.\n * @returns The full EvaluateResult (decision, action, ruleId, reason, newState).\n */\n evaluate(observation: Observation): EvaluateResult;\n /**\n * Set a per-napplet policy override (allow / deny / ask).\n *\n * @param napplet - The napplet dTag (version-agnostic identity key).\n * @param policy - Hard policy override for this napplet.\n */\n setPolicy(napplet: string, policy: NappletPolicy): void;\n /**\n * Set a per-(napplet, opClass) token-bucket rate limit.\n *\n * @param napplet - The napplet dTag.\n * @param opClass - The operation class string.\n * @param limit - The rate limit to apply.\n */\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void;\n /**\n * Set a global rate limit applied to all op-classes that have no specific entry.\n *\n * @param napplet - The napplet dTag.\n * @param limit - The global fallback rate limit.\n */\n setGlobalRate(napplet: string, limit: RateLimit): void;\n /**\n * Add a content matcher to the firewall config.\n *\n * @param matcher - The content matcher to append.\n */\n addMatcher(matcher: ContentMatcher): void;\n /** Return the current firewall config. */\n getConfig(): FirewallConfig;\n /** Persist the current firewall config via the persistence hook. Best-effort. */\n persist(): void;\n /** Load previously persisted firewall config. Counters are NOT restored. */\n load(): void;\n /** Reset config to defaultConfig() and counters to createState(), then persist empty. */\n clear(): void;\n}\n\n/**\n * Create a firewall state container backed by @kehto/firewall and optionally\n * persisted via the given persistence hooks.\n *\n * When `persistence` is absent (or undefined), firewall config is in-memory\n * only and resets on container recreation. This is safe for hosts that do not\n * need durable per-napplet policies.\n *\n * @param persistence - Optional storage backend for firewall config.\n * @returns A FirewallStateContainer instance.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * firewall.setPolicy('chat', 'allow');\n * firewall.persist();\n * ```\n */\nexport function createFirewallState(\n persistence?: FirewallPersistence,\n): FirewallStateContainer {\n let config: FirewallConfig = defaultConfig();\n let counters: FirewallState = createState();\n\n return {\n evaluate(observation: Observation): EvaluateResult {\n const result = evaluate(config, counters, observation);\n counters = result.newState; // CRITICAL: advance ephemeral counter state\n return result;\n },\n\n setPolicy(napplet: string, policy: NappletPolicy): void {\n config = setPolicy(config, napplet, policy);\n },\n\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void {\n config = setRateLimit(config, napplet, opClass, limit);\n },\n\n setGlobalRate(napplet: string, limit: RateLimit): void {\n config = setGlobalRate(config, napplet, limit);\n },\n\n addMatcher(matcher: ContentMatcher): void {\n config = addMatcher(config, matcher);\n },\n\n getConfig(): FirewallConfig {\n return config;\n },\n\n persist(): void {\n try {\n persistence?.persist(serialize(config));\n } catch { /* persistence is best-effort */ }\n },\n\n load(): void {\n try {\n const raw = persistence?.load() ?? null;\n if (!raw) return;\n config = deserialize(raw);\n // counters are deliberately NOT restored (RUNTIME-03: ephemeral)\n } catch {\n config = defaultConfig();\n }\n },\n\n clear(): void {\n config = defaultConfig();\n counters = createState();\n try { persistence?.persist(''); } catch { /* best-effort */ }\n },\n };\n}\n","/**\n * manifest-cache.ts — Manifest cache with persistence hooks.\n *\n * Caches NIP-5A manifest data (aggregate hashes) per napplet identity.\n * Delegates storage to ManifestPersistence — no localStorage.\n */\n\nimport type { ManifestPersistence, ManifestCacheEntry, VerificationCacheEntry } from './types.js';\n\n/**\n * Cache for verified napplet manifest entries.\n * Used to detect napplet updates (aggregateHash changes) across sessions.\n *\n * @example\n * ```ts\n * const cache = createManifestCache(persistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * ```\n */\nexport interface ManifestCache {\n /** Get a cached manifest entry by pubkey and dTag. */\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined;\n /** Set (upsert) a manifest cache entry and persist. */\n set(entry: ManifestCacheEntry): void;\n /** Check if a specific hash is cached for a pubkey/dTag combination. */\n has(pubkey: string, dTag: string, hash: string): boolean;\n /** Get the requires list for a cached manifest, or empty array if not found. */\n getRequires(pubkey: string, dTag: string): string[];\n /** Remove a cached entry for a pubkey/dTag and persist. */\n remove(pubkey: string, dTag: string): void;\n /** Load the cache from persistence. */\n load(): void;\n /** Persist the cache to storage. */\n persist(): void;\n /** Clear all cached entries. */\n clear(): void;\n\n /** Get a cached verification result by manifest event ID. */\n getVerification(eventId: string): VerificationCacheEntry | undefined;\n\n /** Cache a verification result keyed by manifest event ID. */\n setVerification(eventId: string, result: VerificationCacheEntry): void;\n\n /** Check if a manifest event ID has been verified. */\n hasVerification(eventId: string): boolean;\n\n /** Clear all verification cache entries. */\n clearVerifications(): void;\n}\n\n/**\n * Create a manifest cache backed by the given persistence hooks.\n *\n * @param persistence - Storage backend for manifest data\n * @returns A ManifestCache instance\n *\n * @example\n * ```ts\n * import { createManifestCache } from '@kehto/runtime';\n *\n * const cache = createManifestCache(manifestPersistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * cache.has('abc...', 'chat', 'dead'); // true\n * ```\n */\nexport function createManifestCache(persistence: ManifestPersistence): ManifestCache {\n const cache = new Map<string, ManifestCacheEntry>();\n const verificationCache = new Map<string, VerificationCacheEntry>();\n\n function cacheKey(pubkey: string, dTag: string): string {\n return `${pubkey}:${dTag}`;\n }\n\n const self: ManifestCache = {\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined {\n return cache.get(cacheKey(pubkey, dTag));\n },\n\n set(entry: ManifestCacheEntry): void {\n cache.set(cacheKey(entry.pubkey, entry.dTag), entry);\n self.persist();\n },\n\n has(pubkey: string, dTag: string, hash: string): boolean {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return !!entry && entry.aggregateHash === hash;\n },\n\n getRequires(pubkey: string, dTag: string): string[] {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return entry?.requires ?? [];\n },\n\n remove(pubkey: string, dTag: string): void {\n cache.delete(cacheKey(pubkey, dTag));\n self.persist();\n },\n\n load(): void {\n try {\n const raw = persistence.load();\n if (!raw) return;\n const entries = JSON.parse(raw) as Array<[string, ManifestCacheEntry]>;\n cache.clear();\n for (const [key, val] of entries) cache.set(key, val);\n } catch { cache.clear(); }\n },\n\n persist(): void {\n try {\n persistence.persist(JSON.stringify(Array.from(cache.entries())));\n } catch { /* persistence is best-effort */ }\n },\n\n clear(): void {\n cache.clear();\n verificationCache.clear();\n try { persistence.persist(''); } catch { /* best-effort */ }\n },\n\n getVerification(eventId: string): VerificationCacheEntry | undefined {\n return verificationCache.get(eventId);\n },\n\n setVerification(eventId: string, result: VerificationCacheEntry): void {\n verificationCache.set(eventId, result);\n // Verification cache is in-memory only — no persistence needed\n // since manifest event IDs are immutable and re-fetching is cheap\n },\n\n hasVerification(eventId: string): boolean {\n return verificationCache.has(eventId);\n },\n\n clearVerifications(): void {\n verificationCache.clear();\n },\n };\n\n return self;\n}\n","/**\n * replay.ts — Replay detection module.\n *\n * Tracks seen event IDs and validates timestamps to prevent\n * duplicate event processing and replay attacks.\n */\n\nimport type { NostrEvent } from '@napplet/core';\n\n/**\n * Replay detection window in seconds — events older than this are rejected.\n * Relocated inline from the former @napplet/core compatibility shim\n * (DRIFT-CORE-06, Phase 24). Numeric value preserved unchanged from the shim\n * to hold behavioral parity with the Phase 23 test baseline.\n */\nconst REPLAY_WINDOW_SECONDS = 30;\n\n/**\n * Replay detection engine. Tracks seen event IDs and validates timestamps.\n *\n * @example\n * ```ts\n * const detector = createReplayDetector();\n * const reason = detector.check(event);\n * if (reason !== null) { // reject event }\n * ```\n */\nexport interface ReplayDetector {\n /**\n * Check if an event should be rejected as a replay and record it as processed.\n * Returns null if the event is valid, or a string reason if it should be rejected.\n */\n check(event: NostrEvent): string | null;\n\n /**\n * Check an event and atomically reserve its ID for pending processing.\n * Call {@link commit} after processing succeeds or {@link release} after it fails.\n *\n * @param event - Signed event whose ID should be reserved\n * @returns null if reserved, or a string reason if it should be rejected\n */\n reserve(event: NostrEvent): string | null;\n\n /**\n * Commit a pending reservation as successfully processed.\n *\n * @param eventId - Reserved signed event ID\n */\n commit(eventId: string): void;\n\n /**\n * Release a pending reservation so a failed operation can be retried.\n * Committed event IDs are never removed by this method.\n *\n * @param eventId - Reserved signed event ID\n */\n release(eventId: string): void;\n\n /** Clear all tracked event IDs. */\n clear(): void;\n}\n\n/**\n * Create a replay detector that rejects duplicate events and events\n * with timestamps outside the replay window.\n *\n * @param getReplayWindow - Optional getter for a dynamic replay window override.\n * When provided, its return value is used instead of the module-level constant.\n * Called on every check, so changes take effect immediately.\n * @returns A ReplayDetector instance\n *\n * @example\n * ```ts\n * import { createReplayDetector } from '@kehto/runtime';\n *\n * const detector = createReplayDetector();\n * const reason = detector.check(event);\n * if (reason !== null) {\n * // Reject — duplicate, stale, or future-dated\n * }\n * ```\n */\nexport function createReplayDetector(getReplayWindow?: () => number | undefined): ReplayDetector {\n type ReplayEntry = {\n state: 'pending' | 'committed';\n timestamp: number;\n };\n\n const seenEventIds = new Map<string, ReplayEntry>();\n\n function checkAndTrack(event: NostrEvent, state: ReplayEntry['state']): string | null {\n const replayWindow = getReplayWindow?.() ?? REPLAY_WINDOW_SECONDS;\n const now = Math.floor(Date.now() / 1000);\n for (const [id, entry] of seenEventIds) {\n if (entry.state === 'committed' && now - entry.timestamp > replayWindow) {\n seenEventIds.delete(id);\n }\n }\n if (now - event.created_at > replayWindow) return 'invalid: event created_at too old';\n if (event.created_at - now > 10) return 'invalid: event created_at in the future';\n if (seenEventIds.has(event.id)) return 'duplicate: already processed';\n seenEventIds.set(event.id, { state, timestamp: now });\n return null;\n }\n\n return {\n check(event: NostrEvent): string | null {\n return checkAndTrack(event, 'committed');\n },\n\n reserve(event: NostrEvent): string | null {\n return checkAndTrack(event, 'pending');\n },\n\n commit(eventId: string): void {\n const entry = seenEventIds.get(eventId);\n if (entry?.state !== 'pending') return;\n seenEventIds.set(eventId, {\n state: 'committed',\n timestamp: Math.floor(Date.now() / 1000),\n });\n },\n\n release(eventId: string): void {\n if (seenEventIds.get(eventId)?.state === 'pending') {\n seenEventIds.delete(eventId);\n }\n },\n\n clear(): void {\n seenEventIds.clear();\n },\n };\n}\n","/**\n * event-buffer.ts — Ring buffer and subscription delivery engine.\n *\n * Buffers events in a fixed-size ring buffer and delivers matching\n * events to subscribed napplets via the abstract sendToNapplet transport.\n */\n\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { SendToNapplet } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { EnforceResult } from './enforce.js';\n\n/** Default ring buffer size. */\nexport const RING_BUFFER_SIZE = 100;\n\n/** Subscription entry — tracks a subscription for a specific napplet window. */\nexport interface SubscriptionEntry {\n windowId: string;\n filters: NostrFilter[];\n}\n\n/**\n * Check if an event matches a single NIP-01 filter.\n * Pure function — no side effects.\n *\n * @param event - The event to check\n * @param filter - The filter to match against\n * @returns True if the event matches the filter\n *\n * @example\n * ```ts\n * import { matchesFilter } from '@kehto/runtime';\n *\n * matchesFilter(event, { kinds: [1], authors: ['abc'] });\n * // true if event.kind === 1 and event.pubkey starts with 'abc'\n * ```\n */\nexport function matchesFilter(event: NostrEvent, filter: NostrFilter): boolean {\n if (filter.ids !== undefined && !filter.ids.some((id) => event.id.startsWith(id))) return false;\n if (filter.authors !== undefined && !filter.authors.some((a) => event.pubkey.startsWith(a))) return false;\n if (filter.kinds !== undefined && !filter.kinds.includes(event.kind)) return false;\n if (filter.since !== undefined && event.created_at < filter.since) return false;\n if (filter.until !== undefined && event.created_at > filter.until) return false;\n for (const [key, values] of Object.entries(filter)) {\n if (!key.startsWith('#') || values === undefined) continue;\n const tagName = key.slice(1);\n const tagValues = values as string[];\n const eventTagValues = event.tags.filter((t) => t[0] === tagName).map((t) => t[1]);\n if (!tagValues.some((v) => eventTagValues.includes(v))) return false;\n }\n return true;\n}\n\n/**\n * Check if an event matches any filter in a list.\n * Returns true for empty filter lists.\n *\n * @param event - The event to check\n * @param filters - The filters to match against\n * @returns True if the event matches any filter (or if filters is empty)\n *\n * @example\n * ```ts\n * import { matchesAnyFilter } from '@kehto/runtime';\n *\n * matchesAnyFilter(event, [{ kinds: [1] }, { kinds: [7] }]);\n * // true if event is kind 1 or kind 7\n * ```\n */\nexport function matchesAnyFilter(event: NostrEvent, filters: NostrFilter[]): boolean {\n if (filters.length === 0) return true;\n return filters.some((filter) => matchesFilter(event, filter));\n}\n\n/** Event buffer and subscription delivery engine. */\nexport interface EventBuffer {\n /** Add an event to the ring buffer and deliver to matching subscriptions. */\n bufferAndDeliver(event: NostrEvent, senderId: string | null): void;\n\n /** Deliver an event to matching subscriptions without buffering. */\n deliverToSubscriptions(event: NostrEvent, senderId: string | null): void;\n\n /** Get the current subscription map (for REQ handler to register subs). */\n getSubscriptions(): Map<string, SubscriptionEntry>;\n\n /** Get buffered events (for REQ replay). */\n getBufferedEvents(): readonly NostrEvent[];\n\n /** Clear the buffer. */\n clear(): void;\n}\n\n/**\n * Create an event buffer with subscription delivery.\n *\n * @param sendToNapplet - Transport function to send messages to napplets\n * @param sessionRegistry - Identity registry for looking up napplet pubkeys\n * @param enforce - Enforcement function for checking relay:read on recipients\n * @param subscriptions - Shared subscription map (owned by the runtime)\n * @param getBufferSize - Optional getter for a dynamic buffer size override.\n * When provided, its return value is used instead of RING_BUFFER_SIZE.\n * Called on every bufferAndDeliver, so changes take effect immediately.\n * @returns An EventBuffer instance\n *\n * @example\n * ```ts\n * import { createEventBuffer } from '@kehto/runtime';\n *\n * const buffer = createEventBuffer(sendToNapplet, sessionRegistry, enforce, subscriptions);\n * buffer.bufferAndDeliver(event, senderWindowId);\n * ```\n */\nexport function createEventBuffer(\n sendToNapplet: SendToNapplet,\n sessionRegistry: SessionRegistry,\n enforce: (pubkey: string, capability: Capability, message?: unknown[]) => EnforceResult,\n subscriptions: Map<string, SubscriptionEntry>,\n getBufferSize?: () => number,\n): EventBuffer {\n const buffer: NostrEvent[] = [];\n\n function deliverToSubscriptions(event: NostrEvent, senderId: string | null): void {\n const pTag = event.tags?.find((t) => t[0] === 'p');\n const targetPubkey = pTag?.[1];\n\n for (const [subKey, sub] of subscriptions) {\n if (senderId !== null && sub.windowId === senderId) continue;\n\n const recipientPubkey = sessionRegistry.getPubkey(sub.windowId);\n if (recipientPubkey) {\n const recipientResult = enforce(recipientPubkey, 'relay:read', ['EVENT', event]);\n if (!recipientResult.allowed) continue;\n }\n\n if (targetPubkey) {\n const subPubkey = recipientPubkey;\n if (subPubkey !== targetPubkey) continue;\n }\n\n if (!matchesAnyFilter(event, sub.filters)) continue;\n\n const prefix = `${sub.windowId}:`;\n if (!subKey.startsWith(prefix)) continue;\n const subId = subKey.slice(prefix.length);\n\n sendToNapplet(sub.windowId, ['EVENT', subId, event]);\n }\n }\n\n return {\n bufferAndDeliver(event: NostrEvent, senderId: string | null): void {\n const maxSize = getBufferSize?.() ?? RING_BUFFER_SIZE;\n if (buffer.length >= maxSize) buffer.shift();\n buffer.push(event);\n deliverToSubscriptions(event, senderId);\n },\n\n deliverToSubscriptions,\n\n getSubscriptions(): Map<string, SubscriptionEntry> { return subscriptions; },\n\n getBufferedEvents(): readonly NostrEvent[] { return buffer; },\n\n clear(): void {\n buffer.length = 0;\n },\n };\n}\n","import type { NostrEvent } from '@napplet/core';\nimport type { ResourceSidecarEntry } from '@napplet/nap/resource/types';\n\n/** Metadata attached to a raw Nostr event read result. */\nexport interface RelayEventSidecar {\n /** Pre-resolved resources owned by NAP-RESOURCE policy. */\n resources?: ResourceSidecarEntry[];\n /** Advisory relays where the shell observed the event or expects follow-up reads to work. */\n relayHints?: string[];\n}\n\n/** Raw event plus optional read-side metadata, shared by read-style NAP surfaces. */\nexport interface RelayEventResult {\n /** Raw Nostr event. */\n event: NostrEvent;\n /** Optional runtime-observed sidecar metadata. */\n sidecar?: RelayEventSidecar;\n}\n\n/** Relay event envelope shape accepted during the result-shape migration. */\nexport interface RelayEventCarrier {\n result?: unknown;\n event?: unknown;\n sidecar?: unknown;\n resources?: unknown;\n relay?: unknown;\n}\n\nfunction stringList(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const out = [...new Set(value.filter((item): item is string => typeof item === 'string' && item.length > 0))];\n return out.length > 0 ? out : undefined;\n}\n\nfunction sidecarFromUnknown(value: unknown): RelayEventSidecar | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const input = value as RelayEventSidecar;\n const sidecar: RelayEventSidecar = {};\n if (Array.isArray(input.resources) && input.resources.length > 0) sidecar.resources = input.resources;\n const relayHints = stringList(input.relayHints);\n if (relayHints) sidecar.relayHints = relayHints;\n return sidecar.resources || sidecar.relayHints ? sidecar : undefined;\n}\n\n/** Build a canonical relay event result, omitting an empty sidecar. */\nexport function createRelayEventResult(event: NostrEvent, sidecar?: RelayEventSidecar): RelayEventResult {\n const normalized = sidecarFromUnknown(sidecar);\n return normalized ? { event, sidecar: normalized } : { event };\n}\n\n/** Build a relay event result with advisory relay hints. */\nexport function createRelayEventResultWithHints(event: NostrEvent, relayHints?: string[]): RelayEventResult {\n return createRelayEventResult(event, relayHints && relayHints.length > 0 ? { relayHints } : undefined);\n}\n\n/**\n * Read either the current `result` wire shape or the pre-result migration shape.\n * This keeps internal service composition working while emitters move to\n * canonical `RelayEventResult` envelopes.\n */\nexport function relayEventResultFromCarrier(carrier: RelayEventCarrier): RelayEventResult | null {\n if (carrier.result && typeof carrier.result === 'object') {\n const result = carrier.result as RelayEventResult;\n if (result.event && typeof result.event === 'object') return createRelayEventResult(result.event, result.sidecar);\n }\n if (carrier.event && typeof carrier.event === 'object') {\n const sidecar = sidecarFromUnknown(carrier.sidecar) ?? sidecarFromUnknown({ resources: carrier.resources });\n const relayHints = typeof carrier.relay === 'string' && carrier.relay.length > 0 ? [carrier.relay] : undefined;\n const merged: RelayEventSidecar = sidecar ? { ...sidecar } : {};\n if (relayHints) merged.relayHints = relayHints;\n return createRelayEventResult(carrier.event as NostrEvent, merged);\n }\n return null;\n}\n","\nimport { createDispatch, type NappletMessage, type NostrEvent, type NapHandler } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { Observation } from '@kehto/firewall';\n\nimport type {\n RuntimeAdapter, ConsentHandler, FirewallEvent,\n ServiceHandler, ServiceRegistry, ServiceInfo, ServiceRuntimeContext,\n} from './types.js';\nimport { notifyServiceWindowDestroyed } from './service-dispatch.js';\nimport { createSessionRegistry, type SessionRegistry } from './session-registry.js';\nimport { createAclState, type AclStateContainer } from './acl-state.js';\nimport { createFirewallState, type FirewallStateContainer } from './firewall-state.js';\nimport { createManifestCache, type ManifestCache } from './manifest-cache.js';\nimport { createReplayDetector, type ReplayDetector } from './replay.js';\nimport { createEventBuffer, RING_BUFFER_SIZE, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport { createEnforceGate, createNapEnforceGate, resolveCapabilitiesNap, formatDenialReason } from './enforce.js';\nimport { createRelayHandler } from './relay-handler.js';\nimport { createIdentityHandler } from './identity-handler.js';\nimport { createCountHandler } from './count-handler.js';\nimport { createIncRuntime, type IncRuntime } from './inc-handler.js';\nimport { createRuntimeDomainHandlers, type RuntimeDomainHandlers } from './domain-handlers.js';\nimport {\n createCanonicalDomainResult,\n createIntentPolicyDenial,\n isIdentityOrThemeMessage,\n} from './domain-results.js';\n\n/**\n * The napplet protocol engine — handles NIP-5D NAP domain dispatch,\n * ACL enforcement, subscription lifecycle.\n *\n * @example\n * ```ts\n * import { createRuntime } from '@kehto/runtime';\n *\n * const runtime = createRuntime(hooks);\n * runtime.handleMessage('window-1', { type: 'relay.req', id: 'sub1', filters: [{ kinds: [1] }] });\n * ```\n */\nexport interface Runtime {\n /**\n * Handle an incoming NIP-5D NappletMessage envelope from a napplet.\n * The caller is responsible for identifying the source (windowId).\n * Legacy NIP-01 arrays are silently dropped (clean break — no dual-mode).\n *\n * @param windowId - The identifier of the napplet that sent the message\n * @param msg - The raw message (NappletMessage envelopes are processed; other types dropped)\n */\n handleMessage(windowId: string, msg: unknown): void;\n\n /**\n * Inject a shell-originated event into subscription delivery.\n *\n * @param topic - The event topic tag value\n * @param payload - The event content\n */\n injectEvent(topic: string, payload: unknown): void;\n\n /** Destroy the runtime, persisting state and clearing all internal state. */\n destroy(): void;\n\n /** Register a handler for consent requests on destructive signing kinds. */\n registerConsentHandler(handler: ConsentHandler): void;\n\n /**\n * Register a service handler dynamically after runtime creation.\n * If a handler is already registered for this name, it is replaced.\n *\n * @param name - Service name (e.g., 'audio', 'notifications')\n * @param handler - The service handler implementation\n */\n registerService(name: string, handler: ServiceHandler): void;\n\n /**\n * Unregister a service handler by name. No-op if the name is not registered.\n *\n * @param name - Service name to remove\n */\n unregisterService(name: string): void;\n\n /**\n * Clean up all state associated with a napplet window.\n * Removes subscriptions, pending state, and notifies service handlers.\n *\n * @param windowId - The window to clean up\n */\n destroyWindow(windowId: string): void;\n\n /** Access the identity registry (for shell adapter to read napplet session state). */\n readonly sessionRegistry: SessionRegistry;\n\n /** Access the ACL state container. */\n readonly aclState: AclStateContainer;\n\n /** Access the firewall state container (for tests to pre-set policy/rules). */\n readonly firewallState: FirewallStateContainer;\n\n /** Access the manifest cache. */\n readonly manifestCache: ManifestCache;\n}\n\ntype RuntimeNapHandlers = RuntimeDomainHandlers & {\n relay: (windowId: string, msg: NappletMessage) => void;\n identity: (windowId: string, msg: NappletMessage) => void;\n count: (windowId: string, msg: NappletMessage) => void;\n inc: (windowId: string, msg: NappletMessage) => void;\n};\n\ntype RuntimeInstanceContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n registeredServices: Map<string, ServiceInfo>;\n replayDetector: ReplayDetector;\n subscriptions: Map<string, SubscriptionEntry>;\n eventBuffer: EventBuffer;\n incRuntime: IncRuntime;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n firewallState: FirewallStateContainer;\n manifestCache: ManifestCache;\n consentHandlerRef: { current: ConsentHandler | null };\n handleMessage: Runtime['handleMessage'];\n};\n\nfunction createRegisteredServices(serviceRegistry: ServiceRegistry): Map<string, ServiceInfo> {\n const registeredServices = new Map<string, ServiceInfo>();\n for (const [name, handler] of Object.entries(serviceRegistry)) {\n registeredServices.set(name, {\n name: handler.descriptor.name,\n version: handler.descriptor.version,\n description: handler.descriptor.description,\n });\n }\n return registeredServices;\n}\n\nfunction createNapEnvelopeDispatcher(handlers: RuntimeNapHandlers): (windowId: string, envelope: NappletMessage) => void {\n let currentWindowId: string | null = null;\n const napDispatch = createDispatch();\n const adapt = (handler: (windowId: string, msg: NappletMessage) => void): NapHandler => (msg) => {\n if (currentWindowId !== null) handler(currentWindowId, msg);\n };\n\n napDispatch.registerNap('relay', adapt(handlers.relay));\n napDispatch.registerNap('identity', adapt(handlers.identity));\n napDispatch.registerNap('count', adapt(handlers.count));\n napDispatch.registerNap('keys', adapt(handlers.keys));\n napDispatch.registerNap('media', adapt(handlers.media));\n napDispatch.registerNap('notify', adapt(handlers.notify));\n napDispatch.registerNap('storage', adapt(handlers.storage));\n napDispatch.registerNap('inc', adapt(handlers.inc));\n napDispatch.registerNap('theme', adapt(handlers.theme));\n napDispatch.registerNap('config', adapt(handlers.config));\n napDispatch.registerNap('resource', adapt(handlers.resource));\n napDispatch.registerNap('cvm', adapt(handlers.cvm));\n napDispatch.registerNap('outbox', adapt(handlers.outbox));\n napDispatch.registerNap('upload', adapt(handlers.upload));\n napDispatch.registerNap('intent', adapt(handlers.intent));\n napDispatch.registerNap('link', adapt(handlers.link));\n napDispatch.registerNap('common', adapt(handlers.common));\n napDispatch.registerNap('lists', adapt(handlers.lists));\n napDispatch.registerNap('serial', adapt(handlers.serial));\n napDispatch.registerNap('ble', adapt(handlers.ble));\n napDispatch.registerNap('webrtc', adapt(handlers.webrtc));\n napDispatch.registerNap('dm', adapt(handlers.dm));\n\n return (windowId, envelope) => {\n currentWindowId = windowId;\n try {\n napDispatch.dispatch(envelope);\n } finally {\n currentWindowId = null;\n }\n };\n}\n\n/**\n * Compute the UTF-8 byte length of a string without TextEncoder (ES2022-safe).\n * Mirrors the same helper in state-handler.ts to avoid cross-file import.\n */\nfunction utf8ByteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Extract kind and byte-size from a message envelope (publish-style ops only).\n * Guards against malformed envelopes — returns undefined fields on any failure.\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @returns `{ kind?, size? }` — both optional; undefined when not a publish-style op.\n */\nfunction extractKindSize(envelope: NappletMessage): { kind?: number; size?: number } {\n const ev = (envelope as NappletMessage & { event?: unknown }).event;\n if (typeof ev !== 'object' || ev === null) return {};\n const kind = typeof (ev as { kind?: unknown }).kind === 'number'\n ? (ev as { kind: number }).kind\n : undefined;\n let size: number | undefined;\n try {\n size = utf8ByteLength(JSON.stringify(ev));\n } catch { /* malformed event — size stays undefined */ }\n return { kind, size };\n}\n\n/**\n * Build a normalized Observation from a message envelope and runtime context.\n * The runtime owns the clock (Date.now()); the pure engine never reads it.\n * Focus is sourced exclusively via getFocusContext (FOCUS-01) — never napplet-self-reported.\n * When getFocusContext is absent, defaults to `{ focused: true }` (safe relax-only default).\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @param windowId - Source napplet window identifier.\n * @param senderCap - Resolved sender capability string (or null).\n * @param sessionRegistry - Used to resolve dTag and registeredAt for the window.\n * @param getFocusContext - Optional shell-supplied focus query hook.\n * @returns A complete Observation ready for evaluate().\n */\nfunction buildObservation(\n envelope: NappletMessage,\n windowId: string,\n senderCap: string | null,\n sessionRegistry: SessionRegistry,\n getFocusContext?: RuntimeAdapter['getFocusContext'],\n): Observation {\n const now = Date.now();\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n const napplet = entry?.dTag ?? '';\n const initElapsedMs = now - (entry?.registeredAt ?? now);\n const focus = getFocusContext?.(windowId) ?? { focused: true };\n const opClass = senderCap ?? envelope.type;\n const { kind, size } = extractKindSize(envelope);\n return {\n napplet,\n opClass,\n kind,\n size,\n initElapsedMs,\n // `windowId` comes from the host's source-bound session lookup, never the\n // napplet message envelope. It scopes startup burst accounting to this\n // registered realm while rate limits remain dTag-wide.\n initKey: windowId,\n focused: focus.focused,\n msSinceFocusGain: focus.msSinceFocusGain,\n now,\n };\n}\n\n/** Configuration for createFirewallGate. */\ninterface FirewallGateConfig {\n firewallState: FirewallStateContainer;\n sessionRegistry: SessionRegistry;\n hooks: RuntimeAdapter;\n fireConsent: (windowId: string, napplet: string) => void;\n}\n\n/**\n * Create the firewall gate closure to inject into createMessageHandler.\n * Returns 'dispatch' to allow the message through or 'drop' to reject it.\n *\n * Decision→action mapping (RUNTIME-01, RUNTIME-04, POLICY-02):\n * - reject → error envelope + drop (no dispatch)\n * - prompt → error envelope + fireConsent + drop (ask path)\n * - pass + flag → onFirewallEvent audit + dispatch\n * - pass (ignore/allow) → dispatch (no audit)\n *\n * @param config - Gate configuration (firewallState, sessionRegistry, hooks, fireConsent).\n * @returns A gate function `(windowId, envelope, senderCap) => 'dispatch' | 'drop'`.\n */\nfunction createFirewallGate(config: FirewallGateConfig): (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop' {\n const { firewallState, sessionRegistry, hooks, fireConsent } = config;\n\n return function firewallGate(windowId: string, envelope: NappletMessage, senderCap: string | null): 'dispatch' | 'drop' {\n const obs = buildObservation(envelope, windowId, senderCap, sessionRegistry, hooks.getFocusContext);\n const result = firewallState.evaluate(obs);\n const { decision, action, ruleId, reason } = result;\n const napplet = obs.napplet;\n const opClass = obs.opClass;\n\n if (decision === 'reject' || decision === 'prompt') {\n const intentDenial = createIntentPolicyDenial(envelope);\n if (intentDenial) {\n hooks.sendToNapplet(windowId, intentDenial);\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n if (decision === 'prompt') fireConsent(windowId, napplet);\n return 'drop';\n }\n\n const canonicalResult = createCanonicalDomainResult(envelope);\n if (canonicalResult) {\n hooks.sendToNapplet(windowId, canonicalResult);\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n if (decision === 'prompt') fireConsent(windowId, napplet);\n return 'drop';\n }\n\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const type = denialResponseType(envelope);\n if (type) {\n hooks.sendToNapplet(windowId, { type, id, error: `firewall: ${reason}` } as NappletMessage);\n }\n\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n\n if (decision === 'prompt') {\n // POLICY-02: reject current message AND fire async consent prompt\n fireConsent(windowId, napplet);\n }\n return 'drop';\n }\n\n // decision === 'pass'\n if (action === 'flag') {\n // RUNTIME-04: flag → audit + dispatch (message is NOT dropped)\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n }\n return 'dispatch';\n };\n}\n\nfunction denialResponseType(envelope: NappletMessage): string | null {\n if (envelope.type.startsWith('intent.')) return null;\n if (envelope.type.startsWith('storage.')) return `${envelope.type}.result`;\n if (envelope.type === 'inc.subscribe' || envelope.type === 'inc.channel.open') {\n return `${envelope.type}.result`;\n }\n if (envelope.type.startsWith('inc.')) return null;\n return `${envelope.type}.error`;\n}\n\nfunction createMessageHandler(\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n enforceNap: ReturnType<typeof createNapEnforceGate>,\n dispatchNapEnvelope: (windowId: string, envelope: NappletMessage) => void,\n firewallGate: (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop',\n): Runtime['handleMessage'] {\n return (windowId: string, msg: unknown): void => {\n if (typeof msg !== 'object' || msg === null || !('type' in msg)) return;\n const envelope = msg as NappletMessage;\n const dotIdx = envelope.type.indexOf('.');\n if (dotIdx === -1) return;\n // NAP-SHELL establishes the sole capability ingress boundary: valid\n // envelopes remain inert until the source-bound shell.ready transition\n // creates a session. This deliberately precedes capability resolution,\n // ACL, firewall, service routing, and domain dispatch.\n if (!sessionRegistry.getEntryByWindowId(windowId)) return;\n const domain = envelope.type.slice(0, dotIdx);\n if (hooks.isDomainAllowed && !hooks.isDomainAllowed(windowId, domain)) return;\n if (isIdentityOrThemeMessage(envelope) && !createCanonicalDomainResult(envelope)) return;\n\n const caps = resolveCapabilitiesNap(envelope);\n if (caps.senderCap) {\n const result = enforceNap(windowId, caps.senderCap as Capability, envelope);\n if (!result.allowed) {\n const intentDenial = createIntentPolicyDenial(envelope);\n if (intentDenial) {\n hooks.sendToNapplet(windowId, intentDenial);\n return;\n }\n\n const canonicalResult = createCanonicalDomainResult(envelope);\n if (canonicalResult) {\n hooks.sendToNapplet(windowId, canonicalResult);\n return;\n }\n\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const error = formatDenialReason(result.capability);\n const type = denialResponseType(envelope);\n if (type) {\n hooks.sendToNapplet(windowId, { type, id, error } as NappletMessage);\n }\n return;\n }\n }\n\n const verdict = firewallGate(windowId, envelope, caps.senderCap);\n if (verdict === 'drop') return;\n\n dispatchNapEnvelope(windowId, envelope);\n };\n}\n\nfunction createInjectedEvent(hooks: RuntimeAdapter, topic: string, payload: unknown): NostrEvent {\n const uuid = hooks.crypto.randomUUID().replace(/-/g, '').slice(0, 64).padEnd(64, '0');\n return {\n id: uuid,\n pubkey: '0'.repeat(64),\n created_at: Math.floor(Date.now() / 1000),\n kind: 29000,\n tags: [['t', topic]],\n content: JSON.stringify(payload),\n sig: '0'.repeat(128),\n };\n}\n\nfunction createRuntimeInstance(context: RuntimeInstanceContext): Runtime {\n const {\n aclState, firewallState, eventBuffer, hooks, incRuntime, manifestCache, registeredServices,\n replayDetector, serviceRegistry, sessionRegistry, subscriptions, consentHandlerRef,\n } = context;\n const undeclaredServiceConsents = new Set<string>();\n const attachedServices = new Map<string, ServiceHandler>();\n const isDomainAllowed = hooks.isDomainAllowed;\n const sendToNapplet = hooks.sendToNapplet;\n const serviceContext: ServiceRuntimeContext = Object.freeze({\n resolveDTag(windowId: string): string | undefined {\n return sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n },\n listWindowIds(): readonly string[] {\n return Object.freeze(\n sessionRegistry.getAllEntries().map((entry) => entry.windowId),\n );\n },\n sendToEligibleNapplet(windowId: string, message: NappletMessage): boolean {\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n if (!entry || typeof message.type !== 'string') return false;\n const dotIndex = message.type.indexOf('.');\n if (dotIndex <= 0) return false;\n const domain = message.type.slice(0, dotIndex);\n try {\n if (isDomainAllowed && !isDomainAllowed(windowId, domain)) return false;\n const { recipientCap } = resolveCapabilitiesNap(message);\n if (!recipientCap) return false;\n if (!aclState.check('', entry.dTag, entry.aggregateHash, recipientCap as Capability)) {\n return false;\n }\n sendToNapplet(windowId, message);\n return true;\n } catch {\n return false;\n }\n },\n });\n\n function attachService(name: string, handler: ServiceHandler): void {\n attachedServices.set(name, handler);\n try {\n handler.onRegistered?.(serviceContext);\n } catch {\n // A failed optional attachment leaves the service safely context-free.\n }\n }\n\n function detachService(name: string): void {\n const handler = attachedServices.get(name);\n if (!handler) return;\n attachedServices.delete(name);\n try {\n handler.onUnregistered?.();\n } catch {\n // Service cleanup is best-effort and cannot block runtime teardown.\n }\n }\n\n for (const [name, handler] of Object.entries(serviceRegistry)) {\n attachService(name, handler);\n }\n\n return {\n handleMessage: context.handleMessage,\n injectEvent(topic: string, payload: unknown): void {\n eventBuffer.bufferAndDeliver(createInjectedEvent(hooks, topic, payload), null);\n },\n destroy(): void {\n for (const name of attachedServices.keys()) detachService(name);\n manifestCache.persist();\n aclState.persist();\n firewallState.persist();\n replayDetector.clear();\n subscriptions.clear();\n incRuntime.clear();\n eventBuffer.clear();\n registeredServices.clear();\n undeclaredServiceConsents.clear();\n },\n registerConsentHandler(handler: ConsentHandler): void {\n consentHandlerRef.current = handler;\n },\n registerService(name: string, handler: ServiceHandler): void {\n detachService(name);\n serviceRegistry[name] = handler;\n registeredServices.set(name, {\n name: handler.descriptor.name,\n version: handler.descriptor.version,\n description: handler.descriptor.description,\n });\n attachService(name, handler);\n },\n unregisterService(name: string): void {\n detachService(name);\n delete serviceRegistry[name];\n registeredServices.delete(name);\n },\n destroyWindow(windowId: string): void {\n for (const [key] of subscriptions) {\n if (key.startsWith(`${windowId}:`)) {\n subscriptions.delete(key);\n hooks.relayPool?.untrackSubscription(key);\n }\n }\n incRuntime.destroyWindow(windowId);\n notifyServiceWindowDestroyed(windowId, serviceRegistry);\n },\n get sessionRegistry() { return sessionRegistry; },\n get aclState() { return aclState; },\n get firewallState() { return firewallState; },\n get manifestCache() { return manifestCache; },\n };\n}\n\n/**\n * Create a runtime instance with dependency injection via hooks.\n *\n * @param hooks - Host application provides relay pool, auth, config, etc.\n * @returns A Runtime instance ready to handle napplet messages\n *\n * @example\n * ```ts\n * const runtime = createRuntime(hooks);\n * // On incoming message from napplet:\n * runtime.handleMessage(windowId, { type: 'relay.req', id: 'sub1', filters: [] });\n * ```\n */\nexport function createRuntime(hooks: RuntimeAdapter): Runtime {\n const subscriptions = new Map<string, SubscriptionEntry>();\n const serviceRegistry: ServiceRegistry = { ...hooks.services };\n const registeredServices = createRegisteredServices(serviceRegistry);\n const sessionRegistry = createSessionRegistry(hooks.onPendingUpdate);\n let incRuntime: IncRuntime | null = null;\n const aclState = createAclState(hooks.aclPersistence, 'permissive', (mutation) => {\n if (mutation.type === 'revoke' && mutation.capability !== 'relay:read') return;\n for (const entry of sessionRegistry.getAllEntries()) {\n if (entry.dTag === mutation.dTag && entry.aggregateHash === mutation.aggregateHash) {\n incRuntime?.revokeWindow(entry.windowId);\n }\n }\n });\n const firewallState = createFirewallState(hooks.firewallPersistence);\n const manifestCache = createManifestCache(hooks.manifestPersistence);\n const replayDetector = createReplayDetector(\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().replayWindowSeconds\n : undefined,\n );\n\n // Shared consent handler ref — lifted to createRuntime scope so the firewall gate\n // can fire it. The gate is built here (outer scope) but registerConsentHandler is\n // called on the runtime instance (inner scope); the ref bridges both closures.\n const consentHandlerRef: { current: ConsentHandler | null } = { current: null };\n\n // fireConsent: invoked by the gate on 'prompt' decisions (POLICY-02 ask path).\n // On resolution the user's choice is persisted as a per-napplet policy so subsequent\n // messages are NOT re-prompted (T-81-04: bounded consent spam prevention).\n const fireConsent = (windowId: string, napplet: string): void => {\n const handler = consentHandlerRef.current;\n if (!handler) return;\n handler({\n type: 'firewall-policy',\n windowId,\n napplet,\n pubkey: '',\n resolve: (allowed: boolean): void => {\n firewallState.setPolicy(napplet, allowed ? 'allow' : 'deny');\n firewallState.persist();\n },\n });\n };\n\n const enforce = createEnforceGate({\n checkAcl: (pubkey, dTag, aggregateHash, capability) =>\n aclState.check(pubkey, dTag, aggregateHash, capability),\n resolveIdentity: (pubkey) => {\n const entry = sessionRegistry.getEntry(pubkey);\n return entry ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash } : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\n\n const enforceNap = createNapEnforceGate({\n checkAcl: (pubkey, dTag, aggregateHash, capability) =>\n aclState.check(pubkey, dTag, aggregateHash, capability),\n resolveIdentityByWindowId: (windowId) => {\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n return entry\n ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash }\n : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\n\n incRuntime = createIncRuntime(\n hooks,\n sessionRegistry,\n (targetWindowId, message) => enforceNap(targetWindowId, 'relay:read', message).allowed,\n );\n\n const firewallGate = createFirewallGate({ firewallState, sessionRegistry, hooks, fireConsent });\n\n const eventBuffer = createEventBuffer(\n hooks.sendToNapplet,\n sessionRegistry,\n enforce,\n subscriptions,\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().ringBufferSize ?? RING_BUFFER_SIZE\n : undefined,\n );\n\n aclState.load();\n firewallState.load();\n manifestCache.load();\n\n const domainHandlers = createRuntimeDomainHandlers({ hooks, serviceRegistry, sessionRegistry, aclState });\n const dispatchNapEnvelope = createNapEnvelopeDispatcher({\n relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),\n identity: createIdentityHandler({ hooks, serviceRegistry }),\n count: createCountHandler({ hooks, serviceRegistry }),\n inc: incRuntime.handleMessage,\n ...domainHandlers,\n });\n const handleMessage = createMessageHandler(hooks, sessionRegistry, enforceNap, dispatchNapEnvelope, firewallGate);\n\n return createRuntimeInstance({\n hooks,\n serviceRegistry,\n registeredServices,\n replayDetector,\n subscriptions,\n eventBuffer,\n incRuntime,\n sessionRegistry,\n aclState,\n firewallState,\n manifestCache,\n consentHandlerRef,\n handleMessage,\n });\n}\n","\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceRegistry, SendToNapplet } from './types.js';\n\n/**\n * Route a NappletMessage envelope to the matching service handler.\n *\n * Direct NAP-domain services are routed by the domain in their wire\n * message.type (e.g., 'signer.signEvent' -> 'signer' service). INC messages\n * are owned by the INC runtime; their topic values are opaque here.\n *\n * Returns true if a service handled the message, false otherwise.\n *\n * @param windowId - The napplet window that sent the message\n * @param message - The NappletMessage envelope to route\n * @param services - The service registry to look up handlers\n * @param sendToNapplet - Callback to send messages back to the napplet\n * @returns true if a service handled the message, false if no matching service\n *\n * @example\n * ```ts\n * const handled = routeServiceMessage(windowId, msg, services, sendToNapplet);\n * if (!handled) { // fallback handling }\n * ```\n */\nexport function routeServiceMessage(\n windowId: string,\n message: NappletMessage,\n services: ServiceRegistry,\n sendToNapplet: SendToNapplet,\n): boolean {\n // INC emissions belong exclusively to the INC runtime.\n if (message.type === 'inc.emit') return false;\n\n // Direct NAP-domain services: signer.*, relay.*, storage.* route by wire type domain.\n const domain = message.type.split('.')[0];\n const handler = services[domain];\n if (handler) {\n handler.handleMessage(windowId, message, (msg) => sendToNapplet(windowId, msg));\n return true;\n }\n\n return false;\n}\n\n/**\n * Notify all registered service handlers that a napplet window was destroyed.\n * Calls onWindowDestroyed() on every handler that implements it.\n * Errors in individual handlers are caught and silently ignored to prevent\n * one service's cleanup failure from blocking others.\n *\n * @param windowId - The destroyed napplet's window identifier\n * @param services - The service registry containing all handlers\n *\n * @example\n * ```ts\n * notifyServiceWindowDestroyed('window-1', services);\n * ```\n */\nexport function notifyServiceWindowDestroyed(\n windowId: string,\n services: ServiceRegistry,\n): void {\n for (const handler of Object.values(services)) {\n try {\n handler.onWindowDestroyed?.(windowId);\n } catch {\n /* Service cleanup is best-effort — don't let one service block others */\n }\n }\n}\n","import type { EventTemplate, NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';\nimport type { RelayMessage } from '@napplet/nap/relay/types';\n\nimport { matchesAnyFilter, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport type { ReplayDetector } from './replay.js';\nimport {\n createRelayEventResult,\n createRelayEventResultWithHints,\n relayEventResultFromCarrier,\n type RelayEventResult,\n} from './relay-result.js';\nimport type { RuntimeAdapter, ServiceHandler, ServiceRegistry } from './types.js';\n\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\ntype RuntimeRelayMessage = RelayMessage & {\n subId?: string;\n filters?: NostrFilter[];\n event?: EventTemplate | NostrEvent;\n result?: RelayEventResult;\n id?: string;\n relay?: string;\n};\n\ntype RelayHandlerContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n subscriptions: Map<string, SubscriptionEntry>;\n eventBuffer: EventBuffer;\n replayDetector: ReplayDetector;\n};\n\nexport type RelayHandler = (windowId: string, msg: NappletMessage) => void;\n\nexport function createRelayHandler(context: RelayHandlerContext): RelayHandler {\n return function handleRelayMessage(windowId: string, msg: NappletMessage): void {\n const m = msg as RuntimeRelayMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'subscribe':\n handleRelaySubscribe(context, windowId, msg, m);\n return;\n case 'close':\n handleRelayClose(context, windowId, msg, m);\n return;\n case 'publish':\n handleRelayPublish(context, windowId, m);\n return;\n case 'publishEncrypted':\n handleRelayPublishEncrypted(context, windowId, msg);\n return;\n case 'query':\n handleRelayQuery(context, windowId, m);\n return;\n default:\n return;\n }\n };\n}\n\nfunction relayServiceFrom(context: RelayHandlerContext): ServiceHandler | undefined {\n return context.serviceRegistry['relay'] ?? context.serviceRegistry['relay-pool'];\n}\n\nfunction isShellKindQuery(filters: NostrFilter[]): boolean {\n return filters.length > 0 && filters.every((filter) => filter.kinds?.every((kind) => kind >= 29000 && kind < 30000));\n}\n\nfunction sendRelayEvent(hooks: RuntimeAdapter, windowId: string, subId: string, result: RelayEventResult): void {\n hooks.sendToNapplet(windowId, { type: 'relay.event', subId, result } as NappletMessage);\n}\n\nfunction normalizeRelayServiceResponse(resp: NappletMessage): NappletMessage {\n const m = resp as RuntimeRelayMessage;\n if (m.type !== 'relay.event') return resp;\n const result = relayEventResultFromCarrier(m);\n return result ? { type: 'relay.event', subId: m.subId, result } as NappletMessage : resp;\n}\n\nfunction handleRelaySubscribe(\n context: RelayHandlerContext,\n windowId: string,\n msg: NappletMessage,\n m: RuntimeRelayMessage,\n): void {\n const { eventBuffer, hooks, serviceRegistry, subscriptions } = context;\n const subId = m.subId ?? '';\n const filters = m.filters ?? [];\n if (!subId) return;\n\n const subKey = `${windowId}:${subId}`;\n subscriptions.set(subKey, { windowId, filters });\n\n const seenIds = new Set<string>();\n function deliver(event: NostrEvent, relayHints?: string[]): void {\n if (seenIds.has(event.id)) return;\n seenIds.add(event.id);\n if (subscriptions.has(subKey)) {\n sendRelayEvent(hooks, windowId, subId, createRelayEventResultWithHints(event, relayHints));\n }\n }\n\n for (const bufferedEvent of eventBuffer.getBufferedEvents()) {\n if (matchesAnyFilter(bufferedEvent, filters)) deliver(bufferedEvent);\n }\n\n const isShellKind = isShellKindQuery(filters);\n const relayService = relayServiceFrom(context);\n const cacheService = !serviceRegistry['relay'] ? serviceRegistry['cache'] : undefined;\n\n if (!isShellKind && relayService) {\n relayService.handleMessage(windowId, msg, (resp: NappletMessage) => {\n if (!subscriptions.has(subKey)) return;\n hooks.sendToNapplet(windowId, normalizeRelayServiceResponse(resp));\n });\n if (cacheService) {\n cacheService.handleMessage(windowId, msg, (resp: NappletMessage) => {\n if (!subscriptions.has(subKey)) return;\n hooks.sendToNapplet(windowId, normalizeRelayServiceResponse(resp));\n });\n }\n return;\n }\n\n const relayHint = typeof m.relay === 'string' && m.relay.length > 0 ? m.relay : undefined;\n deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver, relayHint);\n}\n\nfunction deliverFromRuntimeBackends(\n context: RelayHandlerContext,\n windowId: string,\n subId: string,\n subKey: string,\n filters: NostrFilter[],\n isShellKind: boolean,\n deliver: (event: NostrEvent, relayHints?: string[]) => void,\n relayHint?: string,\n): void {\n const { hooks } = context;\n const cache = hooks.cache;\n\n if (cache?.isAvailable() && !isShellKind) {\n cache.query(filters)\n .then((cachedEvents) => {\n for (const event of cachedEvents) deliver(event);\n })\n .catch(() => {});\n }\n\n const pool = hooks.relayPool;\n if (!pool?.isAvailable() && !isShellKind) {\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n if (!pool?.isAvailable() || isShellKind) return;\n\n const relayUrls = relayHint ? [relayHint] : pool.selectRelayTier(filters);\n let eoseSent = false;\n const eoseFallbackTimer = setTimeout(() => {\n if (!eoseSent) {\n eoseSent = true;\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n }\n }, 15_000);\n\n const subscription = pool.subscribe(filters, (item) => {\n if (item === 'EOSE') {\n clearTimeout(eoseFallbackTimer);\n if (!eoseSent) {\n eoseSent = true;\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n }\n return;\n }\n const event = item as NostrEvent;\n deliver(event, relayUrls);\n if (cache?.isAvailable() && !isShellKind) {\n try { cache.store(event); } catch { return; }\n }\n }, relayUrls);\n\n pool.trackSubscription(subKey, () => {\n clearTimeout(eoseFallbackTimer);\n subscription.unsubscribe();\n });\n}\n\nfunction handleRelayClose(\n context: RelayHandlerContext,\n windowId: string,\n msg: NappletMessage,\n m: RuntimeRelayMessage,\n): void {\n const { hooks, subscriptions } = context;\n const subId = m.subId ?? '';\n if (!subId) return;\n const subKey = `${windowId}:${subId}`;\n subscriptions.delete(subKey);\n\n const relayService = relayServiceFrom(context);\n if (relayService) relayService.handleMessage(windowId, msg, () => {});\n hooks.relayPool?.untrackSubscription(subKey);\n hooks.sendToNapplet(windowId, { type: 'relay.closed', subId, message: '' } as NappletMessage);\n}\n\nfunction handleRelayPublish(\n context: RelayHandlerContext,\n windowId: string,\n m: RuntimeRelayMessage,\n): void {\n const { hooks, replayDetector } = context;\n const eventTemplate = m.event;\n const id = m.id ?? '';\n if (!eventTemplate || typeof eventTemplate !== 'object') {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, 'invalid event template');\n return;\n }\n\n const signer = hooks.auth.getSigner();\n const signEvent = signer?.signEvent?.bind(signer);\n if (!signEvent) {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, 'no signer configured');\n return;\n }\n\n void (async (): Promise<void> => {\n try {\n const signed = await signEvent(eventTemplate);\n if (!signed) {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, 'signEvent returned no event');\n return;\n }\n const replayResult = replayDetector.reserve(signed);\n if (replayResult !== null) {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, replayResult);\n return;\n }\n publishSignedRelayEvent(context, windowId, id, signed, (ok, error) => {\n if (ok) replayDetector.commit(signed.id);\n else replayDetector.release(signed.id);\n sendRelayPublishResult(hooks, windowId, id, ok, ok ? signed : undefined, error);\n });\n } catch (error) {\n sendRelayPublishResult(\n hooks,\n windowId,\n id,\n false,\n undefined,\n error instanceof Error && error.message ? error.message : 'event signing failed',\n );\n }\n })();\n}\n\nfunction sendRelayPublishResult(\n hooks: RuntimeAdapter,\n windowId: string,\n id: string,\n ok: boolean,\n event?: NostrEvent,\n error?: string,\n): void {\n const result = ok && event\n ? { type: 'relay.publish.result', id, ok: true, event, eventId: event.id }\n : { type: 'relay.publish.result', id, ok: false, error: error ?? 'publish failed' };\n hooks.sendToNapplet(windowId, result as NappletMessage);\n}\n\nfunction publishSignedRelayEvent(\n context: RelayHandlerContext,\n windowId: string,\n id: string,\n signed: NostrEvent,\n reply: (ok: boolean, error?: string) => void,\n): void {\n const { eventBuffer, hooks } = context;\n let settled = false;\n const settle = (ok: boolean, error?: string): void => {\n if (settled) return;\n settled = true;\n if (ok) {\n try { eventBuffer.bufferAndDeliver(signed, windowId); } catch { /* local delivery is best-effort */ }\n }\n reply(ok, error);\n };\n\n const relayService = relayServiceFrom(context);\n if (relayService) {\n const publishMessage = { type: 'relay.publish', id, event: signed } as NappletMessage;\n try {\n relayService.handleMessage(windowId, publishMessage, (response: NappletMessage) => {\n if (\n response.type !== 'relay.publish.result'\n && response.type !== 'relay.publish.error'\n ) {\n return;\n }\n const result = response as NappletMessage & {\n ok?: boolean;\n accepted?: boolean;\n error?: string;\n message?: string;\n };\n const ok = response.type === 'relay.publish.result'\n && (result.ok ?? result.accepted ?? false);\n settle(ok, ok ? undefined : result.error ?? result.message ?? 'publish failed');\n });\n } catch (error) {\n settle(\n false,\n error instanceof Error && error.message ? error.message : 'relay service failed',\n );\n }\n return;\n }\n\n if (!hooks.relayPool?.isAvailable()) {\n settle(false, 'no relay pool available');\n return;\n }\n try {\n const publishResult = hooks.relayPool.publish(signed);\n void Promise.resolve(publishResult).then(\n () => settle(true),\n (error: unknown) => settle(\n false,\n error instanceof Error && error.message ? error.message : 'publish failed',\n ),\n );\n } catch (error) {\n settle(\n false,\n error instanceof Error && error.message ? error.message : 'publish failed',\n );\n }\n}\n\nfunction handleRelayPublishEncrypted(\n context: RelayHandlerContext,\n windowId: string,\n msg: NappletMessage,\n): void {\n const { hooks } = context;\n const id = (msg as RuntimeRelayMessage).id ?? '';\n const eventTemplate = (msg as RuntimeRelayMessage).event;\n const peMsg = msg as NappletMessage & { recipient?: string; encryption?: string };\n const recipient = peMsg.recipient ?? '';\n const encryption = (peMsg.encryption ?? 'nip44') as 'nip04' | 'nip44' | string;\n\n const replyPe = (ok: boolean, extra: Record<string, unknown> = {}) => {\n hooks.sendToNapplet(windowId, { type: 'relay.publishEncrypted.result', id, ok, ...extra } as NappletMessage);\n };\n\n if (!recipient) { replyPe(false, { error: 'missing recipient' }); return; }\n if (encryption !== 'nip44' && encryption !== 'nip04') {\n replyPe(false, { error: `unsupported encryption scheme: ${encryption}` });\n return;\n }\n const peSigner = hooks.auth.getSigner();\n if (!peSigner) { replyPe(false, { error: 'no signer configured' }); return; }\n if (!eventTemplate || typeof eventTemplate !== 'object') {\n replyPe(false, { error: 'invalid event template' });\n return;\n }\n\n publishEncrypted(context, windowId, id, recipient, encryption, eventTemplate, replyPe);\n}\n\nfunction publishEncrypted(\n context: RelayHandlerContext,\n windowId: string,\n id: string,\n recipient: string,\n encryption: 'nip04' | 'nip44' | string,\n eventTemplate: EventTemplate,\n replyPe: (ok: boolean, extra?: Record<string, unknown>) => void,\n): void {\n const { hooks } = context;\n const peSigner = hooks.auth.getSigner();\n if (!peSigner) return;\n\n (async (): Promise<void> => {\n try {\n const plaintext = String((eventTemplate as { content?: unknown }).content ?? '');\n const ciphertext: string = encryption === 'nip44'\n ? (await peSigner.nip44?.encrypt(recipient, plaintext)) ?? ''\n : (await peSigner.nip04?.encrypt(recipient, plaintext)) ?? '';\n const eventWithCiphertext = { ...eventTemplate, content: ciphertext };\n const signed = await peSigner.signEvent?.(eventWithCiphertext);\n if (!signed) { replyPe(false, { error: 'signEvent returned null' }); return; }\n\n publishSignedRelayEvent(context, windowId, id, signed, (ok, error) => {\n replyPe(ok, ok\n ? { event: signed, eventId: signed.id }\n : { error: error ?? 'publish failed' });\n });\n } catch (err) {\n replyPe(false, { error: (err as Error)?.message ?? 'encryption failed' });\n }\n })();\n}\n\nfunction handleRelayQuery(\n context: RelayHandlerContext,\n windowId: string,\n m: RuntimeRelayMessage,\n): void {\n const { hooks, serviceRegistry, eventBuffer } = context;\n const id = m.id ?? '';\n const filters = m.filters ?? [];\n\n const seenIds = new Set<string>();\n const events: RelayEventResult[] = [];\n for (const event of eventBuffer.getBufferedEvents()) {\n if (matchesAnyFilter(event, filters) && !seenIds.has(event.id)) {\n seenIds.add(event.id);\n events.push(createRelayEventResult(event));\n }\n }\n\n let settled = false;\n let fallbackTimer: unknown;\n\n function settle(): void {\n if (settled) return;\n settled = true;\n clearTimeout(fallbackTimer);\n hooks.sendToNapplet(windowId, { type: 'relay.query.result', id, events } as NappletMessage);\n }\n\n // Shell-kind queries (kinds 29000-29999): buffer-only, no relay delegation\n if (isShellKindQuery(filters)) {\n settle();\n return;\n }\n\n const relayService = relayServiceFrom(context);\n const cacheService = !serviceRegistry['relay'] ? serviceRegistry['cache'] : undefined;\n\n if (relayService) {\n const svc = relayService;\n // Delegate to registered relay service via a synthesized relay.subscribe\n const subId = '__query__:' + id;\n const subscribeMsg: RuntimeRelayMessage = {\n type: 'relay.subscribe',\n id,\n subId,\n filters,\n };\n if (typeof m.relay === 'string' && m.relay.length > 0) {\n subscribeMsg.relay = m.relay;\n }\n\n let openBackends = cacheService ? 2 : 1;\n\n function onBackendEose(): void {\n openBackends--;\n if (openBackends <= 0) {\n // Tear down the delegated subscriptions\n const closeMsg = { type: 'relay.close', id, subId } as NappletMessage;\n svc.handleMessage(windowId, closeMsg, () => {});\n if (cacheService) {\n cacheService.handleMessage(windowId, closeMsg, () => {});\n }\n settle();\n }\n }\n\n function makeCollector(): (resp: NappletMessage) => void {\n return function collector(resp: NappletMessage): void {\n const r = resp as RuntimeRelayMessage;\n const result = r.type === 'relay.event' ? relayEventResultFromCarrier(r) : null;\n if (result && !seenIds.has(result.event.id)) {\n seenIds.add(result.event.id);\n events.push(result);\n } else if (r.type === 'relay.eose') {\n onBackendEose();\n }\n };\n }\n\n fallbackTimer = setTimeout(() => {\n const closeMsg = { type: 'relay.close', id, subId } as NappletMessage;\n svc.handleMessage(windowId, closeMsg, () => {});\n if (cacheService) {\n cacheService.handleMessage(windowId, closeMsg, () => {});\n }\n settle();\n }, 15_000);\n\n svc.handleMessage(windowId, subscribeMsg as NappletMessage, makeCollector());\n if (cacheService) {\n cacheService.handleMessage(windowId, subscribeMsg as NappletMessage, makeCollector());\n }\n return;\n }\n\n // No registered relay service — fall back to runtime hooks (cache + relay pool)\n const cache = hooks.cache;\n const pool = hooks.relayPool;\n\n if (cache?.isAvailable()) {\n cache.query(filters)\n .then((cachedEvents) => {\n for (const event of cachedEvents) {\n if (!seenIds.has(event.id)) {\n seenIds.add(event.id);\n events.push(createRelayEventResult(event));\n }\n }\n })\n .catch(() => {});\n }\n\n if (!pool?.isAvailable()) {\n settle();\n return;\n }\n\n const relayHint = typeof m.relay === 'string' && m.relay.length > 0 ? m.relay : undefined;\n const relayUrls = relayHint ? [relayHint] : pool.selectRelayTier(filters);\n\n let poolSubscription: { unsubscribe(): void } | undefined;\n\n fallbackTimer = setTimeout(() => {\n poolSubscription?.unsubscribe();\n settle();\n }, 15_000);\n\n poolSubscription = pool.subscribe(filters, (item) => {\n if (item === 'EOSE') {\n clearTimeout(fallbackTimer);\n poolSubscription?.unsubscribe();\n settle();\n return;\n }\n const event = item as NostrEvent;\n if (!seenIds.has(event.id)) {\n seenIds.add(event.id);\n events.push(createRelayEventResultWithHints(event, relayUrls));\n }\n if (cache?.isAvailable()) {\n try { cache.store(event); } catch { return; }\n }\n }, relayUrls);\n}\n","import type { NappletMessage } from '@napplet/core';\n\n/**\n * Fixed non-sensitive theme used when a runtime-owned response cannot obtain\n * a registered theme service. Keep synchronized with the reference service\n * default without importing @kehto/services (which depends on runtime).\n */\nexport const RUNTIME_THEME_FALLBACK = {\n colors: { background: '#0a0a0a', text: '#e0e0e0', primary: '#7aa2f7' },\n} as const;\n\nconst IDENTITY_SAFE_DEFAULTS = {\n 'identity.getPublicKey': { pubkey: '' },\n 'identity.getRelays': { relays: {} },\n 'identity.getProfile': { profile: null },\n 'identity.getFollows': { pubkeys: [] },\n 'identity.getList': { entries: [] },\n 'identity.getZaps': { zaps: [] },\n 'identity.getMutes': { pubkeys: [] },\n 'identity.getBlocked': { pubkeys: [] },\n 'identity.getBadges': { badges: [] },\n} as const;\n\ntype RuntimeOwnedIdentityOrThemeRequest = keyof typeof IDENTITY_SAFE_DEFAULTS | 'theme.get';\n\nfunction isRuntimeOwnedIdentityOrThemeRequest(type: string): type is RuntimeOwnedIdentityOrThemeRequest {\n return type === 'theme.get' || type in IDENTITY_SAFE_DEFAULTS;\n}\n\nfunction createRuntimeOwnedResultEnvelope(type: string, id: string): NappletMessage {\n return Object.assign({ type } as NappletMessage, { id });\n}\n\nfunction intentRequestDetails(message: NappletMessage): { archetype: string; action: string } {\n const request = (message as NappletMessage & {\n request?: { archetype?: unknown; action?: unknown };\n }).request;\n return {\n archetype: typeof request?.archetype === 'string' ? request.archetype : '',\n action: typeof request?.action === 'string' && request.action.length > 0\n ? request.action\n : 'open',\n };\n}\n\nfunction createIntentInvokeRejection(message: NappletMessage, error: string): NappletMessage {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const { archetype, action } = intentRequestDetails(message);\n return {\n type: 'intent.invoke.result',\n id,\n result: { ok: false, archetype, action, handled: false, error },\n } as NappletMessage;\n}\n\n/** Return whether a message belongs to a runtime-owned identity/theme domain. */\nexport function isIdentityOrThemeMessage(message: NappletMessage): boolean {\n return message.type.startsWith('identity.') || message.type.startsWith('theme.');\n}\n\n/**\n * Shape a non-sensitive NAP-INTENT policy denial on its sanctioned result wire.\n *\n * Only napplet-originated intent requests have denial responses. Runtime push,\n * result, obsolete error, and unknown actions return `undefined` so a forged\n * source envelope cannot manufacture another response.\n *\n * @param message - Incoming intent request envelope.\n * @returns A fixed denial result for a sanctioned request, or `undefined`.\n */\nexport function createIntentPolicyDenial(\n message: NappletMessage,\n): NappletMessage | undefined {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n if (message.type === 'intent.invoke') {\n return createIntentInvokeRejection(message, 'invoke rejected');\n }\n if (message.type === 'intent.available' || message.type === 'intent.handlers') {\n return {\n type: `${message.type}.result`,\n id,\n error: 'intent request denied',\n } as NappletMessage;\n }\n return undefined;\n}\n\n/** Shape canonical replies when no NAP-INTENT service is registered. */\nexport function createIntentServiceUnavailableResult(\n message: NappletMessage,\n): NappletMessage | undefined {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n if (message.type === 'intent.invoke') {\n return createIntentInvokeRejection(message, 'no handler');\n }\n if (message.type === 'intent.available') {\n const archetype = (message as NappletMessage & { archetype?: unknown }).archetype;\n return {\n type: 'intent.available.result',\n id,\n availability: {\n archetype: typeof archetype === 'string' ? archetype : '',\n available: false,\n candidates: [],\n hasDefault: false,\n },\n } as NappletMessage;\n }\n if (message.type === 'intent.handlers') {\n return { type: 'intent.handlers.result', id, handlers: [] } as NappletMessage;\n }\n return undefined;\n}\n\n/**\n * Shape an allowlisted runtime-owned response with the ordinary request id.\n *\n * Unsupported identity/theme messages deliberately return undefined so the\n * dispatch boundary can ignore them rather than synthesize a generic error.\n */\nexport function createCanonicalDomainResult(message: NappletMessage): NappletMessage | undefined {\n if (!isRuntimeOwnedIdentityOrThemeRequest(message.type)) return undefined;\n\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n if (message.type === 'theme.get') {\n return Object.assign(\n createRuntimeOwnedResultEnvelope('theme.get.result', id),\n { theme: RUNTIME_THEME_FALLBACK },\n );\n }\n\n return Object.assign(\n createRuntimeOwnedResultEnvelope(`${message.type}.result`, id),\n IDENTITY_SAFE_DEFAULTS[message.type],\n );\n}\n","import type { NappletMessage } from '@napplet/core';\n\nimport type { RuntimeAdapter, ServiceRegistry } from './types.js';\nimport { createCanonicalDomainResult } from './domain-results.js';\n\ntype IdentityHandlerContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n};\n\nexport type IdentityHandler = (windowId: string, msg: NappletMessage) => void;\n\nexport function createIdentityHandler(context: IdentityHandlerContext): IdentityHandler {\n return function handleIdentityMessage(windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const identityService = serviceRegistry['identity'];\n if (identityService) {\n identityService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n\n const action = msg.type.slice('identity.'.length);\n const signer = hooks.auth.getSigner();\n const fallback = createCanonicalDomainResult(msg);\n if (!fallback) return;\n const sendResult = (payload: Record<string, unknown> = {}) => {\n hooks.sendToNapplet(windowId, { ...fallback, ...payload } as NappletMessage);\n };\n\n switch (action) {\n case 'getPublicKey':\n if (!signer) { sendResult({ pubkey: '' }); return; }\n Promise.resolve(signer.getPublicKey?.())\n .then((pubkey) => sendResult({ pubkey: pubkey ?? '' }))\n .catch(() => sendResult({ pubkey: '' }));\n return;\n case 'getRelays':\n if (!signer) { sendResult(); return; }\n Promise.resolve(signer.getRelays?.() ?? {})\n .then((relays) => sendResult({ relays }))\n .catch(() => sendResult());\n return;\n case 'getProfile': sendResult({ profile: null }); return;\n case 'getFollows': sendResult({ pubkeys: [] }); return;\n case 'getList': sendResult({ entries: [] }); return;\n case 'getZaps': sendResult({ zaps: [] }); return;\n case 'getMutes': sendResult({ pubkeys: [] }); return;\n case 'getBlocked': sendResult({ pubkeys: [] }); return;\n case 'getBadges': sendResult({ badges: [] }); return;\n default: return;\n }\n };\n}\n","import type { NappletMessage, NostrFilter } from '@napplet/core';\n\nimport type { RuntimeAdapter, ServiceRegistry } from './types.js';\n\ntype RuntimeCountMessage = NappletMessage & {\n id?: unknown;\n filters?: unknown;\n options?: unknown;\n};\n\ntype CountHandlerContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n};\n\nexport type CountHandler = (windowId: string, msg: NappletMessage) => void;\n\nexport function createCountHandler(context: CountHandlerContext): CountHandler {\n return function handleCountMessage(windowId: string, msg: NappletMessage): void {\n if (msg.type !== 'count.query') return;\n\n const message = msg as RuntimeCountMessage;\n const id = typeof message.id === 'string' ? message.id : '';\n const filters = message.filters;\n\n if (!isValidFilterArray(filters)) {\n sendCountError(context.hooks, windowId, id, 'invalid-filter', 'count.query requires a non-empty filters array');\n return;\n }\n\n if (message.options !== undefined && !isPlainObject(message.options)) {\n sendCountError(context.hooks, windowId, id, 'invalid-filter', 'count.query options must be an object when present');\n return;\n }\n\n const service = context.serviceRegistry['count'];\n if (!service) {\n sendCountError(\n context.hooks,\n windowId,\n id,\n 'count-unavailable',\n 'count service unavailable; refusing to emulate counts by fetching event payloads',\n );\n return;\n }\n\n service.handleMessage(windowId, msg, (resp: NappletMessage) => {\n context.hooks.sendToNapplet(windowId, normalizeCountResult(resp, id));\n });\n };\n}\n\nfunction normalizeCountResult(resp: NappletMessage, id: string): NappletMessage {\n const value = resp as NappletMessage & {\n id?: unknown;\n ok?: unknown;\n count?: unknown;\n approximate?: unknown;\n hll?: unknown;\n relays?: unknown;\n error?: unknown;\n reason?: unknown;\n };\n\n return {\n type: 'count.query.result',\n id: typeof value.id === 'string' ? value.id : id,\n ok: value.ok === true,\n ...(typeof value.count === 'number' && Number.isFinite(value.count) && value.count >= 0\n ? { count: Math.floor(value.count) }\n : {}),\n ...(typeof value.approximate === 'boolean' ? { approximate: value.approximate } : {}),\n ...(typeof value.hll === 'string' ? { hll: value.hll } : {}),\n ...(Array.isArray(value.relays) ? { relays: value.relays.filter((relay): relay is string => typeof relay === 'string') } : {}),\n ...(typeof value.error === 'string' ? { error: value.error } : {}),\n ...(typeof value.reason === 'string' ? { reason: value.reason } : {}),\n } as NappletMessage;\n}\n\nfunction sendCountError(\n hooks: RuntimeAdapter,\n windowId: string,\n id: string,\n error: string,\n reason: string,\n): void {\n hooks.sendToNapplet(windowId, {\n type: 'count.query.result',\n id,\n ok: false,\n error,\n reason,\n } as NappletMessage);\n}\n\nfunction isValidFilterArray(value: unknown): value is NostrFilter[] {\n return Array.isArray(value)\n && value.length > 0\n && value.every((filter) => isPlainObject(filter));\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { NappletMessage } from '@napplet/core';\nimport type { IncMessage } from '@napplet/nap/inc/types';\n\nimport type { SessionRegistry } from './session-registry.js';\nimport type { RuntimeAdapter } from './types.js';\n\ninterface IncChannel {\n channelId: string;\n peerA: string;\n peerB: string;\n}\n\ntype RuntimeIncMessage = IncMessage & {\n id?: string;\n topic?: string;\n payload?: unknown;\n target?: string;\n channelId?: string;\n};\n\ntype IncState = {\n subscriptions: Map<string, Set<string>>;\n channels: Map<string, IncChannel>;\n channelsByWindow: Map<string, Set<string>>;\n};\n\ntype ChannelOpenAuthorizer = (targetWindowId: string, message: NappletMessage) => boolean;\n\nconst MAX_CHANNELS_PER_WINDOW = 32;\n\nexport interface IncRuntime {\n handleMessage(windowId: string, msg: NappletMessage): void;\n destroyWindow(windowId: string): void;\n /**\n * Close a live window's established channels after its open-time authority is revoked.\n *\n * @param windowId - Internal identity of the session whose open-time authority changed\n */\n revokeWindow(windowId: string): void;\n clear(): void;\n}\n\nexport function createIncRuntime(\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n authorizeChannelOpen: ChannelOpenAuthorizer,\n): IncRuntime {\n const state: IncState = {\n subscriptions: new Map(),\n channels: new Map(),\n channelsByWindow: new Map(),\n };\n\n return {\n handleMessage(windowId: string, msg: NappletMessage): void {\n handleIncMessage(state, hooks, sessionRegistry, authorizeChannelOpen, windowId, msg);\n },\n destroyWindow(windowId: string): void {\n removeWindowChannels(state, hooks, windowId);\n removeWindowSubscriptions(state, windowId);\n },\n revokeWindow(windowId: string): void {\n revokeWindowChannels(state, hooks, windowId);\n removeWindowSubscriptions(state, windowId);\n },\n clear(): void {\n state.subscriptions.clear();\n state.channels.clear();\n state.channelsByWindow.clear();\n },\n };\n}\n\nfunction addChannel(state: IncState, channelId: string, peerA: string, peerB: string): void {\n state.channels.set(channelId, { channelId, peerA, peerB });\n for (const windowId of [peerA, peerB]) {\n let set = state.channelsByWindow.get(windowId);\n if (!set) {\n set = new Set();\n state.channelsByWindow.set(windowId, set);\n }\n set.add(channelId);\n }\n}\n\nfunction removeChannel(state: IncState, channelId: string): void {\n const channel = state.channels.get(channelId);\n if (!channel) return;\n state.channels.delete(channelId);\n for (const windowId of [channel.peerA, channel.peerB]) {\n const set = state.channelsByWindow.get(windowId);\n if (!set) continue;\n set.delete(channelId);\n if (set.size === 0) state.channelsByWindow.delete(windowId);\n }\n}\n\nfunction peerOf(state: IncState, channelId: string, self: string): string | null {\n const channel = state.channels.get(channelId);\n if (!channel) return null;\n if (channel.peerA === self) return channel.peerB;\n if (channel.peerB === self) return channel.peerA;\n return null;\n}\n\nfunction resolveTarget(sessionRegistry: SessionRegistry, target: string): string | null {\n return sessionRegistry.getWindowIdByDTag(target) ?? null;\n}\n\nfunction hasReachedChannelLimit(state: IncState, windowId: string): boolean {\n return (state.channelsByWindow.get(windowId)?.size ?? 0) >= MAX_CHANNELS_PER_WINDOW;\n}\n\nfunction handleIncMessage(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n authorizeChannelOpen: ChannelOpenAuthorizer,\n windowId: string,\n msg: NappletMessage,\n): void {\n const m = msg as RuntimeIncMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'emit': handleEmit(state, hooks, sessionRegistry, windowId, m); return;\n case 'subscribe': handleSubscribe(state, hooks, windowId, m); return;\n case 'unsubscribe': handleUnsubscribe(state, windowId, m); return;\n case 'channel.open': handleChannelOpen(state, hooks, sessionRegistry, authorizeChannelOpen, windowId, m); return;\n case 'channel.emit': handleChannelEmit(state, hooks, sessionRegistry, windowId, m); return;\n case 'channel.broadcast': handleChannelBroadcast(state, hooks, sessionRegistry, windowId, m); return;\n case 'channel.list': handleChannelList(state, hooks, sessionRegistry, windowId, m); return;\n case 'channel.close': handleChannelClose(state, hooks, windowId, m); return;\n default: return;\n }\n}\n\nfunction handleEmit(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const topic = m.topic ?? '';\n const sender = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n if (!sender) return;\n if (!topic) return;\n const subscribers = state.subscriptions.get(topic);\n if (!subscribers) return;\n for (const subscriberWindowId of subscribers) {\n if (subscriberWindowId !== windowId) {\n hooks.sendToNapplet(subscriberWindowId, {\n type: 'inc.event',\n topic,\n sender,\n ...(m.payload === undefined ? {} : { payload: m.payload }),\n } as NappletMessage);\n }\n }\n}\n\nfunction handleSubscribe(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const id = m.id ?? '';\n const topic = m.topic ?? '';\n if (!topic) {\n hooks.sendToNapplet(windowId, { type: 'inc.subscribe.result', id, error: 'missing topic' } as NappletMessage);\n return;\n }\n let subscriptions = state.subscriptions.get(topic);\n if (!subscriptions) {\n subscriptions = new Set();\n state.subscriptions.set(topic, subscriptions);\n }\n subscriptions.add(windowId);\n hooks.sendToNapplet(windowId, { type: 'inc.subscribe.result', id } as NappletMessage);\n}\n\nfunction handleUnsubscribe(state: IncState, windowId: string, m: RuntimeIncMessage): void {\n const topic = m.topic ?? '';\n if (!topic) return;\n const subscriptions = state.subscriptions.get(topic);\n if (!subscriptions) return;\n subscriptions.delete(windowId);\n if (subscriptions.size === 0) state.subscriptions.delete(topic);\n}\n\nfunction handleChannelOpen(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n authorizeChannelOpen: ChannelOpenAuthorizer,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const id = m.id ?? '';\n const peerWindow = resolveTarget(sessionRegistry, m.target ?? '');\n const openerDTag = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n const peerDTag = peerWindow ? sessionRegistry.getEntryByWindowId(peerWindow)?.dTag : undefined;\n if (!peerWindow || !openerDTag || !peerDTag) {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'target not found' } as NappletMessage);\n return;\n }\n if (!authorizeChannelOpen(peerWindow, m)) {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'target denied' } as NappletMessage);\n return;\n }\n if (hasReachedChannelLimit(state, windowId) || hasReachedChannelLimit(state, peerWindow)) {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'channel limit reached' } as NappletMessage);\n return;\n }\n const channelId = hooks.crypto.randomUUID().replace(/-/g, '').slice(0, 32);\n addChannel(state, channelId, windowId, peerWindow);\n try {\n hooks.sendToNapplet(peerWindow, { type: 'inc.channel.opened', channelId, peer: openerDTag } as NappletMessage);\n } catch {\n removeChannel(state, channelId);\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'target unavailable' } as NappletMessage);\n return;\n }\n try {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, channelId, peer: peerDTag } as NappletMessage);\n } catch {\n teardownChannel(state, hooks, channelId);\n }\n}\n\nfunction handleChannelEmit(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const peer = peerOf(state, m.channelId ?? '', windowId);\n const sender = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n if (peer && sender) {\n hooks.sendToNapplet(peer, {\n type: 'inc.channel.event',\n channelId: m.channelId ?? '',\n sender,\n ...(m.payload === undefined ? {} : { payload: m.payload }),\n } as NappletMessage);\n }\n}\n\nfunction handleChannelBroadcast(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channels = state.channelsByWindow.get(windowId);\n const sender = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n if (!channels || !sender) return;\n for (const channelId of channels) {\n const peer = peerOf(state, channelId, windowId);\n if (peer) {\n hooks.sendToNapplet(peer, {\n type: 'inc.channel.event',\n channelId,\n sender,\n ...(m.payload === undefined ? {} : { payload: m.payload }),\n } as NappletMessage);\n }\n }\n}\n\nfunction handleChannelList(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channels = [];\n const set = state.channelsByWindow.get(windowId);\n if (set) {\n for (const channelId of set) {\n const peer = peerOf(state, channelId, windowId);\n const peerDTag = peer ? sessionRegistry.getEntryByWindowId(peer)?.dTag : undefined;\n if (peerDTag) channels.push({ id: channelId, peer: peerDTag });\n }\n }\n hooks.sendToNapplet(windowId, { type: 'inc.channel.list.result', id: m.id ?? '', channels } as NappletMessage);\n}\n\nfunction handleChannelClose(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channelId = m.channelId ?? '';\n if (!peerOf(state, channelId, windowId)) return;\n teardownChannel(state, hooks, channelId);\n}\n\nfunction teardownChannel(state: IncState, hooks: RuntimeAdapter, channelId: string, destroyedWindowId?: string): void {\n const channel = state.channels.get(channelId);\n if (!channel) return;\n\n try {\n if (destroyedWindowId) {\n const survivor = peerOf(state, channelId, destroyedWindowId);\n if (survivor) {\n try {\n hooks.sendToNapplet(survivor, { type: 'inc.channel.closed', channelId, reason: 'peer destroyed' } as NappletMessage);\n } catch { /* channel teardown must still complete */ }\n }\n return;\n }\n for (const peer of [channel.peerA, channel.peerB]) {\n try {\n hooks.sendToNapplet(peer, { type: 'inc.channel.closed', channelId } as NappletMessage);\n } catch { /* channel teardown must still complete */ }\n }\n } finally {\n removeChannel(state, channelId);\n }\n}\n\nfunction removeWindowSubscriptions(state: IncState, windowId: string): void {\n for (const [topic, subscriptions] of state.subscriptions) {\n subscriptions.delete(windowId);\n if (subscriptions.size === 0) state.subscriptions.delete(topic);\n }\n}\n\nfunction removeWindowChannels(state: IncState, hooks: RuntimeAdapter, windowId: string): void {\n const channelIds = state.channelsByWindow.get(windowId);\n if (!channelIds) return;\n for (const channelId of Array.from(channelIds)) {\n teardownChannel(state, hooks, channelId, windowId);\n }\n}\n\nfunction revokeWindowChannels(state: IncState, hooks: RuntimeAdapter, windowId: string): void {\n const channelIds = state.channelsByWindow.get(windowId);\n if (!channelIds) return;\n for (const channelId of Array.from(channelIds)) {\n teardownChannel(state, hooks, channelId);\n }\n}\n","/**\n * state-handler.ts — Storage NAP request handler using persistence hooks.\n *\n * Handles napplet storage operations (get, set, remove, keys) via the\n * canonical `@napplet/nap/storage` NIP-5D envelope surface. Delegates\n * storage to StatePersistence. No localStorage, no legacy NIP-01 dispatch.\n *\n * Per-instance scope (NAP-STORAGE, napplet/naps#3): every request carries an\n * optional `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"`\n * folds a stable per-window discriminator (`@i/<windowId>:`) into the key so\n * that multiple open windows of the *same* napplet keep isolated, independently\n * persisted state. Instancing is a property of the data (per call), not the\n * napplet — there is no napplet-wide mode. The napplet never sees or names an\n * instance identifier; it sets `scope` and trusts the shell's Unique + Stable\n * guarantees. `\"shared\"` (or absent) addresses the napplet-wide namespace,\n * byte-identical to the historical behavior.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { StorageMessage } from '@napplet/nap/storage/types';\nimport type { SendToNapplet, StatePersistence } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { AclStateContainer } from './acl-state.js';\n\n/** Canonical NAP-STORAGE scope values. `scope` absent ⇔ `\"shared\"`. */\ntype StorageScope = 'shared' | 'instance';\n\n/**\n * Reserved key segment that marks the per-instance sub-namespace inside a\n * napplet's `(dTag, aggregateHash)` bucket. Server-side only — napplets never\n * see it. Used for `scope: \"instance\"` requests (NAP-STORAGE per-instance scope).\n */\nconst INSTANCE_MARKER = '@i/';\n\n/**\n * Build the per-window instance sub-namespace segment: `@i/<windowId>:`.\n *\n * The discriminator is derived from the runtime's `windowId`, which the shell\n * keeps stable across reload / workspace restore, so per-instance storage\n * persists across sessions (the spec's \"Stable\" guarantee). Two distinct\n * windows of the same napplet get distinct segments (the \"Unique\" guarantee).\n * This format is internal and never exposed to the napplet.\n */\nfunction instanceSegment(windowId: string): string {\n return `${INSTANCE_MARKER}${windowId}:`;\n}\n\n/**\n * Build the storage key for a napplet user key.\n *\n * For `scope: \"shared\"` (default) this is byte-identical to the historical key.\n * For `scope: \"instance\"` the runtime folds in the opaque per-window segment —\n * transparently, with no napplet involvement (the napplet only sets the wire\n * field; it never sees the resulting key shape).\n */\nfunction scopedKey(\n dTag: string,\n aggregateHash: string,\n userKey: string,\n instance = false,\n windowId = '',\n): string {\n const segment = instance ? instanceSegment(windowId) : '';\n return `napplet-state:${dTag}:${aggregateHash}:${segment}${userKey}`;\n}\n\n/** Build a legacy scoped key that includes pubkey (for migration reads). */\nfunction legacyScopedKey(pubkey: string, dTag: string, aggregateHash: string, userKey: string): string {\n return `napplet-state:${pubkey}:${dTag}:${aggregateHash}:${userKey}`;\n}\n\n/** Compute byte length of a UTF-8 string without TextEncoder (ES2022-safe). */\nfunction byteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Handle a NIP-5D NAP storage message from a napplet.\n * Routes to the canonical four `@napplet/nap/storage` actions:\n * - `storage.get` → `storage.get.result` `{ value: string | null }`\n * - `storage.set` → `storage.set.result` `{ ok: boolean }` (canonical only checks `error`)\n * - `storage.remove` → `storage.remove.result` `{ ok: boolean }`\n * - `storage.keys` → `storage.keys.result` `{ keys: string[] }`\n *\n * `storage.clear` is NOT in the canonical `@napplet/nap/storage` union (it was a\n * kehto unilateral extension); attempts produce a `storage.clear.result` envelope\n * with an `error` field set. Internal lifecycle cleanup still uses the\n * `cleanupNappState()` helper below — it is not napplet-reachable.\n *\n * **Per-instance scope (NAP-STORAGE / napplet/naps#3):** each request MAY carry\n * `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"` addresses a\n * per-window sub-namespace keyed by `windowId`, so multiple windows of the same\n * napplet keep isolated state; `\"shared\"` (or absent) addresses the napplet-wide\n * namespace, byte-identical to historical behavior. An unrecognized `scope` value\n * is an invalid request and produces a `.result` envelope with `error` set.\n *\n * **Deviation note (Phase 15 to decide):** Set/remove results emit both `ok`\n * (legacy compat) and an `error` field on failure. Canonical `@napplet/nap/storage`\n * only specifies the optional `error`; napplets check `!result.error` for success.\n * Emitting `ok` preserves backward compatibility with existing in-tree callers.\n * Phase 15 (v1.2 release prep) decides whether to drop `ok` pre-release.\n *\n * @param windowId - The window identifier of the requesting napplet\n * @param msg - The NappletMessage containing the storage request\n * @param sendToNapplet - Transport function to send responses\n * @param sessionRegistry - Identity registry for looking up napplet session identity\n * @param aclState - ACL state for quota checks\n * @param statePersistence - State storage backend\n *\n * @example\n * ```ts\n * import { handleStorageNap } from '@kehto/runtime';\n *\n * handleStorageNap(windowId, { type: 'storage.get', id: 'q1', key: 'draft' },\n * sendToNapplet, sessionRegistry, aclState, statePersistence);\n * ```\n */\nexport function handleStorageNap(\n windowId: string,\n msg: NappletMessage,\n sendToNapplet: SendToNapplet,\n sessionRegistry: SessionRegistry,\n aclState: AclStateContainer,\n statePersistence: StatePersistence,\n): void {\n const m = msg as StorageMessage & {\n id?: string;\n key?: string;\n value?: string;\n scope?: string;\n };\n const id = m.id ?? '';\n const action = msg.type.split('.')[1];\n\n function sendResult(payload: Record<string, unknown>): void {\n sendToNapplet(windowId, { type: `${msg.type}.result`, id, ...payload } as NappletMessage);\n }\n function sendErrorNap(error: string): void {\n sendToNapplet(windowId, { type: `${msg.type}.result`, id, error } as NappletMessage);\n }\n\n // Identity lookup via windowId (NIP-5D path)\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n if (!entry) { sendErrorNap('not registered'); return; }\n\n const { dTag, aggregateHash, pubkey } = entry;\n const prefix = `napplet-state:${dTag}:${aggregateHash}:`;\n const legacyPrefix = pubkey ? `napplet-state:${pubkey}:${dTag}:${aggregateHash}:` : '';\n\n // NAP-STORAGE per-call scope. Absent ⇔ \"shared\" (byte-identical to history).\n // An unrecognized value is an invalid request — the shell MUST return an error.\n if (m.scope !== undefined && m.scope !== 'shared' && m.scope !== 'instance') {\n sendErrorNap(`invalid scope: ${String(m.scope)} (expected \"shared\" or \"instance\")`);\n return;\n }\n const scope: StorageScope = m.scope === 'instance' ? 'instance' : 'shared';\n const isInstance = scope === 'instance';\n // The per-window sub-namespace for this request, when instance-scoped.\n const instancePrefix = `${prefix}${instanceSegment(windowId)}`;\n const keyFor = (userKey: string): string =>\n scopedKey(dTag, aggregateHash, userKey, isInstance, windowId);\n\n switch (action) {\n case 'get': {\n const key = m.key as string;\n if (!key) { sendErrorNap('missing key'); return; }\n // Instance scope addresses a fresh per-window namespace with no legacy data —\n // read only the instance key. Shared scope keeps the triple-read migration.\n if (isInstance) {\n sendResult({ value: statePersistence.get(keyFor(key)) });\n break;\n }\n const newKey = scopedKey(dTag, aggregateHash, key);\n // Triple-read for migration: new format, legacy-with-pubkey, old prefix\n let result = statePersistence.get(newKey);\n if (result === null && pubkey) {\n result = statePersistence.get(legacyScopedKey(pubkey, dTag, aggregateHash, key));\n }\n if (result === null && pubkey) {\n result = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);\n }\n // Canonical @napplet/nap/storage: `value: string | null` — null ⇔ missing.\n sendResult({ value: result });\n break;\n }\n case 'set': {\n const key = m.key as string;\n const value = (m.value as string) ?? '';\n if (!key) { sendErrorNap('missing key'); return; }\n const quota = aclState.getStateQuota(pubkey ?? '', dTag, aggregateHash);\n const sk = keyFor(key);\n const newWriteBytes = byteLength(sk + value);\n // Quota is per napplet identity: `prefix` spans both the shared namespace and\n // every per-instance sub-key, so instance writes draw from the same budget.\n const existingBytes = statePersistence.calculateBytes(prefix, key);\n if (existingBytes + newWriteBytes > quota) {\n sendErrorNap(`quota exceeded: napplet state limit is ${quota} bytes`);\n return;\n }\n const success = statePersistence.set(sk, value);\n sendResult({ ok: success });\n break;\n }\n case 'remove': {\n const key = m.key as string;\n if (!key) { sendErrorNap('missing key'); return; }\n statePersistence.remove(keyFor(key));\n // legacyPrefix exists only while `pubkey` is non-empty (legacy AUTH sessions).\n // Suppress \"unused binding\" warnings: we intentionally retain the computation\n // so future migration lands at call sites, not here.\n void legacyPrefix;\n sendResult({ ok: true });\n break;\n }\n case 'clear': {\n // storage.clear was a kehto unilateral extension; it is NOT in the canonical\n // @napplet/nap/storage union. Napplets hitting this branch receive a\n // storage.clear.result envelope with the error field set. Internal lifecycle\n // cleanup uses cleanupNappState() below (not napplet-reachable).\n sendErrorNap('storage.clear is not in @napplet/nap/storage; action not supported');\n break;\n }\n case 'keys': {\n // Instance scope sees only this window's sub-namespace.\n if (isInstance) {\n const instKeys = statePersistence.keys(instancePrefix);\n const instanceKeySet = new Set<string>();\n for (const k of instKeys) {\n instanceKeySet.add(k.startsWith(instancePrefix) ? k.slice(instancePrefix.length) : k);\n }\n sendResult({ keys: Array.from(instanceKeySet) });\n break;\n }\n const newKeys = statePersistence.keys(prefix);\n const legacyKeys = legacyPrefix ? statePersistence.keys(legacyPrefix) : [];\n const userKeySet = new Set<string>();\n // Shared scope excludes per-instance sub-keys (reserved `@i/` marker) so an\n // instance-scoped napplet's per-window data never leaks into a shared listing.\n for (const k of newKeys) {\n if (!k.startsWith(prefix)) { userKeySet.add(k); continue; }\n const userKey = k.slice(prefix.length);\n if (userKey.startsWith(INSTANCE_MARKER)) continue;\n userKeySet.add(userKey);\n }\n for (const k of legacyKeys) userKeySet.add(k.startsWith(legacyPrefix) ? k.slice(legacyPrefix.length) : k);\n sendResult({ keys: Array.from(userKeySet) });\n break;\n }\n default:\n sendErrorNap(`unknown storage action: ${action}`);\n break;\n }\n}\n\n/**\n * Remove all state entries for a napplet identity.\n * Clears both new-format and legacy-format keys for completeness.\n * Used during napplet cleanup when a window is closed.\n *\n * @param pubkey - The napplet's pubkey (needed for legacy key cleanup)\n * @param dTag - The napplet's dTag\n * @param aggregateHash - The napplet's build hash\n * @param statePersistence - State storage backend\n *\n * @example\n * ```ts\n * import { cleanupNappState } from '@kehto/runtime';\n *\n * cleanupNappState(pubkey, dTag, aggregateHash, statePersistence);\n * ```\n */\nexport function cleanupNappState(\n pubkey: string,\n dTag: string,\n aggregateHash: string,\n statePersistence: StatePersistence,\n): void {\n // Clear new format\n const prefix = `napplet-state:${dTag}:${aggregateHash}:`;\n statePersistence.clear(prefix);\n // Clear legacy format (includes pubkey)\n const legacyPrefix = `napplet-state:${pubkey}:${dTag}:${aggregateHash}:`;\n statePersistence.clear(legacyPrefix);\n}\n","import type { NappletMessage } from '@napplet/core';\nimport {\n createCanonicalDomainResult,\n createIntentServiceUnavailableResult,\n} from './domain-results.js';\n\nimport type { AclStateContainer } from './acl-state.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport { handleStorageNap } from './state-handler.js';\nimport type { RuntimeAdapter, ServiceRegistry } from './types.js';\n\ntype DomainHandler = (windowId: string, msg: NappletMessage) => void;\n\ntype RuntimeDomainContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n};\n\nexport type RuntimeDomainHandlers = {\n storage: DomainHandler;\n media: DomainHandler;\n keys: DomainHandler;\n notify: DomainHandler;\n theme: DomainHandler;\n config: DomainHandler;\n resource: DomainHandler;\n cvm: DomainHandler;\n outbox: DomainHandler;\n upload: DomainHandler;\n intent: DomainHandler;\n link: DomainHandler;\n common: DomainHandler;\n lists: DomainHandler;\n serial: DomainHandler;\n ble: DomainHandler;\n webrtc: DomainHandler;\n dm: DomainHandler;\n};\n\nexport function createRuntimeDomainHandlers(context: RuntimeDomainContext): RuntimeDomainHandlers {\n return {\n storage: (windowId, msg) => handleStorageMessage(context, windowId, msg),\n media: (windowId, msg) => handleMediaMessage(context, windowId, msg),\n keys: (windowId, msg) => handleKeysMessage(context, windowId, msg),\n notify: (windowId, msg) => handleNotifyMessage(context, windowId, msg),\n theme: (windowId, msg) => handleThemeMessage(context, windowId, msg),\n config: (windowId, msg) => handleServiceOnlyMessage(context, 'config', windowId, msg),\n resource: (windowId, msg) => handleServiceOnlyMessage(context, 'resource', windowId, msg),\n cvm: (windowId, msg) => handleServiceOnlyMessage(context, 'cvm', windowId, msg),\n outbox: (windowId, msg) => handleServiceOnlyMessage(context, 'outbox', windowId, msg),\n upload: (windowId, msg) => handleServiceOnlyMessage(context, 'upload', windowId, msg),\n intent: (windowId, msg) => handleServiceOnlyMessage(context, 'intent', windowId, msg),\n link: (windowId, msg) => handleServiceOnlyMessage(context, 'link', windowId, msg),\n common: (windowId, msg) => handleServiceOnlyMessage(context, 'common', windowId, msg),\n lists: (windowId, msg) => handleServiceOnlyMessage(context, 'lists', windowId, msg),\n serial: (windowId, msg) => handleServiceOnlyMessage(context, 'serial', windowId, msg),\n ble: (windowId, msg) => handleServiceOnlyMessage(context, 'ble', windowId, msg),\n webrtc: (windowId, msg) => handleServiceOnlyMessage(context, 'webrtc', windowId, msg),\n dm: (windowId, msg) => handleServiceOnlyMessage(context, 'dm', windowId, msg),\n };\n}\n\nfunction handleStorageMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { aclState, hooks, sessionRegistry } = context;\n handleStorageNap(windowId, msg, hooks.sendToNapplet, sessionRegistry, aclState, hooks.statePersistence);\n}\n\nfunction handleMediaMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const mediaService = serviceRegistry['media'];\n if (mediaService) {\n mediaService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n if (msg.type === 'media.session.create') {\n const m = msg as NappletMessage & { id?: string; owner?: string; sessionId?: string };\n if (m.owner !== 'napplet' && m.owner !== 'shell') {\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n error: 'missing owner',\n } as NappletMessage);\n return;\n }\n if (m.owner === 'shell') {\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n owner: 'shell',\n error: 'unsupported owner mode',\n } as NappletMessage);\n return;\n }\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n sessionId: m.sessionId ?? '',\n owner: m.owner,\n } as NappletMessage);\n }\n}\n\nfunction handleKeysMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry, sessionRegistry } = context;\n if (msg.type === 'keys.forward') {\n if (!sessionRegistry.getEntryByWindowId(windowId)) return;\n forwardHotkey(hooks, msg);\n return;\n }\n const keysService = serviceRegistry['keys'];\n if (keysService) {\n keysService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n if (msg.type === 'keys.registerAction') sendRegisterActionResult(hooks, windowId, msg);\n}\n\nfunction forwardHotkey(hooks: RuntimeAdapter, msg: NappletMessage): void {\n const m = msg as NappletMessage & {\n key?: string; code?: string;\n ctrl?: boolean; alt?: boolean; shift?: boolean; meta?: boolean;\n };\n hooks.hotkeys.executeHotkeyFromForward({\n key: m.key ?? '',\n code: m.code ?? '',\n ctrlKey: !!m.ctrl,\n altKey: !!m.alt,\n shiftKey: !!m.shift,\n metaKey: !!m.meta,\n });\n}\n\nfunction sendRegisterActionResult(hooks: RuntimeAdapter, windowId: string, msg: NappletMessage): void {\n const m = msg as NappletMessage & {\n id?: string; action?: { id: string; defaultKey?: string };\n };\n hooks.sendToNapplet(windowId, {\n type: 'keys.registerAction.result',\n id: m.id ?? '',\n actionId: m.action?.id ?? '',\n } as NappletMessage);\n}\n\nfunction handleNotifyMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const notifyService = serviceRegistry['notify'];\n if (notifyService) {\n notifyService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n if (msg.type === 'notify.send') {\n const m = msg as NappletMessage & { id?: string };\n hooks.sendToNapplet(windowId, { type: 'notify.send.result', id: m.id ?? '', notificationId: `shell-${Date.now()}` } as NappletMessage);\n } else if (msg.type === 'notify.permission.request') {\n const m = msg as NappletMessage & { id?: string };\n hooks.sendToNapplet(windowId, { type: 'notify.permission.result', id: m.id ?? '', granted: true } as NappletMessage);\n }\n}\n\nfunction handleThemeMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const themeService = serviceRegistry['theme'];\n if (themeService) {\n themeService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n const fallback = createCanonicalDomainResult(msg);\n if (fallback) hooks.sendToNapplet(windowId, fallback);\n}\n\nfunction handleServiceOnlyMessage(\n context: RuntimeDomainContext,\n name: 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'link' | 'common' | 'lists' | 'serial' | 'ble' | 'webrtc' | 'dm',\n windowId: string,\n msg: NappletMessage,\n): void {\n if (\n name === 'intent'\n && msg.type !== 'intent.invoke'\n && msg.type !== 'intent.available'\n && msg.type !== 'intent.handlers'\n ) {\n return;\n }\n const service = context.serviceRegistry[name];\n if (!service) {\n if (name === 'intent') {\n const result = createIntentServiceUnavailableResult(msg);\n if (result) context.hooks.sendToNapplet(windowId, result);\n }\n return;\n }\n service.handleMessage(windowId, msg, (resp: NappletMessage) => context.hooks.sendToNapplet(windowId, resp));\n}\n","// @kehto/runtime — Browser-agnostic protocol engine for the napplet protocol.\n\nexport type {\n RuntimeAdapter,\n SendToNapplet,\n RelayPoolAdapter,\n RelaySubscriptionHandle,\n CacheAdapter,\n AuthAdapter,\n Signer,\n ConfigAdapter,\n HotkeyAdapter,\n AclPersistence,\n ManifestPersistence,\n StatePersistence,\n CryptoAdapter,\n WindowManagerAdapter,\n RelayConfigAdapter,\n DmAdapter,\n ConsentRequest,\n ConsentHandler,\n SessionEntry,\n NappKeyEntry, // @deprecated — use SessionEntry\n PendingUpdate,\n PendingUpdateNotifier,\n ManifestCacheEntry,\n AclEntryExternal,\n AclCheckEvent,\n FirewallPersistence,\n FirewallEvent,\n ServiceHandler,\n ServiceRuntimeContext,\n ServiceRegistry,\n CompatibilityReport,\n ServiceInfo,\n ShellSecretPersistence,\n GuidPersistence,\n HashVerifierAdapter,\n VerificationCacheEntry,\n RuntimeConfigOverrides,\n NappletMessage,\n} from './types.js';\n\nexport { createEnforceGate, createNapEnforceGate, resolveCapabilitiesNap, formatDenialReason } from './enforce.js';\nexport type { EnforceResult, EnforceConfig, NapEnforceConfig, IdentityResolver, AclChecker, NapMessage } from './enforce.js';\n\nexport { createSessionRegistry, createNappKeyRegistry } from './session-registry.js';\nexport type { SessionRegistry, NappKeyRegistry } from './session-registry.js';\n\nexport { createAclState } from './acl-state.js';\nexport type { AclMutation, AclMutationObserver, AclStateContainer } from './acl-state.js';\n\nexport { createFirewallState } from './firewall-state.js';\nexport type { FirewallStateContainer } from './firewall-state.js';\n\nexport { createManifestCache } from './manifest-cache.js';\nexport type { ManifestCache } from './manifest-cache.js';\n\nexport { createReplayDetector } from './replay.js';\nexport type { ReplayDetector } from './replay.js';\n\nexport { createEventBuffer, matchesFilter, matchesAnyFilter, RING_BUFFER_SIZE } from './event-buffer.js';\nexport type { EventBuffer, SubscriptionEntry } from './event-buffer.js';\n\nexport {\n createRelayEventResult,\n createRelayEventResultWithHints,\n relayEventResultFromCarrier,\n} from './relay-result.js';\nexport type { RelayEventCarrier, RelayEventResult, RelayEventSidecar } from './relay-result.js';\n\nexport { createRuntime } from './runtime.js';\nexport type { Runtime } from './runtime.js';\n\nexport { handleStorageNap, cleanupNappState } from './state-handler.js';\n\nexport { routeServiceMessage, notifyServiceWindowDestroyed } from './service-dispatch.js';\n\nexport type { Capability } from '@kehto/acl/capabilities';\nexport { ALL_CAPABILITIES } from '@kehto/acl/capabilities';\nexport type { ServiceDescriptor } from './types.js';\n"],"mappings":"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","names":["createState","serialize","deserialize","onBackendEose","makeCollector"]}
1
+ {"version":3,"sources":["../src/enforce.ts","../src/session-registry.ts","../src/acl-state.ts","../src/firewall-state.ts","../src/manifest-cache.ts","../src/replay.ts","../src/event-buffer.ts","../src/relay-result.ts","../src/runtime.ts","../src/service-dispatch.ts","../src/relay-handler.ts","../src/domain-results.ts","../src/identity-handler.ts","../src/count-handler.ts","../src/inc-handler.ts","../src/state-handler.ts","../src/domain-handlers.ts","../src/index.ts"],"sourcesContent":["\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { NapMessage } from '@kehto/acl';\nimport type { AclCheckEvent } from './types.js';\n\n// Re-export NAP capability resolution for consumers who import through enforce.ts\nexport { resolveCapabilitiesNap } from '@kehto/acl';\nexport type { NapMessage } from '@kehto/acl';\n\n/**\n * Result of an enforcement check.\n *\n * @param allowed - Whether the capability check passed\n * @param capability - The capability that was checked (human-readable string)\n * @param reason - Why the decision was reached (v1.7 CLASS-03 / D7). Always set on return.\n */\nexport interface EnforceResult {\n allowed: boolean;\n capability: Capability;\n /**\n * Why the decision was reached. Always set on the return path.\n * Distinct from AclCheckEvent.reason (which is optional for backwards compat).\n */\n reason: 'allowed' | 'capability-missing';\n}\n\n/**\n * Identity lookup function type — resolves a pubkey to its full identity.\n * Provided by sessionRegistry at runtime.\n */\nexport type IdentityResolver = (pubkey: string) => { dTag: string; aggregateHash: string } | undefined;\n\n/**\n * ACL check function type — performs the actual capability check.\n * Provided by @kehto/acl's check() at runtime, or by the legacy aclStore.check().\n */\nexport type AclChecker = (pubkey: string, dTag: string, aggregateHash: string, capability: Capability) => boolean;\n\n/**\n * Enforcement gate configuration.\n *\n * @param checkAcl - The ACL check function (wraps @kehto/acl or legacy aclStore)\n * @param resolveIdentity - Maps pubkey to full identity (dTag, aggregateHash)\n * @param onAclCheck - Optional audit callback. Called on every enforce() check\n * with the identity, capability, and decision.\n */\nexport interface EnforceConfig {\n checkAcl: AclChecker;\n resolveIdentity: IdentityResolver;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate with the given configuration.\n *\n * Returns a function that checks a single capability for a given pubkey.\n * Every call is logged to the audit callback.\n *\n * @param config - Enforcement configuration with ACL checker, identity resolver, and audit hooks\n * @returns An enforce function that checks capabilities and logs decisions\n *\n * @example\n * ```ts\n * const gate = createEnforceGate({\n * checkAcl: aclStore.check,\n * resolveIdentity: (pk) => nappKeyRegistry.getEntry(pk),\n * onAclCheck: hooks.onAclCheck,\n * });\n * const result = gate('abc123...', 'relay:write');\n * // result.allowed === true | false\n * ```\n */\nexport function createEnforceGate(config: EnforceConfig): (pubkey: string, capability: Capability, message?: unknown[]) => EnforceResult {\n const { checkAcl, resolveIdentity, onAclCheck } = config;\n\n return function enforce(pubkey: string, capability: Capability, message?: unknown[]): EnforceResult {\n const entry = resolveIdentity(pubkey);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n\n const allowed = checkAcl(pubkey, dTag, aggregateHash, capability);\n\n // Audit logging — every check, both allows and denials\n const identity = { pubkey, dTag, hash: aggregateHash };\n const decision = allowed ? 'allow' as const : 'deny' as const;\n const reason = allowed ? 'allowed' as const : 'capability-missing' as const;\n\n if (onAclCheck) {\n onAclCheck({ identity, capability, decision, message, reason });\n }\n\n return { allowed, capability, reason };\n };\n}\n\n/**\n * Enforcement gate configuration for NIP-5D NAP handlers.\n * Uses windowId for identity resolution instead of pubkey (which is '' in NIP-5D sessions).\n *\n * @param checkAcl - The ACL check function\n * @param resolveIdentityByWindowId - Maps windowId to identity (dTag, aggregateHash).\n * @param onAclCheck - Optional audit callback, called on every enforceNap() check\n */\nexport interface NapEnforceConfig {\n checkAcl: AclChecker;\n resolveIdentityByWindowId: (windowId: string) => { dTag: string; aggregateHash: string } | undefined;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate for NIP-5D NAP message handlers.\n *\n * Unlike createEnforceGate (which resolves identity by pubkey), this factory\n * resolves identity by windowId — necessary for NIP-5D sessions where pubkey is ''.\n *\n * @param config - NAP enforcement configuration\n * @returns An enforceNap function that resolves identity by windowId and\n * delegates to the ACL check.\n *\n * @example\n * ```ts\n * const gate = createNapEnforceGate({\n * checkAcl: aclStore.check,\n * resolveIdentityByWindowId: (wid) => sessionRegistry.getEntryByWindowId(wid),\n * onAclCheck: hooks.onAclCheck,\n * });\n * const result = gate('win-1', 'relay:write', { type: 'relay.publish' });\n * // result.allowed === true | false\n * ```\n */\nexport function createNapEnforceGate(config: NapEnforceConfig): (windowId: string, capability: Capability, message?: NapMessage) => EnforceResult {\n const { checkAcl, resolveIdentityByWindowId, onAclCheck } = config;\n\n return function enforceNap(windowId: string, capability: Capability, message?: NapMessage): EnforceResult {\n const entry = resolveIdentityByWindowId(windowId);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n\n // NIP-5D: pass empty string for pubkey - toKey() ignores it (uses dTag:hash).\n const allowed = checkAcl('', dTag, aggregateHash, capability);\n\n const identity = { pubkey: '', dTag, hash: aggregateHash };\n const decision = allowed ? 'allow' as const : 'deny' as const;\n const reason = allowed ? 'allowed' as const : 'capability-missing' as const;\n\n if (onAclCheck) {\n onAclCheck({ identity, capability, decision, message, reason });\n }\n\n return { allowed, capability, reason };\n };\n}\n\n/**\n * Format a denial reason string with the standard 'denied:' prefix.\n *\n * @param capability - The denied capability name\n * @returns Formatted denial string, e.g., 'denied: relay:write'\n *\n * @example\n * ```ts\n * formatDenialReason('relay:write')\n * // => 'denied: relay:write'\n * ```\n */\nexport function formatDenialReason(capability: Capability): string {\n return `denied: ${capability}`;\n}\n","\nimport type { SessionEntry, PendingUpdate, PendingUpdateNotifier } from './types.js';\n\n/**\n * Bidirectional registry mapping windowIds to verified napplet pubkeys.\n * Maintained by the runtime after successful AUTH handshakes.\n *\n * @example\n * ```ts\n * const registry = createSessionRegistry();\n * registry.register('win-1', entry);\n * const pubkey = registry.getPubkey('win-1');\n * ```\n */\nexport interface SessionRegistry {\n /** Register a napplet entry, mapping windowId to pubkey and vice versa. */\n register(windowId: string, entry: SessionEntry): void;\n /** Unregister a napplet by windowId, removing both mappings. */\n unregister(windowId: string): void;\n /** Get the pubkey associated with a windowId. */\n getPubkey(windowId: string): string | undefined;\n /** Get the full entry for a napplet pubkey. */\n getEntry(pubkey: string): SessionEntry | undefined;\n /** Get the windowId for a napplet pubkey. */\n getWindowId(pubkey: string): string | undefined;\n /**\n * Resolve an unambiguous live napplet dTag to its internal window ID.\n *\n * A duplicate dTag fails closed rather than selecting an arbitrary session.\n * Window IDs and pubkeys remain internal transport identities and never\n * resolve through this lookup.\n *\n * @param dTag - Runtime-attested napplet dTag\n * @returns The sole owning window ID, or undefined when missing or ambiguous\n */\n getWindowIdByDTag(dTag: string): string | undefined;\n /** Check if a windowId has a registered napplet. */\n isRegistered(windowId: string): boolean;\n /** Get all registered napplet entries. */\n getAllEntries(): SessionEntry[];\n /** Get the instance GUID for a window. */\n getInstanceId(windowId: string): string | undefined;\n /** Set a pending update for a window (napplet reconnected with different hash). */\n setPendingUpdate(windowId: string, update: PendingUpdate): void;\n /** Get a pending update for a window. */\n getPendingUpdate(windowId: string): PendingUpdate | undefined;\n /** Clear a pending update for a window. */\n clearPendingUpdate(windowId: string): void;\n /**\n * Get the full entry for a napplet by windowId directly.\n * NIP-5D: Required for sessions where pubkey is '' (identity established via originRegistry).\n * Unlike getEntry(pubkey), this works when pubkey is empty.\n */\n getEntryByWindowId(windowId: string): SessionEntry | undefined;\n /** Clear all registrations and pending updates. */\n clear(): void;\n}\n\n/** @deprecated Use SessionRegistry. Will be removed in v0.9.0. */\nexport type NappKeyRegistry = SessionRegistry;\n\n/**\n * Create a new SessionRegistry instance.\n *\n * @param notifier - Optional callback invoked when pending updates change\n * @returns A SessionRegistry instance\n *\n * @example\n * ```ts\n * const registry = createSessionRegistry((windowId) => {\n * console.log('Pending update changed for', windowId);\n * });\n * ```\n */\nexport function createSessionRegistry(notifier?: PendingUpdateNotifier): SessionRegistry {\n const byWindowId = new Map<string, string>();\n const byPubkey = new Map<string, SessionEntry>();\n const byWindowIdEntry = new Map<string, SessionEntry>();\n const pendingUpdates = new Map<string, PendingUpdate>();\n\n return {\n register(windowId: string, entry: SessionEntry): void {\n byWindowId.set(windowId, entry.pubkey);\n byPubkey.set(entry.pubkey, entry);\n byWindowIdEntry.set(windowId, entry);\n },\n\n unregister(windowId: string): void {\n const pubkey = byWindowId.get(windowId);\n if (pubkey) {\n byPubkey.delete(pubkey);\n byWindowId.delete(windowId);\n }\n byWindowIdEntry.delete(windowId);\n pendingUpdates.delete(windowId);\n },\n\n getPubkey(windowId: string): string | undefined {\n return byWindowId.get(windowId);\n },\n\n getEntry(pubkey: string): SessionEntry | undefined {\n return byPubkey.get(pubkey);\n },\n\n getWindowId(pubkey: string): string | undefined {\n return byPubkey.get(pubkey)?.windowId;\n },\n\n getWindowIdByDTag(dTag: string): string | undefined {\n let resolvedWindowId: string | undefined;\n for (const [windowId, entry] of byWindowIdEntry) {\n if (entry.dTag !== dTag) continue;\n if (resolvedWindowId !== undefined) return undefined;\n resolvedWindowId = windowId;\n }\n return resolvedWindowId;\n },\n\n isRegistered(windowId: string): boolean {\n return byWindowId.has(windowId);\n },\n\n getAllEntries(): SessionEntry[] {\n return Array.from(byWindowIdEntry.values());\n },\n\n getInstanceId(windowId: string): string | undefined {\n const pubkey = byWindowId.get(windowId);\n if (!pubkey) return undefined;\n return byPubkey.get(pubkey)?.instanceId;\n },\n\n getEntryByWindowId(windowId: string): SessionEntry | undefined {\n return byWindowIdEntry.get(windowId);\n },\n\n setPendingUpdate(windowId: string, update: PendingUpdate): void {\n pendingUpdates.set(windowId, update);\n notifier?.(windowId);\n },\n\n getPendingUpdate(windowId: string): PendingUpdate | undefined {\n return pendingUpdates.get(windowId);\n },\n\n clearPendingUpdate(windowId: string): void {\n pendingUpdates.delete(windowId);\n notifier?.(windowId);\n },\n\n clear(): void {\n byWindowId.clear();\n byPubkey.clear();\n byWindowIdEntry.clear();\n pendingUpdates.clear();\n },\n };\n}\n\n/** @deprecated Use createSessionRegistry. Will be removed in v0.9.0. */\nexport const createNappKeyRegistry = createSessionRegistry;\n","/**\n * acl-state.ts — ACL state container with persistence hooks.\n *\n * Wraps @kehto/acl's pure functions with persistence via\n * AclPersistence. No localStorage or DOM references.\n */\n\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { AclState, Identity } from '@kehto/acl';\nimport {\n createState, check, grant, revoke, block, unblock,\n serialize, deserialize, getQuota,\n CAP_RELAY_READ, CAP_RELAY_WRITE, CAP_CACHE_READ, CAP_CACHE_WRITE,\n CAP_HOTKEY_FORWARD,\n CAP_STATE_READ, CAP_STATE_WRITE,\n toKey,\n} from '@kehto/acl';\nimport type { AclPersistence, AclEntryExternal } from './types.js';\n\nconst CAP_IDENTITY_READ = 1 << 5; // 32 (reclaimed from CAP_SIGN_EVENT)\nconst CAP_KEYS_BIND = 1 << 6; // 64 (reclaimed from CAP_SIGN_NIP04)\nconst CAP_KEYS_FORWARD = 1 << 7; // 128 (reclaimed from CAP_SIGN_NIP44)\nconst CAP_MEDIA_CONTROL = 1 << 10; // 1024\nconst CAP_NOTIFY_SEND = 1 << 11; // 2048\nconst CAP_NOTIFY_CHANNEL = 1 << 12; // 4096\nconst CAP_THEME_READ = 1 << 13; // 8192\nconst CAP_CONFIG_READ = 1 << 14; // 16384 (v1.7 Phase 39 NAP-CONFIG)\nconst CAP_RESOURCE_FETCH = 1 << 15; // 32768 (v1.7 Phase 40 NAP-RESOURCE)\n// 1 << 16 (65536) RETIRED — was identity:decrypt (v1.8); removed as a spec\n// violation. Left as a permanent gap; do NOT reuse this bit (persisted ACL\n// grants are bitfields — reassigning it would silently re-grant old state).\nconst CAP_CVM_CALL = 1 << 17; // 131072 (NAP-CVM ContextVM bridge)\nconst CAP_OUTBOX_READ = 1 << 18; // 262144 (NAP-OUTBOX read-side routing)\nconst CAP_OUTBOX_WRITE = 1 << 19; // 524288 (NAP-OUTBOX shell-signed publish)\nconst CAP_UPLOAD_WRITE = 1 << 20; // 1048576 (NAP-UPLOAD shell-mediated upload)\nconst CAP_INTENT_READ = 1 << 21; // 2097152 (NAP-INTENT archetype introspection)\nconst CAP_INTENT_WRITE = 1 << 22; // 4194304 (NAP-INTENT cross-napplet dispatch)\nconst CAP_DM_READ = 1 << 23; // 8388608 (NAP-DM read/history/subscription)\nconst CAP_DM_WRITE = 1 << 24; // 16777216 (NAP-DM send)\nconst CAP_FS_READ = 1 << 25; // 33554432 (NAP-FS discovery/read/watch)\nconst CAP_FS_WRITE = 1 << 26; // 67108864 (NAP-FS mutations)\n\nconst CAP_MAP: Record<Capability, number> = {\n 'relay:read': CAP_RELAY_READ,\n 'relay:write': CAP_RELAY_WRITE,\n 'cache:read': CAP_CACHE_READ,\n 'cache:write': CAP_CACHE_WRITE,\n 'hotkey:forward': CAP_HOTKEY_FORWARD,\n 'state:read': CAP_STATE_READ,\n 'state:write': CAP_STATE_WRITE,\n 'identity:read': CAP_IDENTITY_READ,\n 'keys:bind': CAP_KEYS_BIND,\n 'keys:forward': CAP_KEYS_FORWARD,\n 'media:control': CAP_MEDIA_CONTROL,\n 'notify:send': CAP_NOTIFY_SEND,\n 'notify:channel': CAP_NOTIFY_CHANNEL,\n 'theme:read': CAP_THEME_READ,\n 'config:read': CAP_CONFIG_READ,\n 'resource:fetch': CAP_RESOURCE_FETCH,\n 'cvm:call': CAP_CVM_CALL,\n 'outbox:read': CAP_OUTBOX_READ,\n 'outbox:write': CAP_OUTBOX_WRITE,\n 'upload:write': CAP_UPLOAD_WRITE,\n 'intent:read': CAP_INTENT_READ,\n 'intent:write': CAP_INTENT_WRITE,\n 'dm:read': CAP_DM_READ,\n 'dm:write': CAP_DM_WRITE,\n 'fs:read': CAP_FS_READ,\n 'fs:write': CAP_FS_WRITE,\n};\n\nconst RUNTIME_CAP_ALL = Object.values(CAP_MAP).reduce((bits, bit) => bits | bit, 0);\n\nfunction capToBit(cap: Capability): number {\n return CAP_MAP[cap] ?? 0;\n}\n\n/** Convert a bitfield to an array of capability strings. */\nfunction bitsToCapabilities(bits: number): Capability[] {\n const result: Capability[] = [];\n for (const [cap, bit] of Object.entries(CAP_MAP)) {\n if (bits & bit) result.push(cap as Capability);\n }\n return result;\n}\n\nfunction toIdentity(pubkey: string, dTag: string, hash: string): Identity {\n return { pubkey, dTag, hash };\n}\n\n/** An ACL authority change that can invalidate runtime-owned routes. */\nexport type AclMutation =\n | { readonly type: 'revoke'; readonly dTag: string; readonly aggregateHash: string; readonly capability: Capability }\n | { readonly type: 'block'; readonly dTag: string; readonly aggregateHash: string };\n\n/** Receives synchronous ACL authority changes after persistence succeeds. */\nexport type AclMutationObserver = (mutation: AclMutation) => void;\n\n/**\n * ACL state container — wraps @kehto/acl's pure functions with\n * persistence and a convenient imperative API.\n *\n * @example\n * ```ts\n * const aclState = createAclState(persistence);\n * aclState.load();\n * const allowed = aclState.check(pubkey, dTag, hash, 'relay:read');\n * ```\n */\nexport interface AclStateContainer {\n check(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): boolean;\n grant(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void;\n revoke(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void;\n block(pubkey: string, dTag: string, aggregateHash: string): void;\n unblock(pubkey: string, dTag: string, aggregateHash: string): void;\n isBlocked(pubkey: string, dTag: string, aggregateHash: string): boolean;\n getEntry(pubkey: string, dTag: string, aggregateHash: string): AclEntryExternal | undefined;\n getAllEntries(): AclEntryExternal[];\n getStateQuota(pubkey: string, dTag: string, aggregateHash: string): number;\n persist(): void;\n load(): void;\n clear(): void;\n}\n\n/**\n * Create an ACL state container backed by @kehto/acl and persisted\n * via the given persistence hooks.\n *\n * @param persistence - Storage backend for ACL state\n * @param defaultPolicy - Default ACL policy for unknown identities\n * @param onMutation - Internal runtime observer for revoke and block facts\n * @returns An AclStateContainer instance\n *\n * @example\n * ```ts\n * const aclState = createAclState(persistence, 'permissive');\n * aclState.load();\n * ```\n */\nexport function createAclState(\n persistence: AclPersistence,\n defaultPolicy: 'permissive' | 'restrictive' = 'permissive',\n onMutation?: AclMutationObserver,\n): AclStateContainer {\n let state: AclState = createState(defaultPolicy);\n\n function ensureRuntimeDefaultEntry(id: Identity): void {\n if (state.defaultPolicy !== 'permissive') return;\n if (state.entries[toKey(id)]) return;\n state = grant(state, id, RUNTIME_CAP_ALL);\n }\n\n return {\n check(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): boolean {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n return check(state, id, capToBit(capability));\n },\n\n grant(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = grant(state, id, capToBit(capability));\n },\n\n revoke(pubkey: string, dTag: string, aggregateHash: string, capability: Capability): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = revoke(state, id, capToBit(capability));\n onMutation?.({ type: 'revoke', dTag, aggregateHash, capability });\n },\n\n block(pubkey: string, dTag: string, aggregateHash: string): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = block(state, id);\n onMutation?.({ type: 'block', dTag, aggregateHash });\n },\n\n unblock(pubkey: string, dTag: string, aggregateHash: string): void {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n ensureRuntimeDefaultEntry(id);\n state = unblock(state, id);\n },\n\n isBlocked(pubkey: string, dTag: string, aggregateHash: string): boolean {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n // A blocked identity fails all checks — check with all runtime caps.\n // If blocked, check returns false even for all caps\n return !check(state, id, RUNTIME_CAP_ALL) && this.getEntry(pubkey, dTag, aggregateHash)?.blocked === true;\n },\n\n getEntry(pubkey: string, dTag: string, aggregateHash: string): AclEntryExternal | undefined {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n const key = `${id.dTag}:${id.hash}`;\n const entry = state.entries[key];\n if (!entry) return undefined;\n return {\n pubkey: pubkey || '',\n capabilities: bitsToCapabilities(entry.caps),\n blocked: entry.blocked,\n stateQuota: entry.quota,\n };\n },\n\n getAllEntries(): AclEntryExternal[] {\n return Object.entries(state.entries).map(([, entry]) => {\n return {\n pubkey: '',\n capabilities: bitsToCapabilities(entry.caps),\n blocked: entry.blocked,\n stateQuota: entry.quota,\n };\n });\n },\n\n getStateQuota(pubkey: string, dTag: string, aggregateHash: string): number {\n const id = toIdentity(pubkey, dTag, aggregateHash);\n return getQuota(state, id);\n },\n\n persist(): void {\n try {\n persistence.persist(serialize(state));\n } catch { /* persistence is best-effort */ }\n },\n\n load(): void {\n try {\n const raw = persistence.load();\n if (!raw) return;\n state = deserialize(raw);\n } catch {\n state = createState(defaultPolicy);\n }\n },\n\n clear(): void {\n state = createState(defaultPolicy);\n try { persistence.persist(''); } catch { /* best-effort */ }\n },\n };\n}\n","/**\n * firewall-state.ts — Firewall state container with persistence hooks.\n *\n * Wraps @kehto/firewall's pure functions with persistence via\n * FirewallPersistence. No localStorage or DOM references.\n *\n * Two independent `let`-bound cells:\n * - `config` — immutable FirewallConfig; persisted on each mutation.\n * - `counters` — ephemeral FirewallState (token-buckets + burst counters);\n * in-memory only, reset on reload (RUNTIME-03).\n *\n * CRITICAL: `evaluate` reassigns `counters = result.newState` on every call.\n * Without this, token buckets never advance and flood events never escalate\n * from 'flag' to 'block'.\n */\n\nimport type {\n Observation,\n FirewallConfig,\n FirewallState,\n NappletPolicy,\n RateLimit,\n ContentMatcher,\n EvaluateResult,\n} from '@kehto/firewall';\nimport {\n evaluate,\n defaultConfig,\n createState,\n serialize,\n deserialize,\n setPolicy,\n setRateLimit,\n setGlobalRate,\n addMatcher,\n} from '@kehto/firewall';\nimport type { FirewallPersistence } from './types.js';\n\n/**\n * Stateful firewall container — wraps @kehto/firewall's pure functions\n * with persistence and a convenient imperative API.\n *\n * Mirrors AclStateContainer from acl-state.ts in structure and naming.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * const result = firewall.evaluate({ napplet: 'chat', opClass: 'relay:write', focused: true, now: Date.now() });\n * ```\n */\nexport interface FirewallStateContainer {\n /**\n * Evaluate an observation against the current firewall config and counters.\n * CRITICAL: advances the in-memory counter state on each call.\n *\n * @param observation - Normalized observation extracted from the napplet message envelope.\n * @returns The full EvaluateResult (decision, action, ruleId, reason, newState).\n */\n evaluate(observation: Observation): EvaluateResult;\n /**\n * Set a per-napplet policy override (allow / deny / ask).\n *\n * @param napplet - The napplet dTag (version-agnostic identity key).\n * @param policy - Hard policy override for this napplet.\n */\n setPolicy(napplet: string, policy: NappletPolicy): void;\n /**\n * Set a per-(napplet, opClass) token-bucket rate limit.\n *\n * @param napplet - The napplet dTag.\n * @param opClass - The operation class string.\n * @param limit - The rate limit to apply.\n */\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void;\n /**\n * Set a global rate limit applied to all op-classes that have no specific entry.\n *\n * @param napplet - The napplet dTag.\n * @param limit - The global fallback rate limit.\n */\n setGlobalRate(napplet: string, limit: RateLimit): void;\n /**\n * Add a content matcher to the firewall config.\n *\n * @param matcher - The content matcher to append.\n */\n addMatcher(matcher: ContentMatcher): void;\n /** Return the current firewall config. */\n getConfig(): FirewallConfig;\n /** Persist the current firewall config via the persistence hook. Best-effort. */\n persist(): void;\n /** Load previously persisted firewall config. Counters are NOT restored. */\n load(): void;\n /** Reset config to defaultConfig() and counters to createState(), then persist empty. */\n clear(): void;\n}\n\n/**\n * Create a firewall state container backed by @kehto/firewall and optionally\n * persisted via the given persistence hooks.\n *\n * When `persistence` is absent (or undefined), firewall config is in-memory\n * only and resets on container recreation. This is safe for hosts that do not\n * need durable per-napplet policies.\n *\n * @param persistence - Optional storage backend for firewall config.\n * @returns A FirewallStateContainer instance.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * firewall.setPolicy('chat', 'allow');\n * firewall.persist();\n * ```\n */\nexport function createFirewallState(\n persistence?: FirewallPersistence,\n): FirewallStateContainer {\n let config: FirewallConfig = defaultConfig();\n let counters: FirewallState = createState();\n\n return {\n evaluate(observation: Observation): EvaluateResult {\n const result = evaluate(config, counters, observation);\n counters = result.newState; // CRITICAL: advance ephemeral counter state\n return result;\n },\n\n setPolicy(napplet: string, policy: NappletPolicy): void {\n config = setPolicy(config, napplet, policy);\n },\n\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void {\n config = setRateLimit(config, napplet, opClass, limit);\n },\n\n setGlobalRate(napplet: string, limit: RateLimit): void {\n config = setGlobalRate(config, napplet, limit);\n },\n\n addMatcher(matcher: ContentMatcher): void {\n config = addMatcher(config, matcher);\n },\n\n getConfig(): FirewallConfig {\n return config;\n },\n\n persist(): void {\n try {\n persistence?.persist(serialize(config));\n } catch { /* persistence is best-effort */ }\n },\n\n load(): void {\n try {\n const raw = persistence?.load() ?? null;\n if (!raw) return;\n config = deserialize(raw);\n // counters are deliberately NOT restored (RUNTIME-03: ephemeral)\n } catch {\n config = defaultConfig();\n }\n },\n\n clear(): void {\n config = defaultConfig();\n counters = createState();\n try { persistence?.persist(''); } catch { /* best-effort */ }\n },\n };\n}\n","/**\n * manifest-cache.ts — Manifest cache with persistence hooks.\n *\n * Caches NIP-5A manifest data (aggregate hashes) per napplet identity.\n * Delegates storage to ManifestPersistence — no localStorage.\n */\n\nimport type { ManifestPersistence, ManifestCacheEntry, VerificationCacheEntry } from './types.js';\n\n/**\n * Cache for verified napplet manifest entries.\n * Used to detect napplet updates (aggregateHash changes) across sessions.\n *\n * @example\n * ```ts\n * const cache = createManifestCache(persistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * ```\n */\nexport interface ManifestCache {\n /** Get a cached manifest entry by pubkey and dTag. */\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined;\n /** Set (upsert) a manifest cache entry and persist. */\n set(entry: ManifestCacheEntry): void;\n /** Check if a specific hash is cached for a pubkey/dTag combination. */\n has(pubkey: string, dTag: string, hash: string): boolean;\n /** Get the requires list for a cached manifest, or empty array if not found. */\n getRequires(pubkey: string, dTag: string): string[];\n /** Remove a cached entry for a pubkey/dTag and persist. */\n remove(pubkey: string, dTag: string): void;\n /** Load the cache from persistence. */\n load(): void;\n /** Persist the cache to storage. */\n persist(): void;\n /** Clear all cached entries. */\n clear(): void;\n\n /** Get a cached verification result by manifest event ID. */\n getVerification(eventId: string): VerificationCacheEntry | undefined;\n\n /** Cache a verification result keyed by manifest event ID. */\n setVerification(eventId: string, result: VerificationCacheEntry): void;\n\n /** Check if a manifest event ID has been verified. */\n hasVerification(eventId: string): boolean;\n\n /** Clear all verification cache entries. */\n clearVerifications(): void;\n}\n\n/**\n * Create a manifest cache backed by the given persistence hooks.\n *\n * @param persistence - Storage backend for manifest data\n * @returns A ManifestCache instance\n *\n * @example\n * ```ts\n * import { createManifestCache } from '@kehto/runtime';\n *\n * const cache = createManifestCache(manifestPersistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * cache.has('abc...', 'chat', 'dead'); // true\n * ```\n */\nexport function createManifestCache(persistence: ManifestPersistence): ManifestCache {\n const cache = new Map<string, ManifestCacheEntry>();\n const verificationCache = new Map<string, VerificationCacheEntry>();\n\n function cacheKey(pubkey: string, dTag: string): string {\n return `${pubkey}:${dTag}`;\n }\n\n const self: ManifestCache = {\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined {\n return cache.get(cacheKey(pubkey, dTag));\n },\n\n set(entry: ManifestCacheEntry): void {\n cache.set(cacheKey(entry.pubkey, entry.dTag), entry);\n self.persist();\n },\n\n has(pubkey: string, dTag: string, hash: string): boolean {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return !!entry && entry.aggregateHash === hash;\n },\n\n getRequires(pubkey: string, dTag: string): string[] {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return entry?.requires ?? [];\n },\n\n remove(pubkey: string, dTag: string): void {\n cache.delete(cacheKey(pubkey, dTag));\n self.persist();\n },\n\n load(): void {\n try {\n const raw = persistence.load();\n if (!raw) return;\n const entries = JSON.parse(raw) as Array<[string, ManifestCacheEntry]>;\n cache.clear();\n for (const [key, val] of entries) cache.set(key, val);\n } catch { cache.clear(); }\n },\n\n persist(): void {\n try {\n persistence.persist(JSON.stringify(Array.from(cache.entries())));\n } catch { /* persistence is best-effort */ }\n },\n\n clear(): void {\n cache.clear();\n verificationCache.clear();\n try { persistence.persist(''); } catch { /* best-effort */ }\n },\n\n getVerification(eventId: string): VerificationCacheEntry | undefined {\n return verificationCache.get(eventId);\n },\n\n setVerification(eventId: string, result: VerificationCacheEntry): void {\n verificationCache.set(eventId, result);\n // Verification cache is in-memory only — no persistence needed\n // since manifest event IDs are immutable and re-fetching is cheap\n },\n\n hasVerification(eventId: string): boolean {\n return verificationCache.has(eventId);\n },\n\n clearVerifications(): void {\n verificationCache.clear();\n },\n };\n\n return self;\n}\n","/**\n * replay.ts — Replay detection module.\n *\n * Tracks seen event IDs and validates timestamps to prevent\n * duplicate event processing and replay attacks.\n */\n\nimport type { NostrEvent } from '@napplet/core';\n\n/**\n * Replay detection window in seconds — events older than this are rejected.\n * Relocated inline from the former @napplet/core compatibility shim\n * (DRIFT-CORE-06, Phase 24). Numeric value preserved unchanged from the shim\n * to hold behavioral parity with the Phase 23 test baseline.\n */\nconst REPLAY_WINDOW_SECONDS = 30;\n\n/**\n * Replay detection engine. Tracks seen event IDs and validates timestamps.\n *\n * @example\n * ```ts\n * const detector = createReplayDetector();\n * const reason = detector.check(event);\n * if (reason !== null) { // reject event }\n * ```\n */\nexport interface ReplayDetector {\n /**\n * Check if an event should be rejected as a replay and record it as processed.\n * Returns null if the event is valid, or a string reason if it should be rejected.\n */\n check(event: NostrEvent): string | null;\n\n /**\n * Check an event and atomically reserve its ID for pending processing.\n * Call {@link commit} after processing succeeds or {@link release} after it fails.\n *\n * @param event - Signed event whose ID should be reserved\n * @returns null if reserved, or a string reason if it should be rejected\n */\n reserve(event: NostrEvent): string | null;\n\n /**\n * Commit a pending reservation as successfully processed.\n *\n * @param eventId - Reserved signed event ID\n */\n commit(eventId: string): void;\n\n /**\n * Release a pending reservation so a failed operation can be retried.\n * Committed event IDs are never removed by this method.\n *\n * @param eventId - Reserved signed event ID\n */\n release(eventId: string): void;\n\n /** Clear all tracked event IDs. */\n clear(): void;\n}\n\n/**\n * Create a replay detector that rejects duplicate events and events\n * with timestamps outside the replay window.\n *\n * @param getReplayWindow - Optional getter for a dynamic replay window override.\n * When provided, its return value is used instead of the module-level constant.\n * Called on every check, so changes take effect immediately.\n * @returns A ReplayDetector instance\n *\n * @example\n * ```ts\n * import { createReplayDetector } from '@kehto/runtime';\n *\n * const detector = createReplayDetector();\n * const reason = detector.check(event);\n * if (reason !== null) {\n * // Reject — duplicate, stale, or future-dated\n * }\n * ```\n */\nexport function createReplayDetector(getReplayWindow?: () => number | undefined): ReplayDetector {\n type ReplayEntry = {\n state: 'pending' | 'committed';\n timestamp: number;\n };\n\n const seenEventIds = new Map<string, ReplayEntry>();\n\n function checkAndTrack(event: NostrEvent, state: ReplayEntry['state']): string | null {\n const replayWindow = getReplayWindow?.() ?? REPLAY_WINDOW_SECONDS;\n const now = Math.floor(Date.now() / 1000);\n for (const [id, entry] of seenEventIds) {\n if (entry.state === 'committed' && now - entry.timestamp > replayWindow) {\n seenEventIds.delete(id);\n }\n }\n if (now - event.created_at > replayWindow) return 'invalid: event created_at too old';\n if (event.created_at - now > 10) return 'invalid: event created_at in the future';\n if (seenEventIds.has(event.id)) return 'duplicate: already processed';\n seenEventIds.set(event.id, { state, timestamp: now });\n return null;\n }\n\n return {\n check(event: NostrEvent): string | null {\n return checkAndTrack(event, 'committed');\n },\n\n reserve(event: NostrEvent): string | null {\n return checkAndTrack(event, 'pending');\n },\n\n commit(eventId: string): void {\n const entry = seenEventIds.get(eventId);\n if (entry?.state !== 'pending') return;\n seenEventIds.set(eventId, {\n state: 'committed',\n timestamp: Math.floor(Date.now() / 1000),\n });\n },\n\n release(eventId: string): void {\n if (seenEventIds.get(eventId)?.state === 'pending') {\n seenEventIds.delete(eventId);\n }\n },\n\n clear(): void {\n seenEventIds.clear();\n },\n };\n}\n","/**\n * event-buffer.ts — Ring buffer and subscription delivery engine.\n *\n * Buffers events in a fixed-size ring buffer and delivers matching\n * events to subscribed napplets via the abstract sendToNapplet transport.\n */\n\nimport type { NostrEvent, NostrFilter } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { SendToNapplet } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { EnforceResult } from './enforce.js';\n\n/** Default ring buffer size. */\nexport const RING_BUFFER_SIZE = 100;\n\n/** Subscription entry — tracks a subscription for a specific napplet window. */\nexport interface SubscriptionEntry {\n windowId: string;\n filters: NostrFilter[];\n}\n\n/**\n * Check if an event matches a single NIP-01 filter.\n * Pure function — no side effects.\n *\n * @param event - The event to check\n * @param filter - The filter to match against\n * @returns True if the event matches the filter\n *\n * @example\n * ```ts\n * import { matchesFilter } from '@kehto/runtime';\n *\n * matchesFilter(event, { kinds: [1], authors: ['abc'] });\n * // true if event.kind === 1 and event.pubkey starts with 'abc'\n * ```\n */\nexport function matchesFilter(event: NostrEvent, filter: NostrFilter): boolean {\n if (filter.ids !== undefined && !filter.ids.some((id) => event.id.startsWith(id))) return false;\n if (filter.authors !== undefined && !filter.authors.some((a) => event.pubkey.startsWith(a))) return false;\n if (filter.kinds !== undefined && !filter.kinds.includes(event.kind)) return false;\n if (filter.since !== undefined && event.created_at < filter.since) return false;\n if (filter.until !== undefined && event.created_at > filter.until) return false;\n for (const [key, values] of Object.entries(filter)) {\n if (!key.startsWith('#') || values === undefined) continue;\n const tagName = key.slice(1);\n const tagValues = values as string[];\n const eventTagValues = event.tags.filter((t) => t[0] === tagName).map((t) => t[1]);\n if (!tagValues.some((v) => eventTagValues.includes(v))) return false;\n }\n return true;\n}\n\n/**\n * Check if an event matches any filter in a list.\n * Returns true for empty filter lists.\n *\n * @param event - The event to check\n * @param filters - The filters to match against\n * @returns True if the event matches any filter (or if filters is empty)\n *\n * @example\n * ```ts\n * import { matchesAnyFilter } from '@kehto/runtime';\n *\n * matchesAnyFilter(event, [{ kinds: [1] }, { kinds: [7] }]);\n * // true if event is kind 1 or kind 7\n * ```\n */\nexport function matchesAnyFilter(event: NostrEvent, filters: NostrFilter[]): boolean {\n if (filters.length === 0) return true;\n return filters.some((filter) => matchesFilter(event, filter));\n}\n\n/** Event buffer and subscription delivery engine. */\nexport interface EventBuffer {\n /** Add an event to the ring buffer and deliver to matching subscriptions. */\n bufferAndDeliver(event: NostrEvent, senderId: string | null): void;\n\n /** Deliver an event to matching subscriptions without buffering. */\n deliverToSubscriptions(event: NostrEvent, senderId: string | null): void;\n\n /** Get the current subscription map (for REQ handler to register subs). */\n getSubscriptions(): Map<string, SubscriptionEntry>;\n\n /** Get buffered events (for REQ replay). */\n getBufferedEvents(): readonly NostrEvent[];\n\n /** Clear the buffer. */\n clear(): void;\n}\n\n/**\n * Create an event buffer with subscription delivery.\n *\n * @param sendToNapplet - Transport function to send messages to napplets\n * @param sessionRegistry - Identity registry for looking up napplet pubkeys\n * @param enforce - Enforcement function for checking relay:read on recipients\n * @param subscriptions - Shared subscription map (owned by the runtime)\n * @param getBufferSize - Optional getter for a dynamic buffer size override.\n * When provided, its return value is used instead of RING_BUFFER_SIZE.\n * Called on every bufferAndDeliver, so changes take effect immediately.\n * @returns An EventBuffer instance\n *\n * @example\n * ```ts\n * import { createEventBuffer } from '@kehto/runtime';\n *\n * const buffer = createEventBuffer(sendToNapplet, sessionRegistry, enforce, subscriptions);\n * buffer.bufferAndDeliver(event, senderWindowId);\n * ```\n */\nexport function createEventBuffer(\n sendToNapplet: SendToNapplet,\n sessionRegistry: SessionRegistry,\n enforce: (pubkey: string, capability: Capability, message?: unknown[]) => EnforceResult,\n subscriptions: Map<string, SubscriptionEntry>,\n getBufferSize?: () => number,\n): EventBuffer {\n const buffer: NostrEvent[] = [];\n\n function deliverToSubscriptions(event: NostrEvent, senderId: string | null): void {\n const pTag = event.tags?.find((t) => t[0] === 'p');\n const targetPubkey = pTag?.[1];\n\n for (const [subKey, sub] of subscriptions) {\n if (senderId !== null && sub.windowId === senderId) continue;\n\n const recipientPubkey = sessionRegistry.getPubkey(sub.windowId);\n if (recipientPubkey) {\n const recipientResult = enforce(recipientPubkey, 'relay:read', ['EVENT', event]);\n if (!recipientResult.allowed) continue;\n }\n\n if (targetPubkey) {\n const subPubkey = recipientPubkey;\n if (subPubkey !== targetPubkey) continue;\n }\n\n if (!matchesAnyFilter(event, sub.filters)) continue;\n\n const prefix = `${sub.windowId}:`;\n if (!subKey.startsWith(prefix)) continue;\n const subId = subKey.slice(prefix.length);\n\n sendToNapplet(sub.windowId, ['EVENT', subId, event]);\n }\n }\n\n return {\n bufferAndDeliver(event: NostrEvent, senderId: string | null): void {\n const maxSize = getBufferSize?.() ?? RING_BUFFER_SIZE;\n if (buffer.length >= maxSize) buffer.shift();\n buffer.push(event);\n deliverToSubscriptions(event, senderId);\n },\n\n deliverToSubscriptions,\n\n getSubscriptions(): Map<string, SubscriptionEntry> { return subscriptions; },\n\n getBufferedEvents(): readonly NostrEvent[] { return buffer; },\n\n clear(): void {\n buffer.length = 0;\n },\n };\n}\n","import type { NostrEvent } from '@napplet/core';\nimport type { ResourceSidecarEntry } from '@napplet/nap/resource/types';\n\n/** Metadata attached to a raw Nostr event read result. */\nexport interface RelayEventSidecar {\n /** Pre-resolved resources owned by NAP-RESOURCE policy. */\n resources?: ResourceSidecarEntry[];\n /** Advisory relays where the shell observed the event or expects follow-up reads to work. */\n relayHints?: string[];\n}\n\n/** Raw event plus optional read-side metadata, shared by read-style NAP surfaces. */\nexport interface RelayEventResult {\n /** Raw Nostr event. */\n event: NostrEvent;\n /** Optional runtime-observed sidecar metadata. */\n sidecar?: RelayEventSidecar;\n}\n\n/** Relay event envelope shape accepted during the result-shape migration. */\nexport interface RelayEventCarrier {\n result?: unknown;\n event?: unknown;\n sidecar?: unknown;\n resources?: unknown;\n relay?: unknown;\n}\n\nfunction stringList(value: unknown): string[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const out = [...new Set(value.filter((item): item is string => typeof item === 'string' && item.length > 0))];\n return out.length > 0 ? out : undefined;\n}\n\nfunction sidecarFromUnknown(value: unknown): RelayEventSidecar | undefined {\n if (!value || typeof value !== 'object') return undefined;\n const input = value as RelayEventSidecar;\n const sidecar: RelayEventSidecar = {};\n if (Array.isArray(input.resources) && input.resources.length > 0) sidecar.resources = input.resources;\n const relayHints = stringList(input.relayHints);\n if (relayHints) sidecar.relayHints = relayHints;\n return sidecar.resources || sidecar.relayHints ? sidecar : undefined;\n}\n\n/** Build a canonical relay event result, omitting an empty sidecar. */\nexport function createRelayEventResult(event: NostrEvent, sidecar?: RelayEventSidecar): RelayEventResult {\n const normalized = sidecarFromUnknown(sidecar);\n return normalized ? { event, sidecar: normalized } : { event };\n}\n\n/** Build a relay event result with advisory relay hints. */\nexport function createRelayEventResultWithHints(event: NostrEvent, relayHints?: string[]): RelayEventResult {\n return createRelayEventResult(event, relayHints && relayHints.length > 0 ? { relayHints } : undefined);\n}\n\n/**\n * Read either the current `result` wire shape or the pre-result migration shape.\n * This keeps internal service composition working while emitters move to\n * canonical `RelayEventResult` envelopes.\n */\nexport function relayEventResultFromCarrier(carrier: RelayEventCarrier): RelayEventResult | null {\n if (carrier.result && typeof carrier.result === 'object') {\n const result = carrier.result as RelayEventResult;\n if (result.event && typeof result.event === 'object') return createRelayEventResult(result.event, result.sidecar);\n }\n if (carrier.event && typeof carrier.event === 'object') {\n const sidecar = sidecarFromUnknown(carrier.sidecar) ?? sidecarFromUnknown({ resources: carrier.resources });\n const relayHints = typeof carrier.relay === 'string' && carrier.relay.length > 0 ? [carrier.relay] : undefined;\n const merged: RelayEventSidecar = sidecar ? { ...sidecar } : {};\n if (relayHints) merged.relayHints = relayHints;\n return createRelayEventResult(carrier.event as NostrEvent, merged);\n }\n return null;\n}\n","\nimport { createDispatch, type NappletMessage, type NostrEvent, type NapHandler } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { Observation } from '@kehto/firewall';\n\nimport type {\n RuntimeAdapter, ConsentHandler, FirewallEvent,\n ServiceHandler, ServiceRegistry, ServiceInfo, ServiceRuntimeContext,\n} from './types.js';\nimport { notifyServiceWindowDestroyed } from './service-dispatch.js';\nimport { createSessionRegistry, type SessionRegistry } from './session-registry.js';\nimport { createAclState, type AclStateContainer } from './acl-state.js';\nimport { createFirewallState, type FirewallStateContainer } from './firewall-state.js';\nimport { createManifestCache, type ManifestCache } from './manifest-cache.js';\nimport { createReplayDetector, type ReplayDetector } from './replay.js';\nimport { createEventBuffer, RING_BUFFER_SIZE, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport { createEnforceGate, createNapEnforceGate, resolveCapabilitiesNap, formatDenialReason } from './enforce.js';\nimport { createRelayHandler } from './relay-handler.js';\nimport { createIdentityHandler } from './identity-handler.js';\nimport { createCountHandler } from './count-handler.js';\nimport { createIncRuntime, type IncRuntime } from './inc-handler.js';\nimport { createRuntimeDomainHandlers, type RuntimeDomainHandlers } from './domain-handlers.js';\nimport {\n createCanonicalDomainResult,\n createIntentPolicyDenial,\n isIdentityOrThemeMessage,\n} from './domain-results.js';\n\n/**\n * The napplet protocol engine — handles NIP-5D NAP domain dispatch,\n * ACL enforcement, subscription lifecycle.\n *\n * @example\n * ```ts\n * import { createRuntime } from '@kehto/runtime';\n *\n * const runtime = createRuntime(hooks);\n * runtime.handleMessage('window-1', { type: 'relay.req', id: 'sub1', filters: [{ kinds: [1] }] });\n * ```\n */\nexport interface Runtime {\n /**\n * Handle an incoming NIP-5D NappletMessage envelope from a napplet.\n * The caller is responsible for identifying the source (windowId).\n * Legacy NIP-01 arrays are silently dropped (clean break — no dual-mode).\n *\n * @param windowId - The identifier of the napplet that sent the message\n * @param msg - The raw message (NappletMessage envelopes are processed; other types dropped)\n */\n handleMessage(windowId: string, msg: unknown): void;\n\n /**\n * Inject a shell-originated event into subscription delivery.\n *\n * @param topic - The event topic tag value\n * @param payload - The event content\n */\n injectEvent(topic: string, payload: unknown): void;\n\n /** Destroy the runtime, persisting state and clearing all internal state. */\n destroy(): void;\n\n /** Register a handler for consent requests on destructive signing kinds. */\n registerConsentHandler(handler: ConsentHandler): void;\n\n /**\n * Register a service handler dynamically after runtime creation.\n * If a handler is already registered for this name, it is replaced.\n *\n * @param name - Service name (e.g., 'audio', 'notifications')\n * @param handler - The service handler implementation\n */\n registerService(name: string, handler: ServiceHandler): void;\n\n /**\n * Unregister a service handler by name. No-op if the name is not registered.\n *\n * @param name - Service name to remove\n */\n unregisterService(name: string): void;\n\n /**\n * Clean up all state associated with a napplet window.\n * Removes subscriptions, pending state, and notifies service handlers.\n *\n * @param windowId - The window to clean up\n */\n destroyWindow(windowId: string): void;\n\n /** Access the identity registry (for shell adapter to read napplet session state). */\n readonly sessionRegistry: SessionRegistry;\n\n /** Access the ACL state container. */\n readonly aclState: AclStateContainer;\n\n /** Access the firewall state container (for tests to pre-set policy/rules). */\n readonly firewallState: FirewallStateContainer;\n\n /** Access the manifest cache. */\n readonly manifestCache: ManifestCache;\n}\n\ntype RuntimeNapHandlers = RuntimeDomainHandlers & {\n relay: (windowId: string, msg: NappletMessage) => void;\n identity: (windowId: string, msg: NappletMessage) => void;\n count: (windowId: string, msg: NappletMessage) => void;\n inc: (windowId: string, msg: NappletMessage) => void;\n};\n\ntype RuntimeInstanceContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n registeredServices: Map<string, ServiceInfo>;\n replayDetector: ReplayDetector;\n subscriptions: Map<string, SubscriptionEntry>;\n eventBuffer: EventBuffer;\n incRuntime: IncRuntime;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n firewallState: FirewallStateContainer;\n manifestCache: ManifestCache;\n consentHandlerRef: { current: ConsentHandler | null };\n handleMessage: Runtime['handleMessage'];\n};\n\nfunction createRegisteredServices(serviceRegistry: ServiceRegistry): Map<string, ServiceInfo> {\n const registeredServices = new Map<string, ServiceInfo>();\n for (const [name, handler] of Object.entries(serviceRegistry)) {\n registeredServices.set(name, {\n name: handler.descriptor.name,\n version: handler.descriptor.version,\n description: handler.descriptor.description,\n });\n }\n return registeredServices;\n}\n\nfunction createNapEnvelopeDispatcher(handlers: RuntimeNapHandlers): (windowId: string, envelope: NappletMessage) => void {\n let currentWindowId: string | null = null;\n const napDispatch = createDispatch();\n const adapt = (handler: (windowId: string, msg: NappletMessage) => void): NapHandler => (msg) => {\n if (currentWindowId !== null) handler(currentWindowId, msg);\n };\n\n napDispatch.registerNap('relay', adapt(handlers.relay));\n napDispatch.registerNap('identity', adapt(handlers.identity));\n napDispatch.registerNap('count', adapt(handlers.count));\n napDispatch.registerNap('keys', adapt(handlers.keys));\n napDispatch.registerNap('media', adapt(handlers.media));\n napDispatch.registerNap('notify', adapt(handlers.notify));\n napDispatch.registerNap('storage', adapt(handlers.storage));\n napDispatch.registerNap('inc', adapt(handlers.inc));\n napDispatch.registerNap('theme', adapt(handlers.theme));\n napDispatch.registerNap('config', adapt(handlers.config));\n napDispatch.registerNap('resource', adapt(handlers.resource));\n napDispatch.registerNap('cvm', adapt(handlers.cvm));\n napDispatch.registerNap('outbox', adapt(handlers.outbox));\n napDispatch.registerNap('upload', adapt(handlers.upload));\n napDispatch.registerNap('intent', adapt(handlers.intent));\n napDispatch.registerNap('link', adapt(handlers.link));\n napDispatch.registerNap('common', adapt(handlers.common));\n napDispatch.registerNap('lists', adapt(handlers.lists));\n napDispatch.registerNap('serial', adapt(handlers.serial));\n napDispatch.registerNap('ble', adapt(handlers.ble));\n napDispatch.registerNap('webrtc', adapt(handlers.webrtc));\n napDispatch.registerNap('dm', adapt(handlers.dm));\n napDispatch.registerNap('fs', adapt(handlers.fs));\n\n return (windowId, envelope) => {\n currentWindowId = windowId;\n try {\n napDispatch.dispatch(envelope);\n } finally {\n currentWindowId = null;\n }\n };\n}\n\n/**\n * Compute the UTF-8 byte length of a string without TextEncoder (ES2022-safe).\n * Mirrors the same helper in state-handler.ts to avoid cross-file import.\n */\nfunction utf8ByteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Extract kind and byte-size from a message envelope (publish-style ops only).\n * Guards against malformed envelopes — returns undefined fields on any failure.\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @returns `{ kind?, size? }` — both optional; undefined when not a publish-style op.\n */\nfunction extractKindSize(envelope: NappletMessage): { kind?: number; size?: number } {\n const ev = (envelope as NappletMessage & { event?: unknown }).event;\n if (typeof ev !== 'object' || ev === null) return {};\n const kind = typeof (ev as { kind?: unknown }).kind === 'number'\n ? (ev as { kind: number }).kind\n : undefined;\n let size: number | undefined;\n try {\n size = utf8ByteLength(JSON.stringify(ev));\n } catch { /* malformed event — size stays undefined */ }\n return { kind, size };\n}\n\n/**\n * Build a normalized Observation from a message envelope and runtime context.\n * The runtime owns the clock (Date.now()); the pure engine never reads it.\n * Focus is sourced exclusively via getFocusContext (FOCUS-01) — never napplet-self-reported.\n * When getFocusContext is absent, defaults to `{ focused: true }` (safe relax-only default).\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @param windowId - Source napplet window identifier.\n * @param senderCap - Resolved sender capability string (or null).\n * @param sessionRegistry - Used to resolve dTag and registeredAt for the window.\n * @param getFocusContext - Optional shell-supplied focus query hook.\n * @returns A complete Observation ready for evaluate().\n */\nfunction buildObservation(\n envelope: NappletMessage,\n windowId: string,\n senderCap: string | null,\n sessionRegistry: SessionRegistry,\n getFocusContext?: RuntimeAdapter['getFocusContext'],\n): Observation {\n const now = Date.now();\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n const napplet = entry?.dTag ?? '';\n const initElapsedMs = now - (entry?.registeredAt ?? now);\n const focus = getFocusContext?.(windowId) ?? { focused: true };\n const opClass = senderCap ?? envelope.type;\n const { kind, size } = extractKindSize(envelope);\n return {\n napplet,\n opClass,\n kind,\n size,\n initElapsedMs,\n // `windowId` comes from the host's source-bound session lookup, never the\n // napplet message envelope. It scopes startup burst accounting to this\n // registered realm while rate limits remain dTag-wide.\n initKey: windowId,\n focused: focus.focused,\n msSinceFocusGain: focus.msSinceFocusGain,\n now,\n };\n}\n\n/** Configuration for createFirewallGate. */\ninterface FirewallGateConfig {\n firewallState: FirewallStateContainer;\n sessionRegistry: SessionRegistry;\n hooks: RuntimeAdapter;\n fireConsent: (windowId: string, napplet: string) => void;\n}\n\n/**\n * Create the firewall gate closure to inject into createMessageHandler.\n * Returns 'dispatch' to allow the message through or 'drop' to reject it.\n *\n * Decision→action mapping (RUNTIME-01, RUNTIME-04, POLICY-02):\n * - reject → error envelope + drop (no dispatch)\n * - prompt → error envelope + fireConsent + drop (ask path)\n * - pass + flag → onFirewallEvent audit + dispatch\n * - pass (ignore/allow) → dispatch (no audit)\n *\n * @param config - Gate configuration (firewallState, sessionRegistry, hooks, fireConsent).\n * @returns A gate function `(windowId, envelope, senderCap) => 'dispatch' | 'drop'`.\n */\nfunction createFirewallGate(config: FirewallGateConfig): (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop' {\n const { firewallState, sessionRegistry, hooks, fireConsent } = config;\n\n return function firewallGate(windowId: string, envelope: NappletMessage, senderCap: string | null): 'dispatch' | 'drop' {\n const obs = buildObservation(envelope, windowId, senderCap, sessionRegistry, hooks.getFocusContext);\n const result = firewallState.evaluate(obs);\n const { decision, action, ruleId, reason } = result;\n const napplet = obs.napplet;\n const opClass = obs.opClass;\n\n if (decision === 'reject' || decision === 'prompt') {\n const intentDenial = createIntentPolicyDenial(envelope);\n if (intentDenial) {\n hooks.sendToNapplet(windowId, intentDenial);\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n if (decision === 'prompt') fireConsent(windowId, napplet);\n return 'drop';\n }\n\n const canonicalResult = createCanonicalDomainResult(envelope);\n if (canonicalResult) {\n hooks.sendToNapplet(windowId, canonicalResult);\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n if (decision === 'prompt') fireConsent(windowId, napplet);\n return 'drop';\n }\n\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const type = denialResponseType(envelope);\n if (type) {\n hooks.sendToNapplet(windowId, { type, id, error: `firewall: ${reason}` } as NappletMessage);\n }\n\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n\n if (decision === 'prompt') {\n // POLICY-02: reject current message AND fire async consent prompt\n fireConsent(windowId, napplet);\n }\n return 'drop';\n }\n\n // decision === 'pass'\n if (action === 'flag') {\n // RUNTIME-04: flag → audit + dispatch (message is NOT dropped)\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n }\n return 'dispatch';\n };\n}\n\nfunction denialResponseType(envelope: NappletMessage): string | null {\n if (envelope.type.startsWith('intent.')) return null;\n if (envelope.type.startsWith('storage.')) return `${envelope.type}.result`;\n if (envelope.type === 'inc.subscribe' || envelope.type === 'inc.channel.open') {\n return `${envelope.type}.result`;\n }\n if (envelope.type.startsWith('inc.')) return null;\n return `${envelope.type}.error`;\n}\n\nfunction createMessageHandler(\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n enforceNap: ReturnType<typeof createNapEnforceGate>,\n dispatchNapEnvelope: (windowId: string, envelope: NappletMessage) => void,\n firewallGate: (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop',\n): Runtime['handleMessage'] {\n return (windowId: string, msg: unknown): void => {\n if (typeof msg !== 'object' || msg === null || !('type' in msg)) return;\n const envelope = msg as NappletMessage;\n const dotIdx = envelope.type.indexOf('.');\n if (dotIdx === -1) return;\n // NAP-SHELL establishes the sole capability ingress boundary: valid\n // envelopes remain inert until the source-bound shell.ready transition\n // creates a session. This deliberately precedes capability resolution,\n // ACL, firewall, service routing, and domain dispatch.\n if (!sessionRegistry.getEntryByWindowId(windowId)) return;\n const domain = envelope.type.slice(0, dotIdx);\n if (hooks.isDomainAllowed && !hooks.isDomainAllowed(windowId, domain)) return;\n if (isIdentityOrThemeMessage(envelope) && !createCanonicalDomainResult(envelope)) return;\n\n const caps = resolveCapabilitiesNap(envelope);\n if (caps.senderCap) {\n const result = enforceNap(windowId, caps.senderCap as Capability, envelope);\n if (!result.allowed) {\n const intentDenial = createIntentPolicyDenial(envelope);\n if (intentDenial) {\n hooks.sendToNapplet(windowId, intentDenial);\n return;\n }\n\n const canonicalResult = createCanonicalDomainResult(envelope);\n if (canonicalResult) {\n hooks.sendToNapplet(windowId, canonicalResult);\n return;\n }\n\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const error = formatDenialReason(result.capability);\n const type = denialResponseType(envelope);\n if (type) {\n hooks.sendToNapplet(windowId, { type, id, error } as NappletMessage);\n }\n return;\n }\n }\n\n const verdict = firewallGate(windowId, envelope, caps.senderCap);\n if (verdict === 'drop') return;\n\n dispatchNapEnvelope(windowId, envelope);\n };\n}\n\nfunction createInjectedEvent(hooks: RuntimeAdapter, topic: string, payload: unknown): NostrEvent {\n const uuid = hooks.crypto.randomUUID().replace(/-/g, '').slice(0, 64).padEnd(64, '0');\n return {\n id: uuid,\n pubkey: '0'.repeat(64),\n created_at: Math.floor(Date.now() / 1000),\n kind: 29000,\n tags: [['t', topic]],\n content: JSON.stringify(payload),\n sig: '0'.repeat(128),\n };\n}\n\nfunction createRuntimeInstance(context: RuntimeInstanceContext): Runtime {\n const {\n aclState, firewallState, eventBuffer, hooks, incRuntime, manifestCache, registeredServices,\n replayDetector, serviceRegistry, sessionRegistry, subscriptions, consentHandlerRef,\n } = context;\n const undeclaredServiceConsents = new Set<string>();\n const attachedServices = new Map<string, ServiceHandler>();\n const isDomainAllowed = hooks.isDomainAllowed;\n const sendToNapplet = hooks.sendToNapplet;\n const serviceContext: ServiceRuntimeContext = Object.freeze({\n resolveDTag(windowId: string): string | undefined {\n return sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n },\n listWindowIds(): readonly string[] {\n return Object.freeze(\n sessionRegistry.getAllEntries().map((entry) => entry.windowId),\n );\n },\n hasCapability(windowId: string, capability: Capability): boolean {\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n return !!entry && aclState.check('', entry.dTag, entry.aggregateHash, capability);\n },\n sendToEligibleNapplet(windowId: string, message: NappletMessage): boolean {\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n if (!entry || typeof message.type !== 'string') return false;\n const dotIndex = message.type.indexOf('.');\n if (dotIndex <= 0) return false;\n const domain = message.type.slice(0, dotIndex);\n try {\n if (isDomainAllowed && !isDomainAllowed(windowId, domain)) return false;\n const { recipientCap } = resolveCapabilitiesNap(message);\n if (!recipientCap) return false;\n if (!aclState.check('', entry.dTag, entry.aggregateHash, recipientCap as Capability)) {\n return false;\n }\n sendToNapplet(windowId, message);\n return true;\n } catch {\n return false;\n }\n },\n });\n\n function attachService(name: string, handler: ServiceHandler): void {\n attachedServices.set(name, handler);\n try {\n handler.onRegistered?.(serviceContext);\n } catch {\n // A failed optional attachment leaves the service safely context-free.\n }\n }\n\n function detachService(name: string): void {\n const handler = attachedServices.get(name);\n if (!handler) return;\n attachedServices.delete(name);\n try {\n handler.onUnregistered?.();\n } catch {\n // Service cleanup is best-effort and cannot block runtime teardown.\n }\n }\n\n for (const [name, handler] of Object.entries(serviceRegistry)) {\n attachService(name, handler);\n }\n\n return {\n handleMessage: context.handleMessage,\n injectEvent(topic: string, payload: unknown): void {\n eventBuffer.bufferAndDeliver(createInjectedEvent(hooks, topic, payload), null);\n },\n destroy(): void {\n for (const name of attachedServices.keys()) detachService(name);\n manifestCache.persist();\n aclState.persist();\n firewallState.persist();\n replayDetector.clear();\n subscriptions.clear();\n incRuntime.clear();\n eventBuffer.clear();\n registeredServices.clear();\n undeclaredServiceConsents.clear();\n },\n registerConsentHandler(handler: ConsentHandler): void {\n consentHandlerRef.current = handler;\n },\n registerService(name: string, handler: ServiceHandler): void {\n detachService(name);\n serviceRegistry[name] = handler;\n registeredServices.set(name, {\n name: handler.descriptor.name,\n version: handler.descriptor.version,\n description: handler.descriptor.description,\n });\n attachService(name, handler);\n },\n unregisterService(name: string): void {\n detachService(name);\n delete serviceRegistry[name];\n registeredServices.delete(name);\n },\n destroyWindow(windowId: string): void {\n for (const [key] of subscriptions) {\n if (key.startsWith(`${windowId}:`)) {\n subscriptions.delete(key);\n hooks.relayPool?.untrackSubscription(key);\n }\n }\n incRuntime.destroyWindow(windowId);\n notifyServiceWindowDestroyed(windowId, serviceRegistry);\n },\n get sessionRegistry() { return sessionRegistry; },\n get aclState() { return aclState; },\n get firewallState() { return firewallState; },\n get manifestCache() { return manifestCache; },\n };\n}\n\n/**\n * Create a runtime instance with dependency injection via hooks.\n *\n * @param hooks - Host application provides relay pool, auth, config, etc.\n * @returns A Runtime instance ready to handle napplet messages\n *\n * @example\n * ```ts\n * const runtime = createRuntime(hooks);\n * // On incoming message from napplet:\n * runtime.handleMessage(windowId, { type: 'relay.req', id: 'sub1', filters: [] });\n * ```\n */\nexport function createRuntime(hooks: RuntimeAdapter): Runtime {\n const subscriptions = new Map<string, SubscriptionEntry>();\n const serviceRegistry: ServiceRegistry = { ...hooks.services };\n const registeredServices = createRegisteredServices(serviceRegistry);\n const sessionRegistry = createSessionRegistry(hooks.onPendingUpdate);\n let incRuntime: IncRuntime | null = null;\n const aclState = createAclState(hooks.aclPersistence, 'permissive', (mutation) => {\n if (mutation.type === 'revoke' && mutation.capability !== 'relay:read') return;\n for (const entry of sessionRegistry.getAllEntries()) {\n if (entry.dTag === mutation.dTag && entry.aggregateHash === mutation.aggregateHash) {\n incRuntime?.revokeWindow(entry.windowId);\n }\n }\n });\n const firewallState = createFirewallState(hooks.firewallPersistence);\n const manifestCache = createManifestCache(hooks.manifestPersistence);\n const replayDetector = createReplayDetector(\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().replayWindowSeconds\n : undefined,\n );\n\n // Shared consent handler ref — lifted to createRuntime scope so the firewall gate\n // can fire it. The gate is built here (outer scope) but registerConsentHandler is\n // called on the runtime instance (inner scope); the ref bridges both closures.\n const consentHandlerRef: { current: ConsentHandler | null } = { current: null };\n\n // fireConsent: invoked by the gate on 'prompt' decisions (POLICY-02 ask path).\n // On resolution the user's choice is persisted as a per-napplet policy so subsequent\n // messages are NOT re-prompted (T-81-04: bounded consent spam prevention).\n const fireConsent = (windowId: string, napplet: string): void => {\n const handler = consentHandlerRef.current;\n if (!handler) return;\n handler({\n type: 'firewall-policy',\n windowId,\n napplet,\n pubkey: '',\n resolve: (allowed: boolean): void => {\n firewallState.setPolicy(napplet, allowed ? 'allow' : 'deny');\n firewallState.persist();\n },\n });\n };\n\n const enforce = createEnforceGate({\n checkAcl: (pubkey, dTag, aggregateHash, capability) =>\n aclState.check(pubkey, dTag, aggregateHash, capability),\n resolveIdentity: (pubkey) => {\n const entry = sessionRegistry.getEntry(pubkey);\n return entry ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash } : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\n\n const enforceNap = createNapEnforceGate({\n checkAcl: (pubkey, dTag, aggregateHash, capability) =>\n aclState.check(pubkey, dTag, aggregateHash, capability),\n resolveIdentityByWindowId: (windowId) => {\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n return entry\n ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash }\n : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\n\n incRuntime = createIncRuntime(\n hooks,\n sessionRegistry,\n (targetWindowId, message) => enforceNap(targetWindowId, 'relay:read', message).allowed,\n );\n\n const firewallGate = createFirewallGate({ firewallState, sessionRegistry, hooks, fireConsent });\n\n const eventBuffer = createEventBuffer(\n hooks.sendToNapplet,\n sessionRegistry,\n enforce,\n subscriptions,\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().ringBufferSize ?? RING_BUFFER_SIZE\n : undefined,\n );\n\n aclState.load();\n firewallState.load();\n manifestCache.load();\n\n const domainHandlers = createRuntimeDomainHandlers({ hooks, serviceRegistry, sessionRegistry, aclState });\n const dispatchNapEnvelope = createNapEnvelopeDispatcher({\n relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),\n identity: createIdentityHandler({ hooks, serviceRegistry }),\n count: createCountHandler({ hooks, serviceRegistry }),\n inc: incRuntime.handleMessage,\n ...domainHandlers,\n });\n const handleMessage = createMessageHandler(hooks, sessionRegistry, enforceNap, dispatchNapEnvelope, firewallGate);\n\n return createRuntimeInstance({\n hooks,\n serviceRegistry,\n registeredServices,\n replayDetector,\n subscriptions,\n eventBuffer,\n incRuntime,\n sessionRegistry,\n aclState,\n firewallState,\n manifestCache,\n consentHandlerRef,\n handleMessage,\n });\n}\n","\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceRegistry, SendToNapplet } from './types.js';\n\n/**\n * Route a NappletMessage envelope to the matching service handler.\n *\n * Direct NAP-domain services are routed by the domain in their wire\n * message.type (e.g., 'signer.signEvent' -> 'signer' service). INC messages\n * are owned by the INC runtime; their topic values are opaque here.\n *\n * Returns true if a service handled the message, false otherwise.\n *\n * @param windowId - The napplet window that sent the message\n * @param message - The NappletMessage envelope to route\n * @param services - The service registry to look up handlers\n * @param sendToNapplet - Callback to send messages back to the napplet\n * @returns true if a service handled the message, false if no matching service\n *\n * @example\n * ```ts\n * const handled = routeServiceMessage(windowId, msg, services, sendToNapplet);\n * if (!handled) { // fallback handling }\n * ```\n */\nexport function routeServiceMessage(\n windowId: string,\n message: NappletMessage,\n services: ServiceRegistry,\n sendToNapplet: SendToNapplet,\n): boolean {\n // INC emissions belong exclusively to the INC runtime.\n if (message.type === 'inc.emit') return false;\n\n // Direct NAP-domain services: signer.*, relay.*, storage.* route by wire type domain.\n const domain = message.type.split('.')[0];\n const handler = services[domain];\n if (handler) {\n handler.handleMessage(windowId, message, (msg) => sendToNapplet(windowId, msg));\n return true;\n }\n\n return false;\n}\n\n/**\n * Notify all registered service handlers that a napplet window was destroyed.\n * Calls onWindowDestroyed() on every handler that implements it.\n * Errors in individual handlers are caught and silently ignored to prevent\n * one service's cleanup failure from blocking others.\n *\n * @param windowId - The destroyed napplet's window identifier\n * @param services - The service registry containing all handlers\n *\n * @example\n * ```ts\n * notifyServiceWindowDestroyed('window-1', services);\n * ```\n */\nexport function notifyServiceWindowDestroyed(\n windowId: string,\n services: ServiceRegistry,\n): void {\n for (const handler of Object.values(services)) {\n try {\n handler.onWindowDestroyed?.(windowId);\n } catch {\n /* Service cleanup is best-effort — don't let one service block others */\n }\n }\n}\n","import type { EventTemplate, NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';\nimport type { RelayMessage } from '@napplet/nap/relay/types';\n\nimport { matchesAnyFilter, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport type { ReplayDetector } from './replay.js';\nimport {\n createRelayEventResult,\n createRelayEventResultWithHints,\n relayEventResultFromCarrier,\n type RelayEventResult,\n} from './relay-result.js';\nimport type { RuntimeAdapter, ServiceHandler, ServiceRegistry } from './types.js';\n\ndeclare function setTimeout(callback: () => void, ms: number): unknown;\ndeclare function clearTimeout(id: unknown): void;\n\ntype RuntimeRelayMessage = RelayMessage & {\n subId?: string;\n filters?: NostrFilter[];\n event?: EventTemplate | NostrEvent;\n result?: RelayEventResult;\n id?: string;\n relay?: string;\n};\n\ntype RelayHandlerContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n subscriptions: Map<string, SubscriptionEntry>;\n eventBuffer: EventBuffer;\n replayDetector: ReplayDetector;\n};\n\nexport type RelayHandler = (windowId: string, msg: NappletMessage) => void;\n\nexport function createRelayHandler(context: RelayHandlerContext): RelayHandler {\n return function handleRelayMessage(windowId: string, msg: NappletMessage): void {\n const m = msg as RuntimeRelayMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'subscribe':\n handleRelaySubscribe(context, windowId, msg, m);\n return;\n case 'close':\n handleRelayClose(context, windowId, msg, m);\n return;\n case 'publish':\n handleRelayPublish(context, windowId, m);\n return;\n case 'publishEncrypted':\n handleRelayPublishEncrypted(context, windowId, msg);\n return;\n case 'query':\n handleRelayQuery(context, windowId, m);\n return;\n default:\n return;\n }\n };\n}\n\nfunction relayServiceFrom(context: RelayHandlerContext): ServiceHandler | undefined {\n return context.serviceRegistry['relay'] ?? context.serviceRegistry['relay-pool'];\n}\n\nfunction isShellKindQuery(filters: NostrFilter[]): boolean {\n return filters.length > 0 && filters.every((filter) => filter.kinds?.every((kind) => kind >= 29000 && kind < 30000));\n}\n\nfunction sendRelayEvent(hooks: RuntimeAdapter, windowId: string, subId: string, result: RelayEventResult): void {\n hooks.sendToNapplet(windowId, { type: 'relay.event', subId, result } as NappletMessage);\n}\n\nfunction normalizeRelayServiceResponse(resp: NappletMessage): NappletMessage {\n const m = resp as RuntimeRelayMessage;\n if (m.type !== 'relay.event') return resp;\n const result = relayEventResultFromCarrier(m);\n return result ? { type: 'relay.event', subId: m.subId, result } as NappletMessage : resp;\n}\n\nfunction handleRelaySubscribe(\n context: RelayHandlerContext,\n windowId: string,\n msg: NappletMessage,\n m: RuntimeRelayMessage,\n): void {\n const { eventBuffer, hooks, serviceRegistry, subscriptions } = context;\n const subId = m.subId ?? '';\n const filters = m.filters ?? [];\n if (!subId) return;\n\n const subKey = `${windowId}:${subId}`;\n subscriptions.set(subKey, { windowId, filters });\n\n const seenIds = new Set<string>();\n function deliver(event: NostrEvent, relayHints?: string[]): void {\n if (seenIds.has(event.id)) return;\n seenIds.add(event.id);\n if (subscriptions.has(subKey)) {\n sendRelayEvent(hooks, windowId, subId, createRelayEventResultWithHints(event, relayHints));\n }\n }\n\n for (const bufferedEvent of eventBuffer.getBufferedEvents()) {\n if (matchesAnyFilter(bufferedEvent, filters)) deliver(bufferedEvent);\n }\n\n const isShellKind = isShellKindQuery(filters);\n const relayService = relayServiceFrom(context);\n const cacheService = !serviceRegistry['relay'] ? serviceRegistry['cache'] : undefined;\n\n if (!isShellKind && relayService) {\n relayService.handleMessage(windowId, msg, (resp: NappletMessage) => {\n if (!subscriptions.has(subKey)) return;\n hooks.sendToNapplet(windowId, normalizeRelayServiceResponse(resp));\n });\n if (cacheService) {\n cacheService.handleMessage(windowId, msg, (resp: NappletMessage) => {\n if (!subscriptions.has(subKey)) return;\n hooks.sendToNapplet(windowId, normalizeRelayServiceResponse(resp));\n });\n }\n return;\n }\n\n const relayHint = typeof m.relay === 'string' && m.relay.length > 0 ? m.relay : undefined;\n deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver, relayHint);\n}\n\nfunction deliverFromRuntimeBackends(\n context: RelayHandlerContext,\n windowId: string,\n subId: string,\n subKey: string,\n filters: NostrFilter[],\n isShellKind: boolean,\n deliver: (event: NostrEvent, relayHints?: string[]) => void,\n relayHint?: string,\n): void {\n const { hooks } = context;\n const cache = hooks.cache;\n\n if (cache?.isAvailable() && !isShellKind) {\n cache.query(filters)\n .then((cachedEvents) => {\n for (const event of cachedEvents) deliver(event);\n })\n .catch(() => {});\n }\n\n const pool = hooks.relayPool;\n if (!pool?.isAvailable() && !isShellKind) {\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n if (!pool?.isAvailable() || isShellKind) return;\n\n const relayUrls = relayHint ? [relayHint] : pool.selectRelayTier(filters);\n let eoseSent = false;\n const eoseFallbackTimer = setTimeout(() => {\n if (!eoseSent) {\n eoseSent = true;\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n }\n }, 15_000);\n\n const subscription = pool.subscribe(filters, (item) => {\n if (item === 'EOSE') {\n clearTimeout(eoseFallbackTimer);\n if (!eoseSent) {\n eoseSent = true;\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n }\n return;\n }\n const event = item as NostrEvent;\n deliver(event, relayUrls);\n if (cache?.isAvailable() && !isShellKind) {\n try { cache.store(event); } catch { return; }\n }\n }, relayUrls);\n\n pool.trackSubscription(subKey, () => {\n clearTimeout(eoseFallbackTimer);\n subscription.unsubscribe();\n });\n}\n\nfunction handleRelayClose(\n context: RelayHandlerContext,\n windowId: string,\n msg: NappletMessage,\n m: RuntimeRelayMessage,\n): void {\n const { hooks, subscriptions } = context;\n const subId = m.subId ?? '';\n if (!subId) return;\n const subKey = `${windowId}:${subId}`;\n subscriptions.delete(subKey);\n\n const relayService = relayServiceFrom(context);\n if (relayService) relayService.handleMessage(windowId, msg, () => {});\n hooks.relayPool?.untrackSubscription(subKey);\n hooks.sendToNapplet(windowId, { type: 'relay.closed', subId, message: '' } as NappletMessage);\n}\n\nfunction handleRelayPublish(\n context: RelayHandlerContext,\n windowId: string,\n m: RuntimeRelayMessage,\n): void {\n const { hooks, replayDetector } = context;\n const eventTemplate = m.event;\n const id = m.id ?? '';\n if (!eventTemplate || typeof eventTemplate !== 'object') {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, 'invalid event template');\n return;\n }\n\n const signer = hooks.auth.getSigner();\n const signEvent = signer?.signEvent?.bind(signer);\n if (!signEvent) {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, 'no signer configured');\n return;\n }\n\n void (async (): Promise<void> => {\n try {\n const signed = await signEvent(eventTemplate);\n if (!signed) {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, 'signEvent returned no event');\n return;\n }\n const replayResult = replayDetector.reserve(signed);\n if (replayResult !== null) {\n sendRelayPublishResult(hooks, windowId, id, false, undefined, replayResult);\n return;\n }\n publishSignedRelayEvent(context, windowId, id, signed, (ok, error) => {\n if (ok) replayDetector.commit(signed.id);\n else replayDetector.release(signed.id);\n sendRelayPublishResult(hooks, windowId, id, ok, ok ? signed : undefined, error);\n });\n } catch (error) {\n sendRelayPublishResult(\n hooks,\n windowId,\n id,\n false,\n undefined,\n error instanceof Error && error.message ? error.message : 'event signing failed',\n );\n }\n })();\n}\n\nfunction sendRelayPublishResult(\n hooks: RuntimeAdapter,\n windowId: string,\n id: string,\n ok: boolean,\n event?: NostrEvent,\n error?: string,\n): void {\n const result = ok && event\n ? { type: 'relay.publish.result', id, ok: true, event, eventId: event.id }\n : { type: 'relay.publish.result', id, ok: false, error: error ?? 'publish failed' };\n hooks.sendToNapplet(windowId, result as NappletMessage);\n}\n\nfunction publishSignedRelayEvent(\n context: RelayHandlerContext,\n windowId: string,\n id: string,\n signed: NostrEvent,\n reply: (ok: boolean, error?: string) => void,\n): void {\n const { eventBuffer, hooks } = context;\n let settled = false;\n const settle = (ok: boolean, error?: string): void => {\n if (settled) return;\n settled = true;\n if (ok) {\n try { eventBuffer.bufferAndDeliver(signed, windowId); } catch { /* local delivery is best-effort */ }\n }\n reply(ok, error);\n };\n\n const relayService = relayServiceFrom(context);\n if (relayService) {\n const publishMessage = { type: 'relay.publish', id, event: signed } as NappletMessage;\n try {\n relayService.handleMessage(windowId, publishMessage, (response: NappletMessage) => {\n if (\n response.type !== 'relay.publish.result'\n && response.type !== 'relay.publish.error'\n ) {\n return;\n }\n const result = response as NappletMessage & {\n ok?: boolean;\n accepted?: boolean;\n error?: string;\n message?: string;\n };\n const ok = response.type === 'relay.publish.result'\n && (result.ok ?? result.accepted ?? false);\n settle(ok, ok ? undefined : result.error ?? result.message ?? 'publish failed');\n });\n } catch (error) {\n settle(\n false,\n error instanceof Error && error.message ? error.message : 'relay service failed',\n );\n }\n return;\n }\n\n if (!hooks.relayPool?.isAvailable()) {\n settle(false, 'no relay pool available');\n return;\n }\n try {\n const publishResult = hooks.relayPool.publish(signed);\n void Promise.resolve(publishResult).then(\n () => settle(true),\n (error: unknown) => settle(\n false,\n error instanceof Error && error.message ? error.message : 'publish failed',\n ),\n );\n } catch (error) {\n settle(\n false,\n error instanceof Error && error.message ? error.message : 'publish failed',\n );\n }\n}\n\nfunction handleRelayPublishEncrypted(\n context: RelayHandlerContext,\n windowId: string,\n msg: NappletMessage,\n): void {\n const { hooks } = context;\n const id = (msg as RuntimeRelayMessage).id ?? '';\n const eventTemplate = (msg as RuntimeRelayMessage).event;\n const peMsg = msg as NappletMessage & { recipient?: string; encryption?: string };\n const recipient = peMsg.recipient ?? '';\n const encryption = (peMsg.encryption ?? 'nip44') as 'nip04' | 'nip44' | string;\n\n const replyPe = (ok: boolean, extra: Record<string, unknown> = {}) => {\n hooks.sendToNapplet(windowId, { type: 'relay.publishEncrypted.result', id, ok, ...extra } as NappletMessage);\n };\n\n if (!recipient) { replyPe(false, { error: 'missing recipient' }); return; }\n if (encryption !== 'nip44' && encryption !== 'nip04') {\n replyPe(false, { error: `unsupported encryption scheme: ${encryption}` });\n return;\n }\n const peSigner = hooks.auth.getSigner();\n if (!peSigner) { replyPe(false, { error: 'no signer configured' }); return; }\n if (!eventTemplate || typeof eventTemplate !== 'object') {\n replyPe(false, { error: 'invalid event template' });\n return;\n }\n\n publishEncrypted(context, windowId, id, recipient, encryption, eventTemplate, replyPe);\n}\n\nfunction publishEncrypted(\n context: RelayHandlerContext,\n windowId: string,\n id: string,\n recipient: string,\n encryption: 'nip04' | 'nip44' | string,\n eventTemplate: EventTemplate,\n replyPe: (ok: boolean, extra?: Record<string, unknown>) => void,\n): void {\n const { hooks } = context;\n const peSigner = hooks.auth.getSigner();\n if (!peSigner) return;\n\n (async (): Promise<void> => {\n try {\n const plaintext = String((eventTemplate as { content?: unknown }).content ?? '');\n const ciphertext: string = encryption === 'nip44'\n ? (await peSigner.nip44?.encrypt(recipient, plaintext)) ?? ''\n : (await peSigner.nip04?.encrypt(recipient, plaintext)) ?? '';\n const eventWithCiphertext = { ...eventTemplate, content: ciphertext };\n const signed = await peSigner.signEvent?.(eventWithCiphertext);\n if (!signed) { replyPe(false, { error: 'signEvent returned null' }); return; }\n\n publishSignedRelayEvent(context, windowId, id, signed, (ok, error) => {\n replyPe(ok, ok\n ? { event: signed, eventId: signed.id }\n : { error: error ?? 'publish failed' });\n });\n } catch (err) {\n replyPe(false, { error: (err as Error)?.message ?? 'encryption failed' });\n }\n })();\n}\n\nfunction handleRelayQuery(\n context: RelayHandlerContext,\n windowId: string,\n m: RuntimeRelayMessage,\n): void {\n const { hooks, serviceRegistry, eventBuffer } = context;\n const id = m.id ?? '';\n const filters = m.filters ?? [];\n\n const seenIds = new Set<string>();\n const events: RelayEventResult[] = [];\n for (const event of eventBuffer.getBufferedEvents()) {\n if (matchesAnyFilter(event, filters) && !seenIds.has(event.id)) {\n seenIds.add(event.id);\n events.push(createRelayEventResult(event));\n }\n }\n\n let settled = false;\n let fallbackTimer: unknown;\n\n function settle(): void {\n if (settled) return;\n settled = true;\n clearTimeout(fallbackTimer);\n hooks.sendToNapplet(windowId, { type: 'relay.query.result', id, events } as NappletMessage);\n }\n\n // Shell-kind queries (kinds 29000-29999): buffer-only, no relay delegation\n if (isShellKindQuery(filters)) {\n settle();\n return;\n }\n\n const relayService = relayServiceFrom(context);\n const cacheService = !serviceRegistry['relay'] ? serviceRegistry['cache'] : undefined;\n\n if (relayService) {\n const svc = relayService;\n // Delegate to registered relay service via a synthesized relay.subscribe\n const subId = '__query__:' + id;\n const subscribeMsg: RuntimeRelayMessage = {\n type: 'relay.subscribe',\n id,\n subId,\n filters,\n };\n if (typeof m.relay === 'string' && m.relay.length > 0) {\n subscribeMsg.relay = m.relay;\n }\n\n let openBackends = cacheService ? 2 : 1;\n\n function onBackendEose(): void {\n openBackends--;\n if (openBackends <= 0) {\n // Tear down the delegated subscriptions\n const closeMsg = { type: 'relay.close', id, subId } as NappletMessage;\n svc.handleMessage(windowId, closeMsg, () => {});\n if (cacheService) {\n cacheService.handleMessage(windowId, closeMsg, () => {});\n }\n settle();\n }\n }\n\n function makeCollector(): (resp: NappletMessage) => void {\n return function collector(resp: NappletMessage): void {\n const r = resp as RuntimeRelayMessage;\n const result = r.type === 'relay.event' ? relayEventResultFromCarrier(r) : null;\n if (result && !seenIds.has(result.event.id)) {\n seenIds.add(result.event.id);\n events.push(result);\n } else if (r.type === 'relay.eose') {\n onBackendEose();\n }\n };\n }\n\n fallbackTimer = setTimeout(() => {\n const closeMsg = { type: 'relay.close', id, subId } as NappletMessage;\n svc.handleMessage(windowId, closeMsg, () => {});\n if (cacheService) {\n cacheService.handleMessage(windowId, closeMsg, () => {});\n }\n settle();\n }, 15_000);\n\n svc.handleMessage(windowId, subscribeMsg as NappletMessage, makeCollector());\n if (cacheService) {\n cacheService.handleMessage(windowId, subscribeMsg as NappletMessage, makeCollector());\n }\n return;\n }\n\n // No registered relay service — fall back to runtime hooks (cache + relay pool)\n const cache = hooks.cache;\n const pool = hooks.relayPool;\n\n if (cache?.isAvailable()) {\n cache.query(filters)\n .then((cachedEvents) => {\n for (const event of cachedEvents) {\n if (!seenIds.has(event.id)) {\n seenIds.add(event.id);\n events.push(createRelayEventResult(event));\n }\n }\n })\n .catch(() => {});\n }\n\n if (!pool?.isAvailable()) {\n settle();\n return;\n }\n\n const relayHint = typeof m.relay === 'string' && m.relay.length > 0 ? m.relay : undefined;\n const relayUrls = relayHint ? [relayHint] : pool.selectRelayTier(filters);\n\n let poolSubscription: { unsubscribe(): void } | undefined;\n\n fallbackTimer = setTimeout(() => {\n poolSubscription?.unsubscribe();\n settle();\n }, 15_000);\n\n poolSubscription = pool.subscribe(filters, (item) => {\n if (item === 'EOSE') {\n clearTimeout(fallbackTimer);\n poolSubscription?.unsubscribe();\n settle();\n return;\n }\n const event = item as NostrEvent;\n if (!seenIds.has(event.id)) {\n seenIds.add(event.id);\n events.push(createRelayEventResultWithHints(event, relayUrls));\n }\n if (cache?.isAvailable()) {\n try { cache.store(event); } catch { return; }\n }\n }, relayUrls);\n}\n","import type { NappletMessage } from '@napplet/core';\n\n/**\n * Fixed non-sensitive theme used when a runtime-owned response cannot obtain\n * a registered theme service. Keep synchronized with the reference service\n * default without importing @kehto/services (which depends on runtime).\n */\nexport const RUNTIME_THEME_FALLBACK = {\n colors: { background: '#0a0a0a', text: '#e0e0e0', primary: '#7aa2f7' },\n} as const;\n\nconst IDENTITY_SAFE_DEFAULTS = {\n 'identity.getPublicKey': { pubkey: '' },\n 'identity.getRelays': { relays: {} },\n 'identity.getProfile': { profile: null },\n 'identity.getFollows': { pubkeys: [] },\n 'identity.getList': { entries: [] },\n 'identity.getZaps': { zaps: [] },\n 'identity.getMutes': { pubkeys: [] },\n 'identity.getBlocked': { pubkeys: [] },\n 'identity.getBadges': { badges: [] },\n} as const;\n\ntype RuntimeOwnedIdentityOrThemeRequest = keyof typeof IDENTITY_SAFE_DEFAULTS | 'theme.get';\n\nfunction isRuntimeOwnedIdentityOrThemeRequest(type: string): type is RuntimeOwnedIdentityOrThemeRequest {\n return type === 'theme.get' || type in IDENTITY_SAFE_DEFAULTS;\n}\n\nfunction createRuntimeOwnedResultEnvelope(type: string, id: string): NappletMessage {\n return Object.assign({ type } as NappletMessage, { id });\n}\n\nfunction intentRequestDetails(message: NappletMessage): { archetype: string; action: string } {\n const request = (message as NappletMessage & {\n request?: { archetype?: unknown; action?: unknown };\n }).request;\n return {\n archetype: typeof request?.archetype === 'string' ? request.archetype : '',\n action: typeof request?.action === 'string' && request.action.length > 0\n ? request.action\n : 'open',\n };\n}\n\nfunction createIntentInvokeRejection(message: NappletMessage, error: string): NappletMessage {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n const { archetype, action } = intentRequestDetails(message);\n return {\n type: 'intent.invoke.result',\n id,\n result: { ok: false, archetype, action, handled: false, error },\n } as NappletMessage;\n}\n\n/** Return whether a message belongs to a runtime-owned identity/theme domain. */\nexport function isIdentityOrThemeMessage(message: NappletMessage): boolean {\n return message.type.startsWith('identity.') || message.type.startsWith('theme.');\n}\n\n/**\n * Shape a non-sensitive NAP-INTENT policy denial on its sanctioned result wire.\n *\n * Only napplet-originated intent requests have denial responses. Runtime push,\n * result, obsolete error, and unknown actions return `undefined` so a forged\n * source envelope cannot manufacture another response.\n *\n * @param message - Incoming intent request envelope.\n * @returns A fixed denial result for a sanctioned request, or `undefined`.\n */\nexport function createIntentPolicyDenial(\n message: NappletMessage,\n): NappletMessage | undefined {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n if (message.type === 'intent.invoke') {\n return createIntentInvokeRejection(message, 'invoke rejected');\n }\n if (message.type === 'intent.available' || message.type === 'intent.handlers') {\n return {\n type: `${message.type}.result`,\n id,\n error: 'intent request denied',\n } as NappletMessage;\n }\n return undefined;\n}\n\n/** Shape canonical replies when no NAP-INTENT service is registered. */\nexport function createIntentServiceUnavailableResult(\n message: NappletMessage,\n): NappletMessage | undefined {\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n if (message.type === 'intent.invoke') {\n return createIntentInvokeRejection(message, 'no handler');\n }\n if (message.type === 'intent.available') {\n const archetype = (message as NappletMessage & { archetype?: unknown }).archetype;\n return {\n type: 'intent.available.result',\n id,\n availability: {\n archetype: typeof archetype === 'string' ? archetype : '',\n available: false,\n candidates: [],\n hasDefault: false,\n },\n } as NappletMessage;\n }\n if (message.type === 'intent.handlers') {\n return { type: 'intent.handlers.result', id, handlers: [] } as NappletMessage;\n }\n return undefined;\n}\n\n/**\n * Shape an allowlisted runtime-owned response with the ordinary request id.\n *\n * Unsupported identity/theme messages deliberately return undefined so the\n * dispatch boundary can ignore them rather than synthesize a generic error.\n */\nexport function createCanonicalDomainResult(message: NappletMessage): NappletMessage | undefined {\n if (!isRuntimeOwnedIdentityOrThemeRequest(message.type)) return undefined;\n\n const id = (message as NappletMessage & { id?: string }).id ?? '';\n if (message.type === 'theme.get') {\n return Object.assign(\n createRuntimeOwnedResultEnvelope('theme.get.result', id),\n { theme: RUNTIME_THEME_FALLBACK },\n );\n }\n\n return Object.assign(\n createRuntimeOwnedResultEnvelope(`${message.type}.result`, id),\n IDENTITY_SAFE_DEFAULTS[message.type],\n );\n}\n","import type { NappletMessage } from '@napplet/core';\n\nimport type { RuntimeAdapter, ServiceRegistry } from './types.js';\nimport { createCanonicalDomainResult } from './domain-results.js';\n\ntype IdentityHandlerContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n};\n\nexport type IdentityHandler = (windowId: string, msg: NappletMessage) => void;\n\nexport function createIdentityHandler(context: IdentityHandlerContext): IdentityHandler {\n return function handleIdentityMessage(windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const identityService = serviceRegistry['identity'];\n if (identityService) {\n identityService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n\n const action = msg.type.slice('identity.'.length);\n const signer = hooks.auth.getSigner();\n const fallback = createCanonicalDomainResult(msg);\n if (!fallback) return;\n const sendResult = (payload: Record<string, unknown> = {}) => {\n hooks.sendToNapplet(windowId, { ...fallback, ...payload } as NappletMessage);\n };\n\n switch (action) {\n case 'getPublicKey':\n if (!signer) { sendResult({ pubkey: '' }); return; }\n Promise.resolve(signer.getPublicKey?.())\n .then((pubkey) => sendResult({ pubkey: pubkey ?? '' }))\n .catch(() => sendResult({ pubkey: '' }));\n return;\n case 'getRelays':\n if (!signer) { sendResult(); return; }\n Promise.resolve(signer.getRelays?.() ?? {})\n .then((relays) => sendResult({ relays }))\n .catch(() => sendResult());\n return;\n case 'getProfile': sendResult({ profile: null }); return;\n case 'getFollows': sendResult({ pubkeys: [] }); return;\n case 'getList': sendResult({ entries: [] }); return;\n case 'getZaps': sendResult({ zaps: [] }); return;\n case 'getMutes': sendResult({ pubkeys: [] }); return;\n case 'getBlocked': sendResult({ pubkeys: [] }); return;\n case 'getBadges': sendResult({ badges: [] }); return;\n default: return;\n }\n };\n}\n","import type { NappletMessage, NostrFilter } from '@napplet/core';\n\nimport type { RuntimeAdapter, ServiceRegistry } from './types.js';\n\ntype RuntimeCountMessage = NappletMessage & {\n id?: unknown;\n filters?: unknown;\n options?: unknown;\n};\n\ntype CountHandlerContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n};\n\nexport type CountHandler = (windowId: string, msg: NappletMessage) => void;\n\nexport function createCountHandler(context: CountHandlerContext): CountHandler {\n return function handleCountMessage(windowId: string, msg: NappletMessage): void {\n if (msg.type !== 'count.query') return;\n\n const message = msg as RuntimeCountMessage;\n const id = typeof message.id === 'string' ? message.id : '';\n const filters = message.filters;\n\n if (!isValidFilterArray(filters)) {\n sendCountError(context.hooks, windowId, id, 'invalid-filter', 'count.query requires a non-empty filters array');\n return;\n }\n\n if (message.options !== undefined && !isPlainObject(message.options)) {\n sendCountError(context.hooks, windowId, id, 'invalid-filter', 'count.query options must be an object when present');\n return;\n }\n\n const service = context.serviceRegistry['count'];\n if (!service) {\n sendCountError(\n context.hooks,\n windowId,\n id,\n 'count-unavailable',\n 'count service unavailable; refusing to emulate counts by fetching event payloads',\n );\n return;\n }\n\n service.handleMessage(windowId, msg, (resp: NappletMessage) => {\n context.hooks.sendToNapplet(windowId, normalizeCountResult(resp, id));\n });\n };\n}\n\nfunction normalizeCountResult(resp: NappletMessage, id: string): NappletMessage {\n const value = resp as NappletMessage & {\n id?: unknown;\n ok?: unknown;\n count?: unknown;\n approximate?: unknown;\n hll?: unknown;\n relays?: unknown;\n error?: unknown;\n reason?: unknown;\n };\n\n return {\n type: 'count.query.result',\n id: typeof value.id === 'string' ? value.id : id,\n ok: value.ok === true,\n ...(typeof value.count === 'number' && Number.isFinite(value.count) && value.count >= 0\n ? { count: Math.floor(value.count) }\n : {}),\n ...(typeof value.approximate === 'boolean' ? { approximate: value.approximate } : {}),\n ...(typeof value.hll === 'string' ? { hll: value.hll } : {}),\n ...(Array.isArray(value.relays) ? { relays: value.relays.filter((relay): relay is string => typeof relay === 'string') } : {}),\n ...(typeof value.error === 'string' ? { error: value.error } : {}),\n ...(typeof value.reason === 'string' ? { reason: value.reason } : {}),\n } as NappletMessage;\n}\n\nfunction sendCountError(\n hooks: RuntimeAdapter,\n windowId: string,\n id: string,\n error: string,\n reason: string,\n): void {\n hooks.sendToNapplet(windowId, {\n type: 'count.query.result',\n id,\n ok: false,\n error,\n reason,\n } as NappletMessage);\n}\n\nfunction isValidFilterArray(value: unknown): value is NostrFilter[] {\n return Array.isArray(value)\n && value.length > 0\n && value.every((filter) => isPlainObject(filter));\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","import type { NappletMessage } from '@napplet/core';\nimport type { IncMessage } from '@napplet/nap/inc/types';\n\nimport type { SessionRegistry } from './session-registry.js';\nimport type { RuntimeAdapter } from './types.js';\n\ninterface IncChannel {\n channelId: string;\n peerA: string;\n peerB: string;\n}\n\ntype RuntimeIncMessage = IncMessage & {\n id?: string;\n topic?: string;\n payload?: unknown;\n target?: string;\n channelId?: string;\n};\n\ntype IncState = {\n subscriptions: Map<string, Set<string>>;\n channels: Map<string, IncChannel>;\n channelsByWindow: Map<string, Set<string>>;\n};\n\ntype ChannelOpenAuthorizer = (targetWindowId: string, message: NappletMessage) => boolean;\n\nconst MAX_CHANNELS_PER_WINDOW = 32;\n\nexport interface IncRuntime {\n handleMessage(windowId: string, msg: NappletMessage): void;\n destroyWindow(windowId: string): void;\n /**\n * Close a live window's established channels after its open-time authority is revoked.\n *\n * @param windowId - Internal identity of the session whose open-time authority changed\n */\n revokeWindow(windowId: string): void;\n clear(): void;\n}\n\nexport function createIncRuntime(\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n authorizeChannelOpen: ChannelOpenAuthorizer,\n): IncRuntime {\n const state: IncState = {\n subscriptions: new Map(),\n channels: new Map(),\n channelsByWindow: new Map(),\n };\n\n return {\n handleMessage(windowId: string, msg: NappletMessage): void {\n handleIncMessage(state, hooks, sessionRegistry, authorizeChannelOpen, windowId, msg);\n },\n destroyWindow(windowId: string): void {\n removeWindowChannels(state, hooks, windowId);\n removeWindowSubscriptions(state, windowId);\n },\n revokeWindow(windowId: string): void {\n revokeWindowChannels(state, hooks, windowId);\n removeWindowSubscriptions(state, windowId);\n },\n clear(): void {\n state.subscriptions.clear();\n state.channels.clear();\n state.channelsByWindow.clear();\n },\n };\n}\n\nfunction addChannel(state: IncState, channelId: string, peerA: string, peerB: string): void {\n state.channels.set(channelId, { channelId, peerA, peerB });\n for (const windowId of [peerA, peerB]) {\n let set = state.channelsByWindow.get(windowId);\n if (!set) {\n set = new Set();\n state.channelsByWindow.set(windowId, set);\n }\n set.add(channelId);\n }\n}\n\nfunction removeChannel(state: IncState, channelId: string): void {\n const channel = state.channels.get(channelId);\n if (!channel) return;\n state.channels.delete(channelId);\n for (const windowId of [channel.peerA, channel.peerB]) {\n const set = state.channelsByWindow.get(windowId);\n if (!set) continue;\n set.delete(channelId);\n if (set.size === 0) state.channelsByWindow.delete(windowId);\n }\n}\n\nfunction peerOf(state: IncState, channelId: string, self: string): string | null {\n const channel = state.channels.get(channelId);\n if (!channel) return null;\n if (channel.peerA === self) return channel.peerB;\n if (channel.peerB === self) return channel.peerA;\n return null;\n}\n\nfunction resolveTarget(sessionRegistry: SessionRegistry, target: string): string | null {\n return sessionRegistry.getWindowIdByDTag(target) ?? null;\n}\n\nfunction hasReachedChannelLimit(state: IncState, windowId: string): boolean {\n return (state.channelsByWindow.get(windowId)?.size ?? 0) >= MAX_CHANNELS_PER_WINDOW;\n}\n\nfunction handleIncMessage(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n authorizeChannelOpen: ChannelOpenAuthorizer,\n windowId: string,\n msg: NappletMessage,\n): void {\n const m = msg as RuntimeIncMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'emit': handleEmit(state, hooks, sessionRegistry, windowId, m); return;\n case 'subscribe': handleSubscribe(state, hooks, windowId, m); return;\n case 'unsubscribe': handleUnsubscribe(state, windowId, m); return;\n case 'channel.open': handleChannelOpen(state, hooks, sessionRegistry, authorizeChannelOpen, windowId, m); return;\n case 'channel.emit': handleChannelEmit(state, hooks, sessionRegistry, windowId, m); return;\n case 'channel.broadcast': handleChannelBroadcast(state, hooks, sessionRegistry, windowId, m); return;\n case 'channel.list': handleChannelList(state, hooks, sessionRegistry, windowId, m); return;\n case 'channel.close': handleChannelClose(state, hooks, windowId, m); return;\n default: return;\n }\n}\n\nfunction handleEmit(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const topic = m.topic ?? '';\n const sender = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n if (!sender) return;\n if (!topic) return;\n const subscribers = state.subscriptions.get(topic);\n if (!subscribers) return;\n for (const subscriberWindowId of subscribers) {\n if (subscriberWindowId !== windowId) {\n hooks.sendToNapplet(subscriberWindowId, {\n type: 'inc.event',\n topic,\n sender,\n ...(m.payload === undefined ? {} : { payload: m.payload }),\n } as NappletMessage);\n }\n }\n}\n\nfunction handleSubscribe(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const id = m.id ?? '';\n const topic = m.topic ?? '';\n if (!topic) {\n hooks.sendToNapplet(windowId, { type: 'inc.subscribe.result', id, error: 'missing topic' } as NappletMessage);\n return;\n }\n let subscriptions = state.subscriptions.get(topic);\n if (!subscriptions) {\n subscriptions = new Set();\n state.subscriptions.set(topic, subscriptions);\n }\n subscriptions.add(windowId);\n hooks.sendToNapplet(windowId, { type: 'inc.subscribe.result', id } as NappletMessage);\n}\n\nfunction handleUnsubscribe(state: IncState, windowId: string, m: RuntimeIncMessage): void {\n const topic = m.topic ?? '';\n if (!topic) return;\n const subscriptions = state.subscriptions.get(topic);\n if (!subscriptions) return;\n subscriptions.delete(windowId);\n if (subscriptions.size === 0) state.subscriptions.delete(topic);\n}\n\nfunction handleChannelOpen(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n authorizeChannelOpen: ChannelOpenAuthorizer,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const id = m.id ?? '';\n const peerWindow = resolveTarget(sessionRegistry, m.target ?? '');\n const openerDTag = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n const peerDTag = peerWindow ? sessionRegistry.getEntryByWindowId(peerWindow)?.dTag : undefined;\n if (!peerWindow || !openerDTag || !peerDTag) {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'target not found' } as NappletMessage);\n return;\n }\n if (!authorizeChannelOpen(peerWindow, m)) {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'target denied' } as NappletMessage);\n return;\n }\n if (hasReachedChannelLimit(state, windowId) || hasReachedChannelLimit(state, peerWindow)) {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'channel limit reached' } as NappletMessage);\n return;\n }\n const channelId = hooks.crypto.randomUUID().replace(/-/g, '').slice(0, 32);\n addChannel(state, channelId, windowId, peerWindow);\n try {\n hooks.sendToNapplet(peerWindow, { type: 'inc.channel.opened', channelId, peer: openerDTag } as NappletMessage);\n } catch {\n removeChannel(state, channelId);\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, error: 'target unavailable' } as NappletMessage);\n return;\n }\n try {\n hooks.sendToNapplet(windowId, { type: 'inc.channel.open.result', id, channelId, peer: peerDTag } as NappletMessage);\n } catch {\n teardownChannel(state, hooks, channelId);\n }\n}\n\nfunction handleChannelEmit(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const peer = peerOf(state, m.channelId ?? '', windowId);\n const sender = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n if (peer && sender) {\n hooks.sendToNapplet(peer, {\n type: 'inc.channel.event',\n channelId: m.channelId ?? '',\n sender,\n ...(m.payload === undefined ? {} : { payload: m.payload }),\n } as NappletMessage);\n }\n}\n\nfunction handleChannelBroadcast(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channels = state.channelsByWindow.get(windowId);\n const sender = sessionRegistry.getEntryByWindowId(windowId)?.dTag;\n if (!channels || !sender) return;\n for (const channelId of channels) {\n const peer = peerOf(state, channelId, windowId);\n if (peer) {\n hooks.sendToNapplet(peer, {\n type: 'inc.channel.event',\n channelId,\n sender,\n ...(m.payload === undefined ? {} : { payload: m.payload }),\n } as NappletMessage);\n }\n }\n}\n\nfunction handleChannelList(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channels = [];\n const set = state.channelsByWindow.get(windowId);\n if (set) {\n for (const channelId of set) {\n const peer = peerOf(state, channelId, windowId);\n const peerDTag = peer ? sessionRegistry.getEntryByWindowId(peer)?.dTag : undefined;\n if (peerDTag) channels.push({ id: channelId, peer: peerDTag });\n }\n }\n hooks.sendToNapplet(windowId, { type: 'inc.channel.list.result', id: m.id ?? '', channels } as NappletMessage);\n}\n\nfunction handleChannelClose(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channelId = m.channelId ?? '';\n if (!peerOf(state, channelId, windowId)) return;\n teardownChannel(state, hooks, channelId);\n}\n\nfunction teardownChannel(state: IncState, hooks: RuntimeAdapter, channelId: string, destroyedWindowId?: string): void {\n const channel = state.channels.get(channelId);\n if (!channel) return;\n\n try {\n if (destroyedWindowId) {\n const survivor = peerOf(state, channelId, destroyedWindowId);\n if (survivor) {\n try {\n hooks.sendToNapplet(survivor, { type: 'inc.channel.closed', channelId, reason: 'peer destroyed' } as NappletMessage);\n } catch { /* channel teardown must still complete */ }\n }\n return;\n }\n for (const peer of [channel.peerA, channel.peerB]) {\n try {\n hooks.sendToNapplet(peer, { type: 'inc.channel.closed', channelId } as NappletMessage);\n } catch { /* channel teardown must still complete */ }\n }\n } finally {\n removeChannel(state, channelId);\n }\n}\n\nfunction removeWindowSubscriptions(state: IncState, windowId: string): void {\n for (const [topic, subscriptions] of state.subscriptions) {\n subscriptions.delete(windowId);\n if (subscriptions.size === 0) state.subscriptions.delete(topic);\n }\n}\n\nfunction removeWindowChannels(state: IncState, hooks: RuntimeAdapter, windowId: string): void {\n const channelIds = state.channelsByWindow.get(windowId);\n if (!channelIds) return;\n for (const channelId of Array.from(channelIds)) {\n teardownChannel(state, hooks, channelId, windowId);\n }\n}\n\nfunction revokeWindowChannels(state: IncState, hooks: RuntimeAdapter, windowId: string): void {\n const channelIds = state.channelsByWindow.get(windowId);\n if (!channelIds) return;\n for (const channelId of Array.from(channelIds)) {\n teardownChannel(state, hooks, channelId);\n }\n}\n","/**\n * state-handler.ts — Storage NAP request handler using persistence hooks.\n *\n * Handles napplet storage operations (get, set, remove, keys) via the\n * canonical `@napplet/nap/storage` NIP-5D envelope surface. Delegates\n * storage to StatePersistence. No localStorage, no legacy NIP-01 dispatch.\n *\n * Per-instance scope (NAP-STORAGE, napplet/naps#3): every request carries an\n * optional `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"`\n * folds a stable per-window discriminator (`@i/<windowId>:`) into the key so\n * that multiple open windows of the *same* napplet keep isolated, independently\n * persisted state. Instancing is a property of the data (per call), not the\n * napplet — there is no napplet-wide mode. The napplet never sees or names an\n * instance identifier; it sets `scope` and trusts the shell's Unique + Stable\n * guarantees. `\"shared\"` (or absent) addresses the napplet-wide namespace,\n * byte-identical to the historical behavior.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { StorageMessage } from '@napplet/nap/storage/types';\nimport type { SendToNapplet, StatePersistence } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { AclStateContainer } from './acl-state.js';\n\n/** Canonical NAP-STORAGE scope values. `scope` absent ⇔ `\"shared\"`. */\ntype StorageScope = 'shared' | 'instance';\n\n/**\n * Reserved key segment that marks the per-instance sub-namespace inside a\n * napplet's `(dTag, aggregateHash)` bucket. Server-side only — napplets never\n * see it. Used for `scope: \"instance\"` requests (NAP-STORAGE per-instance scope).\n */\nconst INSTANCE_MARKER = '@i/';\n\n/**\n * Build the per-window instance sub-namespace segment: `@i/<windowId>:`.\n *\n * The discriminator is derived from the runtime's `windowId`, which the shell\n * keeps stable across reload / workspace restore, so per-instance storage\n * persists across sessions (the spec's \"Stable\" guarantee). Two distinct\n * windows of the same napplet get distinct segments (the \"Unique\" guarantee).\n * This format is internal and never exposed to the napplet.\n */\nfunction instanceSegment(windowId: string): string {\n return `${INSTANCE_MARKER}${windowId}:`;\n}\n\n/**\n * Build the storage key for a napplet user key.\n *\n * For `scope: \"shared\"` (default) this is byte-identical to the historical key.\n * For `scope: \"instance\"` the runtime folds in the opaque per-window segment —\n * transparently, with no napplet involvement (the napplet only sets the wire\n * field; it never sees the resulting key shape).\n */\nfunction scopedKey(\n dTag: string,\n aggregateHash: string,\n userKey: string,\n instance = false,\n windowId = '',\n): string {\n const segment = instance ? instanceSegment(windowId) : '';\n return `napplet-state:${dTag}:${aggregateHash}:${segment}${userKey}`;\n}\n\n/** Build a legacy scoped key that includes pubkey (for migration reads). */\nfunction legacyScopedKey(pubkey: string, dTag: string, aggregateHash: string, userKey: string): string {\n return `napplet-state:${pubkey}:${dTag}:${aggregateHash}:${userKey}`;\n}\n\n/** Compute byte length of a UTF-8 string without TextEncoder (ES2022-safe). */\nfunction byteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Handle a NIP-5D NAP storage message from a napplet.\n * Routes to the canonical four `@napplet/nap/storage` actions:\n * - `storage.get` → `storage.get.result` `{ value: string | null }`\n * - `storage.set` → `storage.set.result` `{ ok: boolean }` (canonical only checks `error`)\n * - `storage.remove` → `storage.remove.result` `{ ok: boolean }`\n * - `storage.keys` → `storage.keys.result` `{ keys: string[] }`\n *\n * `storage.clear` is NOT in the canonical `@napplet/nap/storage` union (it was a\n * kehto unilateral extension); attempts produce a `storage.clear.result` envelope\n * with an `error` field set. Internal lifecycle cleanup still uses the\n * `cleanupNappState()` helper below — it is not napplet-reachable.\n *\n * **Per-instance scope (NAP-STORAGE / napplet/naps#3):** each request MAY carry\n * `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"` addresses a\n * per-window sub-namespace keyed by `windowId`, so multiple windows of the same\n * napplet keep isolated state; `\"shared\"` (or absent) addresses the napplet-wide\n * namespace, byte-identical to historical behavior. An unrecognized `scope` value\n * is an invalid request and produces a `.result` envelope with `error` set.\n *\n * **Deviation note (Phase 15 to decide):** Set/remove results emit both `ok`\n * (legacy compat) and an `error` field on failure. Canonical `@napplet/nap/storage`\n * only specifies the optional `error`; napplets check `!result.error` for success.\n * Emitting `ok` preserves backward compatibility with existing in-tree callers.\n * Phase 15 (v1.2 release prep) decides whether to drop `ok` pre-release.\n *\n * @param windowId - The window identifier of the requesting napplet\n * @param msg - The NappletMessage containing the storage request\n * @param sendToNapplet - Transport function to send responses\n * @param sessionRegistry - Identity registry for looking up napplet session identity\n * @param aclState - ACL state for quota checks\n * @param statePersistence - State storage backend\n *\n * @example\n * ```ts\n * import { handleStorageNap } from '@kehto/runtime';\n *\n * handleStorageNap(windowId, { type: 'storage.get', id: 'q1', key: 'draft' },\n * sendToNapplet, sessionRegistry, aclState, statePersistence);\n * ```\n */\nexport function handleStorageNap(\n windowId: string,\n msg: NappletMessage,\n sendToNapplet: SendToNapplet,\n sessionRegistry: SessionRegistry,\n aclState: AclStateContainer,\n statePersistence: StatePersistence,\n): void {\n const m = msg as StorageMessage & {\n id?: string;\n key?: string;\n value?: string;\n scope?: string;\n };\n const id = m.id ?? '';\n const action = msg.type.split('.')[1];\n\n function sendResult(payload: Record<string, unknown>): void {\n sendToNapplet(windowId, { type: `${msg.type}.result`, id, ...payload } as NappletMessage);\n }\n function sendErrorNap(error: string): void {\n sendToNapplet(windowId, { type: `${msg.type}.result`, id, error } as NappletMessage);\n }\n\n // Identity lookup via windowId (NIP-5D path)\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n if (!entry) { sendErrorNap('not registered'); return; }\n\n const { dTag, aggregateHash, pubkey } = entry;\n const prefix = `napplet-state:${dTag}:${aggregateHash}:`;\n const legacyPrefix = pubkey ? `napplet-state:${pubkey}:${dTag}:${aggregateHash}:` : '';\n\n // NAP-STORAGE per-call scope. Absent ⇔ \"shared\" (byte-identical to history).\n // An unrecognized value is an invalid request — the shell MUST return an error.\n if (m.scope !== undefined && m.scope !== 'shared' && m.scope !== 'instance') {\n sendErrorNap(`invalid scope: ${String(m.scope)} (expected \"shared\" or \"instance\")`);\n return;\n }\n const scope: StorageScope = m.scope === 'instance' ? 'instance' : 'shared';\n const isInstance = scope === 'instance';\n // The per-window sub-namespace for this request, when instance-scoped.\n const instancePrefix = `${prefix}${instanceSegment(windowId)}`;\n const keyFor = (userKey: string): string =>\n scopedKey(dTag, aggregateHash, userKey, isInstance, windowId);\n\n switch (action) {\n case 'get': {\n const key = m.key as string;\n if (!key) { sendErrorNap('missing key'); return; }\n // Instance scope addresses a fresh per-window namespace with no legacy data —\n // read only the instance key. Shared scope keeps the triple-read migration.\n if (isInstance) {\n sendResult({ value: statePersistence.get(keyFor(key)) });\n break;\n }\n const newKey = scopedKey(dTag, aggregateHash, key);\n // Triple-read for migration: new format, legacy-with-pubkey, old prefix\n let result = statePersistence.get(newKey);\n if (result === null && pubkey) {\n result = statePersistence.get(legacyScopedKey(pubkey, dTag, aggregateHash, key));\n }\n if (result === null && pubkey) {\n result = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);\n }\n // Canonical @napplet/nap/storage: `value: string | null` — null ⇔ missing.\n sendResult({ value: result });\n break;\n }\n case 'set': {\n const key = m.key as string;\n const value = (m.value as string) ?? '';\n if (!key) { sendErrorNap('missing key'); return; }\n const quota = aclState.getStateQuota(pubkey ?? '', dTag, aggregateHash);\n const sk = keyFor(key);\n const newWriteBytes = byteLength(sk + value);\n // Quota is per napplet identity: `prefix` spans both the shared namespace and\n // every per-instance sub-key, so instance writes draw from the same budget.\n const existingBytes = statePersistence.calculateBytes(prefix, key);\n if (existingBytes + newWriteBytes > quota) {\n sendErrorNap(`quota exceeded: napplet state limit is ${quota} bytes`);\n return;\n }\n const success = statePersistence.set(sk, value);\n sendResult({ ok: success });\n break;\n }\n case 'remove': {\n const key = m.key as string;\n if (!key) { sendErrorNap('missing key'); return; }\n statePersistence.remove(keyFor(key));\n // legacyPrefix exists only while `pubkey` is non-empty (legacy AUTH sessions).\n // Suppress \"unused binding\" warnings: we intentionally retain the computation\n // so future migration lands at call sites, not here.\n void legacyPrefix;\n sendResult({ ok: true });\n break;\n }\n case 'clear': {\n // storage.clear was a kehto unilateral extension; it is NOT in the canonical\n // @napplet/nap/storage union. Napplets hitting this branch receive a\n // storage.clear.result envelope with the error field set. Internal lifecycle\n // cleanup uses cleanupNappState() below (not napplet-reachable).\n sendErrorNap('storage.clear is not in @napplet/nap/storage; action not supported');\n break;\n }\n case 'keys': {\n // Instance scope sees only this window's sub-namespace.\n if (isInstance) {\n const instKeys = statePersistence.keys(instancePrefix);\n const instanceKeySet = new Set<string>();\n for (const k of instKeys) {\n instanceKeySet.add(k.startsWith(instancePrefix) ? k.slice(instancePrefix.length) : k);\n }\n sendResult({ keys: Array.from(instanceKeySet) });\n break;\n }\n const newKeys = statePersistence.keys(prefix);\n const legacyKeys = legacyPrefix ? statePersistence.keys(legacyPrefix) : [];\n const userKeySet = new Set<string>();\n // Shared scope excludes per-instance sub-keys (reserved `@i/` marker) so an\n // instance-scoped napplet's per-window data never leaks into a shared listing.\n for (const k of newKeys) {\n if (!k.startsWith(prefix)) { userKeySet.add(k); continue; }\n const userKey = k.slice(prefix.length);\n if (userKey.startsWith(INSTANCE_MARKER)) continue;\n userKeySet.add(userKey);\n }\n for (const k of legacyKeys) userKeySet.add(k.startsWith(legacyPrefix) ? k.slice(legacyPrefix.length) : k);\n sendResult({ keys: Array.from(userKeySet) });\n break;\n }\n default:\n sendErrorNap(`unknown storage action: ${action}`);\n break;\n }\n}\n\n/**\n * Remove all state entries for a napplet identity.\n * Clears both new-format and legacy-format keys for completeness.\n * Used during napplet cleanup when a window is closed.\n *\n * @param pubkey - The napplet's pubkey (needed for legacy key cleanup)\n * @param dTag - The napplet's dTag\n * @param aggregateHash - The napplet's build hash\n * @param statePersistence - State storage backend\n *\n * @example\n * ```ts\n * import { cleanupNappState } from '@kehto/runtime';\n *\n * cleanupNappState(pubkey, dTag, aggregateHash, statePersistence);\n * ```\n */\nexport function cleanupNappState(\n pubkey: string,\n dTag: string,\n aggregateHash: string,\n statePersistence: StatePersistence,\n): void {\n // Clear new format\n const prefix = `napplet-state:${dTag}:${aggregateHash}:`;\n statePersistence.clear(prefix);\n // Clear legacy format (includes pubkey)\n const legacyPrefix = `napplet-state:${pubkey}:${dTag}:${aggregateHash}:`;\n statePersistence.clear(legacyPrefix);\n}\n","import type { NappletMessage } from '@napplet/core';\nimport {\n createCanonicalDomainResult,\n createIntentServiceUnavailableResult,\n} from './domain-results.js';\n\nimport type { AclStateContainer } from './acl-state.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport { handleStorageNap } from './state-handler.js';\nimport type { RuntimeAdapter, ServiceRegistry } from './types.js';\n\ntype DomainHandler = (windowId: string, msg: NappletMessage) => void;\n\ntype RuntimeDomainContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n};\n\nexport type RuntimeDomainHandlers = {\n storage: DomainHandler;\n media: DomainHandler;\n keys: DomainHandler;\n notify: DomainHandler;\n theme: DomainHandler;\n config: DomainHandler;\n resource: DomainHandler;\n cvm: DomainHandler;\n outbox: DomainHandler;\n upload: DomainHandler;\n intent: DomainHandler;\n link: DomainHandler;\n common: DomainHandler;\n lists: DomainHandler;\n serial: DomainHandler;\n ble: DomainHandler;\n webrtc: DomainHandler;\n dm: DomainHandler;\n fs: DomainHandler;\n};\n\nexport function createRuntimeDomainHandlers(context: RuntimeDomainContext): RuntimeDomainHandlers {\n return {\n storage: (windowId, msg) => handleStorageMessage(context, windowId, msg),\n media: (windowId, msg) => handleMediaMessage(context, windowId, msg),\n keys: (windowId, msg) => handleKeysMessage(context, windowId, msg),\n notify: (windowId, msg) => handleNotifyMessage(context, windowId, msg),\n theme: (windowId, msg) => handleThemeMessage(context, windowId, msg),\n config: (windowId, msg) => handleServiceOnlyMessage(context, 'config', windowId, msg),\n resource: (windowId, msg) => handleServiceOnlyMessage(context, 'resource', windowId, msg),\n cvm: (windowId, msg) => handleServiceOnlyMessage(context, 'cvm', windowId, msg),\n outbox: (windowId, msg) => handleServiceOnlyMessage(context, 'outbox', windowId, msg),\n upload: (windowId, msg) => handleServiceOnlyMessage(context, 'upload', windowId, msg),\n intent: (windowId, msg) => handleServiceOnlyMessage(context, 'intent', windowId, msg),\n link: (windowId, msg) => handleServiceOnlyMessage(context, 'link', windowId, msg),\n common: (windowId, msg) => handleServiceOnlyMessage(context, 'common', windowId, msg),\n lists: (windowId, msg) => handleServiceOnlyMessage(context, 'lists', windowId, msg),\n serial: (windowId, msg) => handleServiceOnlyMessage(context, 'serial', windowId, msg),\n ble: (windowId, msg) => handleServiceOnlyMessage(context, 'ble', windowId, msg),\n webrtc: (windowId, msg) => handleServiceOnlyMessage(context, 'webrtc', windowId, msg),\n dm: (windowId, msg) => handleServiceOnlyMessage(context, 'dm', windowId, msg),\n fs: (windowId, msg) => handleServiceOnlyMessage(context, 'fs', windowId, msg),\n };\n}\n\nfunction handleStorageMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { aclState, hooks, sessionRegistry } = context;\n handleStorageNap(windowId, msg, hooks.sendToNapplet, sessionRegistry, aclState, hooks.statePersistence);\n}\n\nfunction handleMediaMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const mediaService = serviceRegistry['media'];\n if (mediaService) {\n mediaService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n if (msg.type === 'media.session.create') {\n const m = msg as NappletMessage & { id?: string; owner?: string; sessionId?: string };\n if (m.owner !== 'napplet' && m.owner !== 'shell') {\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n error: 'missing owner',\n } as NappletMessage);\n return;\n }\n if (m.owner === 'shell') {\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n owner: 'shell',\n error: 'unsupported owner mode',\n } as NappletMessage);\n return;\n }\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n sessionId: m.sessionId ?? '',\n owner: m.owner,\n } as NappletMessage);\n }\n}\n\nfunction handleKeysMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry, sessionRegistry } = context;\n if (msg.type === 'keys.forward') {\n if (!sessionRegistry.getEntryByWindowId(windowId)) return;\n forwardHotkey(hooks, msg);\n return;\n }\n const keysService = serviceRegistry['keys'];\n if (keysService) {\n keysService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n if (msg.type === 'keys.registerAction') sendRegisterActionResult(hooks, windowId, msg);\n}\n\nfunction forwardHotkey(hooks: RuntimeAdapter, msg: NappletMessage): void {\n const m = msg as NappletMessage & {\n key?: string; code?: string;\n ctrl?: boolean; alt?: boolean; shift?: boolean; meta?: boolean;\n };\n hooks.hotkeys.executeHotkeyFromForward({\n key: m.key ?? '',\n code: m.code ?? '',\n ctrlKey: !!m.ctrl,\n altKey: !!m.alt,\n shiftKey: !!m.shift,\n metaKey: !!m.meta,\n });\n}\n\nfunction sendRegisterActionResult(hooks: RuntimeAdapter, windowId: string, msg: NappletMessage): void {\n const m = msg as NappletMessage & {\n id?: string; action?: { id: string; defaultKey?: string };\n };\n hooks.sendToNapplet(windowId, {\n type: 'keys.registerAction.result',\n id: m.id ?? '',\n actionId: m.action?.id ?? '',\n } as NappletMessage);\n}\n\nfunction handleNotifyMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const notifyService = serviceRegistry['notify'];\n if (notifyService) {\n notifyService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n if (msg.type === 'notify.send') {\n const m = msg as NappletMessage & { id?: string };\n hooks.sendToNapplet(windowId, { type: 'notify.send.result', id: m.id ?? '', notificationId: `shell-${Date.now()}` } as NappletMessage);\n } else if (msg.type === 'notify.permission.request') {\n const m = msg as NappletMessage & { id?: string };\n hooks.sendToNapplet(windowId, { type: 'notify.permission.result', id: m.id ?? '', granted: true } as NappletMessage);\n }\n}\n\nfunction handleThemeMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const themeService = serviceRegistry['theme'];\n if (themeService) {\n themeService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n const fallback = createCanonicalDomainResult(msg);\n if (fallback) hooks.sendToNapplet(windowId, fallback);\n}\n\nfunction handleServiceOnlyMessage(\n context: RuntimeDomainContext,\n name: 'config' | 'resource' | 'cvm' | 'outbox' | 'upload' | 'intent' | 'link' | 'common' | 'lists' | 'serial' | 'ble' | 'webrtc' | 'dm' | 'fs',\n windowId: string,\n msg: NappletMessage,\n): void {\n if (\n name === 'intent'\n && msg.type !== 'intent.invoke'\n && msg.type !== 'intent.available'\n && msg.type !== 'intent.handlers'\n ) {\n return;\n }\n const service = context.serviceRegistry[name];\n if (!service) {\n if (name === 'intent') {\n const result = createIntentServiceUnavailableResult(msg);\n if (result) context.hooks.sendToNapplet(windowId, result);\n }\n return;\n }\n service.handleMessage(windowId, msg, (resp: NappletMessage) => context.hooks.sendToNapplet(windowId, resp));\n}\n","// @kehto/runtime — Browser-agnostic protocol engine for the napplet protocol.\n\nexport type {\n RuntimeAdapter,\n SendToNapplet,\n RelayPoolAdapter,\n RelaySubscriptionHandle,\n CacheAdapter,\n AuthAdapter,\n Signer,\n ConfigAdapter,\n HotkeyAdapter,\n AclPersistence,\n ManifestPersistence,\n StatePersistence,\n CryptoAdapter,\n WindowManagerAdapter,\n RelayConfigAdapter,\n DmAdapter,\n ConsentRequest,\n ConsentHandler,\n SessionEntry,\n NappKeyEntry, // @deprecated — use SessionEntry\n PendingUpdate,\n PendingUpdateNotifier,\n ManifestCacheEntry,\n AclEntryExternal,\n AclCheckEvent,\n FirewallPersistence,\n FirewallEvent,\n ServiceHandler,\n ServiceRuntimeContext,\n ServiceRegistry,\n CompatibilityReport,\n ServiceInfo,\n ShellSecretPersistence,\n GuidPersistence,\n HashVerifierAdapter,\n VerificationCacheEntry,\n RuntimeConfigOverrides,\n NappletMessage,\n} from './types.js';\n\nexport { createEnforceGate, createNapEnforceGate, resolveCapabilitiesNap, formatDenialReason } from './enforce.js';\nexport type { EnforceResult, EnforceConfig, NapEnforceConfig, IdentityResolver, AclChecker, NapMessage } from './enforce.js';\n\nexport { createSessionRegistry, createNappKeyRegistry } from './session-registry.js';\nexport type { SessionRegistry, NappKeyRegistry } from './session-registry.js';\n\nexport { createAclState } from './acl-state.js';\nexport type { AclMutation, AclMutationObserver, AclStateContainer } from './acl-state.js';\n\nexport { createFirewallState } from './firewall-state.js';\nexport type { FirewallStateContainer } from './firewall-state.js';\n\nexport { createManifestCache } from './manifest-cache.js';\nexport type { ManifestCache } from './manifest-cache.js';\n\nexport { createReplayDetector } from './replay.js';\nexport type { ReplayDetector } from './replay.js';\n\nexport { createEventBuffer, matchesFilter, matchesAnyFilter, RING_BUFFER_SIZE } from './event-buffer.js';\nexport type { EventBuffer, SubscriptionEntry } from './event-buffer.js';\n\nexport {\n createRelayEventResult,\n createRelayEventResultWithHints,\n relayEventResultFromCarrier,\n} from './relay-result.js';\nexport type { RelayEventCarrier, RelayEventResult, RelayEventSidecar } from './relay-result.js';\n\nexport { createRuntime } from './runtime.js';\nexport type { Runtime } from './runtime.js';\n\nexport { handleStorageNap, cleanupNappState } from './state-handler.js';\n\nexport { routeServiceMessage, notifyServiceWindowDestroyed } from './service-dispatch.js';\n\nexport type { Capability } from '@kehto/acl/capabilities';\nexport { ALL_CAPABILITIES } from '@kehto/acl/capabilities';\nexport type { ServiceDescriptor } from './types.js';\n"],"mappings":"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","names":["createState","serialize","deserialize","onBackendEose","makeCollector"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kehto/runtime",
3
- "version": "0.20.1",
3
+ "version": "0.22.0",
4
4
  "description": "Browser-agnostic protocol engine for the napplet protocol — message dispatch, ACL enforcement, AUTH handshake",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "@noble/hashes": "^2.0.0",
24
24
  "@noble/curves": "^2.0.0",
25
- "@kehto/acl": "^0.17.0",
25
+ "@kehto/acl": "^0.18.0",
26
26
  "@kehto/firewall": "^0.5.0"
27
27
  },
28
28
  "peerDependencies": {