@manfred-kunze-dev/iot-cli
v3.6.3
Published
Command-line interface for the iot platform by Manfred Kunze Development
Readme
iot CLI
Command-line interface for the 2kw.io IoT platform by Manfred Kunze Development.
Manage devices, sensors, sites, readings, events and more — straight from your terminal.
Installation
# Latest stable release
npm install -g @manfred-kunze-dev/iot-cli
# Pre-release (dev channel)
npm install -g @manfred-kunze-dev/iot-cli@devThe CLI is installed as iot, mkd-iot, and mki (short alias) — pick whichever you prefer:
iot --version
mki --versionThe CLI will notify you when a newer version is available.
Quick Start
# Authenticate
iot auth login
# Verify
iot auth statusAuthentication
The CLI supports multiple authentication methods (highest priority first):
| Method | Example |
| --------------------- | ---------------------------------------------------------- |
| CLI flags | --api-key sk_... --base-url https://... |
| Environment variables | IOT_API_KEY, IOT_BASE_URL |
| Local .iot file | JSON file in the current directory or any ancestor |
| Config store | ~/.config/iot-cli/config.json (set via iot auth login) |
The base URL defaults to https://api.2kw.io. Contexts created before the move to 2kw.io
(pointing at https://iot.manfred-kunze.dev/api) are rewritten to the new host automatically
the next time the CLI runs; a .iot file or IOT_BASE_URL naming the old host keeps working
as-is, since the old hostname stays up as an alias.
iot auth login # Interactive setup
iot auth status # Verify credentials
iot auth logout # Clear stored credentialsContexts
The CLI supports kubectl-style contexts for switching between organizations and environments:
# Contexts are created automatically via `iot auth login`
iot context list # List all contexts
iot context use prod # Switch active context
iot context create staging # Create a new context
iot context rename old new # Rename a context
iot context delete old # Remove a context
iot context current # Print just the active name (script-friendly)You can also switch contexts for a single invocation:
iot --context staging auth statusFlashing devices
iot flash provisions an SD card that already carries a 2kw-os base image
(write that with Raspberry Pi Imager first). It mints a registration token,
writes a validated bootstrap.toml onto the boot partition, and waits for the
device to enrol.
# One-off, fully explicit
iot flash --name "Werk2 Gateway" --target D:\ \
--ssh-key ~/.ssh/rpi_test.pub \
--wifi-ssid Plant-Primary --wifi-psk 'secret'
# Fleet flashing: store the stable bits once, then only name each device
iot flash profile set bench --ssh-key ~/.ssh/rpi_test.pub --wifi-mode store
iot flash profile use bench
iot flash --name "Bench Pi 3"
# Customer site: profile holds the SSH key, WiFi is prompted and never stored
iot flash profile set customer --ssh-key ~/.ssh/rpi_test.pub --wifi-mode prompt-always
iot flash --profile customer --name "Acme Line 2"
# CI / scripting
iot flash --name ci-device --target /media/ci/boot --ssh-key ./key.pub \
--wifi-ssid CI --wifi-psk ci --non-interactive --no-wait --jsonValues resolve as flag → env → profile → prompt → error. With
--non-interactive, a missing value exits non-zero naming the flag instead of
prompting.
Not in scope: downloading or writing the OS image itself.
Commands (foundation — v1)
| Command | Description |
| --------- | ---------------------------------------------- |
| auth | Login, logout, and check auth status |
| config | Get, set, and list global configuration values |
| context | Manage CLI contexts for multiple environments |
| docs | Browse API documentation from the terminal |
Resource commands (devices, sensors, sites, readings, events, …) ship in later releases.
Global Options
| Flag | Description |
| ------------------ | --------------------------------------------------------------------------------------- |
| --api-key <key> | Override the API key |
| --base-url <url> | Override the base URL |
| --context <name> | Use a named context for this invocation without switching the active one |
| --json | Output raw JSON instead of formatted tables |
| --no-color | Disable colored output |
| --verbose | Print resolved base URL, masked key, and request method+path on stderr before each call |
Output
Stdout carries the command's data payload (table, JSON, created resource ID). Everything else — spinners, errors, the update banner — goes to stderr. This makes pipelines like iot devices list --json | jq clean.
Exit codes:
| Code | Meaning | | ---- | ----------------------- | | 0 | Success | | 1 | Generic error | | 2 | Not authenticated (401) | | 3 | Forbidden (403) | | 4 | Not found (404) | | 5 | Conflict (409) | | 6 | Validation error (422) | | 7 | Server error (5xx) | | 8 | Network error |
Development
# Install deps
npm ci
# Build
npm run build
# Watch + rebuild
npm run dev
# Type-check only
npm run typecheck
# Run tests
npm test
# Regenerate typed client from a running dev backend
SPRING_PROFILES_ACTIVE=spec ./mvnw.cmd spring-boot:run # run from backend/
npm run generate # run from cli/See docs/superpowers/specs/2026-05-25-iot-cli-foundation-design.md for the full design.
License
Proprietary — see LICENSE for details.
