@robota-sdk/agent-transport-webrtc 3.0.0-beta.82 → 3.0.0-beta.83
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 +496 -0
- package/README.md +105 -0
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.cts +402 -16
- package/dist/node/index.d.cts.map +1 -1
- package/dist/node/index.d.ts +402 -16
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +13 -10
- package/src/__tests__/device-mesh-relay.test.ts +439 -0
- package/src/__tests__/device-mesh.test.ts +99 -0
- package/src/__tests__/discovering-mesh-relay.test.ts +234 -0
- package/src/__tests__/enrollment-link.test.ts +103 -0
- package/src/__tests__/fake-datachannel.ts +4 -1
- package/src/__tests__/mesh-discovery.test.ts +100 -6
- package/src/__tests__/mesh-internet.test.ts +160 -4
- package/src/__tests__/mesh-turn-relay.test.ts +159 -0
- package/src/__tests__/nostr-source-condition.test.ts +27 -0
- package/src/__tests__/rtc-channel-close.test.ts +52 -0
- package/src/__tests__/rtc-peer-candidates.test.ts +61 -0
- package/src/__tests__/turn-server-bind.test.ts +59 -0
- package/src/__tests__/turn-server.test.ts +335 -0
- package/src/__tests__/turn-test-client.ts +256 -0
- package/src/__tests__/webrtc-transport.test.ts +2 -1
- package/src/device-mesh-node.ts +294 -14
- package/src/discovering-mesh-relay.ts +96 -38
- package/src/enrollment-link.ts +465 -0
- package/src/index.ts +32 -0
- package/src/mesh-dht.ts +140 -23
- package/src/mesh-mdns.ts +24 -4
- package/src/mesh-peer-link.ts +19 -7
- package/src/mesh-records.ts +60 -13
- package/src/mesh-turn-relay.ts +238 -0
- package/src/rtc-peer.ts +45 -7
- package/src/stun-message.ts +341 -0
- package/src/turn-server.ts +1028 -0
- package/src/webrtc-transport-options.ts +7 -3
- package/src/webrtc-transport.ts +5 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
# @robota-sdk/agent-transport-webrtc
|
|
2
|
+
|
|
3
|
+
## 3.0.0-beta.83
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 28fa8a7: `/devices add` and `/devices join` enrol a new device into your devices.
|
|
8
|
+
|
|
9
|
+
- On a device that holds the signing key, `/devices add` shows a one-time code on the terminal. It
|
|
10
|
+
works once, for five minutes.
|
|
11
|
+
- On the new device, `/devices join [name]` asks for the code on the terminal, creates the device's
|
|
12
|
+
keys, and meets the other device through the signaling relay in
|
|
13
|
+
`transports.webrtc.options.relayUrl`. Both devices need that setting.
|
|
14
|
+
- The new device proves the code over the WebRTC connection's DTLS fingerprints before anything else
|
|
15
|
+
crosses, so a relay in the middle cannot enrol anyone.
|
|
16
|
+
- Both devices then show the same six digits, and both operators compare them and confirm. Someone
|
|
17
|
+
who saw the code cannot choose the digits: the new device commits to its part before it sees the
|
|
18
|
+
existing device's.
|
|
19
|
+
- Only when both operators say yes does the existing device certify the new one and issue a new
|
|
20
|
+
roster. The new device keeps its identity only once the chain it receives verifies.
|
|
21
|
+
- A wrong, expired or already used code is refused. So is a code after a few failed attempts. When
|
|
22
|
+
either operator declines, nothing is issued or kept.
|
|
23
|
+
- The code appears only on the two terminals. It never reaches history, transcripts or the model. A
|
|
24
|
+
code typed as a command argument is refused. Both commands stay user-only and refuse remote
|
|
25
|
+
surfaces.
|
|
26
|
+
- `agent-remote-pairing`: enrollment codes, the enrollment proof, the signed request with its
|
|
27
|
+
commitment, the short authentication string and the frame decoder.
|
|
28
|
+
- `agent-transport-webrtc`: `dialEnrollment` and `listenForEnrollment` provide a data channel bound to
|
|
29
|
+
the negotiated fingerprints.
|
|
30
|
+
|
|
31
|
+
- 963a4e0: Where no direct path joins two of one user's devices, a TURN relay on one of the user's own devices carries the
|
|
32
|
+
connection.
|
|
33
|
+
|
|
34
|
+
- `agent-transport-webrtc` — `TurnServer`, a pure-JavaScript TURN server over UDP (Allocate, Refresh,
|
|
35
|
+
CreatePermission, ChannelBind, Send/Data indications, ChannelData, long-term credentials) with quotas for
|
|
36
|
+
allocations per owner and in all, relayed bytes per second per owner, and allocation lifetime, and an optional
|
|
37
|
+
relayed-port range for a relay behind a NAT. Only what MESSAGE-INTEGRITY covers is read. Requests nobody has
|
|
38
|
+
authenticated are answered at a limited rate (per source and in all) and never with more bytes than they
|
|
39
|
+
carried, and forwarding into private ranges can be turned off (`allowPrivatePeers`). `MeshTurnRelay` runs it
|
|
40
|
+
for the devices of the roster: each pair derives a short-lived credential of its own (`meshRelayCredential`), a
|
|
41
|
+
device the lists drop or revoke can no longer allocate and loses its allocations. `DeviceMeshNode` takes
|
|
42
|
+
`relays` (the relays paired devices advertise, then configured TURN servers, and `relayOnly`) and
|
|
43
|
+
`relayServer`; a connection that needs a relay and has none is refused with `MeshRelayNeededError`, which says
|
|
44
|
+
why a relay was needed and carries the direct attempt's failure. `MeshDht` publishes this device's relay
|
|
45
|
+
endpoints in the sealed hints records (`relayEndpoints`) and reads the peers' (`relayAdverts`). DTLS stays end
|
|
46
|
+
to end; the relay only forwards it.
|
|
47
|
+
- `agent-remote-pairing` — the pair rendezvous derives a `relay-user` tag and `relayPassword`, the relay
|
|
48
|
+
credential's password for one direction and username.
|
|
49
|
+
- `agent-cli` — `transports.mesh.options` takes `relay` (`serve`, `port`, `host`, `publicAddress`, `relayPorts`,
|
|
50
|
+
`allowPrivatePeers`), `turnServers` and `relayOnly`. When the mesh is on, the session's mesh runs the relay,
|
|
51
|
+
advertises it to paired devices only, and uses the fallback order; `/devices` names the relays, and a device
|
|
52
|
+
that needs a relay is reported once with why. Running a relay, or relay-only without TURN servers, needs the
|
|
53
|
+
DHT or pkarr relays, which carry a relay's address to the other devices; a setting that could not work is
|
|
54
|
+
refused, naming it.
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- 6e4c6ee: Device mesh discovery holds up better against endpoints and peers that misbehave.
|
|
59
|
+
|
|
60
|
+
- A direct path's admission deadline starts only from a `hello`, `offer` or `answer`, so ICE candidates
|
|
61
|
+
that trail an admitted connection no longer set a working path aside.
|
|
62
|
+
- An endpoint or signaling carrier that keeps failing is set aside for longer each time it fails
|
|
63
|
+
again, until a connection over it is admitted.
|
|
64
|
+
- An mDNS lookup keeps listening briefly after the first matching answer, so a faster answer from
|
|
65
|
+
another host cannot hide the peer's own.
|
|
66
|
+
- The device lists read from public records are bounded per paired device, each device's newest
|
|
67
|
+
before any device's next, and the chunks of one list carry a shared version, so a read that finds
|
|
68
|
+
chunks of two versions yields no list. Records written before this change are not read as lists.
|
|
69
|
+
- CLI processes that share `~/.robota/devices/address-cache.json` apply each change to the file as it
|
|
70
|
+
is on disk, so one process no longer overwrites what another learned.
|
|
71
|
+
|
|
72
|
+
- 4241fc5: Device mesh follow-ups.
|
|
73
|
+
|
|
74
|
+
- A device with no identity is pointed to `/devices add` on one of the user's devices and `/devices join`
|
|
75
|
+
here, as well as to `/devices init`. The message says that `init` is for the first device only,
|
|
76
|
+
because it creates a separate identity that can never link to the user's other devices. The `/devices`
|
|
77
|
+
description and the `init` subcommand say the same.
|
|
78
|
+
- An identity created mid-session (`/devices init`, or a successful `/devices join`) opens the mesh
|
|
79
|
+
without a restart when `transports.mesh.enabled` is on.
|
|
80
|
+
- If a session stalls for longer than the mesh lock's stale window (for example while the machine
|
|
81
|
+
sleeps), another session can take the mesh over. The stalled session now notices this on its next
|
|
82
|
+
lock refresh, closes its own mesh, and says why. Two sessions no longer run it together.
|
|
83
|
+
`holdExclusiveFileLock` has a new `onLost` option for this.
|
|
84
|
+
- `/peers` and `/handoff` still list and reach linked mesh devices when local same-host peer discovery
|
|
85
|
+
fails. `/peers` says why sessions on this host are not listed. `ICommandLocalPeersAdapter` has a new
|
|
86
|
+
optional `localDiscoveryOff` field for this.
|
|
87
|
+
- `agent-transport-webrtc`: when lists become newer on a node (reissued, revoked, or adopted from a
|
|
88
|
+
peer), the node sends them over every admitted connection instead of waiting for the next handshake.
|
|
89
|
+
Reissues, revocations and enrolments in the CLI take effect this way at once. A receiver adopts a
|
|
90
|
+
pushed list only if it is newer, issued by this user's signing key, and verifies. The push does not
|
|
91
|
+
depend on the peer's capabilities, and it never reaches the application's message handlers.
|
|
92
|
+
|
|
93
|
+
- 05d5391: The embedded TURN relay says why it cannot listen — the port is taken, the host is not an address of
|
|
94
|
+
this machine, or the port needs privileges — and keeps the bind error as `cause`; the CLI names the
|
|
95
|
+
relay setting that cause asks you to change. Mesh options are no longer checked while
|
|
96
|
+
`transports.mesh.enabled` is off, so a mistake in them does not report that the mesh could not start.
|
|
97
|
+
- 598b180: A WebRTC peer hands out its own ICE candidates only once it holds the remote description, so an answerer can no
|
|
98
|
+
longer reach the offerer while the answer is still being applied and have its certificate refused. A data channel
|
|
99
|
+
closed right after a send no longer loses that send: it reads closed at once and its stream is reset after a
|
|
100
|
+
grace. A LAN probe answered with anything but a valid proof is dropped at once.
|
|
101
|
+
- 57280bf: Every published package now declares `"engines": { "node": ">=22.12.0" }`. Before, 27 of the 38
|
|
102
|
+
packages declared no floor (`agent-core`, `agent-tools` and every provider among them),
|
|
103
|
+
`agent-session` and `agent-file-authority` declared `>=20.19.0`, and the other nine declared
|
|
104
|
+
`>=22.0.0`, so a consumer on Node 20 saw at most a warning from a transitive dependency.
|
|
105
|
+
|
|
106
|
+
Why 22.12: `agent-cli` and `agent-ui-terminal` need Node 22 through `ink` 7, and the CommonJS entries
|
|
107
|
+
of `agent-tools` and its dependents, `agent-transport`/`node` and its dependents, and
|
|
108
|
+
`agent-ui-terminal` `require()` ESM-only dependencies (`p-limit`, `jose`, `chalk`), which Node 22
|
|
109
|
+
supports unflagged only from 22.12. `engines` is advisory unless the consumer enables `engine-strict`.
|
|
110
|
+
|
|
111
|
+
No code changes: `tsdown` now reads `node22.12.0` as its build target from the field.
|
|
112
|
+
|
|
113
|
+
- 18c0d5c: Every published package now exports `./package.json`, so `require('<package>/package.json')` and
|
|
114
|
+
`import('<package>/package.json', { with: { type: 'json' } })` work instead of failing with
|
|
115
|
+
`ERR_PACKAGE_PATH_NOT_EXPORTED`, and each tarball now ships the package's `CHANGELOG.md`.
|
|
116
|
+
- Updated dependencies [724fabb]
|
|
117
|
+
- Updated dependencies [d877de2]
|
|
118
|
+
- Updated dependencies [d61e159]
|
|
119
|
+
- Updated dependencies [bfe8ed5]
|
|
120
|
+
- Updated dependencies [28fa8a7]
|
|
121
|
+
- Updated dependencies [963a4e0]
|
|
122
|
+
- Updated dependencies [7b72344]
|
|
123
|
+
- Updated dependencies [6ae3f28]
|
|
124
|
+
- Updated dependencies [57f57f5]
|
|
125
|
+
- Updated dependencies [ba822c1]
|
|
126
|
+
- Updated dependencies [6e6b06b]
|
|
127
|
+
- Updated dependencies [57280bf]
|
|
128
|
+
- Updated dependencies [18c0d5c]
|
|
129
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.83
|
|
130
|
+
- @robota-sdk/agent-transport@3.0.0-beta.83
|
|
131
|
+
- @robota-sdk/agent-remote-pairing@3.0.0-beta.83
|
|
132
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.83
|
|
133
|
+
- @robota-sdk/agent-interface-session-mobility@3.0.0-beta.83
|
|
134
|
+
|
|
135
|
+
## 3.0.0-beta.82
|
|
136
|
+
|
|
137
|
+
### Minor Changes
|
|
138
|
+
|
|
139
|
+
- e15e22b: Two of one user's devices can connect to each other over WebRTC, admitted by the device handshake.
|
|
140
|
+
|
|
141
|
+
- `agent-transport-webrtc` — `DeviceMeshNode` keeps one connection per device pair, in either role: the
|
|
142
|
+
device with the lower id offers and the other answers, so concurrent attempts resolve by rule. Both
|
|
143
|
+
roles bind the handshake to the certificate the DTLS layer verified and take one remote description
|
|
144
|
+
with exactly one fingerprint; before admission only handshake frames cross, and a connection counts
|
|
145
|
+
as admitted only once both sides admitted each other. Each connection gets its own DTLS certificate
|
|
146
|
+
(`createDtlsKeys`). A new attempt never displaces an admitted connection until it is admitted
|
|
147
|
+
itself, and attempts per pair are paced. Lists adopted in a handshake, or handed over through
|
|
148
|
+
`refresh`, apply at once, and a device they revoke loses its connection. Signaling runs through
|
|
149
|
+
`IMeshRelay`: `WsMeshRelayClient` for the self-hosted relay's `presence` / `message` frames
|
|
150
|
+
(topics capped per source and relay-wide), `createInMemoryMeshRelayHub` for tests.
|
|
151
|
+
- `agent-remote-pairing` — the pair's two relay inbox topics, one per direction, come from their
|
|
152
|
+
pairwise secret (`derivePairRendezvous(...).relayInbox()`).
|
|
153
|
+
- `agent-cli` — a device holding the signing key reissues its roster and revocation list before they
|
|
154
|
+
expire while an interactive session runs. The host can open this device's mesh endpoint from the
|
|
155
|
+
identity under `~/.robota/devices`, saving newer lists a peer hands over; no command starts it yet.
|
|
156
|
+
- `agent-command` — `/devices add|join` still reports that enrolment is not available yet.
|
|
157
|
+
|
|
158
|
+
- e15e22b: The Node WebRTC transport runs on `node-datachannel` (libdatachannel, DTLS by OpenSSL). The DTLS stack
|
|
159
|
+
verifies handshake signatures, so a channel binding names the party that holds the certificate's key.
|
|
160
|
+
It is an optional dependency with a prebuilt binary per platform; where it cannot load, the WebRTC
|
|
161
|
+
transport reports itself unavailable instead of falling back to another implementation.
|
|
162
|
+
|
|
163
|
+
- `agent-transport-webrtc` — `RtcPeer` / `RtcChannel` wrap one connection; `loadDataChannel` replaces
|
|
164
|
+
`loadWerift` (and the `loadWerift` option becomes `loadDataChannel`). Every connection has its own DTLS
|
|
165
|
+
certificate, no ICE server is contacted unless configured, and `werift` is no longer a peer dependency.
|
|
166
|
+
A pairing peer that finishes the handshake first and speaks at once no longer has that frame dropped: it
|
|
167
|
+
is held until this side accepts, and discarded if it does not.
|
|
168
|
+
- `agent-cli` — depends on `node-datachannel` (optional) instead of `werift`.
|
|
169
|
+
|
|
170
|
+
- c7f9203: Connected sessions can send each other files.
|
|
171
|
+
|
|
172
|
+
- `/peers send-file <session-id> <path>` sends a copy of any file the operator can read to another
|
|
173
|
+
live session on this host.
|
|
174
|
+
- The model sends a file only through the `peer_send_file` tool. Every call asks the user, showing
|
|
175
|
+
the path, size, hash and destination; no permission mode, rule or remembered consent answers it.
|
|
176
|
+
The tool reaches only files inside the workspace whose path does not look like it holds secrets
|
|
177
|
+
(`.env*`, `~/.ssh`, keys and credentials), and it does not exist in a turn a peer's message started.
|
|
178
|
+
- The receiving operator approves every file. A received file is kept as an inert copy (mode 0600)
|
|
179
|
+
under `~/.robota/peer-files/<sender>/`. It is never run and never placed in the model's context.
|
|
180
|
+
The conversation is told only its name, size and sha256. A name that leaves that directory is
|
|
181
|
+
refused, a symbolic link is never written through, and nothing is overwritten.
|
|
182
|
+
- Transfers travel on a channel of their own (a separate connection on this host, a separate data
|
|
183
|
+
channel between devices), in chunks the receiver paces, up to 32 MiB, and are kept only when the
|
|
184
|
+
whole content matches the offered sha256. A transfer that ends early is discarded; there is no
|
|
185
|
+
resume.
|
|
186
|
+
|
|
187
|
+
**API**
|
|
188
|
+
|
|
189
|
+
- `agent-interface-session-mobility`: the `file` capability, which asks the operator for every
|
|
190
|
+
request; `ConnectionAuthority.authorizeFile`; `IFileOffer` and `IFileFrameChannel`.
|
|
191
|
+
- `agent-transport/node`: `sendFileOverChannel` and `receiveFileOverChannel`, the carrier over any
|
|
192
|
+
`IFileFrameChannel`; `DEFAULT_MAX_FILE_BYTES`.
|
|
193
|
+
- `agent-transport-webrtc`: `IDeviceMeshLink.openFileChannel` and `onFileChannel`.
|
|
194
|
+
- `agent-remote-pairing`: `file` joins `DEVICE_CAPABILITIES`. A device certificate that names it is
|
|
195
|
+
refused as malformed by an earlier version.
|
|
196
|
+
- `agent-core`: `IToolPermissionProfile.notInPeerTurn` withholds a tool from a turn a peer's message
|
|
197
|
+
started.
|
|
198
|
+
- `agent-framework`: `ICommandLocalPeersAdapter.prepareFile`.
|
|
199
|
+
|
|
200
|
+
- 004fe7f: `/handoff` moves a session over a real connection.
|
|
201
|
+
|
|
202
|
+
- `/handoff <session-id>` pushes this conversation to another Robota session on this machine. The
|
|
203
|
+
same carrier also runs between two of the user's devices over their mesh connection; no command
|
|
204
|
+
opens that connection yet.
|
|
205
|
+
- A hand-off is push-only: only the operator of the session that holds it starts one. A session or
|
|
206
|
+
device that asks another for its session is refused.
|
|
207
|
+
- The source signs a grant for that one transfer over that one channel with its device key, so a
|
|
208
|
+
hand-off needs the device identity from `/devices init`. The receiving side checks it against the
|
|
209
|
+
sender's certificate, then asks its own operator; without a yes nothing is sent.
|
|
210
|
+
- The session travels on the file-transfer carrier, is kept aside until it matches the manifest, and
|
|
211
|
+
is saved without being started. The operator there resumes it with `robota --resume <id>`.
|
|
212
|
+
- The source gives the session up, and ends, only once the receiving side confirms it saved it.
|
|
213
|
+
Every other outcome leaves the session where it was; if the confirmation is lost, `/handoff` to the
|
|
214
|
+
same session again resends the same transfer, which the receiver settles without saving it twice.
|
|
215
|
+
Peer attribution (`driverId`, `turnSource`)
|
|
216
|
+
travels with it.
|
|
217
|
+
- `/handoff` stays user-only. Its description tells the model to suggest the command to the user.
|
|
218
|
+
- `agent-transport-webrtc`: an admitted mesh link exposes the DTLS fingerprints it is bound to, and
|
|
219
|
+
`judgeHandoffGrant` is exported.
|
|
220
|
+
- `agent-interface-session-mobility`: a hand-off carrier may move the sealed payload whole
|
|
221
|
+
(`sendPayload`), the destination verifies it with `receivePayload`, and the source can report its
|
|
222
|
+
open transfer (`status`) and abandon it for any refusal the destination names.
|
|
223
|
+
|
|
224
|
+
- 189c21e: The device mesh can find and signal a peer device beyond the local network, through public
|
|
225
|
+
infrastructure that sees only signed ciphertext.
|
|
226
|
+
|
|
227
|
+
- **Rendezvous records (BEP 44):** each device publishes, per peer, its connection hints and the newest
|
|
228
|
+
device revocation lists it holds as mutable items on the BitTorrent Mainline DHT (`bittorrent-dht`,
|
|
229
|
+
directly over UDP), or through pkarr relays (`createPkarrRelayStore`, for clients that cannot reach
|
|
230
|
+
the DHT). Every record is signed by a one-time key of the pair, direction, epoch and purpose, stored
|
|
231
|
+
under a rotating salt, AEAD-sealed and padded to a fixed size; publish times are jittered per pair.
|
|
232
|
+
Lookups read the peer's records at the current and adjacent epochs and reject anything that does not
|
|
233
|
+
verify or open for the pair. Lists too large for one record are split into fixed-size chunks.
|
|
234
|
+
`MeshDht` is the candidate source and publisher, and its `latestLists` feeds the freshness lookup
|
|
235
|
+
before a remote admission with every list candidate found, newest first.
|
|
236
|
+
- **Nostr signaling:** `NostrMeshRelay` carries live SDP/ICE as ephemeral events on several relays,
|
|
237
|
+
under per-epoch, per-direction keys and kinds, with our own AEAD over the payload and no tags.
|
|
238
|
+
- **Order:** `DiscoveringMeshRelay` tries the address cache, mDNS, then DHT records for candidates,
|
|
239
|
+
and when none answers the signaling carriers in order — Nostr, then the self-hosted relay. A
|
|
240
|
+
carrier that carries no admission in time is set aside for the next one.
|
|
241
|
+
- **Defaults:** `DEFAULT_NOSTR_RELAYS` and `DEFAULT_PKARR_RELAYS` list well-known relays of several
|
|
242
|
+
operators; `transports.mesh.options` (`dht`, `pkarrRelays`, `nostrRelays`) replaces them, parsed by
|
|
243
|
+
the CLI's `parseMeshInternetSettings`, and `openDeviceMesh` takes them as `internet`. No command
|
|
244
|
+
starts the mesh yet.
|
|
245
|
+
- `agent-remote-pairing`: `signingSeed`, `sealRecord` and `openRecord` take a record purpose
|
|
246
|
+
(`hints`, the default and unchanged; `revocation`; `signal`), and two tag purposes are added. The
|
|
247
|
+
device handshake's `fetchLatestLists` may return several candidates per list kind; each is verified
|
|
248
|
+
and the newest that verifies counts, so a forged "newer" list cannot hide a real one.
|
|
249
|
+
- `agent-cli` declares the mesh's runtime dependencies (`bittorrent-dht`, `nostr-tools`,
|
|
250
|
+
`multicast-dns`), which its bundle leaves external.
|
|
251
|
+
|
|
252
|
+
**Breaking (pre-release, hence minor):** `IDiscoveringMeshRelayOptions.advertiser` becomes
|
|
253
|
+
`advertisers` (a list).
|
|
254
|
+
|
|
255
|
+
- 5e924ee: Two of one user's devices can find each other on the local network before the relay.
|
|
256
|
+
|
|
257
|
+
- `agent-remote-pairing` — `derivePairRendezvous` derives every place a device pair meets from their
|
|
258
|
+
pairwise secret, separated by direction: rotating tags (hourly epochs, looked up one epoch either
|
|
259
|
+
way), the seed of each epoch's one-time signing key, sealed connection-hint records, and the relay
|
|
260
|
+
inbox topics. It takes the lists in force and refuses a device they do not name with the same
|
|
261
|
+
key-agreement key, or revoke, so a rotated or revoked key stops deriving.
|
|
262
|
+
- `agent-transport-webrtc` — `startLanMeshRelay` / `DiscoveringMeshRelay` look for a peer in the
|
|
263
|
+
address cache, then with mDNS (`MeshMdns`, over `multicast-dns`), then on the self-hosted relay, and
|
|
264
|
+
carry signals to the peer's direct endpoint (`startMeshLanListener`) on rotating pairwise topics,
|
|
265
|
+
sent only as hashes. An endpoint must prove it holds the pair's topic before it carries signals,
|
|
266
|
+
and is set aside when no admission follows. The mDNS announcement names no product, device or
|
|
267
|
+
host, pads its instance count with names that hold for the epoch, and answers queries at a bounded
|
|
268
|
+
rate. Discovery yields candidates only: admission is still the device handshake, and an address is
|
|
269
|
+
remembered only after an admission it carried.
|
|
270
|
+
- `agent-cli` — the device mesh endpoint can look on the local network (`lan` option), remembering
|
|
271
|
+
the addresses that worked in an owner-only `~/.robota/devices/address-cache.json`; no command starts
|
|
272
|
+
it yet.
|
|
273
|
+
|
|
274
|
+
### Patch Changes
|
|
275
|
+
|
|
276
|
+
- Updated dependencies [e15e22b]
|
|
277
|
+
- Updated dependencies [e15e22b]
|
|
278
|
+
- Updated dependencies [c7f9203]
|
|
279
|
+
- Updated dependencies [004fe7f]
|
|
280
|
+
- Updated dependencies [189c21e]
|
|
281
|
+
- Updated dependencies [5e924ee]
|
|
282
|
+
- @robota-sdk/agent-remote-pairing@3.0.0-beta.82
|
|
283
|
+
- @robota-sdk/agent-interface-session-mobility@3.0.0-beta.82
|
|
284
|
+
- @robota-sdk/agent-transport@3.0.0-beta.82
|
|
285
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.82
|
|
286
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.82
|
|
287
|
+
|
|
288
|
+
## 3.0.0-beta.81
|
|
289
|
+
|
|
290
|
+
### Minor Changes
|
|
291
|
+
|
|
292
|
+
- 007fd90: Authority per connection: pairing stays with the local operator, and driving needs the operator's yes.
|
|
293
|
+
|
|
294
|
+
- `agent-interface-session-mobility` — `ConnectionAuthority` decides what one connection may do.
|
|
295
|
+
`presence` and `message` are allowed; `delegate` and `handoff` ask the receiving operator for every
|
|
296
|
+
request; `observe` and `drive` ask once per connection. With no `IOperatorApprover` the answer is
|
|
297
|
+
no. `authorizeDelegation` turns an approved task into a peer turn from where admission placed the
|
|
298
|
+
peer, ignoring anything else on the request, so the receiver's policy decides what it may do.
|
|
299
|
+
- `agent-transport-webrtc` — `connectionApproval` asks the operator before a connection reaches the
|
|
300
|
+
session, after every proof has run. Frames the peer sends meanwhile are held (bounded) and delivered
|
|
301
|
+
only after a yes. A channel that closes first withdraws the question (the approval context carries
|
|
302
|
+
an `AbortSignal`), and a later answer admits nothing. A first-pairing device is pinned for
|
|
303
|
+
reconnect only once it is admitted.
|
|
304
|
+
- `agent-command` — `/remote-control enable` and `revoke` from a connected surface are refused, and
|
|
305
|
+
`status` never shows a connected surface the pairing link.
|
|
306
|
+
- `agent-framework` — the `remote-control-enable` host action runs only for the operator's own
|
|
307
|
+
command, whichever command asked for it.
|
|
308
|
+
- `agent-cli` — every remote-control connection, a returning trusted device included, is put to the
|
|
309
|
+
operator on the host terminal; without an interactive terminal it is refused.
|
|
310
|
+
|
|
311
|
+
### Patch Changes
|
|
312
|
+
|
|
313
|
+
- Updated dependencies [3038eb7]
|
|
314
|
+
- Updated dependencies [b2e0afe]
|
|
315
|
+
- Updated dependencies [44fc732]
|
|
316
|
+
- Updated dependencies [007fd90]
|
|
317
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.81
|
|
318
|
+
- @robota-sdk/agent-interface-session-mobility@3.0.0-beta.81
|
|
319
|
+
- @robota-sdk/agent-remote-pairing@3.0.0-beta.81
|
|
320
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.81
|
|
321
|
+
- @robota-sdk/agent-transport@3.0.0-beta.81
|
|
322
|
+
|
|
323
|
+
## 3.0.0-beta.80
|
|
324
|
+
|
|
325
|
+
### Major Changes
|
|
326
|
+
|
|
327
|
+
- e82215f: **ARCH-011: replace the ambiguous transport lifecycle stub with executable conformance.**
|
|
328
|
+
|
|
329
|
+
`ITransportAdapter` now requires a frozen `service | runner` lifecycle descriptor. `start()` resolves
|
|
330
|
+
at the concrete transport's documented readiness boundary; start before attach and repeated active
|
|
331
|
+
start reject a stable lifecycle error, repeated stop is safe, and stopped adapters can reattach and
|
|
332
|
+
restart.
|
|
333
|
+
|
|
334
|
+
Runner adapters launch separately and expose a typed terminal outcome through
|
|
335
|
+
`waitForCompletion()`. The registry accepts base adapters, rejects duplicate names, keeps
|
|
336
|
+
configuration as an optional capability, returns complete ordered records whose pending slots become
|
|
337
|
+
registry-owned `abandoned` outcomes on stop/rollback, and exposes a real-runner-only first-failure
|
|
338
|
+
wait. It serializes startup/stop, rejects active restart before mutation, and reverses partial startup
|
|
339
|
+
from the currently failing adapter with typed safe rollback details. Runtime host and serve mode
|
|
340
|
+
propagate real nonzero runner results without treating normal shutdown abandonment as failure.
|
|
341
|
+
|
|
342
|
+
HTTP, MCP, both WebSocket adapters, WebRTC, and headless invoke one shared public conformance kit.
|
|
343
|
+
The former `TuiTransport` export is removed because it ignored the attached session; use `renderApp`
|
|
344
|
+
or `TuiInteractionChannel`, which honestly own their session lifecycle.
|
|
345
|
+
|
|
346
|
+
### Minor Changes
|
|
347
|
+
|
|
348
|
+
- 9db63ee: Add named session capability roles and explicit capability-host queries while preserving the legacy
|
|
349
|
+
`IInteractiveSession` interface shape. HTTP, MCP, protocol, WS, WebRTC, and headless transports now
|
|
350
|
+
declare only the session roles they consume, and the direct aggregate-cast floor is zero.
|
|
351
|
+
- 2ebff01: Emit the complete persisted checkpoint and branch lifecycle, forward plan, context-refresh, and
|
|
352
|
+
branch events through protocol transports, and render deterministic bounded notices in the TUI.
|
|
353
|
+
Transport-owned delivery failures now enter the owning carrier cleanup lifecycle without reversing
|
|
354
|
+
an already-committed session operation.
|
|
355
|
+
- 94cc355: REMOTE-002 Stage A: extract the transport-neutral session bridge + wire protocol into a new
|
|
356
|
+
`@robota-sdk/agent-transport-protocol` package (`createWsHandler`, `TClientMessage`/`TServerMessage`); repoint
|
|
357
|
+
`agent-transport-ws` and `agent-web-ui` at it (no pass-through re-export). Add a new
|
|
358
|
+
`@robota-sdk/agent-transport-webrtc` package: a `WebRtcTransport` (`IConfigurableTransport`, `defaultEnabled:false`)
|
|
359
|
+
that carries an `IInteractiveSession` over an `RTCDataChannel` reusing the shared handler, with a lazily-loaded
|
|
360
|
+
optional `werift` peer dependency that throws an explicit "WebRTC transport unavailable" error on absence (never a
|
|
361
|
+
silent no-op). No user-facing enable path and no auth in Stage A (that lands in Stage B); the transport is proven
|
|
362
|
+
by an in-process loopback data-channel round-trip only.
|
|
363
|
+
|
|
364
|
+
(Bump target corrected during REL-023 triage: `@robota-sdk/agent-web-ui` was dissolved by GUI-006 (#1141, 2026-07-12) before this work was ever published; its protocol-consumer role now lives in `@robota-sdk/agent-transport-webrtc-web`.)
|
|
365
|
+
|
|
366
|
+
- 2c69a3f: REMOTE-004 Stage B2: production WebRTC signaling + relay abuse-hardening (still no user-facing enable path).
|
|
367
|
+
|
|
368
|
+
- Add `WsSignalingClient` — a production `ISignalingClient` over a `ws` socket to the `@robota-sdk/remote-signaling`
|
|
369
|
+
relay (Node host-side): joins a rendezvous, buffers signals produced before the socket opens and flushes them,
|
|
370
|
+
and surfaces relay/socket errors through an explicit `onError` (no silent degrade). An `onReady` callback fires
|
|
371
|
+
once the rendezvous join is confirmed.
|
|
372
|
+
- Expose opt-in `forceTurn` on `IWebRtcTransportOptions` (relay-only ICE) as defense-in-depth.
|
|
373
|
+
- The private `@robota-sdk/remote-signaling` relay is hardened in-layer (safe by default): a per-source
|
|
374
|
+
token-bucket bounds join floods, rendezvous ids are single-use (a distinct third peer is refused for the id's
|
|
375
|
+
lifetime, even after one of the pair leaves), a half-open rendezvous expires after a TTL, and concurrent
|
|
376
|
+
rendezvous are capped — all with injected clock/scheduler for deterministic tests.
|
|
377
|
+
- `CVE-2024-29415` (werift-transitive `ip` SSRF) is discharged as a reviewed re-accept: werift never calls the
|
|
378
|
+
vulnerable `ip.isPublic`/`isPrivate`/`address` (verified + guarded by a regression test), so the
|
|
379
|
+
`ignoreCves` entry is retained with a documented non-reachability rationale.
|
|
380
|
+
|
|
381
|
+
- 92bf33e: Add the pairing gate to the WebRTC transport (REMOTE-008 Step 1, security milestone). When a pairing
|
|
382
|
+
`secret` is configured, the data channel is phase-separated: pre-accept it carries only pairing frames
|
|
383
|
+
(routed to the directional-HMAC handshake bound to the DTLS fingerprints; any non-pairing frame is
|
|
384
|
+
dropped), and only after the handshake accepts is the session bridge built — fail closed on
|
|
385
|
+
mismatch/timeout (channel closed, session never exposed). Without a `secret` the channel is exposed
|
|
386
|
+
immediately, unchanged. Introduces a dependency on the zero-dep `@robota-sdk/agent-remote-pairing` leaf
|
|
387
|
+
(the gate must live where the SDP fingerprints and channel frames are visible).
|
|
388
|
+
- 9f602df: Add user-supplied TURN fallback for remote control (REMOTE-010 / Stage E1) so P2P works behind symmetric
|
|
389
|
+
NAT / restrictive firewalls. The host reads + validates `transports.webrtc.options.iceServers`/`forceTurn`
|
|
390
|
+
at the agent-cli composition root (a fail-closed validator narrowing the untyped value → `IIceServer[]`;
|
|
391
|
+
`IWebRtcTransportOptions.iceServers` widened to carry TURN `username`/`credential`), and the browser reads a
|
|
392
|
+
validated `ice`/`forceTurn` pairing-URL query param (fail-closed decoder for the attacker-influenceable value;
|
|
393
|
+
`forceTurn` → `iceTransportPolicy: 'relay'`) — both threaded into their `RTCPeerConnection`. `forceTurn` without
|
|
394
|
+
a TURN server fails closed (else ICE gathers no candidates and silently never connects). Absent ICE config ⇒
|
|
395
|
+
host-candidate-only, unchanged.
|
|
396
|
+
|
|
397
|
+
(Bump target corrected during REL-023 triage: `@robota-sdk/agent-web-ui` was dissolved by GUI-006 (#1141, 2026-07-12) before this work was ever published; the browser-side ICE/`forceTurn` handling described here now lives in `@robota-sdk/agent-transport-webrtc-web`.)
|
|
398
|
+
|
|
399
|
+
### Patch Changes
|
|
400
|
+
|
|
401
|
+
- 235da81: **BREAKING — ARCH-030: `createWsHandler` takes the carrier's delivery boundary, not a raw `send`.**
|
|
402
|
+
|
|
403
|
+
`createWsHandler` had two outbound semantics on one connection. The session-event fan-out went through
|
|
404
|
+
a guard that reported carrier failures through `onDeliveryError`; every reply to an inbound frame got
|
|
405
|
+
the raw `send`. Eleven reply families were unguarded — five resolving from a Promise continuation, so a
|
|
406
|
+
reply landing after a disconnect escaped as an **unhandled rejection** while the carrier's cleanup was
|
|
407
|
+
never notified, and six synchronous ones that threw into the carrier's inbound listener instead.
|
|
408
|
+
|
|
409
|
+
`IWsHandlerOptions` now takes a single `deliver: TOutboundDeliver` in place of `send` and
|
|
410
|
+
`onDeliveryError`. **The carrier builds the boundary** from its own sink and its own failure policy and
|
|
411
|
+
passes it down — not the reverse, because a protocol layer handed a raw sink so it can hand a wrapper
|
|
412
|
+
back leaves the raw sink reachable, which is how the twelfth reply family gets added unguarded.
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
// before
|
|
416
|
+
const { onMessage, cleanup } = createWsHandler({
|
|
417
|
+
session,
|
|
418
|
+
send: (msg) => ws.send(JSON.stringify(msg)),
|
|
419
|
+
onDeliveryError: (error) => ws.close(1011, error.message),
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// after
|
|
423
|
+
const deliver = createOutboundDelivery(
|
|
424
|
+
(msg) => ws.send(JSON.stringify(msg)),
|
|
425
|
+
(error) => ws.close(1011, error.message),
|
|
426
|
+
);
|
|
427
|
+
const { onMessage, cleanup } = createWsHandler({ session, deliver });
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
`TOutboundDeliver` is branded and `createOutboundDelivery` is its only producer, so a plain
|
|
431
|
+
`(message: TServerMessage) => void` is refused by the compiler wherever a boundary is required.
|
|
432
|
+
|
|
433
|
+
**The boundary latches:** it reports at most one delivery failure per connection, after which frames are
|
|
434
|
+
dropped without a further report. All three carriers already treated a delivery failure as terminal and
|
|
435
|
+
each had grown its own latch; it belongs upstream of all three. `SessionResumeBridge` builds a fresh
|
|
436
|
+
boundary per `attach`, which is what un-latches the session after a reconnect, and buffers a frame
|
|
437
|
+
before the boundary so a dropped one still replays.
|
|
438
|
+
|
|
439
|
+
**`ISubscribeSessionEventsOptions` is no longer exported** from the package barrel. It is the options bag
|
|
440
|
+
of `subscribeSessionEvents`, which is package-internal, and it was already absent from the SPEC's public
|
|
441
|
+
API table. Its `onDeliveryError` member is gone regardless — carrier-failure containment is the
|
|
442
|
+
boundary's job now.
|
|
443
|
+
|
|
444
|
+
`agent-transport-ws` and `agent-transport-webrtc` are `patch`: `WsSessionDelivery` (whose raw `send` is
|
|
445
|
+
now private, with `deliver` the only public sink) and `PairingGate` are not on their packages' barrels,
|
|
446
|
+
and every barrel export of both packages keeps its signature.
|
|
447
|
+
|
|
448
|
+
- 4f3c075: Assemble complete, verified package generations before switching build output; preserve the previous generation on build failure and pack only verified regular-file images. Include copied CLI web assets in affected-build ordering and artifact transfer. Preserve the CLI version in managed build paths. Public runtime contracts remain compatible (patch).
|
|
449
|
+
- 2db1b97: Remote-control pairing binds to the negotiated DTLS certificate.
|
|
450
|
+
|
|
451
|
+
- The Node host reads the remote fingerprint from the certificate the DTLS layer verified, not from the answer's
|
|
452
|
+
SDP text, and builds the pairing gate once the DTLS handshake completes.
|
|
453
|
+
- An SDP must advertise exactly one DTLS fingerprint. `extractDtlsFingerprint` now throws when two different
|
|
454
|
+
fingerprints are present, and `extractDtlsFingerprintAttribute` returns the algorithm with the value.
|
|
455
|
+
- A start takes one answer (host) and a connection takes one offer (browser client); a later description is
|
|
456
|
+
ignored.
|
|
457
|
+
|
|
458
|
+
- Updated dependencies [37b4bd7]
|
|
459
|
+
- Updated dependencies [50d2c9f]
|
|
460
|
+
- Updated dependencies [a5961c9]
|
|
461
|
+
- Updated dependencies [4dd45cc]
|
|
462
|
+
- Updated dependencies [040f31f]
|
|
463
|
+
- Updated dependencies [4c5148e]
|
|
464
|
+
- Updated dependencies [0116a29]
|
|
465
|
+
- Updated dependencies [e82215f]
|
|
466
|
+
- Updated dependencies [52b7346]
|
|
467
|
+
- Updated dependencies [9db63ee]
|
|
468
|
+
- Updated dependencies [b078afa]
|
|
469
|
+
- Updated dependencies [2ebff01]
|
|
470
|
+
- Updated dependencies [9db63ee]
|
|
471
|
+
- Updated dependencies [2ebff01]
|
|
472
|
+
- Updated dependencies [4772067]
|
|
473
|
+
- Updated dependencies [0f98419]
|
|
474
|
+
- Updated dependencies [64ba748]
|
|
475
|
+
- Updated dependencies [d312755]
|
|
476
|
+
- Updated dependencies [3244fb8]
|
|
477
|
+
- Updated dependencies [1e3f91a]
|
|
478
|
+
- Updated dependencies [4f3c075]
|
|
479
|
+
- Updated dependencies [1e40b5b]
|
|
480
|
+
- Updated dependencies [7669851]
|
|
481
|
+
- Updated dependencies [4b76cfa]
|
|
482
|
+
- Updated dependencies [2db1b97]
|
|
483
|
+
- Updated dependencies [07b627f]
|
|
484
|
+
- Updated dependencies [1f45110]
|
|
485
|
+
- Updated dependencies [9665c6e]
|
|
486
|
+
- Updated dependencies [44393be]
|
|
487
|
+
- Updated dependencies [c7fa299]
|
|
488
|
+
- Updated dependencies [5134b3b]
|
|
489
|
+
- Updated dependencies [833afe1]
|
|
490
|
+
- Updated dependencies [5c5ff23]
|
|
491
|
+
- Updated dependencies [9814afc]
|
|
492
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.80
|
|
493
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.80
|
|
494
|
+
- @robota-sdk/agent-transport@3.0.0-beta.80
|
|
495
|
+
- @robota-sdk/agent-interface-session-mobility@3.0.0-beta.80
|
|
496
|
+
- @robota-sdk/agent-remote-pairing@3.0.0-beta.80
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# @robota-sdk/agent-transport-webrtc
|
|
2
|
+
|
|
3
|
+
WebRTC peer-to-peer transport for the Robota SDK (Node.js). `WebRtcTransport` carries a Robota
|
|
4
|
+
session over an `RTCDataChannel`, so a remote client can drive a live session directly, peer to
|
|
5
|
+
peer, without session content passing through a server. It reuses the transport-neutral session
|
|
6
|
+
bridge and wire protocol from `@robota-sdk/agent-transport`, the same ones the WebSocket transport
|
|
7
|
+
uses. The package also contains the building blocks for connecting one user's devices to each other
|
|
8
|
+
(the device mesh): peer discovery and signaling, device enrollment and a TURN relay.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @robota-sdk/agent-transport-webrtc @robota-sdk/agent-transport node-datachannel
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requires Node.js 22 or later. The WebRTC implementation, `node-datachannel` (libdatachannel, a
|
|
17
|
+
native module), is an optional peer dependency loaded lazily. If it is not installed or has no
|
|
18
|
+
prebuilt binary for the platform, `start()` fails with an explicit `WebRTC transport unavailable`
|
|
19
|
+
error; there is no fallback implementation. `@robota-sdk/agent-transport` provides the
|
|
20
|
+
`IProtocolSession` type used below.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { WebRtcTransport, WsSignalingClient } from '@robota-sdk/agent-transport-webrtc';
|
|
26
|
+
import type { IProtocolSession } from '@robota-sdk/agent-transport';
|
|
27
|
+
|
|
28
|
+
declare const session: IProtocolSession; // e.g. a live interactive session
|
|
29
|
+
declare const pairingSecret: string; // shared with the remote client out of band
|
|
30
|
+
|
|
31
|
+
const signaling = new WsSignalingClient({
|
|
32
|
+
url: 'wss://signaling.example.com',
|
|
33
|
+
rendezvous: 'my-rendezvous-id',
|
|
34
|
+
onError: (error) => console.error(error),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const transport = new WebRtcTransport({
|
|
38
|
+
signaling,
|
|
39
|
+
secret: pairingSecret,
|
|
40
|
+
onPaired: () => console.log('remote client paired'),
|
|
41
|
+
onPairingFailed: () => console.log('pairing failed; channel closed'),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
transport.attach(session);
|
|
45
|
+
await transport.start(); // the host is the offerer: creates the peer and data channel, sends the offer
|
|
46
|
+
// ...
|
|
47
|
+
await transport.stop();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Admission is decided at construction. With `secret`, the data channel carries only pairing frames
|
|
51
|
+
until the pairing handshake from `@robota-sdk/agent-remote-pairing` accepts, bound to the DTLS
|
|
52
|
+
fingerprints of the connection; only then is the session exposed, and a mismatch or timeout closes
|
|
53
|
+
the channel. To run without pairing (for example over loopback in tests), pass
|
|
54
|
+
`{ open: true, openReason: '<why this is safe>' }` instead. The constructor throws when neither is
|
|
55
|
+
given, or when both are.
|
|
56
|
+
|
|
57
|
+
Signaling is an injected port (`ISignalingClient`) that only carries SDP offers/answers and ICE
|
|
58
|
+
candidates, never session content. `createInMemorySignalingPair()` wires two in-process clients
|
|
59
|
+
together for tests; `WsSignalingClient` joins a rendezvous on a WebSocket signaling relay.
|
|
60
|
+
|
|
61
|
+
## `WebRtcTransport` options
|
|
62
|
+
|
|
63
|
+
| Option | Type | Description |
|
|
64
|
+
| ------------------------------------------------------------------------ | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
65
|
+
| `signaling` | `ISignalingClient` | Required. Exchanges SDP/ICE with the remote peer. |
|
|
66
|
+
| `secret` | `string` | Pairing secret; gates the data channel until the pairing handshake accepts. |
|
|
67
|
+
| `open` / `openReason` | `boolean` / `string` | Runs without a pairing gate; `openReason` must say why. |
|
|
68
|
+
| `iceServers` | `readonly IIceServer[]` | STUN/TURN servers. Omitted: host candidates only. |
|
|
69
|
+
| `forceTurn` | `boolean` | Restricts ICE to relay (TURN) candidates; needs a TURN server in `iceServers`. |
|
|
70
|
+
| `onPaired` / `onPairingFailed` | callbacks | Called when pairing accepts (session exposed) or rejects/times out (channel closed). |
|
|
71
|
+
| `reconnect` | `IHostReconnectConfig` | With `secret`: admits either a first pairing (with device enrollment) or a pinned-device reconnect. |
|
|
72
|
+
| `connectionApproval` | `IConnectionApproval` | With `secret`: asks an operator to approve each connection before it reaches the session. |
|
|
73
|
+
| `localPeer` | `ILocalPeerProof` | With `secret`: also requires a nonce issued at a guarded local rendezvous. |
|
|
74
|
+
| `resumeBridge` / `onDropped` | `SessionResumeBridge` / callback | With `secret`: `resumeBridge` carries the paired session across channel drops; `onDropped` is called when a paired channel drops. |
|
|
75
|
+
| `onDeliveryError` | callback | Observes an outbound delivery failure before the channel is dropped. |
|
|
76
|
+
| `personalUsageReporter` / `usageReporter` / `storedSessionUsageReporter` | reporters | Host-owned usage read models, available to the peer only after admission. |
|
|
77
|
+
|
|
78
|
+
`start()` before `attach()` throws; `stop()` is safe to call more than once, and starting again
|
|
79
|
+
requires attaching again.
|
|
80
|
+
|
|
81
|
+
## Other exports
|
|
82
|
+
|
|
83
|
+
- Peers: `RtcPeer`, `RtcChannel` and `loadDataChannel`, one peer connection over `node-datachannel`.
|
|
84
|
+
- Device mesh: `DeviceMeshNode` connects one user's devices, admitted by the device handshake.
|
|
85
|
+
Discovery and signaling for it: `DiscoveringMeshRelay`, `MeshMdns`, `MeshDht`, `NostrMeshRelay`,
|
|
86
|
+
`WsMeshRelayClient`, `startLanMeshRelay`.
|
|
87
|
+
- Relay: `TurnServer` and `MeshTurnRelay`, a TURN relay one of the user's devices can run for its
|
|
88
|
+
paired devices.
|
|
89
|
+
- Enrollment: `dialEnrollment` and `listenForEnrollment` open the channel used to enroll a new
|
|
90
|
+
device.
|
|
91
|
+
|
|
92
|
+
## Related packages
|
|
93
|
+
|
|
94
|
+
- [`@robota-sdk/agent-transport`](../agent-transport/README.md): the transport-neutral session
|
|
95
|
+
bridge and wire protocol this transport carries.
|
|
96
|
+
- [`@robota-sdk/agent-remote-pairing`](../agent-remote-pairing/README.md): the pairing handshake and
|
|
97
|
+
DTLS-fingerprint channel binding the pairing gate uses.
|
|
98
|
+
- [`@robota-sdk/agent-transport-ws`](../agent-transport-ws/README.md): the WebSocket transport for
|
|
99
|
+
the same protocol.
|
|
100
|
+
|
|
101
|
+
See [docs/SPEC.md](docs/SPEC.md) for the package contract.
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
Robota is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a [commercial license](../../COMMERCIAL.md). See [LICENSING.md](../../LICENSING.md).
|