orbit-bus 0.1.1

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 (117) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +21 -0
  3. package/README.md +501 -0
  4. package/dist/src/agent_ipc.d.ts +4 -0
  5. package/dist/src/agent_ipc.js +77 -0
  6. package/dist/src/api_contract.d.ts +19 -0
  7. package/dist/src/api_contract.js +81 -0
  8. package/dist/src/api_http.d.ts +23 -0
  9. package/dist/src/api_http.js +62 -0
  10. package/dist/src/call_protection.d.ts +5 -0
  11. package/dist/src/call_protection.js +83 -0
  12. package/dist/src/cell/gateway.d.ts +13 -0
  13. package/dist/src/cell/gateway.js +171 -0
  14. package/dist/src/cell/routing.d.ts +18 -0
  15. package/dist/src/cell/routing.js +48 -0
  16. package/dist/src/cell/template.d.ts +7 -0
  17. package/dist/src/cell/template.js +24 -0
  18. package/dist/src/cli.d.ts +1 -0
  19. package/dist/src/cli.js +305 -0
  20. package/dist/src/commands/agent.d.ts +3 -0
  21. package/dist/src/commands/agent.js +187 -0
  22. package/dist/src/commands/api.d.ts +6 -0
  23. package/dist/src/commands/api.js +226 -0
  24. package/dist/src/commands/bench.d.ts +13 -0
  25. package/dist/src/commands/bench.js +125 -0
  26. package/dist/src/commands/bench_overhead.d.ts +8 -0
  27. package/dist/src/commands/bench_overhead.js +71 -0
  28. package/dist/src/commands/call.d.ts +10 -0
  29. package/dist/src/commands/call.js +45 -0
  30. package/dist/src/commands/cell.d.ts +3 -0
  31. package/dist/src/commands/cell.js +186 -0
  32. package/dist/src/commands/context.d.ts +9 -0
  33. package/dist/src/commands/context.js +71 -0
  34. package/dist/src/commands/dlq_inspect.d.ts +11 -0
  35. package/dist/src/commands/dlq_inspect.js +86 -0
  36. package/dist/src/commands/dlq_purge.d.ts +12 -0
  37. package/dist/src/commands/dlq_purge.js +84 -0
  38. package/dist/src/commands/dlq_replay.d.ts +15 -0
  39. package/dist/src/commands/dlq_replay.js +127 -0
  40. package/dist/src/commands/inspect.d.ts +6 -0
  41. package/dist/src/commands/inspect.js +57 -0
  42. package/dist/src/commands/monitor.d.ts +32 -0
  43. package/dist/src/commands/monitor.js +201 -0
  44. package/dist/src/commands/publish.d.ts +10 -0
  45. package/dist/src/commands/publish.js +64 -0
  46. package/dist/src/commands/serve.d.ts +8 -0
  47. package/dist/src/commands/serve.js +258 -0
  48. package/dist/src/commands/subscribe.d.ts +11 -0
  49. package/dist/src/commands/subscribe.js +78 -0
  50. package/dist/src/commands/trace.d.ts +5 -0
  51. package/dist/src/commands/trace.js +26 -0
  52. package/dist/src/commands/up.d.ts +3 -0
  53. package/dist/src/commands/up.js +91 -0
  54. package/dist/src/config.d.ts +6 -0
  55. package/dist/src/config.js +281 -0
  56. package/dist/src/dlq.d.ts +20 -0
  57. package/dist/src/dlq.js +71 -0
  58. package/dist/src/echo/benchmark.d.ts +10 -0
  59. package/dist/src/echo/benchmark.js +105 -0
  60. package/dist/src/echo/bus.d.ts +22 -0
  61. package/dist/src/echo/bus.js +89 -0
  62. package/dist/src/echo/cli.d.ts +1 -0
  63. package/dist/src/echo/cli.js +135 -0
  64. package/dist/src/echo/client.d.ts +12 -0
  65. package/dist/src/echo/client.js +46 -0
  66. package/dist/src/echo/daemon.d.ts +8 -0
  67. package/dist/src/echo/daemon.js +181 -0
  68. package/dist/src/echo/index.d.ts +6 -0
  69. package/dist/src/echo/index.js +5 -0
  70. package/dist/src/echo/ring_buffer.d.ts +27 -0
  71. package/dist/src/echo/ring_buffer.js +73 -0
  72. package/dist/src/echo/types.d.ts +27 -0
  73. package/dist/src/echo/types.js +1 -0
  74. package/dist/src/echocore.d.ts +2 -0
  75. package/dist/src/echocore.js +6 -0
  76. package/dist/src/envelope.d.ts +14 -0
  77. package/dist/src/envelope.js +92 -0
  78. package/dist/src/errors.d.ts +5 -0
  79. package/dist/src/errors.js +9 -0
  80. package/dist/src/index.d.ts +2 -0
  81. package/dist/src/index.js +12 -0
  82. package/dist/src/jetstream_durable.d.ts +4 -0
  83. package/dist/src/jetstream_durable.js +51 -0
  84. package/dist/src/json_schema.d.ts +6 -0
  85. package/dist/src/json_schema.js +154 -0
  86. package/dist/src/logger.d.ts +16 -0
  87. package/dist/src/logger.js +31 -0
  88. package/dist/src/metrics.d.ts +6 -0
  89. package/dist/src/metrics.js +95 -0
  90. package/dist/src/nats.d.ts +16 -0
  91. package/dist/src/nats.js +129 -0
  92. package/dist/src/orbit_actions.d.ts +4 -0
  93. package/dist/src/orbit_actions.js +129 -0
  94. package/dist/src/otel.d.ts +2 -0
  95. package/dist/src/otel.js +96 -0
  96. package/dist/src/registry.d.ts +10 -0
  97. package/dist/src/registry.js +57 -0
  98. package/dist/src/retry.d.ts +9 -0
  99. package/dist/src/retry.js +32 -0
  100. package/dist/src/rpc_call.d.ts +22 -0
  101. package/dist/src/rpc_call.js +119 -0
  102. package/dist/src/service_adapter.d.ts +7 -0
  103. package/dist/src/service_adapter.js +163 -0
  104. package/dist/src/spec.d.ts +2 -0
  105. package/dist/src/spec.js +30 -0
  106. package/dist/src/subjects.d.ts +2 -0
  107. package/dist/src/subjects.js +4 -0
  108. package/dist/src/trace.d.ts +4 -0
  109. package/dist/src/trace.js +86 -0
  110. package/dist/src/types.d.ts +133 -0
  111. package/dist/src/types.js +1 -0
  112. package/dist/src/util.d.ts +10 -0
  113. package/dist/src/util.js +63 -0
  114. package/dist/src/worker_pool.d.ts +9 -0
  115. package/dist/src/worker_pool.js +163 -0
  116. package/docs/orbit-api-contract.yaml +376 -0
  117. package/package.json +40 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ All notable changes to Orbit are documented here.
4
+
5
+ This project follows Semantic Versioning (`MAJOR.MINOR.PATCH`).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.1] - 2026-03-03
10
+
11
+ ### Added
12
+ - API hardening: token auth, host allowlist, optional TLS/mTLS, metrics endpoint.
13
+ - `GET /readyz` dependency readiness endpoint (NATS flush check) for probe-safe deployments.
14
+ - Strict JSON schema validation for API payloads and service method request/response contracts.
15
+ - Stable HTTP error envelope + explicit status-code mapping.
16
+ - Docker-backed integration test path for `up/serve/call/api/agent`.
17
+ - GitHub CI and release workflows with artifact checks and provenance attestation.
18
+ - Agent socket startup hardening and permission enforcement.
19
+ - Production hardening guide and secure NATS template configs, including 3-node cluster examples.
20
+ - Dependency review and CodeQL workflows plus Dependabot config.
21
+ - Production bootstrap script (`npm run bootstrap:prod`) to generate hardened API config scaffold.
22
+ - TypeScript SDK test suite for auth error mapping, timeout behavior, and call payload parity.
23
+ - Python SDK test suite for auth error mapping, timeout behavior, and call payload parity.
24
+ - CI smoke-install job that validates packaged `orbit`, `echocore`, `orbit-ts`, and `orbit-py` CLIs.
25
+ - API contract and SDK/CLI parity for A2A metadata fields (`taskId`, `threadId`, `parentMessageId`, `capabilities`, `traceparent`, `dedupeKey`) and publish durability.
26
+
27
+ ### Changed
28
+ - OTLP trace exporter now retries transient failures with backoff/jitter and requeues unsent events.
29
+ - npm package publishing flow migrated to trusted publishing (OIDC) with provenance.
30
+ - npm/SDK package manifests now use publish allowlists to prevent shipping tests/docs/local artifacts.
31
+ - Python SDK packaging metadata now includes README/license and correctly packages `orbit_cli` entrypoint module.
32
+ - License metadata aligned for public distribution (`MIT`) across root, TypeScript SDK, and Python SDK.
33
+ - CI/release SDK install and pack steps now use deterministic and directory-scoped commands (`npm ci`, `cd sdk/typescript && ...`).
34
+ - Root test runtime moved from deprecated `--loader ts-node/esm` to `--import tsx`.
35
+ - Python SDK timeout errors now normalize to `OrbitApiError(code=\"TIMEOUT\")` instead of leaking raw timeout exceptions.
36
+ - Docker integration test command arguments were corrected and hardened for local Docker credential/path and Unix socket-path constraints.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Orbit contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,501 @@
1
+ # ORBIT
2
+
3
+ ORBIT is a local-first agent message bus CLI built on NATS with:
4
+
5
+ - Request/reply RPC (`orbit call`, `orbit serve`)
6
+ - Pub/sub (`orbit publish`, `orbit subscribe`)
7
+ - Service discovery + capability inspection (`orbit inspect`)
8
+ - Trace timelines with retries/timeouts (`orbit trace`)
9
+ - Canonical typed envelopes with integrity hash
10
+ - NATS Service API compatibility (`$SRV.PING|INFO|STATS`)
11
+ - JetStream KV service registry + Object Store data packs
12
+ - Context switching (`orbit context ...`)
13
+ - Optional OpenTelemetry OTLP export
14
+ - Benchmarking command (`orbit bench`)
15
+ - Orbit overhead benchmark (`orbit bench-overhead`)
16
+ - Live health/stat monitor (`orbit monitor`)
17
+ - Local persistent control-plane agent (`orbit agent`)
18
+ - External Orbit API service (`orbit api`)
19
+ - Externalized HTTP and persistent-worker method transports
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install
25
+ npm run build
26
+ npm link
27
+ ```
28
+
29
+ ## Commands
30
+
31
+ ```bash
32
+ orbit up
33
+ orbit serve --name <svc> --spec spec.json [--queue workers] [--concurrency 8]
34
+ orbit call <svc>.<method> --json @req.json [--run-id <id>] [--pack-file ./blob.bin] [--timeout-ms 5000] [--retries 2]
35
+ orbit publish <topic> --json @event.json [--run-id <id>] [--pack-file ./blob.bin] [--durable] [--dedupe-key <id>]
36
+ orbit subscribe <topic> [--durable-name <name>] [--stream <name>] [--dlq-topic <topic>] [--ack-wait-ms 30000] [--max-deliver 5] [--require-json]
37
+ orbit dlq-inspect <dlq-topic> [--stream <name>] [--limit 100] [--from-ts <iso>] [--to-ts <iso>] [--error-code <code>] [--source-consumer <name>]
38
+ orbit dlq-purge <dlq-topic> [--stream <name>] [--limit 100] [--from-ts <iso>] [--to-ts <iso>] [--error-code <code>] [--source-consumer <name>] [--dry-run]
39
+ orbit dlq-replay <dlq-topic> --to-topic <topic> [--limit 100] [--stream <name>] [--from-ts <iso>] [--to-ts <iso>] [--error-code <code>] [--source-consumer <name>] [--purge-replayed] [--non-durable-publish]
40
+ orbit inspect <svc>
41
+ orbit trace <run-id>
42
+ orbit context [list|current|use <name>|set <name> --nats-url <url> --timeout-ms <n> --retries <n>]
43
+ orbit bench <svc>.<method> --json @req.json [--duration-s 15] [--concurrency 10] [--ramp-to 50] [--ramp-step-s 1] [--ramp-step-concurrency 2] [--timeout-ms 2000] [--retries 0]
44
+ orbit bench-overhead <svc>.<method> --json @req.json [--iterations 100] [--timeout-ms 2000]
45
+ orbit monitor [--service <svc>] [--interval-ms 2000] [--timeout-ms 1500] [--alerts] [--alert-latency-ms 250] [--alert-error-rate 0.05] [--alert-consecutive 3] [--alert-cooldown-s 30] [--once]
46
+ orbit agent
47
+ orbit api [--host 127.0.0.1] [--port 8787]
48
+ orbit cell <init|start|gateway|status> [...]
49
+ orbit echo <start|publish|subscribe|stats|bench> [...]
50
+ echocore start [--socket /tmp/echocore.sock] [--tcp-port 7777]
51
+ echocore publish --channel agent.loop --json @event.json
52
+ echocore subscribe --channel agent.loop
53
+ echocore bench [--messages 50000] [--bytes 1024]
54
+ ```
55
+
56
+ ## EchoCore
57
+
58
+ `echocore` is a local event-stream module optimized for desktop-agent component wiring:
59
+
60
+ - Shared-memory ring buffers per channel (in-process, zero-copy subscriber views)
61
+ - Backpressure policies (`drop_oldest` or `drop_newest`)
62
+ - Channel isolation
63
+ - Local unix-socket daemon bridge with optional TCP fallback
64
+ - Built-in benchmark command (`echocore bench`) for in-process vs network-framed baseline
65
+
66
+ You can invoke EchoCore either directly (`echocore ...`) or through Orbit (`orbit echo ...`).
67
+
68
+ ### Cell Mode
69
+
70
+ `orbit cell` lets you run a cloud-friendly two-tier topology from one CLI package:
71
+
72
+ - `orbit cell init`: scaffolds production routing template JSON.
73
+ - `orbit cell start`: starts local EchoCore daemon, optional embedded gateway.
74
+ - `orbit cell gateway`: bridges local channels to Orbit network subjects.
75
+ - `orbit cell status`: reports process state and local channel stats.
76
+
77
+ Routing modes (`--mode` or routes file):
78
+
79
+ - `local_only`: local channel only, no network bridge.
80
+ - `replicate`: bi-directional bridge between local channel and network subject.
81
+ - `global_only`: egress local->network only.
82
+
83
+ Examples:
84
+
85
+ ```bash
86
+ orbit cell init --out ./examples/cell.routes.production.json
87
+ orbit cell start --gateway --routes @./examples/cell.routes.production.json
88
+ orbit cell start --gateway --channel agent.loop --mode replicate
89
+ orbit cell gateway --socket ~/.orbit/echocore.sock --channel agent.audit --mode global_only
90
+ orbit cell status
91
+ ```
92
+
93
+ ## Canonical Envelope
94
+
95
+ All bus messages use:
96
+
97
+ ```json
98
+ {
99
+ "id": "uuid",
100
+ "run_id": "uuid",
101
+ "ts": "2026-02-25T12:00:00.000Z",
102
+ "kind": "request|response|event|capability|trace",
103
+ "schema_version": "1.0",
104
+ "payload": {},
105
+ "data_pack": {"bucket":"orbit_datapacks","key":"run/key.bin"},
106
+ "provenance": {},
107
+ "cost": {},
108
+ "a2a": {
109
+ "task_id": "task-123",
110
+ "thread_id": "thread-12",
111
+ "parent_message_id": "msg-1",
112
+ "capabilities": ["search", "retrieve"],
113
+ "traceparent": "w3c-traceparent",
114
+ "dedupe_key": "event-abc"
115
+ },
116
+ "hash": "sha256-of-canonical-fields"
117
+ }
118
+ ```
119
+
120
+ ## Quickstart
121
+
122
+ 1. Start broker:
123
+
124
+ ```bash
125
+ orbit up
126
+ ```
127
+
128
+ 2. Create a service spec (example at `examples/echo.spec.json`) and start adapter:
129
+
130
+ ```bash
131
+ orbit serve --name text --spec examples/echo.spec.json
132
+ orbit serve --name text --spec examples/echo.spec.json --queue text-workers --concurrency 8
133
+ ```
134
+
135
+ 3. Call a method:
136
+
137
+ ```bash
138
+ cat > req.json <<'JSON'
139
+ {"text":"hello orbit"}
140
+ JSON
141
+
142
+ orbit call text.upper --json @req.json
143
+ ```
144
+
145
+ Call with a large binary data-pack attached:
146
+
147
+ ```bash
148
+ orbit call text.upper --json @req.json --pack-file ./artifact.bin
149
+ ```
150
+
151
+ 4. Inspect service capabilities:
152
+
153
+ ```bash
154
+ orbit inspect text
155
+ ```
156
+
157
+ Also interoperates with NATS service introspection:
158
+
159
+ ```bash
160
+ nats req '$SRV.INFO.text' '{}'
161
+ ```
162
+
163
+ 5. Publish/subscribe:
164
+
165
+ ```bash
166
+ orbit subscribe agents.events
167
+ orbit publish agents.events --json '{"type":"build_done","ok":true}'
168
+
169
+ # Durable consumer + DLQ
170
+ orbit subscribe agents.events --durable-name agents-consumer --stream orbit_agents_stream --dlq-topic agents.events.dlq --max-deliver 5 --require-json
171
+
172
+ # Inspect only one consumer's overload failures in a window
173
+ orbit dlq-inspect agents.events.dlq --source-consumer agents-consumer --error-code AGENT_OVERLOADED --from-ts 2026-02-26T00:00:00Z --to-ts 2026-02-26T23:59:59Z
174
+
175
+ # Replay filtered DLQ messages back to primary topic and remove replayed entries
176
+ orbit dlq-replay agents.events.dlq --to-topic agents.events --error-code AGENT_OVERLOADED --source-consumer agents-consumer --limit 100 --purge-replayed
177
+
178
+ # Purge matching DLQ entries (preview first)
179
+ orbit dlq-purge agents.events.dlq --error-code AGENT_OVERLOADED --dry-run
180
+ orbit dlq-purge agents.events.dlq --error-code AGENT_OVERLOADED
181
+ ```
182
+
183
+ 6. View trace timeline:
184
+
185
+ ```bash
186
+ orbit trace <run-id>
187
+ ```
188
+
189
+ 7. Run a load benchmark:
190
+
191
+ ```bash
192
+ orbit bench text.upper --json @req.json --duration-s 20 --concurrency 16 --timeout-ms 1500 --retries 0
193
+ ```
194
+
195
+ Run step-ramp benchmark profile:
196
+
197
+ ```bash
198
+ orbit bench text.upper --json @req.json --duration-s 30 --concurrency 4 --ramp-to 20 --ramp-step-s 2 --ramp-step-concurrency 2
199
+ ```
200
+
201
+ 8. Monitor services live:
202
+
203
+ ```bash
204
+ orbit monitor
205
+ orbit monitor --service text --interval-ms 1000
206
+ orbit monitor --service text --once
207
+ orbit monitor --service text --alerts --alert-latency-ms 200 --alert-error-rate 0.02
208
+ orbit monitor --service text --alerts --alert-consecutive 3 --alert-cooldown-s 45
209
+ ```
210
+
211
+ 9. Start external API service:
212
+
213
+ ```bash
214
+ orbit api --host 127.0.0.1 --port 8787
215
+ ```
216
+
217
+ If `ORBIT_API_TOKEN` (or `api.authToken`) is set, pass `Authorization: Bearer <token>` or `x-orbit-token`.
218
+ `GET /healthz` and `GET /readyz` are open; API action routes and `/metrics` require auth when token auth is enabled.
219
+
220
+ ### Production Bootstrap
221
+
222
+ Generate a hardened API profile with token auth, TLS/mTLS enabled, and explicit runtime limits:
223
+
224
+ ```bash
225
+ npm run bootstrap:prod
226
+ ```
227
+
228
+ This writes `./.orbit/config.production.json`. Merge it into your active `./.orbit/config.json` (or `~/.orbit/config.json`),
229
+ install certs at `~/.orbit/tls`, then start `orbit api`.
230
+
231
+ ## Service Spec Format
232
+
233
+ ```json
234
+ {
235
+ "version": "1.0.0",
236
+ "description": "example service",
237
+ "methods": {
238
+ "methodName": {
239
+ "description": "optional",
240
+ "request_schema": {},
241
+ "response_schema": {},
242
+ "transport": "spawn|worker|http",
243
+ "command": "python3",
244
+ "args": ["script.py", "--x", "{{value}}"],
245
+ "http_endpoint": "http://127.0.0.1:9000/echo",
246
+ "http_method": "POST",
247
+ "headers": {"x-tenant":"acme"},
248
+ "timeout_ms": 5000
249
+ }
250
+ }
251
+ }
252
+ ```
253
+
254
+ `{{path.to.value}}` templates are resolved against request payload fields.
255
+
256
+ Transport notes:
257
+
258
+ - `worker` (default): keeps one process alive and exchanges JSONL messages (`{"id","payload"}` -> `{"id","ok","result|error"}`).
259
+ - `spawn`: starts a process per request.
260
+ - `http`: forwards request payload to `http_endpoint` using `http_method` (default `POST`).
261
+
262
+ Examples:
263
+
264
+ - `examples/echo.spec.json` (spawn)
265
+ - `examples/echo.worker.spec.json` (persistent worker)
266
+ - `examples/echo.http.spec.json` (external HTTP)
267
+
268
+ ## Config
269
+
270
+ ORBIT merges defaults + `~/.orbit/config.json` + `./.orbit/config.json` + env vars.
271
+
272
+ Supported keys:
273
+
274
+ ```json
275
+ {
276
+ "natsUrl": "nats://127.0.0.1:4222",
277
+ "requestTimeoutMs": 5000,
278
+ "retries": 2,
279
+ "activeContext": "default",
280
+ "kvBucket": "orbit_registry",
281
+ "objectStoreBucket": "orbit_datapacks",
282
+ "otel": {"endpoint":"http://127.0.0.1:4318/v1/traces","serviceName":"orbit-cli"},
283
+ "performance": {
284
+ "mode":"balanced",
285
+ "traceSampleRate":0.2,
286
+ "trustedLocal":false,
287
+ "traceBufferMaxEvents":5000,
288
+ "traceFlushIntervalMs":25
289
+ },
290
+ "routing": {"subjectPrefix":"orbit"},
291
+ "api": {
292
+ "authToken": "replace-with-strong-secret",
293
+ "allowedHosts": ["127.0.0.1", "localhost", "::1"],
294
+ "tls": {
295
+ "enabled": false,
296
+ "certFile": "~/.orbit/tls/server.crt",
297
+ "keyFile": "~/.orbit/tls/server.key",
298
+ "caFile": "~/.orbit/tls/ca.crt",
299
+ "requestClientCert": false,
300
+ "requireClientCert": false
301
+ }
302
+ },
303
+ "runtime": {
304
+ "serveMaxInflightGlobal":64,
305
+ "serveMaxInflightPerMethod":16,
306
+ "serveMaxQueueDepth":256,
307
+ "workerPoolSize":2,
308
+ "workerMaxPendingPerWorker":64,
309
+ "apiMaxConcurrent":128,
310
+ "apiMaxBodyBytes":1048576,
311
+ "apiRequestTimeoutMs":15000,
312
+ "agentMaxConcurrent":128,
313
+ "agentMaxRequestBytes":262144,
314
+ "publishDurableEnabled":false,
315
+ "publishDurableTimeoutMs":2500,
316
+ "callRateLimitPerSec":0,
317
+ "circuitBreakerFailureThreshold":5,
318
+ "circuitBreakerCooldownMs":10000,
319
+ "circuitBreakerHalfOpenMax":1,
320
+ "monitorMaxParallel":8,
321
+ "monitorJitterMs":200,
322
+ "monitorDownBackoffFactor":1.6,
323
+ "monitorDownBackoffMaxMs":15000
324
+ },
325
+ "agent": {"enabled":true,"socketPath":"~/.orbit/agent.sock"},
326
+ "contexts": {
327
+ "default": {"natsUrl":"nats://127.0.0.1:4222","requestTimeoutMs":5000,"retries":2},
328
+ "ci": {"natsUrl":"nats://127.0.0.1:5222","requestTimeoutMs":2000,"retries":1}
329
+ },
330
+ "logLevel": "info",
331
+ "dataDir": "~/.orbit"
332
+ }
333
+ ```
334
+
335
+ Production preset profiles for embedded-package use:
336
+
337
+ - low-noise: `examples/embedded.low-noise.config.json`
338
+ - high-throughput: `examples/embedded.high-throughput.config.json`
339
+ - shared-host (strict isolation on crowded machines): `examples/embedded.shared-host.config.json`
340
+
341
+ Quick start:
342
+
343
+ ```bash
344
+ mkdir -p ~/.orbit
345
+ cp examples/embedded.low-noise.config.json ~/.orbit/config.json
346
+ # or:
347
+ # cp examples/embedded.high-throughput.config.json ~/.orbit/config.json
348
+ # cp examples/embedded.shared-host.config.json ~/.orbit/config.json
349
+ ```
350
+
351
+ Env overrides:
352
+
353
+ - `ORBIT_NATS_URL`
354
+ - `ORBIT_TIMEOUT_MS`
355
+ - `ORBIT_RETRIES`
356
+ - `ORBIT_LOG_LEVEL`
357
+ - `ORBIT_DATA_DIR`
358
+ - `ORBIT_NATS_HOST`
359
+ - `ORBIT_NATS_PORT`
360
+ - `ORBIT_NATS_IMAGE`
361
+ - `ORBIT_NATS_CONTAINER`
362
+ - `ORBIT_CONTEXT`
363
+ - `ORBIT_KV_BUCKET`
364
+ - `ORBIT_OBJECT_BUCKET`
365
+ - `ORBIT_OTEL_ENDPOINT`
366
+ - `ORBIT_OTEL_SERVICE`
367
+ - `ORBIT_PERF_MODE` (`balanced`|`hyper`)
368
+ - `ORBIT_TRACE_SAMPLE_RATE` (`0..1`)
369
+ - `ORBIT_TRACE_BUFFER_MAX_EVENTS`
370
+ - `ORBIT_TRACE_FLUSH_INTERVAL_MS`
371
+ - `ORBIT_TRUSTED_LOCAL` (`1`)
372
+ - `ORBIT_SUBJECT_PREFIX`
373
+ - `ORBIT_API_TOKEN`
374
+ - `ORBIT_API_ALLOWED_HOSTS` (CSV, default `127.0.0.1,localhost,::1`)
375
+ - `ORBIT_API_TLS_ENABLED` (`1`|`0`)
376
+ - `ORBIT_API_TLS_CERT_FILE`
377
+ - `ORBIT_API_TLS_KEY_FILE`
378
+ - `ORBIT_API_TLS_CA_FILE`
379
+ - `ORBIT_API_TLS_REQUEST_CLIENT_CERT` (`1`|`0`)
380
+ - `ORBIT_API_TLS_REQUIRE_CLIENT_CERT` (`1`|`0`)
381
+ - `ORBIT_SERVE_MAX_INFLIGHT_GLOBAL`
382
+ - `ORBIT_SERVE_MAX_INFLIGHT_PER_METHOD`
383
+ - `ORBIT_SERVE_MAX_QUEUE_DEPTH`
384
+ - `ORBIT_WORKER_POOL_SIZE`
385
+ - `ORBIT_WORKER_MAX_PENDING`
386
+ - `ORBIT_API_MAX_CONCURRENT`
387
+ - `ORBIT_API_MAX_BODY_BYTES`
388
+ - `ORBIT_API_REQUEST_TIMEOUT_MS`
389
+ - `ORBIT_AGENT_ENABLED` (`1`|`0`)
390
+ - `ORBIT_AGENT_SOCKET`
391
+ - `ORBIT_AGENT_MAX_CONCURRENT`
392
+ - `ORBIT_AGENT_MAX_REQUEST_BYTES`
393
+ - `ORBIT_PUBLISH_DURABLE_ENABLED` (`1`|`0`)
394
+ - `ORBIT_PUBLISH_DURABLE_TIMEOUT_MS`
395
+ - `ORBIT_CALL_RATE_LIMIT_PER_SEC`
396
+ - `ORBIT_CIRCUIT_BREAKER_FAILURE_THRESHOLD`
397
+ - `ORBIT_CIRCUIT_BREAKER_COOLDOWN_MS`
398
+ - `ORBIT_CIRCUIT_BREAKER_HALF_OPEN_MAX`
399
+ - `ORBIT_MONITOR_MAX_PARALLEL`
400
+ - `ORBIT_MONITOR_JITTER_MS`
401
+ - `ORBIT_MONITOR_DOWN_BACKOFF_FACTOR`
402
+ - `ORBIT_MONITOR_DOWN_BACKOFF_MAX_MS`
403
+
404
+ ## Tracing
405
+
406
+ Each run writes JSONL events to `~/.orbit/traces/<run-id>.jsonl` with span timing, retries, and error codes.
407
+ If `ORBIT_OTEL_ENDPOINT` is set, trace events are also exported as OTLP spans over HTTP with retry/backoff.
408
+
409
+ ## Benchmarking
410
+
411
+ `orbit bench` executes concurrent request/reply calls against one method and reports:
412
+
413
+ - total requests / success / failed
414
+ - throughput (req/s)
415
+ - latency: min/avg/p50/p95/p99/max
416
+
417
+ Ramp mode lets you grow load during the run:
418
+
419
+ - `--concurrency`: starting concurrency
420
+ - `--ramp-to`: max concurrency target
421
+ - `--ramp-step-s`: seconds between increases
422
+ - `--ramp-step-concurrency`: workers added per step
423
+
424
+ Use this as a repeatable baseline before/after service or broker changes.
425
+
426
+ `orbit bench-overhead` compares direct NATS RPC against local `orbit agent` IPC+NATS path and reports Orbit-added p50/p95 latency. Start the agent in another terminal first:
427
+
428
+ ```bash
429
+ orbit agent
430
+ orbit bench-overhead text.upper --json @req.json --iterations 200
431
+ ```
432
+
433
+ ## Monitoring
434
+
435
+ `orbit monitor` emits newline-delimited JSON snapshots with:
436
+
437
+ - `service`
438
+ - `status` (`up`/`down`) from `$SRV.PING.<service>`
439
+ - `ping_latency_ms`
440
+ - `error_rate` (from `$SRV.STATS` endpoint counters when available)
441
+ - raw `$SRV.STATS.<service>` payload
442
+
443
+ Alerting options:
444
+
445
+ - `--alerts`: enable alert evaluation
446
+ - `--alert-latency-ms`: alert when ping latency exceeds threshold
447
+ - `--alert-error-rate`: alert when computed error rate exceeds threshold
448
+ - `--alert-consecutive`: require N consecutive failing checks before alerting
449
+ - `--alert-cooldown-s`: suppress repeated alert/resolve emissions for the same code within cooldown window
450
+ - `--alert-no-down`: disables default down-state alerts
451
+
452
+ When enabled, monitor emits explicit `event: "alert"` and `event: "alert_resolved"` rows.
453
+
454
+ ## Contexts
455
+
456
+ ```bash
457
+ orbit context list
458
+ orbit context set dev --nats-url nats://127.0.0.1:4222 --timeout-ms 5000 --retries 2
459
+ orbit context use dev
460
+ orbit context current
461
+ ```
462
+
463
+ ## Testing
464
+
465
+ ```bash
466
+ npm run test
467
+ ```
468
+
469
+ Tests cover:
470
+
471
+ - Envelope validation and tamper detection
472
+ - Retry/timeout behavior
473
+
474
+ ## External API + SDKs
475
+
476
+ Orbit includes two SDKs and two CLIs for external integration:
477
+
478
+ - Orbit CLI: `orbit` (bus/admin runtime)
479
+ - Python CLI: `orbit-py` (HTTP API client)
480
+ - TypeScript SDK: `sdk/typescript`
481
+ - Python SDK: `sdk/python`
482
+
483
+ API service endpoints:
484
+
485
+ - `GET /healthz`
486
+ - `GET /readyz`
487
+ - `GET /metrics` (Prometheus text)
488
+ - `POST /v1/ping`
489
+ - `POST /v1/call`
490
+ - `POST /v1/publish`
491
+ - `POST /v1/inspect`
492
+
493
+ Contract source:
494
+
495
+ - `docs/orbit-api-contract.yaml`
496
+
497
+ ## Production Deployment Notes
498
+
499
+ - Secure NATS configuration templates (TLS + auth + accounts) and 3-node cluster examples are in `examples/nats/`.
500
+ - Hardened deployment checklist is in `docs/production-hardening.md`.
501
+ - Never expose NATS monitoring port publicly; bind monitoring/admin ports to localhost or private networks only.
@@ -0,0 +1,4 @@
1
+ import { OrbitConfig } from "./types.js";
2
+ import { OrbitApiAction } from "./api_contract.js";
3
+ export declare function requestAgent(config: OrbitConfig, action: OrbitApiAction, payload: Record<string, unknown>, timeoutMs: number): Promise<unknown>;
4
+ export declare function canUseAgent(config: OrbitConfig): boolean;
@@ -0,0 +1,77 @@
1
+ import net from "node:net";
2
+ import { OrbitError } from "./errors.js";
3
+ function connectSocket(socketPath) {
4
+ return new Promise((resolve, reject) => {
5
+ const socket = net.createConnection(socketPath);
6
+ socket.once("connect", () => resolve(socket));
7
+ socket.once("error", reject);
8
+ });
9
+ }
10
+ function waitForResponse(socket, requestId, timeoutMs) {
11
+ return new Promise((resolve, reject) => {
12
+ let buf = "";
13
+ const timer = setTimeout(() => {
14
+ socket.destroy();
15
+ reject(new OrbitError("AGENT_TIMEOUT", `agent request timed out after ${timeoutMs}ms`));
16
+ }, timeoutMs);
17
+ timer.unref?.();
18
+ const onData = (chunk) => {
19
+ buf += chunk.toString("utf-8");
20
+ while (true) {
21
+ const newline = buf.indexOf("\n");
22
+ if (newline < 0)
23
+ break;
24
+ const line = buf.slice(0, newline).trim();
25
+ buf = buf.slice(newline + 1);
26
+ if (!line)
27
+ continue;
28
+ let parsed;
29
+ try {
30
+ parsed = JSON.parse(line);
31
+ }
32
+ catch {
33
+ continue;
34
+ }
35
+ if (parsed.id !== requestId)
36
+ continue;
37
+ cleanup();
38
+ socket.end();
39
+ if (!parsed.ok) {
40
+ reject(new OrbitError(parsed.error?.code ?? "AGENT_ERROR", parsed.error?.message ?? "agent request failed"));
41
+ return;
42
+ }
43
+ resolve(parsed.payload);
44
+ return;
45
+ }
46
+ };
47
+ const onError = (err) => {
48
+ cleanup();
49
+ reject(new OrbitError("AGENT_IO_ERROR", "agent socket error", { err }));
50
+ };
51
+ const onClose = () => {
52
+ cleanup();
53
+ reject(new OrbitError("AGENT_CLOSED", "agent socket closed before response"));
54
+ };
55
+ const cleanup = () => {
56
+ clearTimeout(timer);
57
+ socket.off("data", onData);
58
+ socket.off("error", onError);
59
+ socket.off("close", onClose);
60
+ };
61
+ socket.on("data", onData);
62
+ socket.once("error", onError);
63
+ socket.once("close", onClose);
64
+ });
65
+ }
66
+ export async function requestAgent(config, action, payload, timeoutMs) {
67
+ if (!config.agent.enabled)
68
+ throw new OrbitError("AGENT_DISABLED", "agent mode disabled");
69
+ const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
70
+ const socket = await connectSocket(config.agent.socketPath);
71
+ const req = { id, action, payload };
72
+ socket.write(`${JSON.stringify(req)}\n`);
73
+ return waitForResponse(socket, id, timeoutMs);
74
+ }
75
+ export function canUseAgent(config) {
76
+ return Boolean(config.agent.enabled && config.agent.socketPath);
77
+ }
@@ -0,0 +1,19 @@
1
+ export type OrbitApiAction = "call" | "publish" | "inspect" | "ping";
2
+ export interface OrbitApiRequestEnvelope {
3
+ id: string;
4
+ action: OrbitApiAction;
5
+ payload: Record<string, unknown>;
6
+ }
7
+ export interface OrbitApiResponseEnvelope {
8
+ id: string;
9
+ ok: boolean;
10
+ payload?: unknown;
11
+ error?: {
12
+ code?: string;
13
+ message?: string;
14
+ };
15
+ }
16
+ export declare function isOrbitApiAction(value: string): value is OrbitApiAction;
17
+ export declare function actionFromApiPath(pathname: string): OrbitApiAction | null;
18
+ export declare function parseObjectPayload(input: unknown): Record<string, unknown>;
19
+ export declare function validateActionPayload(action: OrbitApiAction, payload: Record<string, unknown>): void;