NervesHub Documentation

repository·main·Indexed 42 days ago

https://github.com/nerves-hub/nerves_hub_web

An open-source, scalable platform for resilient firmware updates and device management designed for Nerves-based hardware fleets. Features include signed firmware delivery, hardware certificate authentication, remote IEx terminals, and an OpenAPI-documented API. Supports v2.x migration, experimental AtomVM packbeam archives via the nh-avm tool, and optimized cross-node messaging using Phoenix.PubSub and the group library.

Tokens
25.6K
Snippets
71
Records
120
Agent score
52%

What's inside NervesHub

  1. Overview of NervesHub

    main

    NervesHub is an open-source, resilient firmware update and device management platform designed to scale with large fleets of devices. It provides features such as:

    • Delivery of signed firmware to authenticated Nerves devices.
    • Hardware device certificate authentication and shared secret authentication.
    • Remote IEx terminal for debugging and device recovery.
    • A comprehensive OpenAPI-documented API.
    • Self-hostable architecture with minimal infrastructure requirements.
  2. Understand NervesHub runtime configuration

    main

    NervesHub uses environment variables defined in config/runtime.exs to configure the application at boot time. This allows you to change settings by restarting a node without rebuilding the application image.

    Key Conventions:

    • Prod only: Variables marked as such are only read when MIX_ENV=prod. In dev or test, settings are pulled from config/dev.exs or config/test.exs instead.
    • Required: The node will refuse to boot if these variables are missing.
    • Booleans: Must be the exact string "true" to be true. Values like "1" or "TRUE" are treated as false.
    • Integers: Must be strictly numeric; non-numeric values will cause a boot crash.
  3. Understand the Device Error Reports extension

    main

    The error_reports extension allows devices to send structured exceptions, exits, and explicit error reports to NervesHub. Unlike the logging extension which sends raw text, error reports are structured (kind, reason, frames) to allow the server to group identical bugs into single issues.

    Key characteristics:

    • Off by default: Must be enabled per product.
    • Unidirectional: Data flows from device -> server via the error_reports:report frame. The server does not poll or acknowledge reports.
    • Batch-oriented: To avoid rate-limiting during crash loops (e.g., supervisor restart storms), clients should send reports in batches. A single message can contain up to 25 reports and costs the same as a single report in terms of rate-limiting tokens.
  4. Implement the Device Metrics protocol frames

    main

    The metrics extension uses specific frames for communication between the server and the device. Once the extension is negotiated, the following frames are used:

    1. metrics:check (Server $\rightarrow$ Device): A request from the platform asking the device to report its metrics. The platform triggers this every 15 minutes when idle, or every 1 minute when a user is viewing the device page.
    2. metrics:report (Device $\rightarrow$ Server): The device sends a JSON payload containing a batch of reports.

    Note: Devices can also report metrics spontaneously without a check frame, subject to rate limits.

    server -> device   metrics:check    {}
    device -> server   metrics:report   {"reports": [{..}, {..}]}
  5. Manage error issue lifecycles

    main

    Issues in NervesHub follow three states:

    • Unresolved: The default state for new issues.
    • Resolved: Mark an issue as resolved once a fix has been shipped. If the error occurs again, the issue is automatically reopened and marked as regressed.
    • Muted: Use this when you are aware of an issue but do not want it appearing in your active queue. Muted issues continue to count occurrences but do not reopen.
  6. Understand Device Update Modes

    main

    Every device operates in one of three update_mode states, which determines how it receives firmware. The deployment group always designates the target firmware; the mode only dictates when the device receives it.

    ModeOrchestrator pushesDevice may pullManual push
    automaticyes, on the deployment's scheduleyes, if eligibleyes
    device_managedneveryes, if eligibleyes
    offneverneveryes

    Key Behaviors:

    • Manual Pushes: A manual push (the "remote fix-it button") always succeeds. For automatic devices, a manual push pauses automatic updates to prevent the deployment from immediately overwriting the manual fix.
    • Eligibility: Pulling is gated by deployment eligibility (e.g., staged rollouts/waves), not by the mode. A device in a rollout wave that hasn't been reached yet will be refused regardless of its mode.
  7. How the Extensions Protocol handshake works

    main

    The Extensions Protocol is a negotiation mechanism used to establish non-firmware communication channels (e.g., health metrics, logs, shell) between the NervesHub platform and a device. Extensions are negotiated rather than assumed to ensure that traffic never interferes with firmware updates and that both sides agree on the capabilities being used.

    The handshake follows a strict four-frame sequence:

    1. Platform Advertisement (extensions:get): The platform sends a list of all available extensions and their versions (newest first) to the device. This is only sent to devices with device_api_version >= 2.2.0.
    2. Device Join (phx_join "extensions"): The device responds by joining the extensions topic, declaring exactly one version per extension it is capable of serving.
    3. Platform Attach List (phx_reply): The platform replies with a list of keys (extension names) that it has authorized for this specific device. An extension might be implemented by the platform but disabled for a specific product or device.
    4. Device Confirmation (<key>:attached): The device sends an attachment confirmation for each authorized key. Only after this step does the platform begin requesting data via <key>:<event> scoped messages.

    Important Constraints:

    • A client must not join the extensions topic before receiving frame 1.
    • If an extension is omitted from the platform's attach list (frame 3), the device should treat this as a local failure/unsupported feature.
    1. server -> device   extensions:get        {"extensions": {"logging": ["0.1.0", "0.0.1"], ..}}
    2. device -> server   phx_join "extensions" {"logging": "0.1.0", "health": "0.0.1"}
    3. server -> device   phx_reply             ["logging", "health"]
    4. device -> server   logging:attached      {}
  8. How NervesHub device authentication works

    main

    NervesHub uses X.509 certificate-based authentication over TLS. Instead of traditional passwords, authentication relies on a device proving possession of a private key corresponding to a certificate signed by a registered signer certificate (also known as a Certificate Authority or CA certificate).

    Key Concepts

    • Device Certificate: An X.509 certificate presented by the device during the TLS handshake.
    • Signer Certificate (CA): A self-signed certificate provided by a NervesHub user within an organization. This certificate is used to sign device certificates. The private key for this certificate must be kept secret.
    • Certificate Pinning: NervesHub pins device certificates to simplify authentication and detect unusual events.
    • Fingerprints: NervesHub uses two types of fingerprints for identification:
      • Certificate Fingerprint: A SHA-1 hash of the device certificate in DER form.
      • Public Key Fingerprint: A SHA-1 hash of the public key in DER form.

    Authentication Logic Flow

    1. Existing Certificate: If the certificate fingerprint is recognized and valid (or if expired certificates are enabled), the device is allowed.
    2. Certificate Rotation: If the certificate fingerprint is unknown but the public key fingerprint matches an existing record, NervesHub checks if the Common Name (CN) matches the Device ID and verifies the signing authority. If valid, it records the new certificate fingerprint.
    3. New Device/Certificate: If both fingerprints are unknown, NervesHub validates the certificate chain against registered signing certificates, verifies the Common Name as the Device ID, and ensures the device is allowed to change its public key before recording the new fingerprints.
  9. How cross-node messaging works in NervesHub

    main

    NervesHub uses two different transport mechanisms for moving messages between nodes, chosen based on the expected traffic and membership patterns:

    1. Phoenix.PubSub (the pg adapter): Used for high-density fan-out or where membership is constant (e.g., a device's own connection channel). It broadcasts to every node, which then filters locally. This is efficient for topics where many nodes are subscribers, but expensive for per-device topics with no active watchers because every device event is shipped to all nodes and discarded.

    2. group library: Used for sparse membership and high traffic. It delivers messages only to nodes that have explicitly joined a key. This is ideal for per-device topics (like heartbeats or logs) where consumers (like a LiveView) only exist temporarily. It avoids unnecessary cross-node traffic for quiet devices, but carries a cost in membership replication (joins/leaves replicate to all nodes).

    Decision Rule

    A topic should use :group when its membership is sparse (consumers exist only while someone is watching) and its traffic is high (constant publishing).

  10. Understand the Device Metrics extension

    main

    The metrics extension is used to transmit numerical device measurements (e.g., CPU temperature, memory usage, load average) to NervesHub.

    Key characteristics:

    • Opt-in: It is disabled by default and must be enabled per product.
    • Decoupled from Health: Unlike the health extension, which bundles metrics with metadata and alarms, metrics is designed for high-frequency reporting without the overhead of re-sending static device metadata.
    • Batching: Devices can send multiple readings in a single message (up to 60 reports) to handle high-frequency sampling or connection outages.
    • Storage: The 'latest set' of metrics is stored in PostgreSQL (for filters and queries), while historical data is stored in ClickHouse (for charts) and expires after 30 days.
  11. Manage Update Capabilities with the Update Grant

    main

    While update_mode defines the current state, managed_updates_allowed is a capability (a "grant") that determines if a device is permitted to switch itself into device_managed mode.

    • Default State: managed_updates_allowed defaults to false. A device must be opted into self-management.
    • Operator Control: Operators can change both update_mode and managed_updates_allowed via the dashboard or API at any time. The grant only restricts what the device can do to itself, not what a human operator can do.
    • Persistence: Revoking the grant does not automatically move a device out of device_managed mode; moving a fleet back into a rollout must be done explicitly by an operator.
  12. AtomVM Support Limitations

    main

    Users should be aware of the following limitations regarding AtomVM support in NervesHub:

    • No Delta Updates: Delta updates are not supported and cannot be generated for AtomVM.
    • Server-Side Only: NervesHub handles ingestion, storage, and offering of packbeams. The device-side agent responsible for writing the archive to an inactive partition and rebooting is not part of this service.
    • No VM/Bootloader Updates: A packbeam update only replaces the application. It does not update the AtomVM runtime or the underlying bootloader; those must be handled via separate artifacts and paths.