When Curb Inc. (later "Powered By Elevation") shut down their cloud services in February 2026, every Curb energy monitor became a brick. This guide documents how to regain full root access to your device and set up local data collection — no cloud required.
- Root shell access via serial console and SSH
- All Lua application source code (sampler, streamer, load-controller, etc.)
- GPG firmware decryption passphrase (universal across all devices)
- Hub configuration with sensor calibration data
- Path to local data pipeline (Home Assistant, InfluxDB, Grafana, MQTT)
These are the same on every Curb device:
| Secret | Value |
|---|---|
| GPG firmware passphrase | uf0ZZkz6n@*k$XeoMm@cA%jn!Cp9M0d8 |
| GPG signing key ID | E13C6056 ("Curb Sign") |
| API auth | Basic auth with serialNumber:secret (secret is per-device, in EEPROM) |
| Update mechanism | wget --no-check-certificate — accepts any cert, falls back to HTTP |
The firmware update mechanism has no security:
- Uses
wget --no-check-certificate(accepts self-signed certs) - Falls back to plain HTTP
- The only "protection" is GPG encryption — and the passphrase is above
Steps:
- Redirect
updates.energycurb.comto your server via your DNS (Pi-hole, router, etc.) - Create a
setup.shthat changes the root password - Encrypt it with the GPG passphrase:
gpg --symmetric --cipher-algo AES256 --passphrase 'uf0ZZkz6n@*k$XeoMm@cA%jn!Cp9M0d8' ... - Serve the encrypted payload and matching MD5 checksum via HTTPS (any cert) or HTTP
- The device checks for updates hourly — it will download, decrypt, and execute your script as root
See the sd_payload/ directory in this repo for ready-made payloads.
Important: You also need to serve a valid os.tar.gz.gpg and os.tar.gz.gpg.md5sum — the update script checks the OS first and exits on failure. Serve a dummy OS update that decrypts to a harmless update-os.sh.
If you can get a Ka-Ro TX28 Starterkit 5 (or similar SODIMM dev board):
- Remove the TX28 SODIMM module from the Curb carrier board
- Insert into the dev board with boot mode jumper set to USB
- Load U-Boot via
imx_usb/ UUU over USB - Dump the NAND flash and extract all secrets
- Modify the rootfs and write it back
The device mounts an SD card at /data/sd. While no scripts auto-run from SD, you can:
- Pull the SD card from the Curb
- Place files that will be picked up by the update mechanism
- The
update.shscript uses/data/sd/tmp/as a working directory
┌─────────────────────────────────────────────┐
│ Curb Energy Monitor │
│ │
│ SoC: i.MX28 (ARM926EJ-S @ 454MHz) │
│ Module: Ka-Ro TX28-41x0 (SODIMM) │
│ RAM: 64MB DDR2 │
│ NAND: 128MB (Micron MT29F1G08ABAEAWP) │
│ OS: Linux 3.16.0-karo, BusyBox, Buildroot │
│ Network: QCA7000 HomePlug AV (powerline) │
│ │
│ NAND Layout: │
│ 0x000000 - 0x300000 U-Boot (3MB) │
│ 0x300000 - 0x380000 Environment (512K) │
│ 0x380000 - 0x400000 Environment2 (512K) │
│ 0x400000 - 0x800000 Linux kernel 1 (4MB) │
│ 0x780000 - actual kernel load offset │
│ 0x800000 - 0xC00000 Linux kernel 2 (4MB) │
│ 0xC00000 - 0xD80000 rootfs1 (UBI) │
│ 0xD80000 - 0x2D80000 rootfs2 (UBI/UBIFS) │
│ 0x2D80000 - end userfs (UBI/UBIFS) │
│ │
│ Active boot: kernel2 + rootfs2 (A/B) │
└─────────────────────────────────────────────┘
The Curb reads energy from ADE chips via SPI, processes it in Lua, and streams to the cloud:
ADE Chips (SPI) → sampler.lua → Message Queue → streamer.lua → Cloud API
→ load-controller.lua → Aggregation
Samples are compressed MessagePack with zlib and a CRC32 trailer:
payload → MessagePack.pack() → zlib.compress() → append CRC32 → HTTP POST
Each sample contains:
{
"t": 1774327936, // Unix timestamp
"h": "xxxxxxxx", // Hub serial number
"g": [ // Groups (one per ADE chip pair)
{
"v": 121.5, // Voltage (V)
"f": 60.0, // Frequency (Hz)
"c": [ // Channels (one per CT clamp)
{
"i": 0.5, // Current (A)
"w": 60.2, // Power (W)
"var": 1.2, // Reactive power (VAR)
"p": 0.99 // Power factor
}
]
}
]
}All API calls use HTTP Basic Auth:
Authorization: Basic base64(serialNumber:secret)
serialNumber: From EEPROM (e.g.,xxxxxxxx)secret: Per-device, stored in EEPROM (e.g.,your-device-secret-from-eeprom)
| Endpoint | Method | Purpose |
|---|---|---|
/v3/samples/{serial} |
POST | Energy sample data (compressed MessagePack) |
/v3/diagnostics/{serial} |
POST | Device diagnostics (compressed MessagePack) |
/v3/hub_config/{serial} |
GET | Download hub configuration (JSON) |
/v3/messages/{serial} |
GET/POST | Hub messaging (JSON) |
The firmware already has influx.lua that can post to InfluxDB line protocol! It was hardcoded to http://138.68.19.36:8086/write?db=curb&precision=s (a Curb development server). You can modify this to point to your local InfluxDB.
Edit /data/hub-config.json to point endpoints at your local server:
{
"endpoints": {
"samples": "http://your-server:8080/v3/samples",
"messages": "http://your-server:8080/v3/messages",
"hub_config": "http://your-server:8080/v3/hub_config",
"diagnostics": "http://your-server:8080/v3/diagnostics"
}
}Build a server that:
- Accepts the compressed MessagePack POST
- Decompresses (zlib) and unpacks (MessagePack)
- Stores in InfluxDB/TimescaleDB
- Serves a static config on GET
The HTTP status page at port 80 already exposes live power data. Use the existing Curb-to-MQTT project to scrape it.
Edit /data/lamarr/influx.lua and change the URL to your local InfluxDB instance. Enable it in the streamer configuration.
| Path | Purpose |
|---|---|
/data/hub-config.json |
Hub configuration with sensor calibration |
/data/lamarr/*.lua |
All application Lua scripts |
/root/.gnupg/passphrase |
GPG passphrase for firmware updates |
/etc/shadow |
Password hashes |
/etc/init.d/S* |
Init scripts (boot sequence) |
/etc/hm.conf |
Health monitor / process supervisor config |
/usr/local/sbin/update.sh |
Firmware update script |
/data/sd/ |
SD card mount point (SQLite databases) |
EEPROM (i2c-0/0-0050) |
JSON: serialNumber, secret, buildCode, hardwareVersion, modelNumber |
After changing the root password, SSH is available:
ssh -o PubkeyAcceptedAlgorithms=+ssh-rsa root@<curb-ip>The old Dropbear only supports legacy ssh-rsa. Add to ~/.ssh/config:
Host curb
HostName <curb-ip>
User root
PubkeyAcceptedAlgorithms +ssh-rsa
- Serial console: J6 header, FTDI 3.3V, 115200 8N1
- Network: via HomePlug AV powerline (QCA7000 on SPI)
- U-Boot: Boot delay overridden by Curb — cannot interrupt without hardware mod
- JTAG: J9 header (1.27mm pitch), no disable fuse — works with OpenOCD
- USB boot: SODIMM pin 8 (BOOTMODE) LOW = USB/UART recovery
Reverse engineered April 2026. This work is for educational purposes and to help Curb device owners recover functionality from their own hardware after the manufacturer abandoned the product.