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.
- package/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +501 -0
- package/dist/src/agent_ipc.d.ts +4 -0
- package/dist/src/agent_ipc.js +77 -0
- package/dist/src/api_contract.d.ts +19 -0
- package/dist/src/api_contract.js +81 -0
- package/dist/src/api_http.d.ts +23 -0
- package/dist/src/api_http.js +62 -0
- package/dist/src/call_protection.d.ts +5 -0
- package/dist/src/call_protection.js +83 -0
- package/dist/src/cell/gateway.d.ts +13 -0
- package/dist/src/cell/gateway.js +171 -0
- package/dist/src/cell/routing.d.ts +18 -0
- package/dist/src/cell/routing.js +48 -0
- package/dist/src/cell/template.d.ts +7 -0
- package/dist/src/cell/template.js +24 -0
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +305 -0
- package/dist/src/commands/agent.d.ts +3 -0
- package/dist/src/commands/agent.js +187 -0
- package/dist/src/commands/api.d.ts +6 -0
- package/dist/src/commands/api.js +226 -0
- package/dist/src/commands/bench.d.ts +13 -0
- package/dist/src/commands/bench.js +125 -0
- package/dist/src/commands/bench_overhead.d.ts +8 -0
- package/dist/src/commands/bench_overhead.js +71 -0
- package/dist/src/commands/call.d.ts +10 -0
- package/dist/src/commands/call.js +45 -0
- package/dist/src/commands/cell.d.ts +3 -0
- package/dist/src/commands/cell.js +186 -0
- package/dist/src/commands/context.d.ts +9 -0
- package/dist/src/commands/context.js +71 -0
- package/dist/src/commands/dlq_inspect.d.ts +11 -0
- package/dist/src/commands/dlq_inspect.js +86 -0
- package/dist/src/commands/dlq_purge.d.ts +12 -0
- package/dist/src/commands/dlq_purge.js +84 -0
- package/dist/src/commands/dlq_replay.d.ts +15 -0
- package/dist/src/commands/dlq_replay.js +127 -0
- package/dist/src/commands/inspect.d.ts +6 -0
- package/dist/src/commands/inspect.js +57 -0
- package/dist/src/commands/monitor.d.ts +32 -0
- package/dist/src/commands/monitor.js +201 -0
- package/dist/src/commands/publish.d.ts +10 -0
- package/dist/src/commands/publish.js +64 -0
- package/dist/src/commands/serve.d.ts +8 -0
- package/dist/src/commands/serve.js +258 -0
- package/dist/src/commands/subscribe.d.ts +11 -0
- package/dist/src/commands/subscribe.js +78 -0
- package/dist/src/commands/trace.d.ts +5 -0
- package/dist/src/commands/trace.js +26 -0
- package/dist/src/commands/up.d.ts +3 -0
- package/dist/src/commands/up.js +91 -0
- package/dist/src/config.d.ts +6 -0
- package/dist/src/config.js +281 -0
- package/dist/src/dlq.d.ts +20 -0
- package/dist/src/dlq.js +71 -0
- package/dist/src/echo/benchmark.d.ts +10 -0
- package/dist/src/echo/benchmark.js +105 -0
- package/dist/src/echo/bus.d.ts +22 -0
- package/dist/src/echo/bus.js +89 -0
- package/dist/src/echo/cli.d.ts +1 -0
- package/dist/src/echo/cli.js +135 -0
- package/dist/src/echo/client.d.ts +12 -0
- package/dist/src/echo/client.js +46 -0
- package/dist/src/echo/daemon.d.ts +8 -0
- package/dist/src/echo/daemon.js +181 -0
- package/dist/src/echo/index.d.ts +6 -0
- package/dist/src/echo/index.js +5 -0
- package/dist/src/echo/ring_buffer.d.ts +27 -0
- package/dist/src/echo/ring_buffer.js +73 -0
- package/dist/src/echo/types.d.ts +27 -0
- package/dist/src/echo/types.js +1 -0
- package/dist/src/echocore.d.ts +2 -0
- package/dist/src/echocore.js +6 -0
- package/dist/src/envelope.d.ts +14 -0
- package/dist/src/envelope.js +92 -0
- package/dist/src/errors.d.ts +5 -0
- package/dist/src/errors.js +9 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +12 -0
- package/dist/src/jetstream_durable.d.ts +4 -0
- package/dist/src/jetstream_durable.js +51 -0
- package/dist/src/json_schema.d.ts +6 -0
- package/dist/src/json_schema.js +154 -0
- package/dist/src/logger.d.ts +16 -0
- package/dist/src/logger.js +31 -0
- package/dist/src/metrics.d.ts +6 -0
- package/dist/src/metrics.js +95 -0
- package/dist/src/nats.d.ts +16 -0
- package/dist/src/nats.js +129 -0
- package/dist/src/orbit_actions.d.ts +4 -0
- package/dist/src/orbit_actions.js +129 -0
- package/dist/src/otel.d.ts +2 -0
- package/dist/src/otel.js +96 -0
- package/dist/src/registry.d.ts +10 -0
- package/dist/src/registry.js +57 -0
- package/dist/src/retry.d.ts +9 -0
- package/dist/src/retry.js +32 -0
- package/dist/src/rpc_call.d.ts +22 -0
- package/dist/src/rpc_call.js +119 -0
- package/dist/src/service_adapter.d.ts +7 -0
- package/dist/src/service_adapter.js +163 -0
- package/dist/src/spec.d.ts +2 -0
- package/dist/src/spec.js +30 -0
- package/dist/src/subjects.d.ts +2 -0
- package/dist/src/subjects.js +4 -0
- package/dist/src/trace.d.ts +4 -0
- package/dist/src/trace.js +86 -0
- package/dist/src/types.d.ts +133 -0
- package/dist/src/types.js +1 -0
- package/dist/src/util.d.ts +10 -0
- package/dist/src/util.js +63 -0
- package/dist/src/worker_pool.d.ts +9 -0
- package/dist/src/worker_pool.js +163 -0
- package/docs/orbit-api-contract.yaml +376 -0
- 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;
|