A complete, from-scratch implementation of the Android Debug Bridge (ADB) protocol in Node.js. This library provides full ADB functionality including device connection, RSA authentication, shell command execution, and file transfers - eliminating clicking sounds on Android TV devices!
Note
Current status:
- Shell + streaming: Stable - command execution, interactive shells, and log/process streaming all work over the real ADB protocol.
- File transfer:
mkdir/remove/move/copy/chmod/diskUsage/find/statwork today via shell commands.listprefers a binary-safe SYNC-based implementation with automatic shell fallback. - Experimental:
push/pull/pushV2/pullV2/listSync/listV2/statV2(real ADB SYNC sub-protocol usage, both the legacy 32-bit and newer 64-bit variants),device.reboot(),device.forward()/device.reverse(),device.install()(both the classic push-then-install and modern streaming install paths), andpairing.pair()(Wi-Fi pairing) are all implemented - built from the ADB protocol spec and covered by unit tests (several exercised against real loopback TCP/TLS servers, not purely mocks) - but none of them have been run against a real device yet. See #1.
- π¨ Breaking: the
devicemodule is now split intodevice(single-targetconnect(host, port)/disconnect(host, port)/remove(host, port)) anddevices(collection-widelist()/disconnect()(all) /remove()(all) /get(idOrLeaf)) - connected devices are still mounted as composed API leaves atapi.devices.<host_port>. A device leaf now persists across a disconnect -disconnect()only tears down the socket (and stays synchronous),connect()on the same host:port later reconnects that same leaf without re-supplying options, andremove()is the new, separate "forget this device" operation (the one that's actuallyasync). - IPv6 support -
device.connect(),discover.subnet(), anddiscover.mdns()all accept IPv6 addresses/CIDRs now, not just IPv4. devices.get(idOrLeaf)(new) - looks up a connected device leaf by"host:port"string or by the leaf object itself; the safe way to re-resolve a leaf reference instead of holding onto a stale one.device.reverse()(experimental) - completes port forwarding with the device β host direction.pairing.pair()(experimental) - Wi-Fi pairing (SPAKE2-over-Ed25519 + TLS 1.3) for Android 11+ wireless debugging.- Streaming APK install (experimental) -
device.install()now triesexec:cmd-based streaming install first, falling back to the classic push-then-install flow. - SYNC V2 (64-bit) -
pushV2/pullV2/statV2/listV2lift the legacy 32-bit size ceiling, with optional brotli compression. - Hardening - closed a shell-injection gap in
devices.mjs's andshell.mjs's convenience shortcuts, capped several unbounded device-controlled memory allocations in the SYNC V2 paths, and fixed a handful of mid-transfer disconnect/failure edge cases. - View full v2.0.0 Changelog
- v1.2.0 (September 2026) - Device discovery (
discover.subnet()CIDR sweep,discover.mdns()for wireless-debugging-advertised devices, both experimental) and a shell-injection fix across everyfiles.*shell-based method (Changelog) - v1.1.1 (September 2026) - Documentation formatting fix (padded slashes between adjacent code spans) - no code changes (PR #16)
- v1.1.0 (September 2026) - Fixed the
list()/stat()regression from v1.0.0, and added a binary-safe SYNC-basedlist()with shell fallback, real ADBreboot:support, TCP port forwarding, and local APK install (Changelog) - v1.0.0 (September 2026) - First tagged release - a callable quick-path default export (dropping
connect()/listDevices()), a real test suite with measured coverage, and a full CI/release pipeline (Changelog)
π For complete version history and detailed release notes, see docs/changelog/ folder.
- β Complete ADB Protocol: TCP connection, CNXN/AUTH handshake, and stream multiplexing implemented from scratch
- β RSA Authentication: Automatic key generation and ADB-specific signature/public-key formatting
- β Stream Multiplexing: Multiple concurrent operations over a single connection
- β Shell Commands: Execute commands, stream output, interactive sessions
- β
File Operations: Shell-based
mkdir/remove/move/copy/chmod/diskUsage/find, plus binary-safe SYNC-basedlist, and experimentalpush/pull(legacy 32-bit) /pushV2/pullV2/listV2/statV2(64-bit) - β
Reboot (experimental): Real
reboot:service, including bootloader/recovery/sideload modes - β
Port Forwarding (experimental):
adb forward/adb reverse-equivalent TCP tunneling, both directions - β
APK Install (experimental):
adb install-equivalent local APK installation - classic push-then-install and modern streaming (exec:cmd package install) paths - β
Wi-Fi Pairing (experimental):
adb pair-equivalent PIN-based pairing (SPAKE2-over-Ed25519 + TLS 1.3) for Android 11+ wireless debugging - β Device Discovery: Support for multiple devices via configuration
- β Error Handling: Robust error handling and connection recovery
npm install @cldmv/droidsockimport droidsock from "@cldmv/droidsock";
// Create the API instance
const api = await droidsock();
// Connect to a device
const device = await api.device.connect("10.6.0.108", 5555);
// Execute a shell command
const output = await device.shell("ls -la");
console.log(output);
// Convenience getters
const model = await device.getModel();
const version = await device.getAndroidVersion();
// Stream commands
const logcat = device.logcat({
onData: (data) => console.log(data)
});
// Clean up
await device.disconnect();Use the references/devices.json file to configure your devices:
{
"livingroom": {
"name": "Living Room TV",
"host": "10.6.0.108",
"port": 5555,
"description": "Main living room Android TV"
},
"bedroom": {
"name": "Master Bedroom TV",
"host": "10.6.0.118",
"port": 5555,
"description": "Master bedroom Android TV"
},
"default": "livingroom"
}droidsock(options) (also createDroidSock) creates the API instance; api.device.connect(host, port, options) connects to a device (IPv4 or IPv6) and returns its live leaf - also reachable afterward at api.devices["<host>_<port>"] (a . becomes _, a : becomes __) - exposing connection state, shell execution/streaming, file operations (push / pull / list / stat), reboot, port forwarding, and APK install. api.device.disconnect(host, port) tears down one device's connection without forgetting it - reconnect later with connect() on the same host:port, no need to re-supply options; api.device.remove(host, port) forgets it entirely. api.devices.list() / disconnect() (all) / remove() (all) / get(idOrLeaf) manage the set of known devices as a whole.
π See docs/API.md for the full method reference, including every option and the experimental/scope caveats on push / pull / list / forward / reverse / install.
# Run basic example with default device
node examples/basic-usage.mjs
# Run with specific device
node examples/basic-usage.mjs livingroom# Stream logcat
node examples/streaming-example.mjs logcat
# Stream top command
node examples/streaming-example.mjs top
# File transfer demo
node examples/streaming-example.mjs filessrc/droidsock.mjs composes the layers below into a single api tree via @cldmv/slothlet:
- Connection Layer (
src/api/connection.mjs): TCP socket + CNXN/AUTH handshake - Authentication Layer (
src/api/auth.mjs): RSA key management and ADB signature/public-key formatting - Stream Layer (
src/api/stream.mjs): ADB stream multiplexing (OPEN/WRTE/OKAY/CLSE) - Shell Layer (
src/api/shell.mjs): Command execution, streaming, and interactive shell APIs - Files Layer (
src/api/files.mjs): Shell-based file operations, a binary-safe SYNCLISTimplementation with automatic shell fallback, and an experimental ADB SYNC sub-protocol implementation for real binary transfer (push/pull) - not yet validated against a real device - Reboot Layer (
src/api/reboot.mjs): Real ADBreboot:service - Forward Layer (
src/api/forward.mjs): TCP port forwarding (host β device) via thetcp:service - Reverse Layer (
src/api/reverse.mjs): TCP port forwarding (device β host) viareverse:forward:/reverse:killforward:and the Stream layer's device-initiated stream handling - Install Layer (
src/api/install.mjs): Local APK install, composed from the Files and Shell layers - Pairing Layer (
src/api/pairing.mjs): Wi-Fi pairing (adb pairequivalent) - a separate TLS 1.3 + SPAKE2 protocol reusing the Authentication layer's persistent RSA identity, not composed with any of the layers above - Device / Devices Layers (
src/api/device.mjs,src/api/devices.mjs): High-level per-device API composing the layers above, split by single-target (device.connect/disconnect/remove) vs. collection-wide (devices.list/disconnect/remove/get) operations. Each device is a real, persistent slothlet leaf atapi.devices.<sanitized host_port>, assigned there byconnect()rather than held in a private module variable, so its methods keep workingself/context access exactly like any other leaf - the leaf outlives any one connection, and onlyremove()unmounts it - Config / Log Layers (
src/api/config.mjs,src/api/log.mjs): Shared configuration and logging
π See docs/PROTOCOL.md for wire-level protocol details (packet structure, auth flow, SYNC sub-protocol framing, reboot/forward service usage).
- Ensure device is on same network
- Enable "ADB over network" in developer options
- Check firewall settings
- Verify IP address and port
- Delete existing keys to force re-authorization:
rm -rf ~/.adb - Ensure device shows authorization dialog
- Check device storage permissions
- "Command timeout": Increase timeout in options
- "Stream not open": Ensure connection is established
- "File not found": Check paths and permissions
The implementation is built directly from the public ADB protocol documentation (AOSP SYNC.TXT and the wire-protocol references), cross-checked against Google's own reference client (google/python-adb) where the public docs are ambiguous, and covered by a mocked Vitest suite. The core connection/shell/stream-multiplexing path has real device usage behind it; the newer SYNC-protocol and service additions (push / pull / listSync / reboot / forward / install) have not yet been run against a real device - see the status note at the top of this README and #1.
Apache-2.0 - see LICENSE for details.
This is a complete implementation of the ADB protocol. For improvements or bug fixes, please submit issues or pull requests.
