Skip to content

OpenWrt Deployment

iliyan85 edited this page Aug 23, 2026 · 3 revisions

OpenWrt Deployment

OpenWrt 25.12 is a production-oriented edge deployment environment for AISMixer and nmea_sproxy. The signed packages run the current Python implementation as OpenWrt services managed by procd. This page covers repository trust, architecture selection, storage planning, installation, service lifecycle, endpoint identities, UDPSEC trust, and physical serial AIS ingress.

Supported scope

The currently published package repositories are:

OpenWrt version Package architecture Repository index
OpenWrt 25.12 x86_64 https://aismixer.net/openwrt/25.12/x86_64/packages.adb
OpenWrt 25.12 mips_24kc https://aismixer.net/openwrt/25.12/mips_24kc/packages.adb

Both architectures use the same AISMixer repository signing key. Select only the repository whose package architecture matches the official OpenWrt feeds configured on the device.

Item Current published support
Repository format Signed APK v3 repository
Published package version 0.2.0-r2 for all three packages
Runtime implementation Python
Service manager procd through /etc/init.d scripts
Distribution Signed public repository at aismixer.net

The current packages do not provide a native C runtime, UCI integration, or OpenWrt multi-instance services. See Current package boundaries and planned work for that implemented/planned boundary.

Select the package architecture

Some tools report a broad machine architecture rather than the architecture used in package-feed paths. For example:

apk --print-arch

may report mips, while the official OpenWrt package feeds configured for the device use mips_24kc. Do not guess the AISMixer repository architecture from apk --print-arch alone. Match the architecture segment used by the official OpenWrt package feeds.

On OpenWrt 25.12, inspect the configured distribution feeds, for example:

grep '/packages/' /etc/apk/repositories.d/distfeeds.list

If that filter does not show enough context, inspect the file directly:

cat /etc/apk/repositories.d/distfeeds.list

/etc/apk/repositories.d/distfeeds.list is the current OpenWrt 25.12 layout; future OpenWrt releases may organize repository configuration differently.

Plan writable storage

The current OpenWrt packages use the Python implementation and dependencies that include cryptographic and serial libraries. Their installed footprint therefore requires materially more writable space than a minimal OpenWrt installation. Check the active root filesystem before installation:

df -h /

Package installation writes into the active OpenWrt overlay. If internal writable storage is insufficient, extroot is a standard OpenWrt deployment option for placing that overlay on external storage. Extroot is not mandatory when the device already has sufficient writable space. When services depend on external storage, choose storage that is stable and available during boot.

Package model

The repository contains three packages:

Package Purpose Direct shared dependency
aismixer-common Shared Python runtime modules, key tooling, and common utilities Python 3
aismixer Mixer/router service, UDPSEC server support, and aismixerctl aismixer-common
nmea_sproxy One station-side UDP or serial input to one UDPSEC or explicit plain-UDP output aismixer-common

aismixer and nmea_sproxy may be installed independently. APK resolves aismixer-common and the other required Python dependencies automatically; operators normally do not install aismixer-common directly.

The package recipe installs these important paths.

AISMixer

/usr/bin/aismixer
/usr/bin/aismixerctl
/usr/lib/aismixer/...
/etc/init.d/aismixer
/etc/aismixer/config.yaml
/etc/aismixer/udp_alias_map.yaml
/etc/aismixer/authorized_keys.yaml
/etc/aismixer/keys/

nmea_sproxy

/usr/bin/nmea_sproxy
/usr/lib/aismixer/nmea_sproxy/...
/etc/init.d/nmea_sproxy
/etc/nmea_sproxy/config.yaml
/etc/nmea_sproxy/keys/

Shared modules are installed below /usr/lib/aismixer/core/; the shared key tool is /usr/lib/aismixer/tools/aismixer_keys.py.

Signed repository and trust bootstrap

The repository trust material is:

The x86_64 and mips_24kc indexes listed above are both signed with this key.

Run the following as root. It adds the AISMixer feed without replacing any operator-defined custom repositories. Set AISMIXER_ARCH only after matching it to the architecture used by the configured official feeds:

AISMIXER_ARCH='x86_64'
# or:
# AISMIXER_ARCH='mips_24kc'

wget -O /etc/apk/keys/aismixer-openwrt.pem \
  https://aismixer.net/openwrt/keys/aismixer-openwrt.pem

chmod 0644 /etc/apk/keys/aismixer-openwrt.pem

REPO_FILE=/etc/apk/repositories.d/customfeeds.list
REPO_URL="https://aismixer.net/openwrt/25.12/${AISMIXER_ARCH}/packages.adb"

grep -qxF "$REPO_URL" "$REPO_FILE" 2>/dev/null || {
  printf '\n# AISMixer OpenWrt 25.12 %s repository\n%s\n' \
    "$AISMIXER_ARCH" "$REPO_URL" >> "$REPO_FILE"
}

apk update
apk add aismixer

To compare the downloaded key with the published value, run:

sha256sum /etc/apk/keys/aismixer-openwrt.pem

/etc/apk/keys/ is the package-manager trust store used here. Once this public key is trusted, APK signatures authenticate the repository and package artifacts. Downloading the key and checking its fingerprint from the same HTTPS origin is a useful identity and integrity check, but it is not an independent second trust channel or stronger authentication than the HTTPS bootstrap itself.

Install the station-side proxy package with:

apk add nmea_sproxy

Do not use --allow-untrusted, and do not perform a blanket apk upgrade as part of AISMixer installation.

Configuration and preserved operator state

The OpenWrt configuration and identity layout is:

Service Path Package treatment
AISMixer /etc/aismixer/config.yaml Declared conffile
AISMixer /etc/aismixer/udp_alias_map.yaml Declared conffile
AISMixer /etc/aismixer/authorized_keys.yaml Declared conffile
AISMixer /etc/aismixer/keys/ Empty directory installed; identity generated locally; no private key packaged
nmea_sproxy /etc/nmea_sproxy/config.yaml Declared conffile
nmea_sproxy /etc/nmea_sproxy/keys/ Empty directory installed; identity and trust material generated or provisioned locally

The declared conffiles are operator-managed configuration and are intended to survive package upgrades. Endpoint private keys and trusted peer material are created or provisioned locally and must not be replaced merely because a package is upgraded.

Use the packaged files as starting points rather than copying large examples from this Wiki:

Service lifecycle with procd

OpenWrt does not use the systemd installation path documented for conventional Linux. The current packages install these procd services:

/etc/init.d/aismixer
/etc/init.d/nmea_sproxy

OpenWrt's package post-install handling enables and attempts to start each installed service. Consequently, apk add aismixer normally prepares the mixer identity and launches AISMixer. With the packaged UDPSEC proxy configuration, apk add nmea_sproxy prepares the station identity but safely leaves the runtime stopped when its required peer key has not been provisioned; the package installation itself still succeeds.

Enable and manage AISMixer with:

/etc/init.d/aismixer enable
/etc/init.d/aismixer start
/etc/init.d/aismixer status
/etc/init.d/aismixer restart

Enable and manage the station proxy with:

/etc/init.d/nmea_sproxy enable
/etc/init.d/nmea_sproxy start
/etc/init.d/nmea_sproxy status
/etc/init.d/nmea_sproxy restart

Both init scripts send runtime standard output and error to OpenWrt logging and register a procd respawn policy after successful startup preparation.

AISMixer startup

Before registering the runtime with procd, the AISMixer init script:

  1. creates /etc/aismixer/keys/ and /run/aismixer;
  2. creates the local server identity when both identity files are absent, or repairs the derived public key from an existing private key; and
  3. refuses an incomplete public-only identity instead of replacing it with a new private identity.

The packaged default configuration enables the local control socket at /run/aismixer/control.sock, and the operator command is /usr/bin/aismixerctl. See Runtime Routing Control for control semantics and authorization boundaries.

nmea_sproxy startup

The current OpenWrt package provides one singleton nmea_sproxy service using /etc/nmea_sproxy/config.yaml. It does not provide the conventional-Linux nmea_sproxy@name systemd template model.

Before registering the runtime with procd, its init script:

  1. verifies that the configuration file exists;
  2. creates or repairs the local station identity without replacing an existing private key;
  3. loads and validates the runtime configuration; and
  4. for UDPSEC output, verifies that the configured peer public key exists, is readable, and is a valid public key.

Only a runtime that passes this preparation is registered with procd. Genuine runtime failures after launch may therefore use procd respawn; a missing UDPSEC trust anchor is handled before launch.

Identity generation and key ownership

Two unrelated trust domains are involved:

  1. APK repository signing identity authenticates repository and package artifacts to APK after the repository public key is trusted. Its private key is not shipped to OpenWrt hosts.
  2. AISMixer/UDPSEC endpoint identities authenticate the mixer and station to each other. Their private keys are generated locally on the OpenWrt host.

The exact endpoint identity files are:

Endpoint Private identity Derived public identity
AISMixer /etc/aismixer/keys/aismixer_private.pem /etc/aismixer/keys/aismixer_public.pem
nmea_sproxy station /etc/nmea_sproxy/keys/station_private.pem /etc/nmea_sproxy/keys/station_public.pem

Private key files remain local. When a valid private key exists, the init scripts preserve it and use the key tool to repair a missing, invalid, or mismatched derived public key. If only the public identity exists, startup refuses to generate a replacement private identity. This prevents an incomplete state from silently changing endpoint identity.

The current init scripts do not select an alternate procd user, so they run in OpenWrt's default root service context. They set each endpoint key directory to mode 0700; the key tool sets private keys to 0600 and derived public keys to 0644.

Provision UDPSEC peer trust

Local identity generation does not establish peer trust. For one nmea_sproxy to AISMixer UDPSEC relation, provision both directions through a trusted operator-controlled channel.

1. Trust AISMixer on the station

Copy only the mixer public identity from:

/etc/aismixer/keys/aismixer_public.pem

to the path configured as remote_public_key on the station. The packaged default is:

/etc/nmea_sproxy/keys/aismixer_public.pem

Never copy the AISMixer private identity to the station.

2. Authorize the station on AISMixer

The first nmea_sproxy start prepares the station identity before checking for the UDPSEC peer key. If that peer key is not provisioned yet, the new station identity remains in place while the runtime safely stays stopped. After the station private identity exists, the OpenWrt-installed key tool can repair its public key if needed and print the compressed base64 value required by AISMixer:

/usr/bin/python3 /usr/lib/aismixer/tools/aismixer_keys.py station \
  --keys-dir /etc/nmea_sproxy/keys \
  --station-id boat_001 \
  --repair-public

Replace boat_001 with the station_id in /etc/nmea_sproxy/config.yaml. Add the printed value to /etc/aismixer/authorized_keys.yaml:

authorized_clients:
  - name: boat_001
    pubkey: <compressed-public-key-base64>

name must exactly match the proxy's configured station_id. Restart AISMixer after changing its authorization file, then start or restart nmea_sproxy:

/etc/init.d/aismixer restart
/etc/init.d/nmea_sproxy restart

Trust material is never fetched, enrolled, or exchanged automatically. See the canonical nmea_sproxy key and trust guide for the full operator workflow.

Expected safe UDPSEC preflight

When UDPSEC output is configured and remote_public_key is missing or cannot be read, the init script logs:

UDPSEC peer public key not provisioned; service not started: <path>

When the file is invalid or cannot be loaded as a public key, it logs:

UDPSEC peer public key invalid or unreadable; service not started: <path>

The runtime is intentionally not launched in either case. This prevents procd from repeatedly respawning a process that cannot establish trust. Package installation may therefore be successful while the proxy runtime is correctly stopped and waiting for peer trust. Provision the AISMixer public key, then start or restart the service.

An explicit plain-UDP output does not require the UDPSEC peer public key. The OpenWrt init script still prepares the local station identity before its general runtime-readiness check, but the plain-UDP runtime does not load or use UDPSEC peer trust.

Input, output, and routing terminology

nmea_sproxy accepts two first-class input types:

  • UDP input, either through an explicit input.type: udp mapping or the backward-compatible top-level form where input: is omitted; and
  • serial or USB virtual serial input through input.type: serial.

UDP input is not deprecated and is not a legacy input type. Only its omitted input: top-level configuration form is described as backward-compatible.

The output types are:

  • UDPSEC, through explicit output.type: udpsec or the legacy, backward-compatible top-level remote_host / remote_port form; and
  • explicit plain UDP through output.type: udp, for a trusted LAN, VPN, or equivalent external security boundary.

Each proxy process represents one local input to one network output. See Secure UDP and nmea_sproxy for configuration and transport details.

AISMixer's original no-routing behavior remains legacy broadcast mode. It is supported and backward-compatible, not deprecated. Routing mode instead uses named targets and target-scoped deduplication. See Routing Engine; OpenWrt packaging does not change those semantics.

Physical serial input

nmea_sproxy supports physical serial input, including USB virtual serial devices exposed by the operating system. It consumes a serial device path through pySerial; it does not implement USB device drivers.

Common device classes are:

Connection Typical serial device Driver boundary
Native UART /dev/ttyS* or another platform-specific TTY path Platform UART support
USB CDC ACM /dev/ttyACM* CDC ACM kernel support
USB-UART adapter such as FTDI, CP210x, or CH34x /dev/ttyUSB* Chipset-appropriate USB-serial kernel support

Use the actual device path discovered on the system. /dev/ttyACM0 in this example is not a universal or persistent path:

input:
  type: serial
  port: /dev/ttyACM0
  baudrate: 38400
  bytesize: 8
  parity: N
  stopbits: 1
  read_timeout: 1.0
  reconnect_delay: 5
  max_line_bytes: 4096

Kernel-driver handling

The driver required for a serial endpoint may be built into the OpenWrt kernel, already installed as a kernel module, or available through the matching OpenWrt kmod package. A device with USB ports does not necessarily include CDC ACM support in its installed image.

For a CDC ACM receiver, check whether the package is installed:

apk info -e kmod-usb-acm

If CDC ACM support is needed, is not already built in or installed, and the matching OpenWrt feed provides it, install:

apk add kmod-usb-acm

When the matching driver is available, OpenWrt hotplug and kmodloader normally handle module loading and device binding automatically. Manual boot-time modprobe commands are not part of the normal setup.

kmod-usb-acm is conditional hardware support, not a mandatory nmea_sproxy dependency. UDP input and native UART input do not require it. USB-UART devices using FTDI, CP210x, CH34x, or another chipset require their matching OpenWrt driver when that support is not already present.

Serial-device names

/dev/ttyACM0 or /dev/ttyUSB0 is the serial character device presented through the Linux TTY layer. /dev/serial/by-id/... is a persistent userspace-created alias commonly available on full Linux distributions, but OpenWrt does not guarantee that this alias exists. Inspect what the kernel and hotplug system actually created:

dmesg | tail -n 30
logread | tail -n 30
ls -l /dev/ttyACM* /dev/ttyUSB* 2>/dev/null

Numbering such as ttyACM0 versus ttyACM1, or ttyUSB0 versus ttyUSB1, is not inherently persistent when several similar devices are attached. Confirm the mapping after hardware or boot-order changes.

Optional raw serial verification

Because pySerial is already part of the nmea_sproxy serial deployment, it is the preferred bounded raw-input check. Substitute the actual device path:

python3 - <<'PY'
import serial

ser = serial.Serial(
    port="/dev/ttyACM0",
    baudrate=38400,
    bytesize=8,
    parity="N",
    stopbits=1,
    timeout=2.0,
)

for _ in range(20):
    line = ser.readline()
    if line:
        print(line.decode("ascii", errors="replace").rstrip())

ser.close()
PY

/dev/ttyACM0 is only an example. Use the device path discovered on the system. 38400 8N1 (38400 baud, 8 data bits, no parity, and one stop bit) is a common AIS/NMEA serial configuration, but the receiver's own configuration is authoritative. Seeing !AIVDM or !AIVDO data verifies the receiver, TTY, and pySerial path independently of nmea_sproxy.

OpenWrt images may omit stty. For optional serial diagnostics, install:

apk add coreutils-stty

Then inspect the endpoint, for example:

stty -F /dev/ttyACM0 -a

coreutils-stty is optional diagnostic tooling, not a runtime dependency. pySerial configures the endpoint used by nmea_sproxy.

Non-TTY USB devices

If a physical receiver uses a vendor-specific USB protocol and the operating system does not expose it as a serial TTY device, the current nmea_sproxy pySerial input cannot consume that device directly. Such hardware requires either an appropriate operating-system or kernel driver that exposes a serial endpoint, or a future separate input backend.

Startup on resource-constrained targets

On resource-constrained OpenWrt systems, cold-starting Python, loading modules from flash or extroot, and importing cryptographic and parser dependencies may make aismixer, nmea_sproxy, or aismixerctl take a few seconds to begin execution. This is startup and import latency. Long-running daemon services do not pay that startup cost for each AIS packet, and page cache may make later launches faster. This observation is not a throughput or capacity benchmark.

OpenWrt 25.12 mips_24kc validation

The published OpenWrt 25.12 mips_24kc package path has been functionally and operationally validated end to end with:

physical AIS serial source
  -> nmea_sproxy
  -> authenticated mutual-ECDHE UDPSEC
  -> AISMixer UDPSEC ingress
  -> processing
  -> egress
  -> aismixerctl runtime control and statistics
  -> procd service operation

The validation also demonstrated bounded ingress, processing, and egress operation without observed processing failures or queue backpressure during the validation run. This was functional and operational validation, not a controlled throughput or capacity benchmark, and it does not guarantee performance for every mips_24kc system.

Compact operational verification

The following commands answer the usual post-install questions:

apk list -I | grep -E '^(aismixer|nmea_sproxy)-'
/etc/init.d/aismixer status
/etc/init.d/nmea_sproxy status
logread -e aismixer
logread -e nmea_sproxy
ls -l /run/aismixer/control.sock
aismixerctl status

Interpret them as follows:

  • apk list -I shows which AISMixer packages are installed;
  • the init scripts report current procd service state;
  • logread shows startup, identity, preflight, and runtime messages;
  • the socket listing confirms whether the configured local control socket exists; and
  • aismixerctl status confirms that the enabled control socket responds.

If the station log contains one of the safe-preflight messages above, complete peer-trust provisioning and start the service again. A disabled control socket is a valid operator configuration; in that case, the socket and aismixerctl status checks are not expected to succeed.

Targeted package updates

Leave the repository URL in customfeeds.list, refresh package metadata, and target only the installed AISMixer package or packages:

apk update
apk add --upgrade aismixer
apk add --upgrade nmea_sproxy

Run only the package commands relevant to the OpenWrt host. APK resolves updates to aismixer-common as a dependency. For a UDPSEC relation, keep both endpoints on compatible runtime versions and restart the affected services after the targeted package update.

Declared conffiles are intended to retain operator configuration. Endpoint identity files are locally generated rather than package payload and must not be replaced during an upgrade. Review package-manager messages and the effective configuration before restarting. Do not use a global apk upgrade as an AISMixer update procedure.

Current package boundaries and planned work

The current packages provide:

  • signed OpenWrt APK packages;
  • procd lifecycle integration;
  • the local configuration and key layout documented above;
  • independent installation of AISMixer and nmea_sproxy;
  • one singleton OpenWrt nmea_sproxy service; and
  • safe UDPSEC startup preflight before the runtime is launched.

Future work may introduce UCI integration, named multiple nmea_sproxy procd instances, and OpenWrt-oriented configuration integration. No such behavior, syntax, or service model is implemented or promised. The conventional-Linux nmea_sproxy@name systemd model will not simply be copied to OpenWrt; any multi-instance design will require a separately defined OpenWrt-specific model.

Further documentation

Clone this wiki locally