Skip to content

Add accountless custom build fleets - #172

Merged
ODevStudio merged 23 commits into
mainfrom
odev/accountless-fleet-pairing
Sep 3, 2026
Merged

Add accountless custom build fleets#172
ODevStudio merged 23 commits into
mainfrom
odev/accountless-fleet-pairing

Conversation

@ODevStudio

@ODevStudio ODevStudio commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add accountless fleet recovery keys, one-time scale pairing, naming, and build assignment
  • persist per-scale device credentials and install assigned custom builds from the OLED menu
  • publish separately signed custom OTA manifests and binaries through the existing Worker and R2 path

Security

  • store only SHA-256 hashes of fleet and device secrets server-side
  • rate-limit pairing claims and require ready canonical combination hashes for assignments
  • serialize pairing and ownership updates in Durable Object transactions
  • keep custom OTA signing separate from official release keys and preserve signed rollback validation

Testing

  • draft PR CI: 11 checks passed
  • local Worker/configurator/custom OTA contracts passed
  • local PlatformIO native tests: 44 passed
  • local PlatformIO builds passed: esp32s3, esp32s3 buildfs, esp32s3-energy-menu
  • desktop/mobile browser checks passed with no overflow or console errors

Deployment

  • requires the HDS_CUSTOM_OTA_SIGNING_KEY_PEM repository secret before merge/deployment
  • deploy the Worker before releasing firmware with the Custom Build menu

@ODevStudio

Copy link
Copy Markdown
Collaborator Author

Blocker-fix verification at 7d556b6eb2f4509b55f40d9215bf9c65ec402a99:

  • Release and custom firmware compilation now consume a derived public-key file. HDS_CUSTOM_OTA_SIGNING_KEY_PEM remains scoped to key derivation/manifest signing and is removed from the PlatformIO subprocess environment. No private key is committed.
  • Pair creation now receives an edge-derived client key, enforces 10 new devices/client/hour plus a 500-device/hour global safety limit, and returns bounded Retry-After values.
  • Durable Object alarms now schedule and clean expired pair records, unclaimed device state, pair counters, and claim counters while preserving linked devices.
  • No fleet build-library or bulk-deployment UX was added.
  • No new external deployment prerequisite is required; the existing HDS_CUSTOM_OTA_SIGNING_KEY_PEM repository secret remains required.

PR checks: all applicable checks passed, including OTA tests on Ubuntu/macOS/Windows, both firmware environments, minimal custom firmware, and the Pressensor plugin build.

Local commands and results:

  • node --test cloudflare/custom-build-worker/test/configurator.test.mjs cloudflare/custom-build-worker/test/worker.test.mjs cloudflare/custom-build-worker/test/fleet.test.mjs - passed, 11/11.
  • node --check docs/custom-build/app.js - passed.
  • node --check docs/custom-build/fleet.js - passed.
  • python tools/test_generate_release_manifest.py - passed, 8 passed/1 skipped.
  • python tools/test_pull_ota_contract.py - passed.
  • python tools/test_release_workflow_contract.py - passed.
  • python tools/test_ota_rollback_contract.py - passed.
  • python tools/test_ota_public_key_header.py - passed, 9/9.
  • python tools/test_custom_ota_public_key_header.py - passed.
  • python tools/test_decent_ota_target_contract.py - passed.
  • python tools/test_custom_build_ota_contract.py - passed.
  • python tools/test_plugin_catalog.py - passed.
  • python tools/test_custom_cache_transport.py - passed.
  • python tools/test_plugin_ci_contract.py - passed.
  • python tools/test_ai_docs_contract.py - passed.
  • python tools/test_custom_build_execution.py - first invocation could not resolve the test's hard-coded openssl command on Windows; rerun with Git for Windows usr/bin prepended to PATH passed.
  • pio run -e esp32s3 -t clean with PLATFORMIO_CORE_DIR=.pio-core - passed.
  • pio run -e esp32s3 with a temporary derived HDS_CUSTOM_OTA_PUBLIC_KEY_FILE - the existing .pio-core remained contaminated by the earlier custom energy-menu framework install and failed on __wrap_log_printf/__atomic_fetch_add_4 linker symbols, including after a target clean.
  • pio run -e esp32s3 with the same temporary public-key input and fresh ignored PLATFORMIO_CORE_DIR=.pio.nosync/pio-core-verify - passed; image size 1,707,701 bytes.
  • pio run -e esp32s3 -t buildfs with the fresh ignored core and temporary public-key input - passed.

Temporary private-key files used by the behavioral key regression and local build were removed after use.

@ODevStudio

Copy link
Copy Markdown
Collaborator Author

Implemented the custom OTA keyring follow-up at 3ae94bd:

  • Added two independent RSA-3072 custom OTA public-key slots; firmware accepts signatures from either slot.
  • Removed the custom private signing key from release/PlatformIO compilation. Firmware compilation consumes only committed public keys.
  • Configured Key 1 as the active HDS_CUSTOM_OTA_SIGNING_KEY_PEM repository secret; Key 2 remains the offline rotation reserve.
  • Added pre-publication DER fingerprint validation requiring the signing secret to match the embedded keyring. A valid but unrelated private key now fails before ota-manifest.json or its signature is created.
  • Added regression coverage for the mismatch and aligned the PR-only temporary signing key with its temporary test keyring.
  • No private key was committed.

All 12 PR checks passed at this head, including normal firmware, Energy Menu, minimal custom firmware, Pressensor, and public-key tests on Ubuntu, macOS, and Windows. Focused local Python/Node contracts, both private/public slot matches, pio run -e esp32s3, and pio run -e esp32s3 -t buildfs also passed.

@ODevStudio
ODevStudio marked this pull request as ready for review September 3, 2026 14:26
@ODevStudio
ODevStudio merged commit 45edef5 into main Sep 3, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant