jetkvm
Recipe card from the charly-check plugin (Commands — runtime CLI verbs).
JetKVM — IP-KVM control from a jetkvm: check verb
Section titled “JetKVM — IP-KVM control from a jetkvm: check verb”Overview
Section titled “Overview”jetkvm: is a DECLARATIVE check/control verb — authored as jetkvm: <method>
inside a candy/box plan check:/run: step. It is NOT a host charly check
subcommand: there is no charly check jetkvm. The verb’s implementation and its
WebRTC/HID client dependency live in the out-of-tree
candy/plugin-jetkvm plugin module; at check time the host dispatches jetkvm:
through the provider registry to that out-of-process plugin, exactly like
vnc:/adb:/cdp:.
It drives a JetKVM (https://jetkvm.com) with no browser, over the device’s own
control plane: local login (POST /auth/login-local) → the signaling websocket
(/webrtc/signaling/client) → the rpc JSON-RPC 2.0 data channel and the binary
hidrpc input channel, with the H.264 video track decoded to a PNG by ffmpeg
(required on the host).
Authoring a jetkvm: step
Section titled “Authoring a jetkvm: step”The method name is the scalar value for a bare-method step (jetkvm: status), or
the method: key of the jetkvm: map when the step carries jetkvm-exclusive
fields (host:, x:, y:, text:, key:, artifact:, …) — those live INSIDE
the jetkvm: map. Only the shared matchers (stdout:, stderr:,
exit_status:) and context:/id:/timeout: stay siblings. All jetkvm: steps
are deploy-context only (they need a reachable device), so author them with
context: [runtime].
- check: the JetKVM is reachable and its control channel answers context: [runtime] jetkvm: method: status host: jk.example.ts.net stdout: - contains: "rpc: true"
- check: a fresh framebuffer is captured as a real PNG context: [runtime] jetkvm: method: screenshot host: jk.example.ts.net artifact: /tmp/kvm.png artifact_min_bytes: 20000READ-ONLY BY DEFAULT — the device-safety gate
Section titled “READ-ONLY BY DEFAULT — the device-safety gate”A JetKVM is a physical appliance wired into a real machine’s keyboard, video
and power. Unlike a container it is NOT disposable, and a stray keystroke or power
action has real consequences. So every mutating method requires
allow_control: true; without it the step reports a documented skip naming
the gate rather than acting. factory-reset and update are refused even WITH
allow_control, because no unattended plan may wipe or reimage a device.
The classification is an allowlist, so a method added later is mutating-by-default and cannot touch a device until it is deliberately classified.
Methods
Section titled “Methods”Read-only (no gate): status, screenshot, version, video-state,
usb-state, atx-state, dc-state, virtual-media-state, storage-files,
wol-devices, macros, keyboard-layout, timezones, cloud-state,
network-state, network-settings, tailscale-status, update-status,
devmode-state, ssh-key, tls-state, extensions, public-ip,
diagnostics, check-media-url, usb-config.
Mutating (need allow_control: true): key, type, key-combo, macro,
click, mouse, move, scroll, drag, power, dc-power, reboot, wol,
virtual-media, usb-device, usb-emulation, set-settings, set-edid,
set-video, set-display, set-audio, set-network, set-tailscale,
set-devmode, set-ssh-key, set-tls, set-keyboard-layout, set-macros,
set-jiggler, set-extension, set-wol-devices, set-log-level,
renew-dhcp, rpc.
Never autonomous: factory-reset, update.
The rpc method is the raw escape hatch: author rpc_method: (the device
JSON-RPC method) and optionally rpc_params: (a JSON object), so a method the
plugin has not typed yet is reachable without a release. It is deliberately
MUTATING — it can invoke ANY device method, so an ungated escape hatch would
be a bypass of the safety gate — and it requires allow_control: true. Even
then it refuses the irreversible reimaging methods (factoryReset,
tryUpdate, tryUpdateComponents), matching the never-autonomous rule its
typed counterparts obey.
Credentials
Section titled “Credentials”Prefer password_secret: (a key in the verb:credential store, resolved
peer-to-peer over the reverse channel) or the JETKVM_AUTH_TOKEN /
JETKVM_PASSWORD environment variables. An auth_token: may also be authored
directly when a session already exists. Never commit a device password to a
charly.yml.
Connection details
Section titled “Connection details”host accepts a bare hostname, host:port, or an explicit http(s):// URL; a
bare value defaults to the device’s plaintext scheme. Set insecure: true to
accept the self-signed certificate a JetKVM ships by default (needed for an
https:// device; also required for the signaling websocket).
The device address is resolved in order: the authored host:, then the
JETKVM_HOST environment variable, then the deploy venue’s address. Author no
host: and set JETKVM_HOST to keep a device-specific hostname out of a
committed plan — the same shape JETKVM_AUTH_TOKEN / JETKVM_PASSWORD already
give credentials. No device hostname belongs in a repository.
Input methods: move/mouse are always a pure position move — an authored
button: is validated but never sent. click/drag press the button, which
defaults to left. x/y (and from_x/from_y) are absolute HID pointer
coordinates in [0,32767], not desktop pixels; map a desktop pixel (px,py)
on a W×H screen with px*32767/(W-1), py*32767/(H-1) (centre of 1920×1080
≈ 16384,16384). key and key-combo resolve over the common USB HID
Keyboard/Keypad usages — letters a–z, digits 0–9, F1–F12, the
navigation/editing keys (Enter, Escape, Tab, arrows, Home, End,
PageUp, PageDown, Insert, Delete, Backspace), punctuation, and
modifier chords like Control_L+Alt_L+Delete; names outside that set fail with
unknown key. Uppercase or shifted symbols (A, !) imply Shift.
NOTE on video: do not gate a screenshot on the device’s getVideoState.ready
field. Measured on firmware 0.5.9, ready reports whether the device’s native
capture PIPELINE is currently running — which is started per WebRTC session and
stopped when the last one disconnects — not whether an HDMI signal is present. It
reads false with error:no_signal even while screenshots succeed. The verb’s
screenshot method is the authority; it opens the session and waits for a frame.
Requirements
Section titled “Requirements”- A reachable JetKVM device on the network
ffmpegon the HOST running the check (it decodes the H.264 keyframe to PNG); provided by thelayer-ffmpegcandy
Running charly ON the device
Section titled “Running charly ON the device”A JetKVM runs an armv7l (32-bit ARM) uClibc/BusyBox userland with no package
manager, so the distro charly packages cannot be installed on it. To put
charly on the appliance, use the opencharly/charly-jetkvm tool, which delivers
the published release binary + welded plugin set over ssh:
It downloads the release assets on the HOST (gh, TLS-validated) and streams them
over ssh into /userdata/charly — the device’s own wget does not validate TLS,
so an on-device download would be MITM-exposed. verify asserts charly version
equals the requested CalVer and that a baked command word dispatches project-less.
The same tool derives the jetkvm: verb’s environment from the ONE ssh
connection — charly-jetkvm env --host root@<device> --export reads the device’s
local_auth_token and prints JETKVM_HOST + JETKVM_AUTH_TOKEN, so a device can
be driven with no committed hostname and no hand-copied token:
charly check run jetkvm-device-readonly # status + screenshotcharly check run jetkvm-input-probe # move/click/drag/key/type + screenshotsThere is deliberately NO charly mcp serve on the device. The appliance has
~199 MB RAM and one core; running the MCP server forked a full CLI model and
exhausted memory, hanging the device. The appliance gets charly only for local
project-less verbs. Driving the KVM remains the host-side jetkvm: verb above,
which talks to the device’s control plane directly and needs no charly on it.
Cross-References
Section titled “Cross-References”/charly-check:check— parent router; thejetkvm:verb dispatches out-of-process viacandy/plugin-jetkvm./charly-check:vnc— the sibling desktop-automation verb (RFB for a container/VM display rather than a physical KVM)./charly-check:adb— the sibling out-of-process device verb./charly-image:layer— layer authoring; the orderedplan:step list is part of everycharly.yml.
When to Use This Skill
Section titled “When to Use This Skill”MUST be invoked when the task involves a JetKVM device, the jetkvm: check
verb, IP-KVM remote control (keyboard/video/mouse or power over the network), or
capturing a screenshot from a machine via a JetKVM. Invoke this skill BEFORE
reading source code or launching Explore agents.