openmv-ota 1.0.0__tar.gz
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.
- openmv_ota-1.0.0/LICENSE +21 -0
- openmv_ota-1.0.0/PKG-INFO +126 -0
- openmv_ota-1.0.0/README.md +79 -0
- openmv_ota-1.0.0/pyproject.toml +125 -0
- openmv_ota-1.0.0/setup.cfg +4 -0
- openmv_ota-1.0.0/src/openmv_ota/__init__.py +10 -0
- openmv_ota-1.0.0/src/openmv_ota/build/__init__.py +13 -0
- openmv_ota-1.0.0/src/openmv_ota/build/cli.py +595 -0
- openmv_ota-1.0.0/src/openmv_ota/build/compile/__init__.py +5 -0
- openmv_ota-1.0.0/src/openmv_ota/build/compile/models.py +55 -0
- openmv_ota-1.0.0/src/openmv_ota/build/compile/mpy.py +71 -0
- openmv_ota-1.0.0/src/openmv_ota/build/compile/stedgeai.py +62 -0
- openmv_ota-1.0.0/src/openmv_ota/build/compile/vela.py +51 -0
- openmv_ota-1.0.0/src/openmv_ota/build/data.py +22 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/__init__.py +17 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/boot.py +629 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/ecdsa_verify.c +135 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/__init__.py +42 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/_lib.py +578 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/csi.py +895 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/datalog.py +246 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/logs.py +531 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_log.py +157 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_netcfg.py +177 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_ota/__init__.py +1903 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_ota/data/installer.py +2303 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_recovery.py +343 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_rtc.py +342 -0
- openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_wdt.py +459 -0
- openmv_ota-1.0.0/src/openmv_ota/build/errors.py +11 -0
- openmv_ota-1.0.0/src/openmv_ota/build/firmware.py +588 -0
- openmv_ota-1.0.0/src/openmv_ota/build/pystrip.py +81 -0
- openmv_ota-1.0.0/src/openmv_ota/build/romfs.py +1446 -0
- openmv_ota-1.0.0/src/openmv_ota/build/sbom.py +222 -0
- openmv_ota-1.0.0/src/openmv_ota/build/staging.py +31 -0
- openmv_ota-1.0.0/src/openmv_ota/cli.py +81 -0
- openmv_ota-1.0.0/src/openmv_ota/client/__init__.py +7 -0
- openmv_ota-1.0.0/src/openmv_ota/client/api.py +508 -0
- openmv_ota-1.0.0/src/openmv_ota/client/cli.py +1379 -0
- openmv_ota-1.0.0/src/openmv_ota/client/config.py +76 -0
- openmv_ota-1.0.0/src/openmv_ota/client/errors.py +11 -0
- openmv_ota-1.0.0/src/openmv_ota/data/boards.json +547 -0
- openmv_ota-1.0.0/src/openmv_ota/data/firmware/OPENMV_AE3/vela.ini +134 -0
- openmv_ota-1.0.0/src/openmv_ota/data/firmware/OPENMV_N6/neuralart.json +10 -0
- openmv_ota-1.0.0/src/openmv_ota/data/firmware/OPENMV_N6/stm32n6.mpool +171 -0
- openmv_ota-1.0.0/src/openmv_ota/data/flashloaders/OPENMV_RT1060/blhost_flash_loader.bin +0 -0
- openmv_ota-1.0.0/src/openmv_ota/data/flashloaders/OPENMV_RT1060/sdphost_flash_loader.bin +0 -0
- openmv_ota-1.0.0/src/openmv_ota/data/n6_bootloader/FlashLayout.tsv +4 -0
- openmv_ota-1.0.0/src/openmv_ota/data/n6_bootloader/MX66UW1G45G_STM32N6570-DK-OBL.bin +0 -0
- openmv_ota-1.0.0/src/openmv_ota/data/n6_bootloader/OpenBootloader_STM32N6570-DK-trusted.stm32 +0 -0
- openmv_ota-1.0.0/src/openmv_ota/data/openmv-cloud-roots.pem +283 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/__init__.py +10 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/alif.py +82 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/arduino.py +81 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/cli.py +201 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/device.py +107 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/dfu.py +63 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/errors.py +11 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/flash.py +729 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/imx.py +246 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/inventory.py +132 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/runner.py +56 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/targets.py +70 -0
- openmv_ota-1.0.0/src/openmv_ota/flash/tools.py +83 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/__init__.py +99 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/_extras.py +34 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/algorithms.py +55 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/bundle.py +47 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/delta.py +231 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/errors.py +11 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/geometry.py +194 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/keys.py +175 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/manifest.py +221 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/partition.py +62 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/payload.py +179 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/rollback.py +65 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/sign.py +63 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/signer.py +151 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/signer_kms.py +202 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/signer_pkcs11.py +116 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/status.py +167 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/trailer.py +254 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/verify.py +83 -0
- openmv_ota-1.0.0/src/openmv_ota/ota/version.py +57 -0
- openmv_ota-1.0.0/src/openmv_ota/project/__init__.py +17 -0
- openmv_ota-1.0.0/src/openmv_ota/project/backends.py +40 -0
- openmv_ota-1.0.0/src/openmv_ota/project/cache.py +33 -0
- openmv_ota-1.0.0/src/openmv_ota/project/cli.py +541 -0
- openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/conformity-assessment-checklist.md +52 -0
- openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/eu-doc.md +25 -0
- openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/security.txt +13 -0
- openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/vuln-disclosure-policy.md +34 -0
- openmv_ota-1.0.0/src/openmv_ota/project/config.py +302 -0
- openmv_ota-1.0.0/src/openmv_ota/project/errors.py +15 -0
- openmv_ota-1.0.0/src/openmv_ota/project/gitrepo.py +156 -0
- openmv_ota-1.0.0/src/openmv_ota/project/history.py +63 -0
- openmv_ota-1.0.0/src/openmv_ota/project/keybackup.py +43 -0
- openmv_ota-1.0.0/src/openmv_ota/project/keys.py +178 -0
- openmv_ota-1.0.0/src/openmv_ota/project/ledger.py +91 -0
- openmv_ota-1.0.0/src/openmv_ota/project/lock.py +112 -0
- openmv_ota-1.0.0/src/openmv_ota/project/passphrase.py +59 -0
- openmv_ota-1.0.0/src/openmv_ota/project/payload_keys.py +169 -0
- openmv_ota-1.0.0/src/openmv_ota/project/project.py +1310 -0
- openmv_ota-1.0.0/src/openmv_ota/project/resolve/__init__.py +5 -0
- openmv_ota-1.0.0/src/openmv_ota/project/resolve/board.py +158 -0
- openmv_ota-1.0.0/src/openmv_ota/project/resolve/firmware.py +55 -0
- openmv_ota-1.0.0/src/openmv_ota/project/resolve/macros.py +37 -0
- openmv_ota-1.0.0/src/openmv_ota/project/resolve/micropython.py +66 -0
- openmv_ota-1.0.0/src/openmv_ota/project/resolve/sdk.py +124 -0
- openmv_ota-1.0.0/src/openmv_ota/project/resolve/submodules.py +18 -0
- openmv_ota-1.0.0/src/openmv_ota/project/sdk_install.py +127 -0
- openmv_ota-1.0.0/src/openmv_ota/romfs/__init__.py +19 -0
- openmv_ota-1.0.0/src/openmv_ota/romfs/boards.py +189 -0
- openmv_ota-1.0.0/src/openmv_ota/romfs/builder.py +211 -0
- openmv_ota-1.0.0/src/openmv_ota/romfs/cli.py +428 -0
- openmv_ota-1.0.0/src/openmv_ota/romfs/container.py +348 -0
- openmv_ota-1.0.0/src/openmv_ota/server/__init__.py +18 -0
- openmv_ota-1.0.0/src/openmv_ota/server/_extras.py +26 -0
- openmv_ota-1.0.0/src/openmv_ota/server/admin.py +1805 -0
- openmv_ota-1.0.0/src/openmv_ota/server/advisor.py +273 -0
- openmv_ota-1.0.0/src/openmv_ota/server/app.py +1094 -0
- openmv_ota-1.0.0/src/openmv_ota/server/auth.py +79 -0
- openmv_ota-1.0.0/src/openmv_ota/server/capability.py +51 -0
- openmv_ota-1.0.0/src/openmv_ota/server/cli.py +452 -0
- openmv_ota-1.0.0/src/openmv_ota/server/datalake.py +41 -0
- openmv_ota-1.0.0/src/openmv_ota/server/datalog.py +63 -0
- openmv_ota-1.0.0/src/openmv_ota/server/deploy/Dockerfile +21 -0
- openmv_ota-1.0.0/src/openmv_ota/server/deploy/docker-compose.yml +70 -0
- openmv_ota-1.0.0/src/openmv_ota/server/docs_static/favicon.ico +0 -0
- openmv_ota-1.0.0/src/openmv_ota/server/docs_static/logo-dark.png +0 -0
- openmv_ota-1.0.0/src/openmv_ota/server/docs_static/logo.png +0 -0
- openmv_ota-1.0.0/src/openmv_ota/server/docs_static/redoc.standalone.js +1832 -0
- openmv_ota-1.0.0/src/openmv_ota/server/errors.py +11 -0
- openmv_ota-1.0.0/src/openmv_ota/server/live.py +116 -0
- openmv_ota-1.0.0/src/openmv_ota/server/metastore.py +2326 -0
- openmv_ota-1.0.0/src/openmv_ota/server/publish.py +360 -0
- openmv_ota-1.0.0/src/openmv_ota/server/ratelimit.py +119 -0
- openmv_ota-1.0.0/src/openmv_ota/server/rollout.py +168 -0
- openmv_ota-1.0.0/src/openmv_ota/server/schemas.py +658 -0
- openmv_ota-1.0.0/src/openmv_ota/server/scopes.py +45 -0
- openmv_ota-1.0.0/src/openmv_ota/server/settings.py +186 -0
- openmv_ota-1.0.0/src/openmv_ota/server/storage.py +130 -0
- openmv_ota-1.0.0/src/openmv_ota/server/verify.py +89 -0
- openmv_ota-1.0.0/src/openmv_ota/server/webhooks.py +202 -0
- openmv_ota-1.0.0/src/openmv_ota.egg-info/PKG-INFO +126 -0
- openmv_ota-1.0.0/src/openmv_ota.egg-info/SOURCES.txt +151 -0
- openmv_ota-1.0.0/src/openmv_ota.egg-info/dependency_links.txt +1 -0
- openmv_ota-1.0.0/src/openmv_ota.egg-info/entry_points.txt +2 -0
- openmv_ota-1.0.0/src/openmv_ota.egg-info/requires.txt +36 -0
- openmv_ota-1.0.0/src/openmv_ota.egg-info/top_level.txt +1 -0
- openmv_ota-1.0.0/tests/test_cli.py +29 -0
- openmv_ota-1.0.0/tests/test_cli_top.py +80 -0
- openmv_ota-1.0.0/tests/test_docs_pages.py +159 -0
openmv_ota-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-2026 OpenMV
|
|
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.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openmv-ota
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Secure over-the-air update tooling for OpenMV cameras (ROMFS OTA, with room for firmware/bootloader OTA).
|
|
5
|
+
Author-email: OpenMV <openmv@openmv.io>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/openmv/openmv-ota
|
|
8
|
+
Project-URL: Source, https://github.com/openmv/openmv-ota
|
|
9
|
+
Project-URL: Issues, https://github.com/openmv/openmv-ota/issues
|
|
10
|
+
Keywords: openmv,ota,romfs,firmware,update,ecdsa,embedded
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
14
|
+
Classifier: Topic :: Security :: Cryptography
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: cryptography>=42
|
|
19
|
+
Requires-Dist: pyserial>=3.5
|
|
20
|
+
Requires-Dist: mpremote>=1.20
|
|
21
|
+
Requires-Dist: httpx>=0.27
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=4; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-xdist>=3; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff; extra == "dev"
|
|
27
|
+
Requires-Dist: hypothesis>=6; extra == "dev"
|
|
28
|
+
Provides-Extra: server
|
|
29
|
+
Requires-Dist: fastapi>=0.110; extra == "server"
|
|
30
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == "server"
|
|
31
|
+
Requires-Dist: pydantic-settings>=2; extra == "server"
|
|
32
|
+
Requires-Dist: python-multipart>=0.0.9; extra == "server"
|
|
33
|
+
Provides-Extra: server-s3
|
|
34
|
+
Requires-Dist: boto3>=1.34; extra == "server-s3"
|
|
35
|
+
Provides-Extra: server-postgres
|
|
36
|
+
Requires-Dist: psycopg[binary,pool]>=3.1; extra == "server-postgres"
|
|
37
|
+
Provides-Extra: hsm
|
|
38
|
+
Requires-Dist: python-pkcs11>=0.7; extra == "hsm"
|
|
39
|
+
Provides-Extra: aws-kms
|
|
40
|
+
Requires-Dist: boto3[crt]>=1.34; extra == "aws-kms"
|
|
41
|
+
Provides-Extra: gcp-kms
|
|
42
|
+
Requires-Dist: google-cloud-kms>=2; extra == "gcp-kms"
|
|
43
|
+
Provides-Extra: azure-kms
|
|
44
|
+
Requires-Dist: azure-keyvault-keys>=4; extra == "azure-kms"
|
|
45
|
+
Requires-Dist: azure-identity>=1; extra == "azure-kms"
|
|
46
|
+
Dynamic: license-file
|
|
47
|
+
|
|
48
|
+
<p align="center">
|
|
49
|
+
<img src="docs/under-construction.svg" width="100%" alt="Under Construction — this project is a work in progress">
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
[](https://github.com/openmv/openmv-ota/actions/workflows/ci.yml)
|
|
53
|
+
[](https://codecov.io/gh/openmv/openmv-ota)
|
|
54
|
+
[](https://github.com/openmv/openmv-ota/blob/main/LICENSE)
|
|
55
|
+

|
|
56
|
+
[](https://github.com/openmv/openmv-ota/network)
|
|
57
|
+
[](https://github.com/openmv/openmv-ota/stargazers)
|
|
58
|
+
[](https://github.com/openmv/openmv-ota/issues)
|
|
59
|
+
|
|
60
|
+
<img width="480" src="https://raw.githubusercontent.com/openmv/openmv-media/master/logos/openmv-logo/logo.png">
|
|
61
|
+
|
|
62
|
+
# OpenMV OTA
|
|
63
|
+
|
|
64
|
+
Secure over-the-air updates for OpenMV cameras: build your application into a
|
|
65
|
+
signed ROMFS image, publish it, and a camera downloads, verifies, and installs
|
|
66
|
+
it — falling back to the last release that worked if anything goes wrong.
|
|
67
|
+
|
|
68
|
+
What a camera downloads is **encrypted**, under a key minted with the project and
|
|
69
|
+
baked into your own firmware, so a published release is ciphertext to the update
|
|
70
|
+
server, to the store it sits in, and to anyone who gets hold of it.
|
|
71
|
+
|
|
72
|
+
**Start with the [tutorial](https://github.com/openmv/openmv-ota/blob/main/docs/tutorial/00-introduction.md)** — the complete,
|
|
73
|
+
navigable reference for every command and the update server's HTTP API, in the
|
|
74
|
+
order you use them: install → project → build → flash → device runtime → update
|
|
75
|
+
server. The command documentation lives there and only there, so it has one
|
|
76
|
+
place to be right.
|
|
77
|
+
|
|
78
|
+
Shipping in the EU? [The compliance mapping](https://github.com/openmv/openmv-ota/blob/main/docs/compliance/cra-red-alignment.md)
|
|
79
|
+
lays out how this stack lines up with the Cyber Resilience Act and RED 3.3 —
|
|
80
|
+
what's covered, and the [residual threats](https://github.com/openmv/openmv-ota/blob/main/docs/compliance/residual-threats.md)
|
|
81
|
+
that aren't — and `project new --ota` scaffolds the fill-in templates
|
|
82
|
+
(conformity checklist, EU DoC, disclosure policy, security.txt) into your
|
|
83
|
+
project's `compliance/`. Found a vulnerability in this stack itself? See
|
|
84
|
+
[the security policy](https://github.com/openmv/openmv-ota/blob/main/SECURITY.md).
|
|
85
|
+
|
|
86
|
+
## Installation
|
|
87
|
+
|
|
88
|
+
> Not yet published. Once the package lands on PyPI, all tools install together:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install openmv-ota
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
For development, install from a checkout:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install -e .
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Contributing to the project
|
|
101
|
+
|
|
102
|
+
Contributions are most welcome. If you are interested in contributing to the project, start by creating a fork of the repository:
|
|
103
|
+
|
|
104
|
+
* https://github.com/openmv/openmv-ota.git
|
|
105
|
+
|
|
106
|
+
Clone the forked repository, and add a remote to the main openmv-ota repository:
|
|
107
|
+
```bash
|
|
108
|
+
git clone https://github.com/<username>/openmv-ota.git
|
|
109
|
+
git -C openmv-ota remote add upstream https://github.com/openmv/openmv-ota.git
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Now the repository is ready for pull requests. To send a pull request, create a new feature branch and push it to origin, and use Github to create the pull request from the forked repository to the upstream openmv/openmv-ota repository. For example:
|
|
113
|
+
```bash
|
|
114
|
+
git checkout -b <some_branch_name>
|
|
115
|
+
<commit changes>
|
|
116
|
+
git push origin -u <some_branch_name>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Contribution guidelines
|
|
120
|
+
Please follow the [best practices](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_pr) when sending pull requests upstream. In general, the pull request should:
|
|
121
|
+
* Fix one problem. Don't try to tackle multiple issues at once.
|
|
122
|
+
* Split the changes into logical groups using git commits.
|
|
123
|
+
* Pull request title should be less than 78 characters, and match this pattern:
|
|
124
|
+
* `<scope>:<1 space><description><.>`
|
|
125
|
+
* Commit subject line should be less than 78 characters, and match this pattern:
|
|
126
|
+
* `<scope>:<1 space><description><.>`
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/under-construction.svg" width="100%" alt="Under Construction — this project is a work in progress">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
[](https://github.com/openmv/openmv-ota/actions/workflows/ci.yml)
|
|
6
|
+
[](https://codecov.io/gh/openmv/openmv-ota)
|
|
7
|
+
[](https://github.com/openmv/openmv-ota/blob/main/LICENSE)
|
|
8
|
+

|
|
9
|
+
[](https://github.com/openmv/openmv-ota/network)
|
|
10
|
+
[](https://github.com/openmv/openmv-ota/stargazers)
|
|
11
|
+
[](https://github.com/openmv/openmv-ota/issues)
|
|
12
|
+
|
|
13
|
+
<img width="480" src="https://raw.githubusercontent.com/openmv/openmv-media/master/logos/openmv-logo/logo.png">
|
|
14
|
+
|
|
15
|
+
# OpenMV OTA
|
|
16
|
+
|
|
17
|
+
Secure over-the-air updates for OpenMV cameras: build your application into a
|
|
18
|
+
signed ROMFS image, publish it, and a camera downloads, verifies, and installs
|
|
19
|
+
it — falling back to the last release that worked if anything goes wrong.
|
|
20
|
+
|
|
21
|
+
What a camera downloads is **encrypted**, under a key minted with the project and
|
|
22
|
+
baked into your own firmware, so a published release is ciphertext to the update
|
|
23
|
+
server, to the store it sits in, and to anyone who gets hold of it.
|
|
24
|
+
|
|
25
|
+
**Start with the [tutorial](https://github.com/openmv/openmv-ota/blob/main/docs/tutorial/00-introduction.md)** — the complete,
|
|
26
|
+
navigable reference for every command and the update server's HTTP API, in the
|
|
27
|
+
order you use them: install → project → build → flash → device runtime → update
|
|
28
|
+
server. The command documentation lives there and only there, so it has one
|
|
29
|
+
place to be right.
|
|
30
|
+
|
|
31
|
+
Shipping in the EU? [The compliance mapping](https://github.com/openmv/openmv-ota/blob/main/docs/compliance/cra-red-alignment.md)
|
|
32
|
+
lays out how this stack lines up with the Cyber Resilience Act and RED 3.3 —
|
|
33
|
+
what's covered, and the [residual threats](https://github.com/openmv/openmv-ota/blob/main/docs/compliance/residual-threats.md)
|
|
34
|
+
that aren't — and `project new --ota` scaffolds the fill-in templates
|
|
35
|
+
(conformity checklist, EU DoC, disclosure policy, security.txt) into your
|
|
36
|
+
project's `compliance/`. Found a vulnerability in this stack itself? See
|
|
37
|
+
[the security policy](https://github.com/openmv/openmv-ota/blob/main/SECURITY.md).
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
> Not yet published. Once the package lands on PyPI, all tools install together:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install openmv-ota
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For development, install from a checkout:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install -e .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Contributing to the project
|
|
54
|
+
|
|
55
|
+
Contributions are most welcome. If you are interested in contributing to the project, start by creating a fork of the repository:
|
|
56
|
+
|
|
57
|
+
* https://github.com/openmv/openmv-ota.git
|
|
58
|
+
|
|
59
|
+
Clone the forked repository, and add a remote to the main openmv-ota repository:
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/<username>/openmv-ota.git
|
|
62
|
+
git -C openmv-ota remote add upstream https://github.com/openmv/openmv-ota.git
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Now the repository is ready for pull requests. To send a pull request, create a new feature branch and push it to origin, and use Github to create the pull request from the forked repository to the upstream openmv/openmv-ota repository. For example:
|
|
66
|
+
```bash
|
|
67
|
+
git checkout -b <some_branch_name>
|
|
68
|
+
<commit changes>
|
|
69
|
+
git push origin -u <some_branch_name>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Contribution guidelines
|
|
73
|
+
Please follow the [best practices](https://developers.google.com/blockly/guides/modify/contribute/write_a_good_pr) when sending pull requests upstream. In general, the pull request should:
|
|
74
|
+
* Fix one problem. Don't try to tackle multiple issues at once.
|
|
75
|
+
* Split the changes into logical groups using git commits.
|
|
76
|
+
* Pull request title should be less than 78 characters, and match this pattern:
|
|
77
|
+
* `<scope>:<1 space><description><.>`
|
|
78
|
+
* Commit subject line should be less than 78 characters, and match this pattern:
|
|
79
|
+
* `<scope>:<1 space><description><.>`
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "openmv-ota"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Secure over-the-air update tooling for OpenMV cameras (ROMFS OTA, with room for firmware/bootloader OTA)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "OpenMV", email = "openmv@openmv.io" }]
|
|
14
|
+
keywords = ["openmv", "ota", "romfs", "firmware", "update", "ecdsa", "embedded"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Topic :: Software Development :: Embedded Systems",
|
|
19
|
+
"Topic :: Security :: Cryptography",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
# Runtime dependencies are added as each tool is built out (see the plan):
|
|
23
|
+
# server: a web framework + object-storage adapters
|
|
24
|
+
# sbom/cve: cyclonedx, osv/nvd scanners
|
|
25
|
+
dependencies = [
|
|
26
|
+
"cryptography>=42", # ECDSA keygen + signing for OTA trailers
|
|
27
|
+
"pyserial>=3.5", # serial-port discovery + 1200-baud touch-to-reset (Arduino)
|
|
28
|
+
"mpremote>=1.20", # reset OpenMV-protocol cameras into the bootloader (machine.bootloader)
|
|
29
|
+
"httpx>=0.27", # the `client` verbs (release publish, rollouts, ...) talk to the server
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
dev = ["pytest>=7", "pytest-cov>=4", "pytest-xdist>=3", "ruff", "hypothesis>=6"]
|
|
34
|
+
# The update server. (`httpx`, which the client verbs and FastAPI's test client both use, is a
|
|
35
|
+
# base dependency: Getting started publishes with a plain `pip install openmv-ota`.)
|
|
36
|
+
# Heavy/optional backends are separate extras, lazily imported only when that backend is
|
|
37
|
+
# selected, so the base `server` install stays lean (local-disk + stdlib sqlite work out of it).
|
|
38
|
+
server = ["fastapi>=0.110", "uvicorn[standard]>=0.29", "pydantic-settings>=2",
|
|
39
|
+
"python-multipart>=0.0.9"]
|
|
40
|
+
server-s3 = ["boto3>=1.34"]
|
|
41
|
+
server-postgres = ["psycopg[binary,pool]>=3.1"]
|
|
42
|
+
# Signer backends: the key never lands on disk as a file. One PKCS#11 backend covers all hardware
|
|
43
|
+
# tokens/HSMs; the three cloud KMS providers are separate extras.
|
|
44
|
+
hsm = ["python-pkcs11>=0.7"]
|
|
45
|
+
aws-kms = ["boto3[crt]>=1.34"]
|
|
46
|
+
gcp-kms = ["google-cloud-kms>=2"]
|
|
47
|
+
azure-kms = ["azure-keyvault-keys>=4", "azure-identity>=1"]
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Homepage = "https://github.com/openmv/openmv-ota"
|
|
51
|
+
Source = "https://github.com/openmv/openmv-ota"
|
|
52
|
+
Issues = "https://github.com/openmv/openmv-ota/issues"
|
|
53
|
+
|
|
54
|
+
[project.scripts]
|
|
55
|
+
openmv-ota = "openmv_ota.cli:main"
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.packages.find]
|
|
58
|
+
where = ["src"]
|
|
59
|
+
|
|
60
|
+
[tool.setuptools.package-data]
|
|
61
|
+
# The on-device SDK ships as package data, bundled into the ROMFS at build time.
|
|
62
|
+
"openmv_ota" = [
|
|
63
|
+
"data/*.json",
|
|
64
|
+
"data/*.pem", # the hosted OpenMV Cloud trust anchors (certs/root.pem)
|
|
65
|
+
"data/firmware/**/*",
|
|
66
|
+
"data/flashloaders/**/*", # prebuilt i.MX RT sdphost/blhost flashloaders
|
|
67
|
+
"data/n6_bootloader/*", # static N6 CubeProgrammer FSBL/loader + FlashLayout.tsv
|
|
68
|
+
"build/device/*.c", # the ECDSA verify C module dropped into modules/
|
|
69
|
+
"build/device/openmv_ota/data/*", # the installer source, scaffolded into projects
|
|
70
|
+
"server/deploy/*", # Dockerfile + compose stack for self-hosters
|
|
71
|
+
"server/docs_static/*", # self-hosted /docs API reference (ReDoc bundle + logo)
|
|
72
|
+
"project/compliance_templates/*", # CRA/RED templates, scaffolded into OTA projects
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
pythonpath = ["src"]
|
|
77
|
+
testpaths = ["tests"]
|
|
78
|
+
addopts = "-n auto --dist worksteal --cov=openmv_ota --cov-report=term-missing --cov-fail-under=100 --import-mode=importlib"
|
|
79
|
+
# Default norecursedirs excludes "build"; we have a tests/build/ dir to collect.
|
|
80
|
+
norecursedirs = ["*.egg", ".*", "_darcs", "CVS", "dist", "node_modules", "venv", "{arch}"]
|
|
81
|
+
|
|
82
|
+
[tool.coverage.run]
|
|
83
|
+
source = ["openmv_ota"]
|
|
84
|
+
|
|
85
|
+
[tool.coverage.report]
|
|
86
|
+
show_missing = true
|
|
87
|
+
exclude_also = [
|
|
88
|
+
"if __name__ == .__main__.:",
|
|
89
|
+
"raise NotImplementedError",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[tool.ruff]
|
|
93
|
+
line-length = 100
|
|
94
|
+
|
|
95
|
+
[tool.ruff.lint]
|
|
96
|
+
# The rule set `ruff check .` is required to pass (CLAUDE.md; enforced in CI).
|
|
97
|
+
# Pinned explicitly so a newer ruff's expanded defaults can't repaint the repo red.
|
|
98
|
+
# S (bandit) and B (bugbear) are the security/bug-risk pass: a finding is fixed, or suppressed
|
|
99
|
+
# on its line with the reason. Only the rules below are waived wholesale, each for its reason.
|
|
100
|
+
select = ["E4", "E7", "E9", "F", "S", "B"]
|
|
101
|
+
ignore = [
|
|
102
|
+
# every subprocess call passes an argv list; shell=True (S602/S604/S605) stays an error
|
|
103
|
+
"S603",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[tool.ruff.lint.flake8-bugbear]
|
|
107
|
+
# FastAPI's dependency-injection idiom is a call in the default, evaluated once by design
|
|
108
|
+
extend-immutable-calls = [
|
|
109
|
+
"fastapi.Depends", "fastapi.File", "fastapi.Query", "fastapi.Header",
|
|
110
|
+
"openmv_ota.server.auth.require_scope",
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
[tool.ruff.lint.per-file-ignores]
|
|
114
|
+
# tests assert, use fixed fake secrets and throwaway paths by design
|
|
115
|
+
"tests/**" = ["S"]
|
|
116
|
+
# MicroPython has no zip(strict=) and no exception chaining (B904/B905); the survival
|
|
117
|
+
# paths (logging, watchdog, RTC, spool replay) must never raise and cannot afford to log
|
|
118
|
+
# the failure they are already handling (S110/S112)
|
|
119
|
+
"src/openmv_ota/build/device/**" = ["B904", "B905", "S110", "S112"]
|
|
120
|
+
# the bench harness drives tools found on the runner's PATH (mpremote, dfu-util, JLinkExe),
|
|
121
|
+
# and tears it down best-effort; its only URL reads are its own localhost bench server
|
|
122
|
+
"ci/**" = ["S607", "S110", "S310"]
|
|
123
|
+
# every value is a bound parameter; the only interpolated text is a column or table name
|
|
124
|
+
# from a code-side whitelist (_order, the constant table tuples, _updatable's allow-lists)
|
|
125
|
+
"src/openmv_ota/server/metastore.py" = ["S608"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""openmv-ota — secure OTA update tooling for OpenMV cameras.
|
|
2
|
+
|
|
3
|
+
Subsystems live under this package. The first is :mod:`openmv_ota.romfs`
|
|
4
|
+
(ROMFS OTA). Whole-firmware and bootloader OTA are intended as future siblings.
|
|
5
|
+
|
|
6
|
+
The authoritative design walkthrough is ``docs/tutorial/`` (the slot model lives
|
|
7
|
+
on its boot-and-rollback and device-library pages).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Firmware-aware artifact builders.
|
|
2
|
+
|
|
3
|
+
`openmv-ota build romfs` compiles a project's app the way the pegged firmware
|
|
4
|
+
expects (``.py`` -> ``.mpy`` with the pegged mpy-cross, NPU models via the pegged
|
|
5
|
+
Vela / ST Edge AI) and packs a ROMFS image per target. It is OTA-agnostic: peg a
|
|
6
|
+
project, build a romfs, flash it.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from .romfs import build_romfs
|
|
12
|
+
|
|
13
|
+
__all__ = ["build_romfs"]
|