@kehto/runtime 0.12.0 → 0.14.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.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/enforce.ts","../src/session-registry.ts","../src/acl-state.ts","../src/firewall-state.ts","../src/manifest-cache.ts","../src/replay.ts","../src/event-buffer.ts","../src/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': the full capability surface (permissive).\n * - 'class-2': all capabilities EXCEPT relay:write, outbox:write, and\n * intent:write - relay:write is the sample restrictive class Plan 38-03\n * exercises, outbox:write is the shell-signed publish op, and intent:write is\n * the focus-stealing cross-napplet dispatch op (all three mirror each other —\n * a read-only class can route/query/introspect but not publish or dispatch).\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' && c !== 'intent: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)\nconst CAP_INTENT_READ = 1 << 21; // 2097152 (NAP-INTENT archetype introspection)\nconst CAP_INTENT_WRITE = 1 << 22; // 4194304 (NAP-INTENT cross-napplet dispatch)\n\nconst CAP_MAP: Record<Capability, number> = {\n 'relay:read': CAP_RELAY_READ,\n 'relay:write': CAP_RELAY_WRITE,\n 'cache:read': CAP_CACHE_READ,\n 'cache:write': CAP_CACHE_WRITE,\n 'hotkey:forward': CAP_HOTKEY_FORWARD,\n 'state:read': CAP_STATE_READ,\n 'state:write': CAP_STATE_WRITE,\n 'identity:read': CAP_IDENTITY_READ,\n 'keys:bind': CAP_KEYS_BIND,\n 'keys:forward': CAP_KEYS_FORWARD,\n 'media:control': CAP_MEDIA_CONTROL,\n 'notify:send': CAP_NOTIFY_SEND,\n 'notify:channel': CAP_NOTIFY_CHANNEL,\n 'theme:read': CAP_THEME_READ,\n 'config:read': CAP_CONFIG_READ,\n 'resource:fetch': CAP_RESOURCE_FETCH,\n 'cvm:call': CAP_CVM_CALL,\n 'outbox:read': CAP_OUTBOX_READ,\n 'outbox:write': CAP_OUTBOX_WRITE,\n 'upload:write': CAP_UPLOAD_WRITE,\n 'intent:read': CAP_INTENT_READ,\n 'intent:write': CAP_INTENT_WRITE,\n};\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 * firewall-state.ts — Firewall state container with persistence hooks.\n *\n * Wraps @kehto/firewall's pure functions with persistence via\n * FirewallPersistence. No localStorage or DOM references.\n *\n * Two independent `let`-bound cells:\n * - `config` — immutable FirewallConfig; persisted on each mutation.\n * - `counters` — ephemeral FirewallState (token-buckets + burst counters);\n * in-memory only, reset on reload (RUNTIME-03).\n *\n * CRITICAL: `evaluate` reassigns `counters = result.newState` on every call.\n * Without this, token buckets never advance and flood events never escalate\n * from 'flag' to 'block'.\n */\n\nimport type {\n Observation,\n FirewallConfig,\n FirewallState,\n NappletPolicy,\n RateLimit,\n ContentMatcher,\n EvaluateResult,\n} from '@kehto/firewall';\nimport {\n evaluate,\n defaultConfig,\n createState,\n serialize,\n deserialize,\n setPolicy,\n setRateLimit,\n setGlobalRate,\n addMatcher,\n} from '@kehto/firewall';\nimport type { FirewallPersistence } from './types.js';\n\n/**\n * Stateful firewall container — wraps @kehto/firewall's pure functions\n * with persistence and a convenient imperative API.\n *\n * Mirrors AclStateContainer from acl-state.ts in structure and naming.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * const result = firewall.evaluate({ napplet: 'chat', opClass: 'relay:write', focused: true, now: Date.now() });\n * ```\n */\nexport interface FirewallStateContainer {\n /**\n * Evaluate an observation against the current firewall config and counters.\n * CRITICAL: advances the in-memory counter state on each call.\n *\n * @param observation - Normalized observation extracted from the napplet message envelope.\n * @returns The full EvaluateResult (decision, action, ruleId, reason, newState).\n */\n evaluate(observation: Observation): EvaluateResult;\n /**\n * Set a per-napplet policy override (allow / deny / ask).\n *\n * @param napplet - The napplet dTag (version-agnostic identity key).\n * @param policy - Hard policy override for this napplet.\n */\n setPolicy(napplet: string, policy: NappletPolicy): void;\n /**\n * Set a per-(napplet, opClass) token-bucket rate limit.\n *\n * @param napplet - The napplet dTag.\n * @param opClass - The operation class string.\n * @param limit - The rate limit to apply.\n */\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void;\n /**\n * Set a global rate limit applied to all op-classes that have no specific entry.\n *\n * @param napplet - The napplet dTag.\n * @param limit - The global fallback rate limit.\n */\n setGlobalRate(napplet: string, limit: RateLimit): void;\n /**\n * Add a content matcher to the firewall config.\n *\n * @param matcher - The content matcher to append.\n */\n addMatcher(matcher: ContentMatcher): void;\n /** Return the current firewall config. */\n getConfig(): FirewallConfig;\n /** Persist the current firewall config via the persistence hook. Best-effort. */\n persist(): void;\n /** Load previously persisted firewall config. Counters are NOT restored. */\n load(): void;\n /** Reset config to defaultConfig() and counters to createState(), then persist empty. */\n clear(): void;\n}\n\n/**\n * Create a firewall state container backed by @kehto/firewall and optionally\n * persisted via the given persistence hooks.\n *\n * When `persistence` is absent (or undefined), firewall config is in-memory\n * only and resets on container recreation. This is safe for hosts that do not\n * need durable per-napplet policies.\n *\n * @param persistence - Optional storage backend for firewall config.\n * @returns A FirewallStateContainer instance.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * firewall.setPolicy('chat', 'allow');\n * firewall.persist();\n * ```\n */\nexport function createFirewallState(\n persistence?: FirewallPersistence,\n): FirewallStateContainer {\n let config: FirewallConfig = defaultConfig();\n let counters: FirewallState = createState();\n\n return {\n evaluate(observation: Observation): EvaluateResult {\n const result = evaluate(config, counters, observation);\n counters = result.newState; // CRITICAL: advance ephemeral counter state\n return result;\n },\n\n setPolicy(napplet: string, policy: NappletPolicy): void {\n config = setPolicy(config, napplet, policy);\n },\n\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void {\n config = setRateLimit(config, napplet, opClass, limit);\n },\n\n setGlobalRate(napplet: string, limit: RateLimit): void {\n config = setGlobalRate(config, napplet, limit);\n },\n\n addMatcher(matcher: ContentMatcher): void {\n config = addMatcher(config, matcher);\n },\n\n getConfig(): FirewallConfig {\n return config;\n },\n\n persist(): void {\n try {\n persistence?.persist(serialize(config));\n } catch { /* persistence is best-effort */ }\n },\n\n load(): void {\n try {\n const raw = persistence?.load() ?? null;\n if (!raw) return;\n config = deserialize(raw);\n // counters are deliberately NOT restored (RUNTIME-03: ephemeral)\n } catch {\n config = defaultConfig();\n }\n },\n\n clear(): void {\n config = defaultConfig();\n counters = createState();\n try { persistence?.persist(''); } catch { /* best-effort */ }\n },\n };\n}\n","/**\n * manifest-cache.ts — Manifest cache with persistence hooks.\n *\n * Caches NIP-5A manifest data (aggregate hashes) per napplet identity.\n * Delegates storage to ManifestPersistence — no localStorage.\n */\n\nimport type { ManifestPersistence, ManifestCacheEntry, VerificationCacheEntry } from './types.js';\n\n/**\n * Cache for verified napplet manifest entries.\n * Used to detect napplet updates (aggregateHash changes) across sessions.\n *\n * @example\n * ```ts\n * const cache = createManifestCache(persistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * ```\n */\nexport interface ManifestCache {\n /** Get a cached manifest entry by pubkey and dTag. */\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined;\n /** Set (upsert) a manifest cache entry and persist. */\n set(entry: ManifestCacheEntry): void;\n /** Check if a specific hash is cached for a pubkey/dTag combination. */\n has(pubkey: string, dTag: string, hash: string): boolean;\n /** Get the requires list for a cached manifest, or empty array if not found. */\n getRequires(pubkey: string, dTag: string): string[];\n /** Remove a cached entry for a pubkey/dTag and persist. */\n remove(pubkey: string, dTag: string): void;\n /** Load the cache from persistence. */\n load(): void;\n /** Persist the cache to storage. */\n persist(): void;\n /** Clear all cached entries. */\n clear(): void;\n\n /** Get a cached verification result by manifest event ID. */\n getVerification(eventId: string): VerificationCacheEntry | undefined;\n\n /** Cache a verification result keyed by manifest event ID. */\n setVerification(eventId: string, result: VerificationCacheEntry): void;\n\n /** Check if a manifest event ID has been verified. */\n hasVerification(eventId: string): boolean;\n\n /** Clear all verification cache entries. */\n clearVerifications(): void;\n}\n\n/**\n * Create a manifest cache backed by the given persistence hooks.\n *\n * @param persistence - Storage backend for manifest data\n * @returns A ManifestCache instance\n *\n * @example\n * ```ts\n * import { createManifestCache } from '@kehto/runtime';\n *\n * const cache = createManifestCache(manifestPersistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * cache.has('abc...', 'chat', 'dead'); // true\n * ```\n */\nexport function createManifestCache(persistence: ManifestPersistence): ManifestCache {\n const cache = new Map<string, ManifestCacheEntry>();\n const verificationCache = new Map<string, VerificationCacheEntry>();\n\n function cacheKey(pubkey: string, dTag: string): string {\n return `${pubkey}:${dTag}`;\n }\n\n const self: ManifestCache = {\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined {\n return cache.get(cacheKey(pubkey, dTag));\n },\n\n set(entry: ManifestCacheEntry): void {\n cache.set(cacheKey(entry.pubkey, entry.dTag), entry);\n self.persist();\n },\n\n has(pubkey: string, dTag: string, hash: string): boolean {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return !!entry && entry.aggregateHash === hash;\n },\n\n getRequires(pubkey: string, dTag: string): string[] {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return entry?.requires ?? [];\n },\n\n remove(pubkey: string, dTag: string): void {\n cache.delete(cacheKey(pubkey, dTag));\n self.persist();\n },\n\n load(): void {\n try {\n const raw = persistence.load();\n if (!raw) return;\n const entries = JSON.parse(raw) as Array<[string, ManifestCacheEntry]>;\n cache.clear();\n for (const [key, val] of entries) cache.set(key, val);\n } catch { cache.clear(); }\n },\n\n persist(): void {\n try {\n persistence.persist(JSON.stringify(Array.from(cache.entries())));\n } catch { /* persistence is best-effort */ }\n },\n\n clear(): void {\n cache.clear();\n verificationCache.clear();\n try { persistence.persist(''); } catch { /* best-effort */ }\n },\n\n getVerification(eventId: string): VerificationCacheEntry | undefined {\n return verificationCache.get(eventId);\n },\n\n setVerification(eventId: string, result: VerificationCacheEntry): void {\n verificationCache.set(eventId, result);\n // Verification cache is in-memory only — no persistence needed\n // since manifest event IDs are immutable and re-fetching is cheap\n },\n\n hasVerification(eventId: string): boolean {\n return verificationCache.has(eventId);\n },\n\n clearVerifications(): void {\n verificationCache.clear();\n },\n };\n\n return self;\n}\n","/**\n * replay.ts — Replay detection module.\n *\n * Tracks seen event IDs and validates timestamps to prevent\n * duplicate event processing and replay attacks.\n */\n\nimport type { NostrEvent } from '@napplet/core';\n\n/**\n * Replay detection window in seconds — events older than this are rejected.\n * Relocated inline from the former @napplet/core compatibility shim\n * (DRIFT-CORE-06, Phase 24). Numeric value preserved unchanged from the shim\n * to hold behavioral parity with the Phase 23 test baseline.\n */\nconst REPLAY_WINDOW_SECONDS = 30;\n\n/**\n * Replay detection engine. Tracks seen event IDs and validates timestamps.\n *\n * @example\n * ```ts\n * const detector = createReplayDetector();\n * const reason = detector.check(event);\n * if (reason !== null) { // reject event }\n * ```\n */\nexport interface ReplayDetector {\n /**\n * Check if an event should be rejected as a replay.\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 NapHandler } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { Observation } from '@kehto/firewall';\n\nimport type {\n RuntimeAdapter, ConsentHandler, FirewallEvent,\n ServiceHandler, ServiceRegistry, ServiceInfo,\n} from './types.js';\nimport { notifyServiceWindowDestroyed } from './service-dispatch.js';\nimport { createSessionRegistry, type SessionRegistry } from './session-registry.js';\nimport { createAclState, type AclStateContainer } from './acl-state.js';\nimport { createFirewallState, type FirewallStateContainer } from './firewall-state.js';\nimport { createManifestCache, type ManifestCache } from './manifest-cache.js';\nimport { createReplayDetector, type ReplayDetector } from './replay.js';\nimport { createEventBuffer, RING_BUFFER_SIZE, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport { createEnforceGate, 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 firewall state container (for tests to pre-set policy/rules). */\n readonly firewallState: FirewallStateContainer;\n\n /** Access the manifest cache. */\n readonly manifestCache: ManifestCache;\n}\n\ntype 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 firewallState: FirewallStateContainer;\n manifestCache: ManifestCache;\n consentHandlerRef: { current: ConsentHandler | null };\n handleMessage: Runtime['handleMessage'];\n};\n\nfunction createRegisteredServices(serviceRegistry: ServiceRegistry): Map<string, ServiceInfo> {\n const registeredServices = new Map<string, ServiceInfo>();\n for (const [name, handler] of Object.entries(serviceRegistry)) {\n registeredServices.set(name, {\n name: handler.descriptor.name,\n version: handler.descriptor.version,\n description: handler.descriptor.description,\n });\n }\n return registeredServices;\n}\n\nfunction 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): NapHandler => (msg) => {\n if (currentWindowId !== null) handler(currentWindowId, msg);\n };\n\n nubDispatch.registerNap('relay', adapt(handlers.relay));\n nubDispatch.registerNap('identity', adapt(handlers.identity));\n nubDispatch.registerNap('keys', adapt(handlers.keys));\n nubDispatch.registerNap('media', adapt(handlers.media));\n nubDispatch.registerNap('notify', adapt(handlers.notify));\n nubDispatch.registerNap('storage', adapt(handlers.storage));\n nubDispatch.registerNap('ifc', adapt(handlers.ifc));\n // D4: inc is the NAP rename of ifc; dual-routed during the back-compat window\n // so >=0.9.0 napplets (which send inc.*) reach the same handler as legacy ifc.*\n nubDispatch.registerNap('inc', adapt(handlers.ifc));\n nubDispatch.registerNap('theme', adapt(handlers.theme));\n nubDispatch.registerNap('config', adapt(handlers.config));\n nubDispatch.registerNap('resource', adapt(handlers.resource));\n nubDispatch.registerNap('cvm', adapt(handlers.cvm));\n nubDispatch.registerNap('outbox', adapt(handlers.outbox));\n nubDispatch.registerNap('upload', adapt(handlers.upload));\n nubDispatch.registerNap('intent', adapt(handlers.intent));\n\n return (windowId, envelope) => {\n currentWindowId = windowId;\n try {\n nubDispatch.dispatch(envelope);\n } finally {\n currentWindowId = null;\n }\n };\n}\n\n/**\n * Compute the UTF-8 byte length of a string without TextEncoder (ES2022-safe).\n * Mirrors the same helper in state-handler.ts to avoid cross-file import.\n */\nfunction utf8ByteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Extract kind and byte-size from a message envelope (publish-style ops only).\n * Guards against malformed envelopes — returns undefined fields on any failure.\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @returns `{ kind?, size? }` — both optional; undefined when not a publish-style op.\n */\nfunction extractKindSize(envelope: NappletMessage): { kind?: number; size?: number } {\n const ev = (envelope as NappletMessage & { event?: unknown }).event;\n if (typeof ev !== 'object' || ev === null) return {};\n const kind = typeof (ev as { kind?: unknown }).kind === 'number'\n ? (ev as { kind: number }).kind\n : undefined;\n let size: number | undefined;\n try {\n size = utf8ByteLength(JSON.stringify(ev));\n } catch { /* malformed event — size stays undefined */ }\n return { kind, size };\n}\n\n/**\n * Build a normalized Observation from a message envelope and runtime context.\n * The runtime owns the clock (Date.now()); the pure engine never reads it.\n * Focus is sourced exclusively via getFocusContext (FOCUS-01) — never napplet-self-reported.\n * When getFocusContext is absent, defaults to `{ focused: true }` (safe relax-only default).\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @param windowId - Source napplet window identifier.\n * @param senderCap - Resolved sender capability string (or null).\n * @param sessionRegistry - Used to resolve dTag and registeredAt for the window.\n * @param getFocusContext - Optional shell-supplied focus query hook.\n * @returns A complete Observation ready for evaluate().\n */\nfunction buildObservation(\n envelope: NappletMessage,\n windowId: string,\n senderCap: string | null,\n sessionRegistry: SessionRegistry,\n getFocusContext?: RuntimeAdapter['getFocusContext'],\n): Observation {\n const now = Date.now();\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n const napplet = entry?.dTag ?? '';\n const initElapsedMs = now - (entry?.registeredAt ?? now);\n const focus = getFocusContext?.(windowId) ?? { focused: true };\n const opClass = senderCap ?? envelope.type;\n const { kind, size } = extractKindSize(envelope);\n return { napplet, opClass, kind, size, initElapsedMs, focused: focus.focused, msSinceFocusGain: focus.msSinceFocusGain, now };\n}\n\n/** Configuration for createFirewallGate. */\ninterface FirewallGateConfig {\n firewallState: FirewallStateContainer;\n sessionRegistry: SessionRegistry;\n hooks: RuntimeAdapter;\n fireConsent: (windowId: string, napplet: string) => void;\n}\n\n/**\n * Create the firewall gate closure to inject into createMessageHandler.\n * Returns 'dispatch' to allow the message through or 'drop' to reject it.\n *\n * Decision→action mapping (RUNTIME-01, RUNTIME-04, POLICY-02):\n * - reject → error envelope + drop (no dispatch)\n * - prompt → error envelope + fireConsent + drop (ask path)\n * - pass + flag → onFirewallEvent audit + dispatch\n * - pass (ignore/allow) → dispatch (no audit)\n *\n * @param config - Gate configuration (firewallState, sessionRegistry, hooks, fireConsent).\n * @returns A gate function `(windowId, envelope, senderCap) => 'dispatch' | 'drop'`.\n */\nfunction createFirewallGate(config: FirewallGateConfig): (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop' {\n const { firewallState, sessionRegistry, hooks, fireConsent } = config;\n\n return function firewallGate(windowId: string, envelope: NappletMessage, senderCap: string | null): 'dispatch' | 'drop' {\n const obs = buildObservation(envelope, windowId, senderCap, sessionRegistry, hooks.getFocusContext);\n const result = firewallState.evaluate(obs);\n const { decision, action, ruleId, reason } = result;\n const napplet = obs.napplet;\n const opClass = obs.opClass;\n\n if (decision === 'reject' || decision === 'prompt') {\n // Mirror the ACL denial envelope shaping (runtime.ts ACL path):\n // storage envelopes → `.result`; all others → `.error` (T-81-03: no internals leaked)\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const isStorageEnvelope = envelope.type.startsWith('storage.');\n const type = isStorageEnvelope ? `${envelope.type}.result` : `${envelope.type}.error`;\n hooks.sendToNapplet(windowId, { type, id, error: `firewall: ${reason}` } as NappletMessage);\n\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n\n if (decision === 'prompt') {\n // POLICY-02: reject current message AND fire async consent prompt\n fireConsent(windowId, napplet);\n }\n return 'drop';\n }\n\n // decision === 'pass'\n if (action === 'flag') {\n // RUNTIME-04: flag → audit + dispatch (message is NOT dropped)\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n }\n return 'dispatch';\n };\n}\n\nfunction createMessageHandler(\n hooks: RuntimeAdapter,\n enforceNub: ReturnType<typeof createNubEnforceGate>,\n dispatchNubEnvelope: (windowId: string, envelope: NappletMessage) => void,\n firewallGate: (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop',\n): Runtime['handleMessage'] {\n return (windowId: string, msg: unknown): void => {\n if (typeof msg !== 'object' || msg === null || !('type' in msg)) return;\n const envelope = msg as NappletMessage;\n const dotIdx = envelope.type.indexOf('.');\n if (dotIdx === -1) return;\n\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 const verdict = firewallGate(windowId, envelope, caps.senderCap);\n if (verdict === 'drop') return;\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, firewallState, eventBuffer, hooks, ifcRuntime, manifestCache, registeredServices,\n replayDetector, serviceRegistry, sessionRegistry, subscriptions, consentHandlerRef,\n } = context;\n const undeclaredServiceConsents = new Set<string>();\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 firewallState.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 consentHandlerRef.current = handler;\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 firewallState() { return firewallState; },\n get manifestCache() { return manifestCache; },\n };\n}\n\n/**\n * Create a runtime instance with dependency injection via hooks.\n *\n * @param hooks - Host application provides relay pool, auth, config, etc.\n * @returns A Runtime instance ready to handle napplet messages\n *\n * @example\n * ```ts\n * const runtime = createRuntime(hooks);\n * // On incoming message from napplet:\n * runtime.handleMessage(windowId, { type: 'relay.req', id: 'sub1', filters: [] });\n * ```\n */\nexport function createRuntime(hooks: RuntimeAdapter): Runtime {\n const subscriptions = new Map<string, SubscriptionEntry>();\n const serviceRegistry: ServiceRegistry = { ...hooks.services };\n const registeredServices = createRegisteredServices(serviceRegistry);\n const sessionRegistry = createSessionRegistry(hooks.onPendingUpdate);\n const aclState = createAclState(hooks.aclPersistence);\n const firewallState = createFirewallState(hooks.firewallPersistence);\n const manifestCache = createManifestCache(hooks.manifestPersistence);\n const replayDetector = createReplayDetector(\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().replayWindowSeconds\n : undefined,\n );\n\n // Shared consent handler ref — lifted to createRuntime scope so the firewall gate\n // can fire it. The gate is built here (outer scope) but registerConsentHandler is\n // called on the runtime instance (inner scope); the ref bridges both closures.\n const consentHandlerRef: { current: ConsentHandler | null } = { current: null };\n\n // fireConsent: invoked by the gate on 'prompt' decisions (POLICY-02 ask path).\n // On resolution the user's choice is persisted as a per-napplet policy so subsequent\n // messages are NOT re-prompted (T-81-04: bounded consent spam prevention).\n const fireConsent = (windowId: string, napplet: string): void => {\n const handler = consentHandlerRef.current;\n if (!handler) return;\n handler({\n type: 'firewall-policy',\n windowId,\n napplet,\n pubkey: '',\n resolve: (allowed: boolean): void => {\n firewallState.setPolicy(napplet, allowed ? 'allow' : 'deny');\n firewallState.persist();\n },\n });\n };\n\n const enforce = createEnforceGate({\n checkAcl: (pubkey, dTag, aggregateHash, capability) =>\n aclState.check(pubkey, dTag, aggregateHash, capability),\n resolveIdentity: (pubkey) => {\n const entry = sessionRegistry.getEntry(pubkey);\n return entry ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash } : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\n\n const 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 firewallGate = createFirewallGate({ firewallState, sessionRegistry, hooks, fireConsent });\n\n const eventBuffer = createEventBuffer(\n hooks.sendToNapplet,\n sessionRegistry,\n enforce,\n subscriptions,\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().ringBufferSize ?? RING_BUFFER_SIZE\n : undefined,\n );\n\n aclState.load();\n firewallState.load();\n manifestCache.load();\n\n const 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, firewallGate);\n\n return createRuntimeInstance({\n hooks,\n serviceRegistry,\n registeredServices,\n replayDetector,\n subscriptions,\n eventBuffer,\n ifcRuntime,\n sessionRegistry,\n aclState,\n firewallState,\n manifestCache,\n consentHandlerRef,\n handleMessage,\n });\n}\n","\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceRegistry, SendToNapplet } from './types.js';\n\n/**\n * Route a NappletMessage envelope to the matching service handler.\n *\n * 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/nap/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/nap/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 IfcDomain = 'ifc' | 'inc';\n\ntype IfcState = {\n subscriptions: Map<string, Set<string>>;\n channels: Map<string, IfcChannel>;\n channelsByWindow: Map<string, Set<string>>;\n /** D6: per-window vocabulary tracking — set on first message a window sends. */\n domainByWindow: Map<string, IfcDomain>;\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 domainByWindow: 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 state.domainByWindow.delete(windowId);\n },\n clear(): void {\n state.subscriptions.clear();\n state.channels.clear();\n state.channelsByWindow.clear();\n state.domainByWindow.clear();\n },\n };\n}\n\n/**\n * Extract the domain prefix from a message type (the portion before the first '.').\n * e.g. 'inc.subscribe' → 'inc', 'ifc.channel.open' → 'ifc'\n */\nfunction domainOf(type: string): IfcDomain {\n const dot = type.indexOf('.');\n const d = dot === -1 ? type : type.slice(0, dot);\n return d === 'inc' ? 'inc' : 'ifc';\n}\n\n/**\n * Look up the tracked vocabulary for a given window, falling back to a provided\n * default when not yet recorded.\n */\nfunction prefixFor(state: IfcState, windowId: string, fallback: IfcDomain): IfcDomain {\n return state.domainByWindow.get(windowId) ?? fallback;\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 // D6: record the vocabulary this window uses on the first message it sends\n const incomingDomain = domainOf(msg.type);\n if (!state.domainByWindow.has(windowId)) {\n state.domainByWindow.set(windowId, incomingDomain);\n }\n\n switch (action) {\n case 'emit': handleEmit(state, hooks, windowId, m, incomingDomain); return;\n case 'subscribe': handleSubscribe(state, hooks, windowId, m, incomingDomain); return;\n case 'unsubscribe': handleUnsubscribe(state, windowId, m); return;\n case 'channel.open': handleChannelOpen(state, hooks, sessionRegistry, windowId, m, incomingDomain); return;\n case 'channel.emit': handleChannelEmit(state, hooks, windowId, m, incomingDomain); return;\n case 'channel.broadcast': handleChannelBroadcast(state, hooks, windowId, m, incomingDomain); return;\n case 'channel.list': handleChannelList(state, hooks, windowId, m, incomingDomain); return;\n case 'channel.close': handleChannelClose(state, hooks, windowId, m, incomingDomain); return;\n default: return;\n }\n}\n\nfunction handleEmit(\n state: IfcState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIfcMessage,\n senderDomain: IfcDomain,\n): 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 // D6: use the recipient's own tracked vocabulary; fall back to sender's domain\n const prefix = prefixFor(state, subscriberWindowId, senderDomain);\n hooks.sendToNapplet(subscriberWindowId, { type: `${prefix}.event`, topic, payload: m.payload, sender: windowId } as NappletMessage);\n }\n }\n}\n\nfunction handleSubscribe(\n state: IfcState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIfcMessage,\n incomingDomain: IfcDomain,\n): void {\n const id = m.id ?? '';\n const topic = m.topic ?? '';\n if (!topic) {\n // D6: direct response echoes the requester's incoming domain prefix\n hooks.sendToNapplet(windowId, { type: `${incomingDomain}.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 // D6: direct response echoes the requester's incoming domain prefix\n hooks.sendToNapplet(windowId, { type: `${incomingDomain}.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 incomingDomain: IfcDomain,\n): void {\n const id = m.id ?? '';\n const peerWindow = resolveTarget(sessionRegistry, m.target ?? '');\n if (!peerWindow) {\n // D6: direct error response echoes the requester's incoming domain prefix\n hooks.sendToNapplet(windowId, { type: `${incomingDomain}.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 // D6: direct response echoes the requester's incoming domain prefix\n hooks.sendToNapplet(windowId, { type: `${incomingDomain}.channel.open.result`, id, channelId, peer: peerWindow } as NappletMessage);\n}\n\nfunction handleChannelEmit(\n state: IfcState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIfcMessage,\n senderDomain: IfcDomain,\n): void {\n const peer = peerOf(state, m.channelId ?? '', windowId);\n if (peer) {\n // D6: push to other napplet uses recipient's own tracked vocabulary\n const prefix = prefixFor(state, peer, senderDomain);\n hooks.sendToNapplet(peer, { type: `${prefix}.channel.event`, channelId: m.channelId ?? '', sender: windowId, payload: m.payload } as NappletMessage);\n }\n}\n\nfunction handleChannelBroadcast(\n state: IfcState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIfcMessage,\n senderDomain: IfcDomain,\n): 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) {\n // D6: push to other napplet uses recipient's own tracked vocabulary\n const prefix = prefixFor(state, peer, senderDomain);\n hooks.sendToNapplet(peer, { type: `${prefix}.channel.event`, channelId, sender: windowId, payload: m.payload } as NappletMessage);\n }\n }\n}\n\nfunction handleChannelList(\n state: IfcState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIfcMessage,\n incomingDomain: IfcDomain,\n): void {\n const channels = [];\n const set = state.channelsByWindow.get(windowId);\n if (set) {\n for (const channelId of set) {\n const peer = peerOf(state, channelId, windowId);\n if (peer) channels.push({ id: channelId, peer });\n }\n }\n // D6: direct response echoes the requester's incoming domain prefix\n hooks.sendToNapplet(windowId, { type: `${incomingDomain}.channel.list.result`, id: m.id ?? '', channels } as NappletMessage);\n}\n\nfunction handleChannelClose(\n state: IfcState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIfcMessage,\n closerDomain: IfcDomain,\n): void {\n const channelId = m.channelId ?? '';\n const peer = peerOf(state, channelId, windowId);\n if (!peer) return;\n // D6: direct response (closed-to-self) echoes the closer's incoming domain prefix\n hooks.sendToNapplet(windowId, { type: `${closerDomain}.channel.closed`, channelId } as NappletMessage);\n // D6: push to peer uses the peer's own tracked vocabulary; fall back to closer's domain\n const peerPrefix = prefixFor(state, peer, closerDomain);\n hooks.sendToNapplet(peer, { type: `${peerPrefix}.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) {\n // D6: push to peer uses the peer's own tracked vocabulary; fall back to destroyee's domain\n const destroyeeDomain = prefixFor(state, windowId, 'ifc');\n const peerPrefix = prefixFor(state, peer, destroyeeDomain);\n hooks.sendToNapplet(peer, { type: `${peerPrefix}.channel.closed`, channelId } as NappletMessage);\n }\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/nap/storage` NIP-5D envelope surface. Delegates\n * storage to StatePersistence. No localStorage, no legacy NIP-01 dispatch.\n *\n * Per-instance scope (NAP-STORAGE, napplet/naps#3): every request carries an\n * optional `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"`\n * folds a stable per-window discriminator (`@i/<windowId>:`) into the key so\n * that multiple open windows of the *same* napplet keep isolated, independently\n * persisted state. Instancing is a property of the data (per call), not the\n * napplet — there is no napplet-wide mode. The napplet never sees or names an\n * instance identifier; it sets `scope` and trusts the shell's Unique + Stable\n * guarantees. `\"shared\"` (or absent) addresses the napplet-wide namespace,\n * byte-identical to the historical behavior.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { StorageMessage } from '@napplet/nap/storage/types';\nimport type { SendToNapplet, StatePersistence } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { AclStateContainer } from './acl-state.js';\n\n/** Canonical NAP-STORAGE scope values. `scope` absent ⇔ `\"shared\"`. */\ntype StorageScope = 'shared' | 'instance';\n\n/**\n * Reserved key segment that marks the per-instance sub-namespace inside a\n * napplet's `(dTag, aggregateHash)` bucket. Server-side only — napplets never\n * see it. Used for `scope: \"instance\"` requests (NAP-STORAGE per-instance scope).\n */\nconst INSTANCE_MARKER = '@i/';\n\n/**\n * Build the per-window instance sub-namespace segment: `@i/<windowId>:`.\n *\n * The discriminator is derived from the runtime's `windowId`, which the shell\n * keeps stable across reload / workspace restore, so per-instance storage\n * persists across sessions (the spec's \"Stable\" guarantee). Two distinct\n * windows of the same napplet get distinct segments (the \"Unique\" guarantee).\n * This format is internal and never exposed to the napplet.\n */\nfunction instanceSegment(windowId: string): string {\n return `${INSTANCE_MARKER}${windowId}:`;\n}\n\n/**\n * Build the storage key for a napplet user key.\n *\n * For `scope: \"shared\"` (default) this is byte-identical to the historical key.\n * For `scope: \"instance\"` the runtime folds in the opaque per-window segment —\n * transparently, with no napplet involvement (the napplet only sets the wire\n * field; it never sees the resulting key shape).\n */\nfunction scopedKey(\n dTag: string,\n aggregateHash: string,\n userKey: string,\n instance = false,\n windowId = '',\n): string {\n const segment = instance ? instanceSegment(windowId) : '';\n return `napplet-state:${dTag}:${aggregateHash}:${segment}${userKey}`;\n}\n\n/** Build a legacy scoped key that includes pubkey (for migration reads). */\nfunction legacyScopedKey(pubkey: string, dTag: string, aggregateHash: string, userKey: string): string {\n return `napplet-state:${pubkey}:${dTag}:${aggregateHash}:${userKey}`;\n}\n\n/** Compute byte length of a UTF-8 string without TextEncoder (ES2022-safe). */\nfunction byteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Handle a NIP-5D NUB storage message from a napplet.\n * Routes to the canonical four `@napplet/nap/storage` actions:\n * - `storage.get` → `storage.get.result` `{ value: string | null }`\n * - `storage.set` → `storage.set.result` `{ ok: boolean }` (canonical only checks `error`)\n * - `storage.remove` → `storage.remove.result` `{ ok: boolean }`\n * - `storage.keys` → `storage.keys.result` `{ keys: string[] }`\n *\n * `storage.clear` is NOT in the canonical `@napplet/nap/storage` union (it was a\n * kehto unilateral extension); attempts produce a `storage.clear.result` envelope\n * with an `error` field set. Internal lifecycle cleanup still uses the\n * `cleanupNappState()` helper below — it is not napplet-reachable.\n *\n * **Per-instance scope (NAP-STORAGE / napplet/naps#3):** each request MAY carry\n * `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"` addresses a\n * per-window sub-namespace keyed by `windowId`, so multiple windows of the same\n * napplet keep isolated state; `\"shared\"` (or absent) addresses the napplet-wide\n * namespace, byte-identical to historical behavior. An unrecognized `scope` value\n * is an invalid request and produces a `.result` envelope with `error` set.\n *\n * **Deviation note (Phase 15 to decide):** Set/remove results emit both `ok`\n * (legacy compat) and an `error` field on failure. Canonical `@napplet/nap/storage`\n * only specifies the optional `error`; napplets check `!result.error` for success.\n * Emitting `ok` preserves backward compatibility with existing in-tree callers.\n * Phase 15 (v1.2 release prep) decides whether to drop `ok` pre-release.\n *\n * @param windowId - The window identifier of the requesting napplet\n * @param msg - The NappletMessage containing the storage request\n * @param sendToNapplet - Transport function to send responses\n * @param sessionRegistry - Identity registry for looking up napplet session identity\n * @param aclState - ACL state for quota checks\n * @param statePersistence - State storage backend\n *\n * @example\n * ```ts\n * import { 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 scope?: string;\n };\n const id = m.id ?? '';\n const action = msg.type.split('.')[1];\n\n function sendResult(payload: Record<string, unknown>): void {\n sendToNapplet(windowId, { type: `${msg.type}.result`, id, ...payload } as NappletMessage);\n }\n function 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 // NAP-STORAGE per-call scope. Absent ⇔ \"shared\" (byte-identical to history).\n // An unrecognized value is an invalid request — the shell MUST return an error.\n if (m.scope !== undefined && m.scope !== 'shared' && m.scope !== 'instance') {\n sendErrorNub(`invalid scope: ${String(m.scope)} (expected \"shared\" or \"instance\")`);\n return;\n }\n const scope: StorageScope = m.scope === 'instance' ? 'instance' : 'shared';\n const isInstance = scope === 'instance';\n // The per-window sub-namespace for this request, when instance-scoped.\n const instancePrefix = `${prefix}${instanceSegment(windowId)}`;\n const keyFor = (userKey: string): string =>\n scopedKey(dTag, aggregateHash, userKey, isInstance, windowId);\n\n switch (action) {\n case 'get': {\n const key = m.key as string;\n if (!key) { sendErrorNub('missing key'); return; }\n // Instance scope addresses a fresh per-window namespace with no legacy data —\n // read only the instance key. Shared scope keeps the triple-read migration.\n if (isInstance) {\n sendResult({ value: statePersistence.get(keyFor(key)) });\n break;\n }\n const newKey = scopedKey(dTag, aggregateHash, key);\n // Triple-read for migration: new format, legacy-with-pubkey, old prefix\n let result = statePersistence.get(newKey);\n if (result === null && pubkey) {\n result = statePersistence.get(legacyScopedKey(pubkey, dTag, aggregateHash, key));\n }\n if (result === null && pubkey) {\n result = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);\n }\n // Canonical @napplet/nap/storage: `value: string | null` — null ⇔ missing.\n sendResult({ value: result });\n break;\n }\n case 'set': {\n const key = m.key as string;\n const value = (m.value as string) ?? '';\n if (!key) { sendErrorNub('missing key'); return; }\n const quota = aclState.getStateQuota(pubkey ?? '', dTag, aggregateHash);\n const sk = keyFor(key);\n const newWriteBytes = byteLength(sk + value);\n // Quota is per napplet identity: `prefix` spans both the shared namespace and\n // every per-instance sub-key, so instance writes draw from the same budget.\n const existingBytes = statePersistence.calculateBytes(prefix, key);\n if (existingBytes + newWriteBytes > quota) {\n 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(keyFor(key));\n // legacyPrefix exists only while `pubkey` is non-empty (legacy AUTH sessions).\n // Suppress \"unused binding\" warnings: we intentionally retain the computation\n // so future migration lands at call sites, not here.\n void legacyPrefix;\n sendResult({ ok: true });\n break;\n }\n case 'clear': {\n // storage.clear was a kehto unilateral extension; it is NOT in the canonical\n // @napplet/nap/storage union. Napplets hitting this branch receive a\n // storage.clear.result envelope with the error field set. Internal lifecycle\n // cleanup uses cleanupNappState() below (not napplet-reachable).\n sendErrorNub('storage.clear is not in @napplet/nap/storage; action not supported');\n break;\n }\n case 'keys': {\n // Instance scope sees only this window's sub-namespace.\n if (isInstance) {\n const instKeys = statePersistence.keys(instancePrefix);\n const instanceKeySet = new Set<string>();\n for (const k of instKeys) {\n instanceKeySet.add(k.startsWith(instancePrefix) ? k.slice(instancePrefix.length) : k);\n }\n sendResult({ keys: Array.from(instanceKeySet) });\n break;\n }\n const newKeys = statePersistence.keys(prefix);\n const legacyKeys = legacyPrefix ? statePersistence.keys(legacyPrefix) : [];\n const userKeySet = new Set<string>();\n // Shared scope excludes per-instance sub-keys (reserved `@i/` marker) so an\n // instance-scoped napplet's per-window data never leaks into a shared listing.\n for (const k of newKeys) {\n if (!k.startsWith(prefix)) { userKeySet.add(k); continue; }\n const userKey = k.slice(prefix.length);\n if (userKey.startsWith(INSTANCE_MARKER)) continue;\n userKeySet.add(userKey);\n }\n for (const k of legacyKeys) userKeySet.add(k.startsWith(legacyPrefix) ? k.slice(legacyPrefix.length) : k);\n sendResult({ keys: Array.from(userKeySet) });\n break;\n }\n default:\n 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 intent: 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 intent: (windowId, msg) => handleServiceOnlyMessage(context, 'intent', 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' | 'intent',\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 FirewallPersistence,\n FirewallEvent,\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 { createFirewallState } from './firewall-state.js';\nexport type { FirewallStateContainer } from './firewall-state.js';\n\nexport { createManifestCache } from './manifest-cache.js';\nexport type { ManifestCache } from './manifest-cache.js';\n\nexport { createReplayDetector } from './replay.js';\nexport type { ReplayDetector } from './replay.js';\n\nexport { createEventBuffer, matchesFilter, matchesAnyFilter, RING_BUFFER_SIZE } from './event-buffer.js';\nexport type { EventBuffer, SubscriptionEntry } from './event-buffer.js';\n\nexport { 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","createState","serialize","deserialize","ALL_CAPABILITIES"]}
1
+ {"version":3,"sources":["../src/enforce.ts","../src/session-registry.ts","../src/acl-state.ts","../src/firewall-state.ts","../src/manifest-cache.ts","../src/replay.ts","../src/event-buffer.ts","../src/runtime.ts","../src/service-dispatch.ts","../src/relay-handler.ts","../src/identity-handler.ts","../src/inc-handler.ts","../src/state-handler.ts","../src/domain-handlers.ts","../src/index.ts"],"sourcesContent":["\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { NapMessage } from '@kehto/acl';\nimport type { AclCheckEvent } from './types.js';\n\n// Re-export NAP capability resolution for consumers who import through enforce.ts\nexport { resolveCapabilitiesNap } from '@kehto/acl';\nexport type { NapMessage } from '@kehto/acl';\n\n/**\n * Result of an enforcement check.\n *\n * @param allowed - Whether the capability check passed\n * @param capability - The capability that was checked (human-readable string)\n * @param reason - Why the decision was reached (v1.7 CLASS-03 / D7). Always set on return.\n */\nexport interface EnforceResult {\n allowed: boolean;\n capability: Capability;\n /**\n * Why the decision was reached. Always set on the return path.\n * Distinct from AclCheckEvent.reason (which is optional for backwards compat).\n */\n reason: 'allowed' | 'capability-missing';\n}\n\n/**\n * Identity lookup function type — resolves a pubkey to its full identity.\n * Provided by sessionRegistry at runtime.\n */\nexport type IdentityResolver = (pubkey: string) => { dTag: string; aggregateHash: string } | undefined;\n\n/**\n * ACL check function type — performs the actual capability check.\n * Provided by @kehto/acl's check() at runtime, or by the legacy aclStore.check().\n */\nexport type AclChecker = (pubkey: string, dTag: string, aggregateHash: string, capability: Capability) => boolean;\n\n/**\n * Enforcement gate configuration.\n *\n * @param checkAcl - The ACL check function (wraps @kehto/acl or legacy aclStore)\n * @param resolveIdentity - Maps pubkey to full identity (dTag, aggregateHash)\n * @param onAclCheck - Optional audit callback. Called on every enforce() check\n * with the identity, capability, and decision.\n */\nexport interface EnforceConfig {\n checkAcl: AclChecker;\n resolveIdentity: IdentityResolver;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate with the given configuration.\n *\n * Returns a function that checks a single capability for a given pubkey.\n * Every call is logged to the audit callback.\n *\n * @param config - Enforcement configuration with ACL checker, identity resolver, and audit hooks\n * @returns An enforce function that checks capabilities and logs decisions\n *\n * @example\n * ```ts\n * const gate = createEnforceGate({\n * checkAcl: aclStore.check,\n * resolveIdentity: (pk) => nappKeyRegistry.getEntry(pk),\n * onAclCheck: hooks.onAclCheck,\n * });\n * const result = gate('abc123...', 'relay:write');\n * // result.allowed === true | false\n * ```\n */\nexport function createEnforceGate(config: EnforceConfig): (pubkey: string, capability: Capability, message?: unknown[]) => EnforceResult {\n const { checkAcl, resolveIdentity, onAclCheck } = config;\n\n return function enforce(pubkey: string, capability: Capability, message?: unknown[]): EnforceResult {\n const entry = resolveIdentity(pubkey);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n\n const allowed = checkAcl(pubkey, dTag, aggregateHash, capability);\n\n // Audit logging — every check, both allows and denials\n const identity = { pubkey, dTag, hash: aggregateHash };\n const decision = allowed ? 'allow' as const : 'deny' as const;\n const reason = allowed ? 'allowed' as const : 'capability-missing' as const;\n\n if (onAclCheck) {\n onAclCheck({ identity, capability, decision, message, reason });\n }\n\n return { allowed, capability, reason };\n };\n}\n\n/**\n * Enforcement gate configuration for NIP-5D NAP handlers.\n * Uses windowId for identity resolution instead of pubkey (which is '' in NIP-5D sessions).\n *\n * @param checkAcl - The ACL check function\n * @param resolveIdentityByWindowId - Maps windowId to identity (dTag, aggregateHash).\n * @param onAclCheck - Optional audit callback, called on every enforceNap() check\n */\nexport interface NapEnforceConfig {\n checkAcl: AclChecker;\n resolveIdentityByWindowId: (windowId: string) => { dTag: string; aggregateHash: string } | undefined;\n onAclCheck?: (event: AclCheckEvent) => void;\n}\n\n/**\n * Create an enforcement gate for NIP-5D NAP message handlers.\n *\n * Unlike createEnforceGate (which resolves identity by pubkey), this factory\n * resolves identity by windowId — necessary for NIP-5D sessions where pubkey is ''.\n *\n * @param config - NAP enforcement configuration\n * @returns An enforceNap function that resolves identity by windowId and\n * delegates to the ACL check.\n *\n * @example\n * ```ts\n * const gate = createNapEnforceGate({\n * checkAcl: aclStore.check,\n * resolveIdentityByWindowId: (wid) => sessionRegistry.getEntryByWindowId(wid),\n * onAclCheck: hooks.onAclCheck,\n * });\n * const result = gate('win-1', 'relay:write', { type: 'relay.publish' });\n * // result.allowed === true | false\n * ```\n */\nexport function createNapEnforceGate(config: NapEnforceConfig): (windowId: string, capability: Capability, message?: NapMessage) => EnforceResult {\n const { checkAcl, resolveIdentityByWindowId, onAclCheck } = config;\n\n return function enforceNap(windowId: string, capability: Capability, message?: NapMessage): EnforceResult {\n const entry = resolveIdentityByWindowId(windowId);\n const dTag = entry?.dTag ?? '';\n const aggregateHash = entry?.aggregateHash ?? '';\n\n // NIP-5D: pass empty string for pubkey - toKey() ignores it (uses dTag:hash).\n const allowed = checkAcl('', dTag, aggregateHash, capability);\n\n const identity = { pubkey: '', dTag, hash: aggregateHash };\n const decision = allowed ? 'allow' as const : 'deny' as const;\n const reason = allowed ? 'allowed' as const : 'capability-missing' as const;\n\n if (onAclCheck) {\n onAclCheck({ identity, capability, decision, message, reason });\n }\n\n return { allowed, capability, reason };\n };\n}\n\n/**\n * Format a denial reason string with the standard 'denied:' prefix.\n *\n * @param capability - The denied capability name\n * @returns Formatted denial string, e.g., 'denied: relay:write'\n *\n * @example\n * ```ts\n * formatDenialReason('relay:write')\n * // => 'denied: relay:write'\n * ```\n */\nexport function formatDenialReason(capability: Capability): string {\n return `denied: ${capability}`;\n}\n","\nimport type { SessionEntry, PendingUpdate, PendingUpdateNotifier } from './types.js';\n\n/**\n * Bidirectional registry mapping windowIds to verified napplet pubkeys.\n * Maintained by the runtime after successful AUTH handshakes.\n *\n * @example\n * ```ts\n * const registry = createSessionRegistry();\n * registry.register('win-1', entry);\n * const pubkey = registry.getPubkey('win-1');\n * ```\n */\nexport interface SessionRegistry {\n /** Register a napplet entry, mapping windowId to pubkey and vice versa. */\n register(windowId: string, entry: SessionEntry): void;\n /** Unregister a napplet by windowId, removing both mappings. */\n unregister(windowId: string): void;\n /** Get the pubkey associated with a windowId. */\n getPubkey(windowId: string): string | undefined;\n /** Get the full entry for a napplet pubkey. */\n getEntry(pubkey: string): SessionEntry | undefined;\n /** Get the windowId for a napplet pubkey. */\n getWindowId(pubkey: string): string | undefined;\n /** 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 NAP-CONFIG)\nconst CAP_RESOURCE_FETCH = 1 << 15; // 32768 (v1.7 Phase 40 NAP-RESOURCE)\n// 1 << 16 (65536) RETIRED — was identity:decrypt (v1.8); removed as a spec\n// violation. Left as a permanent gap; do NOT reuse this bit (persisted ACL\n// grants are bitfields — reassigning it would silently re-grant old state).\nconst CAP_CVM_CALL = 1 << 17; // 131072 (NAP-CVM ContextVM bridge)\nconst CAP_OUTBOX_READ = 1 << 18; // 262144 (NAP-OUTBOX read-side routing)\nconst CAP_OUTBOX_WRITE = 1 << 19; // 524288 (NAP-OUTBOX shell-signed publish)\nconst CAP_UPLOAD_WRITE = 1 << 20; // 1048576 (NAP-UPLOAD shell-mediated upload)\nconst CAP_INTENT_READ = 1 << 21; // 2097152 (NAP-INTENT archetype introspection)\nconst CAP_INTENT_WRITE = 1 << 22; // 4194304 (NAP-INTENT cross-napplet dispatch)\n\nconst CAP_MAP: Record<Capability, number> = {\n 'relay:read': CAP_RELAY_READ,\n 'relay:write': CAP_RELAY_WRITE,\n 'cache:read': CAP_CACHE_READ,\n 'cache:write': CAP_CACHE_WRITE,\n 'hotkey:forward': CAP_HOTKEY_FORWARD,\n 'state:read': CAP_STATE_READ,\n 'state:write': CAP_STATE_WRITE,\n 'identity:read': CAP_IDENTITY_READ,\n 'keys:bind': CAP_KEYS_BIND,\n 'keys:forward': CAP_KEYS_FORWARD,\n 'media:control': CAP_MEDIA_CONTROL,\n 'notify:send': CAP_NOTIFY_SEND,\n 'notify:channel': CAP_NOTIFY_CHANNEL,\n 'theme:read': CAP_THEME_READ,\n 'config:read': CAP_CONFIG_READ,\n 'resource:fetch': CAP_RESOURCE_FETCH,\n 'cvm:call': CAP_CVM_CALL,\n 'outbox:read': CAP_OUTBOX_READ,\n 'outbox:write': CAP_OUTBOX_WRITE,\n 'upload:write': CAP_UPLOAD_WRITE,\n 'intent:read': CAP_INTENT_READ,\n 'intent:write': CAP_INTENT_WRITE,\n};\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 * firewall-state.ts — Firewall state container with persistence hooks.\n *\n * Wraps @kehto/firewall's pure functions with persistence via\n * FirewallPersistence. No localStorage or DOM references.\n *\n * Two independent `let`-bound cells:\n * - `config` — immutable FirewallConfig; persisted on each mutation.\n * - `counters` — ephemeral FirewallState (token-buckets + burst counters);\n * in-memory only, reset on reload (RUNTIME-03).\n *\n * CRITICAL: `evaluate` reassigns `counters = result.newState` on every call.\n * Without this, token buckets never advance and flood events never escalate\n * from 'flag' to 'block'.\n */\n\nimport type {\n Observation,\n FirewallConfig,\n FirewallState,\n NappletPolicy,\n RateLimit,\n ContentMatcher,\n EvaluateResult,\n} from '@kehto/firewall';\nimport {\n evaluate,\n defaultConfig,\n createState,\n serialize,\n deserialize,\n setPolicy,\n setRateLimit,\n setGlobalRate,\n addMatcher,\n} from '@kehto/firewall';\nimport type { FirewallPersistence } from './types.js';\n\n/**\n * Stateful firewall container — wraps @kehto/firewall's pure functions\n * with persistence and a convenient imperative API.\n *\n * Mirrors AclStateContainer from acl-state.ts in structure and naming.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * const result = firewall.evaluate({ napplet: 'chat', opClass: 'relay:write', focused: true, now: Date.now() });\n * ```\n */\nexport interface FirewallStateContainer {\n /**\n * Evaluate an observation against the current firewall config and counters.\n * CRITICAL: advances the in-memory counter state on each call.\n *\n * @param observation - Normalized observation extracted from the napplet message envelope.\n * @returns The full EvaluateResult (decision, action, ruleId, reason, newState).\n */\n evaluate(observation: Observation): EvaluateResult;\n /**\n * Set a per-napplet policy override (allow / deny / ask).\n *\n * @param napplet - The napplet dTag (version-agnostic identity key).\n * @param policy - Hard policy override for this napplet.\n */\n setPolicy(napplet: string, policy: NappletPolicy): void;\n /**\n * Set a per-(napplet, opClass) token-bucket rate limit.\n *\n * @param napplet - The napplet dTag.\n * @param opClass - The operation class string.\n * @param limit - The rate limit to apply.\n */\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void;\n /**\n * Set a global rate limit applied to all op-classes that have no specific entry.\n *\n * @param napplet - The napplet dTag.\n * @param limit - The global fallback rate limit.\n */\n setGlobalRate(napplet: string, limit: RateLimit): void;\n /**\n * Add a content matcher to the firewall config.\n *\n * @param matcher - The content matcher to append.\n */\n addMatcher(matcher: ContentMatcher): void;\n /** Return the current firewall config. */\n getConfig(): FirewallConfig;\n /** Persist the current firewall config via the persistence hook. Best-effort. */\n persist(): void;\n /** Load previously persisted firewall config. Counters are NOT restored. */\n load(): void;\n /** Reset config to defaultConfig() and counters to createState(), then persist empty. */\n clear(): void;\n}\n\n/**\n * Create a firewall state container backed by @kehto/firewall and optionally\n * persisted via the given persistence hooks.\n *\n * When `persistence` is absent (or undefined), firewall config is in-memory\n * only and resets on container recreation. This is safe for hosts that do not\n * need durable per-napplet policies.\n *\n * @param persistence - Optional storage backend for firewall config.\n * @returns A FirewallStateContainer instance.\n *\n * @example\n * ```ts\n * const firewall = createFirewallState(persistence);\n * firewall.load();\n * firewall.setPolicy('chat', 'allow');\n * firewall.persist();\n * ```\n */\nexport function createFirewallState(\n persistence?: FirewallPersistence,\n): FirewallStateContainer {\n let config: FirewallConfig = defaultConfig();\n let counters: FirewallState = createState();\n\n return {\n evaluate(observation: Observation): EvaluateResult {\n const result = evaluate(config, counters, observation);\n counters = result.newState; // CRITICAL: advance ephemeral counter state\n return result;\n },\n\n setPolicy(napplet: string, policy: NappletPolicy): void {\n config = setPolicy(config, napplet, policy);\n },\n\n setRateLimit(napplet: string, opClass: string, limit: RateLimit): void {\n config = setRateLimit(config, napplet, opClass, limit);\n },\n\n setGlobalRate(napplet: string, limit: RateLimit): void {\n config = setGlobalRate(config, napplet, limit);\n },\n\n addMatcher(matcher: ContentMatcher): void {\n config = addMatcher(config, matcher);\n },\n\n getConfig(): FirewallConfig {\n return config;\n },\n\n persist(): void {\n try {\n persistence?.persist(serialize(config));\n } catch { /* persistence is best-effort */ }\n },\n\n load(): void {\n try {\n const raw = persistence?.load() ?? null;\n if (!raw) return;\n config = deserialize(raw);\n // counters are deliberately NOT restored (RUNTIME-03: ephemeral)\n } catch {\n config = defaultConfig();\n }\n },\n\n clear(): void {\n config = defaultConfig();\n counters = createState();\n try { persistence?.persist(''); } catch { /* best-effort */ }\n },\n };\n}\n","/**\n * manifest-cache.ts — Manifest cache with persistence hooks.\n *\n * Caches NIP-5A manifest data (aggregate hashes) per napplet identity.\n * Delegates storage to ManifestPersistence — no localStorage.\n */\n\nimport type { ManifestPersistence, ManifestCacheEntry, VerificationCacheEntry } from './types.js';\n\n/**\n * Cache for verified napplet manifest entries.\n * Used to detect napplet updates (aggregateHash changes) across sessions.\n *\n * @example\n * ```ts\n * const cache = createManifestCache(persistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * ```\n */\nexport interface ManifestCache {\n /** Get a cached manifest entry by pubkey and dTag. */\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined;\n /** Set (upsert) a manifest cache entry and persist. */\n set(entry: ManifestCacheEntry): void;\n /** Check if a specific hash is cached for a pubkey/dTag combination. */\n has(pubkey: string, dTag: string, hash: string): boolean;\n /** Get the requires list for a cached manifest, or empty array if not found. */\n getRequires(pubkey: string, dTag: string): string[];\n /** Remove a cached entry for a pubkey/dTag and persist. */\n remove(pubkey: string, dTag: string): void;\n /** Load the cache from persistence. */\n load(): void;\n /** Persist the cache to storage. */\n persist(): void;\n /** Clear all cached entries. */\n clear(): void;\n\n /** Get a cached verification result by manifest event ID. */\n getVerification(eventId: string): VerificationCacheEntry | undefined;\n\n /** Cache a verification result keyed by manifest event ID. */\n setVerification(eventId: string, result: VerificationCacheEntry): void;\n\n /** Check if a manifest event ID has been verified. */\n hasVerification(eventId: string): boolean;\n\n /** Clear all verification cache entries. */\n clearVerifications(): void;\n}\n\n/**\n * Create a manifest cache backed by the given persistence hooks.\n *\n * @param persistence - Storage backend for manifest data\n * @returns A ManifestCache instance\n *\n * @example\n * ```ts\n * import { createManifestCache } from '@kehto/runtime';\n *\n * const cache = createManifestCache(manifestPersistence);\n * cache.load();\n * cache.set({ pubkey: 'abc...', dTag: 'chat', aggregateHash: 'dead', verifiedAt: Date.now() });\n * cache.has('abc...', 'chat', 'dead'); // true\n * ```\n */\nexport function createManifestCache(persistence: ManifestPersistence): ManifestCache {\n const cache = new Map<string, ManifestCacheEntry>();\n const verificationCache = new Map<string, VerificationCacheEntry>();\n\n function cacheKey(pubkey: string, dTag: string): string {\n return `${pubkey}:${dTag}`;\n }\n\n const self: ManifestCache = {\n get(pubkey: string, dTag: string): ManifestCacheEntry | undefined {\n return cache.get(cacheKey(pubkey, dTag));\n },\n\n set(entry: ManifestCacheEntry): void {\n cache.set(cacheKey(entry.pubkey, entry.dTag), entry);\n self.persist();\n },\n\n has(pubkey: string, dTag: string, hash: string): boolean {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return !!entry && entry.aggregateHash === hash;\n },\n\n getRequires(pubkey: string, dTag: string): string[] {\n const entry = cache.get(cacheKey(pubkey, dTag));\n return entry?.requires ?? [];\n },\n\n remove(pubkey: string, dTag: string): void {\n cache.delete(cacheKey(pubkey, dTag));\n self.persist();\n },\n\n load(): void {\n try {\n const raw = persistence.load();\n if (!raw) return;\n const entries = JSON.parse(raw) as Array<[string, ManifestCacheEntry]>;\n cache.clear();\n for (const [key, val] of entries) cache.set(key, val);\n } catch { cache.clear(); }\n },\n\n persist(): void {\n try {\n persistence.persist(JSON.stringify(Array.from(cache.entries())));\n } catch { /* persistence is best-effort */ }\n },\n\n clear(): void {\n cache.clear();\n verificationCache.clear();\n try { persistence.persist(''); } catch { /* best-effort */ }\n },\n\n getVerification(eventId: string): VerificationCacheEntry | undefined {\n return verificationCache.get(eventId);\n },\n\n setVerification(eventId: string, result: VerificationCacheEntry): void {\n verificationCache.set(eventId, result);\n // Verification cache is in-memory only — no persistence needed\n // since manifest event IDs are immutable and re-fetching is cheap\n },\n\n hasVerification(eventId: string): boolean {\n return verificationCache.has(eventId);\n },\n\n clearVerifications(): void {\n verificationCache.clear();\n },\n };\n\n return self;\n}\n","/**\n * replay.ts — Replay detection module.\n *\n * Tracks seen event IDs and validates timestamps to prevent\n * duplicate event processing and replay attacks.\n */\n\nimport type { NostrEvent } from '@napplet/core';\n\n/**\n * Replay detection window in seconds — events older than this are rejected.\n * Relocated inline from the former @napplet/core compatibility shim\n * (DRIFT-CORE-06, Phase 24). Numeric value preserved unchanged from the shim\n * to hold behavioral parity with the Phase 23 test baseline.\n */\nconst REPLAY_WINDOW_SECONDS = 30;\n\n/**\n * Replay detection engine. Tracks seen event IDs and validates timestamps.\n *\n * @example\n * ```ts\n * const detector = createReplayDetector();\n * const reason = detector.check(event);\n * if (reason !== null) { // reject event }\n * ```\n */\nexport interface ReplayDetector {\n /**\n * Check if an event should be rejected as a replay.\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 NapHandler } from '@napplet/core';\nimport type { Capability } from '@kehto/acl/capabilities';\nimport type { Observation } from '@kehto/firewall';\n\nimport type {\n RuntimeAdapter, ConsentHandler, FirewallEvent,\n ServiceHandler, ServiceRegistry, ServiceInfo,\n} from './types.js';\nimport { notifyServiceWindowDestroyed } from './service-dispatch.js';\nimport { createSessionRegistry, type SessionRegistry } from './session-registry.js';\nimport { createAclState, type AclStateContainer } from './acl-state.js';\nimport { createFirewallState, type FirewallStateContainer } from './firewall-state.js';\nimport { createManifestCache, type ManifestCache } from './manifest-cache.js';\nimport { createReplayDetector, type ReplayDetector } from './replay.js';\nimport { createEventBuffer, RING_BUFFER_SIZE, type EventBuffer, type SubscriptionEntry } from './event-buffer.js';\nimport { createEnforceGate, createNapEnforceGate, resolveCapabilitiesNap, formatDenialReason } from './enforce.js';\nimport { createRelayHandler } from './relay-handler.js';\nimport { createIdentityHandler } from './identity-handler.js';\nimport { createIncRuntime, type IncRuntime } from './inc-handler.js';\nimport { createRuntimeDomainHandlers, type RuntimeDomainHandlers } from './domain-handlers.js';\n\n/**\n * The napplet protocol engine — handles NIP-5D NAP domain dispatch,\n * ACL enforcement, subscription lifecycle.\n *\n * @example\n * ```ts\n * import { createRuntime } from '@kehto/runtime';\n *\n * const runtime = createRuntime(hooks);\n * runtime.handleMessage('window-1', { type: 'relay.req', id: 'sub1', filters: [{ kinds: [1] }] });\n * ```\n */\nexport interface Runtime {\n /**\n * Handle an incoming NIP-5D NappletMessage envelope from a napplet.\n * The caller is responsible for identifying the source (windowId).\n * Legacy NIP-01 arrays are silently dropped (clean break — no dual-mode).\n *\n * @param windowId - The identifier of the napplet that sent the message\n * @param msg - The raw message (NappletMessage envelopes are processed; other types dropped)\n */\n handleMessage(windowId: string, msg: unknown): void;\n\n /**\n * Inject a shell-originated event into subscription delivery.\n *\n * @param topic - The event topic tag value\n * @param payload - The event content\n */\n injectEvent(topic: string, payload: unknown): void;\n\n /** Destroy the runtime, persisting state and clearing all internal state. */\n destroy(): void;\n\n /** Register a handler for consent requests on destructive signing kinds. */\n registerConsentHandler(handler: ConsentHandler): void;\n\n /**\n * Register a service handler dynamically after runtime creation.\n * If a handler is already registered for this name, it is replaced.\n *\n * @param name - Service name (e.g., 'audio', 'notifications')\n * @param handler - The service handler implementation\n */\n registerService(name: string, handler: ServiceHandler): void;\n\n /**\n * Unregister a service handler by name. No-op if the name is not registered.\n *\n * @param name - Service name to remove\n */\n unregisterService(name: string): void;\n\n /**\n * Clean up all state associated with a napplet window.\n * Removes subscriptions, pending state, and notifies service handlers.\n *\n * @param windowId - The window to clean up\n */\n destroyWindow(windowId: string): void;\n\n /** Access the identity registry (for shell adapter to read napplet session state). */\n readonly sessionRegistry: SessionRegistry;\n\n /** Access the ACL state container. */\n readonly aclState: AclStateContainer;\n\n /** Access the firewall state container (for tests to pre-set policy/rules). */\n readonly firewallState: FirewallStateContainer;\n\n /** Access the manifest cache. */\n readonly manifestCache: ManifestCache;\n}\n\ntype RuntimeNapHandlers = RuntimeDomainHandlers & {\n relay: (windowId: string, msg: NappletMessage) => void;\n identity: (windowId: string, msg: NappletMessage) => void;\n inc: (windowId: string, msg: NappletMessage) => void;\n};\n\ntype RuntimeInstanceContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n registeredServices: Map<string, ServiceInfo>;\n replayDetector: ReplayDetector;\n subscriptions: Map<string, SubscriptionEntry>;\n eventBuffer: EventBuffer;\n incRuntime: IncRuntime;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n firewallState: FirewallStateContainer;\n manifestCache: ManifestCache;\n consentHandlerRef: { current: ConsentHandler | null };\n handleMessage: Runtime['handleMessage'];\n};\n\nfunction createRegisteredServices(serviceRegistry: ServiceRegistry): Map<string, ServiceInfo> {\n const registeredServices = new Map<string, ServiceInfo>();\n for (const [name, handler] of Object.entries(serviceRegistry)) {\n registeredServices.set(name, {\n name: handler.descriptor.name,\n version: handler.descriptor.version,\n description: handler.descriptor.description,\n });\n }\n return registeredServices;\n}\n\nfunction createNapEnvelopeDispatcher(handlers: RuntimeNapHandlers): (windowId: string, envelope: NappletMessage) => void {\n let currentWindowId: string | null = null;\n const napDispatch = createDispatch();\n const adapt = (handler: (windowId: string, msg: NappletMessage) => void): NapHandler => (msg) => {\n if (currentWindowId !== null) handler(currentWindowId, msg);\n };\n\n napDispatch.registerNap('relay', adapt(handlers.relay));\n napDispatch.registerNap('identity', adapt(handlers.identity));\n napDispatch.registerNap('keys', adapt(handlers.keys));\n napDispatch.registerNap('media', adapt(handlers.media));\n napDispatch.registerNap('notify', adapt(handlers.notify));\n napDispatch.registerNap('storage', adapt(handlers.storage));\n napDispatch.registerNap('inc', adapt(handlers.inc));\n napDispatch.registerNap('theme', adapt(handlers.theme));\n napDispatch.registerNap('config', adapt(handlers.config));\n napDispatch.registerNap('resource', adapt(handlers.resource));\n napDispatch.registerNap('cvm', adapt(handlers.cvm));\n napDispatch.registerNap('outbox', adapt(handlers.outbox));\n napDispatch.registerNap('upload', adapt(handlers.upload));\n napDispatch.registerNap('intent', adapt(handlers.intent));\n napDispatch.registerNap('link', adapt(handlers.link));\n napDispatch.registerNap('common', adapt(handlers.common));\n napDispatch.registerNap('lists', adapt(handlers.lists));\n napDispatch.registerNap('serial', adapt(handlers.serial));\n napDispatch.registerNap('ble', adapt(handlers.ble));\n napDispatch.registerNap('webrtc', adapt(handlers.webrtc));\n\n return (windowId, envelope) => {\n currentWindowId = windowId;\n try {\n napDispatch.dispatch(envelope);\n } finally {\n currentWindowId = null;\n }\n };\n}\n\n/**\n * Compute the UTF-8 byte length of a string without TextEncoder (ES2022-safe).\n * Mirrors the same helper in state-handler.ts to avoid cross-file import.\n */\nfunction utf8ByteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Extract kind and byte-size from a message envelope (publish-style ops only).\n * Guards against malformed envelopes — returns undefined fields on any failure.\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @returns `{ kind?, size? }` — both optional; undefined when not a publish-style op.\n */\nfunction extractKindSize(envelope: NappletMessage): { kind?: number; size?: number } {\n const ev = (envelope as NappletMessage & { event?: unknown }).event;\n if (typeof ev !== 'object' || ev === null) return {};\n const kind = typeof (ev as { kind?: unknown }).kind === 'number'\n ? (ev as { kind: number }).kind\n : undefined;\n let size: number | undefined;\n try {\n size = utf8ByteLength(JSON.stringify(ev));\n } catch { /* malformed event — size stays undefined */ }\n return { kind, size };\n}\n\n/**\n * Build a normalized Observation from a message envelope and runtime context.\n * The runtime owns the clock (Date.now()); the pure engine never reads it.\n * Focus is sourced exclusively via getFocusContext (FOCUS-01) — never napplet-self-reported.\n * When getFocusContext is absent, defaults to `{ focused: true }` (safe relax-only default).\n *\n * @param envelope - Incoming NappletMessage envelope.\n * @param windowId - Source napplet window identifier.\n * @param senderCap - Resolved sender capability string (or null).\n * @param sessionRegistry - Used to resolve dTag and registeredAt for the window.\n * @param getFocusContext - Optional shell-supplied focus query hook.\n * @returns A complete Observation ready for evaluate().\n */\nfunction buildObservation(\n envelope: NappletMessage,\n windowId: string,\n senderCap: string | null,\n sessionRegistry: SessionRegistry,\n getFocusContext?: RuntimeAdapter['getFocusContext'],\n): Observation {\n const now = Date.now();\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n const napplet = entry?.dTag ?? '';\n const initElapsedMs = now - (entry?.registeredAt ?? now);\n const focus = getFocusContext?.(windowId) ?? { focused: true };\n const opClass = senderCap ?? envelope.type;\n const { kind, size } = extractKindSize(envelope);\n return { napplet, opClass, kind, size, initElapsedMs, focused: focus.focused, msSinceFocusGain: focus.msSinceFocusGain, now };\n}\n\n/** Configuration for createFirewallGate. */\ninterface FirewallGateConfig {\n firewallState: FirewallStateContainer;\n sessionRegistry: SessionRegistry;\n hooks: RuntimeAdapter;\n fireConsent: (windowId: string, napplet: string) => void;\n}\n\n/**\n * Create the firewall gate closure to inject into createMessageHandler.\n * Returns 'dispatch' to allow the message through or 'drop' to reject it.\n *\n * Decision→action mapping (RUNTIME-01, RUNTIME-04, POLICY-02):\n * - reject → error envelope + drop (no dispatch)\n * - prompt → error envelope + fireConsent + drop (ask path)\n * - pass + flag → onFirewallEvent audit + dispatch\n * - pass (ignore/allow) → dispatch (no audit)\n *\n * @param config - Gate configuration (firewallState, sessionRegistry, hooks, fireConsent).\n * @returns A gate function `(windowId, envelope, senderCap) => 'dispatch' | 'drop'`.\n */\nfunction createFirewallGate(config: FirewallGateConfig): (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop' {\n const { firewallState, sessionRegistry, hooks, fireConsent } = config;\n\n return function firewallGate(windowId: string, envelope: NappletMessage, senderCap: string | null): 'dispatch' | 'drop' {\n const obs = buildObservation(envelope, windowId, senderCap, sessionRegistry, hooks.getFocusContext);\n const result = firewallState.evaluate(obs);\n const { decision, action, ruleId, reason } = result;\n const napplet = obs.napplet;\n const opClass = obs.opClass;\n\n if (decision === 'reject' || decision === 'prompt') {\n // Mirror the ACL denial envelope shaping (runtime.ts ACL path):\n // storage envelopes → `.result`; all others → `.error` (T-81-03: no internals leaked)\n const id = (envelope as NappletMessage & { id?: string }).id ?? '';\n const isStorageEnvelope = envelope.type.startsWith('storage.');\n const type = isStorageEnvelope ? `${envelope.type}.result` : `${envelope.type}.error`;\n hooks.sendToNapplet(windowId, { type, id, error: `firewall: ${reason}` } as NappletMessage);\n\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n\n if (decision === 'prompt') {\n // POLICY-02: reject current message AND fire async consent prompt\n fireConsent(windowId, napplet);\n }\n return 'drop';\n }\n\n // decision === 'pass'\n if (action === 'flag') {\n // RUNTIME-04: flag → audit + dispatch (message is NOT dropped)\n hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope } as FirewallEvent);\n }\n return 'dispatch';\n };\n}\n\nfunction createMessageHandler(\n hooks: RuntimeAdapter,\n enforceNap: ReturnType<typeof createNapEnforceGate>,\n dispatchNapEnvelope: (windowId: string, envelope: NappletMessage) => void,\n firewallGate: (windowId: string, envelope: NappletMessage, senderCap: string | null) => 'dispatch' | 'drop',\n): Runtime['handleMessage'] {\n return (windowId: string, msg: unknown): void => {\n if (typeof msg !== 'object' || msg === null || !('type' in msg)) return;\n const envelope = msg as NappletMessage;\n const dotIdx = envelope.type.indexOf('.');\n if (dotIdx === -1) return;\n\n const caps = resolveCapabilitiesNap(envelope);\n if (caps.senderCap) {\n const result = enforceNap(windowId, caps.senderCap as Capability, envelope);\n if (!result.allowed) {\n const 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 const verdict = firewallGate(windowId, envelope, caps.senderCap);\n if (verdict === 'drop') return;\n\n dispatchNapEnvelope(windowId, envelope);\n };\n}\n\nfunction createInjectedEvent(hooks: RuntimeAdapter, topic: string, payload: unknown): NostrEvent {\n const uuid = hooks.crypto.randomUUID().replace(/-/g, '').slice(0, 64).padEnd(64, '0');\n return {\n id: uuid,\n pubkey: '0'.repeat(64),\n created_at: Math.floor(Date.now() / 1000),\n kind: 29000,\n tags: [['t', topic]],\n content: JSON.stringify(payload),\n sig: '0'.repeat(128),\n };\n}\n\nfunction createRuntimeInstance(context: RuntimeInstanceContext): Runtime {\n const {\n aclState, firewallState, eventBuffer, hooks, incRuntime, manifestCache, registeredServices,\n replayDetector, serviceRegistry, sessionRegistry, subscriptions, consentHandlerRef,\n } = context;\n const undeclaredServiceConsents = new Set<string>();\n\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 firewallState.persist();\n replayDetector.clear();\n subscriptions.clear();\n incRuntime.clear();\n eventBuffer.clear();\n registeredServices.clear();\n undeclaredServiceConsents.clear();\n },\n registerConsentHandler(handler: ConsentHandler): void {\n consentHandlerRef.current = handler;\n },\n registerService(name: string, handler: ServiceHandler): void {\n 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 incRuntime.destroyWindow(windowId);\n notifyServiceWindowDestroyed(windowId, serviceRegistry);\n },\n get sessionRegistry() { return sessionRegistry; },\n get aclState() { return aclState; },\n get firewallState() { return firewallState; },\n get manifestCache() { return manifestCache; },\n };\n}\n\n/**\n * Create a runtime instance with dependency injection via hooks.\n *\n * @param hooks - Host application provides relay pool, auth, config, etc.\n * @returns A Runtime instance ready to handle napplet messages\n *\n * @example\n * ```ts\n * const runtime = createRuntime(hooks);\n * // On incoming message from napplet:\n * runtime.handleMessage(windowId, { type: 'relay.req', id: 'sub1', filters: [] });\n * ```\n */\nexport function createRuntime(hooks: RuntimeAdapter): Runtime {\n const subscriptions = new Map<string, SubscriptionEntry>();\n const serviceRegistry: ServiceRegistry = { ...hooks.services };\n const registeredServices = createRegisteredServices(serviceRegistry);\n const sessionRegistry = createSessionRegistry(hooks.onPendingUpdate);\n const aclState = createAclState(hooks.aclPersistence);\n const firewallState = createFirewallState(hooks.firewallPersistence);\n const manifestCache = createManifestCache(hooks.manifestPersistence);\n const replayDetector = createReplayDetector(\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().replayWindowSeconds\n : undefined,\n );\n\n // Shared consent handler ref — lifted to createRuntime scope so the firewall gate\n // can fire it. The gate is built here (outer scope) but registerConsentHandler is\n // called on the runtime instance (inner scope); the ref bridges both closures.\n const consentHandlerRef: { current: ConsentHandler | null } = { current: null };\n\n // fireConsent: invoked by the gate on 'prompt' decisions (POLICY-02 ask path).\n // On resolution the user's choice is persisted as a per-napplet policy so subsequent\n // messages are NOT re-prompted (T-81-04: bounded consent spam prevention).\n const fireConsent = (windowId: string, napplet: string): void => {\n const handler = consentHandlerRef.current;\n if (!handler) return;\n handler({\n type: 'firewall-policy',\n windowId,\n napplet,\n pubkey: '',\n resolve: (allowed: boolean): void => {\n firewallState.setPolicy(napplet, allowed ? 'allow' : 'deny');\n firewallState.persist();\n },\n });\n };\n\n const enforce = createEnforceGate({\n checkAcl: (pubkey, dTag, aggregateHash, capability) =>\n aclState.check(pubkey, dTag, aggregateHash, capability),\n resolveIdentity: (pubkey) => {\n const entry = sessionRegistry.getEntry(pubkey);\n return entry ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash } : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\n\n const enforceNap = createNapEnforceGate({\n checkAcl: (pubkey, dTag, aggregateHash, capability) =>\n aclState.check(pubkey, dTag, aggregateHash, capability),\n resolveIdentityByWindowId: (windowId) => {\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n return entry\n ? { dTag: entry.dTag, aggregateHash: entry.aggregateHash }\n : undefined;\n },\n onAclCheck: hooks.onAclCheck,\n });\n\n const firewallGate = createFirewallGate({ firewallState, sessionRegistry, hooks, fireConsent });\n\n const eventBuffer = createEventBuffer(\n hooks.sendToNapplet,\n sessionRegistry,\n enforce,\n subscriptions,\n hooks.getConfigOverrides\n ? () => hooks.getConfigOverrides!().ringBufferSize ?? RING_BUFFER_SIZE\n : undefined,\n );\n\n aclState.load();\n firewallState.load();\n manifestCache.load();\n\n const incRuntime = createIncRuntime(hooks, sessionRegistry);\n const domainHandlers = createRuntimeDomainHandlers({ hooks, serviceRegistry, sessionRegistry, aclState });\n const dispatchNapEnvelope = createNapEnvelopeDispatcher({\n relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),\n identity: createIdentityHandler({ hooks, serviceRegistry }),\n inc: incRuntime.handleMessage,\n ...domainHandlers,\n });\n const handleMessage = createMessageHandler(hooks, enforceNap, dispatchNapEnvelope, firewallGate);\n\n return createRuntimeInstance({\n hooks,\n serviceRegistry,\n registeredServices,\n replayDetector,\n subscriptions,\n eventBuffer,\n incRuntime,\n sessionRegistry,\n aclState,\n firewallState,\n manifestCache,\n consentHandlerRef,\n handleMessage,\n });\n}\n","\nimport type { NappletMessage } from '@napplet/core';\nimport type { ServiceRegistry, SendToNapplet } from './types.js';\n\n/**\n * Route a NappletMessage envelope to the matching service handler.\n *\n * NAP-domain services are routed by the domain prefix of message.type\n * (e.g., 'signer.signEvent' -> 'signer' service).\n *\n * INC-routed services receive inc.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 // NAP-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 // INC-routed services: audio and notifications receive inc.emit with topic prefix\n const incMessage = message as NappletMessage & { topic?: unknown };\n if (message.type === 'inc.emit' && typeof incMessage.topic === 'string') {\n const prefix = incMessage.topic.split(':')[0];\n const incHandler = services[prefix];\n if (incHandler) {\n incHandler.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/nap/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 relay?: string;\n};\n\ntype RelayHandlerContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n subscriptions: Map<string, SubscriptionEntry>;\n eventBuffer: EventBuffer;\n replayDetector: ReplayDetector;\n};\n\nexport type RelayHandler = (windowId: string, msg: NappletMessage) => void;\n\nexport function createRelayHandler(context: RelayHandlerContext): RelayHandler {\n return function handleRelayMessage(windowId: string, msg: NappletMessage): void {\n const m = msg as RuntimeRelayMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'subscribe':\n handleRelaySubscribe(context, windowId, msg, m);\n return;\n case 'close':\n handleRelayClose(context, windowId, msg, m);\n return;\n case 'publish':\n handleRelayPublish(context, windowId, 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 const relayHint = typeof m.relay === 'string' && m.relay.length > 0 ? m.relay : undefined;\n deliverFromRuntimeBackends(context, windowId, subId, subKey, filters, isShellKind, deliver, relayHint);\n}\n\nfunction deliverFromRuntimeBackends(\n context: RelayHandlerContext,\n windowId: string,\n subId: string,\n subKey: string,\n filters: NostrFilter[],\n isShellKind: boolean,\n deliver: (event: NostrEvent) => void,\n relayHint?: string,\n): void {\n const { hooks } = context;\n const cache = hooks.cache;\n\n if (cache?.isAvailable() && !isShellKind) {\n cache.query(filters)\n .then((cachedEvents) => {\n for (const event of cachedEvents) deliver(event);\n })\n .catch(() => {});\n }\n\n const pool = hooks.relayPool;\n if (!pool?.isAvailable() && !isShellKind) {\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n return;\n }\n if (!pool?.isAvailable() || isShellKind) return;\n\n const relayUrls = relayHint ? [relayHint] : pool.selectRelayTier(filters);\n let eoseSent = false;\n const eoseFallbackTimer = setTimeout(() => {\n if (!eoseSent) {\n eoseSent = true;\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n }\n }, 15_000);\n\n const subscription = pool.subscribe(filters, (item) => {\n if (item === 'EOSE') {\n clearTimeout(eoseFallbackTimer);\n if (!eoseSent) {\n eoseSent = true;\n hooks.sendToNapplet(windowId, { type: 'relay.eose', subId } as NappletMessage);\n }\n return;\n }\n 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 { IncMessage } from '@napplet/nap/inc/types';\n\nimport type { SessionRegistry } from './session-registry.js';\nimport type { RuntimeAdapter } from './types.js';\n\ninterface IncChannel {\n channelId: string;\n peerA: string;\n peerB: string;\n}\n\ntype RuntimeIncMessage = IncMessage & {\n id?: string;\n topic?: string;\n payload?: unknown;\n target?: string;\n channelId?: string;\n};\n\ntype IncState = {\n subscriptions: Map<string, Set<string>>;\n channels: Map<string, IncChannel>;\n channelsByWindow: Map<string, Set<string>>;\n};\n\nexport interface IncRuntime {\n handleMessage(windowId: string, msg: NappletMessage): void;\n destroyWindow(windowId: string): void;\n clear(): void;\n}\n\nexport function createIncRuntime(hooks: RuntimeAdapter, sessionRegistry: SessionRegistry): IncRuntime {\n const state: IncState = {\n subscriptions: new Map(),\n channels: new Map(),\n channelsByWindow: new Map(),\n };\n\n return {\n handleMessage(windowId: string, msg: NappletMessage): void {\n handleIncMessage(state, hooks, sessionRegistry, 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: IncState, channelId: string, peerA: string, peerB: string): void {\n state.channels.set(channelId, { channelId, peerA, peerB });\n for (const windowId of [peerA, peerB]) {\n let set = state.channelsByWindow.get(windowId);\n if (!set) {\n set = new Set();\n state.channelsByWindow.set(windowId, set);\n }\n set.add(channelId);\n }\n}\n\nfunction removeChannel(state: IncState, channelId: string): void {\n const channel = state.channels.get(channelId);\n if (!channel) return;\n state.channels.delete(channelId);\n for (const windowId of [channel.peerA, channel.peerB]) {\n const set = state.channelsByWindow.get(windowId);\n if (!set) continue;\n set.delete(channelId);\n if (set.size === 0) state.channelsByWindow.delete(windowId);\n }\n}\n\nfunction peerOf(state: IncState, channelId: string, self: string): string | null {\n const channel = state.channels.get(channelId);\n if (!channel) return null;\n if (channel.peerA === self) return channel.peerB;\n if (channel.peerB === self) return channel.peerA;\n return null;\n}\n\nfunction resolveTarget(sessionRegistry: SessionRegistry, target: string): string | null {\n 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 handleIncMessage(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n msg: NappletMessage,\n): void {\n const m = msg as RuntimeIncMessage;\n const dotIdx = msg.type.indexOf('.');\n const action = msg.type.slice(dotIdx + 1);\n\n switch (action) {\n case 'emit': handleEmit(state, hooks, 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(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): 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: 'inc.event', topic, payload: m.payload, sender: windowId } as NappletMessage);\n }\n }\n}\n\nfunction handleSubscribe(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const id = m.id ?? '';\n const topic = m.topic ?? '';\n if (!topic) {\n hooks.sendToNapplet(windowId, { type: 'inc.subscribe.result', id, error: 'missing topic' } as NappletMessage);\n return;\n }\n let subscriptions = state.subscriptions.get(topic);\n if (!subscriptions) {\n subscriptions = new Set();\n state.subscriptions.set(topic, subscriptions);\n }\n subscriptions.add(windowId);\n hooks.sendToNapplet(windowId, { type: 'inc.subscribe.result', id } as NappletMessage);\n}\n\nfunction handleUnsubscribe(state: IncState, windowId: string, m: RuntimeIncMessage): void {\n const topic = m.topic ?? '';\n if (!topic) return;\n const subscriptions = state.subscriptions.get(topic);\n if (!subscriptions) return;\n subscriptions.delete(windowId);\n if (subscriptions.size === 0) state.subscriptions.delete(topic);\n}\n\nfunction handleChannelOpen(\n state: IncState,\n hooks: RuntimeAdapter,\n sessionRegistry: SessionRegistry,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const id = m.id ?? '';\n const peerWindow = resolveTarget(sessionRegistry, m.target ?? '');\n if (!peerWindow) {\n hooks.sendToNapplet(windowId, { type: 'inc.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: 'inc.channel.open.result', id, channelId, peer: peerWindow } as NappletMessage);\n}\n\nfunction handleChannelEmit(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const peer = peerOf(state, m.channelId ?? '', windowId);\n if (peer) {\n hooks.sendToNapplet(peer, { type: 'inc.channel.event', channelId: m.channelId ?? '', sender: windowId, payload: m.payload } as NappletMessage);\n }\n}\n\nfunction handleChannelBroadcast(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): 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) {\n hooks.sendToNapplet(peer, { type: 'inc.channel.event', channelId, sender: windowId, payload: m.payload } as NappletMessage);\n }\n }\n}\n\nfunction handleChannelList(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channels = [];\n const set = state.channelsByWindow.get(windowId);\n if (set) {\n for (const channelId of set) {\n const peer = peerOf(state, channelId, windowId);\n if (peer) channels.push({ id: channelId, peer });\n }\n }\n hooks.sendToNapplet(windowId, { type: 'inc.channel.list.result', id: m.id ?? '', channels } as NappletMessage);\n}\n\nfunction handleChannelClose(\n state: IncState,\n hooks: RuntimeAdapter,\n windowId: string,\n m: RuntimeIncMessage,\n): void {\n const channelId = m.channelId ?? '';\n const peer = peerOf(state, channelId, windowId);\n if (!peer) return;\n hooks.sendToNapplet(windowId, { type: 'inc.channel.closed', channelId } as NappletMessage);\n hooks.sendToNapplet(peer, { type: 'inc.channel.closed', channelId } as NappletMessage);\n removeChannel(state, channelId);\n}\n\nfunction removeWindowSubscriptions(state: IncState, windowId: string): void {\n for (const [topic, subscriptions] of state.subscriptions) {\n subscriptions.delete(windowId);\n if (subscriptions.size === 0) state.subscriptions.delete(topic);\n }\n}\n\nfunction removeWindowChannels(state: IncState, hooks: RuntimeAdapter, windowId: string): void {\n const channelIds = state.channelsByWindow.get(windowId);\n if (!channelIds) return;\n for (const channelId of Array.from(channelIds)) {\n const peer = peerOf(state, channelId, windowId);\n if (peer) {\n hooks.sendToNapplet(peer, { type: 'inc.channel.closed', channelId } as NappletMessage);\n }\n removeChannel(state, channelId);\n }\n}\n","/**\n * state-handler.ts — Storage NAP request handler using persistence hooks.\n *\n * Handles napplet storage operations (get, set, remove, keys) via the\n * canonical `@napplet/nap/storage` NIP-5D envelope surface. Delegates\n * storage to StatePersistence. No localStorage, no legacy NIP-01 dispatch.\n *\n * Per-instance scope (NAP-STORAGE, napplet/naps#3): every request carries an\n * optional `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"`\n * folds a stable per-window discriminator (`@i/<windowId>:`) into the key so\n * that multiple open windows of the *same* napplet keep isolated, independently\n * persisted state. Instancing is a property of the data (per call), not the\n * napplet — there is no napplet-wide mode. The napplet never sees or names an\n * instance identifier; it sets `scope` and trusts the shell's Unique + Stable\n * guarantees. `\"shared\"` (or absent) addresses the napplet-wide namespace,\n * byte-identical to the historical behavior.\n */\n\nimport type { NappletMessage } from '@napplet/core';\nimport type { StorageMessage } from '@napplet/nap/storage/types';\nimport type { SendToNapplet, StatePersistence } from './types.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport type { AclStateContainer } from './acl-state.js';\n\n/** Canonical NAP-STORAGE scope values. `scope` absent ⇔ `\"shared\"`. */\ntype StorageScope = 'shared' | 'instance';\n\n/**\n * Reserved key segment that marks the per-instance sub-namespace inside a\n * napplet's `(dTag, aggregateHash)` bucket. Server-side only — napplets never\n * see it. Used for `scope: \"instance\"` requests (NAP-STORAGE per-instance scope).\n */\nconst INSTANCE_MARKER = '@i/';\n\n/**\n * Build the per-window instance sub-namespace segment: `@i/<windowId>:`.\n *\n * The discriminator is derived from the runtime's `windowId`, which the shell\n * keeps stable across reload / workspace restore, so per-instance storage\n * persists across sessions (the spec's \"Stable\" guarantee). Two distinct\n * windows of the same napplet get distinct segments (the \"Unique\" guarantee).\n * This format is internal and never exposed to the napplet.\n */\nfunction instanceSegment(windowId: string): string {\n return `${INSTANCE_MARKER}${windowId}:`;\n}\n\n/**\n * Build the storage key for a napplet user key.\n *\n * For `scope: \"shared\"` (default) this is byte-identical to the historical key.\n * For `scope: \"instance\"` the runtime folds in the opaque per-window segment —\n * transparently, with no napplet involvement (the napplet only sets the wire\n * field; it never sees the resulting key shape).\n */\nfunction scopedKey(\n dTag: string,\n aggregateHash: string,\n userKey: string,\n instance = false,\n windowId = '',\n): string {\n const segment = instance ? instanceSegment(windowId) : '';\n return `napplet-state:${dTag}:${aggregateHash}:${segment}${userKey}`;\n}\n\n/** Build a legacy scoped key that includes pubkey (for migration reads). */\nfunction legacyScopedKey(pubkey: string, dTag: string, aggregateHash: string, userKey: string): string {\n return `napplet-state:${pubkey}:${dTag}:${aggregateHash}:${userKey}`;\n}\n\n/** Compute byte length of a UTF-8 string without TextEncoder (ES2022-safe). */\nfunction byteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const c = str.charCodeAt(i);\n if (c < 0x80) bytes += 1;\n else if (c < 0x800) bytes += 2;\n else if (c < 0xd800 || c >= 0xe000) bytes += 3;\n else { i++; bytes += 4; } // surrogate pair\n }\n return bytes;\n}\n\n/**\n * Handle a NIP-5D NAP storage message from a napplet.\n * Routes to the canonical four `@napplet/nap/storage` actions:\n * - `storage.get` → `storage.get.result` `{ value: string | null }`\n * - `storage.set` → `storage.set.result` `{ ok: boolean }` (canonical only checks `error`)\n * - `storage.remove` → `storage.remove.result` `{ ok: boolean }`\n * - `storage.keys` → `storage.keys.result` `{ keys: string[] }`\n *\n * `storage.clear` is NOT in the canonical `@napplet/nap/storage` union (it was a\n * kehto unilateral extension); attempts produce a `storage.clear.result` envelope\n * with an `error` field set. Internal lifecycle cleanup still uses the\n * `cleanupNappState()` helper below — it is not napplet-reachable.\n *\n * **Per-instance scope (NAP-STORAGE / napplet/naps#3):** each request MAY carry\n * `scope: \"shared\" | \"instance\"` (default `\"shared\"`). `\"instance\"` addresses a\n * per-window sub-namespace keyed by `windowId`, so multiple windows of the same\n * napplet keep isolated state; `\"shared\"` (or absent) addresses the napplet-wide\n * namespace, byte-identical to historical behavior. An unrecognized `scope` value\n * is an invalid request and produces a `.result` envelope with `error` set.\n *\n * **Deviation note (Phase 15 to decide):** Set/remove results emit both `ok`\n * (legacy compat) and an `error` field on failure. Canonical `@napplet/nap/storage`\n * only specifies the optional `error`; napplets check `!result.error` for success.\n * Emitting `ok` preserves backward compatibility with existing in-tree callers.\n * Phase 15 (v1.2 release prep) decides whether to drop `ok` pre-release.\n *\n * @param windowId - The window identifier of the requesting napplet\n * @param msg - The NappletMessage containing the storage request\n * @param sendToNapplet - Transport function to send responses\n * @param sessionRegistry - Identity registry for looking up napplet session identity\n * @param aclState - ACL state for quota checks\n * @param statePersistence - State storage backend\n *\n * @example\n * ```ts\n * import { handleStorageNap } from '@kehto/runtime';\n *\n * handleStorageNap(windowId, { type: 'storage.get', id: 'q1', key: 'draft' },\n * sendToNapplet, sessionRegistry, aclState, statePersistence);\n * ```\n */\nexport function handleStorageNap(\n windowId: string,\n msg: NappletMessage,\n sendToNapplet: SendToNapplet,\n sessionRegistry: SessionRegistry,\n aclState: AclStateContainer,\n statePersistence: StatePersistence,\n): void {\n const m = msg as StorageMessage & {\n id?: string;\n key?: string;\n value?: string;\n scope?: string;\n };\n const id = m.id ?? '';\n const action = msg.type.split('.')[1];\n\n function sendResult(payload: Record<string, unknown>): void {\n sendToNapplet(windowId, { type: `${msg.type}.result`, id, ...payload } as NappletMessage);\n }\n function sendErrorNap(error: string): void {\n sendToNapplet(windowId, { type: `${msg.type}.result`, id, error } as NappletMessage);\n }\n\n // Identity lookup via windowId (NIP-5D path)\n const entry = sessionRegistry.getEntryByWindowId(windowId);\n if (!entry) { sendErrorNap('not registered'); return; }\n\n const { dTag, aggregateHash, pubkey } = entry;\n const prefix = `napplet-state:${dTag}:${aggregateHash}:`;\n const legacyPrefix = pubkey ? `napplet-state:${pubkey}:${dTag}:${aggregateHash}:` : '';\n\n // NAP-STORAGE per-call scope. Absent ⇔ \"shared\" (byte-identical to history).\n // An unrecognized value is an invalid request — the shell MUST return an error.\n if (m.scope !== undefined && m.scope !== 'shared' && m.scope !== 'instance') {\n sendErrorNap(`invalid scope: ${String(m.scope)} (expected \"shared\" or \"instance\")`);\n return;\n }\n const scope: StorageScope = m.scope === 'instance' ? 'instance' : 'shared';\n const isInstance = scope === 'instance';\n // The per-window sub-namespace for this request, when instance-scoped.\n const instancePrefix = `${prefix}${instanceSegment(windowId)}`;\n const keyFor = (userKey: string): string =>\n scopedKey(dTag, aggregateHash, userKey, isInstance, windowId);\n\n switch (action) {\n case 'get': {\n const key = m.key as string;\n if (!key) { sendErrorNap('missing key'); return; }\n // Instance scope addresses a fresh per-window namespace with no legacy data —\n // read only the instance key. Shared scope keeps the triple-read migration.\n if (isInstance) {\n sendResult({ value: statePersistence.get(keyFor(key)) });\n break;\n }\n const newKey = scopedKey(dTag, aggregateHash, key);\n // Triple-read for migration: new format, legacy-with-pubkey, old prefix\n let result = statePersistence.get(newKey);\n if (result === null && pubkey) {\n result = statePersistence.get(legacyScopedKey(pubkey, dTag, aggregateHash, key));\n }\n if (result === null && pubkey) {\n result = statePersistence.get(`napp-state:${pubkey}:${dTag}:${aggregateHash}:${key}`);\n }\n // Canonical @napplet/nap/storage: `value: string | null` — null ⇔ missing.\n sendResult({ value: result });\n break;\n }\n case 'set': {\n const key = m.key as string;\n const value = (m.value as string) ?? '';\n if (!key) { sendErrorNap('missing key'); return; }\n const quota = aclState.getStateQuota(pubkey ?? '', dTag, aggregateHash);\n const sk = keyFor(key);\n const newWriteBytes = byteLength(sk + value);\n // Quota is per napplet identity: `prefix` spans both the shared namespace and\n // every per-instance sub-key, so instance writes draw from the same budget.\n const existingBytes = statePersistence.calculateBytes(prefix, key);\n if (existingBytes + newWriteBytes > quota) {\n sendErrorNap(`quota exceeded: napplet state limit is ${quota} bytes`);\n return;\n }\n const success = statePersistence.set(sk, value);\n sendResult({ ok: success });\n break;\n }\n case 'remove': {\n const key = m.key as string;\n if (!key) { sendErrorNap('missing key'); return; }\n statePersistence.remove(keyFor(key));\n // legacyPrefix exists only while `pubkey` is non-empty (legacy AUTH sessions).\n // Suppress \"unused binding\" warnings: we intentionally retain the computation\n // so future migration lands at call sites, not here.\n void legacyPrefix;\n sendResult({ ok: true });\n break;\n }\n case 'clear': {\n // storage.clear was a kehto unilateral extension; it is NOT in the canonical\n // @napplet/nap/storage union. Napplets hitting this branch receive a\n // storage.clear.result envelope with the error field set. Internal lifecycle\n // cleanup uses cleanupNappState() below (not napplet-reachable).\n sendErrorNap('storage.clear is not in @napplet/nap/storage; action not supported');\n break;\n }\n case 'keys': {\n // Instance scope sees only this window's sub-namespace.\n if (isInstance) {\n const instKeys = statePersistence.keys(instancePrefix);\n const instanceKeySet = new Set<string>();\n for (const k of instKeys) {\n instanceKeySet.add(k.startsWith(instancePrefix) ? k.slice(instancePrefix.length) : k);\n }\n sendResult({ keys: Array.from(instanceKeySet) });\n break;\n }\n const newKeys = statePersistence.keys(prefix);\n const legacyKeys = legacyPrefix ? statePersistence.keys(legacyPrefix) : [];\n const userKeySet = new Set<string>();\n // Shared scope excludes per-instance sub-keys (reserved `@i/` marker) so an\n // instance-scoped napplet's per-window data never leaks into a shared listing.\n for (const k of newKeys) {\n if (!k.startsWith(prefix)) { userKeySet.add(k); continue; }\n const userKey = k.slice(prefix.length);\n if (userKey.startsWith(INSTANCE_MARKER)) continue;\n userKeySet.add(userKey);\n }\n for (const k of legacyKeys) userKeySet.add(k.startsWith(legacyPrefix) ? k.slice(legacyPrefix.length) : k);\n sendResult({ keys: Array.from(userKeySet) });\n break;\n }\n default:\n sendErrorNap(`unknown storage action: ${action}`);\n break;\n }\n}\n\n/**\n * Remove all state entries for a napplet identity.\n * Clears both new-format and legacy-format keys for completeness.\n * Used during napplet cleanup when a window is closed.\n *\n * @param pubkey - The napplet's pubkey (needed for legacy key cleanup)\n * @param dTag - The napplet's dTag\n * @param aggregateHash - The napplet's build hash\n * @param statePersistence - State storage backend\n *\n * @example\n * ```ts\n * import { cleanupNappState } from '@kehto/runtime';\n *\n * cleanupNappState(pubkey, dTag, aggregateHash, statePersistence);\n * ```\n */\nexport function cleanupNappState(\n pubkey: string,\n dTag: string,\n aggregateHash: string,\n statePersistence: StatePersistence,\n): void {\n // Clear new format\n const prefix = `napplet-state:${dTag}:${aggregateHash}:`;\n statePersistence.clear(prefix);\n // Clear legacy format (includes pubkey)\n const legacyPrefix = `napplet-state:${pubkey}:${dTag}:${aggregateHash}:`;\n statePersistence.clear(legacyPrefix);\n}\n","import type { NappletMessage } from '@napplet/core';\n\nimport type { AclStateContainer } from './acl-state.js';\nimport type { SessionRegistry } from './session-registry.js';\nimport { handleStorageNap } from './state-handler.js';\nimport type { RuntimeAdapter, ServiceRegistry } from './types.js';\n\ntype DomainHandler = (windowId: string, msg: NappletMessage) => void;\n\ntype RuntimeDomainContext = {\n hooks: RuntimeAdapter;\n serviceRegistry: ServiceRegistry;\n sessionRegistry: SessionRegistry;\n aclState: AclStateContainer;\n};\n\nexport type RuntimeDomainHandlers = {\n storage: DomainHandler;\n media: DomainHandler;\n keys: DomainHandler;\n notify: DomainHandler;\n theme: DomainHandler;\n config: DomainHandler;\n resource: DomainHandler;\n cvm: DomainHandler;\n outbox: DomainHandler;\n upload: DomainHandler;\n intent: DomainHandler;\n link: DomainHandler;\n common: DomainHandler;\n lists: DomainHandler;\n serial: DomainHandler;\n ble: DomainHandler;\n webrtc: DomainHandler;\n};\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 intent: (windowId, msg) => handleServiceOnlyMessage(context, 'intent', windowId, msg),\n link: (windowId, msg) => handleServiceOnlyMessage(context, 'link', windowId, msg),\n common: (windowId, msg) => handleServiceOnlyMessage(context, 'common', windowId, msg),\n lists: (windowId, msg) => handleServiceOnlyMessage(context, 'lists', windowId, msg),\n serial: (windowId, msg) => handleServiceOnlyMessage(context, 'serial', windowId, msg),\n ble: (windowId, msg) => handleServiceOnlyMessage(context, 'ble', windowId, msg),\n webrtc: (windowId, msg) => handleServiceOnlyMessage(context, 'webrtc', windowId, msg),\n };\n}\n\nfunction handleStorageMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { aclState, hooks, sessionRegistry } = context;\n handleStorageNap(windowId, msg, hooks.sendToNapplet, sessionRegistry, aclState, hooks.statePersistence);\n}\n\nfunction handleMediaMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = context;\n const mediaService = serviceRegistry['media'];\n if (mediaService) {\n mediaService.handleMessage(windowId, msg, (resp: NappletMessage) => hooks.sendToNapplet(windowId, resp));\n return;\n }\n if (msg.type === 'media.session.create') {\n const m = msg as NappletMessage & { id?: string; owner?: string; sessionId?: string };\n if (m.owner !== 'napplet' && m.owner !== 'shell') {\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n error: 'missing owner',\n } as NappletMessage);\n return;\n }\n if (m.owner === 'shell') {\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n owner: 'shell',\n error: 'unsupported owner mode',\n } as NappletMessage);\n return;\n }\n hooks.sendToNapplet(windowId, {\n type: 'media.session.create.result',\n id: m.id ?? '',\n sessionId: m.sessionId ?? '',\n owner: m.owner,\n } as NappletMessage);\n }\n}\n\nfunction handleKeysMessage(context: RuntimeDomainContext, windowId: string, msg: NappletMessage): void {\n const { hooks, serviceRegistry } = 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' | 'intent' | 'link' | 'common' | 'lists' | 'serial' | 'ble' | 'webrtc',\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 FirewallPersistence,\n FirewallEvent,\n ServiceHandler,\n ServiceRegistry,\n CompatibilityReport,\n ServiceInfo,\n ShellSecretPersistence,\n GuidPersistence,\n HashVerifierAdapter,\n VerificationCacheEntry,\n RuntimeConfigOverrides,\n NappletMessage,\n} from './types.js';\n\nexport { createEnforceGate, createNapEnforceGate, resolveCapabilitiesNap, formatDenialReason } from './enforce.js';\nexport type { EnforceResult, EnforceConfig, NapEnforceConfig, IdentityResolver, AclChecker, NapMessage } from './enforce.js';\n\nexport { createSessionRegistry, createNappKeyRegistry } from './session-registry.js';\nexport type { SessionRegistry, NappKeyRegistry } from './session-registry.js';\n\nexport { createAclState } from './acl-state.js';\nexport type { AclStateContainer } from './acl-state.js';\n\nexport { createFirewallState } from './firewall-state.js';\nexport type { FirewallStateContainer } from './firewall-state.js';\n\nexport { createManifestCache } from './manifest-cache.js';\nexport type { ManifestCache } from './manifest-cache.js';\n\nexport { createReplayDetector } from './replay.js';\nexport type { ReplayDetector } from './replay.js';\n\nexport { createEventBuffer, matchesFilter, matchesAnyFilter, RING_BUFFER_SIZE } from './event-buffer.js';\nexport type { EventBuffer, SubscriptionEntry } from './event-buffer.js';\n\nexport { createRuntime } from './runtime.js';\nexport type { Runtime } from './runtime.js';\n\nexport { handleStorageNap, cleanupNappState } from './state-handler.js';\n\nexport { routeServiceMessage, notifyServiceWindowDestroyed } from './service-dispatch.js';\n\nexport type { Capability } from '@kehto/acl/capabilities';\nexport { ALL_CAPABILITIES } from '@kehto/acl/capabilities';\nexport type { ServiceDescriptor } from './types.js';\n"],"mappings":"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","names":["createState","serialize","deserialize"]}