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.
Files changed (153) hide show
  1. openmv_ota-1.0.0/LICENSE +21 -0
  2. openmv_ota-1.0.0/PKG-INFO +126 -0
  3. openmv_ota-1.0.0/README.md +79 -0
  4. openmv_ota-1.0.0/pyproject.toml +125 -0
  5. openmv_ota-1.0.0/setup.cfg +4 -0
  6. openmv_ota-1.0.0/src/openmv_ota/__init__.py +10 -0
  7. openmv_ota-1.0.0/src/openmv_ota/build/__init__.py +13 -0
  8. openmv_ota-1.0.0/src/openmv_ota/build/cli.py +595 -0
  9. openmv_ota-1.0.0/src/openmv_ota/build/compile/__init__.py +5 -0
  10. openmv_ota-1.0.0/src/openmv_ota/build/compile/models.py +55 -0
  11. openmv_ota-1.0.0/src/openmv_ota/build/compile/mpy.py +71 -0
  12. openmv_ota-1.0.0/src/openmv_ota/build/compile/stedgeai.py +62 -0
  13. openmv_ota-1.0.0/src/openmv_ota/build/compile/vela.py +51 -0
  14. openmv_ota-1.0.0/src/openmv_ota/build/data.py +22 -0
  15. openmv_ota-1.0.0/src/openmv_ota/build/device/__init__.py +17 -0
  16. openmv_ota-1.0.0/src/openmv_ota/build/device/boot.py +629 -0
  17. openmv_ota-1.0.0/src/openmv_ota/build/device/ecdsa_verify.c +135 -0
  18. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/__init__.py +42 -0
  19. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/_lib.py +578 -0
  20. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/csi.py +895 -0
  21. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/datalog.py +246 -0
  22. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_cloud/logs.py +531 -0
  23. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_log.py +157 -0
  24. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_netcfg.py +177 -0
  25. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_ota/__init__.py +1903 -0
  26. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_ota/data/installer.py +2303 -0
  27. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_recovery.py +343 -0
  28. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_rtc.py +342 -0
  29. openmv_ota-1.0.0/src/openmv_ota/build/device/openmv_wdt.py +459 -0
  30. openmv_ota-1.0.0/src/openmv_ota/build/errors.py +11 -0
  31. openmv_ota-1.0.0/src/openmv_ota/build/firmware.py +588 -0
  32. openmv_ota-1.0.0/src/openmv_ota/build/pystrip.py +81 -0
  33. openmv_ota-1.0.0/src/openmv_ota/build/romfs.py +1446 -0
  34. openmv_ota-1.0.0/src/openmv_ota/build/sbom.py +222 -0
  35. openmv_ota-1.0.0/src/openmv_ota/build/staging.py +31 -0
  36. openmv_ota-1.0.0/src/openmv_ota/cli.py +81 -0
  37. openmv_ota-1.0.0/src/openmv_ota/client/__init__.py +7 -0
  38. openmv_ota-1.0.0/src/openmv_ota/client/api.py +508 -0
  39. openmv_ota-1.0.0/src/openmv_ota/client/cli.py +1379 -0
  40. openmv_ota-1.0.0/src/openmv_ota/client/config.py +76 -0
  41. openmv_ota-1.0.0/src/openmv_ota/client/errors.py +11 -0
  42. openmv_ota-1.0.0/src/openmv_ota/data/boards.json +547 -0
  43. openmv_ota-1.0.0/src/openmv_ota/data/firmware/OPENMV_AE3/vela.ini +134 -0
  44. openmv_ota-1.0.0/src/openmv_ota/data/firmware/OPENMV_N6/neuralart.json +10 -0
  45. openmv_ota-1.0.0/src/openmv_ota/data/firmware/OPENMV_N6/stm32n6.mpool +171 -0
  46. openmv_ota-1.0.0/src/openmv_ota/data/flashloaders/OPENMV_RT1060/blhost_flash_loader.bin +0 -0
  47. openmv_ota-1.0.0/src/openmv_ota/data/flashloaders/OPENMV_RT1060/sdphost_flash_loader.bin +0 -0
  48. openmv_ota-1.0.0/src/openmv_ota/data/n6_bootloader/FlashLayout.tsv +4 -0
  49. openmv_ota-1.0.0/src/openmv_ota/data/n6_bootloader/MX66UW1G45G_STM32N6570-DK-OBL.bin +0 -0
  50. openmv_ota-1.0.0/src/openmv_ota/data/n6_bootloader/OpenBootloader_STM32N6570-DK-trusted.stm32 +0 -0
  51. openmv_ota-1.0.0/src/openmv_ota/data/openmv-cloud-roots.pem +283 -0
  52. openmv_ota-1.0.0/src/openmv_ota/flash/__init__.py +10 -0
  53. openmv_ota-1.0.0/src/openmv_ota/flash/alif.py +82 -0
  54. openmv_ota-1.0.0/src/openmv_ota/flash/arduino.py +81 -0
  55. openmv_ota-1.0.0/src/openmv_ota/flash/cli.py +201 -0
  56. openmv_ota-1.0.0/src/openmv_ota/flash/device.py +107 -0
  57. openmv_ota-1.0.0/src/openmv_ota/flash/dfu.py +63 -0
  58. openmv_ota-1.0.0/src/openmv_ota/flash/errors.py +11 -0
  59. openmv_ota-1.0.0/src/openmv_ota/flash/flash.py +729 -0
  60. openmv_ota-1.0.0/src/openmv_ota/flash/imx.py +246 -0
  61. openmv_ota-1.0.0/src/openmv_ota/flash/inventory.py +132 -0
  62. openmv_ota-1.0.0/src/openmv_ota/flash/runner.py +56 -0
  63. openmv_ota-1.0.0/src/openmv_ota/flash/targets.py +70 -0
  64. openmv_ota-1.0.0/src/openmv_ota/flash/tools.py +83 -0
  65. openmv_ota-1.0.0/src/openmv_ota/ota/__init__.py +99 -0
  66. openmv_ota-1.0.0/src/openmv_ota/ota/_extras.py +34 -0
  67. openmv_ota-1.0.0/src/openmv_ota/ota/algorithms.py +55 -0
  68. openmv_ota-1.0.0/src/openmv_ota/ota/bundle.py +47 -0
  69. openmv_ota-1.0.0/src/openmv_ota/ota/delta.py +231 -0
  70. openmv_ota-1.0.0/src/openmv_ota/ota/errors.py +11 -0
  71. openmv_ota-1.0.0/src/openmv_ota/ota/geometry.py +194 -0
  72. openmv_ota-1.0.0/src/openmv_ota/ota/keys.py +175 -0
  73. openmv_ota-1.0.0/src/openmv_ota/ota/manifest.py +221 -0
  74. openmv_ota-1.0.0/src/openmv_ota/ota/partition.py +62 -0
  75. openmv_ota-1.0.0/src/openmv_ota/ota/payload.py +179 -0
  76. openmv_ota-1.0.0/src/openmv_ota/ota/rollback.py +65 -0
  77. openmv_ota-1.0.0/src/openmv_ota/ota/sign.py +63 -0
  78. openmv_ota-1.0.0/src/openmv_ota/ota/signer.py +151 -0
  79. openmv_ota-1.0.0/src/openmv_ota/ota/signer_kms.py +202 -0
  80. openmv_ota-1.0.0/src/openmv_ota/ota/signer_pkcs11.py +116 -0
  81. openmv_ota-1.0.0/src/openmv_ota/ota/status.py +167 -0
  82. openmv_ota-1.0.0/src/openmv_ota/ota/trailer.py +254 -0
  83. openmv_ota-1.0.0/src/openmv_ota/ota/verify.py +83 -0
  84. openmv_ota-1.0.0/src/openmv_ota/ota/version.py +57 -0
  85. openmv_ota-1.0.0/src/openmv_ota/project/__init__.py +17 -0
  86. openmv_ota-1.0.0/src/openmv_ota/project/backends.py +40 -0
  87. openmv_ota-1.0.0/src/openmv_ota/project/cache.py +33 -0
  88. openmv_ota-1.0.0/src/openmv_ota/project/cli.py +541 -0
  89. openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/conformity-assessment-checklist.md +52 -0
  90. openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/eu-doc.md +25 -0
  91. openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/security.txt +13 -0
  92. openmv_ota-1.0.0/src/openmv_ota/project/compliance_templates/vuln-disclosure-policy.md +34 -0
  93. openmv_ota-1.0.0/src/openmv_ota/project/config.py +302 -0
  94. openmv_ota-1.0.0/src/openmv_ota/project/errors.py +15 -0
  95. openmv_ota-1.0.0/src/openmv_ota/project/gitrepo.py +156 -0
  96. openmv_ota-1.0.0/src/openmv_ota/project/history.py +63 -0
  97. openmv_ota-1.0.0/src/openmv_ota/project/keybackup.py +43 -0
  98. openmv_ota-1.0.0/src/openmv_ota/project/keys.py +178 -0
  99. openmv_ota-1.0.0/src/openmv_ota/project/ledger.py +91 -0
  100. openmv_ota-1.0.0/src/openmv_ota/project/lock.py +112 -0
  101. openmv_ota-1.0.0/src/openmv_ota/project/passphrase.py +59 -0
  102. openmv_ota-1.0.0/src/openmv_ota/project/payload_keys.py +169 -0
  103. openmv_ota-1.0.0/src/openmv_ota/project/project.py +1310 -0
  104. openmv_ota-1.0.0/src/openmv_ota/project/resolve/__init__.py +5 -0
  105. openmv_ota-1.0.0/src/openmv_ota/project/resolve/board.py +158 -0
  106. openmv_ota-1.0.0/src/openmv_ota/project/resolve/firmware.py +55 -0
  107. openmv_ota-1.0.0/src/openmv_ota/project/resolve/macros.py +37 -0
  108. openmv_ota-1.0.0/src/openmv_ota/project/resolve/micropython.py +66 -0
  109. openmv_ota-1.0.0/src/openmv_ota/project/resolve/sdk.py +124 -0
  110. openmv_ota-1.0.0/src/openmv_ota/project/resolve/submodules.py +18 -0
  111. openmv_ota-1.0.0/src/openmv_ota/project/sdk_install.py +127 -0
  112. openmv_ota-1.0.0/src/openmv_ota/romfs/__init__.py +19 -0
  113. openmv_ota-1.0.0/src/openmv_ota/romfs/boards.py +189 -0
  114. openmv_ota-1.0.0/src/openmv_ota/romfs/builder.py +211 -0
  115. openmv_ota-1.0.0/src/openmv_ota/romfs/cli.py +428 -0
  116. openmv_ota-1.0.0/src/openmv_ota/romfs/container.py +348 -0
  117. openmv_ota-1.0.0/src/openmv_ota/server/__init__.py +18 -0
  118. openmv_ota-1.0.0/src/openmv_ota/server/_extras.py +26 -0
  119. openmv_ota-1.0.0/src/openmv_ota/server/admin.py +1805 -0
  120. openmv_ota-1.0.0/src/openmv_ota/server/advisor.py +273 -0
  121. openmv_ota-1.0.0/src/openmv_ota/server/app.py +1094 -0
  122. openmv_ota-1.0.0/src/openmv_ota/server/auth.py +79 -0
  123. openmv_ota-1.0.0/src/openmv_ota/server/capability.py +51 -0
  124. openmv_ota-1.0.0/src/openmv_ota/server/cli.py +452 -0
  125. openmv_ota-1.0.0/src/openmv_ota/server/datalake.py +41 -0
  126. openmv_ota-1.0.0/src/openmv_ota/server/datalog.py +63 -0
  127. openmv_ota-1.0.0/src/openmv_ota/server/deploy/Dockerfile +21 -0
  128. openmv_ota-1.0.0/src/openmv_ota/server/deploy/docker-compose.yml +70 -0
  129. openmv_ota-1.0.0/src/openmv_ota/server/docs_static/favicon.ico +0 -0
  130. openmv_ota-1.0.0/src/openmv_ota/server/docs_static/logo-dark.png +0 -0
  131. openmv_ota-1.0.0/src/openmv_ota/server/docs_static/logo.png +0 -0
  132. openmv_ota-1.0.0/src/openmv_ota/server/docs_static/redoc.standalone.js +1832 -0
  133. openmv_ota-1.0.0/src/openmv_ota/server/errors.py +11 -0
  134. openmv_ota-1.0.0/src/openmv_ota/server/live.py +116 -0
  135. openmv_ota-1.0.0/src/openmv_ota/server/metastore.py +2326 -0
  136. openmv_ota-1.0.0/src/openmv_ota/server/publish.py +360 -0
  137. openmv_ota-1.0.0/src/openmv_ota/server/ratelimit.py +119 -0
  138. openmv_ota-1.0.0/src/openmv_ota/server/rollout.py +168 -0
  139. openmv_ota-1.0.0/src/openmv_ota/server/schemas.py +658 -0
  140. openmv_ota-1.0.0/src/openmv_ota/server/scopes.py +45 -0
  141. openmv_ota-1.0.0/src/openmv_ota/server/settings.py +186 -0
  142. openmv_ota-1.0.0/src/openmv_ota/server/storage.py +130 -0
  143. openmv_ota-1.0.0/src/openmv_ota/server/verify.py +89 -0
  144. openmv_ota-1.0.0/src/openmv_ota/server/webhooks.py +202 -0
  145. openmv_ota-1.0.0/src/openmv_ota.egg-info/PKG-INFO +126 -0
  146. openmv_ota-1.0.0/src/openmv_ota.egg-info/SOURCES.txt +151 -0
  147. openmv_ota-1.0.0/src/openmv_ota.egg-info/dependency_links.txt +1 -0
  148. openmv_ota-1.0.0/src/openmv_ota.egg-info/entry_points.txt +2 -0
  149. openmv_ota-1.0.0/src/openmv_ota.egg-info/requires.txt +36 -0
  150. openmv_ota-1.0.0/src/openmv_ota.egg-info/top_level.txt +1 -0
  151. openmv_ota-1.0.0/tests/test_cli.py +29 -0
  152. openmv_ota-1.0.0/tests/test_cli_top.py +80 -0
  153. openmv_ota-1.0.0/tests/test_docs_pages.py +159 -0
@@ -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
+ [![CI](https://github.com/openmv/openmv-ota/actions/workflows/ci.yml/badge.svg)](https://github.com/openmv/openmv-ota/actions/workflows/ci.yml)
53
+ [![codecov](https://codecov.io/gh/openmv/openmv-ota/graph/badge.svg?token=KNAA28U57K)](https://codecov.io/gh/openmv/openmv-ota)
54
+ [![GitHub license](https://img.shields.io/github/license/openmv/openmv-ota?label=license%20%E2%9A%96)](https://github.com/openmv/openmv-ota/blob/main/LICENSE)
55
+ ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/openmv/openmv-ota?sort=semver)
56
+ [![GitHub forks](https://img.shields.io/github/forks/openmv/openmv-ota?color=green)](https://github.com/openmv/openmv-ota/network)
57
+ [![GitHub stars](https://img.shields.io/github/stars/openmv/openmv-ota?color=yellow)](https://github.com/openmv/openmv-ota/stargazers)
58
+ [![GitHub issues](https://img.shields.io/github/issues/openmv/openmv-ota?color=orange)](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
+ [![CI](https://github.com/openmv/openmv-ota/actions/workflows/ci.yml/badge.svg)](https://github.com/openmv/openmv-ota/actions/workflows/ci.yml)
6
+ [![codecov](https://codecov.io/gh/openmv/openmv-ota/graph/badge.svg?token=KNAA28U57K)](https://codecov.io/gh/openmv/openmv-ota)
7
+ [![GitHub license](https://img.shields.io/github/license/openmv/openmv-ota?label=license%20%E2%9A%96)](https://github.com/openmv/openmv-ota/blob/main/LICENSE)
8
+ ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/openmv/openmv-ota?sort=semver)
9
+ [![GitHub forks](https://img.shields.io/github/forks/openmv/openmv-ota?color=green)](https://github.com/openmv/openmv-ota/network)
10
+ [![GitHub stars](https://img.shields.io/github/stars/openmv/openmv-ota?color=yellow)](https://github.com/openmv/openmv-ota/stargazers)
11
+ [![GitHub issues](https://img.shields.io/github/issues/openmv/openmv-ota?color=orange)](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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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"]