@kehto/runtime 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -84,21 +84,17 @@ phase records remain history rather than active compatibility guidance.
84
84
 
85
85
  ## NAP-INTENT runtime boundary
86
86
 
87
- Kehto implements merged [NAP-INTENT at
88
- `5ac0490461ca6fec2f0d2e45b4835cf9bc08de24`](https://github.com/napplet/naps/blob/5ac0490461ca6fec2f0d2e45b4835cf9bc08de24/naps/NAP-INTENT.md).
89
- The runtime accepts only source-side `intent.invoke`, `intent.available`, and
90
- `intent.handlers` requests. It validates the exact queryless
91
- `napplet:<archetype>/<action>` identity, derives the sender dTag from the live
92
- authenticated session, and shapes policy denials as sanctioned result
93
- envelopes without exposing ACL or firewall details.
94
-
95
- Registered services receive a frozen `ServiceRuntimeContext`: current dTag
96
- resolution, a frozen live-window snapshot, and recipient-policy-aware sends.
97
- An `intent.invoke.result` with `ok: true` means a host controller selected and
98
- readied the verified target and dispatched its convention. The final result
99
- includes `handled`, `handler`, `windowId`, and `convention`. The target receives
100
- one runtime-attested `inc.event` carrying that queryless convention and opaque
101
- payload; `intent.deliver` is not part of the merged contract.
87
+ Kehto follows [NAP-INTENT PR #106](https://github.com/napplet/naps/blob/fc121fc264615482143eda86125863d2e1f741a2/naps/NAP-INTENT.md) at `fc121fc264615482143eda86125863d2e1f741a2`. The runtime admits source-side `intent.invoke`,
88
+ `intent.available`, and `intent.handlers` through the authenticated session and
89
+ ACL/firewall boundary. Services validate the normalized queryless convention and
90
+ resolve the sender's opaque catalog ID from the trusted source window.
91
+
92
+ Registered services receive a frozen `ServiceRuntimeContext` with live-window
93
+ facts and recipient-policy-aware sends. Success acknowledges retained delivery
94
+ responsibility with `ok`, `archetype`, `action`, `convention`, and `handler`.
95
+ After current target readiness, the controller sends one `intent.deliver` with
96
+ parent-attested sender and opaque payload. Completion belongs to host observation,
97
+ not another source result. No INC carrier is required.
102
98
 
103
99
  ## NAP-OUTBOX denial boundary
104
100
 
@@ -109,8 +105,9 @@ the protocol-defined correlated result wire: `outbox.query.result` with
109
105
  `events: []` and `error`. The runtime does not synthesize an
110
106
  `outbox.query.error` message.
111
107
 
112
- The canonical public `Intent*` contracts are the released `@napplet/core` /
113
- `@napplet/nap` declarations; Kehto retains no local type mirror.
108
+ The installed `@napplet/nap@0.32.0` contract trails PR #106. The bounded local
109
+ `@kehto/services` intent types and their removal condition are documented in
110
+ [conformance policy](https://github.com/kehto/web/blob/main/docs/policies/NIP-5D-CONFORMANCE.md).
114
111
 
115
112
  Phase 105 completed released package adoption and persistent installed-manifest
116
113
  controllers for the live Paja and playground hosts.
@@ -226,10 +223,24 @@ implementations of `FirewallStateContainer` must provide this method.
226
223
  - `cleanupNappState` — remove persisted state when a napplet window closes
227
224
 
228
225
  ### Service dispatch
229
- - `routeServiceMessage` — domain-prefix router into the service registry
226
+ - `routeServiceMessage` — policy-free domain selection primitive; never a fallback for untrusted ingress
230
227
  - `notifyServiceWindowDestroyed` — lifecycle fan-out to every service handler
231
228
  - Service-only NAP domains, including `dm` and `fs`, route through handlers registered with `runtime.registerService('<domain>', handler)`.
232
229
 
230
+ For domains outside the built-in dispatcher, handlers opt in with an exact
231
+ `messageCapabilities` map (existing ACL capability or explicit `null` per inbound
232
+ type). `Runtime.handleMessage` requires a live unblocked source, an explicit
233
+ `isDomainAllowed` grant, the declared ACL grant, and firewall admission. Unknown
234
+ types and extension policy denials stay silent on the wire. Registration alone
235
+ attaches a service; it does not expose or grant a domain. Fixed NAP routes retain
236
+ their own authorization. Pending replies are invalidated by service/source
237
+ replacement or teardown and recheck current domain/ACL policy.
238
+
239
+ Browser hosts must provide the owning extension contract and inject its granted
240
+ namespace binding before scripts run. Stock Paja/playground bindings do not inject
241
+ arbitrary extensions. See [Register a service](../../docs/how-tos/register-service.md#domains-outside-the-built-in-dispatcher)
242
+ for the complete host contract and spec bounds.
243
+
233
244
  ### Types
234
245
  40+ interfaces — including `Runtime`, `RuntimeAdapter`, `SendToNapplet`, `RelayPoolAdapter`, `ServiceHandler`, `ServiceRegistry`, `NappletMessage`, `SessionEntry`, `AclEntryExternal`, `AclCheckEvent`, and the per-adapter hook types — are exported from `./types.js` for host-app integration.
235
246
 
@@ -247,3 +258,7 @@ Generated API module: `docs/api/modules/_kehto_runtime.html` (run `pnpm docs:api
247
258
  ## License
248
259
 
249
260
  MIT
261
+
262
+ ## GAMEPAD integration
263
+
264
+ This non-normative guidance defers to the living [NAP-GAMEPAD](https://github.com/napplet/naps/pull/108). Services export `createGamepadService` and `browserGamepadOptions`; shell exports `denyNappletControllerAccess`. Paja and the playground register one host reader and deny native gamepad/HID/USB paths before frame execution. Source-bound subscriptions receive immediate full snapshots; only the visible host's input owner gets live controls. Focus loss supplies neutral controls and timestamp zero; unsubscribe, window destruction, and service removal release polling. Non-browser or denied native readers report the proposal's unavailable reasons. See [Controller input](https://github.com/kehto/web/blob/main/docs/gamepad.md).
package/dist/index.d.ts CHANGED
@@ -566,9 +566,9 @@ interface ServiceRuntimeContext {
566
566
  /**
567
567
  * Send a runtime-originated message after current recipient policy checks.
568
568
  *
569
- * The message must have a recipient capability mapping. The target must be a
570
- * live session whose immutable domain environment and current ACL grant both
571
- * permit the message.
569
+ * The target must be a live source-bound session whose immutable environment
570
+ * exposes the domain. Mapped recipient capabilities also require their current
571
+ * ACL grant. GAMEPAD state has no invented recipient capability mapping.
572
572
  *
573
573
  * @param windowId - Intended live recipient window.
574
574
  * @param message - Runtime-originated NIP-5D envelope.
@@ -583,12 +583,12 @@ interface ServiceRuntimeContext {
583
583
  *
584
584
  * @example
585
585
  * ```ts
586
- * const audioHandler: ServiceHandler = {
587
- * descriptor: { name: 'audio', version: '1.0.0' },
588
- * handleMessage(windowId, message, send) {
589
- * if (message.type === 'inc.emit') {
590
- * // process audio inc event...
591
- * send({ type: 'inc.emit.result', id: (message as any).id });
586
+ * const exampleHandler: ServiceHandler = {
587
+ * descriptor: { name: 'example', version: '1.0.0' },
588
+ * messageCapabilities: { 'example.ping': null },
589
+ * handleMessage(_windowId, message, send) {
590
+ * if (message.type === 'example.ping') {
591
+ * send({ type: 'example.pong' });
592
592
  * }
593
593
  * },
594
594
  * };
@@ -597,6 +597,19 @@ interface ServiceRuntimeContext {
597
597
  interface ServiceHandler {
598
598
  /** Metadata describing this service. */
599
599
  descriptor: ServiceDescriptor;
600
+ /**
601
+ * Explicit inbound types for a domain outside the built-in dispatcher.
602
+ * Each own key is an exact `domain.action` type; its value is an existing
603
+ * ACL capability or explicit `null` for no additional capability requirement.
604
+ * Omission keeps non-built-in services attachment-only. Built-in NAP routes
605
+ * ignore this map and retain their canonical capability mapping.
606
+ *
607
+ * Extension dispatch also requires a live, unblocked source, an explicit
608
+ * `RuntimeAdapter.isDomainAllowed` grant, and firewall admission. This map
609
+ * neither injects a namespace binding nor grants any capability. The handler
610
+ * owns payload validation and its extension's response contract.
611
+ */
612
+ messageCapabilities?: Readonly<Record<string, Capability | null>>;
600
613
  /**
601
614
  * Attach this handler to one runtime instance.
602
615
  *
@@ -623,14 +636,14 @@ interface ServiceHandler {
623
636
  }
624
637
  /**
625
638
  * Registry of services available to napplets.
626
- * Each key is a service name (e.g., 'audio', 'notifications').
627
- * Napplets discover available services via kind 29010 service discovery events.
639
+ * Keys select exact wire domains for direct routing (e.g., 'notify').
640
+ * Metadata/attachment alone does not expose a domain. Custom domains require
641
+ * ServiceHandler.messageCapabilities and explicit host domain authorization.
628
642
  *
629
643
  * @example
630
644
  * ```ts
631
645
  * const services: ServiceRegistry = {
632
- * audio: audioHandler,
633
- * notifications: notificationHandler,
646
+ * notify: notificationHandler,
634
647
  * };
635
648
  * ```
636
649
  */
@@ -692,8 +705,9 @@ interface RuntimeAdapter {
692
705
  sendToNapplet: SendToNapplet;
693
706
  /**
694
707
  * Determines whether a session-established napplet may invoke a NAP domain.
695
- * When omitted, every domain remains eligible for dispatch. Browser shells
696
- * use this to enforce the immutable environment delivered in `shell.init`.
708
+ * Built-in routes remain eligible when omitted; extension routes require
709
+ * an explicit true result. Browser shells must derive this decision from
710
+ * the source-bound namespace exposed before napplet scripts execute.
697
711
  */
698
712
  isDomainAllowed?: (windowId: string, domain: string) => boolean;
699
713
  /**
@@ -792,14 +806,17 @@ interface RuntimeAdapter {
792
806
  /**
793
807
  * Optional service extensions. Shell/host registers service handlers here
794
808
  * for static initialization. Services can also be added dynamically via
795
- * runtime.registerService(). Each key is a service name (e.g., 'audio').
809
+ * runtime.registerService(). Keys select exact domains (e.g., 'notify').
810
+ * Outside built-in domains, handlers also need messageCapabilities and an
811
+ * explicit isDomainAllowed grant. Hosts own each extension's NAP contract,
812
+ * payload validation, and pre-execution browser namespace binding.
796
813
  *
797
814
  * @example
798
815
  * ```ts
799
816
  * const hooks: RuntimeAdapter = {
800
817
  * // ... required adapters ...
801
818
  * services: {
802
- * audio: myAudioServiceHandler,
819
+ * notify: myNotificationServiceHandler,
803
820
  * },
804
821
  * };
805
822
  * ```
@@ -1456,7 +1473,11 @@ interface Runtime {
1456
1473
  * Register a service handler dynamically after runtime creation.
1457
1474
  * If a handler is already registered for this name, it is replaced.
1458
1475
  *
1459
- * @param name - Service name (e.g., 'audio', 'notifications')
1476
+ * Built-in domains keep their NAP routing. Other domains require the
1477
+ * handler's exact messageCapabilities map and an explicit host domain grant.
1478
+ * Registration never injects namespace bindings or grants capabilities.
1479
+ *
1480
+ * @param name - Exact domain name (e.g., 'notify' or a host extension)
1460
1481
  * @param handler - The service handler implementation
1461
1482
  */
1462
1483
  registerService(name: string, handler: ServiceHandler): void;
@@ -1580,9 +1601,12 @@ declare function cleanupNappState(pubkey: string, dTag: string, aggregateHash: s
1580
1601
  * Route a NappletMessage envelope to the matching service handler.
1581
1602
  *
1582
1603
  * Direct NAP-domain services are routed by the domain in their wire
1583
- * message.type (e.g., 'signer.signEvent' -> 'signer' service). INC messages
1604
+ * message.type (e.g., 'notify.send' -> 'notify' service). INC messages
1584
1605
  * are owned by the INC runtime; their topic values are opaque here.
1585
1606
  *
1607
+ * This low-level selection primitive performs no source, domain, ACL,
1608
+ * messageCapabilities, or firewall checks. Never call it as a fallback for
1609
+ * untrusted input; host ingress must use Runtime.handleMessage.
1586
1610
  * Returns true if a service handled the message, false otherwise.
1587
1611
  *
1588
1612
  * @param windowId - The napplet window that sent the message
package/dist/index.js CHANGED
@@ -565,11 +565,52 @@ function relayEventResultFromCarrier(carrier) {
565
565
  // src/runtime.ts
566
566
  import { createDispatch } from "@napplet/core";
567
567
 
568
+ // src/service-extensions.ts
569
+ import { ALL_CAPABILITIES } from "@kehto/acl/capabilities";
570
+ function createServiceExtensionRouter(context) {
571
+ const { hooks, services, registrations, sessions, acl, enforce, firewall } = context;
572
+ const windowLifetimes = /* @__PURE__ */ new Map();
573
+ function handleMessage(windowId, message) {
574
+ const dot = message.type.indexOf(".");
575
+ const domain = message.type.slice(0, dot);
576
+ if (dot <= 0 || dot === message.type.length - 1 || !/^[a-z][a-z0-9-]*$/.test(domain) || !Object.hasOwn(services, domain)) return;
577
+ const handler = services[domain];
578
+ const policies = handler.messageCapabilities;
579
+ if (!policies || !Object.hasOwn(policies, message.type)) return;
580
+ const capability = policies[message.type];
581
+ if (capability !== null && !ALL_CAPABILITIES.includes(capability)) return;
582
+ const session = sessions.getEntryByWindowId(windowId);
583
+ const registration = registrations.get(domain);
584
+ if (!session || !registration) return;
585
+ const allowed = () => hooks.isDomainAllowed?.(windowId, domain) === true && !acl.isBlocked("", session.dTag, session.aggregateHash) && (capability === null || enforce(windowId, capability, message).allowed);
586
+ if (!allowed() || firewall(windowId, message, capability, false) === "drop") return;
587
+ let lifetime = windowLifetimes.get(windowId);
588
+ if (!lifetime) {
589
+ lifetime = {};
590
+ windowLifetimes.set(windowId, lifetime);
591
+ }
592
+ handler.handleMessage(windowId, message, (response) => {
593
+ if (registrations.get(domain) !== registration || services[domain] !== handler || windowLifetimes.get(windowId) !== lifetime || sessions.getEntryByWindowId(windowId) !== session) return;
594
+ if (!response || typeof response.type !== "string" || !response.type.startsWith(`${domain}.`) || response.type.length <= dot + 1) return;
595
+ if (allowed()) hooks.sendToNapplet(windowId, response);
596
+ });
597
+ }
598
+ return {
599
+ handleMessage,
600
+ destroyWindow(windowId) {
601
+ windowLifetimes.delete(windowId);
602
+ },
603
+ destroy() {
604
+ windowLifetimes.clear();
605
+ }
606
+ };
607
+ }
608
+
568
609
  // src/service-dispatch.ts
569
610
  function routeServiceMessage(windowId, message, services, sendToNapplet) {
570
611
  if (message.type === "inc.emit") return false;
571
612
  const domain = message.type.split(".")[0];
572
- const handler = services[domain];
613
+ const handler = Object.hasOwn(services, domain) ? services[domain] : void 0;
573
614
  if (handler) {
574
615
  handler.handleMessage(windowId, message, (msg) => sendToNapplet(windowId, msg));
575
616
  return true;
@@ -1616,6 +1657,7 @@ function cleanupNappState(pubkey, dTag, aggregateHash, statePersistence) {
1616
1657
  // src/domain-handlers.ts
1617
1658
  function createRuntimeDomainHandlers(context) {
1618
1659
  return {
1660
+ gamepad: (windowId, msg) => handleServiceOnlyMessage(context, "gamepad", windowId, msg),
1619
1661
  storage: (windowId, msg) => handleStorageMessage(context, windowId, msg),
1620
1662
  media: (windowId, msg) => handleMediaMessage(context, windowId, msg),
1621
1663
  keys: (windowId, msg) => handleKeysMessage(context, windowId, msg),
@@ -1766,6 +1808,7 @@ function createNapEnvelopeDispatcher(handlers) {
1766
1808
  const adapt = (handler) => (msg) => {
1767
1809
  if (currentWindowId !== null) handler(currentWindowId, msg);
1768
1810
  };
1811
+ napDispatch.registerNap("gamepad", adapt(handlers.gamepad));
1769
1812
  napDispatch.registerNap("relay", adapt(handlers.relay));
1770
1813
  napDispatch.registerNap("identity", adapt(handlers.identity));
1771
1814
  napDispatch.registerNap("count", adapt(handlers.count));
@@ -1848,13 +1891,18 @@ function buildObservation(envelope, windowId, senderCap, sessionRegistry, getFoc
1848
1891
  }
1849
1892
  function createFirewallGate(config) {
1850
1893
  const { firewallState, sessionRegistry, hooks, fireConsent } = config;
1851
- return function firewallGate(windowId, envelope, senderCap) {
1894
+ return function firewallGate(windowId, envelope, senderCap, respond = true) {
1852
1895
  const obs = buildObservation(envelope, windowId, senderCap, sessionRegistry, hooks.getFocusContext);
1853
1896
  const result = firewallState.evaluate(obs);
1854
1897
  const { decision, action, ruleId, reason } = result;
1855
1898
  const napplet = obs.napplet;
1856
1899
  const opClass = obs.opClass;
1857
1900
  if (decision === "reject" || decision === "prompt") {
1901
+ if (!respond) {
1902
+ hooks.onFirewallEvent?.({ windowId, napplet, opClass, decision, action, ruleId, reason, message: envelope });
1903
+ if (decision === "prompt") fireConsent(windowId, napplet);
1904
+ return "drop";
1905
+ }
1858
1906
  const intentDenial = createIntentPolicyDenial(envelope);
1859
1907
  if (intentDenial) {
1860
1908
  hooks.sendToNapplet(windowId, intentDenial);
@@ -1905,14 +1953,18 @@ function createDenialResponse(envelope, error) {
1905
1953
  const type = denialResponseType(envelope);
1906
1954
  return type ? { type, id, error } : null;
1907
1955
  }
1908
- function createMessageHandler(hooks, sessionRegistry, enforceNap, dispatchNapEnvelope, firewallGate) {
1956
+ function createMessageHandler(hooks, sessionRegistry, enforceNap, dispatchNapEnvelope, serviceRouting, firewallGate) {
1909
1957
  return (windowId, msg) => {
1910
- if (typeof msg !== "object" || msg === null || !("type" in msg)) return;
1958
+ if (typeof msg !== "object" || msg === null || Array.isArray(msg) || !("type" in msg) || typeof msg.type !== "string") return;
1911
1959
  const envelope = msg;
1912
1960
  const dotIdx = envelope.type.indexOf(".");
1913
1961
  if (dotIdx === -1) return;
1914
1962
  if (!sessionRegistry.getEntryByWindowId(windowId)) return;
1915
1963
  const domain = envelope.type.slice(0, dotIdx);
1964
+ if (!serviceRouting.builtInDomains.has(domain)) {
1965
+ serviceRouting.extensions.handleMessage(windowId, envelope);
1966
+ return;
1967
+ }
1916
1968
  if (hooks.isDomainAllowed && !hooks.isDomainAllowed(windowId, domain)) return;
1917
1969
  if (isIdentityOrThemeMessage(envelope) && !createCanonicalDomainResult(envelope)) return;
1918
1970
  const caps = resolveCapabilitiesNap(envelope);
@@ -1965,7 +2017,8 @@ function createRuntimeInstance(context) {
1965
2017
  serviceRegistry,
1966
2018
  sessionRegistry,
1967
2019
  subscriptions,
1968
- consentHandlerRef
2020
+ consentHandlerRef,
2021
+ extensions
1969
2022
  } = context;
1970
2023
  const undeclaredServiceConsents = /* @__PURE__ */ new Set();
1971
2024
  const attachedServices = /* @__PURE__ */ new Map();
@@ -1993,8 +2046,8 @@ function createRuntimeInstance(context) {
1993
2046
  try {
1994
2047
  if (isDomainAllowed && !isDomainAllowed(windowId, domain)) return false;
1995
2048
  const { recipientCap } = resolveCapabilitiesNap(message);
1996
- if (!recipientCap) return false;
1997
- if (!aclState.check("", entry.dTag, entry.aggregateHash, recipientCap)) {
2049
+ if (!recipientCap && message.type !== "gamepad.state") return false;
2050
+ if (recipientCap && !aclState.check("", entry.dTag, entry.aggregateHash, recipientCap)) {
1998
2051
  return false;
1999
2052
  }
2000
2053
  sendToNapplet(windowId, message);
@@ -2029,6 +2082,8 @@ function createRuntimeInstance(context) {
2029
2082
  eventBuffer.bufferAndDeliver(createInjectedEvent(hooks, topic, payload), null);
2030
2083
  },
2031
2084
  destroy() {
2085
+ extensions.destroy();
2086
+ registeredServices.clear();
2032
2087
  for (const name of attachedServices.keys()) detachService(name);
2033
2088
  manifestCache.persist();
2034
2089
  aclState.persist();
@@ -2037,13 +2092,13 @@ function createRuntimeInstance(context) {
2037
2092
  subscriptions.clear();
2038
2093
  incRuntime.clear();
2039
2094
  eventBuffer.clear();
2040
- registeredServices.clear();
2041
2095
  undeclaredServiceConsents.clear();
2042
2096
  },
2043
2097
  registerConsentHandler(handler) {
2044
2098
  consentHandlerRef.current = handler;
2045
2099
  },
2046
2100
  registerService(name, handler) {
2101
+ registeredServices.delete(name);
2047
2102
  detachService(name);
2048
2103
  serviceRegistry[name] = handler;
2049
2104
  registeredServices.set(name, {
@@ -2054,11 +2109,12 @@ function createRuntimeInstance(context) {
2054
2109
  attachService(name, handler);
2055
2110
  },
2056
2111
  unregisterService(name) {
2112
+ registeredServices.delete(name);
2057
2113
  detachService(name);
2058
2114
  delete serviceRegistry[name];
2059
- registeredServices.delete(name);
2060
2115
  },
2061
2116
  destroyWindow(windowId) {
2117
+ extensions.destroyWindow(windowId);
2062
2118
  for (const [key] of subscriptions) {
2063
2119
  if (key.startsWith(`${windowId}:`)) {
2064
2120
  subscriptions.delete(key);
@@ -2087,8 +2143,10 @@ function createRuntime(hooks) {
2087
2143
  const serviceRegistry = { ...hooks.services };
2088
2144
  const registeredServices = createRegisteredServices(serviceRegistry);
2089
2145
  const firewallState = createFirewallState(hooks.firewallPersistence);
2146
+ let extensions;
2090
2147
  const sessionRegistry = createSessionRegistry(hooks.onPendingUpdate, (windowId) => {
2091
2148
  firewallState.resetInitBudget(windowId);
2149
+ extensions?.destroyWindow(windowId);
2092
2150
  });
2093
2151
  let incRuntime = null;
2094
2152
  const aclState = createAclState(hooks.aclPersistence, "permissive", (mutation) => {
@@ -2151,14 +2209,25 @@ function createRuntime(hooks) {
2151
2209
  firewallState.load();
2152
2210
  manifestCache.load();
2153
2211
  const domainHandlers = createRuntimeDomainHandlers({ hooks, serviceRegistry, sessionRegistry, aclState });
2154
- const dispatchNapEnvelope = createNapEnvelopeDispatcher({
2212
+ const napHandlers = {
2155
2213
  relay: createRelayHandler({ hooks, serviceRegistry, subscriptions, eventBuffer, replayDetector }),
2156
2214
  identity: createIdentityHandler({ hooks, serviceRegistry }),
2157
2215
  count: createCountHandler({ hooks, serviceRegistry }),
2158
2216
  inc: incRuntime.handleMessage,
2159
2217
  ...domainHandlers
2218
+ };
2219
+ const dispatchNapEnvelope = createNapEnvelopeDispatcher(napHandlers);
2220
+ const builtInDomains = /* @__PURE__ */ new Set([...Object.keys(napHandlers), "shell", "signer"]);
2221
+ extensions = createServiceExtensionRouter({
2222
+ hooks,
2223
+ services: serviceRegistry,
2224
+ registrations: registeredServices,
2225
+ sessions: sessionRegistry,
2226
+ acl: aclState,
2227
+ enforce: enforceNap,
2228
+ firewall: firewallGate
2160
2229
  });
2161
- const handleMessage = createMessageHandler(hooks, sessionRegistry, enforceNap, dispatchNapEnvelope, firewallGate);
2230
+ const handleMessage = createMessageHandler(hooks, sessionRegistry, enforceNap, dispatchNapEnvelope, { builtInDomains, extensions }, firewallGate);
2162
2231
  return createRuntimeInstance({
2163
2232
  hooks,
2164
2233
  serviceRegistry,
@@ -2172,14 +2241,15 @@ function createRuntime(hooks) {
2172
2241
  firewallState,
2173
2242
  manifestCache,
2174
2243
  consentHandlerRef,
2175
- handleMessage
2244
+ handleMessage,
2245
+ extensions
2176
2246
  });
2177
2247
  }
2178
2248
 
2179
2249
  // src/index.ts
2180
- import { ALL_CAPABILITIES } from "@kehto/acl/capabilities";
2250
+ import { ALL_CAPABILITIES as ALL_CAPABILITIES2 } from "@kehto/acl/capabilities";
2181
2251
  export {
2182
- ALL_CAPABILITIES,
2252
+ ALL_CAPABILITIES2 as ALL_CAPABILITIES,
2183
2253
  RING_BUFFER_SIZE,
2184
2254
  cleanupNappState,
2185
2255
  createAclState,