@alteriom/mqtt-schema 0.2.1 → 0.3.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 +7 -0
- package/README.md +115 -2
- package/dist/cjs/schema_data.d.ts +181 -0
- package/dist/cjs/schema_data.js +214 -1
- package/dist/cjs/schemas/ota/ota-manifest.schema.json +187 -0
- package/dist/esm/schema_data.js +213 -0
- package/dist/esm/schemas/ota/ota-manifest.schema.json +187 -0
- package/package.json +39 -4
- package/schemas/control_response.schema.json +15 -0
- package/schemas/envelope.schema.json +16 -0
- package/schemas/firmware_status.schema.json +17 -0
- package/schemas/gateway_info.schema.json +21 -0
- package/schemas/gateway_metrics.schema.json +26 -0
- package/schemas/mqtt_v1_bundle.json +14 -0
- package/schemas/ota/ota-manifest.schema.json +187 -0
- package/schemas/sensor_data.schema.json +33 -0
- package/schemas/sensor_heartbeat.schema.json +14 -0
- package/schemas/sensor_status.schema.json +14 -0
- package/schemas/validation_rules.md +44 -0
- package/types/ota-manifest.d.ts +63 -0
package/CHANGELOG.md
ADDED
package/README.md
CHANGED
@@ -1,7 +1,21 @@
|
|
1
1
|
# @alteriom/mqtt-schema
|
2
2
|
|
3
|
+

|
4
|
+

|
5
|
+

|
6
|
+

|
7
|
+

|
8
|
+

|
9
|
+

|
10
|
+

|
11
|
+

|
12
|
+

|
13
|
+
[](https://bundlephobia.com/package/@alteriom/mqtt-schema)
|
14
|
+
|
15
|
+
|
3
16
|
Alteriom MQTT v1 JSON Schemas, TypeScript types, and production‑ready validation helpers for integrating firmware MQTT payloads into web or backend services.
|
4
17
|
|
18
|
+
|
5
19
|
## Why this exists
|
6
20
|
Firmware emits structured MQTT payloads that must remain tightly aligned with web, analytics, and gateway logic. This package is the single source of truth for:
|
7
21
|
|
@@ -26,6 +40,8 @@ Firmware emits structured MQTT payloads that must remain tightly aligned with we
|
|
26
40
|
npm install @alteriom/mqtt-schema ajv ajv-formats
|
27
41
|
```
|
28
42
|
|
43
|
+
**Support & Compatibility**: Node 18+ tested; Node 20 primary. Dual CJS/ESM builds.
|
44
|
+
|
29
45
|
## Quick Start
|
30
46
|
|
31
47
|
Validate a JSON payload (object already parsed):
|
@@ -73,6 +89,43 @@ Access raw schema JSON (if you need to introspect or power form generation):
|
|
73
89
|
import envelopeSchema from '@alteriom/mqtt-schema/schemas/envelope.schema.json';
|
74
90
|
```
|
75
91
|
|
92
|
+
## OTA Firmware Manifest Schema (v0.3.1+)
|
93
|
+
|
94
|
+
The package includes OTA firmware manifest schema with both rich and minimal formats.
|
95
|
+
|
96
|
+
**Preferred: Stable alias import** (v0.3.1+):
|
97
|
+
```ts
|
98
|
+
import otaManifestSchema from '@alteriom/mqtt-schema/ota-manifest';
|
99
|
+
import { OtaManifest, isRichManifest } from '@alteriom/mqtt-schema/types/ota-manifest';
|
100
|
+
```
|
101
|
+
|
102
|
+
**Legacy: Deep path import** (still supported):
|
103
|
+
```ts
|
104
|
+
import otaManifestSchema from '@alteriom/mqtt-schema/schemas/ota/ota-manifest.schema.json';
|
105
|
+
```
|
106
|
+
|
107
|
+
**Usage example:**
|
108
|
+
```ts
|
109
|
+
import Ajv from 'ajv';
|
110
|
+
import addFormats from 'ajv-formats';
|
111
|
+
import otaManifestSchema from '@alteriom/mqtt-schema/ota-manifest';
|
112
|
+
import { OtaManifest } from '@alteriom/mqtt-schema/types/ota-manifest';
|
113
|
+
|
114
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
115
|
+
addFormats(ajv);
|
116
|
+
const validate = ajv.compile<OtaManifest>(otaManifestSchema as any);
|
117
|
+
|
118
|
+
const manifest: OtaManifest = JSON.parse(manifestJson);
|
119
|
+
if (!validate(manifest)) {
|
120
|
+
console.error('Invalid OTA manifest:', validate.errors);
|
121
|
+
}
|
122
|
+
```
|
123
|
+
|
124
|
+
Supported formats:
|
125
|
+
- **Rich manifest**: environment + branch + manifests object
|
126
|
+
- **Minimal environment map**: environment → channels mapping
|
127
|
+
- **Chunk variants**: structured objects or SHA256 array
|
128
|
+
|
76
129
|
## API Surface
|
77
130
|
|
78
131
|
```ts
|
@@ -101,9 +154,11 @@ interface ValidationResult {
|
|
101
154
|
```
|
102
155
|
|
103
156
|
### Performance Notes
|
157
|
+
|
104
158
|
All Ajv validator functions are compiled once at module load. For typical web usage (tens to hundreds of validations per page/session) this is faster and simpler than on‑demand compilation. If you need custom Ajv options (e.g., different formats), open an issue—an override hook can be added without breaking changes.
|
105
159
|
|
106
160
|
### Embedded Schemas
|
161
|
+
|
107
162
|
`schema_data.ts` is auto‑generated during build. This avoids dynamic `require()` / `import` of JSON and works cleanly in both Node ESM and bundlers without JSON import assertions. The original JSON files are still published under `schemas/` for tooling or documentation pipelines.
|
108
163
|
|
109
164
|
## Provided Schemas (v1)
|
@@ -131,6 +186,7 @@ All Ajv validator functions are compiled once at module load. For typical web us
|
|
131
186
|
| `schemas/*.json` | JSON | Original schema assets (optional) |
|
132
187
|
|
133
188
|
### Validator Keys
|
189
|
+
|
134
190
|
`sensorData`, `sensorHeartbeat`, `sensorStatus`, `gatewayInfo`, `gatewayMetrics`, `firmwareStatus`, `controlResponse`
|
135
191
|
|
136
192
|
### Classification Heuristics (Simplified)
|
@@ -170,6 +226,8 @@ Backward‑compatible additions: new optional properties or enums, documented in
|
|
170
226
|
|
171
227
|
Issues & PRs welcome. Ensure firmware repo schemas remain the authoritative source—do not manually edit generated `schema_data.ts`.
|
172
228
|
|
229
|
+
**Before opening a PR:** Run `npm run verify` to validate schemas, changelog, and tests.
|
230
|
+
|
173
231
|
## Security
|
174
232
|
|
175
233
|
Schemas are static. No network access. Supply-chain risk minimized by keeping dependencies minimal (Ajv + formats only).
|
@@ -189,14 +247,14 @@ This package is published to BOTH:
|
|
189
247
|
|
190
248
|
Create or update an `.npmrc` with a scoped registry override (auth token with `read:packages` required):
|
191
249
|
|
192
|
-
```
|
250
|
+
```bash
|
193
251
|
@alteriom:registry=https://npm.pkg.github.com
|
194
252
|
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
|
195
253
|
```
|
196
254
|
|
197
255
|
Then install normally:
|
198
256
|
|
199
|
-
```
|
257
|
+
```bash
|
200
258
|
npm install @alteriom/mqtt-schema ajv ajv-formats
|
201
259
|
```
|
202
260
|
|
@@ -226,3 +284,58 @@ npm view @alteriom/mqtt-schema version --registry=https://npm.pkg.github.com
|
|
226
284
|
```
|
227
285
|
|
228
286
|
Both should return the same version.
|
287
|
+
|
288
|
+
## Repository Metadata Compliance
|
289
|
+
|
290
|
+
This repository integrates the `@alteriom/repository-metadata-manager` tooling to continuously validate and report on repository metadata health (description, topics, documentation signals, etc.) within the Alteriom organization standards.
|
291
|
+
|
292
|
+
### Local Usage
|
293
|
+
|
294
|
+
Run a validation (non‑destructive):
|
295
|
+
|
296
|
+
```bash
|
297
|
+
npm run metadata:validate
|
298
|
+
```
|
299
|
+
|
300
|
+
Generate a detailed report:
|
301
|
+
|
302
|
+
```bash
|
303
|
+
npm run metadata:report
|
304
|
+
```
|
305
|
+
|
306
|
+
Configuration lives in `metadata-config.json` (organizationTag `alteriom`). Default detection is auto; adjust if repository classification needs overriding.
|
307
|
+
|
308
|
+
### CI Workflow
|
309
|
+
|
310
|
+
Workflow file: `.github/workflows/metadata-compliance.yml`
|
311
|
+
|
312
|
+
On each push / PR against `main` it will:
|
313
|
+
|
314
|
+
1. Install dependencies
|
315
|
+
2. Run `metadata:validate` (fails job on hard non‑compliance)
|
316
|
+
3. Always attempt a best‑effort report (`metadata:report`) for visibility
|
317
|
+
|
318
|
+
### Tokens / Permissions
|
319
|
+
|
320
|
+
The workflow relies only on the default `GITHUB_TOKEN` for read operations. If future auto‑apply operations are desired, a token with elevated repo scope would be needed and the `metadata:apply` script could be wired (currently omitted to keep CI read‑only).
|
321
|
+
|
322
|
+
### Extending
|
323
|
+
|
324
|
+
If you add new categories of tooling or documentation, re‑run the report to see updated recommendations. For cross‑repo analytics or policy generation, use the original project directly.
|
325
|
+
|
326
|
+
### Applying Metadata (Manual Workflow)
|
327
|
+
|
328
|
+
For authorized maintainers you can run adjustments via GitHub Actions:
|
329
|
+
|
330
|
+
1. Open the "Repository Metadata Apply" workflow under the Actions tab.
|
331
|
+
2. Choose whether to keep `dryRun` (default) or set to `false` to apply.
|
332
|
+
3. Run the workflow; the log will show proposed or applied changes.
|
333
|
+
|
334
|
+
Local dry‑run vs apply:
|
335
|
+
|
336
|
+
```bash
|
337
|
+
npm run metadata:apply:dry # show what would change
|
338
|
+
npm run metadata:apply # apply changes (requires proper permissions via GITHUB_TOKEN)
|
339
|
+
```
|
340
|
+
|
341
|
+
Note: Applying metadata modifies repository settings (description, topics) through the GitHub API; ensure the default token has the necessary repo scopes (in public repositories the workflow GITHUB_TOKEN normally suffices for these fields).
|
@@ -317,3 +317,184 @@ export declare const mqtt_v1_bundle_json: {
|
|
317
317
|
readonly control_response: "control_response.schema.json";
|
318
318
|
};
|
319
319
|
};
|
320
|
+
export declare const ota_ota_manifest_schema: {
|
321
|
+
readonly $id: "https://schemas.alteriom.com/ota/ota-manifest.schema.json";
|
322
|
+
readonly title: "Alteriom OTA Firmware Manifest";
|
323
|
+
readonly description: "Schema for Alteriom OTA firmware manifests supporting both rich and minimal variants";
|
324
|
+
readonly type: "object";
|
325
|
+
readonly oneOf: readonly [{
|
326
|
+
readonly title: "Rich Manifest";
|
327
|
+
readonly description: "Rich manifest format with environment, branch, and manifests object";
|
328
|
+
readonly type: "object";
|
329
|
+
readonly properties: {
|
330
|
+
readonly environment: {
|
331
|
+
readonly type: "string";
|
332
|
+
readonly description: "Target environment (e.g., universal-sensor)";
|
333
|
+
};
|
334
|
+
readonly branch: {
|
335
|
+
readonly type: "string";
|
336
|
+
readonly description: "Source control branch the build originated from";
|
337
|
+
};
|
338
|
+
readonly manifests: {
|
339
|
+
readonly type: "object";
|
340
|
+
readonly description: "Build variants keyed by type (dev, prod, etc.)";
|
341
|
+
readonly patternProperties: {
|
342
|
+
readonly "^[a-z][a-z0-9-]*$": {
|
343
|
+
readonly $ref: "#/$defs/richEntry";
|
344
|
+
};
|
345
|
+
};
|
346
|
+
readonly additionalProperties: false;
|
347
|
+
readonly minProperties: 1;
|
348
|
+
};
|
349
|
+
};
|
350
|
+
readonly required: readonly ["environment", "branch", "manifests"];
|
351
|
+
readonly additionalProperties: true;
|
352
|
+
}, {
|
353
|
+
readonly title: "Minimal Environment Map";
|
354
|
+
readonly description: "Minimal manifest format as environment -> channels mapping";
|
355
|
+
readonly type: "object";
|
356
|
+
readonly patternProperties: {
|
357
|
+
readonly "^[a-z][a-z0-9-]*$": {
|
358
|
+
readonly type: "object";
|
359
|
+
readonly description: "Environment entry with channel mappings";
|
360
|
+
readonly patternProperties: {
|
361
|
+
readonly "^[a-z][a-z0-9-]*$": {
|
362
|
+
readonly $ref: "#/$defs/minimalChannel";
|
363
|
+
};
|
364
|
+
};
|
365
|
+
readonly additionalProperties: false;
|
366
|
+
readonly minProperties: 1;
|
367
|
+
};
|
368
|
+
};
|
369
|
+
readonly additionalProperties: false;
|
370
|
+
readonly minProperties: 1;
|
371
|
+
}];
|
372
|
+
readonly $defs: {
|
373
|
+
readonly richEntry: {
|
374
|
+
readonly title: "Rich Build Entry";
|
375
|
+
readonly description: "Rich manifest build entry (dev or prod)";
|
376
|
+
readonly type: "object";
|
377
|
+
readonly properties: {
|
378
|
+
readonly build_type: {
|
379
|
+
readonly type: "string";
|
380
|
+
readonly enum: readonly ["dev", "prod"];
|
381
|
+
readonly description: "Build type identifier";
|
382
|
+
};
|
383
|
+
readonly file: {
|
384
|
+
readonly type: "string";
|
385
|
+
readonly description: "Firmware binary filename";
|
386
|
+
};
|
387
|
+
readonly size: {
|
388
|
+
readonly type: "integer";
|
389
|
+
readonly minimum: 1;
|
390
|
+
readonly description: "Total firmware size in bytes";
|
391
|
+
};
|
392
|
+
readonly sha256: {
|
393
|
+
readonly type: "string";
|
394
|
+
readonly pattern: "^[a-f0-9]{64}$";
|
395
|
+
readonly description: "SHA256 hash of the full firmware binary (lowercase hex)";
|
396
|
+
};
|
397
|
+
readonly firmware_version: {
|
398
|
+
readonly type: "string";
|
399
|
+
readonly description: "Semantic or build version string";
|
400
|
+
};
|
401
|
+
readonly built: {
|
402
|
+
readonly type: "string";
|
403
|
+
readonly format: "date-time";
|
404
|
+
readonly description: "ISO8601 timestamp when built";
|
405
|
+
};
|
406
|
+
readonly ota_url: {
|
407
|
+
readonly type: "string";
|
408
|
+
readonly format: "uri";
|
409
|
+
readonly description: "Absolute or relative URL to fetch the firmware";
|
410
|
+
};
|
411
|
+
readonly chunk_size: {
|
412
|
+
readonly type: "integer";
|
413
|
+
readonly minimum: 1;
|
414
|
+
readonly description: "Size of each chunk except possibly the last";
|
415
|
+
};
|
416
|
+
readonly chunks: {
|
417
|
+
readonly description: "Either structured chunk objects or array of SHA256 strings";
|
418
|
+
readonly oneOf: readonly [{
|
419
|
+
readonly type: "array";
|
420
|
+
readonly items: {
|
421
|
+
readonly $ref: "#/$defs/chunkObject";
|
422
|
+
};
|
423
|
+
readonly description: "Array of structured chunk objects with metadata";
|
424
|
+
}, {
|
425
|
+
readonly type: "array";
|
426
|
+
readonly items: {
|
427
|
+
readonly type: "string";
|
428
|
+
readonly pattern: "^[a-f0-9]{64}$";
|
429
|
+
readonly description: "SHA256 hash of chunk (lowercase hex)";
|
430
|
+
};
|
431
|
+
readonly description: "Array of SHA256 strings for chunks";
|
432
|
+
}];
|
433
|
+
};
|
434
|
+
};
|
435
|
+
readonly required: readonly ["build_type", "file", "size", "sha256", "firmware_version", "built", "ota_url"];
|
436
|
+
readonly additionalProperties: true;
|
437
|
+
};
|
438
|
+
readonly chunkObject: {
|
439
|
+
readonly title: "OTA Chunk Object";
|
440
|
+
readonly description: "Structured chunk metadata with offset and size";
|
441
|
+
readonly type: "object";
|
442
|
+
readonly properties: {
|
443
|
+
readonly index: {
|
444
|
+
readonly type: "integer";
|
445
|
+
readonly minimum: 0;
|
446
|
+
readonly description: "0-based sequential chunk index";
|
447
|
+
};
|
448
|
+
readonly offset: {
|
449
|
+
readonly type: "integer";
|
450
|
+
readonly minimum: 0;
|
451
|
+
readonly description: "Byte offset within firmware binary";
|
452
|
+
};
|
453
|
+
readonly size: {
|
454
|
+
readonly type: "integer";
|
455
|
+
readonly minimum: 1;
|
456
|
+
readonly description: "Chunk size in bytes";
|
457
|
+
};
|
458
|
+
readonly sha256: {
|
459
|
+
readonly type: "string";
|
460
|
+
readonly pattern: "^[a-f0-9]{64}$";
|
461
|
+
readonly description: "SHA256 hash of the chunk (lowercase hex)";
|
462
|
+
};
|
463
|
+
};
|
464
|
+
readonly required: readonly ["index", "offset", "size", "sha256"];
|
465
|
+
readonly additionalProperties: true;
|
466
|
+
};
|
467
|
+
readonly minimalChannel: {
|
468
|
+
readonly title: "Minimal Channel Entry";
|
469
|
+
readonly description: "Minimal manifest channel entry";
|
470
|
+
readonly type: "object";
|
471
|
+
readonly properties: {
|
472
|
+
readonly file: {
|
473
|
+
readonly type: "string";
|
474
|
+
readonly description: "Firmware binary filename";
|
475
|
+
};
|
476
|
+
readonly size: {
|
477
|
+
readonly type: "integer";
|
478
|
+
readonly minimum: 1;
|
479
|
+
readonly description: "Total firmware size in bytes";
|
480
|
+
};
|
481
|
+
readonly sha256: {
|
482
|
+
readonly type: "string";
|
483
|
+
readonly pattern: "^[a-f0-9]{64}$";
|
484
|
+
readonly description: "SHA256 hash of the firmware binary (lowercase hex)";
|
485
|
+
};
|
486
|
+
readonly version: {
|
487
|
+
readonly type: "string";
|
488
|
+
readonly description: "Firmware version string";
|
489
|
+
};
|
490
|
+
readonly timestamp: {
|
491
|
+
readonly type: "string";
|
492
|
+
readonly format: "date-time";
|
493
|
+
readonly description: "ISO8601 timestamp";
|
494
|
+
};
|
495
|
+
};
|
496
|
+
readonly required: readonly ["file", "size", "sha256", "version", "timestamp"];
|
497
|
+
readonly additionalProperties: true;
|
498
|
+
};
|
499
|
+
};
|
500
|
+
};
|
package/dist/cjs/schema_data.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.mqtt_v1_bundle_json = exports.control_response_schema = exports.firmware_status_schema = exports.gateway_metrics_schema = exports.gateway_info_schema = exports.sensor_status_schema = exports.sensor_heartbeat_schema = exports.sensor_data_schema = exports.envelope_schema = void 0;
|
3
|
+
exports.ota_ota_manifest_schema = exports.mqtt_v1_bundle_json = exports.control_response_schema = exports.firmware_status_schema = exports.gateway_metrics_schema = exports.gateway_info_schema = exports.sensor_status_schema = exports.sensor_heartbeat_schema = exports.sensor_data_schema = exports.envelope_schema = void 0;
|
4
4
|
// AUTO-GENERATED by copy-schemas.cjs. Do not edit manually.
|
5
5
|
/* eslint-disable */
|
6
6
|
exports.envelope_schema = {
|
@@ -400,3 +400,216 @@ exports.mqtt_v1_bundle_json = {
|
|
400
400
|
"control_response": "control_response.schema.json"
|
401
401
|
}
|
402
402
|
};
|
403
|
+
exports.ota_ota_manifest_schema = {
|
404
|
+
"$id": "https://schemas.alteriom.com/ota/ota-manifest.schema.json",
|
405
|
+
"title": "Alteriom OTA Firmware Manifest",
|
406
|
+
"description": "Schema for Alteriom OTA firmware manifests supporting both rich and minimal variants",
|
407
|
+
"type": "object",
|
408
|
+
"oneOf": [
|
409
|
+
{
|
410
|
+
"title": "Rich Manifest",
|
411
|
+
"description": "Rich manifest format with environment, branch, and manifests object",
|
412
|
+
"type": "object",
|
413
|
+
"properties": {
|
414
|
+
"environment": {
|
415
|
+
"type": "string",
|
416
|
+
"description": "Target environment (e.g., universal-sensor)"
|
417
|
+
},
|
418
|
+
"branch": {
|
419
|
+
"type": "string",
|
420
|
+
"description": "Source control branch the build originated from"
|
421
|
+
},
|
422
|
+
"manifests": {
|
423
|
+
"type": "object",
|
424
|
+
"description": "Build variants keyed by type (dev, prod, etc.)",
|
425
|
+
"patternProperties": {
|
426
|
+
"^[a-z][a-z0-9-]*$": {
|
427
|
+
"$ref": "#/$defs/richEntry"
|
428
|
+
}
|
429
|
+
},
|
430
|
+
"additionalProperties": false,
|
431
|
+
"minProperties": 1
|
432
|
+
}
|
433
|
+
},
|
434
|
+
"required": [
|
435
|
+
"environment",
|
436
|
+
"branch",
|
437
|
+
"manifests"
|
438
|
+
],
|
439
|
+
"additionalProperties": true
|
440
|
+
},
|
441
|
+
{
|
442
|
+
"title": "Minimal Environment Map",
|
443
|
+
"description": "Minimal manifest format as environment -> channels mapping",
|
444
|
+
"type": "object",
|
445
|
+
"patternProperties": {
|
446
|
+
"^[a-z][a-z0-9-]*$": {
|
447
|
+
"type": "object",
|
448
|
+
"description": "Environment entry with channel mappings",
|
449
|
+
"patternProperties": {
|
450
|
+
"^[a-z][a-z0-9-]*$": {
|
451
|
+
"$ref": "#/$defs/minimalChannel"
|
452
|
+
}
|
453
|
+
},
|
454
|
+
"additionalProperties": false,
|
455
|
+
"minProperties": 1
|
456
|
+
}
|
457
|
+
},
|
458
|
+
"additionalProperties": false,
|
459
|
+
"minProperties": 1
|
460
|
+
}
|
461
|
+
],
|
462
|
+
"$defs": {
|
463
|
+
"richEntry": {
|
464
|
+
"title": "Rich Build Entry",
|
465
|
+
"description": "Rich manifest build entry (dev or prod)",
|
466
|
+
"type": "object",
|
467
|
+
"properties": {
|
468
|
+
"build_type": {
|
469
|
+
"type": "string",
|
470
|
+
"enum": [
|
471
|
+
"dev",
|
472
|
+
"prod"
|
473
|
+
],
|
474
|
+
"description": "Build type identifier"
|
475
|
+
},
|
476
|
+
"file": {
|
477
|
+
"type": "string",
|
478
|
+
"description": "Firmware binary filename"
|
479
|
+
},
|
480
|
+
"size": {
|
481
|
+
"type": "integer",
|
482
|
+
"minimum": 1,
|
483
|
+
"description": "Total firmware size in bytes"
|
484
|
+
},
|
485
|
+
"sha256": {
|
486
|
+
"type": "string",
|
487
|
+
"pattern": "^[a-f0-9]{64}$",
|
488
|
+
"description": "SHA256 hash of the full firmware binary (lowercase hex)"
|
489
|
+
},
|
490
|
+
"firmware_version": {
|
491
|
+
"type": "string",
|
492
|
+
"description": "Semantic or build version string"
|
493
|
+
},
|
494
|
+
"built": {
|
495
|
+
"type": "string",
|
496
|
+
"format": "date-time",
|
497
|
+
"description": "ISO8601 timestamp when built"
|
498
|
+
},
|
499
|
+
"ota_url": {
|
500
|
+
"type": "string",
|
501
|
+
"format": "uri",
|
502
|
+
"description": "Absolute or relative URL to fetch the firmware"
|
503
|
+
},
|
504
|
+
"chunk_size": {
|
505
|
+
"type": "integer",
|
506
|
+
"minimum": 1,
|
507
|
+
"description": "Size of each chunk except possibly the last"
|
508
|
+
},
|
509
|
+
"chunks": {
|
510
|
+
"description": "Either structured chunk objects or array of SHA256 strings",
|
511
|
+
"oneOf": [
|
512
|
+
{
|
513
|
+
"type": "array",
|
514
|
+
"items": {
|
515
|
+
"$ref": "#/$defs/chunkObject"
|
516
|
+
},
|
517
|
+
"description": "Array of structured chunk objects with metadata"
|
518
|
+
},
|
519
|
+
{
|
520
|
+
"type": "array",
|
521
|
+
"items": {
|
522
|
+
"type": "string",
|
523
|
+
"pattern": "^[a-f0-9]{64}$",
|
524
|
+
"description": "SHA256 hash of chunk (lowercase hex)"
|
525
|
+
},
|
526
|
+
"description": "Array of SHA256 strings for chunks"
|
527
|
+
}
|
528
|
+
]
|
529
|
+
}
|
530
|
+
},
|
531
|
+
"required": [
|
532
|
+
"build_type",
|
533
|
+
"file",
|
534
|
+
"size",
|
535
|
+
"sha256",
|
536
|
+
"firmware_version",
|
537
|
+
"built",
|
538
|
+
"ota_url"
|
539
|
+
],
|
540
|
+
"additionalProperties": true
|
541
|
+
},
|
542
|
+
"chunkObject": {
|
543
|
+
"title": "OTA Chunk Object",
|
544
|
+
"description": "Structured chunk metadata with offset and size",
|
545
|
+
"type": "object",
|
546
|
+
"properties": {
|
547
|
+
"index": {
|
548
|
+
"type": "integer",
|
549
|
+
"minimum": 0,
|
550
|
+
"description": "0-based sequential chunk index"
|
551
|
+
},
|
552
|
+
"offset": {
|
553
|
+
"type": "integer",
|
554
|
+
"minimum": 0,
|
555
|
+
"description": "Byte offset within firmware binary"
|
556
|
+
},
|
557
|
+
"size": {
|
558
|
+
"type": "integer",
|
559
|
+
"minimum": 1,
|
560
|
+
"description": "Chunk size in bytes"
|
561
|
+
},
|
562
|
+
"sha256": {
|
563
|
+
"type": "string",
|
564
|
+
"pattern": "^[a-f0-9]{64}$",
|
565
|
+
"description": "SHA256 hash of the chunk (lowercase hex)"
|
566
|
+
}
|
567
|
+
},
|
568
|
+
"required": [
|
569
|
+
"index",
|
570
|
+
"offset",
|
571
|
+
"size",
|
572
|
+
"sha256"
|
573
|
+
],
|
574
|
+
"additionalProperties": true
|
575
|
+
},
|
576
|
+
"minimalChannel": {
|
577
|
+
"title": "Minimal Channel Entry",
|
578
|
+
"description": "Minimal manifest channel entry",
|
579
|
+
"type": "object",
|
580
|
+
"properties": {
|
581
|
+
"file": {
|
582
|
+
"type": "string",
|
583
|
+
"description": "Firmware binary filename"
|
584
|
+
},
|
585
|
+
"size": {
|
586
|
+
"type": "integer",
|
587
|
+
"minimum": 1,
|
588
|
+
"description": "Total firmware size in bytes"
|
589
|
+
},
|
590
|
+
"sha256": {
|
591
|
+
"type": "string",
|
592
|
+
"pattern": "^[a-f0-9]{64}$",
|
593
|
+
"description": "SHA256 hash of the firmware binary (lowercase hex)"
|
594
|
+
},
|
595
|
+
"version": {
|
596
|
+
"type": "string",
|
597
|
+
"description": "Firmware version string"
|
598
|
+
},
|
599
|
+
"timestamp": {
|
600
|
+
"type": "string",
|
601
|
+
"format": "date-time",
|
602
|
+
"description": "ISO8601 timestamp"
|
603
|
+
}
|
604
|
+
},
|
605
|
+
"required": [
|
606
|
+
"file",
|
607
|
+
"size",
|
608
|
+
"sha256",
|
609
|
+
"version",
|
610
|
+
"timestamp"
|
611
|
+
],
|
612
|
+
"additionalProperties": true
|
613
|
+
}
|
614
|
+
}
|
615
|
+
};
|