TCL HH515L 5G CPE · one Bun binary

Your router speaks
encrypted JSON-RPC.

The HH515L web UI does not send your password. It runs a four-stage handshake — RSA key exchange, PBKDF2-SHA512 against the router’s own salt, AES-256-CBC on every request body, HMAC-SHA256 on every payload. I read that out of the router’s own JavaScript and reimplemented it, so all 348 endpoints are one command away.

$ cpe login   # prompts, never on argv
$ cpe status
Endpoints
348
174 read, 174 write
Binary
1
self-contained, no runtime
Handshake
4
stages, reverse-engineered
Tests
26
crypto from real vectors
Leaves the machine
Nothing
no server, no cloud, no third party

The password is never passed on the command line where ps would show it, never logged, and never written to disk — only the resulting session token is stored, at ~/.cpe/session.json with mode 0600. The router itself is on your LAN, and so is every request.

The catalog was extracted from the router’s own web UI bundle and verified against it: every name in src/endpoints.ts corresponds to a real post() call site in the firmware’s JavaScript, and none are missing.

One paste

Let your agent set it up

Every command speaks --json, so an agent can drive the router directly. Paste this and it installs the binary, logs in without putting your password on a command line, and shows you what it found.

Paste into your agent
Set me up with cpe — a terminal control plane for my TCL HH515L 5G router.
Run this first, then follow it exactly, top to bottom:

    curl -fsSL https://cpe.lukaloehr.com/setup.txt

Ask me its first question before doing anything else. Never put my router
password on a command line, and never run a write endpoint without asking.
Get started

Two steps

Ah, so you haven’t fallen for the vibe yet. Respect. I still read commands before I run them too.

01

Get the binary

Self-contained, no runtime needed — macOS arm64/x64, Linux x64/arm64 on Releases. Verify it against the published SHA256SUMS if you like. Or run from source with Bun; there are no runtime dependencies.

$ chmod +x cpe-darwin-arm64
$ mv cpe-darwin-arm64 ~/.local/bin/cpe
$ sha256sum -c SHA256SUMS --ignore-missing
02

Log in once

One caveat worth knowing: the router locks the account after a few failed logins. cpe login performs exactly one Login call and never retries — if credentials are rejected it prints how many attempts remain rather than swallowing it.

$ cpe login
$ cpe status
$ cpe call GetConnectedDeviceList --json \
    | jq '.ConnectedList[].DeviceName'

Every Set* and action endpoint requires --confirm, including through cpe call. Read-only endpoints never do. cpe reboot is refused; cpe reboot --confirm actually reboots.

How it authenticates

loginAuthType: 7

The router does not accept a plain password. Four stages, all of it read out of the firmware’s own JavaScript and covered by tests built from vectors taken out of the same bundle.

1. Key exchange
GetPubKey returns the router's RSA public key; TmpKey (128 chars) and HmacKey (32) are RSA-encrypted back, and the router returns a SessionId.
2. Parameter encryption
Every params JSON is AES-256-CBC encrypted with a key derived from TmpKey by PBKDF2-SHA256 — 50 iterations, 48 bytes: 32-byte key plus 16-byte IV — in OpenSSL Salted__ format.
3. HMAC signing
The plaintext params are HMAC-SHA256 signed with HmacKey, and the digest travels alongside the encrypted payload.
4. Login
GetDeviceSt returns the router’s salt; the password is PBKDF2-SHA512 hashed against it, 1024 iterations, 64 bytes. The username is nibble-shuffled against a static key — admin encodes to dc13ibej?7.

Responses come back encrypted the same way and are decrypted with TmpKey. PKCS#7 padding is applied once, by the cipher.

Reference

Commands

Everyday

cpe loginAuthenticate — prompt, or $CPE_PASSWORD
cpe statusRouter, network, signal, wifi, data usage
cpe devicesConnected devices, interface and access
cpe smsList SMS threads (--unread for unread)
cpe networkRegistration, signal list, settings
cpe firewallLevel, filters, port triggering, UPnP, ALG
cpe call <endpoint>Any of the 348, directly
cpe endpointsBrowse and search the catalog

Environment

CPE_PASSWORDSkips the interactive prompt
CPE_USERadmin
CPE_BASEhttp://192.168.1.1/jrd/webapi
CPE_TIMEOUT_MS15000
NO_COLOROff automatically when piping

Flags

--jsonCompact single-line, agent-friendly
--confirmRequired for every write or action
--searchSearch the endpoint catalog