service-bridge 1.0.9 → 1.0.10-dev.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +58 -51
  2. package/dist/index.js +781 -33071
  3. package/package.json +13 -2
  4. package/biome.json +0 -28
  5. package/bun.lock +0 -249
  6. package/http/dist/express.d.ts +0 -51
  7. package/http/dist/express.d.ts.map +0 -1
  8. package/http/dist/express.test.d.ts +0 -2
  9. package/http/dist/express.test.d.ts.map +0 -1
  10. package/http/dist/fastify.d.ts +0 -43
  11. package/http/dist/fastify.d.ts.map +0 -1
  12. package/http/dist/fastify.test.d.ts +0 -2
  13. package/http/dist/fastify.test.d.ts.map +0 -1
  14. package/http/dist/index.d.ts +0 -7
  15. package/http/dist/index.d.ts.map +0 -1
  16. package/http/dist/trace.d.ts +0 -19
  17. package/http/dist/trace.d.ts.map +0 -1
  18. package/http/dist/trace.test.d.ts +0 -2
  19. package/http/dist/trace.test.d.ts.map +0 -1
  20. package/http/package.json +0 -49
  21. package/http/src/express.test.ts +0 -125
  22. package/http/src/express.ts +0 -209
  23. package/http/src/fastify.test.ts +0 -142
  24. package/http/src/fastify.ts +0 -159
  25. package/http/src/index.ts +0 -10
  26. package/http/src/sdk-augment.d.ts +0 -11
  27. package/http/src/servicebridge.d.ts +0 -23
  28. package/http/src/trace.test.ts +0 -97
  29. package/http/src/trace.ts +0 -56
  30. package/http/tsconfig.json +0 -17
  31. package/http/tsconfig.test.json +0 -6
  32. package/sdk/dist/generated/servicebridge-package-definition.d.ts +0 -1
  33. package/sdk/dist/grpc-client.d.ts +0 -344
  34. package/sdk/dist/grpc-client.test.d.ts +0 -1
  35. package/sdk/dist/index.d.ts +0 -2
  36. package/sdk/package.json +0 -31
  37. package/sdk/scripts/generate-proto.ts +0 -34
  38. package/sdk/src/generated/servicebridge-package-definition.ts +0 -4
  39. package/sdk/src/grpc-client.d.ts +0 -332
  40. package/sdk/src/grpc-client.d.ts.map +0 -1
  41. package/sdk/src/grpc-client.test.ts +0 -422
  42. package/sdk/src/grpc-client.ts +0 -3101
  43. package/sdk/src/index.d.ts +0 -3
  44. package/sdk/src/index.d.ts.map +0 -1
  45. package/sdk/src/index.ts +0 -31
  46. package/sdk/tsconfig.json +0 -13
package/README.md CHANGED
@@ -95,16 +95,15 @@ bun add service-bridge
95
95
  import { servicebridge } from "service-bridge";
96
96
 
97
97
  const sb = servicebridge(
98
- process.env.SERVICEBRIDGE_URL ?? "0.0.0.0:14445",
98
+ process.env.SERVICEBRIDGE_URL ?? "localhost:14445",
99
99
  process.env.SERVICEBRIDGE_SERVICE_KEY!,
100
- "payments",
101
100
  );
102
101
 
103
102
  sb.handleRpc("charge", async (payload: { orderId: string; amount: number }) => {
104
103
  return { ok: true, txId: `tx_${Date.now()}`, orderId: payload.orderId };
105
104
  });
106
105
 
107
- await sb.serve({ host: "0.0.0.0" });
106
+ await sb.serve({ host: "localhost" });
108
107
  ```
109
108
 
110
109
  ### 3. Call it from another service
@@ -113,9 +112,8 @@ await sb.serve({ host: "0.0.0.0" });
113
112
  import { servicebridge } from "service-bridge";
114
113
 
115
114
  const sb = servicebridge(
116
- process.env.SERVICEBRIDGE_URL ?? "0.0.0.0:14445",
115
+ process.env.SERVICEBRIDGE_URL ?? "localhost:14445",
117
116
  process.env.SERVICEBRIDGE_SERVICE_KEY!,
118
- "orders",
119
117
  );
120
118
 
121
119
  const result = await sb.rpc<{ ok: boolean; txId: string }>("payments/charge", {
@@ -138,7 +136,7 @@ The SDK connects to a ServiceBridge runtime. The fastest way to start:
138
136
  bash <(curl -fsSL https://servicebridge.dev/install.sh)
139
137
  ```
140
138
 
141
- This installs ServiceBridge + PostgreSQL via Docker Compose and generates an admin password automatically. After install, the dashboard is at `http://localhost:14444` and the gRPC control plane at `0.0.0.0:14445`.
139
+ This installs ServiceBridge + PostgreSQL via Docker Compose and generates an admin password automatically. After install, the dashboard is at `http://localhost:14444` and the gRPC control plane at `localhost:14445`.
142
140
 
143
141
  For manual Docker Compose setup, configuration reference, and all runtime environment variables, see the **[Runtime Setup](../README.md#runtime-setup)** section in the main SDK README.
144
142
 
@@ -153,7 +151,7 @@ import { servicebridge } from "service-bridge";
153
151
 
154
152
  // --- Payments service (worker) ---
155
153
 
156
- const payments = servicebridge("0.0.0.0:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!, "payments");
154
+ const payments = servicebridge("localhost:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!);
157
155
 
158
156
  payments.handleRpc("charge", async (payload: { orderId: string; amount: number }, ctx) => {
159
157
  await ctx?.stream.write({ status: "charging", orderId: payload.orderId }, "progress");
@@ -164,13 +162,13 @@ payments.handleRpc("charge", async (payload: { orderId: string; amount: number }
164
162
  return { ok: true, txId: `tx_${Date.now()}` };
165
163
  });
166
164
 
167
- await payments.serve({ host: "0.0.0.0" });
165
+ await payments.serve({ host: "localhost" });
168
166
  ```
169
167
 
170
168
  ```ts
171
169
  // --- Orders service (caller + event publisher) ---
172
170
 
173
- const orders = servicebridge("0.0.0.0:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!, "orders");
171
+ const orders = servicebridge("localhost:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!);
174
172
 
175
173
  // Call payments, then publish event
176
174
  const charge = await orders.rpc<{ ok: boolean; txId: string }>("payments/charge", {
@@ -190,7 +188,7 @@ await orders.event("orders.completed", {
190
188
  ```ts
191
189
  // --- Notifications service (event consumer) ---
192
190
 
193
- const notifications = servicebridge("0.0.0.0:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!, "notifications");
191
+ const notifications = servicebridge("localhost:14445", process.env.SERVICEBRIDGE_SERVICE_KEY!);
194
192
 
195
193
  notifications.handleEvent("orders.*", async (payload, ctx) => {
196
194
  const body = payload as { orderId: string; txId: string };
@@ -198,7 +196,7 @@ notifications.handleEvent("orders.*", async (payload, ctx) => {
198
196
  // ... send email ...
199
197
  });
200
198
 
201
- await notifications.serve({ host: "0.0.0.0" });
199
+ await notifications.serve({ host: "localhost" });
202
200
  ```
203
201
 
204
202
  ```ts
@@ -230,7 +228,7 @@ Every step above — RPC, event publish, event delivery, workflow execution —
230
228
  - **Jobs** — cron, delayed, and workflow-triggered scheduling
231
229
 
232
230
  ### Security
233
- - **Auto mTLS** — automatic certificate provisioning for workers
231
+ - **TLS by default** — control plane TLS + worker mTLS with gRPC certificate provisioning
234
232
  - **Access Policy** — service-level caller/target restrictions and RBAC
235
233
 
236
234
  ### Observability
@@ -267,44 +265,46 @@ ServiceBridge keeps the core API shape consistent across Node.js, Go, and Python
267
265
  constructor, RPC, events, jobs, workflows, `runWorkflow`, streams, serve/stop, and `ServiceBridgeError`.
268
266
 
269
267
  Constructor-level defaults for `timeout`, `retries`, and `retryDelay` are available
270
- across all three SDKs. The following are intentionally language-specific today:
268
+ across all three SDKs. Parity differences are naming-only (language idioms):
271
269
 
272
- - Node-only constructor options: `workerTransport`, `workerTLS`
273
- - Node-only handler hints: `handleRpc.timeout`, `handleRpc.retryable`, `handleRpc.concurrency`, `handleEvent.concurrency`, `handleEvent.prefetch`
274
- - Node-only `serve()` fields: `instanceId`, `weight`, `transport`, `tls`
270
+ - Constructor TLS overrides: `workerTLS`/`caCert` (Node), `WorkerTLS`/`CACert` (Go), `worker_tls`/`ca_cert` (Python)
271
+ - Handler hints: timeout/retryable/concurrency/prefetch are advisory in all SDKs
272
+ - Shared `serve()` fields across SDKs: host, max in-flight, instance ID, weight, and per-serve TLS override
275
273
 
276
- ### `servicebridge(url, serviceKey, serviceName?, opts?)`
274
+ ### `servicebridge(url, serviceKey, opts?)`
277
275
 
278
276
  ```ts
279
277
  function servicebridge(
280
278
  url: string,
281
279
  serviceKey: string,
282
- service?: string,
283
- globalOpts?: ServiceBridgeOpts,
280
+ serviceOrOpts?: string | ServiceBridgeOpts,
281
+ maybeGlobalOpts?: ServiceBridgeOpts,
284
282
  ): ServiceBridgeService
285
283
  ```
286
284
 
287
285
  Creates an SDK client instance.
286
+ Service identity is resolved by the runtime from `serviceKey`; passing a third `service` argument is legacy-only.
288
287
 
289
288
  `ServiceBridgeOpts`:
290
289
 
291
290
  | Option | Type | Default | Description |
292
291
  |---|---|---|---|
293
- | `timeout` | `number` | `30000` | Default timeout (ms) for SDK operations. |
292
+ | `timeout` | `number` | `30000` | Default hard timeout per RPC attempt (ms). |
294
293
  | `retries` | `number` | `3` | Default retry count for `rpc()`. |
295
294
  | `retryDelay` | `number` | `300` | Base backoff delay (ms) for `rpc()`. |
296
295
  | `discoveryRefreshMs` | `number` | `10000` | Discovery refresh period for endpoint updates. |
297
296
  | `queueMaxSize` | `number` | `1000` | Max offline queue size for control-plane writes. |
298
297
  | `queueOverflow` | `"drop-oldest" \| "drop-newest" \| "error"` | `"drop-oldest"` | Overflow strategy for offline queue. |
299
298
  | `heartbeatIntervalMs` | `number` | `10000` | Base heartbeat period for worker registrations. |
300
- | `workerTransport` | `"tls"` | `"tls"` | Worker server transport. |
301
- | `workerTLS` | `WorkerTLSOpts` | auto | Explicit cert/key/CA for worker mTLS. |
302
- | `adminUrl` | `string` | derived from `url` | HTTP admin base URL (TLS provisioning and management API calls). |
303
- | `adminSessionCookie` | `string` | `undefined` | Admin session cookie for browser-authenticated endpoints (e.g. `cancelWorkflowRun`). |
304
- | `adminCsrfToken` | `string` | `undefined` | CSRF token paired with `adminSessionCookie` for unsafe HTTP methods. |
305
- | `adminOrigin` | `string` | `undefined` | Origin header required by admin CSRF/origin checks. |
306
299
  | `captureLogs` | `boolean` | `true` | Forward `console.*` logs to ServiceBridge. |
307
300
 
301
+ ### Advanced TLS overrides
302
+
303
+ | Option | Type | Default | Description |
304
+ |---|---|---|---|
305
+ | `workerTLS` | `WorkerTLSOpts` | auto | Explicit cert/key/CA for worker mTLS. |
306
+ | `caCert` | `string \| Buffer` | from `serviceKey` | Optional control-plane CA override. By default SDK reads CA from sbv2 service key. |
307
+
308
308
  `WorkerTLSOpts`:
309
309
 
310
310
  ```ts
@@ -343,6 +343,10 @@ const user = await sb.rpc<{ id: string; name: string }>("users/get", { id: "u_1"
343
343
  });
344
344
  ```
345
345
 
346
+ `rpc()` is bounded even when a downstream worker is silent:
347
+ each attempt has a hard local timeout, retries are finite (`retries + 1` total attempts),
348
+ and after the final failed attempt the root RPC span is closed with `error`.
349
+
346
350
  ---
347
351
 
348
352
  ### `event(topic, payload?, opts?)`
@@ -503,9 +507,9 @@ Registers an RPC handler. Chainable.
503
507
 
504
508
  | Option | Type | Description |
505
509
  |---|---|---|
506
- | `timeout` | `number` | Node-only timeout hint (currently not hard-enforced by runtime). |
507
- | `retryable` | `boolean` | Node-only retry hint (currently metadata-level, not a strict policy switch). |
508
- | `concurrency` | `number` | Node-only concurrency hint (currently not hard-enforced). |
510
+ | `timeout` | `number` | Advisory timeout hint (currently metadata-level, not hard-enforced by runtime). |
511
+ | `retryable` | `boolean` | Advisory retry hint (currently metadata-level, not a strict policy switch). |
512
+ | `concurrency` | `number` | Advisory concurrency hint (currently not hard-enforced). |
509
513
  | `schema` | `RpcSchemaOpts` | Inline protobuf schema for binary encode/decode. |
510
514
  | `allowedCallers` | `string[]` | Allow-list of caller service names. |
511
515
 
@@ -535,9 +539,9 @@ Registers an event consumer handler. Chainable.
535
539
 
536
540
  | Option | Type | Description |
537
541
  |---|---|---|
538
- | `groupName` | `string` | Consumer group name. Default: `<service>.<pattern>`. |
539
- | `concurrency` | `number` | Node-only concurrency hint (currently not hard-enforced). |
540
- | `prefetch` | `number` | Node-only prefetch hint (currently not hard-enforced). |
542
+ | `groupName` | `string` | Consumer group name. Default: `<service-key-id>.<pattern>`. |
543
+ | `concurrency` | `number` | Advisory concurrency hint (currently not hard-enforced). |
544
+ | `prefetch` | `number` | Advisory prefetch hint (currently not hard-enforced). |
541
545
  | `retryPolicyJson` | `string` | Retry policy JSON string. |
542
546
  | `filterExpr` | `string` | Server-side filter expression. |
543
547
 
@@ -577,15 +581,15 @@ the Node.js process).
577
581
 
578
582
  | Option | Type | Description |
579
583
  |---|---|---|
580
- | `host` | `string` | Bind host. Default: `0.0.0.0`. |
581
- | `instanceId` | `string` | Stable worker instance identifier (Node-only). |
582
- | `weight` | `number` | Scheduling/discovery weight hint (Node-only). |
583
- | `transport` | `"tls"` | Worker transport (Node-only in `serve()`). |
584
- | `tls` | `WorkerTLSOpts` | Per-serve TLS override (Node-only in `serve()`). |
584
+ | `host` | `string` | Bind host. Default: `localhost`. Use `0.0.0.0` in Docker/Kubernetes so ServiceBridge can reach the worker. |
585
+ | `maxInFlight` | `number` | Max in-flight runtime-originated commands over `OpenWorkerSession`. Default: `128`. |
586
+ | `instanceId` | `string` | Stable worker instance identifier. |
587
+ | `weight` | `number` | Scheduling/discovery weight hint. |
588
+ | `tls` | `WorkerTLSOpts` | Per-serve worker TLS override. |
585
589
 
586
590
  ```ts
587
591
  await sb.serve({
588
- host: "0.0.0.0",
592
+ host: "localhost",
589
593
  instanceId: process.env.HOSTNAME,
590
594
  });
591
595
  ```
@@ -693,6 +697,13 @@ identifier used by `ctx.stream.write(...)` (typically a trace ID).
693
697
  | `data` | `unknown` | JSON-decoded chunk payload. |
694
698
  | `runStatus` | `string \| undefined` | Final status on `run_complete`. |
695
699
 
700
+ Behavior:
701
+
702
+ - Auto-reconnect with exponential backoff (`500ms` → `5000ms`) on retryable stream failures.
703
+ - Deduplicates by `sequence` across reconnects.
704
+ - Enforces strict JSON for `type="chunk"` payloads (non-JSON chunk terminates stream with fatal error).
705
+ - Enforces internal queue limit `256`; overflow is fatal (consumer must drain promptly).
706
+
696
707
  ```ts
697
708
  for await (const evt of sb.watchRun(runId, { key: "output", fromSequence: 0 })) {
698
709
  if (evt.type === "chunk") {
@@ -754,7 +765,7 @@ import express from "express";
754
765
  import { servicebridge } from "service-bridge";
755
766
  import { servicebridgeMiddleware, registerExpressRoutes } from "service-bridge/express";
756
767
 
757
- const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY!, "api");
768
+ const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY!);
758
769
  const app = express();
759
770
 
760
771
  app.use(servicebridgeMiddleware({
@@ -810,7 +821,7 @@ import Fastify from "fastify";
810
821
  import { servicebridge } from "service-bridge";
811
822
  import { servicebridgePlugin, wrapHandler } from "service-bridge/fastify";
812
823
 
813
- const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY!, "api");
824
+ const sb = servicebridge(process.env.SERVICEBRIDGE_URL!, process.env.SERVICEBRIDGE_SERVICE_KEY!);
814
825
  const app = Fastify();
815
826
 
816
827
  await app.register(servicebridgePlugin, {
@@ -859,7 +870,9 @@ Runs a Fastify handler inside the current trace context so downstream SDK calls
859
870
  ### TLS behavior
860
871
 
861
872
  - Worker transport is TLS-only.
862
- - If `workerTLS` is not provided, SDK auto-provisions certs through the admin API.
873
+ - Control plane is TLS-only. Trust source is embedded into sbv2 service key by default.
874
+ - Embedded/explicit CA PEM is validated with strict x509 parsing.
875
+ - If `workerTLS` is not provided, SDK auto-provisions worker certs via gRPC `ProvisionWorkerCertificate`.
863
876
  - `workerTLS.cert` and `workerTLS.key` must be provided together.
864
877
  - `serve({ tls })` overrides global `workerTLS` for a specific worker instance.
865
878
 
@@ -879,19 +892,13 @@ The SDK requires values you pass into `servicebridge(...)`. Common setup:
879
892
 
880
893
  | Variable | Required | Example | Description |
881
894
  |---|---|---|---|
882
- | `SERVICEBRIDGE_URL` | yes | `0.0.0.0:14445` | gRPC control plane URL |
883
- | `SERVICEBRIDGE_SERVICE_KEY` | yes | `sb_live_...` | Service authentication key |
884
- | `SERVICEBRIDGE_SERVICE` | yes (worker mode) | `orders` | Service name in registry |
885
- | `SERVICEBRIDGE_ADMIN_URL` | optional | `http://0.0.0.0:14444` | Explicit admin API base URL |
895
+ | `SERVICEBRIDGE_URL` | yes | `localhost:14445` | gRPC control plane URL |
896
+ | `SERVICEBRIDGE_SERVICE_KEY` | yes | `sbv2.<id>.<secret>.<ca>` | Service authentication key (sbv2 only) |
886
897
 
887
898
  ```ts
888
899
  const sb = servicebridge(
889
- process.env.SERVICEBRIDGE_URL ?? "0.0.0.0:14445",
900
+ process.env.SERVICEBRIDGE_URL ?? "localhost:14445",
890
901
  process.env.SERVICEBRIDGE_SERVICE_KEY!,
891
- process.env.SERVICEBRIDGE_SERVICE ?? "orders",
892
- {
893
- adminUrl: process.env.SERVICEBRIDGE_ADMIN_URL,
894
- },
895
902
  );
896
903
  ```
897
904
 
@@ -949,7 +956,7 @@ try {
949
956
  ## FAQ
950
957
 
951
958
  **How does ServiceBridge handle service failures?**
952
- RPC calls have configurable retries with exponential backoff. Events are durable (PostgreSQL-backed) with at-least-once delivery per consumer group. Failed deliveries are retried according to policy, then moved to DLQ. Workflows track step state and can be resumed.
959
+ RPC calls have configurable retries with exponential backoff and hard per-attempt timeouts, so a silent downstream service cannot keep a call pending forever. Events are durable (PostgreSQL-backed) with at-least-once delivery per consumer group. Failed deliveries are retried according to policy, then moved to DLQ. Workflows track step state and can be resumed.
953
960
 
954
961
  **Is there vendor lock-in?**
955
962
  ServiceBridge is self-hosted. The runtime is a single Go binary + PostgreSQL. SDK calls map to standard patterns (RPC, pub/sub, cron) — migrating away means replacing SDK calls with equivalent library calls.