sovereign-sdk-sensor 1.3.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.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: sovereign-sdk-sensor
3
+ Version: 1.3.0
4
+ Summary: Bare-metal Write-Side Custody enforcement for MicroPython sensor nodes with hardware crypto abstraction
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/kenwalger/sovereign-sdk
7
+ Project-URL: Repository, https://github.com/kenwalger/sovereign-sdk
8
+ Project-URL: Changelog, https://github.com/kenwalger/sovereign-sdk/blob/main/CHANGELOG.md
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: Implementation :: MicroPython
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Security
16
+ Classifier: Topic :: System :: Hardware
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+
21
+ # sovereign-sensor — Phase 9
22
+
23
+ A lightweight, MicroPython-compatible Hardware Abstraction Layer (HAL) that enforces
24
+ data custody at the **Point of Genesis** by sealing each sensor observation into a
25
+ versioned, tamper-evident, replay-protected JSON transmission envelope before any
26
+ network transit or cloud ingestion occurs.
27
+
28
+ Zero external dependencies. Internal library code is restricted to standard MicroPython
29
+ built-ins (`json`, `sys`, `hashlib`, `hmac`, `binascii`). Targets ESP32 and Raspberry Pi
30
+ Pico via MicroPython; fully exercisable on CPython 3.12 for desktop CI.
31
+
32
+ ---
33
+
34
+ ## Architecture
35
+
36
+ ### Hardware Abstraction Layer
37
+
38
+ `SovereignCryptoDriver` (`interface.py`) defines a three-method contract:
39
+
40
+ | Method | Contract |
41
+ |---|---|
42
+ | `initialize_hardware() -> None` | Load key material; configure accelerator subsystem. |
43
+ | `sign(payload: bytes) -> bytes` | Return raw binary signature bytes (no encoding). |
44
+ | `algorithm() -> str` | Return a canonical algorithm identifier string. |
45
+
46
+ Two concrete drivers are provided:
47
+
48
+ - **`SoftwareFallbackDriver`** — HMAC-SHA256 over a VFS-resident binary key file.
49
+ Used on all non-ESP32 targets (desktop CI, Raspberry Pi Pico, etc.).
50
+ The class-level `MOCK_KEY_SENTINEL = "/mock/test_gateway.key"` opts into a
51
+ deterministic stub key for desktop testing; every other path that cannot be opened
52
+ raises `RuntimeError` immediately, eliminating silent key substitution. A zero-byte
53
+ key file raises `ValueError` to prevent HMAC keyed with `b""`.
54
+
55
+ - **`ESP32HardwareDriver`** — Skeleton placeholder for the on-chip ECC accelerator.
56
+ `initialize_hardware()` raises `NotImplementedError` until register-level engineering
57
+ is complete; `bootstrap_sensor_node()` catches this and falls back to
58
+ `SoftwareFallbackDriver` automatically so the sealing and VFS layers remain
59
+ exercisable on the workbench.
60
+
61
+ ### Seven-Step Sealing Pipeline (`SovereignEnvelope.seal()`)
62
+
63
+ 1. **Monotonic sequence counter** — computed transiently as `_sequence + 1` and bound
64
+ into the preimage. The in-memory counter and VFS file (default `.sovereign_sequence`)
65
+ are advanced only after signing succeeds, so a `sign()` failure never consumes a
66
+ sequence position or introduces a gap in the on-disk custody timeline. A truncated
67
+ (0-byte) file resets to 0; a negative stored value is clamped to 0. VFS write
68
+ failures degrade gracefully to RAM-only tracking.
69
+
70
+ 2. **Algorithm identifier** — queried from the active driver via `algorithm()` and
71
+ embedded in the authenticated preimage, providing protocol agility without
72
+ a schema change.
73
+
74
+ 3. **Canonical payload serialization** — `json.dumps(payload, separators=(",", ":"), sort_keys=True, ensure_ascii=False)`
75
+ guarantees a byte-identical preimage for semantically equivalent payloads
76
+ regardless of key insertion order. `ensure_ascii=False` forces raw UTF-8 output
77
+ for all characters, eliminating the `\uXXXX`-vs-raw-UTF-8 split-brain divergence
78
+ that would cause cross-platform HMAC verification to fail silently on any payload
79
+ containing characters outside U+007F.
80
+
81
+ 4. **UTF-8 byte-count-prefixed preimage assembly** — `node_id`, `timestamp`, and the
82
+ `algorithm` identifier are each encoded to UTF-8 independently; the prefix for each
83
+ field is the UTF-8 byte count (not the Unicode character count). The preimage is
84
+ assembled from raw byte slices:
85
+
86
+ ```
87
+ 1|{len(node_bytes)}:{node_id}|{len(time_bytes)}:{timestamp}|{seq}|{len(algo_bytes)}:{algo}|{canonical}
88
+ ```
89
+
90
+ Byte-count prefixes close all variable-length field injection surfaces: delimiter
91
+ injection (any two inputs that differ only in where a `|` character falls produce
92
+ identical naive pipe-joined preimage bytes without prefixes) and multi-byte encoding
93
+ ambiguity (a receiver using character-count semantics parses field boundaries at the
94
+ wrong byte offset for any non-ASCII field value).
95
+
96
+ 5. **Driver signing** — raw preimage bytes traverse `driver.sign()`, returning raw
97
+ binary output from the underlying cryptographic primitive.
98
+
99
+ 6. **Hex encoding** — `binascii.hexlify()` maps all byte values `0x00–0xFF` to
100
+ the lowercase alphanumeric characters `0–9`, `a–f`, preventing
101
+ `UnicodeDecodeError` on constrained MicroPython silicon.
102
+
103
+ 7. **Wire frame serialization** — all seven envelope fields (`v`, `n`, `t`, `q`,
104
+ `alg`, `d`, `s`) are packed into a dict and serialized with
105
+ `json.dumps(..., separators=(",", ":"), sort_keys=True, ensure_ascii=False)`,
106
+ freezing the alphabetical key sequence and enforcing raw UTF-8 wire encoding
107
+ independently of MicroPython allocator-driven insertion order.
108
+
109
+ ---
110
+
111
+ ## Quick Start
112
+
113
+ ```python
114
+ from sovereign_sensor import bootstrap_sensor_node
115
+
116
+ # Auto-selects ESP32HardwareDriver or SoftwareFallbackDriver at runtime.
117
+ # Falls back to SoftwareFallbackDriver with a warning if hardware crypto
118
+ # is not yet implemented on the target.
119
+ envelope = bootstrap_sensor_node(
120
+ node_id="node-temperature-01",
121
+ private_key_path="/flash/keys/node.key",
122
+ sequence_file="/flash/.sovereign_sequence",
123
+ )
124
+
125
+ observation = {"sensor": "temperature", "value": 21.4, "unit": "C"}
126
+ wire_bytes = envelope.seal("2026-06-16T12:00:00Z", observation)
127
+ # → b'{"alg":"hmac-sha256","d":{"sensor":"temperature","unit":"C","value":21.4},'
128
+ # '"n":"node-temperature-01","q":1,"s":"<64-char hex>","t":"2026-06-16T12:00:00Z","v":1}'
129
+ ```
130
+
131
+ ### Desktop / CI (mock key sentinel)
132
+
133
+ ```python
134
+ from sovereign_sensor import bootstrap_sensor_node
135
+ from sovereign_sensor.drivers.software_fallback import SoftwareFallbackDriver
136
+
137
+ envelope = bootstrap_sensor_node(
138
+ node_id="ci-node-001",
139
+ private_key_path=SoftwareFallbackDriver.MOCK_KEY_SENTINEL,
140
+ )
141
+ wire = envelope.seal("2026-06-16T00:00:00Z", {"ping": True})
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Invariants
147
+
148
+ | Property | Guarantee |
149
+ |---|---|
150
+ | **Replay protection** | Monotonic `q` counter persisted to VFS; resumes across reboots. |
151
+ | **Sequence atomicity** | Counter advanced only after `sign()` succeeds; a signing failure leaves the on-disk counter unchanged with no gap. |
152
+ | **Key material safety** | Missing or empty key file raises immediately; no silent substitution. |
153
+ | **Preimage determinism** | `sort_keys=True` and `ensure_ascii=False` on payload; byte-count length prefixes on all three variable-length fields. |
154
+ | **Wire frame determinism** | `sort_keys=True` and `ensure_ascii=False` on the outer frame; byte-identical UTF-8 output across CPython and MicroPython builds. |
155
+ | **Encoding safety** | `binascii.hexlify` prevents `UnicodeDecodeError` on raw binary digest bytes. |
156
+ | **Zero dependencies** | No network calls, no PyTorch, no external packages at runtime. |
@@ -0,0 +1,136 @@
1
+ # sovereign-sensor — Phase 9
2
+
3
+ A lightweight, MicroPython-compatible Hardware Abstraction Layer (HAL) that enforces
4
+ data custody at the **Point of Genesis** by sealing each sensor observation into a
5
+ versioned, tamper-evident, replay-protected JSON transmission envelope before any
6
+ network transit or cloud ingestion occurs.
7
+
8
+ Zero external dependencies. Internal library code is restricted to standard MicroPython
9
+ built-ins (`json`, `sys`, `hashlib`, `hmac`, `binascii`). Targets ESP32 and Raspberry Pi
10
+ Pico via MicroPython; fully exercisable on CPython 3.12 for desktop CI.
11
+
12
+ ---
13
+
14
+ ## Architecture
15
+
16
+ ### Hardware Abstraction Layer
17
+
18
+ `SovereignCryptoDriver` (`interface.py`) defines a three-method contract:
19
+
20
+ | Method | Contract |
21
+ |---|---|
22
+ | `initialize_hardware() -> None` | Load key material; configure accelerator subsystem. |
23
+ | `sign(payload: bytes) -> bytes` | Return raw binary signature bytes (no encoding). |
24
+ | `algorithm() -> str` | Return a canonical algorithm identifier string. |
25
+
26
+ Two concrete drivers are provided:
27
+
28
+ - **`SoftwareFallbackDriver`** — HMAC-SHA256 over a VFS-resident binary key file.
29
+ Used on all non-ESP32 targets (desktop CI, Raspberry Pi Pico, etc.).
30
+ The class-level `MOCK_KEY_SENTINEL = "/mock/test_gateway.key"` opts into a
31
+ deterministic stub key for desktop testing; every other path that cannot be opened
32
+ raises `RuntimeError` immediately, eliminating silent key substitution. A zero-byte
33
+ key file raises `ValueError` to prevent HMAC keyed with `b""`.
34
+
35
+ - **`ESP32HardwareDriver`** — Skeleton placeholder for the on-chip ECC accelerator.
36
+ `initialize_hardware()` raises `NotImplementedError` until register-level engineering
37
+ is complete; `bootstrap_sensor_node()` catches this and falls back to
38
+ `SoftwareFallbackDriver` automatically so the sealing and VFS layers remain
39
+ exercisable on the workbench.
40
+
41
+ ### Seven-Step Sealing Pipeline (`SovereignEnvelope.seal()`)
42
+
43
+ 1. **Monotonic sequence counter** — computed transiently as `_sequence + 1` and bound
44
+ into the preimage. The in-memory counter and VFS file (default `.sovereign_sequence`)
45
+ are advanced only after signing succeeds, so a `sign()` failure never consumes a
46
+ sequence position or introduces a gap in the on-disk custody timeline. A truncated
47
+ (0-byte) file resets to 0; a negative stored value is clamped to 0. VFS write
48
+ failures degrade gracefully to RAM-only tracking.
49
+
50
+ 2. **Algorithm identifier** — queried from the active driver via `algorithm()` and
51
+ embedded in the authenticated preimage, providing protocol agility without
52
+ a schema change.
53
+
54
+ 3. **Canonical payload serialization** — `json.dumps(payload, separators=(",", ":"), sort_keys=True, ensure_ascii=False)`
55
+ guarantees a byte-identical preimage for semantically equivalent payloads
56
+ regardless of key insertion order. `ensure_ascii=False` forces raw UTF-8 output
57
+ for all characters, eliminating the `\uXXXX`-vs-raw-UTF-8 split-brain divergence
58
+ that would cause cross-platform HMAC verification to fail silently on any payload
59
+ containing characters outside U+007F.
60
+
61
+ 4. **UTF-8 byte-count-prefixed preimage assembly** — `node_id`, `timestamp`, and the
62
+ `algorithm` identifier are each encoded to UTF-8 independently; the prefix for each
63
+ field is the UTF-8 byte count (not the Unicode character count). The preimage is
64
+ assembled from raw byte slices:
65
+
66
+ ```
67
+ 1|{len(node_bytes)}:{node_id}|{len(time_bytes)}:{timestamp}|{seq}|{len(algo_bytes)}:{algo}|{canonical}
68
+ ```
69
+
70
+ Byte-count prefixes close all variable-length field injection surfaces: delimiter
71
+ injection (any two inputs that differ only in where a `|` character falls produce
72
+ identical naive pipe-joined preimage bytes without prefixes) and multi-byte encoding
73
+ ambiguity (a receiver using character-count semantics parses field boundaries at the
74
+ wrong byte offset for any non-ASCII field value).
75
+
76
+ 5. **Driver signing** — raw preimage bytes traverse `driver.sign()`, returning raw
77
+ binary output from the underlying cryptographic primitive.
78
+
79
+ 6. **Hex encoding** — `binascii.hexlify()` maps all byte values `0x00–0xFF` to
80
+ the lowercase alphanumeric characters `0–9`, `a–f`, preventing
81
+ `UnicodeDecodeError` on constrained MicroPython silicon.
82
+
83
+ 7. **Wire frame serialization** — all seven envelope fields (`v`, `n`, `t`, `q`,
84
+ `alg`, `d`, `s`) are packed into a dict and serialized with
85
+ `json.dumps(..., separators=(",", ":"), sort_keys=True, ensure_ascii=False)`,
86
+ freezing the alphabetical key sequence and enforcing raw UTF-8 wire encoding
87
+ independently of MicroPython allocator-driven insertion order.
88
+
89
+ ---
90
+
91
+ ## Quick Start
92
+
93
+ ```python
94
+ from sovereign_sensor import bootstrap_sensor_node
95
+
96
+ # Auto-selects ESP32HardwareDriver or SoftwareFallbackDriver at runtime.
97
+ # Falls back to SoftwareFallbackDriver with a warning if hardware crypto
98
+ # is not yet implemented on the target.
99
+ envelope = bootstrap_sensor_node(
100
+ node_id="node-temperature-01",
101
+ private_key_path="/flash/keys/node.key",
102
+ sequence_file="/flash/.sovereign_sequence",
103
+ )
104
+
105
+ observation = {"sensor": "temperature", "value": 21.4, "unit": "C"}
106
+ wire_bytes = envelope.seal("2026-06-16T12:00:00Z", observation)
107
+ # → b'{"alg":"hmac-sha256","d":{"sensor":"temperature","unit":"C","value":21.4},'
108
+ # '"n":"node-temperature-01","q":1,"s":"<64-char hex>","t":"2026-06-16T12:00:00Z","v":1}'
109
+ ```
110
+
111
+ ### Desktop / CI (mock key sentinel)
112
+
113
+ ```python
114
+ from sovereign_sensor import bootstrap_sensor_node
115
+ from sovereign_sensor.drivers.software_fallback import SoftwareFallbackDriver
116
+
117
+ envelope = bootstrap_sensor_node(
118
+ node_id="ci-node-001",
119
+ private_key_path=SoftwareFallbackDriver.MOCK_KEY_SENTINEL,
120
+ )
121
+ wire = envelope.seal("2026-06-16T00:00:00Z", {"ping": True})
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Invariants
127
+
128
+ | Property | Guarantee |
129
+ |---|---|
130
+ | **Replay protection** | Monotonic `q` counter persisted to VFS; resumes across reboots. |
131
+ | **Sequence atomicity** | Counter advanced only after `sign()` succeeds; a signing failure leaves the on-disk counter unchanged with no gap. |
132
+ | **Key material safety** | Missing or empty key file raises immediately; no silent substitution. |
133
+ | **Preimage determinism** | `sort_keys=True` and `ensure_ascii=False` on payload; byte-count length prefixes on all three variable-length fields. |
134
+ | **Wire frame determinism** | `sort_keys=True` and `ensure_ascii=False` on the outer frame; byte-identical UTF-8 output across CPython and MicroPython builds. |
135
+ | **Encoding safety** | `binascii.hexlify` prevents `UnicodeDecodeError` on raw binary digest bytes. |
136
+ | **Zero dependencies** | No network calls, no PyTorch, no external packages at runtime. |
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sovereign-sdk-sensor"
7
+ version = "1.3.0"
8
+ description = "Bare-metal Write-Side Custody enforcement for MicroPython sensor nodes with hardware crypto abstraction"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12" # Enforces CPython 3.12+ for desktop development/CI test suites; package library code remains strictly bare-metal MicroPython compatible.
11
+ license = { text = "MIT" }
12
+ dependencies = []
13
+ classifiers = [
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: Implementation :: MicroPython",
19
+ "Intended Audience :: Developers",
20
+ "Topic :: Security",
21
+ "Topic :: System :: Hardware",
22
+ "Topic :: Software Development :: Libraries",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/kenwalger/sovereign-sdk"
27
+ Repository = "https://github.com/kenwalger/sovereign-sdk"
28
+ Changelog = "https://github.com/kenwalger/sovereign-sdk/blob/main/CHANGELOG.md"
29
+
30
+ [tool.setuptools.packages.find]
31
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: sovereign-sdk-sensor
3
+ Version: 1.3.0
4
+ Summary: Bare-metal Write-Side Custody enforcement for MicroPython sensor nodes with hardware crypto abstraction
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/kenwalger/sovereign-sdk
7
+ Project-URL: Repository, https://github.com/kenwalger/sovereign-sdk
8
+ Project-URL: Changelog, https://github.com/kenwalger/sovereign-sdk/blob/main/CHANGELOG.md
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: Implementation :: MicroPython
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Security
16
+ Classifier: Topic :: System :: Hardware
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+
21
+ # sovereign-sensor — Phase 9
22
+
23
+ A lightweight, MicroPython-compatible Hardware Abstraction Layer (HAL) that enforces
24
+ data custody at the **Point of Genesis** by sealing each sensor observation into a
25
+ versioned, tamper-evident, replay-protected JSON transmission envelope before any
26
+ network transit or cloud ingestion occurs.
27
+
28
+ Zero external dependencies. Internal library code is restricted to standard MicroPython
29
+ built-ins (`json`, `sys`, `hashlib`, `hmac`, `binascii`). Targets ESP32 and Raspberry Pi
30
+ Pico via MicroPython; fully exercisable on CPython 3.12 for desktop CI.
31
+
32
+ ---
33
+
34
+ ## Architecture
35
+
36
+ ### Hardware Abstraction Layer
37
+
38
+ `SovereignCryptoDriver` (`interface.py`) defines a three-method contract:
39
+
40
+ | Method | Contract |
41
+ |---|---|
42
+ | `initialize_hardware() -> None` | Load key material; configure accelerator subsystem. |
43
+ | `sign(payload: bytes) -> bytes` | Return raw binary signature bytes (no encoding). |
44
+ | `algorithm() -> str` | Return a canonical algorithm identifier string. |
45
+
46
+ Two concrete drivers are provided:
47
+
48
+ - **`SoftwareFallbackDriver`** — HMAC-SHA256 over a VFS-resident binary key file.
49
+ Used on all non-ESP32 targets (desktop CI, Raspberry Pi Pico, etc.).
50
+ The class-level `MOCK_KEY_SENTINEL = "/mock/test_gateway.key"` opts into a
51
+ deterministic stub key for desktop testing; every other path that cannot be opened
52
+ raises `RuntimeError` immediately, eliminating silent key substitution. A zero-byte
53
+ key file raises `ValueError` to prevent HMAC keyed with `b""`.
54
+
55
+ - **`ESP32HardwareDriver`** — Skeleton placeholder for the on-chip ECC accelerator.
56
+ `initialize_hardware()` raises `NotImplementedError` until register-level engineering
57
+ is complete; `bootstrap_sensor_node()` catches this and falls back to
58
+ `SoftwareFallbackDriver` automatically so the sealing and VFS layers remain
59
+ exercisable on the workbench.
60
+
61
+ ### Seven-Step Sealing Pipeline (`SovereignEnvelope.seal()`)
62
+
63
+ 1. **Monotonic sequence counter** — computed transiently as `_sequence + 1` and bound
64
+ into the preimage. The in-memory counter and VFS file (default `.sovereign_sequence`)
65
+ are advanced only after signing succeeds, so a `sign()` failure never consumes a
66
+ sequence position or introduces a gap in the on-disk custody timeline. A truncated
67
+ (0-byte) file resets to 0; a negative stored value is clamped to 0. VFS write
68
+ failures degrade gracefully to RAM-only tracking.
69
+
70
+ 2. **Algorithm identifier** — queried from the active driver via `algorithm()` and
71
+ embedded in the authenticated preimage, providing protocol agility without
72
+ a schema change.
73
+
74
+ 3. **Canonical payload serialization** — `json.dumps(payload, separators=(",", ":"), sort_keys=True, ensure_ascii=False)`
75
+ guarantees a byte-identical preimage for semantically equivalent payloads
76
+ regardless of key insertion order. `ensure_ascii=False` forces raw UTF-8 output
77
+ for all characters, eliminating the `\uXXXX`-vs-raw-UTF-8 split-brain divergence
78
+ that would cause cross-platform HMAC verification to fail silently on any payload
79
+ containing characters outside U+007F.
80
+
81
+ 4. **UTF-8 byte-count-prefixed preimage assembly** — `node_id`, `timestamp`, and the
82
+ `algorithm` identifier are each encoded to UTF-8 independently; the prefix for each
83
+ field is the UTF-8 byte count (not the Unicode character count). The preimage is
84
+ assembled from raw byte slices:
85
+
86
+ ```
87
+ 1|{len(node_bytes)}:{node_id}|{len(time_bytes)}:{timestamp}|{seq}|{len(algo_bytes)}:{algo}|{canonical}
88
+ ```
89
+
90
+ Byte-count prefixes close all variable-length field injection surfaces: delimiter
91
+ injection (any two inputs that differ only in where a `|` character falls produce
92
+ identical naive pipe-joined preimage bytes without prefixes) and multi-byte encoding
93
+ ambiguity (a receiver using character-count semantics parses field boundaries at the
94
+ wrong byte offset for any non-ASCII field value).
95
+
96
+ 5. **Driver signing** — raw preimage bytes traverse `driver.sign()`, returning raw
97
+ binary output from the underlying cryptographic primitive.
98
+
99
+ 6. **Hex encoding** — `binascii.hexlify()` maps all byte values `0x00–0xFF` to
100
+ the lowercase alphanumeric characters `0–9`, `a–f`, preventing
101
+ `UnicodeDecodeError` on constrained MicroPython silicon.
102
+
103
+ 7. **Wire frame serialization** — all seven envelope fields (`v`, `n`, `t`, `q`,
104
+ `alg`, `d`, `s`) are packed into a dict and serialized with
105
+ `json.dumps(..., separators=(",", ":"), sort_keys=True, ensure_ascii=False)`,
106
+ freezing the alphabetical key sequence and enforcing raw UTF-8 wire encoding
107
+ independently of MicroPython allocator-driven insertion order.
108
+
109
+ ---
110
+
111
+ ## Quick Start
112
+
113
+ ```python
114
+ from sovereign_sensor import bootstrap_sensor_node
115
+
116
+ # Auto-selects ESP32HardwareDriver or SoftwareFallbackDriver at runtime.
117
+ # Falls back to SoftwareFallbackDriver with a warning if hardware crypto
118
+ # is not yet implemented on the target.
119
+ envelope = bootstrap_sensor_node(
120
+ node_id="node-temperature-01",
121
+ private_key_path="/flash/keys/node.key",
122
+ sequence_file="/flash/.sovereign_sequence",
123
+ )
124
+
125
+ observation = {"sensor": "temperature", "value": 21.4, "unit": "C"}
126
+ wire_bytes = envelope.seal("2026-06-16T12:00:00Z", observation)
127
+ # → b'{"alg":"hmac-sha256","d":{"sensor":"temperature","unit":"C","value":21.4},'
128
+ # '"n":"node-temperature-01","q":1,"s":"<64-char hex>","t":"2026-06-16T12:00:00Z","v":1}'
129
+ ```
130
+
131
+ ### Desktop / CI (mock key sentinel)
132
+
133
+ ```python
134
+ from sovereign_sensor import bootstrap_sensor_node
135
+ from sovereign_sensor.drivers.software_fallback import SoftwareFallbackDriver
136
+
137
+ envelope = bootstrap_sensor_node(
138
+ node_id="ci-node-001",
139
+ private_key_path=SoftwareFallbackDriver.MOCK_KEY_SENTINEL,
140
+ )
141
+ wire = envelope.seal("2026-06-16T00:00:00Z", {"ping": True})
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Invariants
147
+
148
+ | Property | Guarantee |
149
+ |---|---|
150
+ | **Replay protection** | Monotonic `q` counter persisted to VFS; resumes across reboots. |
151
+ | **Sequence atomicity** | Counter advanced only after `sign()` succeeds; a signing failure leaves the on-disk counter unchanged with no gap. |
152
+ | **Key material safety** | Missing or empty key file raises immediately; no silent substitution. |
153
+ | **Preimage determinism** | `sort_keys=True` and `ensure_ascii=False` on payload; byte-count length prefixes on all three variable-length fields. |
154
+ | **Wire frame determinism** | `sort_keys=True` and `ensure_ascii=False` on the outer frame; byte-identical UTF-8 output across CPython and MicroPython builds. |
155
+ | **Encoding safety** | `binascii.hexlify` prevents `UnicodeDecodeError` on raw binary digest bytes. |
156
+ | **Zero dependencies** | No network calls, no PyTorch, no external packages at runtime. |
@@ -0,0 +1,13 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/sovereign_sdk_sensor.egg-info/PKG-INFO
4
+ src/sovereign_sdk_sensor.egg-info/SOURCES.txt
5
+ src/sovereign_sdk_sensor.egg-info/dependency_links.txt
6
+ src/sovereign_sdk_sensor.egg-info/top_level.txt
7
+ src/sovereign_sensor/__init__.py
8
+ src/sovereign_sensor/envelope.py
9
+ src/sovereign_sensor/interface.py
10
+ src/sovereign_sensor/drivers/__init__.py
11
+ src/sovereign_sensor/drivers/esp32_hardware.py
12
+ src/sovereign_sensor/drivers/software_fallback.py
13
+ tests/test_sensor.py
@@ -0,0 +1,83 @@
1
+ # packages/sovereign-sensor/src/sovereign_sensor/__init__.py
2
+ """sovereign-sensor — Bare-metal Write-Side Custody enforcement for MicroPython sensor nodes.
3
+
4
+ Provides a Hardware Abstraction Layer that selects between on-chip hardware
5
+ cryptography and pure-Python software fallbacks at runtime, then seals each
6
+ sensor observation into a tamper-evident, versioned, minified JSON transmission
7
+ envelope with monotonic replay protection that survives hardware reboots.
8
+ Zero external dependencies; targets ESP32 and Raspberry Pi Pico via MicroPython.
9
+ """
10
+ import sys
11
+
12
+ from .envelope import SovereignEnvelope
13
+ from .interface import SovereignCryptoDriver
14
+
15
+ __version__ = "1.3.0"
16
+
17
+ __all__ = [
18
+ "SovereignCryptoDriver",
19
+ "SovereignEnvelope",
20
+ "bootstrap_sensor_node",
21
+ ]
22
+
23
+
24
+ def bootstrap_sensor_node(
25
+ node_id: str,
26
+ private_key_path: str,
27
+ sequence_file: str = ".sovereign_sequence",
28
+ ) -> SovereignEnvelope:
29
+ """Instantiate and configure a platform-appropriate sensor envelope factory.
30
+
31
+ Inspects ``sys.platform`` to route between the ESP32 hardware-accelerated
32
+ driver and the pure-Python software fallback. The selected driver is
33
+ initialized (loading key material from ``private_key_path``) before the
34
+ envelope is constructed, guaranteeing the signing subsystem is ready on
35
+ the first ``seal()`` call.
36
+
37
+ If the selected driver's ``initialize_hardware()`` raises
38
+ ``NotImplementedError`` — indicating that hardware crypto acceleration is
39
+ still a skeleton placeholder — a warning is printed and the node falls back
40
+ to ``SoftwareFallbackDriver`` so that the sealing, sequencing, and VFS
41
+ serialization layers remain exercisable on the workbench before
42
+ register-level engineering is complete.
43
+
44
+ :param node_id: Immutable identifier string for this sensor node.
45
+ :type node_id: str
46
+ :param private_key_path: Filesystem path to the node's private signing key,
47
+ or ``SoftwareFallbackDriver.MOCK_KEY_SENTINEL`` for desktop testing.
48
+ :type private_key_path: str
49
+ :param sequence_file: VFS path used to persist the monotonic sequence
50
+ counter across reboots. Defaults to ``".sovereign_sequence"`` in the
51
+ current working directory.
52
+ :type sequence_file: str
53
+ :return: A fully configured ``SovereignEnvelope`` bound to the active driver.
54
+ :rtype: SovereignEnvelope
55
+ """
56
+ platform: str = sys.platform.lower()
57
+ if "esp32" in platform:
58
+ from .drivers.esp32_hardware import ESP32HardwareDriver
59
+ driver: SovereignCryptoDriver = ESP32HardwareDriver(private_key_path)
60
+ else:
61
+ from .drivers.software_fallback import SoftwareFallbackDriver
62
+ driver = SoftwareFallbackDriver(private_key_path)
63
+ _hw_not_implemented: bool = False
64
+ try:
65
+ driver.initialize_hardware()
66
+ except NotImplementedError:
67
+ _hw_not_implemented = True
68
+
69
+ if _hw_not_implemented:
70
+ # Emit the warning outside the except block so the fallback initialization
71
+ # path carries no implicit exception context. Any exception raised by the
72
+ # SoftwareFallbackDriver below will surface with a clean traceback rather
73
+ # than chaining the original NotImplementedError, preventing doubled
74
+ # tracebacks from flooding the serial monitor on constrained MicroPython targets.
75
+ print(
76
+ "WARNING: Hardware crypto accelerator is not yet implemented "
77
+ "(pending low-level register engineering in the next sprint). "
78
+ "Falling back to SoftwareFallbackDriver for this node instance."
79
+ )
80
+ from .drivers.software_fallback import SoftwareFallbackDriver
81
+ driver = SoftwareFallbackDriver(private_key_path)
82
+ driver.initialize_hardware()
83
+ return SovereignEnvelope(node_id, driver, sequence_file=sequence_file)
@@ -0,0 +1,2 @@
1
+ # packages/sovereign-sensor/src/sovereign_sensor/drivers/__init__.py
2
+ """Platform-specific cryptographic signing driver implementations."""
@@ -0,0 +1,67 @@
1
+ # packages/sovereign-sensor/src/sovereign_sensor/drivers/esp32_hardware.py
2
+ """ESP32 hardware-accelerated cryptographic signing driver.
3
+
4
+ Shell implementation targeting the ESP32's on-chip SHA and RSA/ECC
5
+ accelerator peripherals via the MicroPython ``machine`` and ``hashlib``
6
+ HAL bindings. Full low-level register engineering is deferred to the
7
+ next sprint; this module establishes the class contract and import
8
+ surface so the bootstrap router can bind it without modification.
9
+ """
10
+ from ..interface import SovereignCryptoDriver
11
+
12
+
13
+ class ESP32HardwareDriver(SovereignCryptoDriver):
14
+ """Hardware-accelerated signing driver for ESP32 targets.
15
+
16
+ On initialization, this driver will configure the on-chip crypto
17
+ accelerator and load key material from the eFuse or external flash
18
+ partition pointed to by ``private_key_path``.
19
+
20
+ :param private_key_path: Path to the private key in the ESP32 VFS
21
+ (e.g. ``/flash/keys/node.key``).
22
+ :type private_key_path: str
23
+ """
24
+
25
+ def __init__(self, private_key_path: str) -> None:
26
+ self._key_path: str = private_key_path
27
+ self._initialized: bool = False
28
+
29
+ def initialize_hardware(self) -> None:
30
+ """Configure the ESP32 hardware crypto accelerator subsystem.
31
+
32
+ :rtype: None
33
+ :raises NotImplementedError: Until low-level register engineering is complete.
34
+ """
35
+ raise NotImplementedError(
36
+ "ESP32 hardware crypto drivers are pending low-level register engineering "
37
+ "in the next sprint."
38
+ )
39
+
40
+ def algorithm(self) -> str:
41
+ """Return the canonical algorithm identifier for this driver.
42
+
43
+ Next sprint: finalize the identifier once the hardware signing
44
+ primitive (ECDSA P-256 via the ESP32 ECC accelerator) is confirmed.
45
+
46
+ :return: ``"ecdsa-p256"`` — forward-looking placeholder for the
47
+ ESP32 on-chip ECC accelerator signing primitive.
48
+ :rtype: str
49
+ """
50
+ return "ecdsa-p256"
51
+
52
+ def sign(self, payload: bytes) -> bytes:
53
+ """Produce a hardware-accelerated signature over ``payload``.
54
+
55
+ Next sprint: delegate to the ESP32 SHA/ECC hardware engine via
56
+ MicroPython ``hashlib`` acceleration bindings. Returns raw binary
57
+ signature bytes; hex encoding is the envelope layer's responsibility.
58
+
59
+ :param payload: Raw preimage bytes to authenticate.
60
+ :type payload: bytes
61
+ :return: Raw binary signature bytes (no encoding applied).
62
+ :rtype: bytes
63
+ :raises NotImplementedError: Until hardware signing is implemented.
64
+ """
65
+ raise NotImplementedError(
66
+ "ESP32 hardware crypto signing is pending next-sprint implementation."
67
+ )