@fleetless/contracts 1.0.5 → 1.1.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.
Files changed (96) hide show
  1. package/CHANGELOG.md +17 -2
  2. package/CONTRIBUTING.md +100 -75
  3. package/README.md +69 -83
  4. package/SECURITY.md +24 -24
  5. package/artifacts/openapi.json +359 -65
  6. package/artifacts/routes.json +74 -3
  7. package/artifacts/schema/app-list-response.schema.json +2 -2
  8. package/artifacts/schema/app-oidc-provider-list-response.schema.json +1 -1
  9. package/artifacts/schema/app-oidc-provider.schema.json +1 -1
  10. package/artifacts/schema/app-user-list-response.schema.json +2 -2
  11. package/artifacts/schema/app-user.schema.json +2 -2
  12. package/artifacts/schema/app.schema.json +2 -2
  13. package/artifacts/schema/asset-list-response.schema.json +7 -7
  14. package/artifacts/schema/asset-sync-request.schema.json +1 -1
  15. package/artifacts/schema/asset-sync-status.schema.json +1 -1
  16. package/artifacts/schema/asset.schema.json +3 -3
  17. package/artifacts/schema/auth-me-response.schema.json +2 -2
  18. package/artifacts/schema/auth-ok.schema.json +1 -1
  19. package/artifacts/schema/authorization-server-metadata.schema.json +1 -1
  20. package/artifacts/schema/bridge-asset-progress.schema.json +1 -1
  21. package/artifacts/schema/busy-details.schema.json +3 -3
  22. package/artifacts/schema/client-identity.schema.json +1 -1
  23. package/artifacts/schema/client-login-request.schema.json +1 -1
  24. package/artifacts/schema/client-logout-request.schema.json +1 -1
  25. package/artifacts/schema/client-mcp-interaction.schema.json +1 -1
  26. package/artifacts/schema/client-robot-list-item.schema.json +70 -0
  27. package/artifacts/schema/client-robot-list-response.schema.json +83 -0
  28. package/artifacts/schema/cloud-config.schema.json +1 -1
  29. package/artifacts/schema/command-result.schema.json +3 -3
  30. package/artifacts/schema/config-draft-response.schema.json +1 -1
  31. package/artifacts/schema/config-version-response.schema.json +1 -1
  32. package/artifacts/schema/create-server-key-response.schema.json +1 -1
  33. package/artifacts/schema/datapoint-config.schema.json +1 -1
  34. package/artifacts/schema/datapoint-value.schema.json +2 -2
  35. package/artifacts/schema/dynamic-client-registration-request.schema.json +2 -2
  36. package/artifacts/schema/fleetless-user-list-response.schema.json +2 -2
  37. package/artifacts/schema/fleetless-user.schema.json +2 -2
  38. package/artifacts/schema/invoke-or-service-response.schema.json +4 -4
  39. package/artifacts/schema/invoke-response.schema.json +3 -3
  40. package/artifacts/schema/job-actor.schema.json +1 -1
  41. package/artifacts/schema/job-event.schema.json +3 -3
  42. package/artifacts/schema/job-response.schema.json +3 -3
  43. package/artifacts/schema/job-run-list-response.schema.json +2 -2
  44. package/artifacts/schema/job-run.schema.json +2 -2
  45. package/artifacts/schema/job.schema.json +3 -3
  46. package/artifacts/schema/mcp-consent-grant-list-response.schema.json +2 -2
  47. package/artifacts/schema/mcp-consent-grant.schema.json +2 -2
  48. package/artifacts/schema/oauth-authorize-query.schema.json +1 -1
  49. package/artifacts/schema/oauth-token-request.schema.json +1 -1
  50. package/artifacts/schema/patch-org-response.schema.json +1 -1
  51. package/artifacts/schema/patch-robot-response.schema.json +1 -1
  52. package/artifacts/schema/robot-config-doc.schema.json +1 -1
  53. package/artifacts/schema/robot-jobs-response.schema.json +3 -3
  54. package/artifacts/schema/role-list-response.schema.json +1 -1
  55. package/artifacts/schema/role.schema.json +1 -1
  56. package/artifacts/schema/server-key-list-response.schema.json +2 -2
  57. package/artifacts/schema/server-key.schema.json +1 -1
  58. package/artifacts/schema/service-call-response.schema.json +1 -1
  59. package/artifacts/schema/sign-up-response.schema.json +2 -2
  60. package/artifacts/schema/urdf-completeness.schema.json +2 -2
  61. package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +1 -1
  62. package/dist/alerts.d.ts +15 -17
  63. package/dist/alerts.js +15 -17
  64. package/dist/app-users.d.ts +5 -6
  65. package/dist/app-users.js +9 -10
  66. package/dist/apps.d.ts +5 -6
  67. package/dist/apps.js +11 -12
  68. package/dist/assets.js +10 -13
  69. package/dist/audit.d.ts +10 -12
  70. package/dist/audit.js +14 -17
  71. package/dist/client-auth.d.ts +8 -9
  72. package/dist/client-auth.js +14 -15
  73. package/dist/client-robots.d.ts +37 -0
  74. package/dist/client-robots.js +30 -0
  75. package/dist/config-issues.d.ts +3 -3
  76. package/dist/config-issues.js +3 -3
  77. package/dist/config.d.ts +5 -6
  78. package/dist/config.js +7 -8
  79. package/dist/errors.d.ts +4 -4
  80. package/dist/errors.js +8 -9
  81. package/dist/identity.d.ts +4 -5
  82. package/dist/identity.js +7 -8
  83. package/dist/index.d.ts +3 -1
  84. package/dist/index.js +2 -1
  85. package/dist/jobs.js +5 -5
  86. package/dist/mcp.d.ts +11 -9
  87. package/dist/mcp.js +8 -3
  88. package/dist/oauth.d.ts +8 -10
  89. package/dist/oauth.js +13 -15
  90. package/dist/protocol.d.ts +7 -8
  91. package/dist/protocol.js +20 -22
  92. package/dist/realtime.d.ts +2 -2
  93. package/dist/realtime.js +4 -4
  94. package/dist/rest.js +4 -4
  95. package/dist/routes.js +40 -4
  96. package/package.json +1 -1
@@ -23,7 +23,7 @@
23
23
  "type": "string",
24
24
  "format": "uuid",
25
25
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
26
- "description": "The job's id, minted by the cloud when the invocation is accepted. Informative: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
26
+ "description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
27
27
  },
28
28
  "robot_id": {
29
29
  "type": "string",
@@ -47,13 +47,13 @@
47
47
  "cancelled",
48
48
  "lost"
49
49
  ],
50
- "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — and is said out loud rather than left reading `running` because nobody contradicted it."
50
+ "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
51
51
  },
52
52
  "started_at": {
53
53
  "type": "string",
54
54
  "format": "date-time",
55
55
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
56
- "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
56
+ "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
57
57
  },
58
58
  "updated_at": {
59
59
  "type": "string",
@@ -111,7 +111,7 @@
111
111
  ]
112
112
  },
113
113
  "description": {
114
- "description": "Prose about what this value is, for whoever meets it in the console later. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
114
+ "description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
115
115
  "examples": [
116
116
  "What this value is, for whoever meets it in the console."
117
117
  ],
@@ -119,7 +119,7 @@
119
119
  ]
120
120
  },
121
121
  "description": {
122
- "description": "Prose about what this value is, for whoever meets it in the console later. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
122
+ "description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
123
123
  "examples": [
124
124
  "What this value is, for whoever meets it in the console."
125
125
  ],
@@ -9,7 +9,7 @@
9
9
  "type": "string",
10
10
  "format": "uuid",
11
11
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
12
- "description": "The key row, and what the rotate and delete routes address. It is not the key: the secret itself is never carried by this shape."
12
+ "description": "The key row, and what the rotate and delete routes address. It is not the key: this shape never carries the secret."
13
13
  },
14
14
  "app_id": {
15
15
  "type": "string",
@@ -45,7 +45,7 @@
45
45
  ]
46
46
  },
47
47
  "description": {
48
- "description": "Prose about what this value is, for whoever meets it in the console later. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
48
+ "description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
49
49
  "examples": [
50
50
  "What this value is, for whoever meets it in the console."
51
51
  ],
@@ -10,13 +10,13 @@
10
10
  "description": "The datapoint this value belongs to."
11
11
  },
12
12
  "value": {
13
- "description": "The value itself, shaped by the datapoint: a number, a boolean, a string, or the whole ROS message where the configuration names no field inside it. Any `scale` and `offset` the configuration declares have already been applied, at the robot."
13
+ "description": "The value, shaped by the datapoint: a number, a boolean, a string, or the whole ROS message where the configuration names no field inside it. Any `scale` and `offset` the configuration declares have already been applied, at the robot."
14
14
  },
15
15
  "timestamp_ms": {
16
16
  "type": "integer",
17
17
  "minimum": 0,
18
18
  "maximum": 9007199254740991,
19
- "description": "When the value was captured, as a unix timestamp in milliseconds. This is the **bridge's capture time**, never the time the cloud received it — the one exception is the built-in `bridge_state`, which the cloud observes by construction."
19
+ "description": "When the value was captured, as a unix timestamp in milliseconds. The **bridge's capture time**, never the time the cloud received it — the one exception is the built-in `bridge_state`, which the cloud observes by construction."
20
20
  }
21
21
  },
22
22
  "required": [
@@ -11,10 +11,10 @@
11
11
  "minLength": 1,
12
12
  "maxLength": 2000
13
13
  },
14
- "description": "Where the authorization code may be returned, and the one field a registration cannot omit. Each must be an `https` URL, or `http` on an explicit loopback address for a native app that cannot hold a certificate, and none may carry a fragment. There must be between `1` and `5` of them; duplicates are collapsed rather than counted twice. Matched **exactly** at the authorize step against what was registered here."
14
+ "description": "Where the authorization code may be returned, and the one field a registration cannot omit. Each must be an `https` URL, or `http` on an explicit loopback address for a native app that cannot hold a certificate, and none may carry a fragment. Between `1` and `5` of them; duplicates are collapsed rather than counted twice. Matched **exactly** at the authorize step against what was registered here."
15
15
  },
16
16
  "client_name": {
17
- "description": "The name the client calls itself. Optional — a registration without one is recorded under a default name, per RFC 7591's making every metadata field optional. It is **not** vouched for by Fleetless and must never be rendered as if it were: a self-registered client chooses this string, and one has called itself *\"Fleetless Official Helper\"*.",
17
+ "description": "The name the client calls itself. Optional: RFC 7591 makes every metadata field optional, so a registration without one is recorded under a default name. It is **not** vouched for by Fleetless and must never be rendered as if it were: a self-registered client chooses this string, and one has called itself *\"Fleetless Official Helper\"*.",
18
18
  "type": "string",
19
19
  "minLength": 1,
20
20
  "maxLength": 200
@@ -17,7 +17,7 @@
17
17
  "type": "string",
18
18
  "format": "uuid",
19
19
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
20
- "description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at rather than being a filter it applies."
20
+ "description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at, not a filter it applies."
21
21
  },
22
22
  "email": {
23
23
  "type": "string",
@@ -44,7 +44,7 @@
44
44
  "owner",
45
45
  "developer"
46
46
  ],
47
- "description": "The console powers this person holds. **Required** — every Fleetless user is a member of the team and has a tier; the optional version of this field existed only while the org also held people with no console powers to grade, and that pool is gone."
47
+ "description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
48
48
  },
49
49
  "created_at": {
50
50
  "type": "string",
@@ -12,7 +12,7 @@
12
12
  "type": "string",
13
13
  "format": "uuid",
14
14
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
15
- "description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at rather than being a filter it applies."
15
+ "description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at, not a filter it applies."
16
16
  },
17
17
  "email": {
18
18
  "type": "string",
@@ -39,7 +39,7 @@
39
39
  "owner",
40
40
  "developer"
41
41
  ],
42
- "description": "The console powers this person holds. **Required** — every Fleetless user is a member of the team and has a tier; the optional version of this field existed only while the org also held people with no console powers to grade, and that pool is gone."
42
+ "description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
43
43
  },
44
44
  "created_at": {
45
45
  "type": "string",
@@ -11,7 +11,7 @@
11
11
  "type": "string",
12
12
  "format": "uuid",
13
13
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
14
- "description": "The job's id, minted by the cloud when the invocation is accepted. Informative: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
14
+ "description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
15
15
  },
16
16
  "robot_id": {
17
17
  "type": "string",
@@ -35,13 +35,13 @@
35
35
  "cancelled",
36
36
  "lost"
37
37
  ],
38
- "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — and is said out loud rather than left reading `running` because nobody contradicted it."
38
+ "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
39
39
  },
40
40
  "started_at": {
41
41
  "type": "string",
42
42
  "format": "date-time",
43
43
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
44
- "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
44
+ "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
45
45
  },
46
46
  "updated_at": {
47
47
  "type": "string",
@@ -129,7 +129,7 @@
129
129
  "type": "object",
130
130
  "properties": {
131
131
  "result": {
132
- "description": "What the service returned, shaped by the ROS service itself. A service call is awaited to completion, so there is no job to observe afterwards and no id to hold on to."
132
+ "description": "What the service returned, shaped by the ROS service. A service call is awaited to completion, so there is no job to observe afterwards and no id to hold on to."
133
133
  }
134
134
  },
135
135
  "required": [
@@ -9,7 +9,7 @@
9
9
  "type": "string",
10
10
  "format": "uuid",
11
11
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
12
- "description": "The job's id, minted by the cloud when the invocation is accepted. Informative: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
12
+ "description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
13
13
  },
14
14
  "robot_id": {
15
15
  "type": "string",
@@ -33,13 +33,13 @@
33
33
  "cancelled",
34
34
  "lost"
35
35
  ],
36
- "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — and is said out loud rather than left reading `running` because nobody contradicted it."
36
+ "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
37
37
  },
38
38
  "started_at": {
39
39
  "type": "string",
40
40
  "format": "date-time",
41
41
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
42
- "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
42
+ "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
43
43
  },
44
44
  "updated_at": {
45
45
  "type": "string",
@@ -10,7 +10,7 @@
10
10
  "app_user",
11
11
  "server_key"
12
12
  ],
13
- "description": "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool — it is kept so a history page can still render them, and nothing writes it any more."
13
+ "description": "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool — kept so old runs still render; nothing writes it now."
14
14
  },
15
15
  "id": {
16
16
  "type": "string",
@@ -24,7 +24,7 @@
24
24
  "type": "string",
25
25
  "format": "uuid",
26
26
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
27
- "description": "The job's id, minted by the cloud when the invocation is accepted. Informative: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
27
+ "description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
28
28
  },
29
29
  "robot_id": {
30
30
  "type": "string",
@@ -48,13 +48,13 @@
48
48
  "cancelled",
49
49
  "lost"
50
50
  ],
51
- "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — and is said out loud rather than left reading `running` because nobody contradicted it."
51
+ "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
52
52
  },
53
53
  "started_at": {
54
54
  "type": "string",
55
55
  "format": "date-time",
56
56
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
57
- "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
57
+ "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
58
58
  },
59
59
  "updated_at": {
60
60
  "type": "string",
@@ -11,7 +11,7 @@
11
11
  "type": "string",
12
12
  "format": "uuid",
13
13
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
14
- "description": "The job's id, minted by the cloud when the invocation is accepted. Informative: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
14
+ "description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
15
15
  },
16
16
  "robot_id": {
17
17
  "type": "string",
@@ -35,13 +35,13 @@
35
35
  "cancelled",
36
36
  "lost"
37
37
  ],
38
- "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — and is said out loud rather than left reading `running` because nobody contradicted it."
38
+ "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
39
39
  },
40
40
  "started_at": {
41
41
  "type": "string",
42
42
  "format": "date-time",
43
43
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
44
- "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
44
+ "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
45
45
  },
46
46
  "updated_at": {
47
47
  "type": "string",
@@ -11,7 +11,7 @@
11
11
  "type": "string",
12
12
  "format": "uuid",
13
13
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
14
- "description": "The run's id, which is the same id the invocation was answered with — so a caller that kept a job id can find its durable record here later."
14
+ "description": "The run's id — the same id the invocation was answered with, so a caller that kept a job id can find its durable record here later."
15
15
  },
16
16
  "robot_id": {
17
17
  "type": "string",
@@ -128,7 +128,7 @@
128
128
  "app_user",
129
129
  "server_key"
130
130
  ],
131
- "description": "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool — it is kept so a history page can still render them, and nothing writes it any more."
131
+ "description": "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool — kept so old runs still render; nothing writes it now."
132
132
  },
133
133
  "id": {
134
134
  "type": "string",
@@ -6,7 +6,7 @@
6
6
  "type": "string",
7
7
  "format": "uuid",
8
8
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
9
- "description": "The run's id, which is the same id the invocation was answered with — so a caller that kept a job id can find its durable record here later."
9
+ "description": "The run's id — the same id the invocation was answered with, so a caller that kept a job id can find its durable record here later."
10
10
  },
11
11
  "robot_id": {
12
12
  "type": "string",
@@ -123,7 +123,7 @@
123
123
  "app_user",
124
124
  "server_key"
125
125
  ],
126
- "description": "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool — it is kept so a history page can still render them, and nothing writes it any more."
126
+ "description": "What the caller was acting as: a `developer` in the console, an `app_user` of one app, or a `server_key` used by server-side code. A bridge invokes nothing, so it is deliberately not a case here. `end_user` appears only on runs recorded before app users replaced the organisation-wide user pool — kept so old runs still render; nothing writes it now."
127
127
  },
128
128
  "id": {
129
129
  "type": "string",
@@ -6,7 +6,7 @@
6
6
  "type": "string",
7
7
  "format": "uuid",
8
8
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
9
- "description": "The job's id, minted by the cloud when the invocation is accepted. Informative: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
9
+ "description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
10
10
  },
11
11
  "robot_id": {
12
12
  "type": "string",
@@ -30,13 +30,13 @@
30
30
  "cancelled",
31
31
  "lost"
32
32
  ],
33
- "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — and is said out loud rather than left reading `running` because nobody contradicted it."
33
+ "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
34
34
  },
35
35
  "started_at": {
36
36
  "type": "string",
37
37
  "format": "date-time",
38
38
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
39
- "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
39
+ "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
40
40
  },
41
41
  "updated_at": {
42
42
  "type": "string",
@@ -20,12 +20,12 @@
20
20
  "type": "null"
21
21
  }
22
22
  ],
23
- "description": "What the client calls itself, or `null` when its registration is gone and there is no longer anything to have named. **Unverified** — see `client_name_verified`."
23
+ "description": "What the client calls itself, or `null` once its registration is gone. **Unverified** — see `client_name_verified`."
24
24
  },
25
25
  "client_name_verified": {
26
26
  "type": "boolean",
27
27
  "const": false,
28
- "description": "Always `false`. The client registered itself without authentication and chose this name about itself, so it must be rendered as a claim and never as an identity. There is no verified case, which is why this is a literal and not a boolean: a `true` branch would be dead code that looked like a safeguard."
28
+ "description": "Always `false`. The client registered itself without authentication and named itself, so it must be rendered as a claim and never as an identity. There is no verified case, which is why this is a literal and not a boolean: a `true` branch would be dead code that looked like a safeguard."
29
29
  },
30
30
  "granted_at": {
31
31
  "type": "string",
@@ -15,12 +15,12 @@
15
15
  "type": "null"
16
16
  }
17
17
  ],
18
- "description": "What the client calls itself, or `null` when its registration is gone and there is no longer anything to have named. **Unverified** — see `client_name_verified`."
18
+ "description": "What the client calls itself, or `null` once its registration is gone. **Unverified** — see `client_name_verified`."
19
19
  },
20
20
  "client_name_verified": {
21
21
  "type": "boolean",
22
22
  "const": false,
23
- "description": "Always `false`. The client registered itself without authentication and chose this name about itself, so it must be rendered as a claim and never as an identity. There is no verified case, which is why this is a literal and not a boolean: a `true` branch would be dead code that looked like a safeguard."
23
+ "description": "Always `false`. The client registered itself without authentication and named itself, so it must be rendered as a claim and never as an identity. There is no verified case, which is why this is a literal and not a boolean: a `true` branch would be dead code that looked like a safeguard."
24
24
  },
25
25
  "granted_at": {
26
26
  "type": "string",
@@ -20,7 +20,7 @@
20
20
  "code_challenge": {
21
21
  "type": "string",
22
22
  "minLength": 1,
23
- "description": "The PKCE challenge; the verifier is presented at the token endpoint. Only non-emptiness is checked here — length and alphabet are not — since the verifier is what actually has to match."
23
+ "description": "The PKCE challenge; the verifier is presented at the token endpoint. Only non-emptiness is checked here — length and alphabet are not — since the verifier is what has to match."
24
24
  },
25
25
  "code_challenge_method": {
26
26
  "type": "string",
@@ -31,7 +31,7 @@
31
31
  "description": "The PKCE verifier whose `S256` hash was sent as the challenge at the authorize step. Between `43` and `128` unreserved characters, per RFC 7636 §4.1 — it is compared rather than parsed, so a length nobody checks is a length an attacker chooses. PKCE is mandatory for every client under OAuth 2.1."
32
32
  },
33
33
  "resource": {
34
- "description": "The resource the token is being requested for, per RFC 8707. It must match the audience the code was authorized for, or the answer is `invalid_target`; omitted, the code's own audience stands. It becomes the token's `aud`, and a resource refuses a token whose audience names something else — which is what keeps a token minted for one app out of another app's endpoint.",
34
+ "description": "The resource the token is requested for, per RFC 8707. It must match the audience the code was authorized for, or the answer is `invalid_target`; omitted, the code's own audience stands. It becomes the token's `aud`, and a resource refuses a token whose audience names something else — which is what keeps a token minted for one app out of another app's endpoint.",
35
35
  "type": "string",
36
36
  "format": "uri"
37
37
  }
@@ -30,7 +30,7 @@
30
30
  "created_at"
31
31
  ],
32
32
  "additionalProperties": false,
33
- "description": "The organisation as it now stands, after the patch was applied. The whole resource comes back, not only the fields that changed."
33
+ "description": "The organisation as it now stands, after the patch. The whole resource comes back, not only the changed fields."
34
34
  }
35
35
  },
36
36
  "required": [
@@ -30,7 +30,7 @@
30
30
  "created_at"
31
31
  ],
32
32
  "additionalProperties": false,
33
- "description": "The robot as it now stands, after the patch was applied. The whole resource comes back, not only the fields that changed."
33
+ "description": "The robot as it now stands, after the patch. The whole resource comes back, not only the changed fields."
34
34
  }
35
35
  },
36
36
  "required": [
@@ -106,7 +106,7 @@
106
106
  ]
107
107
  },
108
108
  "description": {
109
- "description": "Prose about what this value is, for whoever meets it in the console later. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
109
+ "description": "Prose about what this value is, for whoever meets it in the console. It changes nothing the robot does, so a publish that touches only it pushes no configuration at all — but it is carried verbatim into `robot_describe`, where a model that has never seen this robot reads it. The datapoint is offered whenever the role grants it; without one it is offered with `description: null` and the model has less to go on, as for actions, services, publishers and cameras. Omission is the only way to say nothing; an empty string is refused, here and on all five.",
110
110
  "examples": [
111
111
  "What this value is, for whoever meets it in the console."
112
112
  ],
@@ -11,7 +11,7 @@
11
11
  "type": "string",
12
12
  "format": "uuid",
13
13
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
14
- "description": "The job's id, minted by the cloud when the invocation is accepted. Informative: state is observed by slug, and this id is what a cancel names when a caller wants to stop one specific job rather than whatever is running."
14
+ "description": "The job's id, minted by the cloud when the invocation is accepted. Informative — state is observed by slug; a cancel names this id to stop one specific job rather than whatever is running."
15
15
  },
16
16
  "robot_id": {
17
17
  "type": "string",
@@ -35,13 +35,13 @@
35
35
  "cancelled",
36
36
  "lost"
37
37
  ],
38
- "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — and is said out loud rather than left reading `running` because nobody contradicted it."
38
+ "description": "Where the job stands: `running`, `succeeded`, `failed`, `cancelled` or `lost`. `lost` is a real outcome — the bridge restarted mid-job and the result is gone — stated rather than left reading `running` by default."
39
39
  },
40
40
  "started_at": {
41
41
  "type": "string",
42
42
  "format": "date-time",
43
43
  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
44
- "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge it is **adoption time**, not the real start, because the cloud never minted it and has no honest alternative."
44
+ "description": "When the cloud minted this job, as an ISO 8601 timestamp. For a job adopted from a reconnecting bridge, this is **adoption time**, not the real start — the cloud never minted it."
45
45
  },
46
46
  "updated_at": {
47
47
  "type": "string",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "builtin": {
29
29
  "type": "boolean",
30
- "description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. It does not make them renamable or deletable, because no route renames or deletes any role."
30
+ "description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. It does not make them renamable or deletable — no route does that for any role."
31
31
  }
32
32
  },
33
33
  "required": [
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "builtin": {
24
24
  "type": "boolean",
25
- "description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. It does not make them renamable or deletable, because no route renames or deletes any role."
25
+ "description": "`true` for the two roles every app starts with. Their **rights may be re-scoped** exactly like a custom role's, through `PUT /api/apps/:id/roles/:roleId/permissions` — the flag exists so the console can explain where they came from, not to protect them. It does not make them renamable or deletable — no route does that for any role."
26
26
  }
27
27
  },
28
28
  "required": [
@@ -11,7 +11,7 @@
11
11
  "type": "string",
12
12
  "format": "uuid",
13
13
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
14
- "description": "The key row, and what the rotate and delete routes address. It is not the key: the secret itself is never carried by this shape."
14
+ "description": "The key row, and what the rotate and delete routes address. It is not the key: this shape never carries the secret."
15
15
  },
16
16
  "app_id": {
17
17
  "type": "string",
@@ -54,7 +54,7 @@
54
54
  ],
55
55
  "additionalProperties": false
56
56
  },
57
- "description": "The app's server keys as metadata, oldest first by `created_at`. The raw secret is not here and never will be: it exists once, in the answer to the request that created or rotated the key."
57
+ "description": "The app's server keys as metadata, oldest first by `created_at`. The raw secret is not here and never will be: it exists once, in the response that created or rotated the key."
58
58
  }
59
59
  },
60
60
  "required": [
@@ -6,7 +6,7 @@
6
6
  "type": "string",
7
7
  "format": "uuid",
8
8
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
9
- "description": "The key row, and what the rotate and delete routes address. It is not the key: the secret itself is never carried by this shape."
9
+ "description": "The key row, and what the rotate and delete routes address. It is not the key: this shape never carries the secret."
10
10
  },
11
11
  "app_id": {
12
12
  "type": "string",
@@ -3,7 +3,7 @@
3
3
  "type": "object",
4
4
  "properties": {
5
5
  "result": {
6
- "description": "What the service returned, shaped by the ROS service itself. A service call is awaited to completion, so there is no job to observe afterwards and no id to hold on to."
6
+ "description": "What the service returned, shaped by the ROS service. A service call is awaited to completion, so there is no job to observe afterwards and no id to hold on to."
7
7
  }
8
8
  },
9
9
  "required": [
@@ -44,7 +44,7 @@
44
44
  "type": "string",
45
45
  "format": "uuid",
46
46
  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
47
- "description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at rather than being a filter it applies."
47
+ "description": "The organisation this person belongs to. Every developer route is already scoped to the caller's org, so this confirms what a client is looking at, not a filter it applies."
48
48
  },
49
49
  "email": {
50
50
  "type": "string",
@@ -71,7 +71,7 @@
71
71
  "owner",
72
72
  "developer"
73
73
  ],
74
- "description": "The console powers this person holds. **Required** — every Fleetless user is a member of the team and has a tier; the optional version of this field existed only while the org also held people with no console powers to grade, and that pool is gone."
74
+ "description": "The console powers this person holds. **Required** — every Fleetless user has a tier; it was optional only while the org also held people with no console powers to grade, and that pool is gone."
75
75
  },
76
76
  "created_at": {
77
77
  "type": "string",
@@ -21,7 +21,7 @@
21
21
  "type": "string",
22
22
  "minLength": 1,
23
23
  "maxLength": 500,
24
- "description": "The reference, verbatim, that no stored asset answers — a `package://` URI the workspace does not hold, or an absolute or bare relative path nothing will ever fetch. A developer whose URDF names one of the latter is entitled to be told so."
24
+ "description": "The reference, verbatim, that no stored asset answers — a `package://` URI the workspace does not hold, or an absolute or bare relative path nothing will ever fetch."
25
25
  },
26
26
  "element": {
27
27
  "type": "string",
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "additionalProperties": false
40
40
  },
41
- "description": "The references nothing in the store answers, each with the element that asked for it. A bare count is a dead end that sends a developer hunting through a workspace by hand; the references are what they can act on, so the references travel."
41
+ "description": "The references nothing in the store answers, each with the element that asked for it. A bare count would send a developer hunting through the workspace by hand; the references are what they can act on."
42
42
  }
43
43
  },
44
44
  "required": [
@@ -59,7 +59,7 @@
59
59
  "type": "integer",
60
60
  "exclusiveMinimum": 0,
61
61
  "maximum": 9007199254740991,
62
- "description": "How large the refused file actually is, in bytes. With `limit_bytes` beside it a developer can tell whether to shrink the mesh or raise the limit; \"too large\" alone answers neither."
62
+ "description": "How large the refused file is, in bytes. With `limit_bytes` beside it a developer can tell whether to shrink the mesh or raise the limit; \"too large\" alone answers neither."
63
63
  }
64
64
  },
65
65
  "required": [