@fleetless/sdk 2.1.0 → 3.0.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 +151 -0
- package/CODE_OF_CONDUCT.md +83 -0
- package/CONTRIBUTING.md +130 -0
- package/README.md +206 -62
- package/SECURITY.md +100 -0
- package/dist/index.cjs +2363 -2358
- package/dist/index.d.cts +518 -625
- package/dist/index.d.ts +518 -625
- package/dist/index.js +2363 -2358
- package/package.json +15 -8
package/README.md
CHANGED
|
@@ -9,12 +9,27 @@ The official TypeScript SDK for apps built on [Fleetless](https://fleetless.dev)
|
|
|
9
9
|
which exposes a ROS 2 robot as a hosted REST and realtime API — exactly the
|
|
10
10
|
topics, services and actions a developer chose to publish, under stable slugs,
|
|
11
11
|
behind roles. The SDK covers all of it: datapoints (read, subscribe, recorded
|
|
12
|
-
history), actions, services, publishers, cameras, jobs, assets and URDF,
|
|
13
|
-
|
|
12
|
+
history), actions, services, publishers, cameras, jobs, assets and URDF, and
|
|
13
|
+
the whole client auth API your own sign-in UI calls. It is framework-agnostic,
|
|
14
14
|
ships ESM and CJS with its own types, and runs wherever a `fetch` and a
|
|
15
15
|
`WebSocket` exist — a browser, a mobile webview, Node 20 or newer, and
|
|
16
16
|
server-side with a server key instead of a user session.
|
|
17
17
|
|
|
18
|
+
**The mental model in one paragraph.** A robot runs the Fleetless bridge, a
|
|
19
|
+
ROS 2 node. In the [Fleetless Console](https://console.fleetless.dev) a
|
|
20
|
+
developer picks which topics, services and actions that robot exposes and gives
|
|
21
|
+
each one a stable `snake_case` slug and a role that may reach it. The cloud
|
|
22
|
+
turns those into REST and realtime endpoints. This package is the client for
|
|
23
|
+
those endpoints: you address a robot by its id and a capability by its slug,
|
|
24
|
+
you sign your own users in against your own app's user directory, and you never
|
|
25
|
+
see a ROS name, a topic type or a message definition. Renaming a node on the
|
|
26
|
+
robot is invisible to your app; changing a slug is not.
|
|
27
|
+
|
|
28
|
+
New here? The [getting-started guide](https://docs.fleetless.dev/getting-started/)
|
|
29
|
+
goes from a robot that has never connected to a value on a page, and the
|
|
30
|
+
[app-auth recipe](https://docs.fleetless.dev/recipes/app-auth/) is the
|
|
31
|
+
walkthrough for every sign-in screen.
|
|
32
|
+
|
|
18
33
|
## Why
|
|
19
34
|
|
|
20
35
|
- **One connection, however many subscriptions.** Every datapoint and job
|
|
@@ -40,11 +55,13 @@ server-side with a server key instead of a user session.
|
|
|
40
55
|
`cameras.live` takes a refcounted hold: the first viewer starts the robot
|
|
41
56
|
publishing, the last one leaving stops it. You get a LiveKit room URL and
|
|
42
57
|
token to hand to the LiveKit client of your choice.
|
|
43
|
-
- **
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
58
|
+
- **The whole auth surface, and every screen stays yours.** Fleetless renders
|
|
59
|
+
no page for an app user: registration with email verification, invitations,
|
|
60
|
+
password reset, single sign-on to one of the app's identity providers, and
|
|
61
|
+
the MCP consent screen are all methods on `client.auth`, called from a UI you
|
|
62
|
+
wrote. The federated flow runs its own PKCE (S256 only) against Fleetless;
|
|
63
|
+
`beginOidcLogin` makes no network call, and `completeOidcLogin` checks
|
|
64
|
+
`state` before it exchanges anything.
|
|
48
65
|
- **A token store you control.** Implement two methods to keep a session in
|
|
49
66
|
`localStorage`, a cookie or a native keystore; the default keeps it in
|
|
50
67
|
memory. Refresh is silent and single-flight, so ten concurrent calls that
|
|
@@ -56,8 +73,10 @@ server-side with a server key instead of a user session.
|
|
|
56
73
|
npm i @fleetless/sdk
|
|
57
74
|
```
|
|
58
75
|
|
|
59
|
-
You need an app identifier and an
|
|
60
|
-
|
|
76
|
+
You need an app identifier and the credentials of an **app user** — an
|
|
77
|
+
account inside that app, which the developer's team creates or invites from
|
|
78
|
+
the [Fleetless Console](https://console.fleetless.dev). A console login is a
|
|
79
|
+
Fleetless user, a different identity space, and will not sign in here.
|
|
61
80
|
|
|
62
81
|
```ts checked
|
|
63
82
|
import { createClient } from '@fleetless/sdk'
|
|
@@ -98,45 +117,107 @@ Slugs are the names you chose in the console, in `snake_case`
|
|
|
98
117
|
(`[a-z][a-z0-9]*(_[a-z0-9]+)*`) — never the robot's internal ROS names, which
|
|
99
118
|
is what makes renaming a node on the robot invisible to your app.
|
|
100
119
|
|
|
101
|
-
###
|
|
120
|
+
### Your own sign-in UI
|
|
121
|
+
|
|
122
|
+
Every screen an app user sees belongs to your app. These are the calls behind
|
|
123
|
+
them; the full walkthrough, page by page, is the
|
|
124
|
+
[app-auth recipe](https://docs.fleetless.dev/recipes/app-auth/).
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// Registration. Resolves on 202 — which says a policy-allowed request was
|
|
128
|
+
// accepted, NOT that an account was created. An address the app already knows
|
|
129
|
+
// gets the identical answer with no mail sent, so the only honest thing to
|
|
130
|
+
// render is a sentence about the mailbox.
|
|
131
|
+
await client.auth.register({ email, password, displayName })
|
|
132
|
+
|
|
133
|
+
// The page your app serves at the app's configured `verify_url`. Stores a
|
|
134
|
+
// session, so nobody types a password right after reading their mail.
|
|
135
|
+
await client.auth.verifyEmail(tokenFromTheLink)
|
|
136
|
+
|
|
137
|
+
// Recovery. requestPasswordReset always resolves on 202, for a known address
|
|
138
|
+
// and an unknown one alike.
|
|
139
|
+
await client.auth.requestPasswordReset(email)
|
|
140
|
+
await client.auth.confirmPasswordReset(tokenFromTheLink, newPassword)
|
|
141
|
+
|
|
142
|
+
// The page your app serves at the app's configured `invite_url`.
|
|
143
|
+
await client.auth.acceptInvitation({ token: tokenFromTheLink, password })
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Sign-in through an identity provider
|
|
102
147
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
**App Settings → OAuth clients**; its `client_id` is not your `appIdentifier`.
|
|
148
|
+
An app may carry any number of OpenID Connect providers, configured per app in
|
|
149
|
+
the console. Your page draws the buttons; your app never talks to the provider.
|
|
106
150
|
|
|
107
151
|
```ts
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
|
|
152
|
+
// Draw the buttons. Only enabled providers are listed, slug and name only.
|
|
153
|
+
const providers = await client.auth.listProviders()
|
|
154
|
+
|
|
155
|
+
// 1. Build the URL. No network call: this generates the PKCE verifier and the
|
|
156
|
+
// state, and hands them back, because a redirect is a fresh page load.
|
|
157
|
+
const request = await client.auth.beginOidcLogin({
|
|
158
|
+
slug: 'azure',
|
|
111
159
|
redirectUri: 'https://your-app.example.com/callback',
|
|
112
160
|
})
|
|
113
|
-
|
|
114
|
-
// A redirect is a fresh page load, so persist these two yourself.
|
|
115
161
|
sessionStorage.setItem('fleetless_state', request.state)
|
|
116
162
|
sessionStorage.setItem('fleetless_code_verifier', request.codeVerifier)
|
|
117
|
-
window.location.
|
|
118
|
-
|
|
119
|
-
// 2. On the callback page.
|
|
120
|
-
//
|
|
121
|
-
const params = new
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
163
|
+
window.location.assign(request.url)
|
|
164
|
+
|
|
165
|
+
// 2. On the callback page. A refused sign-in redirects here with `error`
|
|
166
|
+
// rather than a code, so check for that first.
|
|
167
|
+
const params = new URL(window.location.href).searchParams
|
|
168
|
+
const failure = client.auth.oidcErrorFromCallback(params)
|
|
169
|
+
if (failure) {
|
|
170
|
+
// A FleetlessError carrying the documented reason: no_access, email_taken,
|
|
171
|
+
// email_unverified, domain_not_allowed, registration_closed, and the rest.
|
|
172
|
+
showSignInProblem(failure)
|
|
173
|
+
} else {
|
|
174
|
+
await client.auth.completeOidcLogin({
|
|
127
175
|
code: params.get('code') ?? '',
|
|
128
176
|
state: params.get('state') ?? '',
|
|
129
|
-
expectedState,
|
|
130
|
-
codeVerifier,
|
|
131
|
-
clientId: 'oauth-client-id-from-the-console',
|
|
132
|
-
redirectUri: 'https://your-app.example.com/callback',
|
|
177
|
+
expectedState: sessionStorage.getItem('fleetless_state') ?? '',
|
|
178
|
+
codeVerifier: sessionStorage.getItem('fleetless_code_verifier') ?? '',
|
|
133
179
|
})
|
|
134
180
|
// From here on me(), logout() and silent refresh behave exactly as they do
|
|
135
|
-
// after login().
|
|
181
|
+
// after login().
|
|
136
182
|
}
|
|
137
183
|
```
|
|
138
184
|
|
|
139
|
-
|
|
185
|
+
`completeOidcLogin` refuses before any network call, with `state_mismatch`,
|
|
186
|
+
for a mismatch, a missing `state`, **and** an empty `expectedState` — that
|
|
187
|
+
last case is checked first and explicitly, because two empty strings compare
|
|
188
|
+
equal.
|
|
189
|
+
|
|
190
|
+
One callback URL is registered at the provider,
|
|
191
|
+
`https://api.fleetless.dev/api/client/oidc/callback`, the same for every app
|
|
192
|
+
and every provider. It is not your `redirectUri`, which is a page in your app.
|
|
193
|
+
|
|
194
|
+
### The MCP consent screen
|
|
195
|
+
|
|
196
|
+
Only if the app serves its own MCP endpoint. An authorization redirects the
|
|
197
|
+
browser to a page your app serves, with an interaction id; your page signs the
|
|
198
|
+
person in, shows them what is being asked, and answers.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
// Sign the person in FIRST — `already_granted` is derived from the bearer,
|
|
202
|
+
// and an anonymous read answers `false` for everybody.
|
|
203
|
+
const interaction = await client.auth.mcpInteraction(interactionId)
|
|
204
|
+
|
|
205
|
+
// `interaction.client_name` is a name the client typed about itself during an
|
|
206
|
+
// unauthenticated registration. `client_name_verified` is the literal false;
|
|
207
|
+
// render it as a claim, never as an identity. `scopes` is always empty: what a
|
|
208
|
+
// session reaches is decided by the user's role, re-read on every call.
|
|
209
|
+
const { redirectTo } = approved
|
|
210
|
+
? await client.auth.approveMcpInteraction(interactionId)
|
|
211
|
+
: await client.auth.denyMcpInteraction(interactionId)
|
|
212
|
+
window.location.assign(redirectTo) // a denial redirects too
|
|
213
|
+
|
|
214
|
+
// A "connected apps" screen. Withdrawing stops the NEXT authorization; a
|
|
215
|
+
// session already running keeps working for the rest of its 15 minutes.
|
|
216
|
+
const grants = await client.auth.listMcpGrants()
|
|
217
|
+
await client.auth.revokeMcpGrant(grants[0].client_id)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Actions
|
|
140
221
|
|
|
141
222
|
An action is a job with a lifecycle. `invoke` resolves as soon as the job
|
|
142
223
|
exists; feedback, progress and the result arrive over `subscribe`.
|
|
@@ -164,6 +245,35 @@ sub.unsubscribe()
|
|
|
164
245
|
`error.details.running`. A service is the same machinery with a plain result:
|
|
165
246
|
`await client.services.call(robotId, 'set_mode', { mode: 'autonomous' })`.
|
|
166
247
|
|
|
248
|
+
### Publishers, and no teleop helpers
|
|
249
|
+
|
|
250
|
+
`publishers.publish` sends **one** message. There is no `startPublishing`, no
|
|
251
|
+
rate helper, no joystick binding and no "hold this value" call, and that is a
|
|
252
|
+
decision rather than a gap.
|
|
253
|
+
|
|
254
|
+
The safety primitive lives on the robot. A publisher is configured with a
|
|
255
|
+
`timeout_ms` and a failsafe message; if messages stop arriving — including
|
|
256
|
+
because your process crashed, your tab closed, or the network went away — the
|
|
257
|
+
bridge publishes the failsafe itself. Nothing in your event loop has to survive
|
|
258
|
+
for that to happen.
|
|
259
|
+
|
|
260
|
+
A helper here would move the appearance of that guarantee into a browser, where
|
|
261
|
+
it is not true. So *how often* and *when* to publish is your application's
|
|
262
|
+
decision, made where it can see the user's intent:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// A control loop is yours to write, and yours to stop.
|
|
266
|
+
const timer = setInterval(() => {
|
|
267
|
+
void client.publishers.publish(robotId, 'cmd_vel', { 'linear.x': speed })
|
|
268
|
+
}, 100)
|
|
269
|
+
|
|
270
|
+
// Stop publishing and let the robot's own failsafe take over.
|
|
271
|
+
clearInterval(timer)
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
`publish` rejects `publisher_busy` while a different user holds the publisher
|
|
275
|
+
and has not been quiet for its configured timeout.
|
|
276
|
+
|
|
167
277
|
### Cameras
|
|
168
278
|
|
|
169
279
|
```ts
|
|
@@ -184,29 +294,43 @@ await room.disconnect()
|
|
|
184
294
|
await session.release()
|
|
185
295
|
```
|
|
186
296
|
|
|
187
|
-
##
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
297
|
+
## Errors
|
|
298
|
+
|
|
299
|
+
Every refusal arrives as a `FleetlessError` with a stable `code`. Branch on the
|
|
300
|
+
code; never parse the message, which is written for a developer reading a
|
|
301
|
+
console and may change.
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
import { FleetlessError } from '@fleetless/sdk'
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
await client.actions.invoke(robotId, 'dock', {})
|
|
308
|
+
} catch (error) {
|
|
309
|
+
if (!(error instanceof FleetlessError)) throw error
|
|
310
|
+
switch (error.code) {
|
|
311
|
+
case 'busy': return showRunning(error.details?.running)
|
|
312
|
+
case 'robot_offline': return showOffline()
|
|
313
|
+
case 'forbidden': return showNotAllowed()
|
|
314
|
+
case 'rate_limited': return showRetryLater(error.details?.retry_after_ms)
|
|
315
|
+
default: return showUnexpected(error)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
- **`rate_limited` is surfaced, never retried behind your back.** The SDK does
|
|
321
|
+
not sleep and re-send. `error.details.retry_after_ms` carries the wait the
|
|
322
|
+
platform asked for, when it sent one — read it defensively, since a refusal
|
|
323
|
+
from an intermediary may carry no `details` at all.
|
|
324
|
+
- **The one retry the SDK does perform is `token_expired`**, and only once: it
|
|
325
|
+
refreshes the session and re-sends the same request. That refresh is
|
|
326
|
+
single-flight, so ten concurrent calls meeting an expired token share one.
|
|
327
|
+
- **`command_outcome_unknown` is an honest answer, not a failure.** It means
|
|
328
|
+
the realtime connection cycled while a command was in flight and the SDK
|
|
329
|
+
cannot say whether the robot got it. Read the job back by slug through
|
|
330
|
+
`actions.subscribe` to find out.
|
|
331
|
+
- Some codes never reach the network at all — `invalid_option`,
|
|
332
|
+
`state_mismatch`, `no_session`, `untrusted_absolute_url`. They are the SDK
|
|
333
|
+
refusing to send something rather than the platform refusing to accept it.
|
|
210
334
|
|
|
211
335
|
## Documentation and help
|
|
212
336
|
|
|
@@ -216,18 +340,38 @@ gained a name (`LogoutResult`); `.revoked` is unchanged.
|
|
|
216
340
|
robot that has never connected to a value in your app.
|
|
217
341
|
- **[REST and realtime API](https://docs.fleetless.dev/reference/api/)** — the
|
|
218
342
|
wire surface underneath this package.
|
|
219
|
-
- **[
|
|
220
|
-
|
|
343
|
+
- **[Your own login UI](https://docs.fleetless.dev/recipes/app-auth/)** — the
|
|
344
|
+
full walkthrough for every auth screen, and the mistakes that cost the most
|
|
345
|
+
time.
|
|
346
|
+
- **[Identity](https://docs.fleetless.dev/reference/identity/)** — the two
|
|
347
|
+
identity spaces, the federation rules, and what each refusal licenses your
|
|
348
|
+
UI to claim.
|
|
221
349
|
- **[CHANGELOG.md](CHANGELOG.md)** — what changed in each version.
|
|
222
350
|
- Questions, bug reports and feature requests: hello@fleetless.dev.
|
|
223
351
|
|
|
224
352
|
Fleetless is in closed beta. The waiting list is at
|
|
225
353
|
<https://fleetless.dev/#waiting-list>.
|
|
226
354
|
|
|
355
|
+
## Reporting a security issue
|
|
356
|
+
|
|
357
|
+
Email **security@fleetless.dev**. Please do not open a public issue for a
|
|
358
|
+
security report. [SECURITY.md](SECURITY.md) says what is in scope here — how
|
|
359
|
+
this package handles tokens, the PKCE verifier and the `state` value — and what
|
|
360
|
+
belongs to the platform instead.
|
|
361
|
+
|
|
362
|
+
## Contributing
|
|
363
|
+
|
|
364
|
+
Pull requests are welcome at <https://github.com/fleetless/sdk>. Read
|
|
365
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) first: it covers the setup, the checks, the
|
|
366
|
+
Contributor Licence Agreement, and the one rule this repository is strict about
|
|
367
|
+
— the auth suites drive the SDK's own `fetch` against a real `node:http`
|
|
368
|
+
server rather than a double, because a mocked `fetch` cannot see the bugs that
|
|
369
|
+
have actually shipped.
|
|
370
|
+
|
|
371
|
+
By participating you agree to the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
372
|
+
|
|
227
373
|
## Maintainers
|
|
228
374
|
|
|
229
|
-
Maintained by [Dehne Robotik GmbH](https://dehne-robotik.de).
|
|
230
|
-
repository is private and takes no outside contributions; questions and bug
|
|
231
|
-
reports go to hello@fleetless.dev.
|
|
375
|
+
Maintained by [Dehne Robotik GmbH](https://dehne-robotik.de).
|
|
232
376
|
|
|
233
377
|
MIT licensed — see [LICENSE](LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Email **security@fleetless.dev**. Please do not open a public GitHub issue for
|
|
6
|
+
a security report.
|
|
7
|
+
|
|
8
|
+
Include what you found, the SDK version and runtime it happens on (a browser
|
|
9
|
+
name and version, or a Node version), and the smallest sequence of SDK calls
|
|
10
|
+
that shows it. A reproduction against a published version of this package is
|
|
11
|
+
the most useful thing you can send.
|
|
12
|
+
|
|
13
|
+
We acknowledge every report within **3 working days** and follow up with
|
|
14
|
+
either a fix or a written plan within **30 days**. If a report leads to a
|
|
15
|
+
released fix, we credit you by name unless you ask us not to.
|
|
16
|
+
|
|
17
|
+
## What is in scope
|
|
18
|
+
|
|
19
|
+
This repository is the TypeScript SDK that client applications use to talk to
|
|
20
|
+
Fleetless. It runs **in your users' browsers and on your servers**, and it
|
|
21
|
+
holds credentials while it does. That is where its security surface is.
|
|
22
|
+
|
|
23
|
+
Specifically in scope:
|
|
24
|
+
|
|
25
|
+
### Tokens
|
|
26
|
+
|
|
27
|
+
The SDK receives an access token and a refresh token from the platform and
|
|
28
|
+
hands them to a `TokenStore` that **you** implement; the built-in default keeps
|
|
29
|
+
them in memory only and writes them nowhere. A report is in scope if the SDK:
|
|
30
|
+
|
|
31
|
+
- puts a token somewhere the caller did not ask for it to go — a global, a
|
|
32
|
+
URL, a query string, a log line, an error message, or a thrown object's
|
|
33
|
+
properties;
|
|
34
|
+
- sends a token to an origin other than the `apiUrl` the client was
|
|
35
|
+
constructed with;
|
|
36
|
+
- attaches a token to a request it should not have (the SDK refuses an
|
|
37
|
+
absolute URL it did not build — see `untrusted_absolute_url`);
|
|
38
|
+
- keeps a token reachable after `logout()`, or fails to clear the store;
|
|
39
|
+
- races its own silent refresh in a way that lets a stale or a foreign token
|
|
40
|
+
be used.
|
|
41
|
+
|
|
42
|
+
Choosing to persist tokens in `localStorage`, a cookie or a native keystore is
|
|
43
|
+
your decision and its consequences are yours; a defect in how the SDK *hands*
|
|
44
|
+
them to your store is ours.
|
|
45
|
+
|
|
46
|
+
### PKCE and `state` in the federated sign-in flow
|
|
47
|
+
|
|
48
|
+
`beginOidcLogin` generates a PKCE verifier and a `state` value and returns them
|
|
49
|
+
to you, because a redirect is a fresh page load and the SDK has nowhere of its
|
|
50
|
+
own to keep them. `completeOidcLogin` checks `state` before it exchanges
|
|
51
|
+
anything.
|
|
52
|
+
|
|
53
|
+
In scope: a verifier or a `state` with insufficient entropy or a predictable
|
|
54
|
+
source; S256 not being enforced; `completeOidcLogin` exchanging a code when
|
|
55
|
+
`state` does not match, is missing, or when `expectedState` is empty — that
|
|
56
|
+
last case is checked first and explicitly, because two empty strings compare
|
|
57
|
+
equal; any path that reaches the token exchange without the check.
|
|
58
|
+
|
|
59
|
+
**Where you store the verifier and the `state` between the two calls is your
|
|
60
|
+
application's decision**, and this policy cannot cover it. The README shows
|
|
61
|
+
`sessionStorage`, which is a reasonable default and not the only correct one.
|
|
62
|
+
|
|
63
|
+
### The rest of the package
|
|
64
|
+
|
|
65
|
+
- Request construction: path segments are percent-encoded (`pathSegment`), so
|
|
66
|
+
a slug or an identifier shaped like `../../../admin` must not escape its
|
|
67
|
+
position in the URL.
|
|
68
|
+
- The realtime channel: its authentication, its reconnection, and what it
|
|
69
|
+
resends after one.
|
|
70
|
+
- `prepareUrdfScene` and `createMeshLoader`: a robot's own URDF names the URLs
|
|
71
|
+
three.js is asked to fetch, so a reference the SDK does not own must not
|
|
72
|
+
become a network request from your users' browsers.
|
|
73
|
+
- Anything the SDK writes into browser-reachable state.
|
|
74
|
+
- The published npm package `@fleetless/sdk` and its contents, including a
|
|
75
|
+
dependency of it.
|
|
76
|
+
|
|
77
|
+
## What is not in scope
|
|
78
|
+
|
|
79
|
+
**The Fleetless cloud is not in this repository.** A server that fails to
|
|
80
|
+
enforce a permission, an authentication or authorisation flaw in the platform,
|
|
81
|
+
a rate limit, a data leak from an API endpoint, anything about how a token is
|
|
82
|
+
minted or validated — none of those live here, and none of them can be fixed by
|
|
83
|
+
a change to this package. Report them to the same address; say which service
|
|
84
|
+
you were looking at, and we will route it. What we cannot do is treat this
|
|
85
|
+
repository's issue tracker as the place where they are tracked.
|
|
86
|
+
|
|
87
|
+
Also out of scope here: the Fleetless console, the robot-side bridge, the
|
|
88
|
+
`@fleetless/contracts` schemas (they have their own repository and their own
|
|
89
|
+
policy), the documentation site, and any deployment of Fleetless operated by
|
|
90
|
+
someone else.
|
|
91
|
+
|
|
92
|
+
Out of scope in your own application: where you store tokens, how you protect
|
|
93
|
+
your own pages, and any XSS in your app — an attacker who can run script in
|
|
94
|
+
your page can read anything your page can, and no SDK design prevents that.
|
|
95
|
+
|
|
96
|
+
## Supported versions
|
|
97
|
+
|
|
98
|
+
The latest published minor of `@fleetless/sdk` receives fixes. Older minors do
|
|
99
|
+
not; a security fix is released as a new patch on the current minor, and
|
|
100
|
+
upgrading is the remedy.
|