@myrobotaxi/contracts 0.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.
- package/CONTRIBUTING.md +67 -0
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/index.cjs +721 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +615 -0
- package/dist/index.d.ts +615 -0
- package/dist/index.js +719 -0
- package/dist/index.js.map +1 -0
- package/dist/types.cjs +4 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +590 -0
- package/dist/types.d.ts +590 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +77 -0
- package/schemas/vehicle-state.schema.json +343 -0
- package/schemas/ws-envelope.schema.json +65 -0
- package/schemas/ws-messages.schema.json +295 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://myrobotaxi.com/schemas/ws-messages.schema.json",
|
|
4
|
+
"title": "WebSocketMessages",
|
|
5
|
+
"description": "JSON Schemas for every WebSocket payload exchanged between the telemetry server and SDK clients. The vehicle_update payload's `fields` map is a subset of the canonical VehicleState (see vehicle-state.schema.json); per-field types/units/classification live there and are NOT duplicated here. This file holds the wire-only shapes (envelope payloads, control messages, errors).",
|
|
6
|
+
"$defs": {
|
|
7
|
+
"AuthPayload": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"description": "Client->server authentication payload. The first frame on every WebSocket connection MUST be {type: 'auth', payload: {token: '...'}}. The token is opaque to the server's transport layer and is validated by the configured Authenticator (JWT in production, NoopAuthenticator in dev). Anchored by FR-6.1, NFR-3.21.",
|
|
10
|
+
"required": ["token"],
|
|
11
|
+
"additionalProperties": false,
|
|
12
|
+
"properties": {
|
|
13
|
+
"token": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"minLength": 1,
|
|
16
|
+
"description": "Bearer-style session token resolved by the consumer's getToken() callback (FR-6.1). Server validates via Authenticator.ValidateToken. P1 — never log this value (data-classification.md §1.2).",
|
|
17
|
+
"x-classification": "P1"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"AuthOkPayload": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"description": "Server->client positive authentication acknowledgement. v1-required. Emitted as the FIRST frame after successful `Authenticator.ValidateToken` + `GetUserVehicles` + `Hub.Register`. Triggers the SDK `connectionState` transition `connecting -> connected` (state-machine C-3). Without this frame, SDKs on idle vehicles would sit in `connecting` up to one heartbeat interval waiting for telemetry traffic — an unacceptable regression on cold watchOS wakes. See websocket-protocol.md §2.3 for the full handshake contract. Note: `auth_ok` has been pulled OUT of divergence DV-07 and is v1-required; the rest of DV-07 (subscribe/unsubscribe/ping/pong + typed permission_denied) remains deferred. `state-machine.md` §1.3 C-3 trigger alignment is tracked as DV-15 (requires a follow-up MYR-10 amendment PR). Anchored by FR-6.1, FR-8.1, NFR-3.21.",
|
|
24
|
+
"required": ["userId", "vehicleCount", "issuedAt"],
|
|
25
|
+
"additionalProperties": false,
|
|
26
|
+
"properties": {
|
|
27
|
+
"userId": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Opaque cuid of the authenticated user. Echoed back from the resolved token so the SDK can sanity-check ownership set on reconnect (e.g., compare against a cached userId and clear caches if they disagree, defending against consumer token-mixup bugs). P1 per data-classification.md §1.1 User.id.",
|
|
30
|
+
"x-classification": "P1"
|
|
31
|
+
},
|
|
32
|
+
"vehicleCount": {
|
|
33
|
+
"type": "integer",
|
|
34
|
+
"minimum": 0,
|
|
35
|
+
"description": "Size of the user's vehicle set at handshake time (length of `Authenticator.GetUserVehicles(userId)`). Informational integrity check against the SDK's cached snapshot; the authoritative ownership set is populated via the REST snapshot fetch on reconnect (NFR-3.11). P1 because vehicle count leaks the shape of a user's fleet.",
|
|
36
|
+
"x-classification": "P1"
|
|
37
|
+
},
|
|
38
|
+
"issuedAt": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"format": "date-time",
|
|
41
|
+
"description": "Server-authoritative ISO 8601 UTC timestamp at which Hub.Register returned success. Debug/telemetry only — the SDK MUST NOT use this value for any behavioral decision. P2 because it reveals server-side timing to an observer.",
|
|
42
|
+
"x-classification": "P2"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"VehicleUpdatePayload": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"description": "Server->client telemetry update for a single vehicle. The `fields` map carries one or more atomic-group members from VehicleState (see vehicle-state.schema.json). Each call to broadcast carries fields from at most ONE atomic group plus optionally non-grouped fields delivered alongside (NFR-3.1). Anchored by FR-1.1, FR-1.2, FR-1.3, FR-2.1, FR-2.2, FR-2.3, NFR-3.1, NFR-3.2.",
|
|
49
|
+
"required": ["vehicleId", "fields", "timestamp"],
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"properties": {
|
|
52
|
+
"vehicleId": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "Opaque database cuid for the vehicle (NOT the VIN). All SDK API calls use this ID per FR-4.2. VINs are P0 with mandatory last-4 redaction in logs (data-classification.md §2.1) and MUST NOT appear in this payload under any circumstance.",
|
|
55
|
+
"x-classification": "P0"
|
|
56
|
+
},
|
|
57
|
+
"fields": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"description": "Map of field name -> value. Field names and per-field classifications are defined in vehicle-state.schema.json. A single vehicle_update message contains the members of at most ONE atomic group plus any individually-delivered fields (speed, odometerMiles, interiorTemp, exteriorTemp, fsdMilesSinceReset, locationName, locationAddress, lastUpdated). v1 atomic groups: navigation = {destinationName, destinationAddress, destinationLatitude, destinationLongitude, originLatitude, originLongitude, etaMinutes, tripDistanceRemaining, navRouteCoordinates}; charge = {chargeLevel, chargeState, estimatedRange, timeToFull}; gps = {latitude, longitude, heading}; gear = {gearPosition, status}. The `drive` atomic group's single member `startedAt` is delivered via the `drive_started` lifecycle message, not via `vehicle_update.fields` — see DV-13 in websocket-protocol.md §10. Server explicitly clears a navigation field by sending it as JSON null; the SDK MUST atomically null all members of the affected group per NFR-3.9. The optional `driveTrailCoordinates` field (array of [lng, lat] pairs) carries an active drive's accumulated GPS trail (\"where the car has been\") and is not part of a normal atomic group — it is always paired with an active drive and routed to dataState.gps. It is semantically distinct from the navigation group's `navRouteCoordinates` (Tesla's planned route polyline; \"where the car is going\") and the two MUST NOT be conflated.",
|
|
60
|
+
"additionalProperties": true,
|
|
61
|
+
"x-classification": "mixed (per-field; see vehicle-state.schema.json)"
|
|
62
|
+
},
|
|
63
|
+
"timestamp": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"format": "date-time",
|
|
66
|
+
"description": "Server-authoritative ISO 8601 UTC timestamp for this update. Not the Tesla telemetry emission time — this is the server's `time.Now().UTC()` at broadcast (or the event's CreatedAt for non-nav fields). Drives the SDK's lastUpdated state and feeds NFR-3.11 snapshot resume.",
|
|
67
|
+
"x-classification": "P0"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"DriveStartedPayload": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"description": "Server->client message announcing the start of a new drive. Also carries the drive atomic group's `startedAt` field, which is the v1 home of `tripStartTime` (relocated from the navigation group per DV-13 — see websocket-protocol.md §4.2). The drive group is NOT delivered via vehicle_update.fields; it is delivered via this message. Anchored by FR-3.1, NFR-3.1.",
|
|
74
|
+
"required": ["vehicleId", "driveId", "startLocation", "startedAt", "timestamp"],
|
|
75
|
+
"additionalProperties": false,
|
|
76
|
+
"properties": {
|
|
77
|
+
"vehicleId": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "Opaque vehicle cuid.",
|
|
80
|
+
"x-classification": "P0"
|
|
81
|
+
},
|
|
82
|
+
"driveId": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"description": "Opaque drive cuid. Matches the eventual Drive row's id when the drive completes and is persisted.",
|
|
85
|
+
"x-classification": "P0"
|
|
86
|
+
},
|
|
87
|
+
"startLocation": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"description": "GPS coordinates where the drive began. Encrypted at rest in the eventual Drive row but transported plaintext over WSS (NFR-3.22, NFR-3.25).",
|
|
90
|
+
"required": ["latitude", "longitude"],
|
|
91
|
+
"additionalProperties": false,
|
|
92
|
+
"properties": {
|
|
93
|
+
"latitude": {
|
|
94
|
+
"type": "number",
|
|
95
|
+
"minimum": -90,
|
|
96
|
+
"maximum": 90,
|
|
97
|
+
"x-classification": "P1",
|
|
98
|
+
"x-encrypted-at-rest": true
|
|
99
|
+
},
|
|
100
|
+
"longitude": {
|
|
101
|
+
"type": "number",
|
|
102
|
+
"minimum": -180,
|
|
103
|
+
"maximum": 180,
|
|
104
|
+
"x-classification": "P1",
|
|
105
|
+
"x-encrypted-at-rest": true
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"startedAt": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"format": "date-time",
|
|
112
|
+
"description": "v1 home of `tripStartTime` (per DV-13). Server-authoritative ISO 8601 UTC timestamp at which the drive detector's `started_at` was recorded. SDKs MUST surface this as the authoritative tripStartTime for consumers. Value is always equal to `timestamp` for drive_started; the two fields are separate so `timestamp` can remain the uniform envelope-event time across every server->client message while `startedAt` carries the semantic drive-lifecycle meaning.",
|
|
113
|
+
"x-classification": "P0"
|
|
114
|
+
},
|
|
115
|
+
"timestamp": {
|
|
116
|
+
"type": "string",
|
|
117
|
+
"format": "date-time",
|
|
118
|
+
"description": "Server-authoritative ISO 8601 UTC timestamp at which the drive detector (`internal/drives/`) declared the drive started. Equal to `startedAt` for this message type.",
|
|
119
|
+
"x-classification": "P0"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"DriveEndedPayload": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"description": "Server->client message announcing a completed drive that passed the micro-drive filter (state-machine.md §3.5). The wire payload is intentionally a SUMMARY — it carries only the handful of fields the SDK needs for an immediate 'drive finished' toast or card. The full FR-3.4 drive record (energy used, FSD miles, intervention count, start/end charge level, start/end addresses, full route polyline) is fetched on demand via REST `GET /drives/{id}` (see rest-api.md) using the SDK's `fetchDrive(driveId)` helper. Neither the TypeScript nor the Swift SDK auto-fetches the full record on drive_ended; doing so would burn cellular bandwidth on idle consumers (per NFR-3.36, this is especially bad for watchOS). Tracked as DV-11 (RESOLVED) in websocket-protocol.md §10. Anchored by FR-3.1, FR-3.4.",
|
|
126
|
+
"required": ["vehicleId", "driveId", "distance", "durationSeconds", "avgSpeed", "maxSpeed", "timestamp"],
|
|
127
|
+
"additionalProperties": false,
|
|
128
|
+
"properties": {
|
|
129
|
+
"vehicleId": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"description": "Opaque vehicle cuid.",
|
|
132
|
+
"x-classification": "P0"
|
|
133
|
+
},
|
|
134
|
+
"driveId": {
|
|
135
|
+
"type": "string",
|
|
136
|
+
"description": "Opaque drive cuid (matches the persisted Drive row id). Input to `fetchDrive(driveId)` for the full FR-3.4 record.",
|
|
137
|
+
"x-classification": "P0"
|
|
138
|
+
},
|
|
139
|
+
"distance": {
|
|
140
|
+
"type": "number",
|
|
141
|
+
"minimum": 0,
|
|
142
|
+
"description": "Total distance driven in miles. Computed as the haversine sum of route points at drive completion.",
|
|
143
|
+
"x-unit": "miles",
|
|
144
|
+
"x-classification": "P0"
|
|
145
|
+
},
|
|
146
|
+
"durationSeconds": {
|
|
147
|
+
"type": "number",
|
|
148
|
+
"minimum": 0,
|
|
149
|
+
"description": "Drive duration in seconds, as a JSON number (double). Server-side value is `DriveStats.Duration.Seconds()`. This replaces the earlier `duration` string field (Go `time.Duration` format like '12m34s'), which was dropped before v1 ship — there are no pre-v1 consumers, so there is no deprecation tail. TypeScript consumers construct `Temporal.Duration` or a Date delta from this value; Swift consumers map directly to `Duration(secondsComponent:attosecondsComponent:)` or `TimeInterval`. Tracked as DV-12 (RESOLVED) in websocket-protocol.md §10.",
|
|
150
|
+
"x-unit": "seconds",
|
|
151
|
+
"x-classification": "P0"
|
|
152
|
+
},
|
|
153
|
+
"avgSpeed": {
|
|
154
|
+
"type": "number",
|
|
155
|
+
"minimum": 0,
|
|
156
|
+
"x-unit": "mph",
|
|
157
|
+
"x-classification": "P0"
|
|
158
|
+
},
|
|
159
|
+
"maxSpeed": {
|
|
160
|
+
"type": "number",
|
|
161
|
+
"minimum": 0,
|
|
162
|
+
"x-unit": "mph",
|
|
163
|
+
"x-classification": "P0"
|
|
164
|
+
},
|
|
165
|
+
"timestamp": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"format": "date-time",
|
|
168
|
+
"description": "Server-authoritative ISO 8601 UTC timestamp at which the drive detector declared the drive ended.",
|
|
169
|
+
"x-classification": "P0"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"ConnectivityPayload": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"description": "Server->client signal that a vehicle's mTLS connection to the telemetry server has changed state. This is vehicle<->server connectivity, NOT client<->server connectivity (the latter is implicit in the WebSocket connection itself). Anchored by FR-1.3 (extensibility) and FR-8.1 (state surface).",
|
|
176
|
+
"required": ["vehicleId", "online", "timestamp"],
|
|
177
|
+
"additionalProperties": false,
|
|
178
|
+
"properties": {
|
|
179
|
+
"vehicleId": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"x-classification": "P0"
|
|
182
|
+
},
|
|
183
|
+
"online": {
|
|
184
|
+
"type": "boolean",
|
|
185
|
+
"description": "True when the vehicle has an active mTLS Tesla Fleet Telemetry stream into the server. False on disconnect (e.g., vehicle asleep, network drop).",
|
|
186
|
+
"x-classification": "P0"
|
|
187
|
+
},
|
|
188
|
+
"timestamp": {
|
|
189
|
+
"type": "string",
|
|
190
|
+
"format": "date-time",
|
|
191
|
+
"x-classification": "P0"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"HeartbeatPayload": {
|
|
196
|
+
"type": "object",
|
|
197
|
+
"description": "Server->client keepalive frame. The current server emits a bare envelope `{\"type\":\"heartbeat\"}` with NO payload (heartbeat.go computes the message once at init). The optional `timestamp` field is reserved for future use. Anchored by NFR-3.10 (reconnect cadence).",
|
|
198
|
+
"additionalProperties": false,
|
|
199
|
+
"properties": {
|
|
200
|
+
"timestamp": {
|
|
201
|
+
"type": "string",
|
|
202
|
+
"format": "date-time",
|
|
203
|
+
"description": "OPTIONAL and NOT currently emitted. Reserved for future use to expose server clock to clients (would also enable client clock-skew detection). Tracked by follow-up issue.",
|
|
204
|
+
"x-classification": "P0"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"ErrorPayload": {
|
|
209
|
+
"type": "object",
|
|
210
|
+
"description": "Server->client error frame. The server sends this for transport-level failures the client must surface (auth rejection, auth timeout). The Code field is a stable enum that consumers branch on (FR-7.1) — never string-match the Message field. Anchored by FR-7.1, FR-7.3.",
|
|
211
|
+
"required": ["code", "message"],
|
|
212
|
+
"additionalProperties": false,
|
|
213
|
+
"properties": {
|
|
214
|
+
"code": {
|
|
215
|
+
"type": "string",
|
|
216
|
+
"description": "Stable typed error code from the catalog in websocket-protocol.md §6.1.1. Consumer SDKs map these to typed errors per FR-7.1 and MUST NOT string-match on the sibling `message` field. `auth_failed` and `auth_timeout` are emitted today; the remaining values are reserved for PLANNED server emission tracked by divergences DV-02 / DV-07 / DV-08 in websocket-protocol.md §10. `rate_limited` has two carriers: (a) an HTTP 429 response on the WS upgrade (per-IP cap breach, pre-auth) and (b) an `error` frame paired with WebSocket close code 4003 Server Overload (per-user cap breach, post-auth). The SDK MUST treat both carriers as the same typed error and apply the extended reconnect backoff documented in websocket-protocol.md §6.1.1 and §7.1.",
|
|
217
|
+
"enum": [
|
|
218
|
+
"auth_failed",
|
|
219
|
+
"auth_timeout",
|
|
220
|
+
"permission_denied",
|
|
221
|
+
"vehicle_not_owned",
|
|
222
|
+
"rate_limited",
|
|
223
|
+
"internal_error",
|
|
224
|
+
"snapshot_required"
|
|
225
|
+
],
|
|
226
|
+
"x-classification": "P0"
|
|
227
|
+
},
|
|
228
|
+
"message": {
|
|
229
|
+
"type": "string",
|
|
230
|
+
"description": "Human-readable description for logs and developer tooling. P0 BUT MUST NOT contain P1 values (no GPS, no addresses, no tokens, no emails, no VINs unless redacted to ***XXXX). Per data-classification.md §2.1-2.2, error message construction sites MUST use opaque IDs only.",
|
|
231
|
+
"x-classification": "P0"
|
|
232
|
+
},
|
|
233
|
+
"subCode": {
|
|
234
|
+
"type": "string",
|
|
235
|
+
"description": "Optional typed sub-code for branching consumer-visible UI when the primary code is ambiguous across carriers. Currently defined only for `rate_limited`: when the server rejects a connection because it breached the per-user cap (post-auth, close 4003), the error frame MUST carry `subCode: \"device_cap\"` so SDKs can surface an actionable 'Too many devices signed in' message instead of a generic rate-limit toast. Per-IP breaches (HTTP 429 on upgrade) do NOT carry a sub-code because the SDK cannot distinguish NAT-mate floods from user intent. See websocket-protocol.md §6.1.1 for the full device_cap UX contract.",
|
|
236
|
+
"enum": ["device_cap"],
|
|
237
|
+
"x-classification": "P0"
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
"SubscribePayload": {
|
|
242
|
+
"type": "object",
|
|
243
|
+
"description": "PLANNED — NOT accepted by the server today. Client->server request to begin or resume streaming a specific vehicle. Today the server implicitly subscribes the client to ALL vehicles owned by the authenticated user as part of the auth handshake (handler.go authenticateClient -> Authenticator.GetUserVehicles). Adding explicit subscribe/unsubscribe is tracked as divergence DV-07 in websocket-protocol.md §10. `sinceSeq` snapshot-resume depends on divergence DV-02 (envelope `seq`) landing first.",
|
|
244
|
+
"required": ["vehicleId"],
|
|
245
|
+
"additionalProperties": false,
|
|
246
|
+
"properties": {
|
|
247
|
+
"vehicleId": {
|
|
248
|
+
"type": "string",
|
|
249
|
+
"x-classification": "P0"
|
|
250
|
+
},
|
|
251
|
+
"sinceSeq": {
|
|
252
|
+
"type": "integer",
|
|
253
|
+
"minimum": 0,
|
|
254
|
+
"description": "PLANNED. Last envelope `seq` the client successfully processed for this vehicle. The server uses this to choose between snapshot-resume (replay missed frames) and full-refresh (REST snapshot fetch + live stream from current seq).",
|
|
255
|
+
"x-classification": "P0"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
"UnsubscribePayload": {
|
|
260
|
+
"type": "object",
|
|
261
|
+
"description": "PLANNED — NOT accepted by the server today. Client->server request to stop receiving updates for a specific vehicle without closing the underlying WebSocket. Tracked as divergence DV-07 in websocket-protocol.md §10. Note: `auth_ok` has been pulled out of DV-07 and is v1-required (see AuthOkPayload); the rest of DV-07 remains deferred.",
|
|
262
|
+
"required": ["vehicleId"],
|
|
263
|
+
"additionalProperties": false,
|
|
264
|
+
"properties": {
|
|
265
|
+
"vehicleId": {
|
|
266
|
+
"type": "string",
|
|
267
|
+
"x-classification": "P0"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"PingPayload": {
|
|
272
|
+
"type": "object",
|
|
273
|
+
"description": "PLANNED — NOT accepted by the server today. Client->server ping. Today, browser clients rely on the server's outbound `heartbeat` frames (§7.4) and the underlying WebSocket protocol's PING/PONG control frames (handled transparently by coder/websocket) to detect liveness. An app-level `ping` is reserved for platforms where the WebSocket library does not expose RFC 6455 PING/PONG — specifically watchOS extended-runtime sessions and iOS background sockets, per NFR-3.36 / NFR-3.36a-d. The TypeScript SDK runs on browser `WebSocket` and Node `ws`, both of which expose transport-level PING/PONG, so this is a Swift-SDK forward-compatibility concern only. Tracked as divergence DV-07 in websocket-protocol.md §10. Note: `auth_ok` has been pulled out of DV-07 and is v1-required (see AuthOkPayload); the rest of DV-07 remains deferred.",
|
|
274
|
+
"additionalProperties": false,
|
|
275
|
+
"properties": {
|
|
276
|
+
"nonce": {
|
|
277
|
+
"type": "string",
|
|
278
|
+
"description": "Optional opaque value echoed back in the corresponding `pong` for round-trip latency measurement.",
|
|
279
|
+
"x-classification": "P0"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"PongPayload": {
|
|
284
|
+
"type": "object",
|
|
285
|
+
"description": "PLANNED — NOT emitted by the server today. Server->client response to a client-initiated `ping`. Echoes the nonce so the client can compute round-trip latency. Tracked as divergence DV-07 in websocket-protocol.md §10. Note: `auth_ok` has been pulled out of DV-07 and is v1-required (see AuthOkPayload); the rest of DV-07 remains deferred.",
|
|
286
|
+
"additionalProperties": false,
|
|
287
|
+
"properties": {
|
|
288
|
+
"nonce": {
|
|
289
|
+
"type": "string",
|
|
290
|
+
"x-classification": "P0"
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|