Skip to main content

Nanokvm

@svendowideit/nanokvmv2026.10.17.4· MIT· 7d agoBEHAVIORSREPORTSACCEPTED WARNINGS
01README

Operate a Sipeed NanoKVM from swamp: a health verdict, screenshots, GPIO power/reset and virtual media, with the device's health pushed to your swamp-managed OpenTelemetry collector. WHAT IT DOES

A NanoKVM is an out-of-band IP-KVM running Sipeed's own server, not PiKVM's kvmd: a small JSON API over a self-signed HTTPS certificate. Two firmware generations disagree on login — older builds take the password in plaintext and return a bearer token, newer builds take the web UI's AES-encrypted password and set a session cookie — and this model speaks both transparently. health returns a pass/warn/fail verdict with findings — firmware behind, no HDMI signal, virtual media left mounted, HID misconfigured — and records an unreachable device as a failure rather than throwing, so a scheduled check always leaves evidence. emit forwards that verdict to the swamp-managed OTel system (an OTLP log plus gauges), a no-op that says why when no collector is configured.

INSTALL

swamp extension pull @svendowideit/nanokvm

DEPENDENCIES

@svendowideit/curl (pulled automatically) — the HTTP primitive this model drives for the login, every read and the OTLP push. Optional: @svendowideit/otel-settings, whose contract supplies the emit endpoint; without it emit records that it has nowhere to send and does nothing.

RUN

# Store the login password once (never in the model definition), then
# check the device. Vault-first: the model reads vaultName/passwordRef.
swamp vault create @svendowideit/systemd-creds nanokvm
swamp vault put nanokvm NANOKVM_PASSWORD
swamp model @svendowideit/nanokvm method run setup nanokvm \
  --input host=192.0.2.10
swamp model @svendowideit/nanokvm method run health nanokvm

# Capture a frame from the video stream, and press the power button —
# power and msd change state, so they are dry runs until apply=true.
swamp model @svendowideit/nanokvm method run screenshot nanokvm
swamp model @svendowideit/nanokvm method run power nanokvm \
  --input action=power --input apply=true

# Push the latest health to the swamp-managed OTel collector. A no-op
# that records the reason when no @svendowideit/otel-settings contract or
# endpoint is configured.
swamp model @svendowideit/nanokvm method run emit nanokvm

# List every NanoKVM swamp looks after (a repo scan, no network) and emit
# an A-record list ready for a DNS reconciler.
swamp model @svendowideit/nanokvm method run devices nanokvm \
  --input zone=fi.gy

CONFIGURE

Global arguments (set at creation with --global-arg key=value; only host has no default): host NanoKVM address, no scheme (e.g. 192.0.2.10) port HTTPS port (default 443) username web UI user (default admin) vaultName vault holding the password (default empty) passwordRef vault key for the password (default NANOKVM_PASSWORD) envRef env var fallback for the password (default NANOKVM_PASSWORD) password literal password fallback (sensitive; avoid) passwordEncoding how the login sends the password: auto|plain|aes (default auto) curlModel @svendowideit/curl instance name (default nanokvm-curl) otelModel otel-settings model for the emit endpoint (default otel) otelEndpoint OTLP/HTTP base URL overriding the contract (default empty) otelMesh contract mesh to use (default empty = defaultMesh) otelVaultName vault holding the gateway bearer token (default empty) otelTokenRef vault/env key for the token (default OTEL_EXPORTER_OTLP_TOKEN) insecure skip TLS verification (default true) timeoutMs per-request timeout (default 15000) maxClockSkewSeconds tolerated slowness before a warning (default 120)

Method arguments: power takes action (power|reset), durationMs, apply; msd takes action (status|mount|umount), file, cdrom, apply; screenshot takes timeoutMs and label; emit takes health, endpoint and mesh; devices takes zone (the DNS suffix for each device's name). setup takes the connection overrides it can persist. See the README for the full tables.

SETUP

setup is the entry point for a new device. It persists what it is given — even when the configuration is incomplete — to the model's config resource, reads that resource back on the next run and reuses it, and reports which capabilities are enabled: canLogin (a host and a password) and canEmit (a collector contract or an explicit endpoint). Supplying only the host stores the rest and leaves canLogin false; add the vault later and re-run. health refuses to run until a host is configured and reports a missing password as a fail verdict with the reason.

UPGRADE

This extension installs nothing on the host, so an upgrade has nothing to rebuild or reconcile. Refresh the pulled model and its bundled files with one idempotent command:

  # Pull the newest published model and its bundled files.
  swamp extension update

Model definitions record the version they were created with; the bundled upgrades entry migrates them on the next method run. No swamp data is touched.

WHAT IT INSTALLS

Nothing on the host — no services, cron triggers, daemons or webhooks. It runs the curl binary through @svendowideit/curl, talks to the NanoKVM and (optionally) the OTel collector over the network, and writes swamp resources and a JPEG file.

02Behaviors1
@svendowideit/nanokvmv2026.10.17.4nanokvm.ts

Global Arguments

ArgumentTypeDescription
name?stringModel instance name (supplied by swamp).
port?numberHTTPS port of the NanoKVM web server.
password?stringLogin password (last resort; prefer vaultName). Recorded as a secret.
passwordEncoding?enumHow the login password is sent. Older NanoKVM firmware expects it in
insecure?booleanSkip TLS certificate verification. NanoKVM's factory certificate is
timeoutMs?numberPer-request timeout, in milliseconds.
maxClockSkewSeconds?numberClock difference tolerated before a `clock_skew` finding.
fn setup(host?: string, port?: number, username?: string, vaultName?: string, passwordRef?: string, envRef?: string, curlModel?: string, otelModel?: string)
Persist this NanoKVM's connection settings, read them back, and
ArgumentTypeDescription
host?stringNanoKVM address (persisted for reuse).
port?numberHTTPS port (persisted for reuse).
username?stringWeb UI user (persisted).
vaultName?stringPassword vault name (persisted).
passwordRef?stringPassword vault key (persisted).
envRef?stringPassword environment variable (persisted).
curlModel?stringcurl model instance name (persisted).
otelModel?stringotel-settings model name (persisted).
fn health()
Read-only health check: reachability, authentication, application and
fn power(action: enum, durationMs?: number, apply?: boolean)
Press the NanoKVM's power or reset button over GPIO. A dry run unless
ArgumentTypeDescription
actionenum`power` presses the power button; `reset` presses the reset button.
durationMs?numberHow long to hold the contacts, in ms. 0 uses the device's own default
apply?booleanActually press the button. When false (the default) the method is a dry
fn msd(action: enum, file?: string, cdrom?: boolean, apply?: boolean)
Inspect, mount, or unmount virtual media (an ISO/IMG served to the
ArgumentTypeDescription
actionenum`status` reads the mounted image; `mount` serves `file` to the host;
file?stringImage path as the NanoKVM sees it (e.g. /data/ubuntu.iso). Required for
cdrom?booleanPresent the image as a CD-ROM instead of a removable disk.
apply?booleanActually mount or unmount. `status` is always read-only; mount/umount are
fn screenshot(timeoutMs?: number, label?: string)
Capture a JPEG frame from the NanoKVM video stream and store it as the
ArgumentTypeDescription
timeoutMs?numberHow long to read the MJPEG stream before trimming the first frame.
label?stringName for the stored JPEG frame. Empty uses `screenshot`.
fn emit(health?: unknown, endpoint?: string, mesh?: string)
Push this device's health to the swamp-managed OpenTelemetry system
ArgumentTypeDescription
health?unknownA health record to emit. When omitted, the model's latest `health`
endpoint?stringOTLP/HTTP base URL overriding both the contract and the global argument.
mesh?stringContract mesh to use for this emit.
fn devices(zone?: string)
List every NanoKVM swamp looks after. Scans the repo's model
ArgumentTypeDescription
zone?stringDNS zone appended to each device name to form its fqdn (e.g. `fi.gy`
fn cert(action: enum, name?: string, certDir?: string, crt?: string, key?: string, waitMs?: number, restart?: boolean)
Read the TLS certificate the device serves, or install a Let's
ArgumentTypeDescription
actionenum`status` reads the certificate the device currently serves; `install`
name?stringThe certificate's DNS name (e.g. nanokvm.fi.gy). Required for `install`
certDir?stringCaddy's certificate store. `install` looks for
crt?stringExplicit path to the certificate PEM, overriding the certDir lookup.
key?stringExplicit path to the private-key PEM, overriding the certDir lookup.
waitMs?numberHow long `install` waits for the certificate file to appear (Caddy issues
restart?booleanRestart the NanoKVM web server after writing the files, so it serves the

Resources

config(infinite)— Persisted NanoKVM configuration and enabled capabilities.
health(30d)— One health check of the NanoKVM: signals, verdict, findings.
power(30d)— A power/reset press (or dry run) and its outcome.
msd(30d)— A virtual-media mount/unmount/status and its outcome.
screenshotMeta(7d)— Metadata for a captured screen frame.
emit(30d)— One OTLP push of the device health, or why it was skipped.
devices(30d)— Every NanoKVM swamp looks after, discovered from the repo's model
cert(30d)— The TLS certificate the device serves, or the outcome of installing one.
03Reports1
@svendowideit/nanokvm-health-reportworkflow
nanokvm_health_report.ts

Fleet health report over a workflow run of @svendowideit/nanokvm devices:

nanokvmhealthkvmsummary
04Previous Versions1
2026.10.17.3MIT
05Stats
A
100 / 100
Downloads
5
Archive size
69.7 KB
  • Has README or module doc2/2earned
  • README has a code example1/1earned
  • README is substantive1/1earned
  • Most symbols documented1/1earned
  • No slow types (deprecated)1/1earned
  • Dependencies pass trust audit2/2earned
  • Has description1/1earned
  • Platform support declared (or universal)2/2earned
  • License2/2earned
  • Verified public repository2/2earned

07Platforms