Hardware

Nexting Devices

Nexting Devices SDK · Experimental 0.4 · September 2026

1. What This Is

Nexting Devices is an open device SDK. Your hardware speaks one small BLE protocol to the Nexting App — and nothing else. The App and the Nexting cloud already do the hard part: they translate and forward Claude Code, Codex, Grok, and Cursor sessions in real time. Your device runs no agent code, holds no credentials, and works with those agents from day one.

Your device

any BLE hardware

BLE→

Nexting App + cloud

translate · forward

realtime→

CC · Codex · Grok · Cursor

your agents

You build the hardware; Nexting provides the app, the server, and the agent integrations.

2. What a Device Can Do

Every operation the SDK exposes to a device, for each agent. The App normalizes the differences between Claude Code, Codex, Grok, and Cursor — the device sees one identical surface. Overview first; each capability is specified in full below.

CapabilityDevice operationClaude Code · Codex · Grok · CursorStatus
Permission approvalShow a request with summary + countdown; answer Allow / Deny✓Shipped · approval/1
Agent status displayRender up to 8 agent slots × 6 states, optional label✓Shipped · status/1
Option navigationMove up/down through multi-option prompts, confirm a choiceHost adapterShipped · navigation/1
Command keysDedicated keys: approve, decline, fork, send, switch sessionHost adapterShipped · keys/1
Screen contentReceive conversation text to display on device screensHost adapterShipped · text/1
Rotary controlScrub through option levels pushed from the AppHost adapterShipped · rotary/1
Push-to-talk voiceHold a key, talk to the agentHost adapterShipped · voice/1
Remote configurationKey maps and lighting downloaded from the App, no reflashHost adapterShipped · config/1
Usage displayShow model and bounded token/context countersHost adapterShipped · usage/1

2.1 Permission approval · approval/1 (shipped)

What it solves: mid-task, the agent needs your decision — a Claude Code permission prompt, a Codex approval, a Grok or Cursor ACP permission. The App turns that two-option request into a present message; you answer with one key press on the device, no phone required.

The protocol messages (all four, plus errors):

MessageDirectionFieldsMeaning
presentApp → deviceid (1–64 char request ID) · sum (≤ 240-byte summary) · opt, always ["allow","deny"] · ttl (1–300000 ms countdown)A request awaits your answer; a new present replaces the old one
answerdevice → Appid · ch ("allow" or "deny")Your answer; only valid for the request currently displayed
resolvedApp → deviceid · r (answered / expired / cancelled / replaced)The request is finished; the device clears display and retry state immediately
errorboth waysid · code (bad_message / message_too_large / unsupported_version / unsupported_profile / unknown_request / not_authorized / busy)Protocol-level error; silently dropping malformed input is also legal
One complete approval
App → device   {"v":1,"t":"present","id":"3bb7","sum":"Allow git push?","opt":["allow","deny"],"ttl":30000}
device → App   {"v":1,"t":"answer","id":"3bb7","ch":"allow"}
App → device   {"v":1,"t":"resolved","id":"3bb7","r":"answered"}

Device-side rules:answer only the currently visible request; after sending an answer, wait for resolved — if it does not arrive within one second you may resend the identical answer (never change the choice); duplicate answers are idempotent by request ID and the first hardware choice stays locked. One pending request at a time — prompts with more than two options (like Claude Code’s AskUserQuestion) are not sent to devices today; they belong to option navigation below.

2.2 Agent status display · status/1 (shipped)

What it solves:you run several agents at once and want to know who is working, who is stuck, and who is done — without opening the App. The App pushes every agent session’s live state to the device, which renders it on LEDs, key backlights, or a screen.

The six states (every slot is in exactly one):

StateMeaningTypical rendering
idleSession idle, nothing runningLED off / grey
thinkingReasoning — waiting on model output, planningSlow blink / blue
workingExecuting — editing files, running commandsSolid / green
completeThis round of work is doneSingle flash / cyan
needs_inputBlocked, waiting on a human (permission, question)Fast blink / orange
errorSomething failed, needs a lookSolid / red
App → device (full replacement)
{"v":1,"t":"status","agents":[{"slot":0,"state":"thinking","label":"fix login bug"}]}

Device-side rules: up to eight anonymous slots (0–7); every message is a full replacement — a slot missing from the array is empty, an empty array clears everything; screened devices additionally receive an optional ≤ 64-byte label. Status is volatile: disconnect, reboot, or re-pair and every slot clears — an indicator never shows a stale agent. The App only sends status to devices that declare statusSlots ≥ 1; a device that did not declare it receives nothing, and sending it any traffic is not expected.

2.3 Option navigation · navigation/1

Claude Code’s AskUserQuestion, Codex multi-option confirmations, Cursor questions — prompts with more than two options. The App pushes the option list and the current highlight; the device moves the highlight with keys or a stick and confirms with a select key. The frozen messages are downlinknav_present / nav_resolved and uplink nav_move / nav_select. Sequence numbers reject replay and reordering.

2.4 Command keys · keys/1

Each physical key maps to one agent command: approve (same as answering Allow), decline, fork (branch a new session from the current one), send(inject a preset text into the agent’s input), switch session (rotate through your active agent sessions; status lights follow). Direction: uplink key events; downlink key labels and backlight state (which session is active). The Wire messages are keymap and key_event. The key event is intentionally generic: the trusted Host Agent adapter owns the final meaning and authority.

2.5 Screen content · text/1

An approval summary is only 240 bytes; screened devices want more. The text content capability lets the App push longer conversation excerpts for the device to scroll. The App still minimizes: only necessary conversation text leaves it — credentials and internal identifiers never do. The text message carries bounded plain text for a declared display channel, never markup or arbitrary Agent payloads.

2.6 Rotary control · rotary/1

A rotary encoder scrubs through levels — model tiers, approval strictness, session selection. Direction: uplink rotation deltas; downlink the level set and the current level, which the device can display. The frozen messages are rotary_map, rotary_event, and rotary_press; no internal model or session ID is sent to the device.

2.7 Push-to-talk voice · voice/1

Hold a key on the device and talk; release to send. The Host captures audio and injects the transcription into the current Agent session.voice_event carries start/stop/cancel and voice_state carries progress. Audio and transcripts never cross this profile; microphone permission and capture stay on the Host.

2.8 Remote configuration · config/1

Key maps, lighting schemes, and display preferences download from the App — no reflash. Configurations are versioned; a config that fails validation leaves the previous one untouched. config is applied atomically and config_result reports applied or rejected.

2.9 Usage display · usage/1

usage carries a bounded model label and informational token/context counters; usage_clear removes them. These values drive a display and are never treated as billing authority.

2.10 SDK API surface

LayerImplementationKey API
Device SDKPortable C99nexting_device_decode / encode · nexting_device_stream_push · nexting_device_status_on_message
ReferenceJavaScriptReadable protocol implementation pinned to the shared vectors
Test vectorsJSONapproval-v1.json · status-v1.json — every language validated byte for byte

3. How to Integrate

3.1 Device side: the C99 core

Device firmware is one loop: bytes in → decode → render; user action → encode → bytes out. The C99 core uses fixed buffers, zero heap, and no OS dependency — it ports to any RTOS:

Device main loop (pseudocode, real API signatures)19 lines
// Init: stream parser + status state machine
nexting_device_stream_init(&stream, storage, sizeof storage);
nexting_device_status_init(&status);

// ① BLE bytes arrive → feed the stream parser; it calls back per frame
nexting_device_stream_push(&stream, bytes, len, on_message, ctx);

// ② In the callback: decode → render by type (LED / screen / keys)

Malformed, oversized, or unknown-type input fails closed inside the SDK — your rendering code never sees bad data. The Zephyr reference firmware is this loop, complete, shared across all four boards.

3.3 Authorization flow

A device does nothing out of the box. The user explicitly authorizes it in the Nexting App → an encrypted, bonded link is established → only then does any traffic flow; revoking authorization in the App stops all traffic immediately. Device Info is readable before authorization for compatibility discovery only — reading it grants nothing. One contract serves two identity tiers: Nexting first-party products (PIN, Ring) carry production identity; third-party and DIY devices run under explicit authorization and are labeled as such in the App.

4. Build Your First Device

1. Pick a board. Nordic nRF52840 DK, Seeed XIAO nRF52840 / Sense, XIAO ESP32C3, or XIAO ESP32S3 — one Zephyr application covers all four, Build verified in CI (Zephyr 4.3.0 / SDK 0.17.4).
2. Flash the reference firmware. Out of the box it is a two-button approval pad.
3. Authorize it in the Nexting App and answer a real request from your own agent.

4.1 MultiPad USB CDC developer path

ILX MultiPad is a separate public STM32F103VET6 USB HID + CDC path, not the same transport as the BLE reference boards above. The public adapter reuses the portable C99 SDK and carries the same approval/1 / status/1 frames over CDC; it does not put the App, Agent, or credentials in firmware and does not automatically enroll in the Nexting App over USB.

Repository assetUseCurrent evidence
firmware/multipadadapter, CMake test, Device Info templatehost test passing
tools/multipad-cdc-smoke.pyAA BB CC echo and present senderadapter firmware required
tools/flash-multipad.shbackup and explicit writedoes not enter bootloader
docs/multipad-usb.mdModule/FPC and GitHub tutorialphysical evidence pending

Whether Type-C can flash this unit depends on the opened PCB: the module variant exposes the upstream serial switch and BOOT/RESET buttons; the FPC variant needs SWD/J-Link. Back up the original flash first and use the repository script; never treat a normal CDC port as a bootloader.

4.2 First case: ILX MultiPad integration

This is the first complete public hardware case. Pin the upstreamiLx11/multi-pad firmware, build it unchanged once, then add the public C99 core and nexting_multipad_adapter as a thin CDC binding. The first stable mapping is deliberately small: matrix key 1 answers Allow, matrix key 2 answers Deny, and the remaining keys and rotary controls keep their upstream behavior until their own profiles are released.

StepWhat you doWhat proves it
1 · Pin the sourceCheckout upstream commit 78c1ee5… and keep the original HEXOriginal hash recorded
2 · Add the adapterRoute JSON CDC frames before the legacy AA BB xx branchesC99 + CMake tests pass
3 · Choose the writerModule uses serial only if SERIAL/BOOT/RESET are present; FPC uses ST-Link/J-Link SWDPCB path identified
4 · Verify one flowpresent → Allow/Deny → resolved, then expiry and disconnectSame request ID and clean state

The full source-first walkthrough, build separation, pin evidence, and rollback rules live in the public GitHub guide at docs/cases/multipad-first-case.md. The existing leden.hex from upstream is not a Nexting image; never flash it as the adapted firmware.

4.3 AhaKey X1 BLE case

The AhaKey X1 is a shipping BLE vibecoding keyboard — four keys, an approval lever, an LED strip, an OLED, and a voice-input use case. The current public Devices SDK is Experimental 0.4 and includes the AhaKey directory and device-audio profile. Production PCM binding, target build, mobile conformance closure, and physical-board verification are still pending.

X1 hardwareNexting interfaceBehavior
Keys F18 / F19approval/1Allow / Deny the pending request
Keys F13–F16 + leverkeys/1 slots 0–3 and 7Host-mapped actions
LED stripstatus/1Agent state as IDE light
OLED 160×80text/1Request and status text
Device microphone + voice keydevice-audio/1Device PCM → encrypted BLE → App; no phone-mic substitution

The AhaKey X1 guide explains the current 0.4 contract and the evidence required before the AhaKey files can be called board-verified.

5. Protocol Reference

BLE transport

RoleUUIDPropertiesDirection
Service6EADC0DE-0001-4A21-9C5E-1B7F3D9E42A0Primary—
Downlink6EADC0DE-0002-4A21-9C5E-1B7F3D9E42A0WriteApp → device
Uplink6EADC0DE-0003-4A21-9C5E-1B7F3D9E42A0NotifyDevice → App
Device Info6EADC0DE-0004-4A21-9C5E-1B7F3D9E42A0ReadDevice → App

Wire format

One compact UTF-8 JSON object per message, newline-terminated; frames may split across BLE writes and notifications; official implementations cap a frame at 4096 bytes. Enum values are lowercase; duplicate fields are invalid; unknown types, missing fields, and out-of-range values fail closed — the message is discarded and the previous state is kept.

Device Info — capability declaration

On connect the App reads what the device can render and only sends what the device can consume. No indicator? Omit statusSlots and receive zero status traffic. One LED? Declare 1, not 8.

Device Info
{
  "protocol": "nexting-device",
  "spec": "0.1.0-experimental",
  "wire": [1],
  "profiles": ["approval/1", "status/1", "navigation/1", "keys/1", "rotary/1", "voice/1", "text/1", "usage/1", "config/1"],
  "model": "xiao-nrf52840-ref",
  "fw": "0.1.0",
  "max_message_bytes": 4096,
  "max_summary_bytes": 240,
  "statusSlots": 4
}

6. Ground Rules

Deny by default. A device does nothing until the user explicitly authorizes it in the App; revocation stops all traffic.
Fail closed. Malformed, stale, oversized, or unauthorized input never approves anything and never corrupts the display.
Volatile state. Approvals and status live in RAM and clear on disconnect, reboot, or re-pairing.
Honest capabilities. Declare only what the hardware actually renders.

7. Source Code

Specification, shared test vectors, SDKs, reference firmware, and the full developer guides — quickstart, interface catalog, implementation tracks, conformance levels, use-case gallery:

github.com/Nexting-ai →

Code, SDKs, tests, and reference firmware are Apache-2.0; specifications and documentation are CC BY 4.0. Neither license grants the Nexting name, logo, or Nexting Compatible badge.