Skip to content

RTOP-377: store the hardware ID at install and advertise it on /api/hardware-id - #232

Merged
thiagoralves merged 3 commits into
feature/RTOP-374-dual-licensingfrom
feature/RTOP-377-hardware-id-v2
Oct 27, 2026
Merged

thiagoralves merged 3 commits into
feature/RTOP-374-dual-licensingfrom
feature/RTOP-377-hardware-id-v2

Conversation

@thiagoralves

Copy link
Copy Markdown
Contributor

Summary

Hardware ID v2: a stable ID for the physical machine a runtime licence is bound to, so a licence can be recovered after a wipe or reinstall. Nothing is enforced on the device.

  • scripts/hardware-id.sh: the canonical bash reader (no new dependencies). hw2: + SHA-256 over per-element hashes (device-tree serial, DMI UUID and board serial with placeholder rejection, factory wired NIC MACs, stable CPU fields). No software state; NICs ignored in VMs; WSL2 and Docker Desktop refused. Frozen at v2. Spec and test vectors in docs/HARDWARE_ID.md.
  • Computed once at installation: install.sh --native and scripts/install-docker.sh store the record in the persistent data directory and never replace one.
  • On start, the runtime computes the record only when none is stored and only where it sees the host (native or the bootloader's container), never in a vPLC.
  • In a vPLC the runtime reads the read-only record orchestrator-agent binds at /etc/openplc-runtime/hardware-id.
  • GET /api/hardware-id (any authenticated user) advertises the ID, its source (device or orchestrator) and the element hashes.

The agent and autonomy-node embed this reader and compare against main, so this release must ship first.

Ticket

RTOP-377 — https://autonomylogic.atlassian.net/browse/RTOP-377

How it was tested

  • pytest: reader against every vector and rule (elements, placeholders, each NIC rule, VM, WSL2/Docker Desktop, no strong element, record format, --store under set -euo pipefail), Docker installer block parity, endpoint (401, record, non-admin user, no ID, injected record wins, ensure_stored rules). shellcheck on the reader.
  • SLM-RP4 (arm64): native install, Docker install and bootloader container all store hw2:68e4b276…a043c0; recomputed after deletion; not rewritten on restart; vPLC-style container computes nothing and advertises the injected record; endpoint 401 without a token.
  • x86 CHUWI MiniBook X (Intel N150, Fedora 44, SELinux enforcing): same checks give hw2:0a09e3a3…d544, matching an independent Python computation.
  • local-dev-toolkit end to end: a vPLC created through Edge receives the record read-only and advertises it with source orchestrator.
  • Pending: AC3 and AC5 (OS reinstall/upgrade, 32/64-bit switch, USB NIC) by the developer on the SLM-RP4.

Checklist

  • bash scripts/run-pytest.sh passes (ran the hardware ID and endpoint suites only)
  • pre-commit run clean
  • Docs updated if behavior changed (README, CLAUDE.md, docs/)
  • Follows the shared review process (summary in .claude/review-guidelines.md)

Contributor License Agreement

🤖 Generated with Claude Code

https://claude.ai/code/session_01MRWeC5FUhe5ZwEEbLUGpZ6

thiagoralves and others added 3 commits October 26, 2026 17:22
Runtime licences bind to the machine they were bought for and must be
recoverable after a reinstall, so the ID is derived only from fixed
hardware: device-tree serial, DMI UUID and board serial, factory MACs of
fixed wired NICs, and stable CPU fields. Plain bash so installers can
embed it verbatim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRWeC5FUhe5ZwEEbLUGpZ6
… /api/hardware-id

Both installers write the record once into the runtime data directory, and
a runtime installed before it existed computes it on start where it can see
the host hardware. vPLCs never compute one; they advertise the record the
orchestrator places in the container. Editors read it the same way on every
target through the authenticated endpoint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRWeC5FUhe5ZwEEbLUGpZ6
On x86 the DMI UUID and board serial are readable by root only, so an
unprivileged run derives no ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRWeC5FUhe5ZwEEbLUGpZ6
@thiagoralves
thiagoralves changed the base branch from development to feature/RTOP-374-dual-licensing October 27, 2026 20:37
@thiagoralves
thiagoralves marked this pull request as ready for review October 27, 2026 20:47
@thiagoralves
thiagoralves merged commit 237018d into feature/RTOP-374-dual-licensing Oct 27, 2026
3 of 5 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