Xteink X4 & X3

Beyond the default. Your reader, your way.

Community and official firmwares that change how you read—different interfaces, features, and workflows. Discover alternatives that fit you.

Ruby

Ruby

vRuby_v0.1.3 X4 · X3
5 stars 1 forks Last release: Jul 2026

This firmware is similar to the Pwnagotchi for the Raspberry Pi but utilizes the X3 and X4 hardware and turns the device into a stealthy pocket sized, long-battery-life passive RF signal analyzer

https://github.com/0xKnowles/Ruby

How it works
-Capture: WifiSniffer puts the ESP32-C3's radio into 802.11 monitor mode and hops across channels, extracting beacon/probe-request SSIDs, MAC addresses, and — when it hears one — the presence of a WPA 4-way handshake and which message number it is. The radio holds its current channel for a few seconds whenever it sees part of a handshake, instead of hopping away and missing the rest of it mid-exchange. BleScanner runs a passive BLE scan (it never sends a SCAN_REQ) and records advertised MACs, names, and manufacturer-data length. Neither path associates, pairs, or connects to anything, and BLE scanning never transmits.
-Catalog: Every observation is deduplicated by SignalCatalog against a bounded in-RAM hash set. The first time a given MAC/role is seen, that's a "unique" event: it increments a lifetime counter and nudges the creature's expression.
-Log: Independently of deduplication, every observation is encrypted (AES-256-GCM via mbedtls, one record per packet) and appended to a daily log file on the SD card. The AES key is derived from a random seed generated on first boot plus this chip's eFuse MAC address and lives only in internal NVS flash — pulling the SD card gets you ciphertext with no key beside it. Note this means every sighting is logged, not just first-ever ones — a busy RF environment (a handful of phones re-advertising over BLE every few hundred milliseconds, a dozen APs beaconing every ~100ms) can write a meaningful volume of records per hour. That's intentional for a capture tool, but budget SD card space and export/rotate accordingly.
-React: The creature's shape (RubyManager) never changes. See repo for what expressions mean, when it shows, and how the separate Level 1-5 badge tracks lifetime EXP alongside it without touching the art.
-Display: The dashboard is mostly static: a header, Ruby's box with its live Mood readout, boxed RF stat panels, a footer. The creature lives in a fixed corner box that redraws on its own ~1.2s cadence using HalDisplay::FAST_REFRESH without ever touching (or clearScreen()-ing) any pixel outside that box

Dashboard: the creature, a Level 1-5 badge beside its "RUBY" name chip and a matching EXP progress bar in the header next to the battery badge, its current Mood, an "-- ACTIVE --"/"-- PAUSED --" tag under it. Beside the creature's box, a RECENT DEVICES card shows a handful of the latest observations in full detail (MAC, signal bars + RSSI + time-ago, device name), and a SIGNAL HISTORY card next to it stacks two sections. Both cards extend down the full height of the box. HANDSHAKES is a plain "N ago" text list of recent captures, newest first, persisted across reboots (unlike Recent Devices' live, RAM-only feed) so it stays genuinely historical rather than resetting to empty on every power-on — handshakes are too rare to track meaningfully via a feed any ordinary AP/client/BLE traffic cycles through in moments anyway. SIGNAL is the same recent-observations feed's RSSI as a live bar chart. Below that, SIGNALS (unique AP/client/BLE/handshake counts, plus a "Last Hand Shook:" row showing the BSSID of the most recent handshake capture, "none yet" until the first one) and CAPTURE STATUS (a single "Capture" row naming whichever of WiFi monitor/BLE scan is actually active, log size, this session's uptime, and lifetime Total Uptime across every boot) sit side by side. A bubble along the bottom edge of Ruby's box, tailing up into her face, shows a quirky, hacker-flavored one-liner reflecting Ruby's current mood, briefly replaced by a speech bubble reacting to a new device, a captured handshake, a level up, or a nearby deauth alert. A banner briefly announces "DEAUTH NEARBY: network", "LEVEL UP → Lv.N", or "HANDSHAKE CAPTURED: network" (in that priority order) when any of them happens, falling back to the raw MAC address if it hasn't advertised a beacon recently enough to still be cached. Confirm → Settings, Left → toggle Pause (same button pauses and resumes WiFi/BLE capture — no screen change, and it stops DeauthEngine too since that only ever fires from the observation stream capture produces), Right → Export/Maintenance, Up → Recent Devices, Down → Log Viewer, Back → force a full ghost-clearing refresh.
Settings: toggle WiFi/BLE capture, adjust WiFi channel dwell time, lock WiFi monitor to a single channel instead of hopping all 13, set the ghost-clear refresh interval, choose what a short Power-button tap does (Refresh / Screenshot / Pause), turn on raw handshake capture (off by default), manage the whitelist/blacklist (opens a live network scan to add/remove targets), reveal the log's AES key, wipe the log, or reset the Dashboard's SIGNALS counters (and Ruby's Level/EXP, handshake-capture history, and lifetime Total Uptime, all kept in sync with it) back to zero.
Export/Maintenance: how to pull captures off the SD card, plus the current session's record count and (when any exist) raw-capture file stats, a PMKID-capable-capture count, and deauth burst/frame counters.
Recent Devices: a live, RAM-only feed of the last 16 WiFi/BLE observations (type, MAC, RSSI, SSID/name, time since seen), newest first, read-only, falling back to a vendor-name lookup for entries with no SSID/name of their own. Each entry's RSSI reading is paired with a small 4-bar signal-strength icon for an at-a-glance read on how strong the observation was, without parsing the dBm number. Laid out as a dense multi-column grid so all 16 entries always fit on screen at once. This is separate from both SignalCatalog and the encrypted log. Nothing shown here is persisted; it's lost on reboot along with the rest of RAM. To manage the active-deauth target lists, use Settings' Whitelist/Blacklist rows instead.
Log Viewer browses the encrypted capture log on the device itself, no PC required. Switch between daily log files, Up/Down page through records within a file — around 27 at a time on this panel, newest first.

Raw handshake capture (crackable .pcap)
Everything above is deliberately metadata-only: the encrypted log records that a handshake happened and which message numbers were seen, never the key material itself. Raw handshake capture is a separate, opt-in capability for a different purpose.
For auditing the password strength of networks you own, offline, with tools like hashcat or hcxpcapngtool can be used with the captured .pcap file on the SD card.
Off by default. Turn it on at Settings → Raw handshake capture. The Settings screen shows a warning explaining the tradeoff below before you flip it on.
When enabled, it captures the WPA 4-way-handshake frames verbatim (ANonce/SNonce/MIC included) plus the SSID-bearing beacon for each network involved, and writes them to a standard libpcap file at /.ruby/pcap/YYYYMMDD.pcap — the same format hashcat/hcxpcapngtool already expect, so a captured file can be fed straight in to attempt a crack.
These files are plaintext, unlike everything else Ruby writes to SD. A cracking tool needs the exact bytes that went over the air, so this one path can't be routed through the AES-256-GCM encrypted log the way every other observation is. MaintenanceActivity calls this out explicitly whenever raw-capture files exist.
The Export screen also shows a PMKID-capable count. How many captured message-1 frames carry the PMKID key-data element, each one independently crackable via hashcat's -m 22000 PMKID mode without needing the rest of the handshake at all.

Screenshots

Page updated: Jul 21, 2026


More firmwares to discover

Other alternatives for your Xteink reader.