@continuonai/rcan-ts 0.5.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -182
- package/dist/browser.d.mts +337 -16
- package/dist/browser.mjs +758 -16
- package/dist/browser.mjs.map +1 -1
- package/dist/index.d.mts +337 -16
- package/dist/index.d.ts +337 -16
- package/dist/index.js +798 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +756 -16
- package/dist/index.mjs.map +1 -1
- package/dist/rcan-validate.js +17 -2
- package/dist/rcan.iife.js +3 -3
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,62 +1,50 @@
|
|
|
1
1
|
# rcan-ts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript SDK for the [RCAN protocol](https://rcan.dev/spec/) — build robots that communicate securely, audit every action, and enforce safety gates in Node.js or the browser.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@continuonai/rcan-ts)
|
|
6
|
+
[](https://rcan.dev/spec/)
|
|
6
7
|
[](https://github.com/continuonai/rcan-ts/actions)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @continuonai/rcan-ts@0.6.0
|
|
10
15
|
```
|
|
11
16
|
|
|
12
|
-
|
|
17
|
+
Node 18+ required (uses Web Crypto API for `transport.encodeMinimal`).
|
|
13
18
|
|
|
14
|
-
###
|
|
19
|
+
### Browser / CDN (no build step)
|
|
15
20
|
|
|
16
21
|
```html
|
|
17
22
|
<script src="https://unpkg.com/@continuonai/rcan-ts/dist/rcan.iife.js"></script>
|
|
18
23
|
<script>
|
|
19
|
-
const uri =
|
|
24
|
+
const uri = RCAN.RobotURI.parse('rcan://registry.rcan.dev/acme/arm/v1/unit-001');
|
|
20
25
|
console.log(uri.manufacturer); // acme
|
|
21
26
|
</script>
|
|
22
27
|
```
|
|
23
28
|
|
|
24
|
-
Also available via jsDelivr:
|
|
25
|
-
```html
|
|
26
|
-
<script src="https://cdn.jsdelivr.net/npm/@continuonai/rcan-ts/dist/rcan.iife.js"></script>
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
29
|
## Quick Start
|
|
32
30
|
|
|
33
|
-
### Robot URI
|
|
34
|
-
|
|
35
|
-
Every robot has a globally unique, resolvable address:
|
|
36
|
-
|
|
37
31
|
```typescript
|
|
38
|
-
import { RobotURI } from "@continuonai/rcan-ts";
|
|
32
|
+
import { RobotURI, RCANMessage, ConfidenceGate } from "@continuonai/rcan-ts";
|
|
33
|
+
import { ReplayCache } from "@continuonai/rcan-ts";
|
|
34
|
+
import { AuditChain } from "@continuonai/rcan-ts";
|
|
39
35
|
|
|
36
|
+
// 1. Address a robot
|
|
40
37
|
const uri = RobotURI.build({
|
|
41
38
|
manufacturer: "acme",
|
|
42
|
-
model: "
|
|
39
|
+
model: "arm",
|
|
43
40
|
version: "v2",
|
|
44
41
|
deviceId: "unit-001",
|
|
45
42
|
});
|
|
46
|
-
|
|
47
|
-
// rcan://registry.rcan.dev/acme/robotarm/v2/unit-001
|
|
48
|
-
|
|
49
|
-
const parsed = RobotURI.parse("rcan://registry.rcan.dev/acme/robotarm/v2/unit-001");
|
|
50
|
-
console.log(parsed.namespace); // "acme/robotarm"
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Building a Message
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { RCANMessage, ConfidenceGate } from "@continuonai/rcan-ts";
|
|
43
|
+
// rcan://registry.rcan.dev/acme/arm/v2/unit-001
|
|
57
44
|
|
|
45
|
+
// 2. Gate on AI confidence before acting
|
|
58
46
|
const gate = new ConfidenceGate(0.8);
|
|
59
|
-
const confidence = 0.91;
|
|
47
|
+
const confidence = 0.91;
|
|
60
48
|
|
|
61
49
|
if (gate.allows(confidence)) {
|
|
62
50
|
const msg = new RCANMessage({
|
|
@@ -64,190 +52,110 @@ if (gate.allows(confidence)) {
|
|
|
64
52
|
target: uri,
|
|
65
53
|
params: { distance_m: 1.0 },
|
|
66
54
|
confidence,
|
|
67
|
-
modelIdentity: "
|
|
55
|
+
modelIdentity: "gemini-2.5-flash",
|
|
68
56
|
});
|
|
69
|
-
console.log(msg.toJSONString(2));
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Human-in-the-Loop Gate
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
import { HiTLGate } from "@continuonai/rcan-ts";
|
|
77
57
|
|
|
78
|
-
|
|
79
|
-
const
|
|
58
|
+
// 3. Replay attack prevention
|
|
59
|
+
const cache = new ReplayCache({ windowSeconds: 300 });
|
|
60
|
+
if (cache.isReplay(msg.msgId)) throw new Error("Replay attack detected");
|
|
61
|
+
cache.record(msg.msgId);
|
|
80
62
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
63
|
+
// 4. ESTOP with QoS 2 (EXACTLY_ONCE) — never dropped
|
|
64
|
+
const estop = new RCANMessage({
|
|
65
|
+
cmd: "estop",
|
|
66
|
+
target: uri,
|
|
67
|
+
qos: 2, // QoSLevel.EXACTLY_ONCE
|
|
68
|
+
});
|
|
86
69
|
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Tamper-Evident Audit Chain
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
import { AuditChain } from "@continuonai/rcan-ts";
|
|
93
70
|
|
|
71
|
+
// 5. Tamper-evident audit chain
|
|
94
72
|
const chain = new AuditChain("your-hmac-secret");
|
|
95
|
-
|
|
96
73
|
chain.append({
|
|
97
74
|
action: "move_forward",
|
|
98
75
|
robotUri: uri.toString(),
|
|
99
76
|
confidence: 0.91,
|
|
100
77
|
safetyApproved: true,
|
|
101
78
|
});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const { valid, count, errors } = chain.verifyAll();
|
|
79
|
+
const { valid, count } = chain.verifyAll();
|
|
105
80
|
console.log(`Chain valid: ${valid}, ${count} records`);
|
|
106
81
|
|
|
107
|
-
// Export
|
|
82
|
+
// Export as JSONL for long-term storage
|
|
108
83
|
const jsonl = chain.toJSONL();
|
|
109
84
|
```
|
|
110
85
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
|
134
|
-
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
| `validateConfigAgainstSchema` | Validate a config object against the live JSON schema |
|
|
147
|
-
| `validateNodeAgainstSchema` | Validate a node manifest against the node schema |
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## Distributed Registry Nodes (§17)
|
|
152
|
-
|
|
153
|
-
RCAN v1.2 §17 introduces a federated registry network. `NodeClient` resolves RRNs from any node — root or delegated authoritative.
|
|
86
|
+
## What's in v0.6.0
|
|
87
|
+
|
|
88
|
+
| Module | Description |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `message` | Core `RCANMessage` envelope with all v1.6 fields |
|
|
91
|
+
| `address` | `RobotURI` — parse, build, and validate RCAN robot addresses |
|
|
92
|
+
| `audit` | `AuditChain` + `CommitmentRecord` — tamper-evident HMAC-chained logs |
|
|
93
|
+
| `gates` | `ConfidenceGate`, `HiTLGate` — safety gates for AI-driven actions |
|
|
94
|
+
| `replay` | `ReplayCache` — sliding-window replay attack prevention (GAP-03) |
|
|
95
|
+
| `clock` | `ClockSyncStatus` — NTP clock sync verification (GAP-04) |
|
|
96
|
+
| `qos` | `QoSLevel` — FIRE_AND_FORGET / ACKNOWLEDGED / EXACTLY_ONCE (GAP-11) |
|
|
97
|
+
| `consent` | Consent wire protocol — request/grant/deny (GAP-05) |
|
|
98
|
+
| `revocation` | Robot identity revocation with TTL cache (GAP-02) |
|
|
99
|
+
| `trainingConsent` | Training data consent, GDPR/EU AI Act Annex III §5 (GAP-10) |
|
|
100
|
+
| `delegation` | Command delegation chain, max 4 hops, Ed25519-signed (GAP-01) |
|
|
101
|
+
| `offline` | Offline operation mode — ESTOP always allowed (GAP-06) |
|
|
102
|
+
| `faultReport` | `FaultCode` structured fault taxonomy (GAP-20) |
|
|
103
|
+
| `federation` | Federated consent — cross-registry trust, DNS discovery (GAP-16) |
|
|
104
|
+
| `transport` | Constrained transports — compact CBOR, 32-byte ESTOP minimal, BLE (GAP-17) |
|
|
105
|
+
| `multimodal` | Multi-modal payloads — inline/ref media, streaming (GAP-18) |
|
|
106
|
+
| `identity` | Level of Assurance — LoA policies, JWT parsing (GAP-14) |
|
|
107
|
+
| `keys` | Key rotation with JWKS-compatible `KeyStore` (GAP-09) |
|
|
108
|
+
| `configUpdate` | `CONFIG_UPDATE` protocol with safety scope enforcement (GAP-07) |
|
|
109
|
+
| `node` | `NodeClient` — resolve RRNs across federated registry nodes (§17) |
|
|
110
|
+
| `validate` | L1/L2/L3 conformance validation for configs, messages, URIs |
|
|
111
|
+
| `schema` | Canonical JSON schema validation against rcan.dev |
|
|
112
|
+
|
|
113
|
+
## Protocol 66 Compliance
|
|
114
|
+
|
|
115
|
+
- **ESTOP always delivered** — send with `qos: 2` (`EXACTLY_ONCE`); never blocked
|
|
116
|
+
- **Local safety wins** — `OfflineMode` enforces limits without cloud connectivity
|
|
117
|
+
- **Confidence gates run locally** — `ConfidenceGate` makes no network calls
|
|
118
|
+
- **Audit chain required** — `AuditChain.verifyAll()` before executing flagged commands
|
|
119
|
+
|
|
120
|
+
## Registry Resolution
|
|
154
121
|
|
|
155
122
|
```typescript
|
|
156
|
-
import { NodeClient } from
|
|
123
|
+
import { NodeClient } from "@continuonai/rcan-ts";
|
|
157
124
|
|
|
158
125
|
const client = new NodeClient();
|
|
159
126
|
|
|
160
127
|
// Resolve an RRN across the federation
|
|
161
|
-
const result = await client.resolve(
|
|
162
|
-
console.log(
|
|
163
|
-
console.log(
|
|
164
|
-
|
|
165
|
-
// Discover the authoritative node for a namespace
|
|
166
|
-
const node = await client.discover('RRN-BD-000000000001');
|
|
167
|
-
console.log(`Authoritative: ${node.operator} at ${node.api_base}`);
|
|
168
|
-
|
|
169
|
-
// List all known registry nodes
|
|
170
|
-
const nodes = await client.listNodes();
|
|
171
|
-
nodes.forEach(n => console.log(`${n.operator}: ${n.namespace_prefix}`));
|
|
172
|
-
|
|
173
|
-
// Verify a node manifest
|
|
174
|
-
const manifest = await client.getNodeManifest('https://registry.example.com');
|
|
175
|
-
const isValid = client.verifyNode(manifest);
|
|
176
|
-
|
|
177
|
-
// Error handling
|
|
178
|
-
import { RCANNodeNotFoundError, RCANNodeTrustError } from '@continuonai/rcan-ts';
|
|
179
|
-
try {
|
|
180
|
-
const result = await client.resolve('RRN-UNKNOWN-000000000001');
|
|
181
|
-
} catch (e) {
|
|
182
|
-
if (e instanceof RCANNodeNotFoundError) {
|
|
183
|
-
console.log(`Not found: ${e.rrn}`);
|
|
184
|
-
} else if (e instanceof RCANNodeTrustError) {
|
|
185
|
-
console.log(`Trust failure: ${e.reason}`);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
```
|
|
128
|
+
const result = await client.resolve("RRN-000000000001");
|
|
129
|
+
console.log(result.record.name); // "Bob"
|
|
130
|
+
console.log(result.record.verification_tier); // "verified"
|
|
189
131
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Root namespace: RRN-000000000001 (12-digit recommended, 8-digit still valid)
|
|
194
|
-
Delegated: RRN-BD-000000000001 (prefix 2-8 alphanumeric chars)
|
|
195
|
-
Legacy (valid): RRN-00000001 (8-digit, backward compatible)
|
|
132
|
+
// Discover which node is authoritative
|
|
133
|
+
const node = await client.discover("RRN-BD-000000000001");
|
|
134
|
+
console.log(node.operator); // "Boston Dynamics, Inc."
|
|
196
135
|
```
|
|
197
136
|
|
|
198
|
-
##
|
|
199
|
-
|
|
200
|
-
Validate configs against the canonical JSON schema published at rcan.dev:
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
import { validateConfigAgainstSchema, validateNodeAgainstSchema } from '@continuonai/rcan-ts';
|
|
204
|
-
|
|
205
|
-
// Validate a RCAN config against the canonical schema from rcan.dev
|
|
206
|
-
const result = await validateConfigAgainstSchema(myConfig);
|
|
207
|
-
if (!result.valid) {
|
|
208
|
-
console.error('Config invalid:', result.errors);
|
|
209
|
-
} else if (result.skipped) {
|
|
210
|
-
console.warn('Schema validation skipped (rcan.dev unreachable)');
|
|
211
|
-
}
|
|
137
|
+
## Spec Compliance
|
|
212
138
|
|
|
213
|
-
|
|
214
|
-
const nodeResult = await validateNodeAgainstSchema(manifest);
|
|
215
|
-
```
|
|
139
|
+
Implements [RCAN v1.6](https://rcan.dev/spec/) — 405 tests, 0 skipped.
|
|
216
140
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
```html
|
|
220
|
-
<script src="https://unpkg.com/@continuonai/rcan-ts/dist/rcan.iife.js"></script>
|
|
221
|
-
<script>
|
|
222
|
-
const { validateConfig, NodeClient } = window.RCAN;
|
|
223
|
-
const client = new NodeClient();
|
|
224
|
-
client.resolve('RRN-000000000001').then(r => console.log(r));
|
|
225
|
-
</script>
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
---
|
|
141
|
+
API surface is intentionally identical to rcan-py: `RobotURI`, `RCANMessage`, `ConfidenceGate`, `HiTLGate`, `AuditChain`, and `validateConfig` work the same way in both languages.
|
|
229
142
|
|
|
230
143
|
## Ecosystem
|
|
231
144
|
|
|
232
|
-
| Package |
|
|
233
|
-
|
|
234
|
-
| [rcan-py](https://github.com/continuonai/rcan-py) |
|
|
235
|
-
| **rcan-ts** (this) |
|
|
236
|
-
| [
|
|
237
|
-
|
|
238
|
-
|
|
145
|
+
| Package | Version | Purpose |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| [rcan-py](https://github.com/continuonai/rcan-py) | v0.6.0 | Python SDK |
|
|
148
|
+
| **rcan-ts** (this) | v0.6.0 | TypeScript SDK |
|
|
149
|
+
| [rcan-spec](https://github.com/continuonai/rcan-spec) | v1.6.0 | Protocol spec |
|
|
150
|
+
| [OpenCastor](https://github.com/craigm26/OpenCastor) | v2026.3.17.1 | Robot runtime (reference impl) |
|
|
151
|
+
| [RRF](https://robotregistryfoundation.org) | v1.6.0 | Robot identity registry |
|
|
152
|
+
| [Fleet UI](https://app.opencastor.com) | live | Web fleet dashboard |
|
|
239
153
|
|
|
240
|
-
##
|
|
154
|
+
## Contributing
|
|
241
155
|
|
|
242
|
-
|
|
243
|
-
- 📖 [RCAN Spec v1.5](https://rcan.dev/spec) — full protocol specification
|
|
244
|
-
- 🌐 [rcan.dev](https://rcan.dev) — robot registry and documentation
|
|
245
|
-
- 🐍 [rcan-py](https://github.com/continuonai/rcan-py) — Python SDK
|
|
246
|
-
- 🤖 [OpenCastor](https://github.com/craigm26/OpenCastor) — Python robot runtime (RCAN reference implementation)
|
|
247
|
-
- 🖥️ [OpenCastor Fleet UI](https://app.opencastor.com) — Flutter web app for remote fleet management (uses rcan-ts for message construction)
|
|
248
|
-
- 🏛️ [Robot Registry Foundation](https://robotregistryfoundation.org) — global robot identity registry
|
|
156
|
+
Issues and PRs welcome at [github.com/continuonai/rcan-ts](https://github.com/continuonai/rcan-ts).
|
|
249
157
|
|
|
250
|
-
|
|
158
|
+
Spec discussions: [github.com/continuonai/rcan-spec/issues](https://github.com/continuonai/rcan-spec/issues)
|
|
251
159
|
|
|
252
160
|
## License
|
|
253
161
|
|