From c98e77e1bb76806ae460c7d59829433588f15c8b Mon Sep 17 00:00:00 2001 From: Jake Thomas Date: Sat, 25 Jul 2026 11:29:47 -0400 Subject: [PATCH 1/2] Improve installation documentation --- README.md | 80 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 7b8a4f6..14d9bd1 100644 --- a/README.md +++ b/README.md @@ -3,62 +3,94 @@ [![CI](https://github.com/open-ships/n2k-cli/actions/workflows/test.yaml/badge.svg)](https://github.com/open-ships/n2k-cli/actions/workflows/test.yaml) [![Release](https://img.shields.io/github/v/release/open-ships/n2k-cli)](https://github.com/open-ships/n2k-cli/releases) -`n2k-cli` provides the `n2k` command-line interface for NMEA 2000 (N2K), the -CAN-based network that connects marine instruments such as GPS, depth, wind, -engine, and autopilot systems. It decodes, records, replays, validates, and -inspects traffic from CAN hardware, USB-CAN adapters, WiFi gateways, and -capture files. +Rich terminal tools for inspecting NMEA 2000 networks. -Run `n2k` without arguments for a searchable Bubble Tea command center with -guided, autocomplete-enabled workflows. Every operation also remains available -as a stable, non-interactive subcommand for scripts and JSON pipelines. +`n2k` decodes, records, replays, validates, and inspects NMEA 2000 traffic from +SocketCAN interfaces, USB-CAN adapters, network gateways, and capture files. +Run it without arguments for a searchable Bubble Tea command center, or use its +deterministic subcommands in scripts and JSON pipelines. + +- Guided interactive workflows with fuzzy search and autocomplete. +- Typed PGN decoding with physical values, units, and exact wire data. +- Live SocketCAN, USB-CAN, TCP, and UDP sources. +- Replayable candump and owned-observation recording. +- CEL filtering, schema exploration, device inventory, and validation. +- Install-aware updates through Homebrew, Go, or verified release binaries. The CLI is powered by the [`open-ships/n2k`](https://github.com/open-ships/n2k) Go library. ![n2k sniff decoding NMEA 2000 PGNs as typed JSON lines with exact wire values](.github/demo.svg) -## Quick Start — No Boat Required +## Install -The repository bundles a real six-second capture from a sailing vessel, so the -first run works at a desk: +Choose either installation path. Both provide the same `n2k` command, and the +built-in updater remembers how the binary was installed. -```bash -git clone https://github.com/open-ships/n2k-cli && cd n2k-cli -go run ./cmd/n2k sniff --file testdata/sample.log | jq . -``` +### Homebrew -Frames carrying vessel position, routes, or identity were removed from the -sample for privacy. +Homebrew is the simplest option on macOS and Linux: -## Install +```bash +brew install --cask open-ships/tap/n2k +n2k version +``` -Install the latest release with Homebrew: +Homebrew upgrades are also available directly: ```bash -brew install --cask open-ships/tap/n2k +brew upgrade --cask open-ships/tap/n2k ``` -Or install it directly with Go: +### Go + +With Go 1.25.8 or newer installed: ```bash go install github.com/open-ships/n2k-cli/cmd/n2k@latest +n2k version ``` Go writes the binary to `GOBIN`, which defaults to `$(go env GOPATH)/bin`; -that directory must be on `PATH`. +make sure that directory is on `PATH`: + +```bash +export PATH="$(go env GOPATH)/bin:$PATH" +``` + +If you set a custom `GOBIN`, `n2k update` preserves it and replaces that same +installation. -Prebuilt binaries for Linux, macOS, and Windows are available on the +### Release binaries + +Checksum-protected archives for macOS, Linux, and Windows are published on the [`n2k-cli` releases page](https://github.com/open-ships/n2k-cli/releases). -To build the CLI from a checkout: +### Build from source + +For contributors or local development: ```bash +git clone https://github.com/open-ships/n2k-cli +cd n2k-cli just build # writes bin/n2k ./bin/n2k --help just install # optional: installs n2k into Go's bin directory ``` +## Quick Start — No Boat Required + +The repository includes a privacy-scrubbed, six-second sailing-vessel capture, +so you can try the decoder without CAN hardware: + +```bash +git clone --depth 1 https://github.com/open-ships/n2k-cli +cd n2k-cli +n2k sniff --file testdata/sample.log --output text +``` + +Frames containing vessel position, routes, or identity were removed. + ## The `n2k` CLI ### Interactive command center From db723f238a95f63f17705d4cd8a766d3dd80e3b5 Mon Sep 17 00:00:00 2001 From: Jake Thomas Date: Sat, 25 Jul 2026 11:48:27 -0400 Subject: [PATCH 2/2] Add curl installer and visual README --- .github/tui.svg | 40 +++++++++ .github/workflows/test.yaml | 11 +++ README.md | 87 +++++++++++++++----- install.sh | 158 ++++++++++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+), 20 deletions(-) create mode 100644 .github/tui.svg create mode 100755 install.sh diff --git a/.github/tui.svg b/.github/tui.svg new file mode 100644 index 0000000..a85d0f9 --- /dev/null +++ b/.github/tui.svg @@ -0,0 +1,40 @@ + + + + + + + + n2k command center + + n2k + NMEA 2000 developer console + N2K COMMAND CENTER + 7 workflows + + + + Sniff decoded traffic + Stream typed JSON or physical-value text from CAN, USB, gateway, or capture + + Record raw observations + Capture replayable candump or context-rich observation JSON lines + + Replay a capture + Decode a candump file with optional source timing and CEL filtering + + Validate a source + Summarize typed and undecodable messages; optionally enforce strict mode + + Inventory network devices + Actively discover a live bus or inspect device identities captured in a file + + Explore the PGN schema + Autocomplete known PGNs and inspect fields, ranges, units, and confidence + + Update n2k + Check GitHub releases and update through Homebrew, Go, or a verified binary + + + 1–7 jump • / filter • enter configure • ? help • q quit + diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 03e16db..778f29a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -26,6 +26,17 @@ jobs: - name: Test run: go test ./... + - name: Test release installer + shell: bash + run: | + install_dir="$RUNNER_TEMP/n2k-install" + N2K_INSTALL_DIR="$install_dir" sh ./install.sh + binary=n2k + if [ "$RUNNER_OS" = Windows ]; then + binary=n2k.exe + fi + "$install_dir/$binary" version + race: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 14d9bd1..bae2098 100644 --- a/README.md +++ b/README.md @@ -3,33 +3,49 @@ [![CI](https://github.com/open-ships/n2k-cli/actions/workflows/test.yaml/badge.svg)](https://github.com/open-ships/n2k-cli/actions/workflows/test.yaml) [![Release](https://img.shields.io/github/v/release/open-ships/n2k-cli)](https://github.com/open-ships/n2k-cli/releases) -Rich terminal tools for inspecting NMEA 2000 networks. +**Turn raw NMEA 2000 traffic into typed, searchable, scriptable data.** -`n2k` decodes, records, replays, validates, and inspects NMEA 2000 traffic from -SocketCAN interfaces, USB-CAN adapters, network gateways, and capture files. -Run it without arguments for a searchable Bubble Tea command center, or use its -deterministic subcommands in scripts and JSON pipelines. +Marine-network problems often begin as opaque CAN frames: which device sent +this message, what does the payload mean, and can the failure be reproduced? +`n2k` makes that traffic useful without hiding the wire. It combines a guided +Bubble Tea command center with deterministic CLI commands for decoding, +recording, replaying, validating, filtering, and device discovery. -- Guided interactive workflows with fuzzy search and autocomplete. -- Typed PGN decoding with physical values, units, and exact wire data. -- Live SocketCAN, USB-CAN, TCP, and UDP sources. -- Replayable candump and owned-observation recording. -- CEL filtering, schema exploration, device inventory, and validation. -- Install-aware updates through Homebrew, Go, or verified release binaries. - -The CLI is powered by the +The CLI is powered by the typed [`open-ships/n2k`](https://github.com/open-ships/n2k) Go library. -![n2k sniff decoding NMEA 2000 PGNs as typed JSON lines with exact wire values](.github/demo.svg) - ## Install -Choose either installation path. Both provide the same `n2k` command, and the -built-in updater remembers how the binary was installed. +Every installation provides the same `n2k` command. + +### One-line installer + +Copy and run: + +```bash +curl -fsSL https://raw.githubusercontent.com/open-ships/n2k-cli/main/install.sh | sh +``` + +The installer detects the platform, downloads the matching release, verifies +its SHA-256 checksum, and installs `n2k` into `~/.local/bin` without `sudo`. If +that directory is not already on `PATH`, it prints the exact command to add it. + +| Operating system | Architectures | Shell | +|------------------|---------------|-------| +| macOS | Intel (`amd64`), Apple Silicon (`arm64`) | `sh`, `bash`, or `zsh` | +| Linux | `amd64`, `arm64` | `sh`, `bash`, or `zsh` | +| Windows | `amd64`, `arm64` | Git Bash, MSYS2, or Cygwin | + +WSL is detected as Linux. To choose another destination or pin a release: + +```bash +curl -fsSL https://raw.githubusercontent.com/open-ships/n2k-cli/main/install.sh \ + | N2K_INSTALL_DIR="$HOME/bin" N2K_VERSION=v1.0.2 sh +``` ### Homebrew -Homebrew is the simplest option on macOS and Linux: +On macOS or Linux: ```bash brew install --cask open-ships/tap/n2k @@ -44,7 +60,7 @@ brew upgrade --cask open-ships/tap/n2k ### Go -With Go 1.25.8 or newer installed: +With Go 1.25.8 or newer: ```bash go install github.com/open-ships/n2k-cli/cmd/n2k@latest @@ -78,6 +94,37 @@ just build # writes bin/n2k just install # optional: installs n2k into Go's bin directory ``` +## Why `n2k`? + +### Guided when you are exploring + +Run `n2k` with no arguments. Search the workflow palette, choose a source, and +configure the operation with validated fields and autocomplete. Before +anything touches the network, `n2k` shows the equivalent copyable CLI command. + +![The n2k Bubble Tea command center showing searchable decode, record, replay, validate, device, schema, and update workflows](.github/tui.svg) + +### Precise when you are automating + +The same workflows have stable flags, meaningful exit codes, dynamic shell +completion, CEL filters, and JSON-lines output. Typed values remain paired with +their exact wire representation, so pipelines are convenient without losing +diagnostic fidelity. + +![The n2k CLI decoding a sailing-vessel capture into typed JSON lines with PGNs and exact wire values](.github/demo.svg) + +### One tool for the whole debugging loop + +| Need | What `n2k` provides | +|------|---------------------| +| Inspect traffic now | Decode SocketCAN, USB-CAN, TCP, UDP, candump, and gzip captures. | +| Reproduce a problem | Record owned observations, then replay with original or disabled timing. | +| Reduce the firehose | Filter messages with CEL and select typed, text, JSON, or unknown-PGN output. | +| Find devices | Actively discover a writable network or passively inventory a saved capture. | +| Measure decoder coverage | Validate a source, count undecodable PGNs, and fail CI in strict mode. | +| Understand a PGN | Autocomplete every known PGN and inspect fields, units, ranges, and confidence. | +| Keep tooling current | Update through Homebrew, Go, or checksum-verified release binaries. | + ## Quick Start — No Boat Required The repository includes a privacy-scrubbed, six-second sailing-vessel capture, @@ -91,7 +138,7 @@ n2k sniff --file testdata/sample.log --output text Frames containing vessel position, routes, or identity were removed. -## The `n2k` CLI +## Rich terminal workflows ### Interactive command center diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..70ee5b0 --- /dev/null +++ b/install.sh @@ -0,0 +1,158 @@ +#!/bin/sh + +set -eu + +repository="open-ships/n2k-cli" +program="n2k" + +say() { + printf '%s\n' "$*" +} + +fail() { + printf 'n2k installer: %s\n' "$*" >&2 + exit 1 +} + +need() { + command -v "$1" >/dev/null 2>&1 || fail "$1 is required" +} + +curl_https() { + curl --proto '=https' --tlsv1.2 --retry 3 --retry-delay 1 "$@" +} + +detect_platform() { + case "$(uname -s)" in + Darwin) + os="darwin" + archive_format="tar.gz" + binary_name="$program" + ;; + Linux) + os="linux" + archive_format="tar.gz" + binary_name="$program" + ;; + MINGW* | MSYS* | CYGWIN*) + os="windows" + archive_format="zip" + binary_name="${program}.exe" + ;; + *) + fail "unsupported operating system: $(uname -s)" + ;; + esac + + case "$(uname -m)" in + x86_64 | amd64) + arch="amd64" + ;; + arm64 | aarch64) + arch="arm64" + ;; + *) + fail "unsupported architecture: $(uname -m)" + ;; + esac +} + +latest_version() { + latest_url="$( + curl_https -fsSL \ + -o /dev/null \ + -w '%{url_effective}' \ + "https://github.com/${repository}/releases/latest" + )" + version="${latest_url##*/}" + version="${version#v}" + [ -n "$version" ] || fail "could not determine the latest release" +} + +sha256_file() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$1" | awk '{print $1}' + elif command -v openssl >/dev/null 2>&1; then + openssl dgst -sha256 "$1" | awk '{print $NF}' + else + fail "sha256sum, shasum, or openssl is required to verify the download" + fi +} + +verify_archive() { + expected="$( + awk -v archive="$archive_name" \ + '$2 == archive || $2 == ("*" archive) { print $1; exit }' \ + "$temporary_dir/checksums.txt" + )" + [ -n "$expected" ] || fail "checksums.txt has no entry for $archive_name" + + actual="$(sha256_file "$temporary_dir/$archive_name")" + [ "$actual" = "$expected" ] || fail "SHA-256 verification failed for $archive_name" +} + +install_binary() { + case "$archive_format" in + tar.gz) + need tar + tar -xOzf "$temporary_dir/$archive_name" "$binary_name" >"$extracted" + ;; + zip) + need unzip + unzip -p "$temporary_dir/$archive_name" "$binary_name" >"$extracted" + ;; + esac + + [ -s "$extracted" ] || fail "release archive does not contain $binary_name" + + mkdir -p "$install_dir" + [ -w "$install_dir" ] || fail "$install_dir is not writable; set N2K_INSTALL_DIR to a writable directory" + + staged="$install_dir/.${binary_name}.new.$$" + cp "$extracted" "$staged" + chmod 755 "$staged" + mv -f "$staged" "$install_dir/$binary_name" +} + +need curl +need awk +need uname +detect_platform + +if [ -n "${N2K_VERSION:-}" ]; then + version="${N2K_VERSION#v}" +else + latest_version +fi + +install_dir="${N2K_INSTALL_DIR:-${HOME:?HOME is required}/.local/bin}" +archive_name="${program}_${version}_${os}_${arch}.${archive_format}" +download_root="https://github.com/${repository}/releases/download/v${version}" +temporary_dir="$(mktemp -d "${TMPDIR:-/tmp}/n2k-install.XXXXXX")" +extracted="$temporary_dir/$binary_name" +trap 'rm -rf "$temporary_dir"' 0 HUP INT TERM + +say "Installing n2k v${version} for ${os}/${arch}..." +curl_https -fsSL \ + -o "$temporary_dir/$archive_name" \ + "$download_root/$archive_name" +curl_https -fsSL \ + -o "$temporary_dir/checksums.txt" \ + "$download_root/checksums.txt" + +verify_archive +install_binary + +say "Installed $install_dir/$binary_name" +case ":${PATH:-}:" in +*":$install_dir:"*) ;; +*) + say "" + say "Add n2k to your PATH:" + say " export PATH=\"$install_dir:\$PATH\"" + ;; +esac +say "" +say "Run: n2k --help"