@nice-code/action 0.25.0 → 0.26.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.
Files changed (39) hide show
  1. package/README.md +52 -0
  2. package/build/{AcceptorHandler-BizUtq4u.d.mts → AcceptorHandler-CLbwu2Pa.d.mts} +74 -16
  3. package/build/{AcceptorHandler-CxPfZtIl.d.cts → AcceptorHandler-Du292dpC.d.cts} +74 -16
  4. package/build/{ActionDevtoolsCore-D9KBBI2V.d.cts → ActionDevtoolsCore-DGwzONZT.d.mts} +2 -2
  5. package/build/{ActionDevtoolsCore-xZjAtB4H.d.mts → ActionDevtoolsCore-dH4K4w3B.d.cts} +2 -2
  6. package/build/advanced/index.cjs +1 -1
  7. package/build/advanced/index.d.cts +12 -6
  8. package/build/advanced/index.d.mts +12 -6
  9. package/build/advanced/index.mjs +1 -1
  10. package/build/{createHibernatableWsServerAdapter-BkjESd01.mjs → createHibernatableWsServerAdapter-BD5n-Ev9.mjs} +176 -75
  11. package/build/createHibernatableWsServerAdapter-BD5n-Ev9.mjs.map +1 -0
  12. package/build/{createHibernatableWsServerAdapter-FSDWrxoF.cjs → createHibernatableWsServerAdapter-j96U9vgo.cjs} +175 -74
  13. package/build/{createHibernatableWsServerAdapter-BkjESd01.mjs.map → createHibernatableWsServerAdapter-j96U9vgo.cjs.map} +1 -1
  14. package/build/devtools/browser/index.cjs.map +1 -1
  15. package/build/devtools/browser/index.d.cts +1 -1
  16. package/build/devtools/browser/index.d.mts +1 -1
  17. package/build/devtools/browser/index.mjs.map +1 -1
  18. package/build/devtools/server/index.d.cts +1 -1
  19. package/build/devtools/server/index.d.mts +1 -1
  20. package/build/{httpAcceptorCarrier-BQYaXI9j.cjs → httpAcceptorCarrier-By0Qa__L.cjs} +2 -2
  21. package/build/httpAcceptorCarrier-By0Qa__L.cjs.map +1 -0
  22. package/build/{httpAcceptorCarrier-DWqsCz3h.mjs → httpAcceptorCarrier-moSmtBxr.mjs} +2 -2
  23. package/build/httpAcceptorCarrier-moSmtBxr.mjs.map +1 -0
  24. package/build/index.cjs +2 -2
  25. package/build/index.cjs.map +1 -1
  26. package/build/index.d.cts +1 -1
  27. package/build/index.d.mts +1 -1
  28. package/build/index.mjs +2 -2
  29. package/build/index.mjs.map +1 -1
  30. package/build/platform/cloudflare/index.cjs +1 -1
  31. package/build/platform/cloudflare/index.d.cts +1 -1
  32. package/build/platform/cloudflare/index.d.mts +1 -1
  33. package/build/platform/cloudflare/index.mjs +1 -1
  34. package/build/react-query/index.d.cts +1 -1
  35. package/build/react-query/index.d.mts +1 -1
  36. package/package.json +5 -4
  37. package/build/createHibernatableWsServerAdapter-FSDWrxoF.cjs.map +0 -1
  38. package/build/httpAcceptorCarrier-BQYaXI9j.cjs.map +0 -1
  39. package/build/httpAcceptorCarrier-DWqsCz3h.mjs.map +0 -1
package/README.md CHANGED
@@ -506,6 +506,11 @@ this._server = serveDurableObject(this.ctx, bridgeChannel, { runtime, httpFallba
506
506
  The matching connector points its `httpCarrier` at `/bridge/:id/action`. `pickStub` may be async (e.g. to
507
507
  resolve an id first) and receives the parsed `URL` alongside the request.
508
508
 
509
+ This is for routing to a per-id *durable* instance (one that keeps a live WebSocket or per-instance
510
+ state). If a secure endpoint needs **neither**, skip the DO entirely — a single stateless `serveChannel`
511
+ route serves the exchange directly (see
512
+ [Serving without a Durable Object](#serving-without-a-durable-object--stateless-secure-http-exchange)).
513
+
509
514
  > Need to handle the exchange yourself instead of handing the endpoint to `serveChannel`? The secure
510
515
  > exchange acceptor and its plain `{k:"act",w}` envelope codec are exported from the **main** entry:
511
516
  > `ExchangeAcceptor` (drive the handshake + token sessions + decrypt over your own `fetch`), and
@@ -578,6 +583,53 @@ plain HTTP fallback by giving the acceptor a `httpAcceptorCarrier({ secure: fals
578
583
 
579
584
  ---
580
585
 
586
+ ## Serving without a Durable Object — stateless secure HTTP exchange
587
+
588
+ The secure HTTP exchange is **stateless**: its handshake and session ride sealed tokens (sealed under
589
+ the server's own crypto identity), so any request can be served by any isolate. You don't need a Durable
590
+ Object — or any sticky instance — just to keep a handshake's two POSTs together. A single secure-exchange
591
+ acceptor is therefore just `serveChannel` over an HTTP carrier, on any backend (a plain Worker route, a
592
+ Node handler):
593
+
594
+ ```ts
595
+ // server.ts — a stateless secure-exchange endpoint (no Durable Object)
596
+ const server = serveChannel(runtime, appChannel, {
597
+ clientEnv,
598
+ storage, // backs the (read-mostly) crypto identity; sessions never touch it
599
+ carriers: [httpAcceptorCarrier()],
600
+ handlers: [appHandler],
601
+ });
602
+ // route any action POST straight to it:
603
+ // app.post("/action", (req) => server.fetch(req))
604
+ ```
605
+
606
+ The only thing the exchange reads from `storage` is the server's crypto identity. On a **strongly
607
+ consistent** store (Node memory, a DO's storage, D1) the default lazy identity is fork-safe. On an
608
+ **eventually consistent** store (Cloudflare KV) provision the identity once, up front, so a transient
609
+ read-miss can never mint a *second* identity (which trust-on-first-use-pinned clients would then reject):
610
+
611
+ ```ts
612
+ import { ClientCryptoKeyLink } from "@nice-code/util";
613
+
614
+ // `required` never mints on the request path — an unprovisioned link throws IdentityNotProvisionedError
615
+ // instead of forking a fresh identity on a storage hiccup.
616
+ const link = new ClientCryptoKeyLink({ storageAdapter: storage, identityMode: "required" });
617
+ await link.provisionIdentity(); // once, out-of-band (a deploy step / first boot)
618
+
619
+ const server = serveChannel(runtime, appChannel, {
620
+ clientEnv, storage, link, // pass the provisioned, required-mode identity
621
+ carriers: [httpAcceptorCarrier()],
622
+ handlers: [appHandler],
623
+ });
624
+ ```
625
+
626
+ > **On Cloudflare Workers**, build the server lazily on the first request (memoized), not at module
627
+ > scope — the runtime forbids generating random ids and doing I/O in the global scope. A live WebSocket
628
+ > still wants a Durable Object (a hibernatable socket needs a stable instance); this statelessness is for
629
+ > the request/reply HTTP exchange.
630
+
631
+ ---
632
+
581
633
  ## Multiple carriers on one runtime
582
634
 
583
635
  `serveChannel` accepts **any number of duplex carriers** (e.g. WebSocket + WebRTC) plus at most one
@@ -781,6 +781,16 @@ declare function createInMemoryTofuVerifyKeyResolver(): IClientVerifyKeyResolver
781
781
  * Storage-backed trust-on-first-use resolver: pins survive process restarts / Durable Object eviction
782
782
  * (e.g. back it with `createDurableObjectStorageAdapter`). Same policy as the in-memory variant — trust
783
783
  * + pin the first verify key per client identity, reject a different one thereafter.
784
+ *
785
+ * Fail-closed by construction: a thrown storage read (`getJson`) or first-pin write (`updateJsonWithDef`)
786
+ * propagates out of `resolve`, which makes `onProve` reject the handshake — a storage error can never be
787
+ * mistaken for "first use" and silently trusted. (A genuine `undefined`/absent read is the only path to
788
+ * a fresh pin; the underlying adapters never coerce a thrown read to `undefined`.) Keep it that way: do
789
+ * not wrap the storage calls in a `try/catch` that swallows the error.
790
+ *
791
+ * On an *eventually-consistent* store a stale "absent" read re-pins the **same** verify key the client
792
+ * just presented (it is signature-verified before this runs), so the worst case is a harmless re-write,
793
+ * never a weakened trust decision. Cross-isolate-strong pinning still wants a strongly-consistent store.
784
794
  */
785
795
  declare function createStorageTofuVerifyKeyResolver(storageAdapter: StorageAdapter): IClientVerifyKeyResolver;
786
796
  interface IClientHandshakeConfig {
@@ -808,11 +818,37 @@ interface IServerHandshakeConfig {
808
818
  /** Trust decision for a client's verify key. Defaults to in-memory TOFU. */
809
819
  verifyKeyResolver?: IClientVerifyKeyResolver;
810
820
  }
811
- declare function createServerHandshake(config: IServerHandshakeConfig): {
821
+ /**
822
+ * The server handshake's in-flight state between `onHello` and `onProve` — everything `onProve` needs
823
+ * to verify the client's proof and settle the result. Fully serializable (all JSON / serialized-key
824
+ * strings), so a stateless server can {@link IServerHandshake.exportPending} it after `hello`, carry it
825
+ * across requests (e.g. sealed into a continuation token), and {@link IServerHandshake.restorePending}
826
+ * it on a *different* instance before `prove` — no in-memory handshake to co-locate. The `serverNonce`
827
+ * is already folded into `challenge`, so it need not be carried separately.
828
+ */
829
+ interface IServerHandshakePending {
830
+ client: IRuntimeCoordinate;
831
+ linkedClientId: TTypeAndId;
832
+ challenge: string;
833
+ clientVerifyKey: TSerializedCryptoKeyData_Ed25519_Raw;
834
+ negotiatedLevel: ESecurityLevel;
835
+ keyMaterial?: IHandshakeEncryptionKeyMaterial;
836
+ }
837
+ interface IServerHandshake {
812
838
  onHello(hello: THsHello): Promise<THsWelcome | THsReject>;
813
- onProve(prove: THsProve): Promise<THsAccept | THsReject>; /** The completed handshake result once `onProve` has accepted, else `undefined`. */
839
+ onProve(prove: THsProve): Promise<THsAccept | THsReject>;
814
840
  getResult(): IHandshakeResult | undefined;
815
- };
841
+ /** The serializable in-flight state after `onHello` (else `undefined`). See {@link IServerHandshakePending}. */
842
+ exportPending(): IServerHandshakePending | undefined;
843
+ /**
844
+ * Restore exported `pending` onto a *fresh* handshake instance so `onProve` can run without the
845
+ * `onHello` that produced it (the stateless / cross-instance path). Re-links the client's verify key
846
+ * — `onHello`'s `linkClient` was in-memory only and won't exist on this instance — so the signature
847
+ * check in `onProve` succeeds. Build the instance with the same `config` (identity link + coordinate).
848
+ */
849
+ restorePending(pending: IServerHandshakePending): Promise<void>;
850
+ }
851
+ declare function createServerHandshake(config: IServerHandshakeConfig): IServerHandshake;
816
852
  //#endregion
817
853
  //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createSecureActionServer.d.ts
818
854
  interface ISecureAcceptorHandlerOptions<TConn> {
@@ -1847,6 +1883,14 @@ interface IServeChannelOptions<TO_ACCEPTOR extends readonly ActionDomain<any>[],
1847
1883
  *
1848
1884
  * Required only when at least one carrier is secure (the default). A fully-plain server (every carrier
1849
1885
  * `secure: false`) needs no storage and may omit it.
1886
+ *
1887
+ * The secure HTTP exchange is stateless — its handshake + session ride sealed tokens, so it touches
1888
+ * this store only for the (read-mostly) identity, never per session. That lets a single secure-exchange
1889
+ * server (`carriers: [httpAcceptorCarrier()]`) run on a stateless Worker/Node backend with no Durable
1890
+ * Object. On a *strongly-consistent* store (DO storage, D1, Node memory) the default lazy identity is
1891
+ * fork-safe. On an *eventually-consistent* store (Cloudflare KV) pass an explicit {@link link} built
1892
+ * with `identityMode: "required"` and `provisionIdentity()` it once out-of-band, so a transient read
1893
+ * miss can never fork a second identity (which pinned clients would then permanently reject).
1850
1894
  */
1851
1895
  storage?: StorageAdapter;
1852
1896
  /**
@@ -2287,29 +2331,39 @@ interface IExchangeAcceptorConfig {
2287
2331
  security: IExchangeAcceptorSecurity;
2288
2332
  /** The runtime that executes an inbound action wire and produces its result. */
2289
2333
  runtime: ActionRuntime;
2334
+ /**
2335
+ * How long a minted session ticket stays valid (ms). After it expires the client's next action is
2336
+ * rejected and it must re-handshake. Defaults to 12h — long enough for an ordinary session's life,
2337
+ * since a sealed ticket carries no server state to revoke before then. Keep it shorter for a more
2338
+ * tightly time-boxed session.
2339
+ */
2340
+ sessionTtlMs?: number;
2290
2341
  }
2291
2342
  /**
2292
2343
  * Acceptor (accept-in) side of the secure exchange protocol — the HTTP counterpart to
2293
2344
  * {@link AcceptorSecureSession}. Each POST body is one {@link decodeExchangeRequest} envelope; the
2294
- * acceptor drives the server handshake over the two `hs` POSTs (correlated by `hsid`, since stateless
2295
- * requests can't rely on channel ordering), mints a session **token** on accept, and on every later `act`
2296
- * POST resolves the session by token, decrypts the body (at `encrypted`), routes it through the runtime,
2297
- * and returns the (encrypted) result inline as the reply.
2345
+ * acceptor drives the server handshake over the two `hs` POSTs, mints a session **token** on accept, and
2346
+ * on every later `act` POST resolves the session by token, decrypts the body (at `encrypted`), routes it
2347
+ * through the runtime, and returns the (encrypted) result inline as the reply.
2298
2348
  *
2299
- * Sessions and in-flight handshakes are held in memory fine for a single-instance server. (Surviving a
2300
- * Durable-Object eviction would persist each token's `keyMaterial` and re-derive the key on a miss, the
2301
- * same primitive `AcceptorSecureSession.rehydrate` uses; left as a follow-up.)
2349
+ * **Stateless.** It holds no in-memory handshakes or sessions: the in-flight handshake `pending` is
2350
+ * sealed into the `hsc` continuation token returned on `welcome` and echoed back on `prove`, and the live
2351
+ * session is sealed into the `t` token replayed on every `act`. Both are sealed under the acceptor's own
2352
+ * persisted identity ({@link createExchangeTicketSealer}), so any isolate that loaded the same identity
2353
+ * can serve any POST — no request needs to co-locate with another (no Durable Object required just to
2354
+ * pin a handshake to one instance). A tampered, wrong-key, or expired token opens to "no valid session".
2302
2355
  */
2303
2356
  declare class ExchangeAcceptor {
2304
2357
  private readonly _security;
2305
2358
  private readonly _runtime;
2306
2359
  private readonly _allowedLevels;
2307
2360
  private readonly _noneAllowed;
2308
- private readonly _pendingHandshakes;
2309
- private readonly _sessions;
2361
+ private readonly _sealer;
2362
+ private readonly _sessionTtlMs;
2310
2363
  constructor(config: IExchangeAcceptorConfig);
2311
2364
  /** Process one POST body (an exchange envelope), returning the reply body to send back. */
2312
2365
  handlePost(body: string): Promise<string>;
2366
+ private _makeHandshake;
2313
2367
  private _handleHandshake;
2314
2368
  private _handleAction;
2315
2369
  private _err;
@@ -2327,15 +2381,18 @@ declare class ExchangeAcceptor {
2327
2381
  * the plaintext wire.
2328
2382
  * - `encrypted` — same, but the wire is AES-GCM ciphertext, base64 in the `c` field.
2329
2383
  *
2330
- * The handshake runs as two `hs` exchanges (hello→welcome, prove→accept) correlated by a client-chosen
2331
- * `hsid`, since stateless requests can't rely on channel ordering. The `accept` reply carries the token.
2384
+ * The handshake runs as two `hs` exchanges (hello→welcome, prove→accept). The acceptor keeps **no**
2385
+ * in-flight state between them: the `welcome` reply carries a sealed handshake-continuation token `hsc`
2386
+ * (the acceptor's `pending` state, opaque to the client), which the connector echoes on the `prove`
2387
+ * request. The `accept` reply then carries the (sealed) session `t`oken replayed on every later `act`.
2388
+ * All server-side continuity rides these sealed tokens, so no request needs to co-locate with another.
2332
2389
  */
2333
2390
  type TWireJson = TActionPayload_Any_JsonObject<any, any>;
2334
2391
  /** Connector → acceptor request envelope. */
2335
2392
  type TExchangeRequest = {
2336
2393
  k: "hs";
2337
- hsid: string;
2338
2394
  m: string;
2395
+ hsc?: string;
2339
2396
  } | {
2340
2397
  k: "act";
2341
2398
  t?: string;
@@ -2349,6 +2406,7 @@ type TExchangeRequest = {
2349
2406
  type TExchangeReply = {
2350
2407
  k: "hs";
2351
2408
  m: string;
2409
+ hsc?: string;
2352
2410
  t?: string;
2353
2411
  } | {
2354
2412
  k: "act";
@@ -3097,4 +3155,4 @@ declare class AcceptorHandler<TConn = unknown> extends PeerLinkHandler {
3097
3155
  declare const createAcceptorHandler: <TConn = unknown>(options: IAcceptorHandlerOptions<TConn>) => AcceptorHandler<TConn>;
3098
3156
  //#endregion
3099
3157
  export { httpAcceptorCarrier as $, createInMemoryTofuVerifyKeyResolver as $n, TCarrier as $t, TActionResultOutcome as A, TOnResolveIncomingResponseJson as An, IActionCore_JsonObject as Ar, IHibernatableWsServerAdapterOptions as At, decodeExchangeReply as B, Transport as Bn, TPossibleDomainIdList as Br, TChannelAcceptorCases as Bt, IActionProgress_Custom as C, IUpdateActionRunConfig_Output as Cn, IRuntimeCoordinate as Cr, serveChannel as Ct, TActionPayload_Any_Instance as D, TOnResolveIncomingRequest as Dn, TRuntimeCoordinateEnvId as Dr, TAcceptorCarrier as Dt, IActionRouteItemHandler as E, TOnResolveAnyIncomingActionData_Json as En, RuntimeCoordinate as Er, IExchangeAcceptorCarrier as Et, decodeActionFrame as F, TTransportStatusInfo as Fn, TActionDomainSchema as Fr, createConnectionStateStore as Ft, IExchangeAcceptorSecurity as G, IClientHandshakeConfig as Gn, TActionSchemaOptions as Gr, defineChannel as Gt, encodeExchange as H, createSecureAcceptorHandler as Hn, EActionResponseMode as Hr, acceptChannel as Ht, EErrId_NiceAction as I, TTransportStatusInfo_GetTransport_Output as In, TDomainActionId as Ir, IAcceptChannelOptions as It, IHttpCarrierOptions as J, IHandshakeEncryptionKeyMaterial as Jn, IActionWireFormat as Jt, EErrId_NiceTransport as K, IClientVerifyKeyResolveInput as Kn, TActionSerializationDefinition as Kr, IBinaryWireSessionOptions as Kt, err_nice_action as L, TUpdateActionRunConfig as Ln, TInferInputFromSchema as Lr, IActionChannel as Lt, isActionPayload_Request_JsonObject as M, TSendReturnDataMethod as Mn, IActionDomainChildOptions as Mr, ConnectionStateStore as Mt, isActionPayload_Any_JsonObject as N, TTransportCache as Nn, IActionRootDomain as Nr, IConnectionAttachment as Nt, TActionPayload_Any_JsonObject as O, TOnResolveIncomingRequestJson as On, TRuntimeCoordinateStringId as Or, isExchangeAcceptorCarrier as Ot, IActionFrameDecoder as P, TTransportInitializationFinishedInfo as Pn, TActionDomainChildDef as Pr, IConnectionStateStoreOptions as Pt, IHttpAcceptorCarrierOptions as Q, createClientHandshake as Qn, IExchangeCarrierSource as Qt, TExchangeReply as R, TransportConnection as Rn, TInferOutputFromSchema as Rr, IConnectChannelOptions as Rt, IActionPayload_Result_JsonObject as S, ITransportStatusInfo_Unsupported as Sn, ActionPayload_Progress as Sr, IServeConnectionStateOptions as St, IActionProgress_Percentage as T, TOnResolveAnyIncomingActionData as Tn, IRuntimeFullCoordinates as Tr, IDuplexAcceptorCarrier as Tt, ExchangeAcceptor as U, EHandshakeMessageType as Un, TInferActionError as Ur, acceptChannelConnections as Ut, decodeExchangeRequest as V, ISecureAcceptorHandlerOptions as Vn, ActionSchema as Vr, TChannelPushHandlers as Vt, IExchangeAcceptorConfig as W, ESecurityLevel as Wn, actionSchema as Wr, connectChannel as Wt, TCarrierFetch as X, IServerHandshakeConfig as Xn, IDuplexCarrierSource as Xt, IHttpCarrierRequest as Y, IHandshakeResult as Yn, IDuplexCarrier as Yt, httpCarrier as Z, THandshakeMessage as Zn, IExchangeCarrier as Zt, IActionPayload_Data_Base as _, ITransportRouteInfo as _n, IRunningActionUpdate_Success as _r, TServeHostOptions as _t, TAcceptorConnectionCaseFn as a, ETransportStatus as an, ActionLocalHandler as ar, err_nice_transport_ws as at, IActionPayload_Request_JsonObject as b, ITransportStatusInfo_Initializing as bn, TRunningActionUpdateListener as br, IConnectionContext as bt, createAcceptorHandler as c, IActionTransportReady as cn, createActionRootDomain as cr, IRtcDataChannelLike as ct, ActionCore as d, IActionTransportResolvers as dn, ERunningActionFinishedType as dr, inMemoryCarrier as dt, TFrame$1 as en, createServerHandshake as er, IWsCarrierOptions as et, ActionPayload_Request as f, ISecureClientConfig as fn, ERunningActionState as fr, IInMemoryChannelPair as ft, IActionPayload_Base_JsonObject as g, ITransportRouteClientParams as gn, IRunningActionUpdate_Started as gr, IChannelHostAdapter as gt, IActionPayload_Base as h, ITransportRouteActionParams as hn, IRunningActionUpdate_Progress as hr, err_nice_external_client as ht, TAcceptorCaseFn as i, ETransportShape as in, runtimeLinkId as ir, EErrId_NiceTransport_WebSocket as it, isActionPayload_Result_JsonObject as j, TSendActionDataMethod as jn, IActionDomain as jr, createHibernatableWsServerAdapter as jt, TActionProgress as k, TOnResolveIncomingResponse as kn, IActionCore as kr, IDuplexConnectionRouter as kt, ActionDomain as l, IActionTransportReadyData_Base as ln, ActionRootDomain as lr, rtcDataChannelByteChannel as lt, EActionProgressType as m, ITransportMethod_SendActionData_Input as mn, IRunningActionUpdate_Abort as mr, createInMemoryChannelPair as mt, IAcceptorConnectionBinding as n, createConnectorHandler as nn, decodeHandshakeMessage as nr, IWsAcceptorCarrierOptions as nt, TActionChannelFormatMessage as o, IActionTransportDef as on, createLocalHandler as or, IRtcCarrierOptions as ot, EActionPayloadType as p, ITransportDispatchAction as pn, ERunningActionUpdateType as pr, IInMemoryServerEndpoint as pt, err_nice_transport as q, IClientVerifyKeyResolver as qn, TTransportedValue as qr, createBinaryWireSessionFactory as qt, IAcceptorHandlerOptions as r, PeerLinkHandler as rn, encodeHandshakeMessage as rr, wsAcceptorCarrier as rt, TActionConnectionEncoding as s, IActionTransportInitialized as sn, MaybePromise as sr, rtcCarrier as st, AcceptorHandler as t, ConnectorHandler as tn, createStorageTofuVerifyKeyResolver as tr, wsCarrier as tt, ActionRuntime as u, IActionTransportReadyData_Methods as un, RunningAction as ur, IInMemoryCarrier as ut, IActionPayload_Progress as v, ITransportStatusInfo_Base as vn, TRunningActionUpdate as vr, serveHost as vt, IActionProgress_None as w, TGetTransportFn as wn, IRuntimeCoordinateSpecifics as wr, IAcceptorAttachmentStore as wt, IActionPayload_Result as x, ITransportStatusInfo_Ready as xn, ActionPayload_Result as xr, IServeChannelOptions as xt, IActionPayload_Progress_JsonObject as y, ITransportStatusInfo_Failed as yn, TRunningActionUpdateFinished as yr, IChannelServer as yt, TExchangeRequest as z, ITransportConnectionContext as zn, TPossibleDomainId as zr, IConnectTransport as zt };
3100
- //# sourceMappingURL=AcceptorHandler-BizUtq4u.d.mts.map
3158
+ //# sourceMappingURL=AcceptorHandler-CLbwu2Pa.d.mts.map
@@ -781,6 +781,16 @@ declare function createInMemoryTofuVerifyKeyResolver(): IClientVerifyKeyResolver
781
781
  * Storage-backed trust-on-first-use resolver: pins survive process restarts / Durable Object eviction
782
782
  * (e.g. back it with `createDurableObjectStorageAdapter`). Same policy as the in-memory variant — trust
783
783
  * + pin the first verify key per client identity, reject a different one thereafter.
784
+ *
785
+ * Fail-closed by construction: a thrown storage read (`getJson`) or first-pin write (`updateJsonWithDef`)
786
+ * propagates out of `resolve`, which makes `onProve` reject the handshake — a storage error can never be
787
+ * mistaken for "first use" and silently trusted. (A genuine `undefined`/absent read is the only path to
788
+ * a fresh pin; the underlying adapters never coerce a thrown read to `undefined`.) Keep it that way: do
789
+ * not wrap the storage calls in a `try/catch` that swallows the error.
790
+ *
791
+ * On an *eventually-consistent* store a stale "absent" read re-pins the **same** verify key the client
792
+ * just presented (it is signature-verified before this runs), so the worst case is a harmless re-write,
793
+ * never a weakened trust decision. Cross-isolate-strong pinning still wants a strongly-consistent store.
784
794
  */
785
795
  declare function createStorageTofuVerifyKeyResolver(storageAdapter: StorageAdapter): IClientVerifyKeyResolver;
786
796
  interface IClientHandshakeConfig {
@@ -808,11 +818,37 @@ interface IServerHandshakeConfig {
808
818
  /** Trust decision for a client's verify key. Defaults to in-memory TOFU. */
809
819
  verifyKeyResolver?: IClientVerifyKeyResolver;
810
820
  }
811
- declare function createServerHandshake(config: IServerHandshakeConfig): {
821
+ /**
822
+ * The server handshake's in-flight state between `onHello` and `onProve` — everything `onProve` needs
823
+ * to verify the client's proof and settle the result. Fully serializable (all JSON / serialized-key
824
+ * strings), so a stateless server can {@link IServerHandshake.exportPending} it after `hello`, carry it
825
+ * across requests (e.g. sealed into a continuation token), and {@link IServerHandshake.restorePending}
826
+ * it on a *different* instance before `prove` — no in-memory handshake to co-locate. The `serverNonce`
827
+ * is already folded into `challenge`, so it need not be carried separately.
828
+ */
829
+ interface IServerHandshakePending {
830
+ client: IRuntimeCoordinate;
831
+ linkedClientId: TTypeAndId;
832
+ challenge: string;
833
+ clientVerifyKey: TSerializedCryptoKeyData_Ed25519_Raw;
834
+ negotiatedLevel: ESecurityLevel;
835
+ keyMaterial?: IHandshakeEncryptionKeyMaterial;
836
+ }
837
+ interface IServerHandshake {
812
838
  onHello(hello: THsHello): Promise<THsWelcome | THsReject>;
813
- onProve(prove: THsProve): Promise<THsAccept | THsReject>; /** The completed handshake result once `onProve` has accepted, else `undefined`. */
839
+ onProve(prove: THsProve): Promise<THsAccept | THsReject>;
814
840
  getResult(): IHandshakeResult | undefined;
815
- };
841
+ /** The serializable in-flight state after `onHello` (else `undefined`). See {@link IServerHandshakePending}. */
842
+ exportPending(): IServerHandshakePending | undefined;
843
+ /**
844
+ * Restore exported `pending` onto a *fresh* handshake instance so `onProve` can run without the
845
+ * `onHello` that produced it (the stateless / cross-instance path). Re-links the client's verify key
846
+ * — `onHello`'s `linkClient` was in-memory only and won't exist on this instance — so the signature
847
+ * check in `onProve` succeeds. Build the instance with the same `config` (identity link + coordinate).
848
+ */
849
+ restorePending(pending: IServerHandshakePending): Promise<void>;
850
+ }
851
+ declare function createServerHandshake(config: IServerHandshakeConfig): IServerHandshake;
816
852
  //#endregion
817
853
  //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createSecureActionServer.d.ts
818
854
  interface ISecureAcceptorHandlerOptions<TConn> {
@@ -1847,6 +1883,14 @@ interface IServeChannelOptions<TO_ACCEPTOR extends readonly ActionDomain<any>[],
1847
1883
  *
1848
1884
  * Required only when at least one carrier is secure (the default). A fully-plain server (every carrier
1849
1885
  * `secure: false`) needs no storage and may omit it.
1886
+ *
1887
+ * The secure HTTP exchange is stateless — its handshake + session ride sealed tokens, so it touches
1888
+ * this store only for the (read-mostly) identity, never per session. That lets a single secure-exchange
1889
+ * server (`carriers: [httpAcceptorCarrier()]`) run on a stateless Worker/Node backend with no Durable
1890
+ * Object. On a *strongly-consistent* store (DO storage, D1, Node memory) the default lazy identity is
1891
+ * fork-safe. On an *eventually-consistent* store (Cloudflare KV) pass an explicit {@link link} built
1892
+ * with `identityMode: "required"` and `provisionIdentity()` it once out-of-band, so a transient read
1893
+ * miss can never fork a second identity (which pinned clients would then permanently reject).
1850
1894
  */
1851
1895
  storage?: StorageAdapter;
1852
1896
  /**
@@ -2287,29 +2331,39 @@ interface IExchangeAcceptorConfig {
2287
2331
  security: IExchangeAcceptorSecurity;
2288
2332
  /** The runtime that executes an inbound action wire and produces its result. */
2289
2333
  runtime: ActionRuntime;
2334
+ /**
2335
+ * How long a minted session ticket stays valid (ms). After it expires the client's next action is
2336
+ * rejected and it must re-handshake. Defaults to 12h — long enough for an ordinary session's life,
2337
+ * since a sealed ticket carries no server state to revoke before then. Keep it shorter for a more
2338
+ * tightly time-boxed session.
2339
+ */
2340
+ sessionTtlMs?: number;
2290
2341
  }
2291
2342
  /**
2292
2343
  * Acceptor (accept-in) side of the secure exchange protocol — the HTTP counterpart to
2293
2344
  * {@link AcceptorSecureSession}. Each POST body is one {@link decodeExchangeRequest} envelope; the
2294
- * acceptor drives the server handshake over the two `hs` POSTs (correlated by `hsid`, since stateless
2295
- * requests can't rely on channel ordering), mints a session **token** on accept, and on every later `act`
2296
- * POST resolves the session by token, decrypts the body (at `encrypted`), routes it through the runtime,
2297
- * and returns the (encrypted) result inline as the reply.
2345
+ * acceptor drives the server handshake over the two `hs` POSTs, mints a session **token** on accept, and
2346
+ * on every later `act` POST resolves the session by token, decrypts the body (at `encrypted`), routes it
2347
+ * through the runtime, and returns the (encrypted) result inline as the reply.
2298
2348
  *
2299
- * Sessions and in-flight handshakes are held in memory fine for a single-instance server. (Surviving a
2300
- * Durable-Object eviction would persist each token's `keyMaterial` and re-derive the key on a miss, the
2301
- * same primitive `AcceptorSecureSession.rehydrate` uses; left as a follow-up.)
2349
+ * **Stateless.** It holds no in-memory handshakes or sessions: the in-flight handshake `pending` is
2350
+ * sealed into the `hsc` continuation token returned on `welcome` and echoed back on `prove`, and the live
2351
+ * session is sealed into the `t` token replayed on every `act`. Both are sealed under the acceptor's own
2352
+ * persisted identity ({@link createExchangeTicketSealer}), so any isolate that loaded the same identity
2353
+ * can serve any POST — no request needs to co-locate with another (no Durable Object required just to
2354
+ * pin a handshake to one instance). A tampered, wrong-key, or expired token opens to "no valid session".
2302
2355
  */
2303
2356
  declare class ExchangeAcceptor {
2304
2357
  private readonly _security;
2305
2358
  private readonly _runtime;
2306
2359
  private readonly _allowedLevels;
2307
2360
  private readonly _noneAllowed;
2308
- private readonly _pendingHandshakes;
2309
- private readonly _sessions;
2361
+ private readonly _sealer;
2362
+ private readonly _sessionTtlMs;
2310
2363
  constructor(config: IExchangeAcceptorConfig);
2311
2364
  /** Process one POST body (an exchange envelope), returning the reply body to send back. */
2312
2365
  handlePost(body: string): Promise<string>;
2366
+ private _makeHandshake;
2313
2367
  private _handleHandshake;
2314
2368
  private _handleAction;
2315
2369
  private _err;
@@ -2327,15 +2381,18 @@ declare class ExchangeAcceptor {
2327
2381
  * the plaintext wire.
2328
2382
  * - `encrypted` — same, but the wire is AES-GCM ciphertext, base64 in the `c` field.
2329
2383
  *
2330
- * The handshake runs as two `hs` exchanges (hello→welcome, prove→accept) correlated by a client-chosen
2331
- * `hsid`, since stateless requests can't rely on channel ordering. The `accept` reply carries the token.
2384
+ * The handshake runs as two `hs` exchanges (hello→welcome, prove→accept). The acceptor keeps **no**
2385
+ * in-flight state between them: the `welcome` reply carries a sealed handshake-continuation token `hsc`
2386
+ * (the acceptor's `pending` state, opaque to the client), which the connector echoes on the `prove`
2387
+ * request. The `accept` reply then carries the (sealed) session `t`oken replayed on every later `act`.
2388
+ * All server-side continuity rides these sealed tokens, so no request needs to co-locate with another.
2332
2389
  */
2333
2390
  type TWireJson = TActionPayload_Any_JsonObject<any, any>;
2334
2391
  /** Connector → acceptor request envelope. */
2335
2392
  type TExchangeRequest = {
2336
2393
  k: "hs";
2337
- hsid: string;
2338
2394
  m: string;
2395
+ hsc?: string;
2339
2396
  } | {
2340
2397
  k: "act";
2341
2398
  t?: string;
@@ -2349,6 +2406,7 @@ type TExchangeRequest = {
2349
2406
  type TExchangeReply = {
2350
2407
  k: "hs";
2351
2408
  m: string;
2409
+ hsc?: string;
2352
2410
  t?: string;
2353
2411
  } | {
2354
2412
  k: "act";
@@ -3097,4 +3155,4 @@ declare class AcceptorHandler<TConn = unknown> extends PeerLinkHandler {
3097
3155
  declare const createAcceptorHandler: <TConn = unknown>(options: IAcceptorHandlerOptions<TConn>) => AcceptorHandler<TConn>;
3098
3156
  //#endregion
3099
3157
  export { httpAcceptorCarrier as $, createInMemoryTofuVerifyKeyResolver as $n, TCarrier as $t, TActionResultOutcome as A, TOnResolveIncomingResponseJson as An, IActionCore_JsonObject as Ar, IHibernatableWsServerAdapterOptions as At, decodeExchangeReply as B, Transport as Bn, TPossibleDomainIdList as Br, TChannelAcceptorCases as Bt, IActionProgress_Custom as C, IUpdateActionRunConfig_Output as Cn, IRuntimeCoordinate as Cr, serveChannel as Ct, TActionPayload_Any_Instance as D, TOnResolveIncomingRequest as Dn, TRuntimeCoordinateEnvId as Dr, TAcceptorCarrier as Dt, IActionRouteItemHandler as E, TOnResolveAnyIncomingActionData_Json as En, RuntimeCoordinate as Er, IExchangeAcceptorCarrier as Et, decodeActionFrame as F, TTransportStatusInfo as Fn, TActionDomainSchema as Fr, createConnectionStateStore as Ft, IExchangeAcceptorSecurity as G, IClientHandshakeConfig as Gn, TActionSchemaOptions as Gr, defineChannel as Gt, encodeExchange as H, createSecureAcceptorHandler as Hn, EActionResponseMode as Hr, acceptChannel as Ht, EErrId_NiceAction as I, TTransportStatusInfo_GetTransport_Output as In, TDomainActionId as Ir, IAcceptChannelOptions as It, IHttpCarrierOptions as J, IHandshakeEncryptionKeyMaterial as Jn, IActionWireFormat as Jt, EErrId_NiceTransport as K, IClientVerifyKeyResolveInput as Kn, TActionSerializationDefinition as Kr, IBinaryWireSessionOptions as Kt, err_nice_action as L, TUpdateActionRunConfig as Ln, TInferInputFromSchema as Lr, IActionChannel as Lt, isActionPayload_Request_JsonObject as M, TSendReturnDataMethod as Mn, IActionDomainChildOptions as Mr, ConnectionStateStore as Mt, isActionPayload_Any_JsonObject as N, TTransportCache as Nn, IActionRootDomain as Nr, IConnectionAttachment as Nt, TActionPayload_Any_JsonObject as O, TOnResolveIncomingRequestJson as On, TRuntimeCoordinateStringId as Or, isExchangeAcceptorCarrier as Ot, IActionFrameDecoder as P, TTransportInitializationFinishedInfo as Pn, TActionDomainChildDef as Pr, IConnectionStateStoreOptions as Pt, IHttpAcceptorCarrierOptions as Q, createClientHandshake as Qn, IExchangeCarrierSource as Qt, TExchangeReply as R, TransportConnection as Rn, TInferOutputFromSchema as Rr, IConnectChannelOptions as Rt, IActionPayload_Result_JsonObject as S, ITransportStatusInfo_Unsupported as Sn, ActionPayload_Progress as Sr, IServeConnectionStateOptions as St, IActionProgress_Percentage as T, TOnResolveAnyIncomingActionData as Tn, IRuntimeFullCoordinates as Tr, IDuplexAcceptorCarrier as Tt, ExchangeAcceptor as U, EHandshakeMessageType as Un, TInferActionError as Ur, acceptChannelConnections as Ut, decodeExchangeRequest as V, ISecureAcceptorHandlerOptions as Vn, ActionSchema as Vr, TChannelPushHandlers as Vt, IExchangeAcceptorConfig as W, ESecurityLevel as Wn, actionSchema as Wr, connectChannel as Wt, TCarrierFetch as X, IServerHandshakeConfig as Xn, IDuplexCarrierSource as Xt, IHttpCarrierRequest as Y, IHandshakeResult as Yn, IDuplexCarrier as Yt, httpCarrier as Z, THandshakeMessage as Zn, IExchangeCarrier as Zt, IActionPayload_Data_Base as _, ITransportRouteInfo as _n, IRunningActionUpdate_Success as _r, TServeHostOptions as _t, TAcceptorConnectionCaseFn as a, ETransportStatus as an, ActionLocalHandler as ar, err_nice_transport_ws as at, IActionPayload_Request_JsonObject as b, ITransportStatusInfo_Initializing as bn, TRunningActionUpdateListener as br, IConnectionContext as bt, createAcceptorHandler as c, IActionTransportReady as cn, createActionRootDomain as cr, IRtcDataChannelLike as ct, ActionCore as d, IActionTransportResolvers as dn, ERunningActionFinishedType as dr, inMemoryCarrier as dt, TFrame$1 as en, createServerHandshake as er, IWsCarrierOptions as et, ActionPayload_Request as f, ISecureClientConfig as fn, ERunningActionState as fr, IInMemoryChannelPair as ft, IActionPayload_Base_JsonObject as g, ITransportRouteClientParams as gn, IRunningActionUpdate_Started as gr, IChannelHostAdapter as gt, IActionPayload_Base as h, ITransportRouteActionParams as hn, IRunningActionUpdate_Progress as hr, err_nice_external_client as ht, TAcceptorCaseFn as i, ETransportShape as in, runtimeLinkId as ir, EErrId_NiceTransport_WebSocket as it, isActionPayload_Result_JsonObject as j, TSendActionDataMethod as jn, IActionDomain as jr, createHibernatableWsServerAdapter as jt, TActionProgress as k, TOnResolveIncomingResponse as kn, IActionCore as kr, IDuplexConnectionRouter as kt, ActionDomain as l, IActionTransportReadyData_Base as ln, ActionRootDomain as lr, rtcDataChannelByteChannel as lt, EActionProgressType as m, ITransportMethod_SendActionData_Input as mn, IRunningActionUpdate_Abort as mr, createInMemoryChannelPair as mt, IAcceptorConnectionBinding as n, createConnectorHandler as nn, decodeHandshakeMessage as nr, IWsAcceptorCarrierOptions as nt, TActionChannelFormatMessage as o, IActionTransportDef as on, createLocalHandler as or, IRtcCarrierOptions as ot, EActionPayloadType as p, ITransportDispatchAction as pn, ERunningActionUpdateType as pr, IInMemoryServerEndpoint as pt, err_nice_transport as q, IClientVerifyKeyResolver as qn, TTransportedValue as qr, createBinaryWireSessionFactory as qt, IAcceptorHandlerOptions as r, PeerLinkHandler as rn, encodeHandshakeMessage as rr, wsAcceptorCarrier as rt, TActionConnectionEncoding as s, IActionTransportInitialized as sn, MaybePromise as sr, rtcCarrier as st, AcceptorHandler as t, ConnectorHandler as tn, createStorageTofuVerifyKeyResolver as tr, wsCarrier as tt, ActionRuntime as u, IActionTransportReadyData_Methods as un, RunningAction as ur, IInMemoryCarrier as ut, IActionPayload_Progress as v, ITransportStatusInfo_Base as vn, TRunningActionUpdate as vr, serveHost as vt, IActionProgress_None as w, TGetTransportFn as wn, IRuntimeCoordinateSpecifics as wr, IAcceptorAttachmentStore as wt, IActionPayload_Result as x, ITransportStatusInfo_Ready as xn, ActionPayload_Result as xr, IServeChannelOptions as xt, IActionPayload_Progress_JsonObject as y, ITransportStatusInfo_Failed as yn, TRunningActionUpdateFinished as yr, IChannelServer as yt, TExchangeRequest as z, ITransportConnectionContext as zn, TPossibleDomainId as zr, IConnectTransport as zt };
3100
- //# sourceMappingURL=AcceptorHandler-CxPfZtIl.d.cts.map
3158
+ //# sourceMappingURL=AcceptorHandler-Du292dpC.d.cts.map
@@ -1,4 +1,4 @@
1
- import { Cr as IRuntimeCoordinate, k as TActionProgress } from "./AcceptorHandler-CxPfZtIl.cjs";
1
+ import { Cr as IRuntimeCoordinate, k as TActionProgress } from "./AcceptorHandler-CLbwu2Pa.mjs";
2
2
  //#region ../nice-devtools-shared/src/components/PanelChrome.d.ts
3
3
  /** Where a devtools panel is docked. */
4
4
  type TDevtoolsPosition = "dock-bottom" | "dock-top" | "dock-left" | "dock-right";
@@ -76,4 +76,4 @@ declare class ActionDevtoolsCore {
76
76
  }
77
77
  //#endregion
78
78
  export { TDevtoolsActionStatus as a, IDevtoolsObservableDomain as i, IActionDevtoolsCoreOptions as n, TDevtoolsListener as o, IDevtoolsActionEntry as r, TDevtoolsPosition as s, ActionDevtoolsCore as t };
79
- //# sourceMappingURL=ActionDevtoolsCore-D9KBBI2V.d.cts.map
79
+ //# sourceMappingURL=ActionDevtoolsCore-DGwzONZT.d.mts.map
@@ -1,4 +1,4 @@
1
- import { Cr as IRuntimeCoordinate, k as TActionProgress } from "./AcceptorHandler-BizUtq4u.mjs";
1
+ import { Cr as IRuntimeCoordinate, k as TActionProgress } from "./AcceptorHandler-Du292dpC.cjs";
2
2
  //#region ../nice-devtools-shared/src/components/PanelChrome.d.ts
3
3
  /** Where a devtools panel is docked. */
4
4
  type TDevtoolsPosition = "dock-bottom" | "dock-top" | "dock-left" | "dock-right";
@@ -76,4 +76,4 @@ declare class ActionDevtoolsCore {
76
76
  }
77
77
  //#endregion
78
78
  export { TDevtoolsActionStatus as a, IDevtoolsObservableDomain as i, IActionDevtoolsCoreOptions as n, TDevtoolsListener as o, IDevtoolsActionEntry as r, TDevtoolsPosition as s, ActionDevtoolsCore as t };
79
- //# sourceMappingURL=ActionDevtoolsCore-xZjAtB4H.d.mts.map
79
+ //# sourceMappingURL=ActionDevtoolsCore-dH4K4w3B.d.cts.map
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_createHibernatableWsServerAdapter = require("../createHibernatableWsServerAdapter-FSDWrxoF.cjs");
2
+ const require_createHibernatableWsServerAdapter = require("../createHibernatableWsServerAdapter-j96U9vgo.cjs");
3
3
  let msgpackr = require("msgpackr");
4
4
  //#region src/ActionRuntime/Transport/codec/createBinaryWireAdapter.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { An as TOnResolveIncomingResponseJson, At as IHibernatableWsServerAdapterOptions, B as decodeExchangeReply, Bn as Transport, Cn as IUpdateActionRunConfig_Output, Dn as TOnResolveIncomingRequest, En as TOnResolveAnyIncomingActionData_Json, Fn as TTransportStatusInfo, Ft as createConnectionStateStore, G as IExchangeAcceptorSecurity, Gn as IClientHandshakeConfig, H as encodeExchange, Hn as createSecureAcceptorHandler, In as TTransportStatusInfo_GetTransport_Output, Jn as IHandshakeEncryptionKeyMaterial, Jt as IActionWireFormat, Kt as IBinaryWireSessionOptions, Ln as TUpdateActionRunConfig, Mn as TSendReturnDataMethod, Mt as ConnectionStateStore, Nn as TTransportCache, Nt as IConnectionAttachment, On as TOnResolveIncomingRequestJson, Pn as TTransportInitializationFinishedInfo, Pt as IConnectionStateStoreOptions, Qn as createClientHandshake, R as TExchangeReply, Rn as TransportConnection, Sn as ITransportStatusInfo_Unsupported, Tn as TOnResolveAnyIncomingActionData, U as ExchangeAcceptor, Un as EHandshakeMessageType, V as decodeExchangeRequest, Vn as ISecureAcceptorHandlerOptions, W as IExchangeAcceptorConfig, Xn as IServerHandshakeConfig, Yn as IHandshakeResult, Yt as IDuplexCarrier, Zn as THandshakeMessage, Zt as IExchangeCarrier, _n as ITransportRouteInfo, a as TAcceptorConnectionCaseFn, an as ETransportStatus, bn as ITransportStatusInfo_Initializing, c as createAcceptorHandler, cn as IActionTransportReady, dn as IActionTransportResolvers, er as createServerHandshake, fn as ISecureClientConfig, gn as ITransportRouteClientParams, hn as ITransportRouteActionParams, i as TAcceptorCaseFn, in as ETransportShape, jn as TSendActionDataMethod, jt as createHibernatableWsServerAdapter, kn as TOnResolveIncomingResponse, kt as IDuplexConnectionRouter, l as ActionDomain, ln as IActionTransportReadyData_Base, mn as ITransportMethod_SendActionData_Input, n as IAcceptorConnectionBinding, nn as createConnectorHandler, nr as decodeHandshakeMessage, o as TActionChannelFormatMessage, on as IActionTransportDef, pn as ITransportDispatchAction, qt as createBinaryWireSessionFactory, r as IAcceptorHandlerOptions, rn as PeerLinkHandler, rr as encodeHandshakeMessage, s as TActionConnectionEncoding, sn as IActionTransportInitialized, t as AcceptorHandler, tn as ConnectorHandler, u as ActionRuntime, un as IActionTransportReadyData_Methods, vn as ITransportStatusInfo_Base, wn as TGetTransportFn, xn as ITransportStatusInfo_Ready, yn as ITransportStatusInfo_Failed, z as TExchangeRequest, zn as ITransportConnectionContext } from "../AcceptorHandler-CxPfZtIl.cjs";
2
- import { ClientCryptoKeyLink, TTypeAndId } from "@nice-code/util";
1
+ import { An as TOnResolveIncomingResponseJson, At as IHibernatableWsServerAdapterOptions, B as decodeExchangeReply, Bn as Transport, Cn as IUpdateActionRunConfig_Output, Dn as TOnResolveIncomingRequest, En as TOnResolveAnyIncomingActionData_Json, Fn as TTransportStatusInfo, Ft as createConnectionStateStore, G as IExchangeAcceptorSecurity, Gn as IClientHandshakeConfig, H as encodeExchange, Hn as createSecureAcceptorHandler, In as TTransportStatusInfo_GetTransport_Output, Jn as IHandshakeEncryptionKeyMaterial, Jt as IActionWireFormat, Kt as IBinaryWireSessionOptions, Ln as TUpdateActionRunConfig, Mn as TSendReturnDataMethod, Mt as ConnectionStateStore, Nn as TTransportCache, Nt as IConnectionAttachment, On as TOnResolveIncomingRequestJson, Pn as TTransportInitializationFinishedInfo, Pt as IConnectionStateStoreOptions, Qn as createClientHandshake, R as TExchangeReply, Rn as TransportConnection, Sn as ITransportStatusInfo_Unsupported, Tn as TOnResolveAnyIncomingActionData, U as ExchangeAcceptor, Un as EHandshakeMessageType, V as decodeExchangeRequest, Vn as ISecureAcceptorHandlerOptions, W as IExchangeAcceptorConfig, Xn as IServerHandshakeConfig, Yn as IHandshakeResult, Yt as IDuplexCarrier, Zn as THandshakeMessage, Zt as IExchangeCarrier, _n as ITransportRouteInfo, a as TAcceptorConnectionCaseFn, an as ETransportStatus, bn as ITransportStatusInfo_Initializing, c as createAcceptorHandler, cn as IActionTransportReady, dn as IActionTransportResolvers, er as createServerHandshake, fn as ISecureClientConfig, gn as ITransportRouteClientParams, hn as ITransportRouteActionParams, i as TAcceptorCaseFn, in as ETransportShape, jn as TSendActionDataMethod, jt as createHibernatableWsServerAdapter, kn as TOnResolveIncomingResponse, kt as IDuplexConnectionRouter, l as ActionDomain, ln as IActionTransportReadyData_Base, mn as ITransportMethod_SendActionData_Input, n as IAcceptorConnectionBinding, nn as createConnectorHandler, nr as decodeHandshakeMessage, o as TActionChannelFormatMessage, on as IActionTransportDef, pn as ITransportDispatchAction, qt as createBinaryWireSessionFactory, r as IAcceptorHandlerOptions, rn as PeerLinkHandler, rr as encodeHandshakeMessage, s as TActionConnectionEncoding, sn as IActionTransportInitialized, t as AcceptorHandler, tn as ConnectorHandler, u as ActionRuntime, un as IActionTransportReadyData_Methods, vn as ITransportStatusInfo_Base, wn as TGetTransportFn, xn as ITransportStatusInfo_Ready, yn as ITransportStatusInfo_Failed, z as TExchangeRequest, zn as ITransportConnectionContext } from "../AcceptorHandler-Du292dpC.cjs";
2
+ import { ClientCryptoKeyLink } from "@nice-code/util";
3
3
 
4
4
  //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createActionFetchHandler.d.ts
5
5
  interface IActionFetchHandlerOptions {
@@ -99,16 +99,22 @@ interface IActionFrameCrypto {
99
99
  }
100
100
  interface IActionFrameCryptoConfig {
101
101
  link: ClientCryptoKeyLink;
102
- /** The handshake-established link id for the remote (key + connection-registry id). */
103
- linkedClientId: TTypeAndId;
102
+ /**
103
+ * This session's handshake key material. The shared AES-GCM key is derived from it **once** and held
104
+ * for the life of the connection — it is NOT re-read per frame from the link's per-`linkedClientId`
105
+ * cache, so a second secure session to the same peer (e.g. a secure HTTP exchange beside a secure
106
+ * WebSocket, both sharing one DO crypto identity) can't overwrite this connection's key.
107
+ */
108
+ keyMaterial: IHandshakeEncryptionKeyMaterial;
104
109
  }
105
110
  /**
106
111
  * Build the encrypt/decrypt transform for a connection whose handshake settled on the `encrypted`
107
- * level. Keyed by the link + `linkedClientId`, so it reuses the cached shared AES-GCM key.
112
+ * level. The shared key is derived once from {@link IActionFrameCryptoConfig.keyMaterial} and captured
113
+ * for this connection alone, decoupling it from the link's shared key cache.
108
114
  */
109
115
  declare function createActionFrameCrypto({
110
116
  link,
111
- linkedClientId
117
+ keyMaterial
112
118
  }: IActionFrameCryptoConfig): IActionFrameCrypto;
113
119
  //#endregion
114
120
  //#region src/ActionRuntime/Transport/Exchange/TransportExchange.types.d.ts
@@ -1,5 +1,5 @@
1
- import { An as TOnResolveIncomingResponseJson, At as IHibernatableWsServerAdapterOptions, B as decodeExchangeReply, Bn as Transport, Cn as IUpdateActionRunConfig_Output, Dn as TOnResolveIncomingRequest, En as TOnResolveAnyIncomingActionData_Json, Fn as TTransportStatusInfo, Ft as createConnectionStateStore, G as IExchangeAcceptorSecurity, Gn as IClientHandshakeConfig, H as encodeExchange, Hn as createSecureAcceptorHandler, In as TTransportStatusInfo_GetTransport_Output, Jn as IHandshakeEncryptionKeyMaterial, Jt as IActionWireFormat, Kt as IBinaryWireSessionOptions, Ln as TUpdateActionRunConfig, Mn as TSendReturnDataMethod, Mt as ConnectionStateStore, Nn as TTransportCache, Nt as IConnectionAttachment, On as TOnResolveIncomingRequestJson, Pn as TTransportInitializationFinishedInfo, Pt as IConnectionStateStoreOptions, Qn as createClientHandshake, R as TExchangeReply, Rn as TransportConnection, Sn as ITransportStatusInfo_Unsupported, Tn as TOnResolveAnyIncomingActionData, U as ExchangeAcceptor, Un as EHandshakeMessageType, V as decodeExchangeRequest, Vn as ISecureAcceptorHandlerOptions, W as IExchangeAcceptorConfig, Xn as IServerHandshakeConfig, Yn as IHandshakeResult, Yt as IDuplexCarrier, Zn as THandshakeMessage, Zt as IExchangeCarrier, _n as ITransportRouteInfo, a as TAcceptorConnectionCaseFn, an as ETransportStatus, bn as ITransportStatusInfo_Initializing, c as createAcceptorHandler, cn as IActionTransportReady, dn as IActionTransportResolvers, er as createServerHandshake, fn as ISecureClientConfig, gn as ITransportRouteClientParams, hn as ITransportRouteActionParams, i as TAcceptorCaseFn, in as ETransportShape, jn as TSendActionDataMethod, jt as createHibernatableWsServerAdapter, kn as TOnResolveIncomingResponse, kt as IDuplexConnectionRouter, l as ActionDomain, ln as IActionTransportReadyData_Base, mn as ITransportMethod_SendActionData_Input, n as IAcceptorConnectionBinding, nn as createConnectorHandler, nr as decodeHandshakeMessage, o as TActionChannelFormatMessage, on as IActionTransportDef, pn as ITransportDispatchAction, qt as createBinaryWireSessionFactory, r as IAcceptorHandlerOptions, rn as PeerLinkHandler, rr as encodeHandshakeMessage, s as TActionConnectionEncoding, sn as IActionTransportInitialized, t as AcceptorHandler, tn as ConnectorHandler, u as ActionRuntime, un as IActionTransportReadyData_Methods, vn as ITransportStatusInfo_Base, wn as TGetTransportFn, xn as ITransportStatusInfo_Ready, yn as ITransportStatusInfo_Failed, z as TExchangeRequest, zn as ITransportConnectionContext } from "../AcceptorHandler-BizUtq4u.mjs";
2
- import { ClientCryptoKeyLink, TTypeAndId } from "@nice-code/util";
1
+ import { An as TOnResolveIncomingResponseJson, At as IHibernatableWsServerAdapterOptions, B as decodeExchangeReply, Bn as Transport, Cn as IUpdateActionRunConfig_Output, Dn as TOnResolveIncomingRequest, En as TOnResolveAnyIncomingActionData_Json, Fn as TTransportStatusInfo, Ft as createConnectionStateStore, G as IExchangeAcceptorSecurity, Gn as IClientHandshakeConfig, H as encodeExchange, Hn as createSecureAcceptorHandler, In as TTransportStatusInfo_GetTransport_Output, Jn as IHandshakeEncryptionKeyMaterial, Jt as IActionWireFormat, Kt as IBinaryWireSessionOptions, Ln as TUpdateActionRunConfig, Mn as TSendReturnDataMethod, Mt as ConnectionStateStore, Nn as TTransportCache, Nt as IConnectionAttachment, On as TOnResolveIncomingRequestJson, Pn as TTransportInitializationFinishedInfo, Pt as IConnectionStateStoreOptions, Qn as createClientHandshake, R as TExchangeReply, Rn as TransportConnection, Sn as ITransportStatusInfo_Unsupported, Tn as TOnResolveAnyIncomingActionData, U as ExchangeAcceptor, Un as EHandshakeMessageType, V as decodeExchangeRequest, Vn as ISecureAcceptorHandlerOptions, W as IExchangeAcceptorConfig, Xn as IServerHandshakeConfig, Yn as IHandshakeResult, Yt as IDuplexCarrier, Zn as THandshakeMessage, Zt as IExchangeCarrier, _n as ITransportRouteInfo, a as TAcceptorConnectionCaseFn, an as ETransportStatus, bn as ITransportStatusInfo_Initializing, c as createAcceptorHandler, cn as IActionTransportReady, dn as IActionTransportResolvers, er as createServerHandshake, fn as ISecureClientConfig, gn as ITransportRouteClientParams, hn as ITransportRouteActionParams, i as TAcceptorCaseFn, in as ETransportShape, jn as TSendActionDataMethod, jt as createHibernatableWsServerAdapter, kn as TOnResolveIncomingResponse, kt as IDuplexConnectionRouter, l as ActionDomain, ln as IActionTransportReadyData_Base, mn as ITransportMethod_SendActionData_Input, n as IAcceptorConnectionBinding, nn as createConnectorHandler, nr as decodeHandshakeMessage, o as TActionChannelFormatMessage, on as IActionTransportDef, pn as ITransportDispatchAction, qt as createBinaryWireSessionFactory, r as IAcceptorHandlerOptions, rn as PeerLinkHandler, rr as encodeHandshakeMessage, s as TActionConnectionEncoding, sn as IActionTransportInitialized, t as AcceptorHandler, tn as ConnectorHandler, u as ActionRuntime, un as IActionTransportReadyData_Methods, vn as ITransportStatusInfo_Base, wn as TGetTransportFn, xn as ITransportStatusInfo_Ready, yn as ITransportStatusInfo_Failed, z as TExchangeRequest, zn as ITransportConnectionContext } from "../AcceptorHandler-CLbwu2Pa.mjs";
2
+ import { ClientCryptoKeyLink } from "@nice-code/util";
3
3
 
4
4
  //#region src/ActionRuntime/Handler/PeerLink/Acceptor/createActionFetchHandler.d.ts
5
5
  interface IActionFetchHandlerOptions {
@@ -99,16 +99,22 @@ interface IActionFrameCrypto {
99
99
  }
100
100
  interface IActionFrameCryptoConfig {
101
101
  link: ClientCryptoKeyLink;
102
- /** The handshake-established link id for the remote (key + connection-registry id). */
103
- linkedClientId: TTypeAndId;
102
+ /**
103
+ * This session's handshake key material. The shared AES-GCM key is derived from it **once** and held
104
+ * for the life of the connection — it is NOT re-read per frame from the link's per-`linkedClientId`
105
+ * cache, so a second secure session to the same peer (e.g. a secure HTTP exchange beside a secure
106
+ * WebSocket, both sharing one DO crypto identity) can't overwrite this connection's key.
107
+ */
108
+ keyMaterial: IHandshakeEncryptionKeyMaterial;
104
109
  }
105
110
  /**
106
111
  * Build the encrypt/decrypt transform for a connection whose handshake settled on the `encrypted`
107
- * level. Keyed by the link + `linkedClientId`, so it reuses the cached shared AES-GCM key.
112
+ * level. The shared key is derived once from {@link IActionFrameCryptoConfig.keyMaterial} and captured
113
+ * for this connection alone, decoupling it from the link's shared key cache.
108
114
  */
109
115
  declare function createActionFrameCrypto({
110
116
  link,
111
- linkedClientId
117
+ keyMaterial
112
118
  }: IActionFrameCryptoConfig): IActionFrameCrypto;
113
119
  //#endregion
114
120
  //#region src/ActionRuntime/Transport/Exchange/TransportExchange.types.d.ts
@@ -1,4 +1,4 @@
1
- import { A as encodeHandshakeMessage, C as EHandshakeMessageType, D as createServerHandshake, F as ConnectorHandler, I as createConnectorHandler, L as PeerLinkHandler, R as ETransportShape, T as createClientHandshake, _ as extractWirePayload, a as ExchangeAcceptor, b as createAcceptorHandler, c as decodeExchangeReply, d as Transport, f as createBinaryWireSessionFactory, g as buildActionRouteDictionary, h as assembleWireJson, i as createActionFetchHandler, k as decodeHandshakeMessage, l as decodeExchangeRequest, m as ReversePayloadType, n as ConnectionStateStore, o as LinkTransport, p as PayloadTypeToInt, r as createConnectionStateStore, s as ExchangeTransport, t as createHibernatableWsServerAdapter, u as encodeExchange, v as createSecureAcceptorHandler, x as createActionFrameCrypto, y as AcceptorHandler, z as ETransportStatus } from "../createHibernatableWsServerAdapter-BkjESd01.mjs";
1
+ import { A as encodeHandshakeMessage, C as EHandshakeMessageType, D as createServerHandshake, F as ConnectorHandler, I as createConnectorHandler, L as PeerLinkHandler, R as ETransportShape, T as createClientHandshake, _ as extractWirePayload, a as ExchangeAcceptor, b as createAcceptorHandler, c as decodeExchangeReply, d as Transport, f as createBinaryWireSessionFactory, g as buildActionRouteDictionary, h as assembleWireJson, i as createActionFetchHandler, k as decodeHandshakeMessage, l as decodeExchangeRequest, m as ReversePayloadType, n as ConnectionStateStore, o as LinkTransport, p as PayloadTypeToInt, r as createConnectionStateStore, s as ExchangeTransport, t as createHibernatableWsServerAdapter, u as encodeExchange, v as createSecureAcceptorHandler, x as createActionFrameCrypto, y as AcceptorHandler, z as ETransportStatus } from "../createHibernatableWsServerAdapter-BD5n-Ev9.mjs";
2
2
  import { pack, unpack } from "msgpackr";
3
3
  //#region src/ActionRuntime/Transport/codec/createBinaryWireAdapter.ts
4
4
  /**