NervesGate (nerves_gate v0.1.0)

Copy Markdown

Public commands and status queries for the gateway. No client process is needed.

Network

{:ok, settings} = NervesGate.network_validate(settings)
{:ok, trial_id} = NervesGate.network_propose(settings)
:ok = NervesGate.network_confirm(trial_id)
# Or: NervesGate.network_cancel(trial_id)

Proposals acknowledge temporary application, not persistence. Unconfirmed changes roll back after 60 seconds. Only first setup through server-verified USB may auto-save, within its independent 15-second bound.

Tailscale

:ok = NervesGate.remote_connect("gateway")
{:ok, url} = NervesGate.remote_login_url()
:ok = NervesGate.remote_configure("new-name")
:ok = NervesGate.remote_disconnect()

Remote writes acknowledge acceptance, not daemon completion. Read status/1 for progress. Login URLs are private and never included in status.

Reading and observing

:ok = NervesGate.subscribe()
status = NervesGate.status()

Status reads the domains independently; it is not an atomic snapshot or cached state. Subscriptions belong to the caller. Property notifications are hints to read again; subscribe before reading and after reconnecting.

Commands return :ok, {:ok, value} or {:error, %{code: code, message: message}}. They do not bundle a status read. On an unknown outcome, read before retrying: caller timeout does not cancel admitted work.

Options:

  • :access — trusted server-observed USB access for network_propose/2 only. Never populate it from user parameters or forwarded headers.
  • :expected — the state an editor's draft started from: saved network settings for network_propose/2, or the Tailscale hostname for remote_connect/2 and remote_configure/2. The owning process rejects a stale draft before any change.
  • :network, :remote, :equipment, :indicators, :buses — domain servers for tests or independent instances. No client object or extra process is required.

Summary

Functions

Start a receive-only capture: at most five seconds, 200 CAN frames or 4096 serial bytes.

Configure an allowlisted bus port. CAN applies to the kernel; RS485 opt-in resets on restart.

Read the private distribution cookie. Never include it in status, notifications or logs.

Save a local equipment label. IDs come from status; an empty label removes it.

Run one explicit ARP-only scan of the confirmed equipment subnet (at most /24 and five seconds).

Advertise the confirmed dedicated equipment IPv4 subnet. Returns after local readback.

Withdraw the confirmed equipment subnet, preserving other routes and Tailscale preferences.

Identify with all three LEDs for five seconds, then resume current status patterns.

Send one explicitly authorized Modbus RTU read (function 3/4), with no retry or scan.

Restore the saved network settings for the matching trial.

Persist an applied trial after checking its ID, deadline and current readiness.

Apply a protected trial. Returns its ID; use that exact ID to confirm or cancel.

Validate and normalize network settings without a running gateway or any effects.

Request a Tailscale hostname change without replacing other daemon preferences.

Request Tailscale connection/enrollment; optional auth_key: is used once, never saved. Returns acceptance.

Request Tailscale down without signing out. May interrupt callers using that VPN.

Read the private HTTPS enrollment URL, or nil. Do not log or broadcast it.

Validate and persist one OpenSSH public key before allowing new SSH logins.

Persist removal by fingerprint from status.ssh.keys. Existing sessions remain open.

Read domain status, capabilities and action availability.

Commission application storage: use a card, explicitly format it, or skip.

Subscribe the calling process to domain observations; read status afterward.

Types

command_result()

@type command_result() :: :ok | {:error, error()}

error()

@type error() :: %{code: String.t(), message: String.t()}

result(value)

@type result(value) :: {:ok, value} | {:error, error()}

Functions

bus_capture(port, opts \\ [])

Start a receive-only capture: at most five seconds, 200 CAN frames or 4096 serial bytes.

bus_configure(port, params, opts \\ [])

Configure an allowlisted bus port. CAN applies to the kernel; RS485 opt-in resets on restart.

distribution_cookie()

Read the private distribution cookie. Never include it in status, notifications or logs.

equipment_label(id, label, opts \\ [])

@spec equipment_label(String.t(), String.t(), keyword()) :: command_result()

Save a local equipment label. IDs come from status; an empty label removes it.

equipment_scan(opts \\ [])

@spec equipment_scan(keyword()) :: command_result()

Run one explicit ARP-only scan of the confirmed equipment subnet (at most /24 and five seconds).

equipment_share(opts \\ [])

@spec equipment_share(keyword()) :: command_result()

Advertise the confirmed dedicated equipment IPv4 subnet. Returns after local readback.

equipment_unshare(opts \\ [])

@spec equipment_unshare(keyword()) :: command_result()

Withdraw the confirmed equipment subnet, preserving other routes and Tailscale preferences.

identify(opts \\ [])

@spec identify(keyword()) :: command_result()

Identify with all three LEDs for five seconds, then resume current status patterns.

modbus_read(port, params, opts \\ [])

Send one explicitly authorized Modbus RTU read (function 3/4), with no retry or scan.

network_cancel(trial_id, opts \\ [])

@spec network_cancel(String.t(), keyword()) :: command_result()

Restore the saved network settings for the matching trial.

network_confirm(trial_id, opts \\ [])

@spec network_confirm(String.t(), keyword()) :: command_result()

Persist an applied trial after checking its ID, deadline and current readiness.

network_propose(settings, opts \\ [])

@spec network_propose(map(), keyword()) :: result(String.t())

Apply a protected trial. Returns its ID; use that exact ID to confirm or cancel.

network_validate(settings)

@spec network_validate(map()) :: result(map())

Validate and normalize network settings without a running gateway or any effects.

remote_configure(hostname, opts \\ [])

@spec remote_configure(String.t(), keyword()) :: command_result()

Request a Tailscale hostname change without replacing other daemon preferences.

remote_connect(hostname, opts \\ [])

@spec remote_connect(String.t(), keyword()) :: command_result()

Request Tailscale connection/enrollment; optional auth_key: is used once, never saved. Returns acceptance.

remote_disconnect(opts \\ [])

@spec remote_disconnect(keyword()) :: command_result()

Request Tailscale down without signing out. May interrupt callers using that VPN.

remote_login_url(opts \\ [])

@spec remote_login_url(keyword()) :: result(String.t() | nil)

Read the private HTTPS enrollment URL, or nil. Do not log or broadcast it.

ssh_add_key(key)

@spec ssh_add_key(String.t()) :: command_result()

Validate and persist one OpenSSH public key before allowing new SSH logins.

ssh_remove_key(fingerprint)

@spec ssh_remove_key(String.t()) :: command_result()

Persist removal by fingerprint from status.ssh.keys. Existing sessions remain open.

status(opts \\ [])

@spec status(keyword()) :: map()

Read domain status, capabilities and action availability.

storage_setup(action, card_id \\ nil, confirmation \\ nil)

Commission application storage: use a card, explicitly format it, or skip.

subscribe(opts \\ [])

@spec subscribe(keyword()) :: command_result()

Subscribe the calling process to domain observations; read status afterward.

target()

version()