@downtrace/protocol 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Raúl Jiménez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # @downtrace/protocol
2
+
3
+ The ingestion contract between Downtrace agents and the Downtrace cloud, as a JSON Schema (draft 2020-12) plus the TypeScript types generated from it.
4
+
5
+ - `schema/v0/aggregates.schema.json` — the contract. Everything else derives from it.
6
+ - `schema/v0/fixtures/{valid,invalid}/` — examples every implementation must accept and reject.
7
+ - Exports: `PROTOCOL_VERSION`, `AGGREGATES_PATH`, `AGGREGATES_SCHEMA_V0`, `LATENCY_BOUNDARIES_V0`, `LATENCY_BUCKETS_V0`, `latencyBucket()`, and the types `AggregatesBatch`, `Interval`, `Endpoint`, `LatencyHistogram`, …
8
+
9
+ ## v0 in one sentence
10
+
11
+ Every 10 seconds, for every route, an agent sends how many requests there were, how many failed, how statuses split, and a **35-bucket fixed latency histogram** (0.5 ms → 60 s). Fixed buckets add up across instances, so the cloud can compute fleet-wide percentiles.
12
+
13
+ `POST {DOWNTRACE_URL}/v0/aggregates` · `Authorization: Bearer <token>` · JSON · 202 accepted, 400 invalid, 401 bad token.
14
+
15
+ ## Source
16
+
17
+ Developed in a monorepo and mirrored read-only to [RadW2020/downtrace-agent](https://github.com/RadW2020/downtrace-agent). MIT.
@@ -0,0 +1,334 @@
1
+ //#region src/generated/aggregates.d.ts
2
+ /**
3
+ * A batch of per-interval, per-route aggregates sent by an agent to the cloud (POST /v0/aggregates).
4
+ */
5
+ interface AggregatesBatch {
6
+ /**
7
+ * Protocol version this batch conforms to.
8
+ */
9
+ protocol: "0.1.0";
10
+ agent: AgentInfo;
11
+ instance: InstanceInfo;
12
+ deploy: DeployInfo;
13
+ /**
14
+ * @minItems 1
15
+ * @maxItems 6
16
+ */
17
+ intervals: [Interval] | [Interval, Interval] | [Interval, Interval, Interval] | [Interval, Interval, Interval, Interval] | [Interval, Interval, Interval, Interval, Interval] | [Interval, Interval, Interval, Interval, Interval, Interval];
18
+ }
19
+ interface AgentInfo {
20
+ name: string;
21
+ version: string;
22
+ runtime: "node" | "go";
23
+ runtimeVersion: string;
24
+ }
25
+ interface InstanceInfo {
26
+ /**
27
+ * Random identifier generated once per process.
28
+ */
29
+ id: string;
30
+ hostname: string;
31
+ pid: number;
32
+ }
33
+ interface DeployInfo {
34
+ /**
35
+ * Deployed version or commit; "unknown" when undetectable.
36
+ */
37
+ version: string;
38
+ environment: string;
39
+ }
40
+ interface Interval {
41
+ /**
42
+ * Interval start, Unix epoch milliseconds.
43
+ */
44
+ start: number;
45
+ durationMs: number;
46
+ /**
47
+ * @maxItems 4096
48
+ */
49
+ endpoints: Endpoint[];
50
+ }
51
+ interface Endpoint {
52
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "OTHER";
53
+ /**
54
+ * Normalised route template, e.g. /products/:id, or (other).
55
+ */
56
+ route: string;
57
+ count: number;
58
+ /**
59
+ * Responses with a 5xx status.
60
+ */
61
+ errors: number;
62
+ status: StatusClasses;
63
+ latency: LatencyHistogram;
64
+ }
65
+ interface StatusClasses {
66
+ /**
67
+ * 2xx
68
+ */
69
+ success: number;
70
+ /**
71
+ * 3xx
72
+ */
73
+ redirect: number;
74
+ /**
75
+ * 4xx
76
+ */
77
+ clientError: number;
78
+ /**
79
+ * 5xx
80
+ */
81
+ serverError: number;
82
+ }
83
+ /**
84
+ * Fixed-bucket latency histogram in milliseconds. counts[i] is the number of requests with latency <= boundaries[i]; the last bucket holds everything above the last boundary. Fixed buckets add up across instances.
85
+ */
86
+ interface LatencyHistogram {
87
+ /**
88
+ * @minItems 35
89
+ * @maxItems 35
90
+ */
91
+ counts: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number];
92
+ /**
93
+ * Sum of latencies in ms.
94
+ */
95
+ sum: number;
96
+ /**
97
+ * Maximum latency in ms.
98
+ */
99
+ max: number;
100
+ }
101
+ //#endregion
102
+ //#region src/generated/boundaries.d.ts
103
+ /** Upper bounds (ms) of the fixed latency buckets; the last bucket is open-ended. */
104
+ declare const LATENCY_BOUNDARIES_V0: readonly number[];
105
+ /** Number of buckets: boundaries + 1 open-ended bucket. */
106
+ declare const LATENCY_BUCKETS_V0 = 35;
107
+ //#endregion
108
+ //#region src/index.d.ts
109
+ /** Version of the ingestion protocol spoken by agents and the cloud. */
110
+ declare const PROTOCOL_VERSION = "0.1.0";
111
+ /** Path, relative to the ingest URL, that receives AggregatesBatch payloads. */
112
+ declare const AGGREGATES_PATH = "/v0/aggregates";
113
+ /** The JSON Schema (draft 2020-12) for AggregatesBatch, for validators on either side. */
114
+ declare const AGGREGATES_SCHEMA_V0: {
115
+ $schema: string;
116
+ $id: string;
117
+ title: string;
118
+ description: string;
119
+ type: string;
120
+ additionalProperties: boolean;
121
+ required: string[];
122
+ properties: {
123
+ protocol: {
124
+ type: string;
125
+ const: string;
126
+ description: string;
127
+ };
128
+ agent: {
129
+ $ref: string;
130
+ };
131
+ instance: {
132
+ $ref: string;
133
+ };
134
+ deploy: {
135
+ $ref: string;
136
+ };
137
+ intervals: {
138
+ type: string;
139
+ minItems: number;
140
+ maxItems: number;
141
+ items: {
142
+ $ref: string;
143
+ };
144
+ };
145
+ };
146
+ $defs: {
147
+ AgentInfo: {
148
+ title: string;
149
+ type: string;
150
+ additionalProperties: boolean;
151
+ required: string[];
152
+ properties: {
153
+ name: {
154
+ type: string;
155
+ minLength: number;
156
+ maxLength: number;
157
+ };
158
+ version: {
159
+ type: string;
160
+ minLength: number;
161
+ maxLength: number;
162
+ };
163
+ runtime: {
164
+ enum: string[];
165
+ };
166
+ runtimeVersion: {
167
+ type: string;
168
+ minLength: number;
169
+ maxLength: number;
170
+ };
171
+ };
172
+ };
173
+ InstanceInfo: {
174
+ title: string;
175
+ type: string;
176
+ additionalProperties: boolean;
177
+ required: string[];
178
+ properties: {
179
+ id: {
180
+ type: string;
181
+ minLength: number;
182
+ maxLength: number;
183
+ description: string;
184
+ };
185
+ hostname: {
186
+ type: string;
187
+ minLength: number;
188
+ maxLength: number;
189
+ };
190
+ pid: {
191
+ type: string;
192
+ minimum: number;
193
+ };
194
+ };
195
+ };
196
+ DeployInfo: {
197
+ title: string;
198
+ type: string;
199
+ additionalProperties: boolean;
200
+ required: string[];
201
+ properties: {
202
+ version: {
203
+ type: string;
204
+ minLength: number;
205
+ maxLength: number;
206
+ description: string;
207
+ };
208
+ environment: {
209
+ type: string;
210
+ minLength: number;
211
+ maxLength: number;
212
+ };
213
+ };
214
+ };
215
+ Interval: {
216
+ title: string;
217
+ type: string;
218
+ additionalProperties: boolean;
219
+ required: string[];
220
+ properties: {
221
+ start: {
222
+ type: string;
223
+ minimum: number;
224
+ description: string;
225
+ };
226
+ durationMs: {
227
+ type: string;
228
+ minimum: number;
229
+ maximum: number;
230
+ };
231
+ endpoints: {
232
+ type: string;
233
+ maxItems: number;
234
+ items: {
235
+ $ref: string;
236
+ };
237
+ };
238
+ };
239
+ };
240
+ Endpoint: {
241
+ title: string;
242
+ type: string;
243
+ additionalProperties: boolean;
244
+ required: string[];
245
+ properties: {
246
+ method: {
247
+ enum: string[];
248
+ };
249
+ route: {
250
+ type: string;
251
+ minLength: number;
252
+ maxLength: number;
253
+ description: string;
254
+ };
255
+ count: {
256
+ type: string;
257
+ minimum: number;
258
+ };
259
+ errors: {
260
+ type: string;
261
+ minimum: number;
262
+ description: string;
263
+ };
264
+ status: {
265
+ $ref: string;
266
+ };
267
+ latency: {
268
+ $ref: string;
269
+ };
270
+ };
271
+ };
272
+ StatusClasses: {
273
+ title: string;
274
+ type: string;
275
+ additionalProperties: boolean;
276
+ required: string[];
277
+ properties: {
278
+ success: {
279
+ type: string;
280
+ minimum: number;
281
+ description: string;
282
+ };
283
+ redirect: {
284
+ type: string;
285
+ minimum: number;
286
+ description: string;
287
+ };
288
+ clientError: {
289
+ type: string;
290
+ minimum: number;
291
+ description: string;
292
+ };
293
+ serverError: {
294
+ type: string;
295
+ minimum: number;
296
+ description: string;
297
+ };
298
+ };
299
+ };
300
+ LatencyHistogram: {
301
+ title: string;
302
+ description: string;
303
+ type: string;
304
+ additionalProperties: boolean;
305
+ required: string[];
306
+ "x-latency-boundaries-ms": number[];
307
+ properties: {
308
+ counts: {
309
+ type: string;
310
+ minItems: number;
311
+ maxItems: number;
312
+ items: {
313
+ type: string;
314
+ minimum: number;
315
+ };
316
+ };
317
+ sum: {
318
+ type: string;
319
+ minimum: number;
320
+ description: string;
321
+ };
322
+ max: {
323
+ type: string;
324
+ minimum: number;
325
+ description: string;
326
+ };
327
+ };
328
+ };
329
+ };
330
+ };
331
+ /** Index of the bucket a latency (ms) falls into: first boundary >= latency, else the open-ended last bucket. */
332
+ declare function latencyBucket(ms: number): number;
333
+ //#endregion
334
+ export { AGGREGATES_PATH, AGGREGATES_SCHEMA_V0, type AgentInfo, type AggregatesBatch, type DeployInfo, type Endpoint, type InstanceInfo, type Interval, LATENCY_BOUNDARIES_V0, LATENCY_BUCKETS_V0, type LatencyHistogram, PROTOCOL_VERSION, type StatusClasses, latencyBucket };
package/dist/index.js ADDED
@@ -0,0 +1,339 @@
1
+ //#region schema/v0/aggregates.schema.json
2
+ var aggregates_schema_default = {
3
+ $schema: "https://json-schema.org/draft/2020-12/schema",
4
+ $id: "https://downtrace.io/schema/v0/aggregates.schema.json",
5
+ title: "AggregatesBatch",
6
+ description: "A batch of per-interval, per-route aggregates sent by an agent to the cloud (POST /v0/aggregates).",
7
+ type: "object",
8
+ additionalProperties: false,
9
+ required: [
10
+ "protocol",
11
+ "agent",
12
+ "instance",
13
+ "deploy",
14
+ "intervals"
15
+ ],
16
+ properties: {
17
+ "protocol": {
18
+ "type": "string",
19
+ "const": "0.1.0",
20
+ "description": "Protocol version this batch conforms to."
21
+ },
22
+ "agent": { "$ref": "#/$defs/AgentInfo" },
23
+ "instance": { "$ref": "#/$defs/InstanceInfo" },
24
+ "deploy": { "$ref": "#/$defs/DeployInfo" },
25
+ "intervals": {
26
+ "type": "array",
27
+ "minItems": 1,
28
+ "maxItems": 6,
29
+ "items": { "$ref": "#/$defs/Interval" }
30
+ }
31
+ },
32
+ $defs: {
33
+ "AgentInfo": {
34
+ "title": "AgentInfo",
35
+ "type": "object",
36
+ "additionalProperties": false,
37
+ "required": [
38
+ "name",
39
+ "version",
40
+ "runtime",
41
+ "runtimeVersion"
42
+ ],
43
+ "properties": {
44
+ "name": {
45
+ "type": "string",
46
+ "minLength": 1,
47
+ "maxLength": 128
48
+ },
49
+ "version": {
50
+ "type": "string",
51
+ "minLength": 1,
52
+ "maxLength": 64
53
+ },
54
+ "runtime": { "enum": ["node", "go"] },
55
+ "runtimeVersion": {
56
+ "type": "string",
57
+ "minLength": 1,
58
+ "maxLength": 64
59
+ }
60
+ }
61
+ },
62
+ "InstanceInfo": {
63
+ "title": "InstanceInfo",
64
+ "type": "object",
65
+ "additionalProperties": false,
66
+ "required": [
67
+ "id",
68
+ "hostname",
69
+ "pid"
70
+ ],
71
+ "properties": {
72
+ "id": {
73
+ "type": "string",
74
+ "minLength": 1,
75
+ "maxLength": 64,
76
+ "description": "Random identifier generated once per process."
77
+ },
78
+ "hostname": {
79
+ "type": "string",
80
+ "minLength": 1,
81
+ "maxLength": 253
82
+ },
83
+ "pid": {
84
+ "type": "integer",
85
+ "minimum": 0
86
+ }
87
+ }
88
+ },
89
+ "DeployInfo": {
90
+ "title": "DeployInfo",
91
+ "type": "object",
92
+ "additionalProperties": false,
93
+ "required": ["version", "environment"],
94
+ "properties": {
95
+ "version": {
96
+ "type": "string",
97
+ "minLength": 1,
98
+ "maxLength": 128,
99
+ "description": "Deployed version or commit; \"unknown\" when undetectable."
100
+ },
101
+ "environment": {
102
+ "type": "string",
103
+ "minLength": 1,
104
+ "maxLength": 64
105
+ }
106
+ }
107
+ },
108
+ "Interval": {
109
+ "title": "Interval",
110
+ "type": "object",
111
+ "additionalProperties": false,
112
+ "required": [
113
+ "start",
114
+ "durationMs",
115
+ "endpoints"
116
+ ],
117
+ "properties": {
118
+ "start": {
119
+ "type": "integer",
120
+ "minimum": 0,
121
+ "description": "Interval start, Unix epoch milliseconds."
122
+ },
123
+ "durationMs": {
124
+ "type": "integer",
125
+ "minimum": 1,
126
+ "maximum": 36e5
127
+ },
128
+ "endpoints": {
129
+ "type": "array",
130
+ "maxItems": 4096,
131
+ "items": { "$ref": "#/$defs/Endpoint" }
132
+ }
133
+ }
134
+ },
135
+ "Endpoint": {
136
+ "title": "Endpoint",
137
+ "type": "object",
138
+ "additionalProperties": false,
139
+ "required": [
140
+ "method",
141
+ "route",
142
+ "count",
143
+ "errors",
144
+ "status",
145
+ "latency"
146
+ ],
147
+ "properties": {
148
+ "method": { "enum": [
149
+ "GET",
150
+ "POST",
151
+ "PUT",
152
+ "PATCH",
153
+ "DELETE",
154
+ "HEAD",
155
+ "OPTIONS",
156
+ "OTHER"
157
+ ] },
158
+ "route": {
159
+ "type": "string",
160
+ "minLength": 1,
161
+ "maxLength": 256,
162
+ "description": "Normalised route template, e.g. /products/:id, or (other)."
163
+ },
164
+ "count": {
165
+ "type": "integer",
166
+ "minimum": 0
167
+ },
168
+ "errors": {
169
+ "type": "integer",
170
+ "minimum": 0,
171
+ "description": "Responses with a 5xx status."
172
+ },
173
+ "status": { "$ref": "#/$defs/StatusClasses" },
174
+ "latency": { "$ref": "#/$defs/LatencyHistogram" }
175
+ }
176
+ },
177
+ "StatusClasses": {
178
+ "title": "StatusClasses",
179
+ "type": "object",
180
+ "additionalProperties": false,
181
+ "required": [
182
+ "success",
183
+ "redirect",
184
+ "clientError",
185
+ "serverError"
186
+ ],
187
+ "properties": {
188
+ "success": {
189
+ "type": "integer",
190
+ "minimum": 0,
191
+ "description": "2xx"
192
+ },
193
+ "redirect": {
194
+ "type": "integer",
195
+ "minimum": 0,
196
+ "description": "3xx"
197
+ },
198
+ "clientError": {
199
+ "type": "integer",
200
+ "minimum": 0,
201
+ "description": "4xx"
202
+ },
203
+ "serverError": {
204
+ "type": "integer",
205
+ "minimum": 0,
206
+ "description": "5xx"
207
+ }
208
+ }
209
+ },
210
+ "LatencyHistogram": {
211
+ "title": "LatencyHistogram",
212
+ "description": "Fixed-bucket latency histogram in milliseconds. counts[i] is the number of requests with latency <= boundaries[i]; the last bucket holds everything above the last boundary. Fixed buckets add up across instances.",
213
+ "type": "object",
214
+ "additionalProperties": false,
215
+ "required": [
216
+ "counts",
217
+ "sum",
218
+ "max"
219
+ ],
220
+ "x-latency-boundaries-ms": [
221
+ .5,
222
+ 1,
223
+ 1.5,
224
+ 2,
225
+ 3,
226
+ 4,
227
+ 5,
228
+ 7.5,
229
+ 10,
230
+ 15,
231
+ 20,
232
+ 30,
233
+ 40,
234
+ 50,
235
+ 75,
236
+ 100,
237
+ 150,
238
+ 200,
239
+ 300,
240
+ 400,
241
+ 500,
242
+ 750,
243
+ 1e3,
244
+ 1500,
245
+ 2e3,
246
+ 3e3,
247
+ 4e3,
248
+ 5e3,
249
+ 7500,
250
+ 1e4,
251
+ 15e3,
252
+ 2e4,
253
+ 3e4,
254
+ 6e4
255
+ ],
256
+ "properties": {
257
+ "counts": {
258
+ "type": "array",
259
+ "minItems": 35,
260
+ "maxItems": 35,
261
+ "items": {
262
+ "type": "integer",
263
+ "minimum": 0
264
+ }
265
+ },
266
+ "sum": {
267
+ "type": "number",
268
+ "minimum": 0,
269
+ "description": "Sum of latencies in ms."
270
+ },
271
+ "max": {
272
+ "type": "number",
273
+ "minimum": 0,
274
+ "description": "Maximum latency in ms."
275
+ }
276
+ }
277
+ }
278
+ }
279
+ };
280
+ //#endregion
281
+ //#region src/generated/boundaries.ts
282
+ /** Upper bounds (ms) of the fixed latency buckets; the last bucket is open-ended. */
283
+ const LATENCY_BOUNDARIES_V0 = [
284
+ .5,
285
+ 1,
286
+ 1.5,
287
+ 2,
288
+ 3,
289
+ 4,
290
+ 5,
291
+ 7.5,
292
+ 10,
293
+ 15,
294
+ 20,
295
+ 30,
296
+ 40,
297
+ 50,
298
+ 75,
299
+ 100,
300
+ 150,
301
+ 200,
302
+ 300,
303
+ 400,
304
+ 500,
305
+ 750,
306
+ 1e3,
307
+ 1500,
308
+ 2e3,
309
+ 3e3,
310
+ 4e3,
311
+ 5e3,
312
+ 7500,
313
+ 1e4,
314
+ 15e3,
315
+ 2e4,
316
+ 3e4,
317
+ 6e4
318
+ ];
319
+ /** Number of buckets: boundaries + 1 open-ended bucket. */
320
+ const LATENCY_BUCKETS_V0 = 35;
321
+ //#endregion
322
+ //#region src/index.ts
323
+ /** Version of the ingestion protocol spoken by agents and the cloud. */
324
+ const PROTOCOL_VERSION = "0.1.0";
325
+ /** Path, relative to the ingest URL, that receives AggregatesBatch payloads. */
326
+ const AGGREGATES_PATH = "/v0/aggregates";
327
+ /** The JSON Schema (draft 2020-12) for AggregatesBatch, for validators on either side. */
328
+ const AGGREGATES_SCHEMA_V0 = aggregates_schema_default;
329
+ /** Index of the bucket a latency (ms) falls into: first boundary >= latency, else the open-ended last bucket. */
330
+ function latencyBucket(ms) {
331
+ const n = LATENCY_BOUNDARIES_V0.length;
332
+ for (let i = 0; i < n; i++) {
333
+ const bound = LATENCY_BOUNDARIES_V0[i];
334
+ if (bound !== void 0 && ms <= bound) return i;
335
+ }
336
+ return n;
337
+ }
338
+ //#endregion
339
+ export { AGGREGATES_PATH, AGGREGATES_SCHEMA_V0, LATENCY_BOUNDARIES_V0, LATENCY_BUCKETS_V0, PROTOCOL_VERSION, latencyBucket };