Complete reference for every bombercat command and subcommand: purpose, flags, examples and expected output.
Control plane only. Every command that talks to a board does so over the USB-serial control protocol. No APDUs travel over serial; relayed APDUs go over WiFi/TCP to the nfcgate-server. capture streams a copy of them over the control link — see capture.
- Invocation
- Global options
- Device selection:
-d/-p - Auto-flash
- Commands
- Environment variables
- Exit codes
- Glossary
- Old anchor links (pre-restructure)
Run from the tools/ directory:
python3 bombercat.py <command> [options]The docs write it as bombercat for brevity. To get that short form, either add a shell alias:
alias bombercat='python3 /abs/path/to/tools/bombercat.py'or install shell completion, which also lets you run python bombercat.py <TAB>.
Every command and group accepts -h / --help.
Placed before the command (bombercat -v device list):
| Option | Description |
|---|---|
-v, --verbose |
Raise the log level to INFO (shows the rich logger's info lines; off by default, which is WARNING). Repeatable (-vv). |
--auto-flash / --no-auto-flash |
Allow, or forbid, a command to reflash the board when it needs firmware the board doesn't have. Unset by default — see Auto-flash for what that means. |
-h, --help |
Show help and exit. |
-v is also accepted after the command (bombercat device list -v) wherever a subcommand lists it in its own Options: — both positions mean the same thing, and the higher count wins if you somehow pass both. On the tags commands specifically, -v does more than raise the log level: it traces the raw >/< wire protocol to stderr (tx cyan, rx dim), and -vv adds an elapsed-time stamp and byte count ahead of each line:
bombercat tags read -v> ping
< +OK bombercat
bombercat tags read -vv> [ 0.000s] ( 4B) ping
< [ 0.017s] ( 13B) +OK bombercat
Traced output always goes to stderr, so --json | jq keeps working with -v on — stdout carries only the JSON. set pass … lines are redacted at any level, since that's the WiFi/relay secret in plain text. Other commands don't wire a tracer yet, so -v there is log-level only.
Every run prints the ASCII header panel with the CLI version and a random tagline before the command output. (The header is suppressed while generating shell completion.)
Every command that talks to a board takes the same two mutually-exclusive selectors:
| Option | Description |
|---|---|
-p, --port PATH |
Raw serial port (/dev/ttyACM0, COM3). Used as-is; no enumeration. |
-d, --device ID |
Stable device ID from bombercat device list (for multiple boards). |
Resolution rules (implemented in resolve_port, see protocol):
--portwins and is used verbatim.--deviceselects one of the numbered devices without handshaking the others.- With neither given and exactly one BomberCat attached, it is auto-detected by handshake.
- Zero or several attached and no selector → a clean error telling you to pass
-d/--port.
-d and -p are mutually exclusive; passing both is an error.
One board attached — no selector needed:
bombercat device info device @ /dev/ttyACM0
┌──────────┬─────────────────┐
│ port │ /dev/ttyACM0 │
│ fw │ 0.9.7 │
│ fw_name │ NFCGate │
│ state │ idle │
└──────────┴─────────────────┘
Auto-detection handshakes every candidate port; with exactly one answering
BomberCat, that's the target. Zero or several attached with no selector is a
clean error, not a guess — it tells you to pass -d/--port.
Several boards — pick one by stable ID:
bombercat device list id port usb id answered fw fw_name
1 /dev/ttyACM0 1209:005E ✓ 0.9.7 NFCGate
2 /dev/ttyACM1 1209:005E ✓ 1.1.1.0 magspoof
bombercat -d 1 relay status
bombercat -d 2 magspoof showIDs come from a stable USB identity (serial number, then topology, then port
path — see Port discovery & numbering), so -d 1
keeps pointing at the same physical board across replugs, as long as the
same set of boards stays attached. Confirm a mapping before trusting it —
blink the LED with bombercat identify -d 1.
Bypass discovery — name the port directly:
bombercat -p /dev/ttyACM0 status
bombercat status -p COM3--port is used as-is, no handshake of anything else. Useful when discovery
itself is what you're debugging, or the board's USB identity isn't recognized
(see BOMBERCAT_VID/BOMBERCAT_PID).
Two boards for a relay pair: each peer needs its own selector — see the relay cheat sheet for a full two-board session.
bombercat -d 1 relay run --role reader
bombercat -d 2 relay run --role cardFor every troubleshooting scenario around a selector (wrong board answering,
IDs renumbering, zero/several boards found), see
Wrong board answers to -d and
No BomberCat found / board not detected.
Every command that needs a specific firmware capability — tags, tags mifare, readers, magspoof, emvy, relay/config, capture — checks what's actually flashed before it opens the control link, and can fix a mismatch itself instead of just failing (docs/AUTOFLASH_PLAN.md). status and flash never do this: status's job is to report, and flash already is the explicit flash.
What triggers it. The board answers, but with firmware that doesn't provide what the command needs — e.g. tags read on a board running magspoof:
✗ `tags` needs DetectTags; this board is running magspoof.
bombercat flash DetectTags
(Exit code 3 — see Exit codes.) A board that answers nothing at all is a different, older error (nothing responded on <port>) — that's a connection problem, not a firmware one, and auto-flash never applies to it.
Whether it offers to fix it, or just reports the mismatch, follows three states — first one set wins:
--auto-flash/--no-auto-flashon the command line.BOMBERCAT_AUTO_FLASH∈always|ask|never.- Unset:
askon an interactive terminal,neverwhen stdin isn't a TTY (a script or a pipe is never reflashed without an explicit opt-in above).
| Policy | Board identified with certainty (handshake) | Board identified only by USB id (firmware unknown) |
|---|---|---|
never |
Reports the mismatch above and stops (exit 3). |
Same. |
ask |
Prompts … Flash it now?; declining reports the mismatch and stops. |
Same prompt. |
always |
Flashes without asking. | Still prompts — an unrecognized firmware might be a newer one that already works; reflashing it without asking would be the one case always shouldn't skip. |
If the firmware currently on the board is NFCGate, the mismatch message adds a specific warning before you confirm: flashing over it erases its saved WiFi/relay config, and suggests bombercat config show first if you'll need those values again. Every other firmware pairing has nothing comparable to lose.
Once consented, flashing follows the same path as bombercat flash <name> — including its bootloader-drive handling (a headless box without udisks2 gets the same actionable panel instead of a bare timeout, see flash: no RPI-RP2 drive appears) — and re-checks the board afterwards before letting the original command proceed. If that re-check can't confirm the new firmware in time, the command prints a warning and continues anyway rather than failing outright — the board usually still works even when the probe doesn't.
See tags read (or similar) reflashed my board for how to turn this off for good.
Each command group has its own detailed reference page under docs/commands/:
| Command | Description | Reference |
|---|---|---|
device |
Discover and inspect BomberCat devices over USB-serial. | device list, device info, identify |
status |
Report which firmware is flashed and what it can do. | status |
flash |
Download and flash a BomberCat firmware image. | flash, flash --list |
relay |
NFCGate relay: configure it, run it, and watch the APDU relay. | config wifi/nfcgate/show, run, stop, status, monitor |
capture |
Capture relayed APDUs to pcap (live Wireshark and/or a file). | capture start, capture stop |
tags |
NFC tag detection over the DetectTags firmware's PN7150 reader. | read, watch, scan, info |
tags mifare |
Mifare Classic auth/read/write/dump over the MifareClassic firmware. | auth, read, write, sector, keys, check, dump, restore, code, write-text |
readers |
NFC reader/terminal detection over the DetectReaders firmware. | read, watch, scan, info |
magspoof |
Magstripe emulation: play, show, watch, NFC, and multi-card store. | play, show, watch, info, nfc *, card * |
emvy |
EMVyBomberCat swiss-army: EMV read, APDU passthrough, tag/magstripe/NDEF/EMV-card emulation (auto-flashable, then gated by firmware identity). | info, read, apdu, tag, mag, cardscan, emu *, nfcinfo, reboot |
proto |
Nanopb protobuf sources for the NFCGate relay. | proto gen |
testserver |
Local nfcgate-server for relay testing (no hardware/RF). | run, verify, smoke |
completion |
Install shell tab completion for bombercat. | completion install |
setup-env |
Install the udev rules and join the dialout/plugdev groups (Linux). |
sudo bombercat setup-env |
| Variable | Used by | Meaning |
|---|---|---|
BOMBERCAT_VID / BOMBERCAT_PID |
device discovery | Declare a custom USB VID/PID for a board re-flashed with a non-stock USB identity (hex 0x1209 or decimal). Both must be set to add the pair to the match list. |
BOMBERCAT_AUTO_FLASH |
auto-flash | always | ask | never — whether a command may reflash a board running the wrong firmware. Overridden by --auto-flash/--no-auto-flash; overrides the ask-on-a-TTY/never-otherwise default. |
BOMBERCAT_FIRMWARE_REPO |
flash |
GitHub repo to pull firmware releases from (default ElectronicCats/bombercat-firmware). Point it at a fork to test against its releases. |
BOMBERCAT_FIRMWARE_CACHE |
flash |
Where downloaded images live (default ~/.bombercat/firmware). |
GITHUB_TOKEN |
flash |
Sent as a bearer token to the GitHub API, raising the 60 req/h unauthenticated limit. Optional. |
BOMBERCAT_SMOKE_VENV |
testserver smoke |
Path to the throwaway protobuf venv (default tools/.venv-smoke). |
SERVER_REPO |
testserver/fetch_server.sh |
Use an existing server clone / mirror instead of cloning. |
PORT |
testserver/run.sh |
Host port for the server (set for you by testserver run -p). |
0— success.1— a handled error: no board found, handshake failed, aset/run/capturerejected by the device, a missing file, etc. These are reported as a clean one-line message (with a leading✗), never a Python traceback. A stray traceback is a bug worth reporting.2—--strictmode incapture start: link ended with zero frames captured.3— the board is running firmware that can't do what was asked, and auto-flash didn't fix it (never/declined, or the release has no matching image).130— interrupted (Ctrl-C / EOF).
Before this file was split into docs/commands/*.md, every command lived here as one heading and its anchor was reference.md#<name>. If you (or an external doc) still have one of those bookmarked, each anchor below is kept alive and points at the new location.
Expand: old anchor → new location
| Old anchor | New location |
|---|---|
#device |
device |
#device-list |
device list |
#device-info |
device info |
#identify |
identify |
#status |
status |
#firmwares |
Firmware Capability Table |
#flash |
flash |
#flash---list |
flash --list |
#naming-the-image |
Naming the image |
#tab-completion |
Tab completion |
#flashing |
Flashing |
#the-release-cache |
The release cache |
#relay |
relay |
#relay-config |
relay config |
#relay-config-wifi |
relay config wifi |
#relay-config-nfcgate |
relay config nfcgate |
#relay-config-show |
relay config show |
#relay-run |
relay run |
#relay-stop |
relay stop |
#relay-status |
relay status |
#relay-monitor |
relay monitor |
#capture |
capture |
#capture-start |
capture start |
#capture-stop |
capture stop |
#tags |
tags |
#tags-read |
tags read |
#tags-watch |
tags watch |
#tags-scan |
tags scan |
#tags-info |
tags info |
#tags-mifare |
tags mifare (auth/read/write/sector/keys) |
#tags-mifare-check |
tags mifare check |
#tags-mifare-dump |
tags mifare dump |
#tags-mifare-restore |
tags mifare restore |
#tags-mifare-code |
tags mifare code |
#tags-mifare-write-text |
tags mifare write-text |
#readers |
readers |
#readers-read |
readers read |
#readers-watch |
readers watch |
#readers-scan |
readers scan |
#readers-info |
readers info |
#dev-tooling |
proto and testserver |
#proto |
proto |
#proto-gen |
proto gen |
#testserver |
testserver |
#testserver-run |
testserver run |
#testserver-smoke |
testserver smoke |
#completion |
completion |
#completion-install |
completion install |