@smthrs/gateway 1.0.0-rc.7 → 1.0.0-rc.8

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
@@ -14,12 +14,12 @@ The mounts, the bind and credential policy, the projections and their rows, the
14
14
 
15
15
  ## Install
16
16
 
17
- Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
17
+ Published on npm under the `next` tag. Name `@next` explicitly: `latest` still points at 0.35.0, an older release.
18
18
 
19
19
  Node 26.4.0 or later is required. `effect@^4.0.4` is a required peer.
20
20
 
21
21
  ```bash
22
- pnpm add @smthrs/gateway effect
22
+ pnpm add @smthrs/gateway@next effect@^4.0.4
23
23
  ```
24
24
 
25
25
  ## Public API
@@ -1 +1 @@
1
- {"version":3,"file":"GatewayServer.d.ts","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GatewayServer.d.ts","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"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"}
@@ -232,6 +232,7 @@ const rpcPaths = ["/rpc", "/projections", "/sync", "/branch"];
232
232
  const boundedPostPaths = [
233
233
  ...rpcPaths,
234
234
  "/runtime/v1/command",
235
+ "/runtime/v1/monitor",
235
236
  "/runtime/v1/observe"
236
237
  ];
237
238
  const protectedPaths = [
@@ -242,6 +243,7 @@ const protectedPaths = [
242
243
  "/projections/ws",
243
244
  "/sync/ws",
244
245
  "/runtime/v1/command",
246
+ "/runtime/v1/monitor",
245
247
  "/runtime/v1/observe"
246
248
  ];
247
249
  const routedPath = (url) => {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/GatewayServer.ts"],
4
- "sourcesContent": ["/**\n * The assembled workspace gateway: one HTTP surface carrying the control\n * plane, the sync read path, the served projections, and a health probe.\n *\n * Mounts:\n *\n * | Path | Protocol | Serves |\n * | --- | --- | --- |\n * | `POST /rpc` | RPC over HTTP | `@smthrs/control` `ControlRpcs` |\n * | `/rpc/ws` | RPC over WebSocket | `ControlRpcs`, including a kept-alive `Watch` |\n * | `POST /projections` | RPC over HTTP | `GatewayRpcs` |\n * | `/projections/ws` | RPC over WebSocket | `GatewayRpcs`, including `Projection.Subscribe` |\n * | `POST /sync` | RPC over HTTP | `@smthrs/sync` `SyncRpcs` |\n * | `/sync/ws` | RPC over WebSocket | `SyncRpcs` |\n * | `GET /health` | JSON | `GatewaySchema.GatewayHealth` plus the package version |\n * | `POST /auth/ticket` | JSON | A single-use {@link Ticket} that opens one protected socket |\n *\n * `/health` is deliberately unauthenticated: a supervisor decides whether to\n * keep or replace a gateway process by asking which workspace it belongs to,\n * and a probe that needed a credential could not answer that question about a\n * gateway it did not start. The response carries identity only: the\n * workspace hash, the gateway id, the protocol version, and the package\n * version. It never carries a token, a run, or a path.\n *\n * @since 1.0.0\n */\n\nimport { Control } from \"@smthrs/control/Control\"\nimport * as ControlError from \"@smthrs/control/ControlError\"\nimport * as ControlExecutor from \"@smthrs/control/ControlExecutor\"\nimport { ControlPrincipal } from \"@smthrs/control/ControlRpcs\"\nimport type * as ControlSchema from \"@smthrs/control/ControlSchema\"\nimport * as ControlServer from \"@smthrs/control/ControlServer\"\nimport { SyncRpcs } from \"@smthrs/sync/SyncRpcs\"\nimport * as SyncServer from \"@smthrs/sync/SyncServer\"\nimport { Effect, Layer, Option, Schema, Stream, type Types } from \"effect\"\nimport * as ByteSize from \"effect/ByteSize\"\nimport { Headers, HttpRouter, HttpServerRequest, HttpServerResponse } from \"effect/http\"\nimport { RpcSerialization, RpcServer } from \"effect/rpc\"\nimport { GatewayError, settingRefusal } from \"./GatewayError.ts\"\nimport { GatewayRpcs } from \"./GatewayRpcs.ts\"\nimport * as GatewaySchema from \"./GatewaySchema.ts\"\nimport * as HostBranchPresence from \"./HostBranchPresence.ts\"\nimport { heartbeatIntervalMillis, Projections } from \"./Projections.ts\"\nimport * as RunHistory from \"./RunHistory.ts\"\nimport * as RuntimeBridge from \"./RuntimeBridge.ts\"\n\n/**\n * What `GET /health` answers: the `GatewaySchema.GatewayHealth` identity plus\n * the version of the package serving it.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Health = Schema.Struct({\n ...GatewaySchema.GatewayHealth.fields,\n version: Schema.String,\n runtimeBridge: Schema.optional(RuntimeBridge.Identity)\n})\n\n/**\n * What `GET /health` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Health = typeof Health.Type\n\nconst encodeHealth = Schema.encodeUnknownSync(Health)\n\n/**\n * Mounts the unauthenticated health probe.\n *\n * @param health the identity this process serves under\n * @since 1.0.0\n * @category layers\n */\nexport const layerHealth = (health: Health): Layer.Layer<never, never, HttpRouter.HttpRouter> =>\n HttpRouter.add(\"GET\", \"/health\", HttpServerResponse.jsonUnsafe(encodeHealth(health)))\n\n/**\n * The gateway's own RPC handlers over the read path and the control plane.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerHandlers = GatewayRpcs.toLayer(\n Effect.gen(function*() {\n const projections = yield* Projections\n const control = yield* Control\n // Only a host that owns the project's stores can fork or verify its runs.\n const history = yield* Effect.serviceOption(RunHistory.RunHistory)\n const historyHost = Option.match(history, {\n onNone: () => Effect.fail(new ControlError.Unavailable({ feature: \"run history\", ticket: \"smithers#3242\" })),\n onSome: Effect.succeed\n })\n // Forking writes a durable run and verifying drives the project's flow\n // code, so neither is an anonymous loopback read.\n const operator = Effect.gen(function*() {\n const principal = yield* ControlPrincipal\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n })\n return GatewayRpcs.of({\n \"Projection.Snapshot\": Effect.fn(\"Gateway.snapshot\")(\n ({ selector, after }) => projections.snapshot(selector, after),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Projection.Subscribe\": ({ after, selector }) =>\n Stream.tapCause(projections.subscribe(selector, after), ControlServer.logDefect),\n /* One operator command. Control owns the atomic decision plus durable\n * resume delegation; the gateway is only a transport adapter. */\n \"Approval.Submit\": Effect.fn(\"Gateway.submitApproval\")((input) =>\n Effect.gen(function*() {\n // The identity the shared `ControlAuth` middleware authenticated, as\n // `ControlServer` stamps it on `Approve` and `Deny`. This mount is\n // the same decision under a different payload, so it is answerable\n // to the same operator, and a decision journaled under the\n // composition's default operator names the wrong one.\n const principal = yield* ControlPrincipal\n // Answering a human wait uses signal rather than the approval policy.\n // Do not let that alternate mutation path bypass loopback authentication.\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n const payload = {\n target: input.target,\n scope: input.scope,\n idempotencyKey: input.idempotencyKey,\n principal\n }\n // A gate that asked a question is answered, not granted, and the\n // PAYLOAD says which kind it is: a human wait carries its durable\n // wait token as the target's digest, and nothing else does. Reading\n // it here rather than trusting the client to have sent `answer` is\n // the difference between a clear refusal and the one an operator\n // actually saw \u2014 a client that forgot the answer fell through to\n // `Control.approve`, which looked for a registered approval token,\n // found none, and reported `/control/RunNotFound` for a run that was\n // listed, rendered, and waiting (workspace 4bb93306, run-1).\n //\n // `target.runId` is the run the person opened and `wait.name` the\n // wait point's own name; `Control.signal` routes the pair into the\n // run tree, to whichever execution is holding it.\n const wait = ControlExecutor.answerableWait(input.target)\n if (wait !== undefined && input.target._tag === \"Node\") {\n // Neither half of a grant answers a question. A denial has nowhere\n // to go \u2014 the run is parked on a value, and refusing to supply one\n // leaves it parked until its own attempt budget or deadline ends\n // it \u2014 and an approval with no value supplies none.\n if (input.decision === \"deny\") {\n return yield* new ControlError.InvalidInput({\n issue: `decision: \"${wait.name}\" asks a question, which is answered rather than denied`\n })\n }\n if (input.answer === undefined) {\n return yield* new ControlError.InvalidInput({\n issue: `answer: \"${wait.name}\" asks a question, so a decision alone does not answer it`\n })\n }\n const decision = yield* control.signal({\n runId: input.target.runId,\n signal: { name: wait.name, payload: input.answer },\n idempotencyKey: input.idempotencyKey,\n principal\n })\n return { decision }\n }\n const decision = input.decision === \"approve\"\n ? yield* control.approve(payload)\n : yield* control.deny(payload)\n return { decision }\n }), Effect.tapCause(ControlServer.logDefect)),\n \"Run.Fork\": Effect.fn(\"Gateway.fork\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.fork(input))),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Run.Verify\": Effect.fn(\"Gateway.verify\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.verify(input))),\n Effect.tapCause(ControlServer.logDefect)\n )\n })\n })\n)\n\n/**\n * The kind a `Watch` keepalive carries.\n *\n * `ControlRpcs.Watch` answers `ControlSchema.ControlEvent` and has no frame of\n * its own for a keepalive, so the gateway publishes one as an event whose kind\n * no emitter uses. A fold that does not know the kind ignores it, which is\n * what every fold in this package and in `@smthrs/cli` `Forensics` already\n * does with an unknown kind.\n *\n * @since 1.0.0\n * @category models\n */\nexport const watchHeartbeatKind = \"control.gateway.heartbeat\"\n\n/**\n * Merges a keepalive into a followed `Watch` stream.\n *\n * A followed stream is silent while a run is thinking, and a relay cuts an\n * idle tunnel at 600 s. The Effect RPC client\n * sends its own `Ping` every 5 s, but a non-Effect consumer behind a relay\n * sends nothing, so the keepalive has to come from the server.\n *\n * The keepalive repeats the last delivered sequence and run-scoped cursor,\n * including an expansion offset, so it cannot advance or rewind a checkpoint.\n * It carries the watched run id so a client routing by run keeps routing.\n * A snapshot read (`follow: false`) is left alone: it has to end.\n */\nconst keptAlive = (\n millis: number,\n filter: ControlSchema.WatchFilter,\n events: Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError>\n): Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError> => {\n if (filter.follow === false) return events\n let cursor = filter.afterCursor ??\n (filter.afterSequence === undefined ? undefined : { sequence: filter.afterSequence })\n let sequence = cursor?.sequence ?? 0\n const tracked = Stream.map(events, (event) => {\n sequence = event.sequence\n cursor = event.cursor ?? { sequence }\n return event\n })\n const beats = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (occurredAt): ControlSchema.ControlEvent => ({\n sequence,\n ...(filter.runId === undefined || cursor === undefined ? {} : { cursor }),\n kind: watchHeartbeatKind,\n ...(filter.runId === undefined ? {} : { runId: filter.runId }),\n occurredAt,\n payload: null\n })\n )\n )\n )\n return Stream.merge(tracked, beats, { haltStrategy: \"left\" })\n}\n\n/**\n * The control service the `/rpc` mounts read through: the ambient one, with\n * the keepalive merged into `watch`.\n *\n * Wrapping the service rather than re-declaring the handlers keeps\n * `@smthrs/control` `ControlServer` the single definition of what every\n * procedure does, including the principal it stamps on every mutation that\n * records who asked. Only `watch` behaves differently, and only in what it\n * adds.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerKeepAlive = (\n millis: number = heartbeatIntervalMillis\n): Layer.Layer<Control, never, Control> =>\n Layer.effect(Control)(\n Effect.map(Control, (control) =>\n Control.of({ ...control, watch: (filter) => keptAlive(millis, filter, control.watch(filter)) }))\n )\n\n/**\n * The read path the `/projections` mounts serve through: the ambient one, with\n * a followed subscription's keepalive re-timed to the bind's cadence.\n *\n * `Projections.make` already merges a `heartbeat` frame at the cadence it was\n * built with. A host that supplies `Projections.layer` and sets the cadence at\n * the bind expects one option to govern both keepalives, so this replaces the\n * service's beats with beats at the bind's cadence rather than adding a second\n * channel. Snapshot reads are left alone: they have to end.\n *\n * @param millis how often an idle followed subscription emits a keepalive\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsKeepAlive = (millis: number): Layer.Layer<Projections, never, Projections> =>\n Layer.effect(Projections)(\n Effect.map(Projections, (projections) =>\n Projections.of({\n ...projections,\n subscribe: (selector, after) => keptAliveSubscription(millis, projections.subscribe(selector, after))\n }))\n )\n\n/** Replaces a subscription's keepalive channel with one at the given cadence. */\nconst keptAliveSubscription = <F extends GatewaySchema.GatewayFrame>(\n millis: number,\n frames: Stream.Stream<F, GatewayError>\n): Stream.Stream<F | GatewaySchema.HeartbeatFrame, GatewayError> => {\n // The first tick is dropped for the same reason `Projections` drops it: an\n // immediate keepalive would arrive before the snapshot it keeps alive.\n const beats: Stream.Stream<GatewaySchema.HeartbeatFrame> = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (atMs): GatewaySchema.HeartbeatFrame => ({ _tag: \"heartbeat\", atMs })\n )\n )\n )\n return Stream.merge(\n Stream.filter(frames, (frame) => frame._tag !== \"heartbeat\"),\n beats,\n { haltStrategy: \"left\" }\n )\n}\n\n/**\n * Mounts the control plane on `POST /rpc` and `/rpc/ws`, with the keepalive.\n *\n * @param millis how often an idle followed watch emits one, defaulting to\n * `Projections.heartbeatIntervalMillis`\n * @since 1.0.0\n * @category layers\n */\nexport const layerControlHttp = (millis?: number | undefined) =>\n ControlServer.layerHttp.pipe(Layer.provide(layerKeepAlive(millis)))\n\nconst gateway = RpcServer.layer(GatewayRpcs, { disableFatalDefects: true })\n\nconst sync = RpcServer.layer(SyncRpcs, { disableFatalDefects: true })\n\n/**\n * Mounts the gateway's projections on `POST /projections` and\n * `/projections/ws`.\n *\n * Both protocols are mounted together because a client reads a snapshot over\n * the request/response path and follows deltas over the socket, and pointing\n * those at two different servers would let them disagree.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsHttp = Layer.mergeAll(\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/projections\" })),\n Layer.fresh\n ),\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/projections/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * Mounts the sync read path on `POST /sync` and `/sync/ws`.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerSyncHttp = Layer.mergeAll(\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/sync\" })),\n Layer.fresh\n ),\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/sync/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * The POST mounts that carry RPC request messages and nothing else.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const rpcPaths: ReadonlyArray<string> = [\"/rpc\", \"/projections\", \"/sync\", \"/branch\"]\n\n/**\n * POST mounts whose request bodies are bounded before their handlers read.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const boundedPostPaths: ReadonlyArray<string> = [\n ...rpcPaths,\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * RPC paths whose upgrade or request must pass edge authentication.\n *\n * `POST /rpc` is deliberately not one of them. The control mount authenticates\n * in band through `@smthrs/control` `ControlRpcs.ControlAuth`, whose declared\n * error is `ControlError.Unauthorized`, and that typed control error is the\n * refusal the control plane publishes: \"Missing, malformed, empty, and\n * incorrect credentials all return the same typed `Unauthorized` control\n * error\" (`packages/smithers/gateway/docs/guides/serve-beyond-loopback.md`). An edge 401 answered\n * ahead of the mount, and `ControlClient` filters a non-2xx status, so every\n * refusal reached a caller as a `TransportError`, the one error class\n * `@smthrs/control` reserves for a request that failed *before* a declared\n * control response reached it. It also made the gateway refuse a call\n * differently from `NodeControl.layerServer` hosting the very same\n * `ControlRpcs`. The body limit below still applies to `/rpc`, so an\n * unauthenticated caller buys one bounded body read and nothing else.\n *\n * The `/rpc/ws` upgrade stays here. A socket is a resource the server holds\n * open, the RPC middleware can only refuse frames on a socket that already\n * exists, and a refused handshake has no RPC channel to answer a typed error\n * on, so its refusal is a transport fact either way.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const protectedPaths: ReadonlyArray<string> = [\n \"/projections\",\n \"/branch\",\n \"/sync\",\n \"/rpc/ws\",\n \"/projections/ws\",\n \"/sync/ws\",\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * The mount a request target reaches, spelled the way {@link rpcPaths} and\n * {@link protectedPaths} spell it.\n *\n * The guard runs before the router and has to reach the router's verdict, or\n * an alias walks past the credential check and the body limit and is answered\n * by the mount anyway. Measured against `HttpRouter` on this tree, all of\n * `/%72pc`, `/rpc;transport-parameter`, `/rpc/`, `//rpc`, `///rpc`, `/rpc//`,\n * `/RPC`, `/./rpc`, and `/foo/../rpc` reach the `/rpc` handler, while\n * `/rpc%2f`, `/rpc%3Bp`, `/%2frpc`, and `/rpc%20` do not. This is not only\n * about crafted aliases: `@smthrs/control` `ControlClient`'s HTTP protocol\n * posts every call to `/rpc/`, so a literal comparison skipped the body limit\n * on the path the product's own client uses.\n *\n * Each segment is therefore taken without its `;` parameter, empty segments are\n * dropped, and what is left is decoded with `decodeURI` and compared without\n * regard to case. `decodeURI` is the decoder rather than `decodeURIComponent`\n * because it leaves a reserved character encoded, which is what keeps\n * `/rpc%2f` a different path here exactly as it is to the router. An invalid\n * escape is left as written, since it names no mount either way.\n *\n * Dot segments are resolved by `URL` before the split. The normalization errs\n * toward matching: a spelling this resolves to a mount that the router then\n * refuses is answered 401 rather than 404, which costs an unauthenticated\n * caller nothing it was entitled to.\n *\n * @param url the request target, origin-relative or absolute\n * @since 1.0.0\n * @category constructors\n */\nexport const routedPath = (url: string): string => {\n // An empty leading segment is a protocol-relative URL to `URL`, so `//rpc`\n // would parse as the host `rpc` and lose the path entirely. Anchoring an\n // origin-relative target on the origin keeps every segment. An absolute\n // target that does not parse at all names no mount, so it is compared as\n // written and reaches the router as the unknown route it is.\n const target = url.startsWith(\"/\") ? `http://gateway.invalid${url}` : url\n const parsed = URL.parse(target, \"http://gateway.invalid\")\n if (parsed === null) return url\n const segments: Array<string> = []\n for (const segment of parsed.pathname.split(\"/\")) {\n const parameter = segment.indexOf(\";\")\n const named = parameter < 0 ? segment : segment.slice(0, parameter)\n if (named === \"\") continue\n try {\n segments.push(decodeURI(named))\n } catch {\n segments.push(named)\n }\n }\n return `/${segments.join(\"/\")}`.toLowerCase()\n}\n\n/**\n * The mount that exchanges a bearer credential for a WebSocket {@link Ticket}.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPath = \"/auth/ticket\"\n\n/**\n * The protected socket mounts a {@link Ticket} opens.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPaths: ReadonlyArray<string> = [\"/rpc/ws\", \"/projections/ws\", \"/sync/ws\"]\n\n/**\n * How long an unredeemed {@link Ticket} stays valid (thirty seconds).\n *\n * @since 1.0.0\n * @category constants\n */\nexport const defaultTicketMillis = 30_000\n\n/** Outstanding tickets kept at once; the oldest is dropped past this. */\nconst maxOutstandingTickets = 1024\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * A browser `WebSocket` cannot set an `Authorization` header, so a browser\n * client first posts its bearer to `/auth/ticket`, then opens a protected\n * socket as `<mount>?ticket=<ticket>` within `expiresInMs`. The first upgrade\n * that presents the ticket consumes it, whether or not the upgrade completes;\n * a replay or a late one is refused 401. The socket then runs under the\n * bearer the ticket was issued for, so every frame authenticates as it would\n * with the header.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Ticket = Schema.Struct({\n ticket: Schema.String,\n expiresInMs: Schema.Number\n})\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Ticket = typeof Ticket.Type\n\nconst encodeTicket = Schema.encodeUnknownSync(Ticket)\n\n/** 256 random bits, hex-encoded so a query string carries them unescaped. */\nconst newTicket = (): string =>\n Array.from(crypto.getRandomValues(new Uint8Array(32)), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")\n\n/** The `ticket` query parameter of a request target, when it has one. */\nconst presentedTicket = (url: string): string | undefined => {\n const parsed = URL.parse(url, \"http://gateway.invalid\")\n const ticket = parsed?.searchParams.get(\"ticket\")\n return ticket === null || ticket === undefined || ticket === \"\" ? undefined : ticket\n}\n\n/**\n * Default maximum request body accepted by an RPC mount (one MiB).\n *\n * @category constants\n * @since 1.0.0\n */\nexport const defaultMaxRequestBodyBytes = 1024 * 1024\n\n/**\n * Ingress policy enforced before an RPC transport parses a request.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface IngressOptions {\n /** Accepted Host header names, without ports. Defaults to loopback names. */\n readonly allowedHosts?: ReadonlyArray<string> | undefined\n readonly maxRequestBodyBytes?: number | undefined\n /** Confine requests to loopback Host values and browser origins. */\n readonly loopbackOnly?: boolean | undefined\n /**\n * Edge authentication for {@link protectedPaths}, told which mount the\n * request reaches. When set, `POST /auth/ticket` exchanges a credential it\n * accepts for a {@link Ticket}, and a redeemed ticket's credential is judged\n * again for the mount its upgrade reaches.\n */\n readonly authorize?: (\n headers: Readonly<Record<string, string>>,\n path: string\n ) => Effect.Effect<boolean>\n /** How long an unredeemed ticket stays valid. Default {@link defaultTicketMillis}. */\n readonly ticketMillis?: number | undefined\n}\n\nconst encodeGatewayError = Schema.encodeUnknownSync(GatewayError)\n\n/** Host parsing stays strict so URL normalization cannot admit a rebinding name. */\nconst requestAuthority = (host: string | undefined): URL | undefined => {\n if (host === undefined || !/^(?:[a-zA-Z0-9.-]+|\\[[a-fA-F0-9:]+\\])(?::[0-9]+)?$/.test(host)) return undefined\n const parsed = URL.parse(`http://${host}`)\n return parsed === null ? undefined : parsed\n}\n\nconst browserRequestRefusal = (\n headers: Readonly<Record<string, string>>,\n allowedHosts: ReadonlyArray<string>,\n loopbackOnly: boolean\n): { readonly error: GatewayError; readonly status: number } | undefined => {\n const authority = requestAuthority(headers.host)\n if (\n authority === undefined || !allowedHosts.includes(authority.hostname) ||\n (loopbackOnly && !loopbackHost.test(headers.host!))\n ) return { error: invalidHostRequest(), status: 421 }\n const origin = headers.origin\n // CLI clients do not send Origin. Browsers must prove they came from this\n // exact authority, including the port, on HTTP requests AND WS upgrades.\n if (origin === undefined) return undefined\n const parsed = URL.parse(origin)\n return parsed !== null && (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") &&\n parsed.origin === origin && parsed.host === authority.host\n ? undefined\n : { error: invalidOriginRequest(), status: 403 }\n}\n\n/** One refusal on the wire: the typed error, under the status it answers. */\nconst refuse = (error: GatewayError, status: number) =>\n HttpServerResponse.jsonUnsafe(encodeGatewayError(error), { status })\n\nconst malformedRequest = (path: string) =>\n new GatewayError({ code: \"malformed_request\", message: `POST ${path} carries no RPC request message` })\n\nconst unauthorizedRequest = () =>\n new GatewayError({ code: \"unauthorized\", message: \"A valid bearer credential is required\" })\n\nconst invalidHostRequest = () =>\n new GatewayError({ code: \"invalid_host\", message: \"This gateway does not allow the supplied Host value\" })\n\nconst invalidOriginRequest = () =>\n new GatewayError({ code: \"invalid_origin\", message: \"The browser Origin must match an allowed gateway Host\" })\n\nconst requestTooLarge = (path: string, maxBytes: number) =>\n new GatewayError({\n code: \"request_too_large\",\n message: `POST ${path} exceeds the ${maxBytes}-byte request limit`\n })\n\n/** The message `@effect/platform-node-shared` `NodeStream` gives a size overflow. */\nconst maxBytesExceeded = \"maxBytes exceeded\"\n\n/**\n * The names that mean this machine only, as a bind address is spelled.\n *\n * This is a list of spellings a person types, not a subnet test: `127.0.0.2`\n * is loopback to the kernel and is not on it. The Node host's\n * `isLoopbackHost`, the Host-header names below, and the ingress regex all\n * derive from this one list so widening it is one edit.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostNames: ReadonlyArray<string> = [\"127.0.0.1\", \"localhost\", \"::1\"]\n\n/**\n * {@link loopbackHostNames} as a Host header spells them: an IPv6 address is\n * bracketed as a URL authority. This is the default `allowedHosts`.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostHeaderNames: ReadonlyArray<string> = loopbackHostNames.map((name) =>\n name.includes(\":\") ? `[${name}]` : name\n)\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\nconst loopbackHost = new RegExp(`^(?:${loopbackHostHeaderNames.map(escapeRegExp).join(\"|\")})(?::\\\\d{1,5})?$`, \"i\")\n\n/**\n * Whether a failed request-body read hit the configured size limit rather than\n * failing for another reason.\n *\n * `@effect/platform-node` raises the limit as an `HttpServerError` whose\n * `reason` is a `RequestParseError` carrying the `Error(\"maxBytes exceeded\")`\n * that `@effect/platform-node-shared` `NodeStream.toString` throws\n * (`NodeHttpIncomingMessage.text` passes `MaxBodySize` in as `maxBytes`, and\n * `NodeHttpServer`'s `ServerRequestImpl` wraps whatever it throws). Nothing on\n * the wire distinguishes it otherwise, so the check is that exact shape and a\n * version bump of those packages is the thing to re-read it against.\n *\n * Every other read failure, a transport reset among them, is not a size\n * overflow. Answering 413 for all of them told a client to shrink a request\n * that was never too big.\n *\n * @param error the failure a body read produced\n * @since 1.0.0\n * @category predicates\n */\nexport const exceededBodyLimit = (error: unknown): boolean => {\n const reason = typeof error === \"object\" && error !== null\n ? (error as { readonly reason?: unknown }).reason\n : undefined\n const cause = typeof reason === \"object\" && reason !== null\n ? (reason as { readonly cause?: unknown }).cause\n : undefined\n return cause instanceof Error && cause.message === maxBytesExceeded\n}\n\n/**\n * The refusal a failed request-body read earns, and the status it answers\n * under.\n *\n * A body over the limit is 413 `request_too_large`. Every other read failure,\n * a truncated body or a reset transport among them, is 400\n * `malformed_request`: the request cannot be retried smaller, because its size\n * was never the problem.\n *\n * @param path the mount the request was made to\n * @param maxBytes the configured limit\n * @param error the failure the body read produced\n * @since 1.0.0\n * @category constructors\n */\nexport const bodyRefusal = (\n path: string,\n maxBytes: number,\n error: unknown\n): { readonly error: GatewayError; readonly status: number } =>\n exceededBodyLimit(error)\n ? { error: requestTooLarge(path, maxBytes), status: 413 }\n : {\n error: new GatewayError({\n code: \"malformed_request\",\n message: `POST ${path} carries a body the server could not read`\n }),\n status: 400\n }\n\n/**\n * Whether a request body carries at least one RPC message the server can act\n * on.\n *\n * The transport's own parser answers, not a second reading of the wire\n * format: whatever `RpcSerialization` the host composed decodes the body, and\n * the answer is no only when that decode throws or yields no tagged message.\n * A body naming a procedure that does not exist is a yes \u2014 that is an\n * RPC-level defect the protocol itself reports.\n *\n * A binary framing is always a yes. Its body is not text, so there is nothing\n * to read here, and the mount owns it.\n *\n * @since 1.0.0\n * @category predicates\n */\nexport const carriesRpcRequest = (\n serialization: RpcSerialization.RpcSerialization[\"Service\"],\n body: string\n): boolean => {\n if (!serialization.contentType.includes(\"json\")) return true\n try {\n const decoded = serialization.makeUnsafe().decode(body)\n return decoded.length > 0 && decoded.every((message: unknown) =>\n typeof message === \"object\" && message !== null &&\n typeof (message as { readonly _tag?: unknown })._tag === \"string\"\n )\n } catch {\n return false\n }\n}\n\n/**\n * Enforces local Host/Origin policy and edge authentication before reading a\n * body or upgrading a socket, then validates RPC body size and framing.\n *\n * `effect/rpc` hands every decoded message to the server loop, and a\n * body that decodes to something else \u2014 `{}`, `[]`, prose, nothing at all \u2014\n * reached it as a message with no tag and died there, so the gateway answered\n * `500 Internal Server Error` with an empty body (release validation). That is the\n * wrong half of the contract twice over: it tells an operator the gateway\n * broke, and it tells a client to retry a request that can never succeed.\n *\n * The body reader is cached per request, so reading the body here does not\n * consume the body the mount reads. It is read through the reader the mount\n * uses, `text` under a JSON serialization and `arrayBuffer` under a binary\n * one, so the bytes the mount decodes are the bytes the client sent.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerIngress = (options: IngressOptions = {}) => {\n const refusal = settingRefusal(\"The gateway request body limit\", options.maxRequestBodyBytes) ??\n settingRefusal(\"The gateway ticket lifetime\", options.ticketMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n const maxBytes = options.maxRequestBodyBytes ?? defaultMaxRequestBodyBytes\n const allowedHosts = options.allowedHosts ?? loopbackHostHeaderNames\n const ticketMillis = options.ticketMillis ?? defaultTicketMillis\n return HttpRouter.middleware(\n Effect.gen(function*() {\n const serialization = yield* RpcSerialization.RpcSerialization\n const binary = !serialization.contentType.includes(\"json\")\n /** Outstanding tickets: the Authorization header each was issued under, and when it lapses. */\n const tickets = new Map<string, { readonly authorization: string; readonly expiresAt: number }>()\n const issue = (authorization: string, now: number): Ticket => {\n for (const [ticket, entry] of tickets) {\n if (entry.expiresAt <= now) tickets.delete(ticket)\n }\n if (tickets.size >= maxOutstandingTickets) tickets.delete(tickets.keys().next().value!)\n const ticket = newTicket()\n tickets.set(ticket, { authorization, expiresAt: now + ticketMillis })\n return { ticket, expiresInMs: ticketMillis }\n }\n // Lookup and delete run without yielding, so of two upgrades presenting\n // one ticket exactly one gets its header.\n const redeem = (url: string, now: number): string | undefined => {\n const ticket = presentedTicket(url)\n const entry = ticket === undefined ? undefined : tickets.get(ticket)\n if (entry === undefined) return undefined\n tickets.delete(ticket!)\n return entry.expiresAt > now ? entry.authorization : undefined\n }\n return (httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, Types.unhandled>) =>\n Effect.gen(function*() {\n const request = yield* HttpServerRequest.HttpServerRequest\n const browserRefusal = browserRequestRefusal(request.headers, allowedHosts, options.loopbackOnly === true)\n if (browserRefusal !== undefined) return refuse(browserRefusal.error, browserRefusal.status)\n const path = routedPath(request.url)\n if (path === ticketPath && request.method === \"POST\" && options.authorize !== undefined) {\n const authorization = request.headers.authorization\n if (authorization === undefined || !(yield* options.authorize(request.headers, path))) {\n return refuse(unauthorizedRequest(), 401)\n }\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n return HttpServerResponse.jsonUnsafe(encodeTicket(issue(authorization, now)), {\n headers: { \"cache-control\": \"no-store\" }\n })\n }\n if (protectedPaths.includes(path) && options.authorize !== undefined) {\n const authorized = yield* options.authorize(request.headers, path)\n if (!authorized) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n const authorization = ticketPaths.includes(path) ? redeem(request.url, now) : undefined\n if (authorization === undefined) return refuse(unauthorizedRequest(), 401)\n // A ticket stands for the credential it was issued under, which\n // may not reach every mount a ticket can open.\n const headers = Headers.set(request.headers, \"authorization\", authorization)\n if (!(yield* options.authorize(headers, path))) return refuse(unauthorizedRequest(), 401)\n // The socket protocol stamps the upgrade's headers on every\n // frame, so the redeemed bearer authenticates each one.\n return yield* Effect.provideService(\n httpEffect,\n HttpServerRequest.HttpServerRequest,\n request.modify({ headers })\n )\n }\n }\n if (request.method !== \"POST\" || !boundedPostPaths.includes(path)) return yield* httpEffect\n // A declared length is a hint that saves reading a body already\n // known to be too big. It is never trusted the other way: a body\n // that declares less than it sends is still measured by the read,\n // and a chunked body declares nothing at all.\n const declaredLength = Number(request.headers[\"content-length\"])\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n return refuse(requestTooLarge(path, maxBytes), 413)\n }\n // The body is read through the reader the mount will use. The Node\n // adapter caches whichever reader ran first and derives the other\n // from it, so a text read here would hand a binary mount a UTF-8\n // re-encoding of its MessagePack bytes, with every invalid sequence\n // replaced. A binary body is bounded here and framed by the mount.\n const read = yield* (binary ? Effect.as(request.arrayBuffer, undefined) : request.text).pipe(\n Effect.provideService(HttpServerRequest.MaxBodySize, ByteSize.bytes(maxBytes)),\n Effect.match({\n onFailure: (error): { readonly body: string | undefined; readonly error: unknown } => ({\n body: undefined,\n error\n }),\n onSuccess: (body) => ({ body, error: undefined })\n })\n )\n if (read.error !== undefined) {\n const answer = bodyRefusal(path, maxBytes, read.error)\n return refuse(answer.error, answer.status)\n }\n if (read.body === undefined) return yield* httpEffect\n return !rpcPaths.includes(path) || carriesRpcRequest(serialization, read.body)\n ? yield* httpEffect\n : refuse(malformedRequest(path), 400)\n })\n }),\n { global: true }\n )\n}\n\n/**\n * How an assembled gateway is configured.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface LayerOptions {\n /**\n * How often an idle followed `Watch` on `/rpc/ws` and an idle followed\n * `Projection.Subscribe` on `/projections/ws` emit a keepalive. Unset, the\n * `Watch` keepalive runs at `Projections.heartbeatIntervalMillis` and the\n * supplied `Projections` service keeps the cadence it was built with.\n */\n readonly heartbeatMillis?: number | undefined\n /** The ingress policy the RPC mounts run behind. */\n readonly ingress?: IngressOptions | undefined\n /** Enables the authenticated Go-to-Control runtime bridge. */\n readonly runtimeBridge?: RuntimeBridge.Config | undefined\n}\n\n/**\n * The whole gateway surface, as one application layer a host serves.\n *\n * The caller supplies the transport serialization, the authentication\n * middleware for both RPC groups, and the services the mounts read through\n * (`Control`, `Projections`, `SyncServer`). `@smthrs/gateway/node/NodeGateway`\n * is the Node host that binds this to a socket.\n *\n * A keepalive cadence or a body limit that is not a positive safe integer is\n * refused here, before anything binds, through the layer's typed\n * `GatewayError` channel.\n *\n * @param health the identity `GET /health` answers with\n * @param options the keepalive cadence and the ingress policy\n * @since 1.0.0\n * @category layers\n */\nexport const layer = (health: Health, options: LayerOptions = {}) => {\n const refusal = settingRefusal(\"The gateway keepalive cadence\", options.heartbeatMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n return Layer.mergeAll(\n layerControlHttp(options.heartbeatMillis),\n options.heartbeatMillis === undefined\n ? layerProjectionsHttp\n : layerProjectionsHttp.pipe(Layer.provide(layerProjectionsKeepAlive(options.heartbeatMillis))),\n layerSyncHttp,\n layerHealth(health),\n ...(options.runtimeBridge === undefined\n ? []\n : [RuntimeBridge.layer(options.runtimeBridge), HostBranchPresence.layer(options.runtimeBridge)]),\n layerIngress(options.ingress)\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * The assembled workspace gateway: one HTTP surface carrying the control\n * plane, the sync read path, the served projections, and a health probe.\n *\n * Mounts:\n *\n * | Path | Protocol | Serves |\n * | --- | --- | --- |\n * | `POST /rpc` | RPC over HTTP | `@smthrs/control` `ControlRpcs` |\n * | `/rpc/ws` | RPC over WebSocket | `ControlRpcs`, including a kept-alive `Watch` |\n * | `POST /projections` | RPC over HTTP | `GatewayRpcs` |\n * | `/projections/ws` | RPC over WebSocket | `GatewayRpcs`, including `Projection.Subscribe` |\n * | `POST /sync` | RPC over HTTP | `@smthrs/sync` `SyncRpcs` |\n * | `/sync/ws` | RPC over WebSocket | `SyncRpcs` |\n * | `GET /health` | JSON | `GatewaySchema.GatewayHealth` plus the package version |\n * | `POST /auth/ticket` | JSON | A single-use {@link Ticket} that opens one protected socket |\n *\n * `/health` is deliberately unauthenticated: a supervisor decides whether to\n * keep or replace a gateway process by asking which workspace it belongs to,\n * and a probe that needed a credential could not answer that question about a\n * gateway it did not start. The response carries identity only: the\n * workspace hash, the gateway id, the protocol version, and the package\n * version. It never carries a token, a run, or a path.\n *\n * @since 1.0.0\n */\n\nimport { Control } from \"@smthrs/control/Control\"\nimport * as ControlError from \"@smthrs/control/ControlError\"\nimport * as ControlExecutor from \"@smthrs/control/ControlExecutor\"\nimport { ControlPrincipal } from \"@smthrs/control/ControlRpcs\"\nimport type * as ControlSchema from \"@smthrs/control/ControlSchema\"\nimport * as ControlServer from \"@smthrs/control/ControlServer\"\nimport { SyncRpcs } from \"@smthrs/sync/SyncRpcs\"\nimport * as SyncServer from \"@smthrs/sync/SyncServer\"\nimport { Effect, Layer, Option, Schema, Stream, type Types } from \"effect\"\nimport * as ByteSize from \"effect/ByteSize\"\nimport { Headers, HttpRouter, HttpServerRequest, HttpServerResponse } from \"effect/http\"\nimport { RpcSerialization, RpcServer } from \"effect/rpc\"\nimport { GatewayError, settingRefusal } from \"./GatewayError.ts\"\nimport { GatewayRpcs } from \"./GatewayRpcs.ts\"\nimport * as GatewaySchema from \"./GatewaySchema.ts\"\nimport * as HostBranchPresence from \"./HostBranchPresence.ts\"\nimport { heartbeatIntervalMillis, Projections } from \"./Projections.ts\"\nimport * as RunHistory from \"./RunHistory.ts\"\nimport * as RuntimeBridge from \"./RuntimeBridge.ts\"\n\n/**\n * What `GET /health` answers: the `GatewaySchema.GatewayHealth` identity plus\n * the version of the package serving it.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Health = Schema.Struct({\n ...GatewaySchema.GatewayHealth.fields,\n version: Schema.String,\n runtimeBridge: Schema.optional(RuntimeBridge.Identity)\n})\n\n/**\n * What `GET /health` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Health = typeof Health.Type\n\nconst encodeHealth = Schema.encodeUnknownSync(Health)\n\n/**\n * Mounts the unauthenticated health probe.\n *\n * @param health the identity this process serves under\n * @since 1.0.0\n * @category layers\n */\nexport const layerHealth = (health: Health): Layer.Layer<never, never, HttpRouter.HttpRouter> =>\n HttpRouter.add(\"GET\", \"/health\", HttpServerResponse.jsonUnsafe(encodeHealth(health)))\n\n/**\n * The gateway's own RPC handlers over the read path and the control plane.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerHandlers = GatewayRpcs.toLayer(\n Effect.gen(function*() {\n const projections = yield* Projections\n const control = yield* Control\n // Only a host that owns the project's stores can fork or verify its runs.\n const history = yield* Effect.serviceOption(RunHistory.RunHistory)\n const historyHost = Option.match(history, {\n onNone: () => Effect.fail(new ControlError.Unavailable({ feature: \"run history\", ticket: \"smithers#3242\" })),\n onSome: Effect.succeed\n })\n // Forking writes a durable run and verifying drives the project's flow\n // code, so neither is an anonymous loopback read.\n const operator = Effect.gen(function*() {\n const principal = yield* ControlPrincipal\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n })\n return GatewayRpcs.of({\n \"Projection.Snapshot\": Effect.fn(\"Gateway.snapshot\")(\n ({ selector, after }) => projections.snapshot(selector, after),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Projection.Subscribe\": ({ after, selector }) =>\n Stream.tapCause(projections.subscribe(selector, after), ControlServer.logDefect),\n /* One operator command. Control owns the atomic decision plus durable\n * resume delegation; the gateway is only a transport adapter. */\n \"Approval.Submit\": Effect.fn(\"Gateway.submitApproval\")((input) =>\n Effect.gen(function*() {\n // The identity the shared `ControlAuth` middleware authenticated, as\n // `ControlServer` stamps it on `Approve` and `Deny`. This mount is\n // the same decision under a different payload, so it is answerable\n // to the same operator, and a decision journaled under the\n // composition's default operator names the wrong one.\n const principal = yield* ControlPrincipal\n // Answering a human wait uses signal rather than the approval policy.\n // Do not let that alternate mutation path bypass loopback authentication.\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n const payload = {\n target: input.target,\n scope: input.scope,\n idempotencyKey: input.idempotencyKey,\n principal\n }\n // A gate that asked a question is answered, not granted, and the\n // PAYLOAD says which kind it is: a human wait carries its durable\n // wait token as the target's digest, and nothing else does. Reading\n // it here rather than trusting the client to have sent `answer` is\n // the difference between a clear refusal and the one an operator\n // actually saw \u2014 a client that forgot the answer fell through to\n // `Control.approve`, which looked for a registered approval token,\n // found none, and reported `/control/RunNotFound` for a run that was\n // listed, rendered, and waiting (workspace 4bb93306, run-1).\n //\n // `target.runId` is the run the person opened and `wait.name` the\n // wait point's own name; `Control.signal` routes the pair into the\n // run tree, to whichever execution is holding it.\n const wait = ControlExecutor.answerableWait(input.target)\n if (wait !== undefined && input.target._tag === \"Node\") {\n // Neither half of a grant answers a question. A denial has nowhere\n // to go \u2014 the run is parked on a value, and refusing to supply one\n // leaves it parked until its own attempt budget or deadline ends\n // it \u2014 and an approval with no value supplies none.\n if (input.decision === \"deny\") {\n return yield* new ControlError.InvalidInput({\n issue: `decision: \"${wait.name}\" asks a question, which is answered rather than denied`\n })\n }\n if (input.answer === undefined) {\n return yield* new ControlError.InvalidInput({\n issue: `answer: \"${wait.name}\" asks a question, so a decision alone does not answer it`\n })\n }\n const decision = yield* control.signal({\n runId: input.target.runId,\n signal: { name: wait.name, payload: input.answer },\n idempotencyKey: input.idempotencyKey,\n principal\n })\n return { decision }\n }\n const decision = input.decision === \"approve\"\n ? yield* control.approve(payload)\n : yield* control.deny(payload)\n return { decision }\n }), Effect.tapCause(ControlServer.logDefect)),\n \"Run.Fork\": Effect.fn(\"Gateway.fork\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.fork(input))),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Run.Verify\": Effect.fn(\"Gateway.verify\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.verify(input))),\n Effect.tapCause(ControlServer.logDefect)\n )\n })\n })\n)\n\n/**\n * The kind a `Watch` keepalive carries.\n *\n * `ControlRpcs.Watch` answers `ControlSchema.ControlEvent` and has no frame of\n * its own for a keepalive, so the gateway publishes one as an event whose kind\n * no emitter uses. A fold that does not know the kind ignores it, which is\n * what every fold in this package and in `@smthrs/cli` `Forensics` already\n * does with an unknown kind.\n *\n * @since 1.0.0\n * @category models\n */\nexport const watchHeartbeatKind = \"control.gateway.heartbeat\"\n\n/**\n * Merges a keepalive into a followed `Watch` stream.\n *\n * A followed stream is silent while a run is thinking, and a relay cuts an\n * idle tunnel at 600 s. The Effect RPC client\n * sends its own `Ping` every 5 s, but a non-Effect consumer behind a relay\n * sends nothing, so the keepalive has to come from the server.\n *\n * The keepalive repeats the last delivered sequence and run-scoped cursor,\n * including an expansion offset, so it cannot advance or rewind a checkpoint.\n * It carries the watched run id so a client routing by run keeps routing.\n * A snapshot read (`follow: false`) is left alone: it has to end.\n */\nconst keptAlive = (\n millis: number,\n filter: ControlSchema.WatchFilter,\n events: Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError>\n): Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError> => {\n if (filter.follow === false) return events\n let cursor = filter.afterCursor ??\n (filter.afterSequence === undefined ? undefined : { sequence: filter.afterSequence })\n let sequence = cursor?.sequence ?? 0\n const tracked = Stream.map(events, (event) => {\n sequence = event.sequence\n cursor = event.cursor ?? { sequence }\n return event\n })\n const beats = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (occurredAt): ControlSchema.ControlEvent => ({\n sequence,\n ...(filter.runId === undefined || cursor === undefined ? {} : { cursor }),\n kind: watchHeartbeatKind,\n ...(filter.runId === undefined ? {} : { runId: filter.runId }),\n occurredAt,\n payload: null\n })\n )\n )\n )\n return Stream.merge(tracked, beats, { haltStrategy: \"left\" })\n}\n\n/**\n * The control service the `/rpc` mounts read through: the ambient one, with\n * the keepalive merged into `watch`.\n *\n * Wrapping the service rather than re-declaring the handlers keeps\n * `@smthrs/control` `ControlServer` the single definition of what every\n * procedure does, including the principal it stamps on every mutation that\n * records who asked. Only `watch` behaves differently, and only in what it\n * adds.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerKeepAlive = (\n millis: number = heartbeatIntervalMillis\n): Layer.Layer<Control, never, Control> =>\n Layer.effect(Control)(\n Effect.map(Control, (control) =>\n Control.of({ ...control, watch: (filter) => keptAlive(millis, filter, control.watch(filter)) }))\n )\n\n/**\n * The read path the `/projections` mounts serve through: the ambient one, with\n * a followed subscription's keepalive re-timed to the bind's cadence.\n *\n * `Projections.make` already merges a `heartbeat` frame at the cadence it was\n * built with. A host that supplies `Projections.layer` and sets the cadence at\n * the bind expects one option to govern both keepalives, so this replaces the\n * service's beats with beats at the bind's cadence rather than adding a second\n * channel. Snapshot reads are left alone: they have to end.\n *\n * @param millis how often an idle followed subscription emits a keepalive\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsKeepAlive = (millis: number): Layer.Layer<Projections, never, Projections> =>\n Layer.effect(Projections)(\n Effect.map(Projections, (projections) =>\n Projections.of({\n ...projections,\n subscribe: (selector, after) => keptAliveSubscription(millis, projections.subscribe(selector, after))\n }))\n )\n\n/** Replaces a subscription's keepalive channel with one at the given cadence. */\nconst keptAliveSubscription = <F extends GatewaySchema.GatewayFrame>(\n millis: number,\n frames: Stream.Stream<F, GatewayError>\n): Stream.Stream<F | GatewaySchema.HeartbeatFrame, GatewayError> => {\n // The first tick is dropped for the same reason `Projections` drops it: an\n // immediate keepalive would arrive before the snapshot it keeps alive.\n const beats: Stream.Stream<GatewaySchema.HeartbeatFrame> = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (atMs): GatewaySchema.HeartbeatFrame => ({ _tag: \"heartbeat\", atMs })\n )\n )\n )\n return Stream.merge(\n Stream.filter(frames, (frame) => frame._tag !== \"heartbeat\"),\n beats,\n { haltStrategy: \"left\" }\n )\n}\n\n/**\n * Mounts the control plane on `POST /rpc` and `/rpc/ws`, with the keepalive.\n *\n * @param millis how often an idle followed watch emits one, defaulting to\n * `Projections.heartbeatIntervalMillis`\n * @since 1.0.0\n * @category layers\n */\nexport const layerControlHttp = (millis?: number | undefined) =>\n ControlServer.layerHttp.pipe(Layer.provide(layerKeepAlive(millis)))\n\nconst gateway = RpcServer.layer(GatewayRpcs, { disableFatalDefects: true })\n\nconst sync = RpcServer.layer(SyncRpcs, { disableFatalDefects: true })\n\n/**\n * Mounts the gateway's projections on `POST /projections` and\n * `/projections/ws`.\n *\n * Both protocols are mounted together because a client reads a snapshot over\n * the request/response path and follows deltas over the socket, and pointing\n * those at two different servers would let them disagree.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsHttp = Layer.mergeAll(\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/projections\" })),\n Layer.fresh\n ),\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/projections/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * Mounts the sync read path on `POST /sync` and `/sync/ws`.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerSyncHttp = Layer.mergeAll(\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/sync\" })),\n Layer.fresh\n ),\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/sync/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * The POST mounts that carry RPC request messages and nothing else.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const rpcPaths: ReadonlyArray<string> = [\"/rpc\", \"/projections\", \"/sync\", \"/branch\"]\n\n/**\n * POST mounts whose request bodies are bounded before their handlers read.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const boundedPostPaths: ReadonlyArray<string> = [\n ...rpcPaths,\n \"/runtime/v1/command\",\n \"/runtime/v1/monitor\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * RPC paths whose upgrade or request must pass edge authentication.\n *\n * `POST /rpc` is deliberately not one of them. The control mount authenticates\n * in band through `@smthrs/control` `ControlRpcs.ControlAuth`, whose declared\n * error is `ControlError.Unauthorized`, and that typed control error is the\n * refusal the control plane publishes: \"Missing, malformed, empty, and\n * incorrect credentials all return the same typed `Unauthorized` control\n * error\" (`packages/smithers/gateway/docs/guides/serve-beyond-loopback.md`). An edge 401 answered\n * ahead of the mount, and `ControlClient` filters a non-2xx status, so every\n * refusal reached a caller as a `TransportError`, the one error class\n * `@smthrs/control` reserves for a request that failed *before* a declared\n * control response reached it. It also made the gateway refuse a call\n * differently from `NodeControl.layerServer` hosting the very same\n * `ControlRpcs`. The body limit below still applies to `/rpc`, so an\n * unauthenticated caller buys one bounded body read and nothing else.\n *\n * The `/rpc/ws` upgrade stays here. A socket is a resource the server holds\n * open, the RPC middleware can only refuse frames on a socket that already\n * exists, and a refused handshake has no RPC channel to answer a typed error\n * on, so its refusal is a transport fact either way.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const protectedPaths: ReadonlyArray<string> = [\n \"/projections\",\n \"/branch\",\n \"/sync\",\n \"/rpc/ws\",\n \"/projections/ws\",\n \"/sync/ws\",\n \"/runtime/v1/command\",\n \"/runtime/v1/monitor\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * The mount a request target reaches, spelled the way {@link rpcPaths} and\n * {@link protectedPaths} spell it.\n *\n * The guard runs before the router and has to reach the router's verdict, or\n * an alias walks past the credential check and the body limit and is answered\n * by the mount anyway. Measured against `HttpRouter` on this tree, all of\n * `/%72pc`, `/rpc;transport-parameter`, `/rpc/`, `//rpc`, `///rpc`, `/rpc//`,\n * `/RPC`, `/./rpc`, and `/foo/../rpc` reach the `/rpc` handler, while\n * `/rpc%2f`, `/rpc%3Bp`, `/%2frpc`, and `/rpc%20` do not. This is not only\n * about crafted aliases: `@smthrs/control` `ControlClient`'s HTTP protocol\n * posts every call to `/rpc/`, so a literal comparison skipped the body limit\n * on the path the product's own client uses.\n *\n * Each segment is therefore taken without its `;` parameter, empty segments are\n * dropped, and what is left is decoded with `decodeURI` and compared without\n * regard to case. `decodeURI` is the decoder rather than `decodeURIComponent`\n * because it leaves a reserved character encoded, which is what keeps\n * `/rpc%2f` a different path here exactly as it is to the router. An invalid\n * escape is left as written, since it names no mount either way.\n *\n * Dot segments are resolved by `URL` before the split. The normalization errs\n * toward matching: a spelling this resolves to a mount that the router then\n * refuses is answered 401 rather than 404, which costs an unauthenticated\n * caller nothing it was entitled to.\n *\n * @param url the request target, origin-relative or absolute\n * @since 1.0.0\n * @category constructors\n */\nexport const routedPath = (url: string): string => {\n // An empty leading segment is a protocol-relative URL to `URL`, so `//rpc`\n // would parse as the host `rpc` and lose the path entirely. Anchoring an\n // origin-relative target on the origin keeps every segment. An absolute\n // target that does not parse at all names no mount, so it is compared as\n // written and reaches the router as the unknown route it is.\n const target = url.startsWith(\"/\") ? `http://gateway.invalid${url}` : url\n const parsed = URL.parse(target, \"http://gateway.invalid\")\n if (parsed === null) return url\n const segments: Array<string> = []\n for (const segment of parsed.pathname.split(\"/\")) {\n const parameter = segment.indexOf(\";\")\n const named = parameter < 0 ? segment : segment.slice(0, parameter)\n if (named === \"\") continue\n try {\n segments.push(decodeURI(named))\n } catch {\n segments.push(named)\n }\n }\n return `/${segments.join(\"/\")}`.toLowerCase()\n}\n\n/**\n * The mount that exchanges a bearer credential for a WebSocket {@link Ticket}.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPath = \"/auth/ticket\"\n\n/**\n * The protected socket mounts a {@link Ticket} opens.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPaths: ReadonlyArray<string> = [\"/rpc/ws\", \"/projections/ws\", \"/sync/ws\"]\n\n/**\n * How long an unredeemed {@link Ticket} stays valid (thirty seconds).\n *\n * @since 1.0.0\n * @category constants\n */\nexport const defaultTicketMillis = 30_000\n\n/** Outstanding tickets kept at once; the oldest is dropped past this. */\nconst maxOutstandingTickets = 1024\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * A browser `WebSocket` cannot set an `Authorization` header, so a browser\n * client first posts its bearer to `/auth/ticket`, then opens a protected\n * socket as `<mount>?ticket=<ticket>` within `expiresInMs`. The first upgrade\n * that presents the ticket consumes it, whether or not the upgrade completes;\n * a replay or a late one is refused 401. The socket then runs under the\n * bearer the ticket was issued for, so every frame authenticates as it would\n * with the header.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Ticket = Schema.Struct({\n ticket: Schema.String,\n expiresInMs: Schema.Number\n})\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Ticket = typeof Ticket.Type\n\nconst encodeTicket = Schema.encodeUnknownSync(Ticket)\n\n/** 256 random bits, hex-encoded so a query string carries them unescaped. */\nconst newTicket = (): string =>\n Array.from(crypto.getRandomValues(new Uint8Array(32)), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")\n\n/** The `ticket` query parameter of a request target, when it has one. */\nconst presentedTicket = (url: string): string | undefined => {\n const parsed = URL.parse(url, \"http://gateway.invalid\")\n const ticket = parsed?.searchParams.get(\"ticket\")\n return ticket === null || ticket === undefined || ticket === \"\" ? undefined : ticket\n}\n\n/**\n * Default maximum request body accepted by an RPC mount (one MiB).\n *\n * @category constants\n * @since 1.0.0\n */\nexport const defaultMaxRequestBodyBytes = 1024 * 1024\n\n/**\n * Ingress policy enforced before an RPC transport parses a request.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface IngressOptions {\n /** Accepted Host header names, without ports. Defaults to loopback names. */\n readonly allowedHosts?: ReadonlyArray<string> | undefined\n readonly maxRequestBodyBytes?: number | undefined\n /** Confine requests to loopback Host values and browser origins. */\n readonly loopbackOnly?: boolean | undefined\n /**\n * Edge authentication for {@link protectedPaths}, told which mount the\n * request reaches. When set, `POST /auth/ticket` exchanges a credential it\n * accepts for a {@link Ticket}, and a redeemed ticket's credential is judged\n * again for the mount its upgrade reaches.\n */\n readonly authorize?: (\n headers: Readonly<Record<string, string>>,\n path: string\n ) => Effect.Effect<boolean>\n /** How long an unredeemed ticket stays valid. Default {@link defaultTicketMillis}. */\n readonly ticketMillis?: number | undefined\n}\n\nconst encodeGatewayError = Schema.encodeUnknownSync(GatewayError)\n\n/** Host parsing stays strict so URL normalization cannot admit a rebinding name. */\nconst requestAuthority = (host: string | undefined): URL | undefined => {\n if (host === undefined || !/^(?:[a-zA-Z0-9.-]+|\\[[a-fA-F0-9:]+\\])(?::[0-9]+)?$/.test(host)) return undefined\n const parsed = URL.parse(`http://${host}`)\n return parsed === null ? undefined : parsed\n}\n\nconst browserRequestRefusal = (\n headers: Readonly<Record<string, string>>,\n allowedHosts: ReadonlyArray<string>,\n loopbackOnly: boolean\n): { readonly error: GatewayError; readonly status: number } | undefined => {\n const authority = requestAuthority(headers.host)\n if (\n authority === undefined || !allowedHosts.includes(authority.hostname) ||\n (loopbackOnly && !loopbackHost.test(headers.host!))\n ) return { error: invalidHostRequest(), status: 421 }\n const origin = headers.origin\n // CLI clients do not send Origin. Browsers must prove they came from this\n // exact authority, including the port, on HTTP requests AND WS upgrades.\n if (origin === undefined) return undefined\n const parsed = URL.parse(origin)\n return parsed !== null && (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") &&\n parsed.origin === origin && parsed.host === authority.host\n ? undefined\n : { error: invalidOriginRequest(), status: 403 }\n}\n\n/** One refusal on the wire: the typed error, under the status it answers. */\nconst refuse = (error: GatewayError, status: number) =>\n HttpServerResponse.jsonUnsafe(encodeGatewayError(error), { status })\n\nconst malformedRequest = (path: string) =>\n new GatewayError({ code: \"malformed_request\", message: `POST ${path} carries no RPC request message` })\n\nconst unauthorizedRequest = () =>\n new GatewayError({ code: \"unauthorized\", message: \"A valid bearer credential is required\" })\n\nconst invalidHostRequest = () =>\n new GatewayError({ code: \"invalid_host\", message: \"This gateway does not allow the supplied Host value\" })\n\nconst invalidOriginRequest = () =>\n new GatewayError({ code: \"invalid_origin\", message: \"The browser Origin must match an allowed gateway Host\" })\n\nconst requestTooLarge = (path: string, maxBytes: number) =>\n new GatewayError({\n code: \"request_too_large\",\n message: `POST ${path} exceeds the ${maxBytes}-byte request limit`\n })\n\n/** The message `@effect/platform-node-shared` `NodeStream` gives a size overflow. */\nconst maxBytesExceeded = \"maxBytes exceeded\"\n\n/**\n * The names that mean this machine only, as a bind address is spelled.\n *\n * This is a list of spellings a person types, not a subnet test: `127.0.0.2`\n * is loopback to the kernel and is not on it. The Node host's\n * `isLoopbackHost`, the Host-header names below, and the ingress regex all\n * derive from this one list so widening it is one edit.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostNames: ReadonlyArray<string> = [\"127.0.0.1\", \"localhost\", \"::1\"]\n\n/**\n * {@link loopbackHostNames} as a Host header spells them: an IPv6 address is\n * bracketed as a URL authority. This is the default `allowedHosts`.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostHeaderNames: ReadonlyArray<string> = loopbackHostNames.map((name) =>\n name.includes(\":\") ? `[${name}]` : name\n)\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\nconst loopbackHost = new RegExp(`^(?:${loopbackHostHeaderNames.map(escapeRegExp).join(\"|\")})(?::\\\\d{1,5})?$`, \"i\")\n\n/**\n * Whether a failed request-body read hit the configured size limit rather than\n * failing for another reason.\n *\n * `@effect/platform-node` raises the limit as an `HttpServerError` whose\n * `reason` is a `RequestParseError` carrying the `Error(\"maxBytes exceeded\")`\n * that `@effect/platform-node-shared` `NodeStream.toString` throws\n * (`NodeHttpIncomingMessage.text` passes `MaxBodySize` in as `maxBytes`, and\n * `NodeHttpServer`'s `ServerRequestImpl` wraps whatever it throws). Nothing on\n * the wire distinguishes it otherwise, so the check is that exact shape and a\n * version bump of those packages is the thing to re-read it against.\n *\n * Every other read failure, a transport reset among them, is not a size\n * overflow. Answering 413 for all of them told a client to shrink a request\n * that was never too big.\n *\n * @param error the failure a body read produced\n * @since 1.0.0\n * @category predicates\n */\nexport const exceededBodyLimit = (error: unknown): boolean => {\n const reason = typeof error === \"object\" && error !== null\n ? (error as { readonly reason?: unknown }).reason\n : undefined\n const cause = typeof reason === \"object\" && reason !== null\n ? (reason as { readonly cause?: unknown }).cause\n : undefined\n return cause instanceof Error && cause.message === maxBytesExceeded\n}\n\n/**\n * The refusal a failed request-body read earns, and the status it answers\n * under.\n *\n * A body over the limit is 413 `request_too_large`. Every other read failure,\n * a truncated body or a reset transport among them, is 400\n * `malformed_request`: the request cannot be retried smaller, because its size\n * was never the problem.\n *\n * @param path the mount the request was made to\n * @param maxBytes the configured limit\n * @param error the failure the body read produced\n * @since 1.0.0\n * @category constructors\n */\nexport const bodyRefusal = (\n path: string,\n maxBytes: number,\n error: unknown\n): { readonly error: GatewayError; readonly status: number } =>\n exceededBodyLimit(error)\n ? { error: requestTooLarge(path, maxBytes), status: 413 }\n : {\n error: new GatewayError({\n code: \"malformed_request\",\n message: `POST ${path} carries a body the server could not read`\n }),\n status: 400\n }\n\n/**\n * Whether a request body carries at least one RPC message the server can act\n * on.\n *\n * The transport's own parser answers, not a second reading of the wire\n * format: whatever `RpcSerialization` the host composed decodes the body, and\n * the answer is no only when that decode throws or yields no tagged message.\n * A body naming a procedure that does not exist is a yes \u2014 that is an\n * RPC-level defect the protocol itself reports.\n *\n * A binary framing is always a yes. Its body is not text, so there is nothing\n * to read here, and the mount owns it.\n *\n * @since 1.0.0\n * @category predicates\n */\nexport const carriesRpcRequest = (\n serialization: RpcSerialization.RpcSerialization[\"Service\"],\n body: string\n): boolean => {\n if (!serialization.contentType.includes(\"json\")) return true\n try {\n const decoded = serialization.makeUnsafe().decode(body)\n return decoded.length > 0 && decoded.every((message: unknown) =>\n typeof message === \"object\" && message !== null &&\n typeof (message as { readonly _tag?: unknown })._tag === \"string\"\n )\n } catch {\n return false\n }\n}\n\n/**\n * Enforces local Host/Origin policy and edge authentication before reading a\n * body or upgrading a socket, then validates RPC body size and framing.\n *\n * `effect/rpc` hands every decoded message to the server loop, and a\n * body that decodes to something else \u2014 `{}`, `[]`, prose, nothing at all \u2014\n * reached it as a message with no tag and died there, so the gateway answered\n * `500 Internal Server Error` with an empty body (release validation). That is the\n * wrong half of the contract twice over: it tells an operator the gateway\n * broke, and it tells a client to retry a request that can never succeed.\n *\n * The body reader is cached per request, so reading the body here does not\n * consume the body the mount reads. It is read through the reader the mount\n * uses, `text` under a JSON serialization and `arrayBuffer` under a binary\n * one, so the bytes the mount decodes are the bytes the client sent.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerIngress = (options: IngressOptions = {}) => {\n const refusal = settingRefusal(\"The gateway request body limit\", options.maxRequestBodyBytes) ??\n settingRefusal(\"The gateway ticket lifetime\", options.ticketMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n const maxBytes = options.maxRequestBodyBytes ?? defaultMaxRequestBodyBytes\n const allowedHosts = options.allowedHosts ?? loopbackHostHeaderNames\n const ticketMillis = options.ticketMillis ?? defaultTicketMillis\n return HttpRouter.middleware(\n Effect.gen(function*() {\n const serialization = yield* RpcSerialization.RpcSerialization\n const binary = !serialization.contentType.includes(\"json\")\n /** Outstanding tickets: the Authorization header each was issued under, and when it lapses. */\n const tickets = new Map<string, { readonly authorization: string; readonly expiresAt: number }>()\n const issue = (authorization: string, now: number): Ticket => {\n for (const [ticket, entry] of tickets) {\n if (entry.expiresAt <= now) tickets.delete(ticket)\n }\n if (tickets.size >= maxOutstandingTickets) tickets.delete(tickets.keys().next().value!)\n const ticket = newTicket()\n tickets.set(ticket, { authorization, expiresAt: now + ticketMillis })\n return { ticket, expiresInMs: ticketMillis }\n }\n // Lookup and delete run without yielding, so of two upgrades presenting\n // one ticket exactly one gets its header.\n const redeem = (url: string, now: number): string | undefined => {\n const ticket = presentedTicket(url)\n const entry = ticket === undefined ? undefined : tickets.get(ticket)\n if (entry === undefined) return undefined\n tickets.delete(ticket!)\n return entry.expiresAt > now ? entry.authorization : undefined\n }\n return (httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, Types.unhandled>) =>\n Effect.gen(function*() {\n const request = yield* HttpServerRequest.HttpServerRequest\n const browserRefusal = browserRequestRefusal(request.headers, allowedHosts, options.loopbackOnly === true)\n if (browserRefusal !== undefined) return refuse(browserRefusal.error, browserRefusal.status)\n const path = routedPath(request.url)\n if (path === ticketPath && request.method === \"POST\" && options.authorize !== undefined) {\n const authorization = request.headers.authorization\n if (authorization === undefined || !(yield* options.authorize(request.headers, path))) {\n return refuse(unauthorizedRequest(), 401)\n }\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n return HttpServerResponse.jsonUnsafe(encodeTicket(issue(authorization, now)), {\n headers: { \"cache-control\": \"no-store\" }\n })\n }\n if (protectedPaths.includes(path) && options.authorize !== undefined) {\n const authorized = yield* options.authorize(request.headers, path)\n if (!authorized) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n const authorization = ticketPaths.includes(path) ? redeem(request.url, now) : undefined\n if (authorization === undefined) return refuse(unauthorizedRequest(), 401)\n // A ticket stands for the credential it was issued under, which\n // may not reach every mount a ticket can open.\n const headers = Headers.set(request.headers, \"authorization\", authorization)\n if (!(yield* options.authorize(headers, path))) return refuse(unauthorizedRequest(), 401)\n // The socket protocol stamps the upgrade's headers on every\n // frame, so the redeemed bearer authenticates each one.\n return yield* Effect.provideService(\n httpEffect,\n HttpServerRequest.HttpServerRequest,\n request.modify({ headers })\n )\n }\n }\n if (request.method !== \"POST\" || !boundedPostPaths.includes(path)) return yield* httpEffect\n // A declared length is a hint that saves reading a body already\n // known to be too big. It is never trusted the other way: a body\n // that declares less than it sends is still measured by the read,\n // and a chunked body declares nothing at all.\n const declaredLength = Number(request.headers[\"content-length\"])\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n return refuse(requestTooLarge(path, maxBytes), 413)\n }\n // The body is read through the reader the mount will use. The Node\n // adapter caches whichever reader ran first and derives the other\n // from it, so a text read here would hand a binary mount a UTF-8\n // re-encoding of its MessagePack bytes, with every invalid sequence\n // replaced. A binary body is bounded here and framed by the mount.\n const read = yield* (binary ? Effect.as(request.arrayBuffer, undefined) : request.text).pipe(\n Effect.provideService(HttpServerRequest.MaxBodySize, ByteSize.bytes(maxBytes)),\n Effect.match({\n onFailure: (error): { readonly body: string | undefined; readonly error: unknown } => ({\n body: undefined,\n error\n }),\n onSuccess: (body) => ({ body, error: undefined })\n })\n )\n if (read.error !== undefined) {\n const answer = bodyRefusal(path, maxBytes, read.error)\n return refuse(answer.error, answer.status)\n }\n if (read.body === undefined) return yield* httpEffect\n return !rpcPaths.includes(path) || carriesRpcRequest(serialization, read.body)\n ? yield* httpEffect\n : refuse(malformedRequest(path), 400)\n })\n }),\n { global: true }\n )\n}\n\n/**\n * How an assembled gateway is configured.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface LayerOptions {\n /**\n * How often an idle followed `Watch` on `/rpc/ws` and an idle followed\n * `Projection.Subscribe` on `/projections/ws` emit a keepalive. Unset, the\n * `Watch` keepalive runs at `Projections.heartbeatIntervalMillis` and the\n * supplied `Projections` service keeps the cadence it was built with.\n */\n readonly heartbeatMillis?: number | undefined\n /** The ingress policy the RPC mounts run behind. */\n readonly ingress?: IngressOptions | undefined\n /** Enables the authenticated Go-to-Control runtime bridge. */\n readonly runtimeBridge?: RuntimeBridge.Config | undefined\n}\n\n/**\n * The whole gateway surface, as one application layer a host serves.\n *\n * The caller supplies the transport serialization, the authentication\n * middleware for both RPC groups, and the services the mounts read through\n * (`Control`, `Projections`, `SyncServer`). `@smthrs/gateway/node/NodeGateway`\n * is the Node host that binds this to a socket.\n *\n * A keepalive cadence or a body limit that is not a positive safe integer is\n * refused here, before anything binds, through the layer's typed\n * `GatewayError` channel.\n *\n * @param health the identity `GET /health` answers with\n * @param options the keepalive cadence and the ingress policy\n * @since 1.0.0\n * @category layers\n */\nexport const layer = (health: Health, options: LayerOptions = {}) => {\n const refusal = settingRefusal(\"The gateway keepalive cadence\", options.heartbeatMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n return Layer.mergeAll(\n layerControlHttp(options.heartbeatMillis),\n options.heartbeatMillis === undefined\n ? layerProjectionsHttp\n : layerProjectionsHttp.pipe(Layer.provide(layerProjectionsKeepAlive(options.heartbeatMillis))),\n layerSyncHttp,\n layerHealth(health),\n ...(options.runtimeBridge === undefined\n ? []\n : [RuntimeBridge.layer(options.runtimeBridge), HostBranchPresence.layer(options.runtimeBridge)]),\n layerIngress(options.ingress)\n )\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BA,qBAAwB;AACxB,mBAA8B;AAC9B,sBAAiC;AACjC,yBAAiC;AAEjC,oBAA+B;AAC/B,sBAAyB;AACzB,iBAA4B;AAC5B,oBAAkE;AAClE,eAA0B;AAC1B,kBAA2E;AAC3E,iBAA4C;AAC5C,0BAA6C;AAC7C,yBAA4B;AAC5B,oBAA+B;AAC/B,yBAAoC;AACpC,yBAAqD;AACrD,iBAA4B;AAC5B,oBAA+B;AASxB,MAAM,SAAS,qBAAO,OAAO;AAAA,EAClC,GAAG,cAAc,cAAc;AAAA,EAC/B,SAAS,qBAAO;AAAA,EAChB,eAAe,qBAAO,SAAS,cAAc,QAAQ;AACvD,CAAC;AAUD,MAAM,eAAe,qBAAO,kBAAkB,MAAM;AAS7C,MAAM,cAAc,CAAC,WAC1B,uBAAW,IAAI,OAAO,WAAW,+BAAmB,WAAW,aAAa,MAAM,CAAC,CAAC;AAQ/E,MAAM,gBAAgB,+BAAY;AAAA,EACvC,qBAAO,IAAI,aAAY;AACrB,UAAM,cAAc,OAAO;AAC3B,UAAM,UAAU,OAAO;AAEvB,UAAM,UAAU,OAAO,qBAAO,cAAc,WAAW,UAAU;AACjE,UAAM,cAAc,qBAAO,MAAM,SAAS;AAAA,MACxC,QAAQ,MAAM,qBAAO,KAAK,IAAI,aAAa,YAAY,EAAE,SAAS,eAAe,QAAQ,gBAAgB,CAAC,CAAC;AAAA,MAC3G,QAAQ,qBAAO;AAAA,IACjB,CAAC;AAGD,UAAM,WAAW,qBAAO,IAAI,aAAY;AACtC,YAAM,YAAY,OAAO;AACzB,UAAI,UAAU,OAAO,cAAc,UAAU,SAAS,aAAa;AACjE,eAAO,OAAO,IAAI,aAAa,aAAa,EAAE,SAAS,qCAAqC,CAAC;AAAA,MAC/F;AAAA,IACF,CAAC;AACD,WAAO,+BAAY,GAAG;AAAA,MACpB,uBAAuB,qBAAO,GAAG,kBAAkB;AAAA,QACjD,CAAC,EAAE,UAAU,MAAM,MAAM,YAAY,SAAS,UAAU,KAAK;AAAA,QAC7D,qBAAO,SAAS,cAAc,SAAS;AAAA,MACzC;AAAA,MACA,wBAAwB,CAAC,EAAE,OAAO,SAAS,MACzC,qBAAO,SAAS,YAAY,UAAU,UAAU,KAAK,GAAG,cAAc,SAAS;AAAA;AAAA;AAAA,MAGjF,mBAAmB,qBAAO,GAAG,wBAAwB,EAAE,CAAC,UACtD,qBAAO,IAAI,aAAY;AAMrB,cAAM,YAAY,OAAO;AAGzB,YAAI,UAAU,OAAO,cAAc,UAAU,SAAS,aAAa;AACjE,iBAAO,OAAO,IAAI,aAAa,aAAa,EAAE,SAAS,qCAAqC,CAAC;AAAA,QAC/F;AACA,cAAM,UAAU;AAAA,UACd,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,UACb,gBAAgB,MAAM;AAAA,UACtB;AAAA,QACF;AAcA,cAAM,OAAO,gBAAgB,eAAe,MAAM,MAAM;AACxD,YAAI,SAAS,UAAa,MAAM,OAAO,SAAS,QAAQ;AAKtD,cAAI,MAAM,aAAa,QAAQ;AAC7B,mBAAO,OAAO,IAAI,aAAa,aAAa;AAAA,cAC1C,OAAO,cAAc,KAAK,IAAI;AAAA,YAChC,CAAC;AAAA,UACH;AACA,cAAI,MAAM,WAAW,QAAW;AAC9B,mBAAO,OAAO,IAAI,aAAa,aAAa;AAAA,cAC1C,OAAO,YAAY,KAAK,IAAI;AAAA,YAC9B,CAAC;AAAA,UACH;AACA,gBAAMA,YAAW,OAAO,QAAQ,OAAO;AAAA,YACrC,OAAO,MAAM,OAAO;AAAA,YACpB,QAAQ,EAAE,MAAM,KAAK,MAAM,SAAS,MAAM,OAAO;AAAA,YACjD,gBAAgB,MAAM;AAAA,YACtB;AAAA,UACF,CAAC;AACD,iBAAO,EAAE,UAAAA,UAAS;AAAA,QACpB;AACA,cAAM,WAAW,MAAM,aAAa,YAChC,OAAO,QAAQ,QAAQ,OAAO,IAC9B,OAAO,QAAQ,KAAK,OAAO;AAC/B,eAAO,EAAE,SAAS;AAAA,MACpB,CAAC,GAAG,qBAAO,SAAS,cAAc,SAAS,CAAC;AAAA,MAC9C,YAAY,qBAAO,GAAG,cAAc;AAAA,QAClC,CAAC,UAAU,qBAAO,QAAQ,UAAU,qBAAO,QAAQ,aAAa,CAAC,SAAS,KAAK,KAAK,KAAK,CAAC,CAAC;AAAA,QAC3F,qBAAO,SAAS,cAAc,SAAS;AAAA,MACzC;AAAA,MACA,cAAc,qBAAO,GAAG,gBAAgB;AAAA,QACtC,CAAC,UAAU,qBAAO,QAAQ,UAAU,qBAAO,QAAQ,aAAa,CAAC,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC;AAAA,QAC7F,qBAAO,SAAS,cAAc,SAAS;AAAA,MACzC;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAcO,MAAM,qBAAqB;AAelC,MAAM,YAAY,CAChB,QACA,QACA,WACyE;AACzE,MAAI,OAAO,WAAW,MAAO,QAAO;AACpC,MAAI,SAAS,OAAO,gBACjB,OAAO,kBAAkB,SAAY,SAAY,EAAE,UAAU,OAAO,cAAc;AACrF,MAAI,WAAW,QAAQ,YAAY;AACnC,QAAM,UAAU,qBAAO,IAAI,QAAQ,CAAC,UAAU;AAC5C,eAAW,MAAM;AACjB,aAAS,MAAM,UAAU,EAAE,SAAS;AACpC,WAAO;AAAA,EACT,CAAC;AACD,QAAM,QAAQ,qBAAO,KAAK,MAAM,EAAE;AAAA,IAChC,qBAAO,KAAK,CAAC;AAAA,IACb,qBAAO;AAAA,MAAU,MACf,qBAAO;AAAA,QACL,qBAAO,UAAU,CAAC,UAAU,MAAM,iBAAiB;AAAA,QACnD,CAAC,gBAA4C;AAAA,UAC3C;AAAA,UACA,GAAI,OAAO,UAAU,UAAa,WAAW,SAAY,CAAC,IAAI,EAAE,OAAO;AAAA,UACvE,MAAM;AAAA,UACN,GAAI,OAAO,UAAU,SAAY,CAAC,IAAI,EAAE,OAAO,OAAO,MAAM;AAAA,UAC5D;AAAA,UACA,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO,qBAAO,MAAM,SAAS,OAAO,EAAE,cAAc,OAAO,CAAC;AAC9D;AAeO,MAAM,iBAAiB,CAC5B,SAAiB,+CAEjB,oBAAM,OAAO,sBAAO;AAAA,EAClB,qBAAO,IAAI,wBAAS,CAAC,YACnB,uBAAQ,GAAG,EAAE,GAAG,SAAS,OAAO,CAAC,WAAW,UAAU,QAAQ,QAAQ,QAAQ,MAAM,MAAM,CAAC,EAAE,CAAC,CAAC;AACnG;AAgBK,MAAM,4BAA4B,CAAC,WACxC,oBAAM,OAAO,8BAAW;AAAA,EACtB,qBAAO,IAAI,gCAAa,CAAC,gBACvB,+BAAY,GAAG;AAAA,IACb,GAAG;AAAA,IACH,WAAW,CAAC,UAAU,UAAU,sBAAsB,QAAQ,YAAY,UAAU,UAAU,KAAK,CAAC;AAAA,EACtG,CAAC,CAAC;AACN;AAGF,MAAM,wBAAwB,CAC5B,QACA,WACkE;AAGlE,QAAM,QAAqD,qBAAO,KAAK,MAAM,EAAE;AAAA,IAC7E,qBAAO,KAAK,CAAC;AAAA,IACb,qBAAO;AAAA,MAAU,MACf,qBAAO;AAAA,QACL,qBAAO,UAAU,CAAC,UAAU,MAAM,iBAAiB;AAAA,QACnD,CAAC,UAAwC,EAAE,MAAM,aAAa,KAAK;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AACA,SAAO,qBAAO;AAAA,IACZ,qBAAO,OAAO,QAAQ,CAAC,UAAU,MAAM,SAAS,WAAW;AAAA,IAC3D;AAAA,IACA,EAAE,cAAc,OAAO;AAAA,EACzB;AACF;AAUO,MAAM,mBAAmB,CAAC,WAC/B,cAAc,UAAU,KAAK,oBAAM,QAAQ,eAAe,MAAM,CAAC,CAAC;AAEpE,MAAM,UAAU,qBAAU,MAAM,gCAAa,EAAE,qBAAqB,KAAK,CAAC;AAE1E,MAAM,OAAO,qBAAU,MAAM,0BAAU,EAAE,qBAAqB,KAAK,CAAC;AAa7D,MAAM,uBAAuB,oBAAM;AAAA,EACxC,QAAQ;AAAA,IACN,oBAAM,QAAQ,aAAa;AAAA,IAC3B,oBAAM,aAAa,qBAAU,kBAAkB,EAAE,MAAM,eAAe,CAAC,CAAC;AAAA,IACxE,oBAAM;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,oBAAM,QAAQ,aAAa;AAAA,IAC3B,oBAAM,aAAa,qBAAU,uBAAuB,EAAE,MAAM,kBAAkB,CAAC,CAAC;AAAA,IAChF,oBAAM;AAAA,EACR;AACF;AAQO,MAAM,gBAAgB,oBAAM;AAAA,EACjC,KAAK;AAAA,IACH,oBAAM,QAAQ,WAAW,aAAa;AAAA,IACtC,oBAAM,aAAa,qBAAU,kBAAkB,EAAE,MAAM,QAAQ,CAAC,CAAC;AAAA,IACjE,oBAAM;AAAA,EACR;AAAA,EACA,KAAK;AAAA,IACH,oBAAM,QAAQ,WAAW,aAAa;AAAA,IACtC,oBAAM,aAAa,qBAAU,uBAAuB,EAAE,MAAM,WAAW,CAAC,CAAC;AAAA,IACzE,oBAAM;AAAA,EACR;AACF;AAQO,MAAM,WAAkC,CAAC,QAAQ,gBAAgB,SAAS,SAAS;AAQnF,MAAM,mBAA0C;AAAA,EACrD,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACF;AA2BO,MAAM,iBAAwC;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAgCO,MAAM,aAAa,CAAC,QAAwB;AAMjD,QAAM,SAAS,IAAI,WAAW,GAAG,IAAI,yBAAyB,GAAG,KAAK;AACtE,QAAM,SAAS,IAAI,MAAM,QAAQ,wBAAwB;AACzD,MAAI,WAAW,KAAM,QAAO;AAC5B,QAAM,WAA0B,CAAC;AACjC,aAAW,WAAW,OAAO,SAAS,MAAM,GAAG,GAAG;AAChD,UAAM,YAAY,QAAQ,QAAQ,GAAG;AACrC,UAAM,QAAQ,YAAY,IAAI,UAAU,QAAQ,MAAM,GAAG,SAAS;AAClE,QAAI,UAAU,GAAI;AAClB,QAAI;AACF,eAAS,KAAK,UAAU,KAAK,CAAC;AAAA,IAChC,QAAQ;AACN,eAAS,KAAK,KAAK;AAAA,IACrB;AAAA,EACF;AACA,SAAO,IAAI,SAAS,KAAK,GAAG,CAAC,GAAG,YAAY;AAC9C;AAQO,MAAM,aAAa;AAQnB,MAAM,cAAqC,CAAC,WAAW,mBAAmB,UAAU;AAQpF,MAAM,sBAAsB;AAGnC,MAAM,wBAAwB;AAgBvB,MAAM,SAAS,qBAAO,OAAO;AAAA,EAClC,QAAQ,qBAAO;AAAA,EACf,aAAa,qBAAO;AACtB,CAAC;AAUD,MAAM,eAAe,qBAAO,kBAAkB,MAAM;AAGpD,MAAM,YAAY,MAChB,MAAM,KAAK,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAG9G,MAAM,kBAAkB,CAAC,QAAoC;AAC3D,QAAM,SAAS,IAAI,MAAM,KAAK,wBAAwB;AACtD,QAAM,SAAS,QAAQ,aAAa,IAAI,QAAQ;AAChD,SAAO,WAAW,QAAQ,WAAW,UAAa,WAAW,KAAK,SAAY;AAChF;AAQO,MAAM,6BAA6B,OAAO;AA4BjD,MAAM,qBAAqB,qBAAO,kBAAkB,gCAAY;AAGhE,MAAM,mBAAmB,CAAC,SAA8C;AACtE,MAAI,SAAS,UAAa,CAAC,qDAAqD,KAAK,IAAI,EAAG,QAAO;AACnG,QAAM,SAAS,IAAI,MAAM,UAAU,IAAI,EAAE;AACzC,SAAO,WAAW,OAAO,SAAY;AACvC;AAEA,MAAM,wBAAwB,CAC5B,SACA,cACA,iBAC0E;AAC1E,QAAM,YAAY,iBAAiB,QAAQ,IAAI;AAC/C,MACE,cAAc,UAAa,CAAC,aAAa,SAAS,UAAU,QAAQ,KACnE,gBAAgB,CAAC,aAAa,KAAK,QAAQ,IAAK,EACjD,QAAO,EAAE,OAAO,mBAAmB,GAAG,QAAQ,IAAI;AACpD,QAAM,SAAS,QAAQ;AAGvB,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,SAAS,IAAI,MAAM,MAAM;AAC/B,SAAO,WAAW,SAAS,OAAO,aAAa,WAAW,OAAO,aAAa,aAC1E,OAAO,WAAW,UAAU,OAAO,SAAS,UAAU,OACtD,SACA,EAAE,OAAO,qBAAqB,GAAG,QAAQ,IAAI;AACnD;AAGA,MAAM,SAAS,CAAC,OAAqB,WACnC,+BAAmB,WAAW,mBAAmB,KAAK,GAAG,EAAE,OAAO,CAAC;AAErE,MAAM,mBAAmB,CAAC,SACxB,IAAI,iCAAa,EAAE,MAAM,qBAAqB,SAAS,QAAQ,IAAI,kCAAkC,CAAC;AAExG,MAAM,sBAAsB,MAC1B,IAAI,iCAAa,EAAE,MAAM,gBAAgB,SAAS,wCAAwC,CAAC;AAE7F,MAAM,qBAAqB,MACzB,IAAI,iCAAa,EAAE,MAAM,gBAAgB,SAAS,sDAAsD,CAAC;AAE3G,MAAM,uBAAuB,MAC3B,IAAI,iCAAa,EAAE,MAAM,kBAAkB,SAAS,wDAAwD,CAAC;AAE/G,MAAM,kBAAkB,CAAC,MAAc,aACrC,IAAI,iCAAa;AAAA,EACf,MAAM;AAAA,EACN,SAAS,QAAQ,IAAI,gBAAgB,QAAQ;AAC/C,CAAC;AAGH,MAAM,mBAAmB;AAalB,MAAM,oBAA2C,CAAC,aAAa,aAAa,KAAK;AASjF,MAAM,0BAAiD,kBAAkB;AAAA,EAAI,CAAC,SACnF,KAAK,SAAS,GAAG,IAAI,IAAI,IAAI,MAAM;AACrC;AAEA,MAAM,eAAe,CAAC,SAAiB,KAAK,QAAQ,uBAAuB,MAAM;AAEjF,MAAM,eAAe,IAAI,OAAO,OAAO,wBAAwB,IAAI,YAAY,EAAE,KAAK,GAAG,CAAC,oBAAoB,GAAG;AAsB1G,MAAM,oBAAoB,CAAC,UAA4B;AAC5D,QAAM,SAAS,OAAO,UAAU,YAAY,UAAU,OACjD,MAAwC,SACzC;AACJ,QAAM,QAAQ,OAAO,WAAW,YAAY,WAAW,OAClD,OAAwC,QACzC;AACJ,SAAO,iBAAiB,SAAS,MAAM,YAAY;AACrD;AAiBO,MAAM,cAAc,CACzB,MACA,UACA,UAEA,kBAAkB,KAAK,IACnB,EAAE,OAAO,gBAAgB,MAAM,QAAQ,GAAG,QAAQ,IAAI,IACtD;AAAA,EACA,OAAO,IAAI,iCAAa;AAAA,IACtB,MAAM;AAAA,IACN,SAAS,QAAQ,IAAI;AAAA,EACvB,CAAC;AAAA,EACD,QAAQ;AACV;AAkBG,MAAM,oBAAoB,CAC/B,eACA,SACY;AACZ,MAAI,CAAC,cAAc,YAAY,SAAS,MAAM,EAAG,QAAO;AACxD,MAAI;AACF,UAAM,UAAU,cAAc,WAAW,EAAE,OAAO,IAAI;AACtD,WAAO,QAAQ,SAAS,KAAK,QAAQ;AAAA,MAAM,CAAC,YAC1C,OAAO,YAAY,YAAY,YAAY,QAC3C,OAAQ,QAAwC,SAAS;AAAA,IAC3D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAqBO,MAAM,eAAe,CAAC,UAA0B,CAAC,MAAM;AAC5D,QAAM,cAAU,oCAAe,kCAAkC,QAAQ,mBAAmB,SAC1F,oCAAe,+BAA+B,QAAQ,YAAY;AACpE,MAAI,YAAY,OAAW,QAAO,oBAAM,cAAc,qBAAO,KAAK,OAAO,CAAC;AAC1E,QAAM,WAAW,QAAQ,uBAAuB;AAChD,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,SAAO,uBAAW;AAAA,IAChB,qBAAO,IAAI,aAAY;AACrB,YAAM,gBAAgB,OAAO,4BAAiB;AAC9C,YAAM,SAAS,CAAC,cAAc,YAAY,SAAS,MAAM;AAEzD,YAAM,UAAU,oBAAI,IAA4E;AAChG,YAAM,QAAQ,CAAC,eAAuB,QAAwB;AAC5D,mBAAW,CAACC,SAAQ,KAAK,KAAK,SAAS;AACrC,cAAI,MAAM,aAAa,IAAK,SAAQ,OAAOA,OAAM;AAAA,QACnD;AACA,YAAI,QAAQ,QAAQ,sBAAuB,SAAQ,OAAO,QAAQ,KAAK,EAAE,KAAK,EAAE,KAAM;AACtF,cAAM,SAAS,UAAU;AACzB,gBAAQ,IAAI,QAAQ,EAAE,eAAe,WAAW,MAAM,aAAa,CAAC;AACpE,eAAO,EAAE,QAAQ,aAAa,aAAa;AAAA,MAC7C;AAGA,YAAM,SAAS,CAAC,KAAa,QAAoC;AAC/D,cAAM,SAAS,gBAAgB,GAAG;AAClC,cAAM,QAAQ,WAAW,SAAY,SAAY,QAAQ,IAAI,MAAM;AACnE,YAAI,UAAU,OAAW,QAAO;AAChC,gBAAQ,OAAO,MAAO;AACtB,eAAO,MAAM,YAAY,MAAM,MAAM,gBAAgB;AAAA,MACvD;AACA,aAAO,CAAC,eACN,qBAAO,IAAI,aAAY;AACrB,cAAM,UAAU,OAAO,8BAAkB;AACzC,cAAM,iBAAiB,sBAAsB,QAAQ,SAAS,cAAc,QAAQ,iBAAiB,IAAI;AACzG,YAAI,mBAAmB,OAAW,QAAO,OAAO,eAAe,OAAO,eAAe,MAAM;AAC3F,cAAM,OAAO,WAAW,QAAQ,GAAG;AACnC,YAAI,SAAS,cAAc,QAAQ,WAAW,UAAU,QAAQ,cAAc,QAAW;AACvF,gBAAM,gBAAgB,QAAQ,QAAQ;AACtC,cAAI,kBAAkB,UAAa,EAAE,OAAO,QAAQ,UAAU,QAAQ,SAAS,IAAI,IAAI;AACrF,mBAAO,OAAO,oBAAoB,GAAG,GAAG;AAAA,UAC1C;AACA,gBAAM,MAAM,OAAO,qBAAO,UAAU,CAAC,UAAU,MAAM,iBAAiB;AACtE,iBAAO,+BAAmB,WAAW,aAAa,MAAM,eAAe,GAAG,CAAC,GAAG;AAAA,YAC5E,SAAS,EAAE,iBAAiB,WAAW;AAAA,UACzC,CAAC;AAAA,QACH;AACA,YAAI,eAAe,SAAS,IAAI,KAAK,QAAQ,cAAc,QAAW;AACpE,gBAAM,aAAa,OAAO,QAAQ,UAAU,QAAQ,SAAS,IAAI;AACjE,cAAI,CAAC,YAAY;AACf,kBAAM,MAAM,OAAO,qBAAO,UAAU,CAAC,UAAU,MAAM,iBAAiB;AACtE,kBAAM,gBAAgB,YAAY,SAAS,IAAI,IAAI,OAAO,QAAQ,KAAK,GAAG,IAAI;AAC9E,gBAAI,kBAAkB,OAAW,QAAO,OAAO,oBAAoB,GAAG,GAAG;AAGzE,kBAAM,UAAU,oBAAQ,IAAI,QAAQ,SAAS,iBAAiB,aAAa;AAC3E,gBAAI,EAAE,OAAO,QAAQ,UAAU,SAAS,IAAI,GAAI,QAAO,OAAO,oBAAoB,GAAG,GAAG;AAGxF,mBAAO,OAAO,qBAAO;AAAA,cACnB;AAAA,cACA,8BAAkB;AAAA,cAClB,QAAQ,OAAO,EAAE,QAAQ,CAAC;AAAA,YAC5B;AAAA,UACF;AAAA,QACF;AACA,YAAI,QAAQ,WAAW,UAAU,CAAC,iBAAiB,SAAS,IAAI,EAAG,QAAO,OAAO;AAKjF,cAAM,iBAAiB,OAAO,QAAQ,QAAQ,gBAAgB,CAAC;AAC/D,YAAI,OAAO,SAAS,cAAc,KAAK,iBAAiB,UAAU;AAChE,iBAAO,OAAO,gBAAgB,MAAM,QAAQ,GAAG,GAAG;AAAA,QACpD;AAMA,cAAM,OAAO,QAAQ,SAAS,qBAAO,GAAG,QAAQ,aAAa,MAAS,IAAI,QAAQ,MAAM;AAAA,UACtF,qBAAO,eAAe,8BAAkB,aAAa,SAAS,MAAM,QAAQ,CAAC;AAAA,UAC7E,qBAAO,MAAM;AAAA,YACX,WAAW,CAAC,WAA2E;AAAA,cACrF,MAAM;AAAA,cACN;AAAA,YACF;AAAA,YACA,WAAW,CAAC,UAAU,EAAE,MAAM,OAAO,OAAU;AAAA,UACjD,CAAC;AAAA,QACH;AACA,YAAI,KAAK,UAAU,QAAW;AAC5B,gBAAM,SAAS,YAAY,MAAM,UAAU,KAAK,KAAK;AACrD,iBAAO,OAAO,OAAO,OAAO,OAAO,MAAM;AAAA,QAC3C;AACA,YAAI,KAAK,SAAS,OAAW,QAAO,OAAO;AAC3C,eAAO,CAAC,SAAS,SAAS,IAAI,KAAK,kBAAkB,eAAe,KAAK,IAAI,IACzE,OAAO,aACP,OAAO,iBAAiB,IAAI,GAAG,GAAG;AAAA,MACxC,CAAC;AAAA,IACL,CAAC;AAAA,IACD,EAAE,QAAQ,KAAK;AAAA,EACjB;AACF;AAuCO,MAAM,QAAQ,CAAC,QAAgB,UAAwB,CAAC,MAAM;AACnE,QAAM,cAAU,oCAAe,iCAAiC,QAAQ,eAAe;AACvF,MAAI,YAAY,OAAW,QAAO,oBAAM,cAAc,qBAAO,KAAK,OAAO,CAAC;AAC1E,SAAO,oBAAM;AAAA,IACX,iBAAiB,QAAQ,eAAe;AAAA,IACxC,QAAQ,oBAAoB,SACxB,uBACA,qBAAqB,KAAK,oBAAM,QAAQ,0BAA0B,QAAQ,eAAe,CAAC,CAAC;AAAA,IAC/F;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,GAAI,QAAQ,kBAAkB,SAC1B,CAAC,IACD,CAAC,cAAc,MAAM,QAAQ,aAAa,GAAG,mBAAmB,MAAM,QAAQ,aAAa,CAAC;AAAA,IAChG,aAAa,QAAQ,OAAO;AAAA,EAC9B;AACF;",
6
6
  "names": ["decision", "ticket"]
7
7
  }
@@ -361,8 +361,8 @@ export declare const execute: (config: Config, control: Control["Service"], prin
361
361
  readonly capabilities: readonly string[];
362
362
  readonly flows: readonly string[];
363
363
  readonly budget: {
364
- readonly tokens?: number | undefined;
365
364
  readonly deadline?: number | undefined;
365
+ readonly tokens?: number | undefined;
366
366
  readonly milliseconds?: number | undefined;
367
367
  readonly usd?: number | undefined;
368
368
  readonly onExceeded?: "warn" | "fail" | "park" | "skip-remaining" | undefined;
@@ -379,8 +379,8 @@ export declare const execute: (config: Config, control: Control["Service"], prin
379
379
  readonly capabilities: readonly string[];
380
380
  readonly flows: readonly string[];
381
381
  readonly budget: {
382
- readonly tokens?: number | undefined;
383
382
  readonly deadline?: number | undefined;
383
+ readonly tokens?: number | undefined;
384
384
  readonly milliseconds?: number | undefined;
385
385
  readonly usd?: number | undefined;
386
386
  readonly onExceeded?: "warn" | "fail" | "park" | "skip-remaining" | undefined;
@@ -397,8 +397,8 @@ export declare const execute: (config: Config, control: Control["Service"], prin
397
397
  readonly capabilities: readonly string[];
398
398
  readonly flows: readonly string[];
399
399
  readonly budget: {
400
- readonly tokens?: number | undefined;
401
400
  readonly deadline?: number | undefined;
401
+ readonly tokens?: number | undefined;
402
402
  readonly milliseconds?: number | undefined;
403
403
  readonly usd?: number | undefined;
404
404
  readonly onExceeded?: "warn" | "fail" | "park" | "skip-remaining" | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"GatewayServer.d.ts","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GatewayServer.d.ts","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"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"}
@@ -287,6 +287,7 @@ export const rpcPaths = ["/rpc", "/projections", "/sync", "/branch"];
287
287
  export const boundedPostPaths = [
288
288
  ...rpcPaths,
289
289
  "/runtime/v1/command",
290
+ "/runtime/v1/monitor",
290
291
  "/runtime/v1/observe"
291
292
  ];
292
293
  /**
@@ -322,6 +323,7 @@ export const protectedPaths = [
322
323
  "/projections/ws",
323
324
  "/sync/ws",
324
325
  "/runtime/v1/command",
326
+ "/runtime/v1/monitor",
325
327
  "/runtime/v1/observe"
326
328
  ];
327
329
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"GatewayServer.js","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The assembled workspace gateway: one HTTP surface carrying the control\n * plane, the sync read path, the served projections, and a health probe.\n *\n * Mounts:\n *\n * | Path | Protocol | Serves |\n * | --- | --- | --- |\n * | `POST /rpc` | RPC over HTTP | `@smthrs/control` `ControlRpcs` |\n * | `/rpc/ws` | RPC over WebSocket | `ControlRpcs`, including a kept-alive `Watch` |\n * | `POST /projections` | RPC over HTTP | `GatewayRpcs` |\n * | `/projections/ws` | RPC over WebSocket | `GatewayRpcs`, including `Projection.Subscribe` |\n * | `POST /sync` | RPC over HTTP | `@smthrs/sync` `SyncRpcs` |\n * | `/sync/ws` | RPC over WebSocket | `SyncRpcs` |\n * | `GET /health` | JSON | `GatewaySchema.GatewayHealth` plus the package version |\n * | `POST /auth/ticket` | JSON | A single-use {@link Ticket} that opens one protected socket |\n *\n * `/health` is deliberately unauthenticated: a supervisor decides whether to\n * keep or replace a gateway process by asking which workspace it belongs to,\n * and a probe that needed a credential could not answer that question about a\n * gateway it did not start. The response carries identity only: the\n * workspace hash, the gateway id, the protocol version, and the package\n * version. It never carries a token, a run, or a path.\n *\n * @since 1.0.0\n */\n\nimport { Control } from \"@smthrs/control/Control\"\nimport * as ControlError from \"@smthrs/control/ControlError\"\nimport * as ControlExecutor from \"@smthrs/control/ControlExecutor\"\nimport { ControlPrincipal } from \"@smthrs/control/ControlRpcs\"\nimport type * as ControlSchema from \"@smthrs/control/ControlSchema\"\nimport * as ControlServer from \"@smthrs/control/ControlServer\"\nimport { SyncRpcs } from \"@smthrs/sync/SyncRpcs\"\nimport * as SyncServer from \"@smthrs/sync/SyncServer\"\nimport { Effect, Layer, Option, Schema, Stream, type Types } from \"effect\"\nimport * as ByteSize from \"effect/ByteSize\"\nimport { Headers, HttpRouter, HttpServerRequest, HttpServerResponse } from \"effect/http\"\nimport { RpcSerialization, RpcServer } from \"effect/rpc\"\nimport { GatewayError, settingRefusal } from \"./GatewayError.ts\"\nimport { GatewayRpcs } from \"./GatewayRpcs.ts\"\nimport * as GatewaySchema from \"./GatewaySchema.ts\"\nimport * as HostBranchPresence from \"./HostBranchPresence.ts\"\nimport { heartbeatIntervalMillis, Projections } from \"./Projections.ts\"\nimport * as RunHistory from \"./RunHistory.ts\"\nimport * as RuntimeBridge from \"./RuntimeBridge.ts\"\n\n/**\n * What `GET /health` answers: the `GatewaySchema.GatewayHealth` identity plus\n * the version of the package serving it.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Health = Schema.Struct({\n ...GatewaySchema.GatewayHealth.fields,\n version: Schema.String,\n runtimeBridge: Schema.optional(RuntimeBridge.Identity)\n})\n\n/**\n * What `GET /health` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Health = typeof Health.Type\n\nconst encodeHealth = Schema.encodeUnknownSync(Health)\n\n/**\n * Mounts the unauthenticated health probe.\n *\n * @param health the identity this process serves under\n * @since 1.0.0\n * @category layers\n */\nexport const layerHealth = (health: Health): Layer.Layer<never, never, HttpRouter.HttpRouter> =>\n HttpRouter.add(\"GET\", \"/health\", HttpServerResponse.jsonUnsafe(encodeHealth(health)))\n\n/**\n * The gateway's own RPC handlers over the read path and the control plane.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerHandlers = GatewayRpcs.toLayer(\n Effect.gen(function*() {\n const projections = yield* Projections\n const control = yield* Control\n // Only a host that owns the project's stores can fork or verify its runs.\n const history = yield* Effect.serviceOption(RunHistory.RunHistory)\n const historyHost = Option.match(history, {\n onNone: () => Effect.fail(new ControlError.Unavailable({ feature: \"run history\", ticket: \"smithers#3242\" })),\n onSome: Effect.succeed\n })\n // Forking writes a durable run and verifying drives the project's flow\n // code, so neither is an anonymous loopback read.\n const operator = Effect.gen(function*() {\n const principal = yield* ControlPrincipal\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n })\n return GatewayRpcs.of({\n \"Projection.Snapshot\": Effect.fn(\"Gateway.snapshot\")(\n ({ selector, after }) => projections.snapshot(selector, after),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Projection.Subscribe\": ({ after, selector }) =>\n Stream.tapCause(projections.subscribe(selector, after), ControlServer.logDefect),\n /* One operator command. Control owns the atomic decision plus durable\n * resume delegation; the gateway is only a transport adapter. */\n \"Approval.Submit\": Effect.fn(\"Gateway.submitApproval\")((input) =>\n Effect.gen(function*() {\n // The identity the shared `ControlAuth` middleware authenticated, as\n // `ControlServer` stamps it on `Approve` and `Deny`. This mount is\n // the same decision under a different payload, so it is answerable\n // to the same operator, and a decision journaled under the\n // composition's default operator names the wrong one.\n const principal = yield* ControlPrincipal\n // Answering a human wait uses signal rather than the approval policy.\n // Do not let that alternate mutation path bypass loopback authentication.\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n const payload = {\n target: input.target,\n scope: input.scope,\n idempotencyKey: input.idempotencyKey,\n principal\n }\n // A gate that asked a question is answered, not granted, and the\n // PAYLOAD says which kind it is: a human wait carries its durable\n // wait token as the target's digest, and nothing else does. Reading\n // it here rather than trusting the client to have sent `answer` is\n // the difference between a clear refusal and the one an operator\n // actually saw — a client that forgot the answer fell through to\n // `Control.approve`, which looked for a registered approval token,\n // found none, and reported `/control/RunNotFound` for a run that was\n // listed, rendered, and waiting (workspace 4bb93306, run-1).\n //\n // `target.runId` is the run the person opened and `wait.name` the\n // wait point's own name; `Control.signal` routes the pair into the\n // run tree, to whichever execution is holding it.\n const wait = ControlExecutor.answerableWait(input.target)\n if (wait !== undefined && input.target._tag === \"Node\") {\n // Neither half of a grant answers a question. A denial has nowhere\n // to go — the run is parked on a value, and refusing to supply one\n // leaves it parked until its own attempt budget or deadline ends\n // it — and an approval with no value supplies none.\n if (input.decision === \"deny\") {\n return yield* new ControlError.InvalidInput({\n issue: `decision: \"${wait.name}\" asks a question, which is answered rather than denied`\n })\n }\n if (input.answer === undefined) {\n return yield* new ControlError.InvalidInput({\n issue: `answer: \"${wait.name}\" asks a question, so a decision alone does not answer it`\n })\n }\n const decision = yield* control.signal({\n runId: input.target.runId,\n signal: { name: wait.name, payload: input.answer },\n idempotencyKey: input.idempotencyKey,\n principal\n })\n return { decision }\n }\n const decision = input.decision === \"approve\"\n ? yield* control.approve(payload)\n : yield* control.deny(payload)\n return { decision }\n }), Effect.tapCause(ControlServer.logDefect)),\n \"Run.Fork\": Effect.fn(\"Gateway.fork\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.fork(input))),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Run.Verify\": Effect.fn(\"Gateway.verify\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.verify(input))),\n Effect.tapCause(ControlServer.logDefect)\n )\n })\n })\n)\n\n/**\n * The kind a `Watch` keepalive carries.\n *\n * `ControlRpcs.Watch` answers `ControlSchema.ControlEvent` and has no frame of\n * its own for a keepalive, so the gateway publishes one as an event whose kind\n * no emitter uses. A fold that does not know the kind ignores it, which is\n * what every fold in this package and in `@smthrs/cli` `Forensics` already\n * does with an unknown kind.\n *\n * @since 1.0.0\n * @category models\n */\nexport const watchHeartbeatKind = \"control.gateway.heartbeat\"\n\n/**\n * Merges a keepalive into a followed `Watch` stream.\n *\n * A followed stream is silent while a run is thinking, and a relay cuts an\n * idle tunnel at 600 s. The Effect RPC client\n * sends its own `Ping` every 5 s, but a non-Effect consumer behind a relay\n * sends nothing, so the keepalive has to come from the server.\n *\n * The keepalive repeats the last delivered sequence and run-scoped cursor,\n * including an expansion offset, so it cannot advance or rewind a checkpoint.\n * It carries the watched run id so a client routing by run keeps routing.\n * A snapshot read (`follow: false`) is left alone: it has to end.\n */\nconst keptAlive = (\n millis: number,\n filter: ControlSchema.WatchFilter,\n events: Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError>\n): Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError> => {\n if (filter.follow === false) return events\n let cursor = filter.afterCursor ??\n (filter.afterSequence === undefined ? undefined : { sequence: filter.afterSequence })\n let sequence = cursor?.sequence ?? 0\n const tracked = Stream.map(events, (event) => {\n sequence = event.sequence\n cursor = event.cursor ?? { sequence }\n return event\n })\n const beats = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (occurredAt): ControlSchema.ControlEvent => ({\n sequence,\n ...(filter.runId === undefined || cursor === undefined ? {} : { cursor }),\n kind: watchHeartbeatKind,\n ...(filter.runId === undefined ? {} : { runId: filter.runId }),\n occurredAt,\n payload: null\n })\n )\n )\n )\n return Stream.merge(tracked, beats, { haltStrategy: \"left\" })\n}\n\n/**\n * The control service the `/rpc` mounts read through: the ambient one, with\n * the keepalive merged into `watch`.\n *\n * Wrapping the service rather than re-declaring the handlers keeps\n * `@smthrs/control` `ControlServer` the single definition of what every\n * procedure does, including the principal it stamps on every mutation that\n * records who asked. Only `watch` behaves differently, and only in what it\n * adds.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerKeepAlive = (\n millis: number = heartbeatIntervalMillis\n): Layer.Layer<Control, never, Control> =>\n Layer.effect(Control)(\n Effect.map(Control, (control) =>\n Control.of({ ...control, watch: (filter) => keptAlive(millis, filter, control.watch(filter)) }))\n )\n\n/**\n * The read path the `/projections` mounts serve through: the ambient one, with\n * a followed subscription's keepalive re-timed to the bind's cadence.\n *\n * `Projections.make` already merges a `heartbeat` frame at the cadence it was\n * built with. A host that supplies `Projections.layer` and sets the cadence at\n * the bind expects one option to govern both keepalives, so this replaces the\n * service's beats with beats at the bind's cadence rather than adding a second\n * channel. Snapshot reads are left alone: they have to end.\n *\n * @param millis how often an idle followed subscription emits a keepalive\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsKeepAlive = (millis: number): Layer.Layer<Projections, never, Projections> =>\n Layer.effect(Projections)(\n Effect.map(Projections, (projections) =>\n Projections.of({\n ...projections,\n subscribe: (selector, after) => keptAliveSubscription(millis, projections.subscribe(selector, after))\n }))\n )\n\n/** Replaces a subscription's keepalive channel with one at the given cadence. */\nconst keptAliveSubscription = <F extends GatewaySchema.GatewayFrame>(\n millis: number,\n frames: Stream.Stream<F, GatewayError>\n): Stream.Stream<F | GatewaySchema.HeartbeatFrame, GatewayError> => {\n // The first tick is dropped for the same reason `Projections` drops it: an\n // immediate keepalive would arrive before the snapshot it keeps alive.\n const beats: Stream.Stream<GatewaySchema.HeartbeatFrame> = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (atMs): GatewaySchema.HeartbeatFrame => ({ _tag: \"heartbeat\", atMs })\n )\n )\n )\n return Stream.merge(\n Stream.filter(frames, (frame) => frame._tag !== \"heartbeat\"),\n beats,\n { haltStrategy: \"left\" }\n )\n}\n\n/**\n * Mounts the control plane on `POST /rpc` and `/rpc/ws`, with the keepalive.\n *\n * @param millis how often an idle followed watch emits one, defaulting to\n * `Projections.heartbeatIntervalMillis`\n * @since 1.0.0\n * @category layers\n */\nexport const layerControlHttp = (millis?: number | undefined) =>\n ControlServer.layerHttp.pipe(Layer.provide(layerKeepAlive(millis)))\n\nconst gateway = RpcServer.layer(GatewayRpcs, { disableFatalDefects: true })\n\nconst sync = RpcServer.layer(SyncRpcs, { disableFatalDefects: true })\n\n/**\n * Mounts the gateway's projections on `POST /projections` and\n * `/projections/ws`.\n *\n * Both protocols are mounted together because a client reads a snapshot over\n * the request/response path and follows deltas over the socket, and pointing\n * those at two different servers would let them disagree.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsHttp = Layer.mergeAll(\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/projections\" })),\n Layer.fresh\n ),\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/projections/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * Mounts the sync read path on `POST /sync` and `/sync/ws`.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerSyncHttp = Layer.mergeAll(\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/sync\" })),\n Layer.fresh\n ),\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/sync/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * The POST mounts that carry RPC request messages and nothing else.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const rpcPaths: ReadonlyArray<string> = [\"/rpc\", \"/projections\", \"/sync\", \"/branch\"]\n\n/**\n * POST mounts whose request bodies are bounded before their handlers read.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const boundedPostPaths: ReadonlyArray<string> = [\n ...rpcPaths,\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * RPC paths whose upgrade or request must pass edge authentication.\n *\n * `POST /rpc` is deliberately not one of them. The control mount authenticates\n * in band through `@smthrs/control` `ControlRpcs.ControlAuth`, whose declared\n * error is `ControlError.Unauthorized`, and that typed control error is the\n * refusal the control plane publishes: \"Missing, malformed, empty, and\n * incorrect credentials all return the same typed `Unauthorized` control\n * error\" (`packages/smithers/gateway/docs/guides/serve-beyond-loopback.md`). An edge 401 answered\n * ahead of the mount, and `ControlClient` filters a non-2xx status, so every\n * refusal reached a caller as a `TransportError`, the one error class\n * `@smthrs/control` reserves for a request that failed *before* a declared\n * control response reached it. It also made the gateway refuse a call\n * differently from `NodeControl.layerServer` hosting the very same\n * `ControlRpcs`. The body limit below still applies to `/rpc`, so an\n * unauthenticated caller buys one bounded body read and nothing else.\n *\n * The `/rpc/ws` upgrade stays here. A socket is a resource the server holds\n * open, the RPC middleware can only refuse frames on a socket that already\n * exists, and a refused handshake has no RPC channel to answer a typed error\n * on, so its refusal is a transport fact either way.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const protectedPaths: ReadonlyArray<string> = [\n \"/projections\",\n \"/branch\",\n \"/sync\",\n \"/rpc/ws\",\n \"/projections/ws\",\n \"/sync/ws\",\n \"/runtime/v1/command\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * The mount a request target reaches, spelled the way {@link rpcPaths} and\n * {@link protectedPaths} spell it.\n *\n * The guard runs before the router and has to reach the router's verdict, or\n * an alias walks past the credential check and the body limit and is answered\n * by the mount anyway. Measured against `HttpRouter` on this tree, all of\n * `/%72pc`, `/rpc;transport-parameter`, `/rpc/`, `//rpc`, `///rpc`, `/rpc//`,\n * `/RPC`, `/./rpc`, and `/foo/../rpc` reach the `/rpc` handler, while\n * `/rpc%2f`, `/rpc%3Bp`, `/%2frpc`, and `/rpc%20` do not. This is not only\n * about crafted aliases: `@smthrs/control` `ControlClient`'s HTTP protocol\n * posts every call to `/rpc/`, so a literal comparison skipped the body limit\n * on the path the product's own client uses.\n *\n * Each segment is therefore taken without its `;` parameter, empty segments are\n * dropped, and what is left is decoded with `decodeURI` and compared without\n * regard to case. `decodeURI` is the decoder rather than `decodeURIComponent`\n * because it leaves a reserved character encoded, which is what keeps\n * `/rpc%2f` a different path here exactly as it is to the router. An invalid\n * escape is left as written, since it names no mount either way.\n *\n * Dot segments are resolved by `URL` before the split. The normalization errs\n * toward matching: a spelling this resolves to a mount that the router then\n * refuses is answered 401 rather than 404, which costs an unauthenticated\n * caller nothing it was entitled to.\n *\n * @param url the request target, origin-relative or absolute\n * @since 1.0.0\n * @category constructors\n */\nexport const routedPath = (url: string): string => {\n // An empty leading segment is a protocol-relative URL to `URL`, so `//rpc`\n // would parse as the host `rpc` and lose the path entirely. Anchoring an\n // origin-relative target on the origin keeps every segment. An absolute\n // target that does not parse at all names no mount, so it is compared as\n // written and reaches the router as the unknown route it is.\n const target = url.startsWith(\"/\") ? `http://gateway.invalid${url}` : url\n const parsed = URL.parse(target, \"http://gateway.invalid\")\n if (parsed === null) return url\n const segments: Array<string> = []\n for (const segment of parsed.pathname.split(\"/\")) {\n const parameter = segment.indexOf(\";\")\n const named = parameter < 0 ? segment : segment.slice(0, parameter)\n if (named === \"\") continue\n try {\n segments.push(decodeURI(named))\n } catch {\n segments.push(named)\n }\n }\n return `/${segments.join(\"/\")}`.toLowerCase()\n}\n\n/**\n * The mount that exchanges a bearer credential for a WebSocket {@link Ticket}.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPath = \"/auth/ticket\"\n\n/**\n * The protected socket mounts a {@link Ticket} opens.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPaths: ReadonlyArray<string> = [\"/rpc/ws\", \"/projections/ws\", \"/sync/ws\"]\n\n/**\n * How long an unredeemed {@link Ticket} stays valid (thirty seconds).\n *\n * @since 1.0.0\n * @category constants\n */\nexport const defaultTicketMillis = 30_000\n\n/** Outstanding tickets kept at once; the oldest is dropped past this. */\nconst maxOutstandingTickets = 1024\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * A browser `WebSocket` cannot set an `Authorization` header, so a browser\n * client first posts its bearer to `/auth/ticket`, then opens a protected\n * socket as `<mount>?ticket=<ticket>` within `expiresInMs`. The first upgrade\n * that presents the ticket consumes it, whether or not the upgrade completes;\n * a replay or a late one is refused 401. The socket then runs under the\n * bearer the ticket was issued for, so every frame authenticates as it would\n * with the header.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Ticket = Schema.Struct({\n ticket: Schema.String,\n expiresInMs: Schema.Number\n})\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Ticket = typeof Ticket.Type\n\nconst encodeTicket = Schema.encodeUnknownSync(Ticket)\n\n/** 256 random bits, hex-encoded so a query string carries them unescaped. */\nconst newTicket = (): string =>\n Array.from(crypto.getRandomValues(new Uint8Array(32)), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")\n\n/** The `ticket` query parameter of a request target, when it has one. */\nconst presentedTicket = (url: string): string | undefined => {\n const parsed = URL.parse(url, \"http://gateway.invalid\")\n const ticket = parsed?.searchParams.get(\"ticket\")\n return ticket === null || ticket === undefined || ticket === \"\" ? undefined : ticket\n}\n\n/**\n * Default maximum request body accepted by an RPC mount (one MiB).\n *\n * @category constants\n * @since 1.0.0\n */\nexport const defaultMaxRequestBodyBytes = 1024 * 1024\n\n/**\n * Ingress policy enforced before an RPC transport parses a request.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface IngressOptions {\n /** Accepted Host header names, without ports. Defaults to loopback names. */\n readonly allowedHosts?: ReadonlyArray<string> | undefined\n readonly maxRequestBodyBytes?: number | undefined\n /** Confine requests to loopback Host values and browser origins. */\n readonly loopbackOnly?: boolean | undefined\n /**\n * Edge authentication for {@link protectedPaths}, told which mount the\n * request reaches. When set, `POST /auth/ticket` exchanges a credential it\n * accepts for a {@link Ticket}, and a redeemed ticket's credential is judged\n * again for the mount its upgrade reaches.\n */\n readonly authorize?: (\n headers: Readonly<Record<string, string>>,\n path: string\n ) => Effect.Effect<boolean>\n /** How long an unredeemed ticket stays valid. Default {@link defaultTicketMillis}. */\n readonly ticketMillis?: number | undefined\n}\n\nconst encodeGatewayError = Schema.encodeUnknownSync(GatewayError)\n\n/** Host parsing stays strict so URL normalization cannot admit a rebinding name. */\nconst requestAuthority = (host: string | undefined): URL | undefined => {\n if (host === undefined || !/^(?:[a-zA-Z0-9.-]+|\\[[a-fA-F0-9:]+\\])(?::[0-9]+)?$/.test(host)) return undefined\n const parsed = URL.parse(`http://${host}`)\n return parsed === null ? undefined : parsed\n}\n\nconst browserRequestRefusal = (\n headers: Readonly<Record<string, string>>,\n allowedHosts: ReadonlyArray<string>,\n loopbackOnly: boolean\n): { readonly error: GatewayError; readonly status: number } | undefined => {\n const authority = requestAuthority(headers.host)\n if (\n authority === undefined || !allowedHosts.includes(authority.hostname) ||\n (loopbackOnly && !loopbackHost.test(headers.host!))\n ) return { error: invalidHostRequest(), status: 421 }\n const origin = headers.origin\n // CLI clients do not send Origin. Browsers must prove they came from this\n // exact authority, including the port, on HTTP requests AND WS upgrades.\n if (origin === undefined) return undefined\n const parsed = URL.parse(origin)\n return parsed !== null && (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") &&\n parsed.origin === origin && parsed.host === authority.host\n ? undefined\n : { error: invalidOriginRequest(), status: 403 }\n}\n\n/** One refusal on the wire: the typed error, under the status it answers. */\nconst refuse = (error: GatewayError, status: number) =>\n HttpServerResponse.jsonUnsafe(encodeGatewayError(error), { status })\n\nconst malformedRequest = (path: string) =>\n new GatewayError({ code: \"malformed_request\", message: `POST ${path} carries no RPC request message` })\n\nconst unauthorizedRequest = () =>\n new GatewayError({ code: \"unauthorized\", message: \"A valid bearer credential is required\" })\n\nconst invalidHostRequest = () =>\n new GatewayError({ code: \"invalid_host\", message: \"This gateway does not allow the supplied Host value\" })\n\nconst invalidOriginRequest = () =>\n new GatewayError({ code: \"invalid_origin\", message: \"The browser Origin must match an allowed gateway Host\" })\n\nconst requestTooLarge = (path: string, maxBytes: number) =>\n new GatewayError({\n code: \"request_too_large\",\n message: `POST ${path} exceeds the ${maxBytes}-byte request limit`\n })\n\n/** The message `@effect/platform-node-shared` `NodeStream` gives a size overflow. */\nconst maxBytesExceeded = \"maxBytes exceeded\"\n\n/**\n * The names that mean this machine only, as a bind address is spelled.\n *\n * This is a list of spellings a person types, not a subnet test: `127.0.0.2`\n * is loopback to the kernel and is not on it. The Node host's\n * `isLoopbackHost`, the Host-header names below, and the ingress regex all\n * derive from this one list so widening it is one edit.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostNames: ReadonlyArray<string> = [\"127.0.0.1\", \"localhost\", \"::1\"]\n\n/**\n * {@link loopbackHostNames} as a Host header spells them: an IPv6 address is\n * bracketed as a URL authority. This is the default `allowedHosts`.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostHeaderNames: ReadonlyArray<string> = loopbackHostNames.map((name) =>\n name.includes(\":\") ? `[${name}]` : name\n)\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\nconst loopbackHost = new RegExp(`^(?:${loopbackHostHeaderNames.map(escapeRegExp).join(\"|\")})(?::\\\\d{1,5})?$`, \"i\")\n\n/**\n * Whether a failed request-body read hit the configured size limit rather than\n * failing for another reason.\n *\n * `@effect/platform-node` raises the limit as an `HttpServerError` whose\n * `reason` is a `RequestParseError` carrying the `Error(\"maxBytes exceeded\")`\n * that `@effect/platform-node-shared` `NodeStream.toString` throws\n * (`NodeHttpIncomingMessage.text` passes `MaxBodySize` in as `maxBytes`, and\n * `NodeHttpServer`'s `ServerRequestImpl` wraps whatever it throws). Nothing on\n * the wire distinguishes it otherwise, so the check is that exact shape and a\n * version bump of those packages is the thing to re-read it against.\n *\n * Every other read failure, a transport reset among them, is not a size\n * overflow. Answering 413 for all of them told a client to shrink a request\n * that was never too big.\n *\n * @param error the failure a body read produced\n * @since 1.0.0\n * @category predicates\n */\nexport const exceededBodyLimit = (error: unknown): boolean => {\n const reason = typeof error === \"object\" && error !== null\n ? (error as { readonly reason?: unknown }).reason\n : undefined\n const cause = typeof reason === \"object\" && reason !== null\n ? (reason as { readonly cause?: unknown }).cause\n : undefined\n return cause instanceof Error && cause.message === maxBytesExceeded\n}\n\n/**\n * The refusal a failed request-body read earns, and the status it answers\n * under.\n *\n * A body over the limit is 413 `request_too_large`. Every other read failure,\n * a truncated body or a reset transport among them, is 400\n * `malformed_request`: the request cannot be retried smaller, because its size\n * was never the problem.\n *\n * @param path the mount the request was made to\n * @param maxBytes the configured limit\n * @param error the failure the body read produced\n * @since 1.0.0\n * @category constructors\n */\nexport const bodyRefusal = (\n path: string,\n maxBytes: number,\n error: unknown\n): { readonly error: GatewayError; readonly status: number } =>\n exceededBodyLimit(error)\n ? { error: requestTooLarge(path, maxBytes), status: 413 }\n : {\n error: new GatewayError({\n code: \"malformed_request\",\n message: `POST ${path} carries a body the server could not read`\n }),\n status: 400\n }\n\n/**\n * Whether a request body carries at least one RPC message the server can act\n * on.\n *\n * The transport's own parser answers, not a second reading of the wire\n * format: whatever `RpcSerialization` the host composed decodes the body, and\n * the answer is no only when that decode throws or yields no tagged message.\n * A body naming a procedure that does not exist is a yes — that is an\n * RPC-level defect the protocol itself reports.\n *\n * A binary framing is always a yes. Its body is not text, so there is nothing\n * to read here, and the mount owns it.\n *\n * @since 1.0.0\n * @category predicates\n */\nexport const carriesRpcRequest = (\n serialization: RpcSerialization.RpcSerialization[\"Service\"],\n body: string\n): boolean => {\n if (!serialization.contentType.includes(\"json\")) return true\n try {\n const decoded = serialization.makeUnsafe().decode(body)\n return decoded.length > 0 && decoded.every((message: unknown) =>\n typeof message === \"object\" && message !== null &&\n typeof (message as { readonly _tag?: unknown })._tag === \"string\"\n )\n } catch {\n return false\n }\n}\n\n/**\n * Enforces local Host/Origin policy and edge authentication before reading a\n * body or upgrading a socket, then validates RPC body size and framing.\n *\n * `effect/rpc` hands every decoded message to the server loop, and a\n * body that decodes to something else — `{}`, `[]`, prose, nothing at all —\n * reached it as a message with no tag and died there, so the gateway answered\n * `500 Internal Server Error` with an empty body (release validation). That is the\n * wrong half of the contract twice over: it tells an operator the gateway\n * broke, and it tells a client to retry a request that can never succeed.\n *\n * The body reader is cached per request, so reading the body here does not\n * consume the body the mount reads. It is read through the reader the mount\n * uses, `text` under a JSON serialization and `arrayBuffer` under a binary\n * one, so the bytes the mount decodes are the bytes the client sent.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerIngress = (options: IngressOptions = {}) => {\n const refusal = settingRefusal(\"The gateway request body limit\", options.maxRequestBodyBytes) ??\n settingRefusal(\"The gateway ticket lifetime\", options.ticketMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n const maxBytes = options.maxRequestBodyBytes ?? defaultMaxRequestBodyBytes\n const allowedHosts = options.allowedHosts ?? loopbackHostHeaderNames\n const ticketMillis = options.ticketMillis ?? defaultTicketMillis\n return HttpRouter.middleware(\n Effect.gen(function*() {\n const serialization = yield* RpcSerialization.RpcSerialization\n const binary = !serialization.contentType.includes(\"json\")\n /** Outstanding tickets: the Authorization header each was issued under, and when it lapses. */\n const tickets = new Map<string, { readonly authorization: string; readonly expiresAt: number }>()\n const issue = (authorization: string, now: number): Ticket => {\n for (const [ticket, entry] of tickets) {\n if (entry.expiresAt <= now) tickets.delete(ticket)\n }\n if (tickets.size >= maxOutstandingTickets) tickets.delete(tickets.keys().next().value!)\n const ticket = newTicket()\n tickets.set(ticket, { authorization, expiresAt: now + ticketMillis })\n return { ticket, expiresInMs: ticketMillis }\n }\n // Lookup and delete run without yielding, so of two upgrades presenting\n // one ticket exactly one gets its header.\n const redeem = (url: string, now: number): string | undefined => {\n const ticket = presentedTicket(url)\n const entry = ticket === undefined ? undefined : tickets.get(ticket)\n if (entry === undefined) return undefined\n tickets.delete(ticket!)\n return entry.expiresAt > now ? entry.authorization : undefined\n }\n return (httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, Types.unhandled>) =>\n Effect.gen(function*() {\n const request = yield* HttpServerRequest.HttpServerRequest\n const browserRefusal = browserRequestRefusal(request.headers, allowedHosts, options.loopbackOnly === true)\n if (browserRefusal !== undefined) return refuse(browserRefusal.error, browserRefusal.status)\n const path = routedPath(request.url)\n if (path === ticketPath && request.method === \"POST\" && options.authorize !== undefined) {\n const authorization = request.headers.authorization\n if (authorization === undefined || !(yield* options.authorize(request.headers, path))) {\n return refuse(unauthorizedRequest(), 401)\n }\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n return HttpServerResponse.jsonUnsafe(encodeTicket(issue(authorization, now)), {\n headers: { \"cache-control\": \"no-store\" }\n })\n }\n if (protectedPaths.includes(path) && options.authorize !== undefined) {\n const authorized = yield* options.authorize(request.headers, path)\n if (!authorized) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n const authorization = ticketPaths.includes(path) ? redeem(request.url, now) : undefined\n if (authorization === undefined) return refuse(unauthorizedRequest(), 401)\n // A ticket stands for the credential it was issued under, which\n // may not reach every mount a ticket can open.\n const headers = Headers.set(request.headers, \"authorization\", authorization)\n if (!(yield* options.authorize(headers, path))) return refuse(unauthorizedRequest(), 401)\n // The socket protocol stamps the upgrade's headers on every\n // frame, so the redeemed bearer authenticates each one.\n return yield* Effect.provideService(\n httpEffect,\n HttpServerRequest.HttpServerRequest,\n request.modify({ headers })\n )\n }\n }\n if (request.method !== \"POST\" || !boundedPostPaths.includes(path)) return yield* httpEffect\n // A declared length is a hint that saves reading a body already\n // known to be too big. It is never trusted the other way: a body\n // that declares less than it sends is still measured by the read,\n // and a chunked body declares nothing at all.\n const declaredLength = Number(request.headers[\"content-length\"])\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n return refuse(requestTooLarge(path, maxBytes), 413)\n }\n // The body is read through the reader the mount will use. The Node\n // adapter caches whichever reader ran first and derives the other\n // from it, so a text read here would hand a binary mount a UTF-8\n // re-encoding of its MessagePack bytes, with every invalid sequence\n // replaced. A binary body is bounded here and framed by the mount.\n const read = yield* (binary ? Effect.as(request.arrayBuffer, undefined) : request.text).pipe(\n Effect.provideService(HttpServerRequest.MaxBodySize, ByteSize.bytes(maxBytes)),\n Effect.match({\n onFailure: (error): { readonly body: string | undefined; readonly error: unknown } => ({\n body: undefined,\n error\n }),\n onSuccess: (body) => ({ body, error: undefined })\n })\n )\n if (read.error !== undefined) {\n const answer = bodyRefusal(path, maxBytes, read.error)\n return refuse(answer.error, answer.status)\n }\n if (read.body === undefined) return yield* httpEffect\n return !rpcPaths.includes(path) || carriesRpcRequest(serialization, read.body)\n ? yield* httpEffect\n : refuse(malformedRequest(path), 400)\n })\n }),\n { global: true }\n )\n}\n\n/**\n * How an assembled gateway is configured.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface LayerOptions {\n /**\n * How often an idle followed `Watch` on `/rpc/ws` and an idle followed\n * `Projection.Subscribe` on `/projections/ws` emit a keepalive. Unset, the\n * `Watch` keepalive runs at `Projections.heartbeatIntervalMillis` and the\n * supplied `Projections` service keeps the cadence it was built with.\n */\n readonly heartbeatMillis?: number | undefined\n /** The ingress policy the RPC mounts run behind. */\n readonly ingress?: IngressOptions | undefined\n /** Enables the authenticated Go-to-Control runtime bridge. */\n readonly runtimeBridge?: RuntimeBridge.Config | undefined\n}\n\n/**\n * The whole gateway surface, as one application layer a host serves.\n *\n * The caller supplies the transport serialization, the authentication\n * middleware for both RPC groups, and the services the mounts read through\n * (`Control`, `Projections`, `SyncServer`). `@smthrs/gateway/node/NodeGateway`\n * is the Node host that binds this to a socket.\n *\n * A keepalive cadence or a body limit that is not a positive safe integer is\n * refused here, before anything binds, through the layer's typed\n * `GatewayError` channel.\n *\n * @param health the identity `GET /health` answers with\n * @param options the keepalive cadence and the ingress policy\n * @since 1.0.0\n * @category layers\n */\nexport const layer = (health: Health, options: LayerOptions = {}) => {\n const refusal = settingRefusal(\"The gateway keepalive cadence\", options.heartbeatMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n return Layer.mergeAll(\n layerControlHttp(options.heartbeatMillis),\n options.heartbeatMillis === undefined\n ? layerProjectionsHttp\n : layerProjectionsHttp.pipe(Layer.provide(layerProjectionsKeepAlive(options.heartbeatMillis))),\n layerSyncHttp,\n layerHealth(health),\n ...(options.runtimeBridge === undefined\n ? []\n : [RuntimeBridge.layer(options.runtimeBridge), HostBranchPresence.layer(options.runtimeBridge)]),\n layerIngress(options.ingress)\n )\n}\n"]}
1
+ {"version":3,"file":"GatewayServer.js","sourceRoot":"","sources":["../../src/GatewayServer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The assembled workspace gateway: one HTTP surface carrying the control\n * plane, the sync read path, the served projections, and a health probe.\n *\n * Mounts:\n *\n * | Path | Protocol | Serves |\n * | --- | --- | --- |\n * | `POST /rpc` | RPC over HTTP | `@smthrs/control` `ControlRpcs` |\n * | `/rpc/ws` | RPC over WebSocket | `ControlRpcs`, including a kept-alive `Watch` |\n * | `POST /projections` | RPC over HTTP | `GatewayRpcs` |\n * | `/projections/ws` | RPC over WebSocket | `GatewayRpcs`, including `Projection.Subscribe` |\n * | `POST /sync` | RPC over HTTP | `@smthrs/sync` `SyncRpcs` |\n * | `/sync/ws` | RPC over WebSocket | `SyncRpcs` |\n * | `GET /health` | JSON | `GatewaySchema.GatewayHealth` plus the package version |\n * | `POST /auth/ticket` | JSON | A single-use {@link Ticket} that opens one protected socket |\n *\n * `/health` is deliberately unauthenticated: a supervisor decides whether to\n * keep or replace a gateway process by asking which workspace it belongs to,\n * and a probe that needed a credential could not answer that question about a\n * gateway it did not start. The response carries identity only: the\n * workspace hash, the gateway id, the protocol version, and the package\n * version. It never carries a token, a run, or a path.\n *\n * @since 1.0.0\n */\n\nimport { Control } from \"@smthrs/control/Control\"\nimport * as ControlError from \"@smthrs/control/ControlError\"\nimport * as ControlExecutor from \"@smthrs/control/ControlExecutor\"\nimport { ControlPrincipal } from \"@smthrs/control/ControlRpcs\"\nimport type * as ControlSchema from \"@smthrs/control/ControlSchema\"\nimport * as ControlServer from \"@smthrs/control/ControlServer\"\nimport { SyncRpcs } from \"@smthrs/sync/SyncRpcs\"\nimport * as SyncServer from \"@smthrs/sync/SyncServer\"\nimport { Effect, Layer, Option, Schema, Stream, type Types } from \"effect\"\nimport * as ByteSize from \"effect/ByteSize\"\nimport { Headers, HttpRouter, HttpServerRequest, HttpServerResponse } from \"effect/http\"\nimport { RpcSerialization, RpcServer } from \"effect/rpc\"\nimport { GatewayError, settingRefusal } from \"./GatewayError.ts\"\nimport { GatewayRpcs } from \"./GatewayRpcs.ts\"\nimport * as GatewaySchema from \"./GatewaySchema.ts\"\nimport * as HostBranchPresence from \"./HostBranchPresence.ts\"\nimport { heartbeatIntervalMillis, Projections } from \"./Projections.ts\"\nimport * as RunHistory from \"./RunHistory.ts\"\nimport * as RuntimeBridge from \"./RuntimeBridge.ts\"\n\n/**\n * What `GET /health` answers: the `GatewaySchema.GatewayHealth` identity plus\n * the version of the package serving it.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Health = Schema.Struct({\n ...GatewaySchema.GatewayHealth.fields,\n version: Schema.String,\n runtimeBridge: Schema.optional(RuntimeBridge.Identity)\n})\n\n/**\n * What `GET /health` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Health = typeof Health.Type\n\nconst encodeHealth = Schema.encodeUnknownSync(Health)\n\n/**\n * Mounts the unauthenticated health probe.\n *\n * @param health the identity this process serves under\n * @since 1.0.0\n * @category layers\n */\nexport const layerHealth = (health: Health): Layer.Layer<never, never, HttpRouter.HttpRouter> =>\n HttpRouter.add(\"GET\", \"/health\", HttpServerResponse.jsonUnsafe(encodeHealth(health)))\n\n/**\n * The gateway's own RPC handlers over the read path and the control plane.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerHandlers = GatewayRpcs.toLayer(\n Effect.gen(function*() {\n const projections = yield* Projections\n const control = yield* Control\n // Only a host that owns the project's stores can fork or verify its runs.\n const history = yield* Effect.serviceOption(RunHistory.RunHistory)\n const historyHost = Option.match(history, {\n onNone: () => Effect.fail(new ControlError.Unavailable({ feature: \"run history\", ticket: \"smithers#3242\" })),\n onSome: Effect.succeed\n })\n // Forking writes a durable run and verifying drives the project's flow\n // code, so neither is an anonymous loopback read.\n const operator = Effect.gen(function*() {\n const principal = yield* ControlPrincipal\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n })\n return GatewayRpcs.of({\n \"Projection.Snapshot\": Effect.fn(\"Gateway.snapshot\")(\n ({ selector, after }) => projections.snapshot(selector, after),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Projection.Subscribe\": ({ after, selector }) =>\n Stream.tapCause(projections.subscribe(selector, after), ControlServer.logDefect),\n /* One operator command. Control owns the atomic decision plus durable\n * resume delegation; the gateway is only a transport adapter. */\n \"Approval.Submit\": Effect.fn(\"Gateway.submitApproval\")((input) =>\n Effect.gen(function*() {\n // The identity the shared `ControlAuth` middleware authenticated, as\n // `ControlServer` stamps it on `Approve` and `Deny`. This mount is\n // the same decision under a different payload, so it is answerable\n // to the same operator, and a decision journaled under the\n // composition's default operator names the wrong one.\n const principal = yield* ControlPrincipal\n // Answering a human wait uses signal rather than the approval policy.\n // Do not let that alternate mutation path bypass loopback authentication.\n if (principal.id === \"loopback\" && principal.kind === \"anonymous\") {\n return yield* new ControlError.Unauthorized({ message: \"An operator credential is required\" })\n }\n const payload = {\n target: input.target,\n scope: input.scope,\n idempotencyKey: input.idempotencyKey,\n principal\n }\n // A gate that asked a question is answered, not granted, and the\n // PAYLOAD says which kind it is: a human wait carries its durable\n // wait token as the target's digest, and nothing else does. Reading\n // it here rather than trusting the client to have sent `answer` is\n // the difference between a clear refusal and the one an operator\n // actually saw — a client that forgot the answer fell through to\n // `Control.approve`, which looked for a registered approval token,\n // found none, and reported `/control/RunNotFound` for a run that was\n // listed, rendered, and waiting (workspace 4bb93306, run-1).\n //\n // `target.runId` is the run the person opened and `wait.name` the\n // wait point's own name; `Control.signal` routes the pair into the\n // run tree, to whichever execution is holding it.\n const wait = ControlExecutor.answerableWait(input.target)\n if (wait !== undefined && input.target._tag === \"Node\") {\n // Neither half of a grant answers a question. A denial has nowhere\n // to go — the run is parked on a value, and refusing to supply one\n // leaves it parked until its own attempt budget or deadline ends\n // it — and an approval with no value supplies none.\n if (input.decision === \"deny\") {\n return yield* new ControlError.InvalidInput({\n issue: `decision: \"${wait.name}\" asks a question, which is answered rather than denied`\n })\n }\n if (input.answer === undefined) {\n return yield* new ControlError.InvalidInput({\n issue: `answer: \"${wait.name}\" asks a question, so a decision alone does not answer it`\n })\n }\n const decision = yield* control.signal({\n runId: input.target.runId,\n signal: { name: wait.name, payload: input.answer },\n idempotencyKey: input.idempotencyKey,\n principal\n })\n return { decision }\n }\n const decision = input.decision === \"approve\"\n ? yield* control.approve(payload)\n : yield* control.deny(payload)\n return { decision }\n }), Effect.tapCause(ControlServer.logDefect)),\n \"Run.Fork\": Effect.fn(\"Gateway.fork\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.fork(input))),\n Effect.tapCause(ControlServer.logDefect)\n ),\n \"Run.Verify\": Effect.fn(\"Gateway.verify\")(\n (input) => Effect.andThen(operator, Effect.flatMap(historyHost, (host) => host.verify(input))),\n Effect.tapCause(ControlServer.logDefect)\n )\n })\n })\n)\n\n/**\n * The kind a `Watch` keepalive carries.\n *\n * `ControlRpcs.Watch` answers `ControlSchema.ControlEvent` and has no frame of\n * its own for a keepalive, so the gateway publishes one as an event whose kind\n * no emitter uses. A fold that does not know the kind ignores it, which is\n * what every fold in this package and in `@smthrs/cli` `Forensics` already\n * does with an unknown kind.\n *\n * @since 1.0.0\n * @category models\n */\nexport const watchHeartbeatKind = \"control.gateway.heartbeat\"\n\n/**\n * Merges a keepalive into a followed `Watch` stream.\n *\n * A followed stream is silent while a run is thinking, and a relay cuts an\n * idle tunnel at 600 s. The Effect RPC client\n * sends its own `Ping` every 5 s, but a non-Effect consumer behind a relay\n * sends nothing, so the keepalive has to come from the server.\n *\n * The keepalive repeats the last delivered sequence and run-scoped cursor,\n * including an expansion offset, so it cannot advance or rewind a checkpoint.\n * It carries the watched run id so a client routing by run keeps routing.\n * A snapshot read (`follow: false`) is left alone: it has to end.\n */\nconst keptAlive = (\n millis: number,\n filter: ControlSchema.WatchFilter,\n events: Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError>\n): Stream.Stream<ControlSchema.ControlEvent, ControlError.ControlError> => {\n if (filter.follow === false) return events\n let cursor = filter.afterCursor ??\n (filter.afterSequence === undefined ? undefined : { sequence: filter.afterSequence })\n let sequence = cursor?.sequence ?? 0\n const tracked = Stream.map(events, (event) => {\n sequence = event.sequence\n cursor = event.cursor ?? { sequence }\n return event\n })\n const beats = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (occurredAt): ControlSchema.ControlEvent => ({\n sequence,\n ...(filter.runId === undefined || cursor === undefined ? {} : { cursor }),\n kind: watchHeartbeatKind,\n ...(filter.runId === undefined ? {} : { runId: filter.runId }),\n occurredAt,\n payload: null\n })\n )\n )\n )\n return Stream.merge(tracked, beats, { haltStrategy: \"left\" })\n}\n\n/**\n * The control service the `/rpc` mounts read through: the ambient one, with\n * the keepalive merged into `watch`.\n *\n * Wrapping the service rather than re-declaring the handlers keeps\n * `@smthrs/control` `ControlServer` the single definition of what every\n * procedure does, including the principal it stamps on every mutation that\n * records who asked. Only `watch` behaves differently, and only in what it\n * adds.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerKeepAlive = (\n millis: number = heartbeatIntervalMillis\n): Layer.Layer<Control, never, Control> =>\n Layer.effect(Control)(\n Effect.map(Control, (control) =>\n Control.of({ ...control, watch: (filter) => keptAlive(millis, filter, control.watch(filter)) }))\n )\n\n/**\n * The read path the `/projections` mounts serve through: the ambient one, with\n * a followed subscription's keepalive re-timed to the bind's cadence.\n *\n * `Projections.make` already merges a `heartbeat` frame at the cadence it was\n * built with. A host that supplies `Projections.layer` and sets the cadence at\n * the bind expects one option to govern both keepalives, so this replaces the\n * service's beats with beats at the bind's cadence rather than adding a second\n * channel. Snapshot reads are left alone: they have to end.\n *\n * @param millis how often an idle followed subscription emits a keepalive\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsKeepAlive = (millis: number): Layer.Layer<Projections, never, Projections> =>\n Layer.effect(Projections)(\n Effect.map(Projections, (projections) =>\n Projections.of({\n ...projections,\n subscribe: (selector, after) => keptAliveSubscription(millis, projections.subscribe(selector, after))\n }))\n )\n\n/** Replaces a subscription's keepalive channel with one at the given cadence. */\nconst keptAliveSubscription = <F extends GatewaySchema.GatewayFrame>(\n millis: number,\n frames: Stream.Stream<F, GatewayError>\n): Stream.Stream<F | GatewaySchema.HeartbeatFrame, GatewayError> => {\n // The first tick is dropped for the same reason `Projections` drops it: an\n // immediate keepalive would arrive before the snapshot it keeps alive.\n const beats: Stream.Stream<GatewaySchema.HeartbeatFrame> = Stream.tick(millis).pipe(\n Stream.drop(1),\n Stream.mapEffect(() =>\n Effect.map(\n Effect.clockWith((clock) => clock.currentTimeMillis),\n (atMs): GatewaySchema.HeartbeatFrame => ({ _tag: \"heartbeat\", atMs })\n )\n )\n )\n return Stream.merge(\n Stream.filter(frames, (frame) => frame._tag !== \"heartbeat\"),\n beats,\n { haltStrategy: \"left\" }\n )\n}\n\n/**\n * Mounts the control plane on `POST /rpc` and `/rpc/ws`, with the keepalive.\n *\n * @param millis how often an idle followed watch emits one, defaulting to\n * `Projections.heartbeatIntervalMillis`\n * @since 1.0.0\n * @category layers\n */\nexport const layerControlHttp = (millis?: number | undefined) =>\n ControlServer.layerHttp.pipe(Layer.provide(layerKeepAlive(millis)))\n\nconst gateway = RpcServer.layer(GatewayRpcs, { disableFatalDefects: true })\n\nconst sync = RpcServer.layer(SyncRpcs, { disableFatalDefects: true })\n\n/**\n * Mounts the gateway's projections on `POST /projections` and\n * `/projections/ws`.\n *\n * Both protocols are mounted together because a client reads a snapshot over\n * the request/response path and follows deltas over the socket, and pointing\n * those at two different servers would let them disagree.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerProjectionsHttp = Layer.mergeAll(\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/projections\" })),\n Layer.fresh\n ),\n gateway.pipe(\n Layer.provide(layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/projections/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * Mounts the sync read path on `POST /sync` and `/sync/ws`.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerSyncHttp = Layer.mergeAll(\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolHttp({ path: \"/sync\" })),\n Layer.fresh\n ),\n sync.pipe(\n Layer.provide(SyncServer.layerHandlers),\n Layer.provideMerge(RpcServer.layerProtocolWebsocket({ path: \"/sync/ws\" })),\n Layer.fresh\n )\n)\n\n/**\n * The POST mounts that carry RPC request messages and nothing else.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const rpcPaths: ReadonlyArray<string> = [\"/rpc\", \"/projections\", \"/sync\", \"/branch\"]\n\n/**\n * POST mounts whose request bodies are bounded before their handlers read.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const boundedPostPaths: ReadonlyArray<string> = [\n ...rpcPaths,\n \"/runtime/v1/command\",\n \"/runtime/v1/monitor\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * RPC paths whose upgrade or request must pass edge authentication.\n *\n * `POST /rpc` is deliberately not one of them. The control mount authenticates\n * in band through `@smthrs/control` `ControlRpcs.ControlAuth`, whose declared\n * error is `ControlError.Unauthorized`, and that typed control error is the\n * refusal the control plane publishes: \"Missing, malformed, empty, and\n * incorrect credentials all return the same typed `Unauthorized` control\n * error\" (`packages/smithers/gateway/docs/guides/serve-beyond-loopback.md`). An edge 401 answered\n * ahead of the mount, and `ControlClient` filters a non-2xx status, so every\n * refusal reached a caller as a `TransportError`, the one error class\n * `@smthrs/control` reserves for a request that failed *before* a declared\n * control response reached it. It also made the gateway refuse a call\n * differently from `NodeControl.layerServer` hosting the very same\n * `ControlRpcs`. The body limit below still applies to `/rpc`, so an\n * unauthenticated caller buys one bounded body read and nothing else.\n *\n * The `/rpc/ws` upgrade stays here. A socket is a resource the server holds\n * open, the RPC middleware can only refuse frames on a socket that already\n * exists, and a refused handshake has no RPC channel to answer a typed error\n * on, so its refusal is a transport fact either way.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const protectedPaths: ReadonlyArray<string> = [\n \"/projections\",\n \"/branch\",\n \"/sync\",\n \"/rpc/ws\",\n \"/projections/ws\",\n \"/sync/ws\",\n \"/runtime/v1/command\",\n \"/runtime/v1/monitor\",\n \"/runtime/v1/observe\"\n]\n\n/**\n * The mount a request target reaches, spelled the way {@link rpcPaths} and\n * {@link protectedPaths} spell it.\n *\n * The guard runs before the router and has to reach the router's verdict, or\n * an alias walks past the credential check and the body limit and is answered\n * by the mount anyway. Measured against `HttpRouter` on this tree, all of\n * `/%72pc`, `/rpc;transport-parameter`, `/rpc/`, `//rpc`, `///rpc`, `/rpc//`,\n * `/RPC`, `/./rpc`, and `/foo/../rpc` reach the `/rpc` handler, while\n * `/rpc%2f`, `/rpc%3Bp`, `/%2frpc`, and `/rpc%20` do not. This is not only\n * about crafted aliases: `@smthrs/control` `ControlClient`'s HTTP protocol\n * posts every call to `/rpc/`, so a literal comparison skipped the body limit\n * on the path the product's own client uses.\n *\n * Each segment is therefore taken without its `;` parameter, empty segments are\n * dropped, and what is left is decoded with `decodeURI` and compared without\n * regard to case. `decodeURI` is the decoder rather than `decodeURIComponent`\n * because it leaves a reserved character encoded, which is what keeps\n * `/rpc%2f` a different path here exactly as it is to the router. An invalid\n * escape is left as written, since it names no mount either way.\n *\n * Dot segments are resolved by `URL` before the split. The normalization errs\n * toward matching: a spelling this resolves to a mount that the router then\n * refuses is answered 401 rather than 404, which costs an unauthenticated\n * caller nothing it was entitled to.\n *\n * @param url the request target, origin-relative or absolute\n * @since 1.0.0\n * @category constructors\n */\nexport const routedPath = (url: string): string => {\n // An empty leading segment is a protocol-relative URL to `URL`, so `//rpc`\n // would parse as the host `rpc` and lose the path entirely. Anchoring an\n // origin-relative target on the origin keeps every segment. An absolute\n // target that does not parse at all names no mount, so it is compared as\n // written and reaches the router as the unknown route it is.\n const target = url.startsWith(\"/\") ? `http://gateway.invalid${url}` : url\n const parsed = URL.parse(target, \"http://gateway.invalid\")\n if (parsed === null) return url\n const segments: Array<string> = []\n for (const segment of parsed.pathname.split(\"/\")) {\n const parameter = segment.indexOf(\";\")\n const named = parameter < 0 ? segment : segment.slice(0, parameter)\n if (named === \"\") continue\n try {\n segments.push(decodeURI(named))\n } catch {\n segments.push(named)\n }\n }\n return `/${segments.join(\"/\")}`.toLowerCase()\n}\n\n/**\n * The mount that exchanges a bearer credential for a WebSocket {@link Ticket}.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPath = \"/auth/ticket\"\n\n/**\n * The protected socket mounts a {@link Ticket} opens.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const ticketPaths: ReadonlyArray<string> = [\"/rpc/ws\", \"/projections/ws\", \"/sync/ws\"]\n\n/**\n * How long an unredeemed {@link Ticket} stays valid (thirty seconds).\n *\n * @since 1.0.0\n * @category constants\n */\nexport const defaultTicketMillis = 30_000\n\n/** Outstanding tickets kept at once; the oldest is dropped past this. */\nconst maxOutstandingTickets = 1024\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * A browser `WebSocket` cannot set an `Authorization` header, so a browser\n * client first posts its bearer to `/auth/ticket`, then opens a protected\n * socket as `<mount>?ticket=<ticket>` within `expiresInMs`. The first upgrade\n * that presents the ticket consumes it, whether or not the upgrade completes;\n * a replay or a late one is refused 401. The socket then runs under the\n * bearer the ticket was issued for, so every frame authenticates as it would\n * with the header.\n *\n * @since 1.0.0\n * @category models\n */\nexport const Ticket = Schema.Struct({\n ticket: Schema.String,\n expiresInMs: Schema.Number\n})\n\n/**\n * What `POST /auth/ticket` answers.\n *\n * @since 1.0.0\n * @category models\n */\nexport type Ticket = typeof Ticket.Type\n\nconst encodeTicket = Schema.encodeUnknownSync(Ticket)\n\n/** 256 random bits, hex-encoded so a query string carries them unescaped. */\nconst newTicket = (): string =>\n Array.from(crypto.getRandomValues(new Uint8Array(32)), (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\")\n\n/** The `ticket` query parameter of a request target, when it has one. */\nconst presentedTicket = (url: string): string | undefined => {\n const parsed = URL.parse(url, \"http://gateway.invalid\")\n const ticket = parsed?.searchParams.get(\"ticket\")\n return ticket === null || ticket === undefined || ticket === \"\" ? undefined : ticket\n}\n\n/**\n * Default maximum request body accepted by an RPC mount (one MiB).\n *\n * @category constants\n * @since 1.0.0\n */\nexport const defaultMaxRequestBodyBytes = 1024 * 1024\n\n/**\n * Ingress policy enforced before an RPC transport parses a request.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface IngressOptions {\n /** Accepted Host header names, without ports. Defaults to loopback names. */\n readonly allowedHosts?: ReadonlyArray<string> | undefined\n readonly maxRequestBodyBytes?: number | undefined\n /** Confine requests to loopback Host values and browser origins. */\n readonly loopbackOnly?: boolean | undefined\n /**\n * Edge authentication for {@link protectedPaths}, told which mount the\n * request reaches. When set, `POST /auth/ticket` exchanges a credential it\n * accepts for a {@link Ticket}, and a redeemed ticket's credential is judged\n * again for the mount its upgrade reaches.\n */\n readonly authorize?: (\n headers: Readonly<Record<string, string>>,\n path: string\n ) => Effect.Effect<boolean>\n /** How long an unredeemed ticket stays valid. Default {@link defaultTicketMillis}. */\n readonly ticketMillis?: number | undefined\n}\n\nconst encodeGatewayError = Schema.encodeUnknownSync(GatewayError)\n\n/** Host parsing stays strict so URL normalization cannot admit a rebinding name. */\nconst requestAuthority = (host: string | undefined): URL | undefined => {\n if (host === undefined || !/^(?:[a-zA-Z0-9.-]+|\\[[a-fA-F0-9:]+\\])(?::[0-9]+)?$/.test(host)) return undefined\n const parsed = URL.parse(`http://${host}`)\n return parsed === null ? undefined : parsed\n}\n\nconst browserRequestRefusal = (\n headers: Readonly<Record<string, string>>,\n allowedHosts: ReadonlyArray<string>,\n loopbackOnly: boolean\n): { readonly error: GatewayError; readonly status: number } | undefined => {\n const authority = requestAuthority(headers.host)\n if (\n authority === undefined || !allowedHosts.includes(authority.hostname) ||\n (loopbackOnly && !loopbackHost.test(headers.host!))\n ) return { error: invalidHostRequest(), status: 421 }\n const origin = headers.origin\n // CLI clients do not send Origin. Browsers must prove they came from this\n // exact authority, including the port, on HTTP requests AND WS upgrades.\n if (origin === undefined) return undefined\n const parsed = URL.parse(origin)\n return parsed !== null && (parsed.protocol === \"http:\" || parsed.protocol === \"https:\") &&\n parsed.origin === origin && parsed.host === authority.host\n ? undefined\n : { error: invalidOriginRequest(), status: 403 }\n}\n\n/** One refusal on the wire: the typed error, under the status it answers. */\nconst refuse = (error: GatewayError, status: number) =>\n HttpServerResponse.jsonUnsafe(encodeGatewayError(error), { status })\n\nconst malformedRequest = (path: string) =>\n new GatewayError({ code: \"malformed_request\", message: `POST ${path} carries no RPC request message` })\n\nconst unauthorizedRequest = () =>\n new GatewayError({ code: \"unauthorized\", message: \"A valid bearer credential is required\" })\n\nconst invalidHostRequest = () =>\n new GatewayError({ code: \"invalid_host\", message: \"This gateway does not allow the supplied Host value\" })\n\nconst invalidOriginRequest = () =>\n new GatewayError({ code: \"invalid_origin\", message: \"The browser Origin must match an allowed gateway Host\" })\n\nconst requestTooLarge = (path: string, maxBytes: number) =>\n new GatewayError({\n code: \"request_too_large\",\n message: `POST ${path} exceeds the ${maxBytes}-byte request limit`\n })\n\n/** The message `@effect/platform-node-shared` `NodeStream` gives a size overflow. */\nconst maxBytesExceeded = \"maxBytes exceeded\"\n\n/**\n * The names that mean this machine only, as a bind address is spelled.\n *\n * This is a list of spellings a person types, not a subnet test: `127.0.0.2`\n * is loopback to the kernel and is not on it. The Node host's\n * `isLoopbackHost`, the Host-header names below, and the ingress regex all\n * derive from this one list so widening it is one edit.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostNames: ReadonlyArray<string> = [\"127.0.0.1\", \"localhost\", \"::1\"]\n\n/**\n * {@link loopbackHostNames} as a Host header spells them: an IPv6 address is\n * bracketed as a URL authority. This is the default `allowedHosts`.\n *\n * @since 1.0.0\n * @category constants\n */\nexport const loopbackHostHeaderNames: ReadonlyArray<string> = loopbackHostNames.map((name) =>\n name.includes(\":\") ? `[${name}]` : name\n)\n\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\nconst loopbackHost = new RegExp(`^(?:${loopbackHostHeaderNames.map(escapeRegExp).join(\"|\")})(?::\\\\d{1,5})?$`, \"i\")\n\n/**\n * Whether a failed request-body read hit the configured size limit rather than\n * failing for another reason.\n *\n * `@effect/platform-node` raises the limit as an `HttpServerError` whose\n * `reason` is a `RequestParseError` carrying the `Error(\"maxBytes exceeded\")`\n * that `@effect/platform-node-shared` `NodeStream.toString` throws\n * (`NodeHttpIncomingMessage.text` passes `MaxBodySize` in as `maxBytes`, and\n * `NodeHttpServer`'s `ServerRequestImpl` wraps whatever it throws). Nothing on\n * the wire distinguishes it otherwise, so the check is that exact shape and a\n * version bump of those packages is the thing to re-read it against.\n *\n * Every other read failure, a transport reset among them, is not a size\n * overflow. Answering 413 for all of them told a client to shrink a request\n * that was never too big.\n *\n * @param error the failure a body read produced\n * @since 1.0.0\n * @category predicates\n */\nexport const exceededBodyLimit = (error: unknown): boolean => {\n const reason = typeof error === \"object\" && error !== null\n ? (error as { readonly reason?: unknown }).reason\n : undefined\n const cause = typeof reason === \"object\" && reason !== null\n ? (reason as { readonly cause?: unknown }).cause\n : undefined\n return cause instanceof Error && cause.message === maxBytesExceeded\n}\n\n/**\n * The refusal a failed request-body read earns, and the status it answers\n * under.\n *\n * A body over the limit is 413 `request_too_large`. Every other read failure,\n * a truncated body or a reset transport among them, is 400\n * `malformed_request`: the request cannot be retried smaller, because its size\n * was never the problem.\n *\n * @param path the mount the request was made to\n * @param maxBytes the configured limit\n * @param error the failure the body read produced\n * @since 1.0.0\n * @category constructors\n */\nexport const bodyRefusal = (\n path: string,\n maxBytes: number,\n error: unknown\n): { readonly error: GatewayError; readonly status: number } =>\n exceededBodyLimit(error)\n ? { error: requestTooLarge(path, maxBytes), status: 413 }\n : {\n error: new GatewayError({\n code: \"malformed_request\",\n message: `POST ${path} carries a body the server could not read`\n }),\n status: 400\n }\n\n/**\n * Whether a request body carries at least one RPC message the server can act\n * on.\n *\n * The transport's own parser answers, not a second reading of the wire\n * format: whatever `RpcSerialization` the host composed decodes the body, and\n * the answer is no only when that decode throws or yields no tagged message.\n * A body naming a procedure that does not exist is a yes — that is an\n * RPC-level defect the protocol itself reports.\n *\n * A binary framing is always a yes. Its body is not text, so there is nothing\n * to read here, and the mount owns it.\n *\n * @since 1.0.0\n * @category predicates\n */\nexport const carriesRpcRequest = (\n serialization: RpcSerialization.RpcSerialization[\"Service\"],\n body: string\n): boolean => {\n if (!serialization.contentType.includes(\"json\")) return true\n try {\n const decoded = serialization.makeUnsafe().decode(body)\n return decoded.length > 0 && decoded.every((message: unknown) =>\n typeof message === \"object\" && message !== null &&\n typeof (message as { readonly _tag?: unknown })._tag === \"string\"\n )\n } catch {\n return false\n }\n}\n\n/**\n * Enforces local Host/Origin policy and edge authentication before reading a\n * body or upgrading a socket, then validates RPC body size and framing.\n *\n * `effect/rpc` hands every decoded message to the server loop, and a\n * body that decodes to something else — `{}`, `[]`, prose, nothing at all —\n * reached it as a message with no tag and died there, so the gateway answered\n * `500 Internal Server Error` with an empty body (release validation). That is the\n * wrong half of the contract twice over: it tells an operator the gateway\n * broke, and it tells a client to retry a request that can never succeed.\n *\n * The body reader is cached per request, so reading the body here does not\n * consume the body the mount reads. It is read through the reader the mount\n * uses, `text` under a JSON serialization and `arrayBuffer` under a binary\n * one, so the bytes the mount decodes are the bytes the client sent.\n *\n * @since 1.0.0\n * @category layers\n */\nexport const layerIngress = (options: IngressOptions = {}) => {\n const refusal = settingRefusal(\"The gateway request body limit\", options.maxRequestBodyBytes) ??\n settingRefusal(\"The gateway ticket lifetime\", options.ticketMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n const maxBytes = options.maxRequestBodyBytes ?? defaultMaxRequestBodyBytes\n const allowedHosts = options.allowedHosts ?? loopbackHostHeaderNames\n const ticketMillis = options.ticketMillis ?? defaultTicketMillis\n return HttpRouter.middleware(\n Effect.gen(function*() {\n const serialization = yield* RpcSerialization.RpcSerialization\n const binary = !serialization.contentType.includes(\"json\")\n /** Outstanding tickets: the Authorization header each was issued under, and when it lapses. */\n const tickets = new Map<string, { readonly authorization: string; readonly expiresAt: number }>()\n const issue = (authorization: string, now: number): Ticket => {\n for (const [ticket, entry] of tickets) {\n if (entry.expiresAt <= now) tickets.delete(ticket)\n }\n if (tickets.size >= maxOutstandingTickets) tickets.delete(tickets.keys().next().value!)\n const ticket = newTicket()\n tickets.set(ticket, { authorization, expiresAt: now + ticketMillis })\n return { ticket, expiresInMs: ticketMillis }\n }\n // Lookup and delete run without yielding, so of two upgrades presenting\n // one ticket exactly one gets its header.\n const redeem = (url: string, now: number): string | undefined => {\n const ticket = presentedTicket(url)\n const entry = ticket === undefined ? undefined : tickets.get(ticket)\n if (entry === undefined) return undefined\n tickets.delete(ticket!)\n return entry.expiresAt > now ? entry.authorization : undefined\n }\n return (httpEffect: Effect.Effect<HttpServerResponse.HttpServerResponse, Types.unhandled>) =>\n Effect.gen(function*() {\n const request = yield* HttpServerRequest.HttpServerRequest\n const browserRefusal = browserRequestRefusal(request.headers, allowedHosts, options.loopbackOnly === true)\n if (browserRefusal !== undefined) return refuse(browserRefusal.error, browserRefusal.status)\n const path = routedPath(request.url)\n if (path === ticketPath && request.method === \"POST\" && options.authorize !== undefined) {\n const authorization = request.headers.authorization\n if (authorization === undefined || !(yield* options.authorize(request.headers, path))) {\n return refuse(unauthorizedRequest(), 401)\n }\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n return HttpServerResponse.jsonUnsafe(encodeTicket(issue(authorization, now)), {\n headers: { \"cache-control\": \"no-store\" }\n })\n }\n if (protectedPaths.includes(path) && options.authorize !== undefined) {\n const authorized = yield* options.authorize(request.headers, path)\n if (!authorized) {\n const now = yield* Effect.clockWith((clock) => clock.currentTimeMillis)\n const authorization = ticketPaths.includes(path) ? redeem(request.url, now) : undefined\n if (authorization === undefined) return refuse(unauthorizedRequest(), 401)\n // A ticket stands for the credential it was issued under, which\n // may not reach every mount a ticket can open.\n const headers = Headers.set(request.headers, \"authorization\", authorization)\n if (!(yield* options.authorize(headers, path))) return refuse(unauthorizedRequest(), 401)\n // The socket protocol stamps the upgrade's headers on every\n // frame, so the redeemed bearer authenticates each one.\n return yield* Effect.provideService(\n httpEffect,\n HttpServerRequest.HttpServerRequest,\n request.modify({ headers })\n )\n }\n }\n if (request.method !== \"POST\" || !boundedPostPaths.includes(path)) return yield* httpEffect\n // A declared length is a hint that saves reading a body already\n // known to be too big. It is never trusted the other way: a body\n // that declares less than it sends is still measured by the read,\n // and a chunked body declares nothing at all.\n const declaredLength = Number(request.headers[\"content-length\"])\n if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n return refuse(requestTooLarge(path, maxBytes), 413)\n }\n // The body is read through the reader the mount will use. The Node\n // adapter caches whichever reader ran first and derives the other\n // from it, so a text read here would hand a binary mount a UTF-8\n // re-encoding of its MessagePack bytes, with every invalid sequence\n // replaced. A binary body is bounded here and framed by the mount.\n const read = yield* (binary ? Effect.as(request.arrayBuffer, undefined) : request.text).pipe(\n Effect.provideService(HttpServerRequest.MaxBodySize, ByteSize.bytes(maxBytes)),\n Effect.match({\n onFailure: (error): { readonly body: string | undefined; readonly error: unknown } => ({\n body: undefined,\n error\n }),\n onSuccess: (body) => ({ body, error: undefined })\n })\n )\n if (read.error !== undefined) {\n const answer = bodyRefusal(path, maxBytes, read.error)\n return refuse(answer.error, answer.status)\n }\n if (read.body === undefined) return yield* httpEffect\n return !rpcPaths.includes(path) || carriesRpcRequest(serialization, read.body)\n ? yield* httpEffect\n : refuse(malformedRequest(path), 400)\n })\n }),\n { global: true }\n )\n}\n\n/**\n * How an assembled gateway is configured.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface LayerOptions {\n /**\n * How often an idle followed `Watch` on `/rpc/ws` and an idle followed\n * `Projection.Subscribe` on `/projections/ws` emit a keepalive. Unset, the\n * `Watch` keepalive runs at `Projections.heartbeatIntervalMillis` and the\n * supplied `Projections` service keeps the cadence it was built with.\n */\n readonly heartbeatMillis?: number | undefined\n /** The ingress policy the RPC mounts run behind. */\n readonly ingress?: IngressOptions | undefined\n /** Enables the authenticated Go-to-Control runtime bridge. */\n readonly runtimeBridge?: RuntimeBridge.Config | undefined\n}\n\n/**\n * The whole gateway surface, as one application layer a host serves.\n *\n * The caller supplies the transport serialization, the authentication\n * middleware for both RPC groups, and the services the mounts read through\n * (`Control`, `Projections`, `SyncServer`). `@smthrs/gateway/node/NodeGateway`\n * is the Node host that binds this to a socket.\n *\n * A keepalive cadence or a body limit that is not a positive safe integer is\n * refused here, before anything binds, through the layer's typed\n * `GatewayError` channel.\n *\n * @param health the identity `GET /health` answers with\n * @param options the keepalive cadence and the ingress policy\n * @since 1.0.0\n * @category layers\n */\nexport const layer = (health: Health, options: LayerOptions = {}) => {\n const refusal = settingRefusal(\"The gateway keepalive cadence\", options.heartbeatMillis)\n if (refusal !== undefined) return Layer.effectDiscard(Effect.fail(refusal))\n return Layer.mergeAll(\n layerControlHttp(options.heartbeatMillis),\n options.heartbeatMillis === undefined\n ? layerProjectionsHttp\n : layerProjectionsHttp.pipe(Layer.provide(layerProjectionsKeepAlive(options.heartbeatMillis))),\n layerSyncHttp,\n layerHealth(health),\n ...(options.runtimeBridge === undefined\n ? []\n : [RuntimeBridge.layer(options.runtimeBridge), HostBranchPresence.layer(options.runtimeBridge)]),\n layerIngress(options.ingress)\n )\n}\n"]}
@@ -361,8 +361,8 @@ export declare const execute: (config: Config, control: Control["Service"], prin
361
361
  readonly capabilities: readonly string[];
362
362
  readonly flows: readonly string[];
363
363
  readonly budget: {
364
- readonly tokens?: number | undefined;
365
364
  readonly deadline?: number | undefined;
365
+ readonly tokens?: number | undefined;
366
366
  readonly milliseconds?: number | undefined;
367
367
  readonly usd?: number | undefined;
368
368
  readonly onExceeded?: "warn" | "fail" | "park" | "skip-remaining" | undefined;
@@ -379,8 +379,8 @@ export declare const execute: (config: Config, control: Control["Service"], prin
379
379
  readonly capabilities: readonly string[];
380
380
  readonly flows: readonly string[];
381
381
  readonly budget: {
382
- readonly tokens?: number | undefined;
383
382
  readonly deadline?: number | undefined;
383
+ readonly tokens?: number | undefined;
384
384
  readonly milliseconds?: number | undefined;
385
385
  readonly usd?: number | undefined;
386
386
  readonly onExceeded?: "warn" | "fail" | "park" | "skip-remaining" | undefined;
@@ -397,8 +397,8 @@ export declare const execute: (config: Config, control: Control["Service"], prin
397
397
  readonly capabilities: readonly string[];
398
398
  readonly flows: readonly string[];
399
399
  readonly budget: {
400
- readonly tokens?: number | undefined;
401
400
  readonly deadline?: number | undefined;
401
+ readonly tokens?: number | undefined;
402
402
  readonly milliseconds?: number | undefined;
403
403
  readonly usd?: number | undefined;
404
404
  readonly onExceeded?: "warn" | "fail" | "park" | "skip-remaining" | undefined;
package/docs/README.md CHANGED
@@ -56,7 +56,9 @@ embedding the surface in a process of your own.
56
56
 
57
57
  ## Install
58
58
 
59
- Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/flows/flow/docs/installation.md#use-the-libraries).
59
+ ```bash
60
+ npm install @smthrs/gateway@next effect
61
+ ```
60
62
 
61
63
  The package needs Node.js 26.4.0 or later. For the peers and the
62
64
  services a running composition supplies, see [Installation](./installation.md).
package/docs/api.md CHANGED
@@ -117,8 +117,8 @@ The current monitor omits the raw journal, which its reader pages from the
117
117
  | `ticketPath` | `"/auth/ticket"` | Exchanges an accepted bearer for a `Ticket`. Served only when `authorize` is set. |
118
118
  | `ticketPaths` | `ReadonlyArray<string>` | `["/rpc/ws", "/projections/ws", "/sync/ws"]`: the sockets a ticket opens as `<mount>?ticket=<ticket>`. |
119
119
  | `defaultTicketMillis` | `number` | 30,000. How long an unredeemed ticket stays valid. |
120
- | `rpcPaths` | `ReadonlyArray<string>` | `["/rpc", "/projections", "/sync"]`: the `POST` mounts that carry RPC request messages. |
121
- | `boundedPostPaths` | `ReadonlyArray<string>` | RPC mounts plus both runtime bridge routes; every body is bounded before a handler reads it. |
120
+ | `rpcPaths` | `ReadonlyArray<string>` | `["/rpc", "/projections", "/sync", "/branch"]`: the `POST` mounts that carry RPC request messages. |
121
+ | `boundedPostPaths` | `ReadonlyArray<string>` | RPC mounts plus the three runtime bridge routes; every body is bounded before a handler reads it. |
122
122
  | `protectedPaths` | `ReadonlyArray<string>` | Projection, sync, WebSocket, and runtime bridge paths that pass edge authentication. |
123
123
  | `defaultMaxRequestBodyBytes` | `number` | 1,048,576. The default maximum request body accepted by an RPC mount. |
124
124
  | `loopbackHostNames` | `ReadonlyArray<string>` | `["127.0.0.1", "localhost", "::1"]`: the names that mean this machine only, as a bind address is spelled. |
@@ -22,10 +22,10 @@ that supply storage, the selected SQLite driver, and the control runtime's ports
22
22
 
23
23
  ```bash
24
24
  pnpm add -D \
25
- @smthrs/database@1.0.0-rc.0 \
26
- @smthrs/journal@1.0.0-rc.0 \
27
- @smthrs/notifications@1.0.0-rc.0 \
28
- @smthrs/registry@1.0.0-rc.0 \
25
+ @smthrs/database@next \
26
+ @smthrs/journal@next \
27
+ @smthrs/notifications@next \
28
+ @smthrs/registry@next \
29
29
  effect \
30
30
  @effect/sql-sqlite-node
31
31
  ```
@@ -7,7 +7,12 @@ sidebar:
7
7
 
8
8
  ## Install
9
9
 
10
- Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/docs/installation.md#install-the-cli).
10
+ Published on npm under the `next` tag. Name `@next` explicitly: `latest`
11
+ still points at 0.35.0, an older release.
12
+
13
+ ```bash
14
+ pnpm add @smthrs/gateway@next effect@^4.0.4
15
+ ```
11
16
 
12
17
  ## Requirements
13
18
 
@@ -18,11 +23,7 @@ Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/m
18
23
 
19
24
  `effect` is the only peer, in the range `^4.0.4`. Node and Bun adapters
20
25
  are bundled privately, built from 4.0.4, including their shared implementation.
21
- Hosts need no separate adapter install:
22
-
23
- ```bash
24
- pnpm add effect@^4.0.4
25
- ```
26
+ Hosts need no separate adapter install.
26
27
 
27
28
  Use `@smthrs/gateway/node/NodeGateway` on Node or
28
29
  `@smthrs/gateway/bun/BunGateway` on Bun. The root and protocol subpaths need
@@ -84,6 +85,7 @@ The full composition, with the storage underneath it, is in
84
85
  ## Running without composing anything
85
86
 
86
87
  [`smthrs serve`](/cli/serve) hosts this exact assembly for a project on disk.
87
- If you want a gateway rather than a library, use the CLI instead. Not on npm yet; see [Installation](https://github.com/smithersai/smithers/blob/main/packages/smithers/docs/installation.md#install-the-cli).
88
+ If you want a gateway rather than a library, install the CLI instead, also
89
+ under the `next` tag: `pnpm add -g @smthrs/cli@next`.
88
90
 
89
91
  Then see the [Quickstart](./quickstart.md).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@smthrs/gateway",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.7",
4
+ "version": "1.0.0-rc.8",
5
5
  "license": "MIT",
6
6
  "smthrs": {
7
7
  "group": "agent",
@@ -223,12 +223,12 @@
223
223
  "tag": "next"
224
224
  },
225
225
  "dependencies": {
226
- "@smthrs/control": "1.0.0-rc.7",
227
- "@smthrs/engine-store": "1.0.0-rc.7",
228
- "@smthrs/flow": "1.0.0-rc.7",
229
- "@smthrs/journal": "1.0.0-rc.7",
230
- "@smthrs/registry": "1.0.0-rc.7",
231
- "@smthrs/sync": "1.0.0-rc.7",
226
+ "@smthrs/control": "1.0.0-rc.8",
227
+ "@smthrs/engine-store": "1.0.0-rc.8",
228
+ "@smthrs/flow": "1.0.0-rc.8",
229
+ "@smthrs/journal": "1.0.0-rc.8",
230
+ "@smthrs/registry": "1.0.0-rc.8",
231
+ "@smthrs/sync": "1.0.0-rc.8",
232
232
  "undici": "8.11.2",
233
233
  "redis": "6.2.1",
234
234
  "@types/ws": "8.18.2",
@@ -239,12 +239,12 @@
239
239
  "@effect/platform-node": "4.0.4",
240
240
  "@effect/vitest": "4.0.4",
241
241
  "@eslint/js": "9.39.5",
242
- "@smthrs/database": "1.0.0-rc.7",
243
- "@smthrs/flows": "1.0.0-rc.7",
244
- "@smthrs/kernel": "1.0.0-rc.7",
245
- "@smthrs/notifications": "1.0.0-rc.7",
246
- "@smthrs/platform-node": "1.0.0-rc.7",
247
- "@smthrs/run-store": "1.0.0-rc.7",
242
+ "@smthrs/database": "1.0.0-rc.8",
243
+ "@smthrs/flows": "1.0.0-rc.8",
244
+ "@smthrs/kernel": "1.0.0-rc.8",
245
+ "@smthrs/notifications": "1.0.0-rc.8",
246
+ "@smthrs/platform-node": "1.0.0-rc.8",
247
+ "@smthrs/run-store": "1.0.0-rc.8",
248
248
  "@types/node": "26.4.1",
249
249
  "@vitest/coverage-v8": "5.0.0",
250
250
  "dprint": "0.57.1",
@@ -386,6 +386,7 @@ export const rpcPaths: ReadonlyArray<string> = ["/rpc", "/projections", "/sync",
386
386
  export const boundedPostPaths: ReadonlyArray<string> = [
387
387
  ...rpcPaths,
388
388
  "/runtime/v1/command",
389
+ "/runtime/v1/monitor",
389
390
  "/runtime/v1/observe"
390
391
  ]
391
392
 
@@ -422,6 +423,7 @@ export const protectedPaths: ReadonlyArray<string> = [
422
423
  "/projections/ws",
423
424
  "/sync/ws",
424
425
  "/runtime/v1/command",
426
+ "/runtime/v1/monitor",
425
427
  "/runtime/v1/observe"
426
428
  ]
427
429