NTAG5, ICODE DNA and SLIX2 all share E0 04 and answer READ_SIGNATURE, so name_iso15
now splits them with an addressed, retried READ_CONFIG (0xC0) probe:
- session register 0xA0 (STATUS_REG) succeeds only on NTAG5 -> "NTAG 5"
- 0xA0 errors but config block 0x00 succeeds (DNA has config mem) -> "ICODE DNA"
- neither (SLIX2 has no READ_CONFIG) -> the 0xBD signature step -> "ICODE SLIX2"
- plain SLIX answers neither -> "ICODE SLIX"
Hardware-verified against a real NTAG5 (VivoKey VK Thermo, UID E0 04 01 58, 0xA0 ->
00 01400000) and a real ICODE DNA (UID E0 04 01 18, 0xA0 -> 01 0F error, 0x00 -> 00 ...).
DNA was previously mislabeled "ICODE SLIX2". catalog_for routes DNA to the SLIX2 catalog
for now (a dedicated DNA catalog with READ_CONFIG + AES is a follow-up). Adds a mocked
unit test covering all four branches from the real captures.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_NTAG5 = the SLIX2 command set minus STAY_QUIET_PERSISTENT (which NTAG5 rejects per the
NTP5210/NTA5332 tables) plus the NTAG5-specific NXP customs: READ_CONFIG (0xC0),
WRITE_CONFIG (0xC1, session registers 0xA0 STATUS_REG / 0xA1 CONFIG_REG), PICK_RANDOM_UID
(0xC2), and READ_SRAM (0xD2) / WRITE_SRAM (0xD3) for the Link/Boost variants. Sourced
from transponders/hf/iso15693/nxp/ntag5_*, routed by catalog_for, verified live.
Not yet wired: auto-identify of NTAG5. NTAG5 and ICODE DNA share UID E0 04 01 18, so
they can't be told apart by UID — routing needs a functional probe (READ_CONFIG of a
session register), which needs a brief NTAG5 tap to nail without risking DNA
misclassification. AES/ISO-29167-10 commands (0x35/0x39/0x3A) noted as a follow-up.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_inv_read_frame builds the complete ICODE inventory-read request body:
[AFI] | mask_len(bits) | mask value | first_block | count-1. INVENTORY_READ and
FAST_INVENTORY_READ gain optional mask_len/mask (selective anticollision, ceil(bits/8)
value bytes) and afi (filter byte + the 0x10 inventory flag, flags -> 0x36). Mask-less
stays the default, so existing frames are unchanged.
With this the ISO 15693 support is datasheet-complete end to end: the full request-flags
byte, both SLIX and SLIX2 command sets, and the inventory-read body. Verified live
(mask-less / 16-bit mask / AFI forms build + decode + round-trip) and unit-tested.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The header builder previously set only high-rate, inventory+1slot, addressed and
option — F15_PROTO_EXT and F15_SELECT were even defined but unused, so several valid
request-flag bits could not be expressed. Complete it: sub-carrier (0x01), protocol-
extension (0x08), Select (0x10), 16-slot inventory (Nb_slots=0), and the inventory AFI
flag (0x10) with its AFI byte inserted before the params. Bits 5-6 are now branched
correctly by mode (Select/Address for non-inventory, AFI/Nb_slots for inventory), per
ISO 15693-3 §7.3.1. iso15_flags_decode decodes every bit in both modes, and the byte-0
flag landmarks gain select (0x12) and inventory+AFI (0x36).
Existing frames are unchanged (verified by the preserved 0x02/0x22/0x42/0x26 tests);
adds coverage for every newly-settable bit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the ICODE SLIX/SLIX2 datasheet command set. These fuse an inventory round
with a block read; unlike every other catalog command they need the inventory bit set
in the request flags. Added an `inventory=` option to iso15_frame (sets inventory +
single-slot, 0x26 with high rate) and both commands to _SLIX (inherited by _SLIX2),
with the mask-less body mask_len 0 | first_block | count-1 (matching the tag model's
_handle_inventory_read: first_block at data[4], num_blocks at data[5]+1).
Verified live (completer names both, opcode 0xA0/0xA1 map back, entry round-trips) and
unit-tested. Mask-based inventory (AFI/mask value) left as a future refinement.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SLIX2 is the SLIX command set plus three SLIX2-only commands and a larger 80-block
memory: READ_SIGNATURE (0xBD, the ECC originality signature identify already probes),
PASSWORD_PROTECTION_64BIT (0xBB), STAY_QUIET_PERSISTENT (0xBC). Built by reusing the
SLIX catalog's commands, so it stays a strict superset; catalog_for routes an identified
"ICODE SLIX2" to it (SLIX2 checked before the SLIX/ICODE fallback).
Verified live through the real completer + PromptSession: command-name completion
surfaces the SLIX2 commands, raw-opcode completion maps 0xBD/0xBB/0xBC back to them, and
entry accepts end to end. Unit test covers the delta + dispatch.
Follow-ups noted in docs/rawcli-todo.md: NTAG5 (needs a finer identify probe first, since
it shares E0 04 with SLIX), and INVENTORY_READ/FAST_INVENTORY_READ for the SLIX catalogs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the last loose end from the byte-line autocomplete fix: the entry-mode toggle
correctly flips the breadcrumb prefix (0x <-> 0b) and requests a redraw. Adds
test_toggle_updates_breadcrumb_prefix and updates the roadmap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The digit key handler applied each keystroke as `buf.document = Document(...)`.
Assigning the buffer document resets prompt_toolkit's complete_state and never
re-fires completion, so the live autocomplete menu never appeared during raw byte
entry -- in BOTH hex and binary (only command-name completion worked, since that
path already used insert_text).
type_digit only ever appends the digit (optionally after an auto-space) or drops it,
so the change is always a pure suffix. Apply it with buf.insert_text, which triggers
completion exactly like an ordinary keystroke.
Verified by driving the real key binding: the completion menu now populates on every
digit in hex and binary (before: complete_state None, start_completion never called).
Adds a regression test asserting the handler routes through insert_text; updates the
rawcli roadmap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Captures the named-transponder MVP (NTAG21x/UL EV1 done; MIFARE Classic next; then ICODE
SLIX/SLIX2/NTAG5), the UX contract (all hints in the completion dropdown, hex + binary), the
binary-mode autocomplete bug (entry._regroup_after resetting buf.document cancels the menu), and
the 15693 header-builder + per-chip-identify foundations.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Exposes pm3py as a pyws workspace, discovered via the pyws.plugins entry point:
- ReaderResource: connects/disconnects a Proxmark3 as `reader`; a missing device is
non-fatal (reports disconnected)
- namespace injection of the transponder/sim classes, SimSession and Proxmark3
- effect declarations so autoreplay/replay withhold writes/field/sim/destructive while
reads and pure model edits replay
Hardware-free tests via AsyncMock. The live `sim` resource is P2.
See docs/plans/2026-07-05-pm3py-pyws-plugin-plan.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README: rewrite Install (from source, submodules, deps, Pi/aarch64); new
'Flashing firmware (pm3flash)' section — CLI usage, --build, image resolution,
cross-compile + PLATFORM note, Raspberry Pi bring-up, programmatic API; point
Firmware Compatibility at it; add a License section (MIT).
- CLAUDE.md: flash.py/_firmware.py/flash_cli.py in the structure; a 'Firmware
flashing' section with the maintainer gotchas (OLD frame, fullimage-only,
page-merge, platform != is_rdv4, the pin).
- roadmap: new 2026-07-05-firmware-flasher plan doc + a Firmware-flashing row.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add docs/plans/README.md as the roadmap index cataloguing all design/plan docs.
- Add the custom ISO14443-A command handling design + plan (L3 NTAG I2C
SECTOR_SELECT / cross-sector reads + native auth; L4 static APDUs + WTX relay),
produced from a multi-agent design workflow.
- Backfill previously-uncommitted plan docs (trace-formatter, ndef-trace-decode,
live-sniff, firmware-upstream-rebase, 14a-live-trace) and NTAG5_SECURITY.md.
- Sync CLAUDE.md package structure with the committed transponder models.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phases 2-4 of package refactor. Creates the full directory tree with
__init__.py files per the design doc. Actual code migration from the
sim worktree is a separate effort — models have deep inheritance chains
and auth mixins that need base class extraction first.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 1 of package refactor. Moves all source modules into pm3py/core/
with file renames (hf_14a→hf_iso14a, hf_15→hf_iso15, hf_mf→hf_mfc)
and client attribute normalization (hf.a14→hf.iso14a, hf.mf→hf.mfc).
pm3py/__init__.py re-exports from core for backward compat.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add table compiler match pattern rules to main CLAUDE.md (was only in worktree)
- Update sim framework description (750+ tests, add DNA/NTAG5 to IC list)
- Update "planned" → "in progress" for Python-driven card sim
- Fix firmware patch size: ~320 lines (was inconsistently ~340 on line 49)
4 tasks: update TableEntry to 120-byte format, compile_nxp_icode,
compile_slix2, wire auto-compile into SimSession.start_15693.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tested retry-based relay and spin-wait approaches. Relay works
mechanically via retry but is unreliable (reader-dependent).
Spin-wait fails due to USB stack latency. Table is the only
viable mechanism for 15693 custom commands.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers: sim_table entry structure with EML actions and group-based
state machine, AES-CMAC for TAM/MAM, native handler access control,
14443-A integration, timing budget analysis.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>