@kehto/runtime 0.7.0 → 0.8.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/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { resolveCapabilitiesNub } from "@kehto/acl";
4
4
  var CLASS_CAPABILITY_ALLOWLIST = Object.freeze({
5
5
  "class-1": new Set(ALL_CAPABILITIES),
6
6
  "class-2": new Set(ALL_CAPABILITIES.filter(
7
- (c) => c !== "relay:write" && c !== "identity:decrypt" && c !== "outbox:write"
7
+ (c) => c !== "relay:write" && c !== "outbox:write"
8
8
  ))
9
9
  });
10
10
  function createEnforceGate(config) {
@@ -154,7 +154,6 @@ var CAP_NOTIFY_CHANNEL = 1 << 12;
154
154
  var CAP_THEME_READ = 1 << 13;
155
155
  var CAP_CONFIG_READ = 1 << 14;
156
156
  var CAP_RESOURCE_FETCH = 1 << 15;
157
- var CAP_IDENTITY_DECRYPT = 1 << 16;
158
157
  var CAP_CVM_CALL = 1 << 17;
159
158
  var CAP_OUTBOX_READ = 1 << 18;
160
159
  var CAP_OUTBOX_WRITE = 1 << 19;
@@ -176,7 +175,6 @@ var CAP_MAP = {
176
175
  "theme:read": CAP_THEME_READ,
177
176
  "config:read": CAP_CONFIG_READ,
178
177
  "resource:fetch": CAP_RESOURCE_FETCH,
179
- "identity:decrypt": CAP_IDENTITY_DECRYPT,
180
178
  "cvm:call": CAP_CVM_CALL,
181
179
  "outbox:read": CAP_OUTBOX_READ,
182
180
  "outbox:write": CAP_OUTBOX_WRITE,
@@ -1244,9 +1242,8 @@ function createMessageHandler(hooks, enforceNub, dispatchNubEnvelope) {
1244
1242
  const result = enforceNub(windowId, caps.senderCap, envelope);
1245
1243
  if (!result.allowed) {
1246
1244
  const id = envelope.id ?? "";
1247
- const isIdentityDecrypt = envelope.type === "identity.decrypt";
1248
1245
  const isStorageEnvelope = envelope.type.startsWith("storage.");
1249
- const error = isIdentityDecrypt ? result.reason === "class-forbidden" ? "class-forbidden" : "policy-denied" : formatDenialReason(result.capability);
1246
+ const error = formatDenialReason(result.capability);
1250
1247
  const type = isStorageEnvelope ? `${envelope.type}.result` : `${envelope.type}.error`;
1251
1248
  hooks.sendToNapplet(windowId, { type, id, error });
1252
1249
  return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/enforce.ts","../src/session-registry.ts","../src/acl-state.ts","../src/manifest-cache.ts","../src/replay.ts","../src/event-buffer.ts","../src/runtime.ts","../src/service-dispatch.ts","../src/relay-handler.ts","../src/identity-handler.ts","../src/ifc-handler.ts","../src/state-handler.ts","../src/domain-handlers.ts","../src/index.ts"],"sourcesContent":["\nimport { ALL_CAPABILITIES, type Capability } from '@kehto/acl/capabilities';\nimport type { NubMessage } from '@kehto/acl';\nimport type { AclCheckEvent, NappletClass } from './types.js';\n\n// Re-export NUB capability resolution for consumers who import through enforce.ts\nexport { resolveCapabilitiesNub } from '@kehto/acl';\nexport type { NubMessage } from '@kehto/acl';\n\n/**\n * Hardcoded per-class capability allowlist. The permissive default\n * (class === null) bypasses this map entirely - see enforceNub. Additional\n * classes are added when NUB specs publish new class tokens.\n *\n * - 'class-1': all 15 v1.2-era capabilities (permissive full surface).\n * - 'class-2': all capabilities EXCEPT relay:write, identity:decrypt, and\n * outbox:write - relay:write is the sample restrictive class Plan 38-03\n * exercises, identity:decrypt is the NUB-IDENTITY class-1-only decrypt\n * surface, and outbox:write is the shell-signed publish op (mirrors\n * relay:write — a read-only class can route/query but not publish).\n *\n * Unknown class tokens fall through enforceNub's \"treat as maximally\n * restrictive\" branch (deny all) - defensive failsafe, not policy.\n */\nconst CLASS_CAPABILITY_ALLOWLIST: Readonly<Record<string, ReadonlySet<Capability>>> = Object.freeze({\n 'class-1': new Set<Capability>(ALL_CAPABILITIES),\n 'class-2': new Set<Capability>(ALL_CAPABILITIES.filter(\n (c) => c !== 'relay:write' && c !== 'identity:decrypt' && c !== 'outbox:write',\n )),\n});\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 (v1.7 CLASS-03 / D7). Always set on the\n * return path. Distinct from AclCheckEvent.reason (which is optional for\n * backwards compat on the audit surface).\n */\n reason: 'allowed' | 'capability-missing' | 'class-forbidden';\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 NUB 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, class). Returns\n * class posture inline (v1.7 CLASS-03) so the NUB gate can pre-filter class-forbidden capabilities\n * before consulting the ACL check. null class = permissive default (D2).\n * @param onAclCheck - Optional audit callback, called on every enforceNub() check\n */\nexport interface NubEnforceConfig {\n checkAcl: AclChecker;\n resolveIdentityByWindowId: (windowId: string) => { dTag: string; aggregateHash: string; class: NappletClass } | undefined;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate for NIP-5D NUB 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 - NUB enforcement configuration\n * @returns An enforceNub function that resolves identity by windowId\n *\n * @example\n * ```ts\n * const gate = createNubEnforceGate({\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 createNubEnforceGate(config: NubEnforceConfig): (windowId: string, capability: Capability, message?: NubMessage) => EnforceResult {\n const { checkAcl, resolveIdentityByWindowId, onAclCheck } = config;\n\n return function enforceNub(windowId: string, capability: Capability, message?: NubMessage): EnforceResult {\n const entry = resolveIdentityByWindowId(windowId);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n const nappletClass: NappletClass = entry?.class ?? null;\n\n if (nappletClass !== null) {\n const allowlist = CLASS_CAPABILITY_ALLOWLIST[nappletClass];\n // Unknown class token -> treat as maximally restrictive (deny all).\n if (!allowlist || !allowlist.has(capability)) {\n const identity = { pubkey: '', dTag, hash: aggregateHash };\n if (onAclCheck) {\n onAclCheck({\n identity,\n capability,\n decision: 'deny',\n message,\n reason: 'class-forbidden',\n });\n }\n return { allowed: false, capability, reason: 'class-forbidden' };\n }\n }\n\n // Capability check (unchanged from pre-v1.7 except for propagating reason).\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 /** 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 isRegistered(windowId: string): boolean {\n return byWindowId.has(windowId);\n },\n\n getAllEntries(): SessionEntry[] {\n return Array.from(byPubkey.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 NUB-CONFIG)\nconst CAP_RESOURCE_FETCH = 1 << 15; // 32768 (v1.7 Phase 40 NUB-RESOURCE)\nconst CAP_IDENTITY_DECRYPT = 1 << 16; // 65536 (v1.8 Phase 45 NUB-IDENTITY decrypt)\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)\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 'identity:decrypt': CAP_IDENTITY_DECRYPT,\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};\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/**\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 * @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): 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 },\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 },\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 * 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.\n * Returns null if event is valid, or a string reason if it should be rejected.\n */\n check(event: NostrEvent): string | null;\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 const seenEventIds = new Map<string, number>();\n\n return {\n check(event: NostrEvent): string | null {\n const replayWindow = getReplayWindow?.() ?? REPLAY_WINDOW_SECONDS;\n const now = Math.floor(Date.now() / 1000);\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, now);\n for (const [id, timestamp] of seenEventIds) {\n if (now - timestamp > replayWindow) seenEventIds.delete(id);\n }\n return null;\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","\nimport { createDispatch, type NappletMessage, type NostrEvent, type NubHandler } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\n\nimport type {\n RuntimeAdapter, ConsentHandler,\n ServiceHandler, ServiceRegistry, ServiceInfo,\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 { 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, createNubEnforceGate, resolveCapabilitiesNub, formatDenialReason } from './enforce.js';\nimport { createRelayHandler } from './relay-handler.js';\nimport { createIdentityHandler } from './identity-handler.js';\nimport { createIfcRuntime, type IfcRuntime } from './ifc-handler.js';\nimport { createRuntimeDomainHandlers, type RuntimeDomainHandlers } from './domain-handlers.js';\n\n/**\n * The napplet protocol engine — handles NIP-5D NUB 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 manifest cache. */\n readonly manifestCache: ManifestCache;\n}\n\ntype RuntimeNubHandlers = RuntimeDomainHandlers & {\n relay: (windowId: string, msg: NappletMessage) => void;\n identity: (windowId: string, msg: NappletMessage) => void;\n ifc: (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 ifcRuntime: IfcRuntime;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n manifestCache: ManifestCache;\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 createNubEnvelopeDispatcher(handlers: RuntimeNubHandlers): (windowId: string, envelope: NappletMessage) => void {\n let currentWindowId: string | null = null;\n const nubDispatch = createDispatch();\n const adapt = (handler: (windowId: string, msg: NappletMessage) => void): NubHandler => (msg) => {\n if (currentWindowId !== null) handler(currentWindowId, msg);\n };\n\n nubDispatch.registerNub('relay', adapt(handlers.relay));\n nubDispatch.registerNub('identity', adapt(handlers.identity));\n nubDispatch.registerNub('keys', adapt(handlers.keys));\n nubDispatch.registerNub('media', adapt(handlers.media));\n nubDispatch.registerNub('notify', adapt(handlers.notify));\n nubDispatch.registerNub('storage', adapt(handlers.storage));\n nubDispatch.registerNub('ifc', adapt(handlers.ifc));\n nubDispatch.registerNub('theme', adapt(handlers.theme));\n nubDispatch.registerNub('config', adapt(handlers.config));\n nubDispatch.registerNub('resource', adapt(handlers.resource));\n nubDispatch.registerNub('cvm', adapt(handlers.cvm));\n nubDispatch.registerNub('outbox', adapt(handlers.outbox));\n nubDispatch.registerNub('upload', adapt(handlers.upload));\n\n return (windowId, envelope) => {\n currentWindowId = windowId;\n try {\n nubDispatch.dispatch(envelope);\n } finally {\n currentWindowId = null;\n }\n };\n}\n\nfunction createMessageHandler(\n hooks: RuntimeAdapter,\n enforceNub: ReturnType<typeof createNubEnforceGate>,\n dispatchNubEnvelope: (windowId: string, envelope: NappletMessage) => void,\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\n const caps = resolveCapabilitiesNub(envelope);\n if (caps.senderCap) {\n const result = enforceNub(windowId, caps.senderCap as Capability, envelope);\n if (!result.allowed) {\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const isIdentityDecrypt = envelope.type === 'identity.decrypt';\n const isStorageEnvelope = envelope.type.startsWith('storage.');\n const error = isIdentityDecrypt\n ? result.reason === 'class-forbidden' ? 'class-forbidden' : 'policy-denied'\n : formatDenialReason(result.capability);\n const type = isStorageEnvelope ? `${envelope.type}.result` : `${envelope.type}.error`;\n hooks.sendToNapplet(windowId, { type, id, error } as NappletMessage);\n return;\n }\n }\n\n dispatchNubEnvelope(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, eventBuffer, hooks, ifcRuntime, manifestCache, registeredServices,\n replayDetector, serviceRegistry, sessionRegistry, subscriptions,\n } = context;\n const undeclaredServiceConsents = new Set<string>();\n let consentHandler: ConsentHandler | null = null;\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 manifestCache.persist();\n aclState.persist();\n replayDetector.clear();\n subscriptions.clear();\n ifcRuntime.clear();\n eventBuffer.clear();\n registeredServices.clear();\n undeclaredServiceConsents.clear();\n },\n registerConsentHandler(handler: ConsentHandler): void {\n consentHandler = handler;\n void consentHandler;\n },\n registerService(name: string, handler: ServiceHandler): void {\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 },\n unregisterService(name: string): void {\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 ifcRuntime.destroyWindow(windowId);\n notifyServiceWindowDestroyed(windowId, serviceRegistry);\n },\n get sessionRegistry() { return sessionRegistry; },\n get aclState() { return aclState; },\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 const aclState = createAclState(hooks.aclPersistence);\n const manifestCache = createManifestCache(hooks.manifestPersistence);\n const replayDetector = createReplayDetector(\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().replayWindowSeconds\n : undefined,\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 enforceNub = createNubEnforceGate({\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, class: entry.class }\n : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\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 manifestCache.load();\n\n const ifcRuntime = createIfcRuntime(hooks, sessionRegistry);\n const domainHandlers = createRuntimeDomainHandlers({ hooks, serviceRegistry, sessionRegistry, aclState });\n const dispatchNubEnvelope = createNubEnvelopeDispatcher({\n relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),\n identity: createIdentityHandler({ hooks, serviceRegistry }),\n ifc: ifcRuntime.handleMessage,\n ...domainHandlers,\n });\n const handleMessage = createMessageHandler(hooks, enforceNub, dispatchNubEnvelope);\n\n return createRuntimeInstance({\n hooks,\n serviceRegistry,\n registeredServices,\n replayDetector,\n subscriptions,\n eventBuffer,\n ifcRuntime,\n sessionRegistry,\n aclState,\n manifestCache,\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 * NUB-domain services are routed by the domain prefix of message.type\n * (e.g., 'signer.signEvent' -> 'signer' service).\n *\n * IFC-routed services receive ifc.emit messages and are routed by the\n * topic prefix before ':' (e.g., topic 'audio:play' -> 'audio' service).\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 // NUB-domain services: signer.*, relay.*, storage.* route by type prefix\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 // IFC-routed services: audio and notifications receive ifc.emit with topic prefix\n const ifcMessage = message as NappletMessage & { topic?: unknown };\n if (message.type === 'ifc.emit' && typeof ifcMessage.topic === 'string') {\n const prefix = ifcMessage.topic.split(':')[0];\n const ifcHandler = services[prefix];\n if (ifcHandler) {\n ifcHandler.handleMessage(windowId, message, (msg) => sendToNapplet(windowId, msg));\n return true;\n }\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 { NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';\nimport type { RelayMessage } from '@napplet/nub/relay/types';\n\nimport { matchesAnyFilter, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport type { ReplayDetector } from './replay.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?: NostrEvent;\n id?: 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, msg, 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 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): void {\n if (seenIds.has(event.id)) return;\n seenIds.add(event.id);\n if (subscriptions.has(subKey)) {\n hooks.sendToNapplet(windowId, { type: 'relay.event', subId, event } as NappletMessage);\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, resp);\n });\n if (cacheService) {\n cacheService.handleMessage(windowId, msg, (resp: NappletMessage) => {\n if (!subscriptions.has(subKey)) return;\n hooks.sendToNapplet(windowId, resp);\n });\n }\n return;\n }\n\n deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver);\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) => void,\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 = 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 deliver(item as NostrEvent);\n if (cache?.isAvailable() && !isShellKind) {\n try { cache.store(item as NostrEvent); } 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 msg: NappletMessage,\n m: RuntimeRelayMessage,\n): void {\n const { eventBuffer, hooks, replayDetector } = context;\n const event = m.event;\n const id = m.id ?? '';\n if (!event || typeof event !== 'object') {\n hooks.sendToNapplet(windowId, { type: 'relay.publish.error', id, error: 'invalid event' } as NappletMessage);\n return;\n }\n\n const replayResult = replayDetector.check(event);\n if (replayResult !== null) {\n hooks.sendToNapplet(windowId, { type: 'relay.publish.result', id, accepted: false, message: replayResult } as NappletMessage);\n return;\n }\n\n const relayService = relayServiceFrom(context);\n if (relayService) {\n relayService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n } else if (hooks.relayPool?.isAvailable()) {\n hooks.relayPool.publish(event);\n hooks.sendToNapplet(windowId, { type: 'relay.publish.result', id, accepted: true } as NappletMessage);\n } else {\n hooks.sendToNapplet(windowId, { type: 'relay.publish.result', id, accepted: false, message: 'no relay pool available' } as NappletMessage);\n }\n\n eventBuffer.bufferAndDeliver(event, windowId);\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: NostrEvent,\n replyPe: (ok: boolean, extra?: Record<string, unknown>) => void,\n): void {\n const { eventBuffer, 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 as object), content: ciphertext } as NostrEvent;\n const signed = await peSigner.signEvent?.(eventWithCiphertext);\n if (!signed) { replyPe(false, { error: 'signEvent returned null' }); return; }\n\n publishSignedEncrypted(context, windowId, id, signed, replyPe);\n try { eventBuffer.bufferAndDeliver(signed, windowId); } catch { return; }\n } catch (err) {\n replyPe(false, { error: (err as Error)?.message ?? 'encryption failed' });\n }\n })();\n}\n\nfunction publishSignedEncrypted(\n context: RelayHandlerContext,\n windowId: string,\n id: string,\n signed: NostrEvent,\n replyPe: (ok: boolean, extra?: Record<string, unknown>) => void,\n): void {\n const { hooks } = context;\n const relayService = relayServiceFrom(context);\n if (!relayService) {\n if (hooks.relayPool?.isAvailable()) {\n hooks.relayPool.publish(signed);\n replyPe(true, { event: signed, eventId: signed.id });\n } else {\n replyPe(false, { error: 'no relay pool available' });\n }\n return;\n }\n\n const publishMsg = { type: 'relay.publish', id, event: signed } as NappletMessage;\n let replied = false;\n relayService.handleMessage(windowId, publishMsg, (resp: NappletMessage) => {\n if (replied) return;\n const r = resp as NappletMessage & {\n ok?: boolean; accepted?: boolean; eventId?: string;\n message?: string; error?: string;\n };\n if (typeof r.type !== 'string' || !r.type.startsWith('relay.publish')) return;\n const okVal = r.ok ?? r.accepted ?? false;\n replied = true;\n const publishResult = { event: signed, eventId: signed.id } as {\n event: NostrEvent;\n eventId: string;\n error?: string;\n };\n if (!okVal) publishResult.error = r.error ?? r.message ?? 'publish failed';\n replyPe(okVal, publishResult);\n });\n\n if (!replied) {\n replied = true;\n replyPe(true, { event: signed, eventId: signed.id });\n }\n}\n\nfunction handleRelayQuery(\n context: RelayHandlerContext,\n windowId: string,\n m: RuntimeRelayMessage,\n): void {\n const id = m.id ?? '';\n const filters = m.filters ?? [];\n let count = 0;\n for (const event of context.eventBuffer.getBufferedEvents()) {\n if (matchesAnyFilter(event, filters)) count++;\n }\n context.hooks.sendToNapplet(windowId, { type: 'relay.query.result', id, count } as NappletMessage);\n}\n","import type { NappletMessage } from '@napplet/core';\n\nimport type { RuntimeAdapter, ServiceRegistry } from './types.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 id = (msg as NappletMessage & { id?: string }).id ?? '';\n const action = msg.type.slice('identity.'.length);\n const signer = hooks.auth.getSigner();\n const sendError = (error: string) => {\n hooks.sendToNapplet(windowId, { type: `${msg.type}.error`, id, error } as NappletMessage);\n };\n const sendResult = (payload: Record<string, unknown>) => {\n hooks.sendToNapplet(windowId, { type: `${msg.type}.result`, id, ...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((err: unknown) => sendError((err as Error)?.message ?? 'getPublicKey failed'));\n return;\n case 'getRelays':\n if (!signer) { sendError('no signer configured'); return; }\n Promise.resolve(signer.getRelays?.() ?? {})\n .then((relays) => sendResult({ relays }))\n .catch((err: unknown) => sendError((err as Error)?.message ?? 'getRelays failed'));\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:\n sendError(`Unknown identity action: ${action}`);\n }\n };\n}\n","import type { NappletMessage } from '@napplet/core';\nimport type { IfcMessage } from '@napplet/nub/ifc/types';\n\nimport type { SessionRegistry } from './session-registry.js';\nimport type { RuntimeAdapter } from './types.js';\n\ninterface IfcChannel {\n channelId: string;\n peerA: string;\n peerB: string;\n}\n\ntype RuntimeIfcMessage = IfcMessage & {\n id?: string;\n topic?: string;\n payload?: unknown;\n target?: string;\n channelId?: string;\n};\n\ntype IfcState = {\n subscriptions: Map<string, Set<string>>;\n channels: Map<string, IfcChannel>;\n channelsByWindow: Map<string, Set<string>>;\n};\n\nexport interface IfcRuntime {\n handleMessage(windowId: string, msg: NappletMessage): void;\n destroyWindow(windowId: string): void;\n clear(): void;\n}\n\nexport function createIfcRuntime(hooks: RuntimeAdapter, sessionRegistry: SessionRegistry): IfcRuntime {\n const state: IfcState = {\n subscriptions: new Map(),\n channels: new Map(),\n channelsByWindow: new Map(),\n };\n\n return {\n handleMessage(windowId: string, msg: NappletMessage): void {\n handleIfcMessage(state, hooks, sessionRegistry, windowId, msg);\n },\n destroyWindow(windowId: string): void {\n removeWindowChannels(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: IfcState, 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: IfcState, 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: IfcState, 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 if (sessionRegistry.getEntryByWindowId(target)) return target;\n const entries = sessionRegistry.getAllEntries();\n const byPubkey = entries.find((entry) => entry.pubkey === target);\n return byPubkey?.windowId ?? null;\n}\n\nfunction handleIfcMessage(\n state: IfcState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n msg: NappletMessage,\n): void {\n const m = msg as RuntimeIfcMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'emit': handleEmit(state, hooks, 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, windowId, m); return;\n case 'channel.emit': handleChannelEmit(state, hooks, windowId, m); return;\n case 'channel.broadcast': handleChannelBroadcast(state, hooks, windowId, m); return;\n case 'channel.list': handleChannelList(state, hooks, windowId, m); return;\n case 'channel.close': handleChannelClose(state, hooks, windowId, m); return;\n default: return;\n }\n}\n\nfunction handleEmit(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const topic = m.topic ?? '';\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, { type: 'ifc.event', topic, payload: m.payload, sender: windowId } as NappletMessage);\n }\n }\n}\n\nfunction handleSubscribe(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const id = m.id ?? '';\n const topic = m.topic ?? '';\n if (!topic) {\n hooks.sendToNapplet(windowId, { type: 'ifc.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: 'ifc.subscribe.result', id } as NappletMessage);\n}\n\nfunction handleUnsubscribe(state: IfcState, windowId: string, m: RuntimeIfcMessage): 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: IfcState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIfcMessage,\n): void {\n const id = m.id ?? '';\n const peerWindow = resolveTarget(sessionRegistry, m.target ?? '');\n if (!peerWindow) {\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.open.result', id, error: 'target not found' } as NappletMessage);\n return;\n }\n const channelId = hooks.crypto.randomUUID().replace(/-/g, '').slice(0, 32);\n addChannel(state, channelId, windowId, peerWindow);\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.open.result', id, channelId, peer: peerWindow } as NappletMessage);\n}\n\nfunction handleChannelEmit(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const peer = peerOf(state, m.channelId ?? '', windowId);\n if (peer) hooks.sendToNapplet(peer, { type: 'ifc.channel.event', channelId: m.channelId ?? '', sender: windowId, payload: m.payload } as NappletMessage);\n}\n\nfunction handleChannelBroadcast(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const channels = state.channelsByWindow.get(windowId);\n if (!channels) return;\n for (const channelId of channels) {\n const peer = peerOf(state, channelId, windowId);\n if (peer) hooks.sendToNapplet(peer, { type: 'ifc.channel.event', channelId, sender: windowId, payload: m.payload } as NappletMessage);\n }\n}\n\nfunction handleChannelList(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): 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 if (peer) channels.push({ id: channelId, peer });\n }\n }\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.list.result', id: m.id ?? '', channels } as NappletMessage);\n}\n\nfunction handleChannelClose(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const channelId = m.channelId ?? '';\n const peer = peerOf(state, channelId, windowId);\n if (!peer) return;\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.closed', channelId } as NappletMessage);\n hooks.sendToNapplet(peer, { type: 'ifc.channel.closed', channelId } as NappletMessage);\n removeChannel(state, channelId);\n}\n\nfunction removeWindowSubscriptions(state: IfcState, 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: IfcState, 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 const peer = peerOf(state, channelId, windowId);\n if (peer) hooks.sendToNapplet(peer, { type: 'ifc.channel.closed', channelId } as NappletMessage);\n removeChannel(state, channelId);\n }\n}\n","/**\n * state-handler.ts — Storage NUB request handler using persistence hooks.\n *\n * Handles napplet storage operations (get, set, remove, keys) via the\n * canonical `@napplet/nub/storage` NIP-5D envelope surface. Delegates\n * storage to StatePersistence. No localStorage, no legacy NIP-01 dispatch.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { StorageMessage } from '@napplet/nub/storage/types';\nimport type { SendToNapplet, StatePersistence } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { AclStateContainer } from './acl-state.js';\n\nfunction scopedKey(dTag: string, aggregateHash: string, userKey: string): string {\n return `napplet-state:${dTag}:${aggregateHash}:${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 NUB storage message from a napplet.\n * Routes to the canonical four `@napplet/nub/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/nub/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 * **Deviation note (Phase 15 to decide):** Set/remove results emit both `ok`\n * (legacy compat) and an `error` field on failure. Canonical `@napplet/nub/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 { handleStorageNub } from '@kehto/runtime';\n *\n * handleStorageNub(windowId, { type: 'storage.get', id: 'q1', key: 'draft' },\n * sendToNapplet, sessionRegistry, aclState, statePersistence);\n * ```\n */\nexport function handleStorageNub(\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 };\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 sendErrorNub(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) { sendErrorNub('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 switch (action) {\n case 'get': {\n const key = m.key as string;\n if (!key) { sendErrorNub('missing key'); return; }\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/nub/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) { sendErrorNub('missing key'); return; }\n const quota = aclState.getStateQuota(pubkey ?? '', dTag, aggregateHash);\n const sk = scopedKey(dTag, aggregateHash, key);\n const newWriteBytes = byteLength(sk + value);\n const existingBytes = statePersistence.calculateBytes(prefix, key);\n if (existingBytes + newWriteBytes > quota) {\n sendErrorNub(`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) { sendErrorNub('missing key'); return; }\n statePersistence.remove(scopedKey(dTag, aggregateHash, 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/nub/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 sendErrorNub('storage.clear is not in @napplet/nub/storage; action not supported');\n break;\n }\n case 'keys': {\n const newKeys = statePersistence.keys(prefix);\n const legacyKeys = legacyPrefix ? statePersistence.keys(legacyPrefix) : [];\n const userKeySet = new Set<string>();\n for (const k of newKeys) userKeySet.add(k.startsWith(prefix) ? k.slice(prefix.length) : k);\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 sendErrorNub(`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';\n\nimport type { AclStateContainer } from './acl-state.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport { handleStorageNub } 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};\n\nconst THEME_FALLBACK_DEFAULT = {\n colors: { background: '#0a0a0a', text: '#e0e0e0', primary: '#7aa2f7' },\n} as const;\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 };\n}\n\nfunction handleStorageMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { aclState, hooks, sessionRegistry } = context;\n handleStorageNub(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 } = context;\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.forward') {\n forwardHotkey(hooks, msg);\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 ...(m.action?.defaultKey ? { binding: m.action.defaultKey } : {}),\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 if (msg.type === 'theme.get') {\n const m = msg as NappletMessage & { id?: string };\n hooks.sendToNapplet(windowId, {\n type: 'theme.get.result',\n id: m.id ?? '',\n theme: THEME_FALLBACK_DEFAULT,\n } as NappletMessage);\n }\n}\n\nfunction handleServiceOnlyMessage(\n context: RuntimeDomainContext,\n name: 'config' | 'resource' | 'cvm' | 'outbox' | 'upload',\n windowId: string,\n msg: NappletMessage,\n): void {\n const service = context.serviceRegistry[name];\n if (!service) return;\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 ServiceHandler,\n ServiceRegistry,\n CompatibilityReport,\n ServiceInfo,\n ShellSecretPersistence,\n GuidPersistence,\n HashVerifierAdapter,\n VerificationCacheEntry,\n RuntimeConfigOverrides,\n NappletMessage,\n NappletClass,\n} from './types.js';\n\nexport { createEnforceGate, createNubEnforceGate, resolveCapabilitiesNub, formatDenialReason } from './enforce.js';\nexport type { EnforceResult, EnforceConfig, NubEnforceConfig, IdentityResolver, AclChecker, NubMessage } 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 { AclStateContainer } from './acl-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 { createRuntime } from './runtime.js';\nexport type { Runtime } from './runtime.js';\n\nexport { handleStorageNub, 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":["identity","ALL_CAPABILITIES"]}
1
+ {"version":3,"sources":["../src/enforce.ts","../src/session-registry.ts","../src/acl-state.ts","../src/manifest-cache.ts","../src/replay.ts","../src/event-buffer.ts","../src/runtime.ts","../src/service-dispatch.ts","../src/relay-handler.ts","../src/identity-handler.ts","../src/ifc-handler.ts","../src/state-handler.ts","../src/domain-handlers.ts","../src/index.ts"],"sourcesContent":["\nimport { ALL_CAPABILITIES, type Capability } from '@kehto/acl/capabilities';\nimport type { NubMessage } from '@kehto/acl';\nimport type { AclCheckEvent, NappletClass } from './types.js';\n\n// Re-export NUB capability resolution for consumers who import through enforce.ts\nexport { resolveCapabilitiesNub } from '@kehto/acl';\nexport type { NubMessage } from '@kehto/acl';\n\n/**\n * Hardcoded per-class capability allowlist. The permissive default\n * (class === null) bypasses this map entirely - see enforceNub. Additional\n * classes are added when NUB specs publish new class tokens.\n *\n * - 'class-1': all 15 v1.2-era capabilities (permissive full surface).\n * - 'class-2': all capabilities EXCEPT relay:write and outbox:write -\n * relay:write is the sample restrictive class Plan 38-03 exercises, and\n * outbox:write is the shell-signed publish op (mirrors relay:write — a\n * read-only class can route/query but not publish).\n *\n * Unknown class tokens fall through enforceNub's \"treat as maximally\n * restrictive\" branch (deny all) - defensive failsafe, not policy.\n */\nconst CLASS_CAPABILITY_ALLOWLIST: Readonly<Record<string, ReadonlySet<Capability>>> = Object.freeze({\n 'class-1': new Set<Capability>(ALL_CAPABILITIES),\n 'class-2': new Set<Capability>(ALL_CAPABILITIES.filter(\n (c) => c !== 'relay:write' && c !== 'outbox:write',\n )),\n});\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 (v1.7 CLASS-03 / D7). Always set on the\n * return path. Distinct from AclCheckEvent.reason (which is optional for\n * backwards compat on the audit surface).\n */\n reason: 'allowed' | 'capability-missing' | 'class-forbidden';\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 NUB 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, class). Returns\n * class posture inline (v1.7 CLASS-03) so the NUB gate can pre-filter class-forbidden capabilities\n * before consulting the ACL check. null class = permissive default (D2).\n * @param onAclCheck - Optional audit callback, called on every enforceNub() check\n */\nexport interface NubEnforceConfig {\n checkAcl: AclChecker;\n resolveIdentityByWindowId: (windowId: string) => { dTag: string; aggregateHash: string; class: NappletClass } | undefined;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate for NIP-5D NUB 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 - NUB enforcement configuration\n * @returns An enforceNub function that resolves identity by windowId\n *\n * @example\n * ```ts\n * const gate = createNubEnforceGate({\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 createNubEnforceGate(config: NubEnforceConfig): (windowId: string, capability: Capability, message?: NubMessage) => EnforceResult {\n const { checkAcl, resolveIdentityByWindowId, onAclCheck } = config;\n\n return function enforceNub(windowId: string, capability: Capability, message?: NubMessage): EnforceResult {\n const entry = resolveIdentityByWindowId(windowId);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n const nappletClass: NappletClass = entry?.class ?? null;\n\n if (nappletClass !== null) {\n const allowlist = CLASS_CAPABILITY_ALLOWLIST[nappletClass];\n // Unknown class token -> treat as maximally restrictive (deny all).\n if (!allowlist || !allowlist.has(capability)) {\n const identity = { pubkey: '', dTag, hash: aggregateHash };\n if (onAclCheck) {\n onAclCheck({\n identity,\n capability,\n decision: 'deny',\n message,\n reason: 'class-forbidden',\n });\n }\n return { allowed: false, capability, reason: 'class-forbidden' };\n }\n }\n\n // Capability check (unchanged from pre-v1.7 except for propagating reason).\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 /** 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 isRegistered(windowId: string): boolean {\n return byWindowId.has(windowId);\n },\n\n getAllEntries(): SessionEntry[] {\n return Array.from(byPubkey.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 NUB-CONFIG)\nconst CAP_RESOURCE_FETCH = 1 << 15; // 32768 (v1.7 Phase 40 NUB-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)\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};\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/**\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 * @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): 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 },\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 },\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 * 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.\n * Returns null if event is valid, or a string reason if it should be rejected.\n */\n check(event: NostrEvent): string | null;\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 const seenEventIds = new Map<string, number>();\n\n return {\n check(event: NostrEvent): string | null {\n const replayWindow = getReplayWindow?.() ?? REPLAY_WINDOW_SECONDS;\n const now = Math.floor(Date.now() / 1000);\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, now);\n for (const [id, timestamp] of seenEventIds) {\n if (now - timestamp > replayWindow) seenEventIds.delete(id);\n }\n return null;\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","\nimport { createDispatch, type NappletMessage, type NostrEvent, type NubHandler } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\n\nimport type {\n RuntimeAdapter, ConsentHandler,\n ServiceHandler, ServiceRegistry, ServiceInfo,\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 { 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, createNubEnforceGate, resolveCapabilitiesNub, formatDenialReason } from './enforce.js';\nimport { createRelayHandler } from './relay-handler.js';\nimport { createIdentityHandler } from './identity-handler.js';\nimport { createIfcRuntime, type IfcRuntime } from './ifc-handler.js';\nimport { createRuntimeDomainHandlers, type RuntimeDomainHandlers } from './domain-handlers.js';\n\n/**\n * The napplet protocol engine — handles NIP-5D NUB 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 manifest cache. */\n readonly manifestCache: ManifestCache;\n}\n\ntype RuntimeNubHandlers = RuntimeDomainHandlers & {\n relay: (windowId: string, msg: NappletMessage) => void;\n identity: (windowId: string, msg: NappletMessage) => void;\n ifc: (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 ifcRuntime: IfcRuntime;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n manifestCache: ManifestCache;\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 createNubEnvelopeDispatcher(handlers: RuntimeNubHandlers): (windowId: string, envelope: NappletMessage) => void {\n let currentWindowId: string | null = null;\n const nubDispatch = createDispatch();\n const adapt = (handler: (windowId: string, msg: NappletMessage) => void): NubHandler => (msg) => {\n if (currentWindowId !== null) handler(currentWindowId, msg);\n };\n\n nubDispatch.registerNub('relay', adapt(handlers.relay));\n nubDispatch.registerNub('identity', adapt(handlers.identity));\n nubDispatch.registerNub('keys', adapt(handlers.keys));\n nubDispatch.registerNub('media', adapt(handlers.media));\n nubDispatch.registerNub('notify', adapt(handlers.notify));\n nubDispatch.registerNub('storage', adapt(handlers.storage));\n nubDispatch.registerNub('ifc', adapt(handlers.ifc));\n nubDispatch.registerNub('theme', adapt(handlers.theme));\n nubDispatch.registerNub('config', adapt(handlers.config));\n nubDispatch.registerNub('resource', adapt(handlers.resource));\n nubDispatch.registerNub('cvm', adapt(handlers.cvm));\n nubDispatch.registerNub('outbox', adapt(handlers.outbox));\n nubDispatch.registerNub('upload', adapt(handlers.upload));\n\n return (windowId, envelope) => {\n currentWindowId = windowId;\n try {\n nubDispatch.dispatch(envelope);\n } finally {\n currentWindowId = null;\n }\n };\n}\n\nfunction createMessageHandler(\n hooks: RuntimeAdapter,\n enforceNub: ReturnType<typeof createNubEnforceGate>,\n dispatchNubEnvelope: (windowId: string, envelope: NappletMessage) => void,\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\n const caps = resolveCapabilitiesNub(envelope);\n if (caps.senderCap) {\n const result = enforceNub(windowId, caps.senderCap as Capability, envelope);\n if (!result.allowed) {\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const isStorageEnvelope = envelope.type.startsWith('storage.');\n const error = formatDenialReason(result.capability);\n const type = isStorageEnvelope ? `${envelope.type}.result` : `${envelope.type}.error`;\n hooks.sendToNapplet(windowId, { type, id, error } as NappletMessage);\n return;\n }\n }\n\n dispatchNubEnvelope(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, eventBuffer, hooks, ifcRuntime, manifestCache, registeredServices,\n replayDetector, serviceRegistry, sessionRegistry, subscriptions,\n } = context;\n const undeclaredServiceConsents = new Set<string>();\n let consentHandler: ConsentHandler | null = null;\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 manifestCache.persist();\n aclState.persist();\n replayDetector.clear();\n subscriptions.clear();\n ifcRuntime.clear();\n eventBuffer.clear();\n registeredServices.clear();\n undeclaredServiceConsents.clear();\n },\n registerConsentHandler(handler: ConsentHandler): void {\n consentHandler = handler;\n void consentHandler;\n },\n registerService(name: string, handler: ServiceHandler): void {\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 },\n unregisterService(name: string): void {\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 ifcRuntime.destroyWindow(windowId);\n notifyServiceWindowDestroyed(windowId, serviceRegistry);\n },\n get sessionRegistry() { return sessionRegistry; },\n get aclState() { return aclState; },\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 const aclState = createAclState(hooks.aclPersistence);\n const manifestCache = createManifestCache(hooks.manifestPersistence);\n const replayDetector = createReplayDetector(\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().replayWindowSeconds\n : undefined,\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 enforceNub = createNubEnforceGate({\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, class: entry.class }\n : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\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 manifestCache.load();\n\n const ifcRuntime = createIfcRuntime(hooks, sessionRegistry);\n const domainHandlers = createRuntimeDomainHandlers({ hooks, serviceRegistry, sessionRegistry, aclState });\n const dispatchNubEnvelope = createNubEnvelopeDispatcher({\n relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),\n identity: createIdentityHandler({ hooks, serviceRegistry }),\n ifc: ifcRuntime.handleMessage,\n ...domainHandlers,\n });\n const handleMessage = createMessageHandler(hooks, enforceNub, dispatchNubEnvelope);\n\n return createRuntimeInstance({\n hooks,\n serviceRegistry,\n registeredServices,\n replayDetector,\n subscriptions,\n eventBuffer,\n ifcRuntime,\n sessionRegistry,\n aclState,\n manifestCache,\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 * NUB-domain services are routed by the domain prefix of message.type\n * (e.g., 'signer.signEvent' -> 'signer' service).\n *\n * IFC-routed services receive ifc.emit messages and are routed by the\n * topic prefix before ':' (e.g., topic 'audio:play' -> 'audio' service).\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 // NUB-domain services: signer.*, relay.*, storage.* route by type prefix\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 // IFC-routed services: audio and notifications receive ifc.emit with topic prefix\n const ifcMessage = message as NappletMessage & { topic?: unknown };\n if (message.type === 'ifc.emit' && typeof ifcMessage.topic === 'string') {\n const prefix = ifcMessage.topic.split(':')[0];\n const ifcHandler = services[prefix];\n if (ifcHandler) {\n ifcHandler.handleMessage(windowId, message, (msg) => sendToNapplet(windowId, msg));\n return true;\n }\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 { NappletMessage, NostrEvent, NostrFilter } from '@napplet/core';\nimport type { RelayMessage } from '@napplet/nub/relay/types';\n\nimport { matchesAnyFilter, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport type { ReplayDetector } from './replay.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?: NostrEvent;\n id?: 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, msg, 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 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): void {\n if (seenIds.has(event.id)) return;\n seenIds.add(event.id);\n if (subscriptions.has(subKey)) {\n hooks.sendToNapplet(windowId, { type: 'relay.event', subId, event } as NappletMessage);\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, resp);\n });\n if (cacheService) {\n cacheService.handleMessage(windowId, msg, (resp: NappletMessage) => {\n if (!subscriptions.has(subKey)) return;\n hooks.sendToNapplet(windowId, resp);\n });\n }\n return;\n }\n\n deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver);\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) => void,\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 = 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 deliver(item as NostrEvent);\n if (cache?.isAvailable() && !isShellKind) {\n try { cache.store(item as NostrEvent); } 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 msg: NappletMessage,\n m: RuntimeRelayMessage,\n): void {\n const { eventBuffer, hooks, replayDetector } = context;\n const event = m.event;\n const id = m.id ?? '';\n if (!event || typeof event !== 'object') {\n hooks.sendToNapplet(windowId, { type: 'relay.publish.error', id, error: 'invalid event' } as NappletMessage);\n return;\n }\n\n const replayResult = replayDetector.check(event);\n if (replayResult !== null) {\n hooks.sendToNapplet(windowId, { type: 'relay.publish.result', id, accepted: false, message: replayResult } as NappletMessage);\n return;\n }\n\n const relayService = relayServiceFrom(context);\n if (relayService) {\n relayService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n } else if (hooks.relayPool?.isAvailable()) {\n hooks.relayPool.publish(event);\n hooks.sendToNapplet(windowId, { type: 'relay.publish.result', id, accepted: true } as NappletMessage);\n } else {\n hooks.sendToNapplet(windowId, { type: 'relay.publish.result', id, accepted: false, message: 'no relay pool available' } as NappletMessage);\n }\n\n eventBuffer.bufferAndDeliver(event, windowId);\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: NostrEvent,\n replyPe: (ok: boolean, extra?: Record<string, unknown>) => void,\n): void {\n const { eventBuffer, 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 as object), content: ciphertext } as NostrEvent;\n const signed = await peSigner.signEvent?.(eventWithCiphertext);\n if (!signed) { replyPe(false, { error: 'signEvent returned null' }); return; }\n\n publishSignedEncrypted(context, windowId, id, signed, replyPe);\n try { eventBuffer.bufferAndDeliver(signed, windowId); } catch { return; }\n } catch (err) {\n replyPe(false, { error: (err as Error)?.message ?? 'encryption failed' });\n }\n })();\n}\n\nfunction publishSignedEncrypted(\n context: RelayHandlerContext,\n windowId: string,\n id: string,\n signed: NostrEvent,\n replyPe: (ok: boolean, extra?: Record<string, unknown>) => void,\n): void {\n const { hooks } = context;\n const relayService = relayServiceFrom(context);\n if (!relayService) {\n if (hooks.relayPool?.isAvailable()) {\n hooks.relayPool.publish(signed);\n replyPe(true, { event: signed, eventId: signed.id });\n } else {\n replyPe(false, { error: 'no relay pool available' });\n }\n return;\n }\n\n const publishMsg = { type: 'relay.publish', id, event: signed } as NappletMessage;\n let replied = false;\n relayService.handleMessage(windowId, publishMsg, (resp: NappletMessage) => {\n if (replied) return;\n const r = resp as NappletMessage & {\n ok?: boolean; accepted?: boolean; eventId?: string;\n message?: string; error?: string;\n };\n if (typeof r.type !== 'string' || !r.type.startsWith('relay.publish')) return;\n const okVal = r.ok ?? r.accepted ?? false;\n replied = true;\n const publishResult = { event: signed, eventId: signed.id } as {\n event: NostrEvent;\n eventId: string;\n error?: string;\n };\n if (!okVal) publishResult.error = r.error ?? r.message ?? 'publish failed';\n replyPe(okVal, publishResult);\n });\n\n if (!replied) {\n replied = true;\n replyPe(true, { event: signed, eventId: signed.id });\n }\n}\n\nfunction handleRelayQuery(\n context: RelayHandlerContext,\n windowId: string,\n m: RuntimeRelayMessage,\n): void {\n const id = m.id ?? '';\n const filters = m.filters ?? [];\n let count = 0;\n for (const event of context.eventBuffer.getBufferedEvents()) {\n if (matchesAnyFilter(event, filters)) count++;\n }\n context.hooks.sendToNapplet(windowId, { type: 'relay.query.result', id, count } as NappletMessage);\n}\n","import type { NappletMessage } from '@napplet/core';\n\nimport type { RuntimeAdapter, ServiceRegistry } from './types.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 id = (msg as NappletMessage & { id?: string }).id ?? '';\n const action = msg.type.slice('identity.'.length);\n const signer = hooks.auth.getSigner();\n const sendError = (error: string) => {\n hooks.sendToNapplet(windowId, { type: `${msg.type}.error`, id, error } as NappletMessage);\n };\n const sendResult = (payload: Record<string, unknown>) => {\n hooks.sendToNapplet(windowId, { type: `${msg.type}.result`, id, ...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((err: unknown) => sendError((err as Error)?.message ?? 'getPublicKey failed'));\n return;\n case 'getRelays':\n if (!signer) { sendError('no signer configured'); return; }\n Promise.resolve(signer.getRelays?.() ?? {})\n .then((relays) => sendResult({ relays }))\n .catch((err: unknown) => sendError((err as Error)?.message ?? 'getRelays failed'));\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:\n sendError(`Unknown identity action: ${action}`);\n }\n };\n}\n","import type { NappletMessage } from '@napplet/core';\nimport type { IfcMessage } from '@napplet/nub/ifc/types';\n\nimport type { SessionRegistry } from './session-registry.js';\nimport type { RuntimeAdapter } from './types.js';\n\ninterface IfcChannel {\n channelId: string;\n peerA: string;\n peerB: string;\n}\n\ntype RuntimeIfcMessage = IfcMessage & {\n id?: string;\n topic?: string;\n payload?: unknown;\n target?: string;\n channelId?: string;\n};\n\ntype IfcState = {\n subscriptions: Map<string, Set<string>>;\n channels: Map<string, IfcChannel>;\n channelsByWindow: Map<string, Set<string>>;\n};\n\nexport interface IfcRuntime {\n handleMessage(windowId: string, msg: NappletMessage): void;\n destroyWindow(windowId: string): void;\n clear(): void;\n}\n\nexport function createIfcRuntime(hooks: RuntimeAdapter, sessionRegistry: SessionRegistry): IfcRuntime {\n const state: IfcState = {\n subscriptions: new Map(),\n channels: new Map(),\n channelsByWindow: new Map(),\n };\n\n return {\n handleMessage(windowId: string, msg: NappletMessage): void {\n handleIfcMessage(state, hooks, sessionRegistry, windowId, msg);\n },\n destroyWindow(windowId: string): void {\n removeWindowChannels(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: IfcState, 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: IfcState, 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: IfcState, 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 if (sessionRegistry.getEntryByWindowId(target)) return target;\n const entries = sessionRegistry.getAllEntries();\n const byPubkey = entries.find((entry) => entry.pubkey === target);\n return byPubkey?.windowId ?? null;\n}\n\nfunction handleIfcMessage(\n state: IfcState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n msg: NappletMessage,\n): void {\n const m = msg as RuntimeIfcMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'emit': handleEmit(state, hooks, 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, windowId, m); return;\n case 'channel.emit': handleChannelEmit(state, hooks, windowId, m); return;\n case 'channel.broadcast': handleChannelBroadcast(state, hooks, windowId, m); return;\n case 'channel.list': handleChannelList(state, hooks, windowId, m); return;\n case 'channel.close': handleChannelClose(state, hooks, windowId, m); return;\n default: return;\n }\n}\n\nfunction handleEmit(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const topic = m.topic ?? '';\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, { type: 'ifc.event', topic, payload: m.payload, sender: windowId } as NappletMessage);\n }\n }\n}\n\nfunction handleSubscribe(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const id = m.id ?? '';\n const topic = m.topic ?? '';\n if (!topic) {\n hooks.sendToNapplet(windowId, { type: 'ifc.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: 'ifc.subscribe.result', id } as NappletMessage);\n}\n\nfunction handleUnsubscribe(state: IfcState, windowId: string, m: RuntimeIfcMessage): 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: IfcState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIfcMessage,\n): void {\n const id = m.id ?? '';\n const peerWindow = resolveTarget(sessionRegistry, m.target ?? '');\n if (!peerWindow) {\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.open.result', id, error: 'target not found' } as NappletMessage);\n return;\n }\n const channelId = hooks.crypto.randomUUID().replace(/-/g, '').slice(0, 32);\n addChannel(state, channelId, windowId, peerWindow);\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.open.result', id, channelId, peer: peerWindow } as NappletMessage);\n}\n\nfunction handleChannelEmit(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const peer = peerOf(state, m.channelId ?? '', windowId);\n if (peer) hooks.sendToNapplet(peer, { type: 'ifc.channel.event', channelId: m.channelId ?? '', sender: windowId, payload: m.payload } as NappletMessage);\n}\n\nfunction handleChannelBroadcast(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const channels = state.channelsByWindow.get(windowId);\n if (!channels) return;\n for (const channelId of channels) {\n const peer = peerOf(state, channelId, windowId);\n if (peer) hooks.sendToNapplet(peer, { type: 'ifc.channel.event', channelId, sender: windowId, payload: m.payload } as NappletMessage);\n }\n}\n\nfunction handleChannelList(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): 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 if (peer) channels.push({ id: channelId, peer });\n }\n }\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.list.result', id: m.id ?? '', channels } as NappletMessage);\n}\n\nfunction handleChannelClose(state: IfcState, hooks: RuntimeAdapter, windowId: string, m: RuntimeIfcMessage): void {\n const channelId = m.channelId ?? '';\n const peer = peerOf(state, channelId, windowId);\n if (!peer) return;\n hooks.sendToNapplet(windowId, { type: 'ifc.channel.closed', channelId } as NappletMessage);\n hooks.sendToNapplet(peer, { type: 'ifc.channel.closed', channelId } as NappletMessage);\n removeChannel(state, channelId);\n}\n\nfunction removeWindowSubscriptions(state: IfcState, 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: IfcState, 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 const peer = peerOf(state, channelId, windowId);\n if (peer) hooks.sendToNapplet(peer, { type: 'ifc.channel.closed', channelId } as NappletMessage);\n removeChannel(state, channelId);\n }\n}\n","/**\n * state-handler.ts — Storage NUB request handler using persistence hooks.\n *\n * Handles napplet storage operations (get, set, remove, keys) via the\n * canonical `@napplet/nub/storage` NIP-5D envelope surface. Delegates\n * storage to StatePersistence. No localStorage, no legacy NIP-01 dispatch.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { StorageMessage } from '@napplet/nub/storage/types';\nimport type { SendToNapplet, StatePersistence } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { AclStateContainer } from './acl-state.js';\n\nfunction scopedKey(dTag: string, aggregateHash: string, userKey: string): string {\n return `napplet-state:${dTag}:${aggregateHash}:${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 NUB storage message from a napplet.\n * Routes to the canonical four `@napplet/nub/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/nub/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 * **Deviation note (Phase 15 to decide):** Set/remove results emit both `ok`\n * (legacy compat) and an `error` field on failure. Canonical `@napplet/nub/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 { handleStorageNub } from '@kehto/runtime';\n *\n * handleStorageNub(windowId, { type: 'storage.get', id: 'q1', key: 'draft' },\n * sendToNapplet, sessionRegistry, aclState, statePersistence);\n * ```\n */\nexport function handleStorageNub(\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 };\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 sendErrorNub(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) { sendErrorNub('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 switch (action) {\n case 'get': {\n const key = m.key as string;\n if (!key) { sendErrorNub('missing key'); return; }\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/nub/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) { sendErrorNub('missing key'); return; }\n const quota = aclState.getStateQuota(pubkey ?? '', dTag, aggregateHash);\n const sk = scopedKey(dTag, aggregateHash, key);\n const newWriteBytes = byteLength(sk + value);\n const existingBytes = statePersistence.calculateBytes(prefix, key);\n if (existingBytes + newWriteBytes > quota) {\n sendErrorNub(`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) { sendErrorNub('missing key'); return; }\n statePersistence.remove(scopedKey(dTag, aggregateHash, 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/nub/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 sendErrorNub('storage.clear is not in @napplet/nub/storage; action not supported');\n break;\n }\n case 'keys': {\n const newKeys = statePersistence.keys(prefix);\n const legacyKeys = legacyPrefix ? statePersistence.keys(legacyPrefix) : [];\n const userKeySet = new Set<string>();\n for (const k of newKeys) userKeySet.add(k.startsWith(prefix) ? k.slice(prefix.length) : k);\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 sendErrorNub(`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';\n\nimport type { AclStateContainer } from './acl-state.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport { handleStorageNub } 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};\n\nconst THEME_FALLBACK_DEFAULT = {\n colors: { background: '#0a0a0a', text: '#e0e0e0', primary: '#7aa2f7' },\n} as const;\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 };\n}\n\nfunction handleStorageMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { aclState, hooks, sessionRegistry } = context;\n handleStorageNub(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 } = context;\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.forward') {\n forwardHotkey(hooks, msg);\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 ...(m.action?.defaultKey ? { binding: m.action.defaultKey } : {}),\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 if (msg.type === 'theme.get') {\n const m = msg as NappletMessage & { id?: string };\n hooks.sendToNapplet(windowId, {\n type: 'theme.get.result',\n id: m.id ?? '',\n theme: THEME_FALLBACK_DEFAULT,\n } as NappletMessage);\n }\n}\n\nfunction handleServiceOnlyMessage(\n context: RuntimeDomainContext,\n name: 'config' | 'resource' | 'cvm' | 'outbox' | 'upload',\n windowId: string,\n msg: NappletMessage,\n): void {\n const service = context.serviceRegistry[name];\n if (!service) return;\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 ServiceHandler,\n ServiceRegistry,\n CompatibilityReport,\n ServiceInfo,\n ShellSecretPersistence,\n GuidPersistence,\n HashVerifierAdapter,\n VerificationCacheEntry,\n RuntimeConfigOverrides,\n NappletMessage,\n NappletClass,\n} from './types.js';\n\nexport { createEnforceGate, createNubEnforceGate, resolveCapabilitiesNub, formatDenialReason } from './enforce.js';\nexport type { EnforceResult, EnforceConfig, NubEnforceConfig, IdentityResolver, AclChecker, NubMessage } 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 { AclStateContainer } from './acl-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 { createRuntime } from './runtime.js';\nexport type { Runtime } from './runtime.js';\n\nexport { handleStorageNub, 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":["identity","ALL_CAPABILITIES"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kehto/runtime",
3
- "version": "0.7.0",
3
+ "version": "0.8.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.7.0"
25
+ "@kehto/acl": "0.8.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@napplet/core": "^0.5.0",