Offline JavaScript / Node.js SDK for TigerTag RFID material identification.
TigerTag is the world's most widely deployed open-source RFID protocol for manufacturing material identification — with over 2 million chips deployed worldwide. Adopted by major brands including eSun, Rosa3D, Sunlu, R3D, Landu and many more. Currently covers filament and resin. Designed to extend to any physical material (sheet goods, wood, PMMA, metals, composites…). All material data is stored directly on the NTAG chip — 100% offline.
TigerTag is the #1 RFID material identification protocol in the 3D printing industry and the only open-source standard with broad manufacturer adoption at scale.
| Metric | Value |
|---|---|
| Chips deployed worldwide | 2 000 000+ |
| Filament / resin brands | eSun · Rosa3D · Sunlu · R3D · Landu · and more |
| Connected printers & slicers | Snapmaker · Bambu Lab · FlashForge · Elegoo · Creality · and more coming |
| Exclusive integrations | HueForge (Transmission Distance) · TD1s by Ajax (filament manager) |
| Cost for end users | 100% free — protocol, SDK, Studio Manager, mobile apps |
| Protocol status | Open source (GPLv3) — free to implement for manufacturers |
| Hardware | Tiger Scale (DIY ~30 € open-source) · TigerTag Pod (read/write desktop + mobile) |
| Ecosystem maturity | Desktop app · Mobile app · Pod · DIY scale · Firebase · Python SDK · JS SDK |
| Chip compatibility | NTAG213 · NTAG215 · NTAG216 · any ISO 14443-3 compatible |
1 — Proof of authenticity (ECDSA-P256)
TigerTag is the only material RFID protocol to offer cryptographic proof of authenticity. Each signed chip carries an ECDSA-P256 signature that binds the chip UID to the product data. Any reader — including this SDK — can verify the signature fully offline, with no server call:
const result = tag.verify(); // ✅ VALID — chip is genuine and untampered
// ❌ INVALID — data has been modified or chip is cloned
// ⬜ NOT SIGNED — unsigned Maker tag (verification not required)No other RFID material protocol provides on-chip cryptographic authentication at this level.
2 — Chips reusable forever
TigerTag chips are never write-locked. Once a spool is finished, the chip gets a second life:
- Erase and reprogram as a fresh TigerTag for a new spool:
TigerTag.erase() - Reprogram with any NFC / NDEF standard for a completely different use case
- Use as a plain NTAG tag in any NFC-capable application
Zero electronic waste. The chip is a permanent, reusable asset — not single-use packaging. No competing protocol offers this combination of authentication and unlimited reusability.
3 — Remote update by the manufacturer (TigerTag+)
TigerTag+ is the only material RFID protocol with remote over-the-air update capability for manufacturers. When a brand publishes improved print settings or corrected temperatures to the TigerTag cloud API, every chip already deployed can receive those updates:
// Fetch latest manufacturer data and apply to chip:
const [patchedTag, changes] = await tag.patchFromApi();
console.log(`${changes.length} field(s) updated by manufacturer`);
// Or inspect what changed before applying:
const diffs = await tag.diffApi();
for (const d of diffs) {
console.log(`${d.field}: chip=${d.chipValue} → manufacturer=${d.apiValue}`);
}4 — Native HueForge integration
TigerTag is the only RFID protocol natively integrated with HueForge. The TD (Transmission
Distance) value is stored directly on the chip (tdValue property) and read by HueForge
without any manual entry. It is also the only protocol supported by TD1s by Ajax,
the open-source filament manager.
| Device | What it does | Price |
|---|---|---|
| Tiger Scale | Open-source DIY ESP32 smart scale — reads the TigerTag, weighs the spool, updates measureAvailable in real time |
~30 € in parts |
| TigerTag Pod | Plug-and-play NFC reader/writer — read and write chips from your desktop (via TigerTag Studio Manager) or from your phone (via TigerTag RFID Connect on iOS and Android) | — |
Everything is free for end users: the protocol, this SDK, TigerTag Studio Manager, the mobile apps, and all community tools. No subscription, no lock-in.
No NFC hardware required — explore the full SDK output directly in your browser.
# Start the dev server
node tools/server.js 7432
# Open in browser
open http://localhost:7432/tools/playground.htmlOr via npm:
npm run playgroundThe playground has five panels:
| Panel | Purpose |
|---|---|
| Sidebar (left) | Build a TigerTag / TigerTag+ / Init tag: choose version, brand, material, colors, print settings. Generate button pinned at the bottom — always visible. |
| Center | Protocol preview cards: Protocol, Material, Colors, Print Settings, Quantity, Traceability, Cloud API |
| SDK Input (collapsible) | Shows the exact TigerTag.create({...}) call for the current tag — the write side. Opens automatically when you click 🔥 Burn. Payload is generated server-side via POST /api/build (SDK is always the authoritative serializer — browser never computes chip bytes). |
| SDK Output (collapsible) | Shows pretty(), describe(), verify(), toRawDict(), toDict(), rawApi(), diffApi() — the read side. Opens automatically on Generate / NFC scan / Import. |
| Raw Hex (modal) | 🔬 Raw Read — reads all 144 bytes (pages 4–39) from every connected reader and shows a structured hex table: page (decimal), offset (bytes), page (hex: 0x04–0x27), B0–B3, u32 BE, annotated field label (value) field_name · …. Signature pages dimmed. Multiple readers shown side-by-side in collapsible panels. Copy hex button outputs one 0x04 B0 B1 B2 B3 line per page with ✓ Copied! feedback. |
SDK Input / Output and Raw Hex reader panels are all collapsible via their adjacent rails.
Available Qty auto-link — the Available Qty field automatically mirrors Initial Qty until you edit it manually. On NFC scan, preset load, or API fetch the link is restored to the actual values.
Place a chip on your reader and the playground auto-populates instantly — no manual action needed.
# Enable live reader support (one-time install)
npm install ws nfc-pcsc
# Then launch as usual — readers detected automatically
npm run playgroundMultiple simultaneous USB readers supported. Each reader gets its own status badge in the
header (● green = connected, ● orange pulse = reading card) and its own Raw Hex panel.
🔥 Burn — once a chip is on a reader, click Burn to write the current payload to all connected readers that hold a card. Writes pages 4–23 (80 bytes) sequentially. The SDK Input panel opens automatically so you can see exactly what was written.
🔬 Raw Read — reads all 144 bytes from every card-holding reader and displays the raw chip memory as a structured hex table with field annotations. Useful for debugging and verifying burns.
Server endpoints:
| Method | Path | Response |
|---|---|---|
GET |
/api/version |
{ version: string } |
POST |
/api/parse |
{ pretty, describe, verify, raw_dict, dict } — full SDK parse of a hex payload |
POST |
/api/build |
{ payload: hex } — TigerTag.create(kwargs).toBytes() — SDK-authoritative payload |
POST |
/api/diff |
{ api_data, diffs, in_sync, error } — chip vs cloud diff |
npm install tigertagZero configuration. Zero network required on first run. Bundled reference databases ship with
the package. Requires Node.js 18+ (uses built-in crypto and fetch — no extra deps).
const { TigerTag } = require('tigertag');
const tag = TigerTag.fromPages(uid, payload); // from your NFC SDK
console.log(tag.pretty()); // human-readable summary
console.log(String(tag.verify())); // ✅ VALID / ⬜ NOT SIGNED / ❌ INVALID
console.log(tag.toDict()); // JSON-ready objectWorks immediately after npm install tigertag. No setup required.
TigerTag is an open-source RFID protocol that stores manufacturing material data directly on NFC chips (NTAG213 / NTAG215 / NTAG216, ISO 14443-3 compatible). No cloud dependency for reading — all data lives on the chip.
Tag types:
| Tag type | idProduct | Offline | Cloud |
|---|---|---|---|
| TigerTag (Maker) | 0xFFFFFFFF |
✅ full data on chip | — |
| TigerTag Init | 0x00000000 |
✅ blank template | — |
| TigerTag+ | numeric ID | ✅ full data on chip | ✅ API for live updates |
Protocol spec: github.com/TigerTag-Project/TigerTag-RFID-Guide
| Method | Input | When to use |
|---|---|---|
TigerTag.fromPages(uid, payload) |
7-byte UID + 80 or 144 bytes | NFC SDK integration (recommended) |
TigerTag.fromDump(data) |
80 / 144 / 180 bytes | Binary dumps, ACR122U raw read |
TigerTag.fromFile(path) |
path to .bin file |
Testing, offline batch processing |
TigerTag.fromRawDict(raw) |
toRawDict() output (snake_case) |
Reconstruct from stored raw dict |
TigerTag.fromCloudDoc(doc) |
Firestore cloud document | Write pipeline: cloud → chip |
fromPages is the primary constructor for production use. NFC SDKs always provide the
UID as a separate property — pass it directly for full signature verification.
fromDump with 180 bytes (full chip dump including system pages) auto-extracts the 7-byte UID.
NFC SDKs always expose the UID as a dedicated property. Pages 0–3 (system pages: lock bytes, capability container) are never part of the user data payload.
| Payload | Pages | UID | Verifiable |
|---|---|---|---|
| 144 bytes | 0x04–0x27 (user data + signature) | Required (7 bytes) | ✅ Yes |
| 80 bytes | 0x04–0x17 (user data, no signature) | Required (7 bytes) | N/A |
| Dump | Content | UID | Verifiable |
|---|---|---|---|
| 180 bytes | Full chip (pages 0–44, includes system pages) | Auto-extracted | ✅ Yes |
| 144 bytes | Partial dump (user data + signature, no system pages) | Not available | ❌ No |
| 80 bytes | User data only | Not available | N/A |
// Read
tag.pretty(db, sigResult) // → string human-readable summary
// Quantity section shows "(= 750 g)" hint when unit ≠ base
tag.describe(db) // → string LLM-friendly paragraph
// Quantity sentence includes "— 750 g available, 1000 g total"
tag.toDict(db) // → object JSON-serializable, all labels resolved
// .measure includes measure_gr/ml/mm/mm2 + measure_available_*
tag.toRawDict() // → object raw protocol fields, no resolution
// includes measure_gr/ml/mm/mm2 + measure_available_* (base-unit)
// color_r2/g2/b2 and color_r3/g3/b3 are zeroed for inactive slots
// num_colors — active color slot count from aspect DB (1/2/3)
// color_list — string[] of #RRGGBB for active slots only
tag.toBytes(includeSignature = false) // → Buffer re-serialize to chip bytes
tag.validate() // → string[] sanity check — list of warnings
tag.verify(db) // → SignatureResult
// Write (immutable — all return a new TigerTag)
TigerTag.create(fields) // → TigerTag build from scratch
TigerTag.asInit(uid) // → TigerTag blank Init tag
TigerTag.erase() // → Buffer(80) zero bytes — write to chip to wipe
TigerTag.fromRawDict(raw) // → TigerTag from toRawDict() snake_case object
TigerTag.fromCloudDoc(doc) // → TigerTag from Firestore cloud doc (data1-data7, TD)
tag.patch(fields) // → TigerTag surgical camelCase field update
tag.patchFromRawDict(raw) // → TigerTag surgical snake_case field update
// Cloud (TigerTag+ only — uses built-in fetch, Node.js 18+)
tag.rawApi(timeout) // → Promise<object|null> fetch live product data
tag.diffApi(apiData, db) // → Promise<ApiDiff[]> compare chip vs API
tag.patchFromApi(apiData, db) // → Promise<[TigerTag, ApiDiff[]]> apply API values
tag.syncDb(dbPath, force) // → Promise<string[]> update reference databasestag.isMaker // true if idProduct === 0xFFFFFFFF
tag.isInit // true if idProduct === 0x00000000
tag.isPlus // true if idProduct is a valid cloud ID
tag.isSigned // true if signature bytes are non-zero
tag.uidHex // "04AABBCCDDEE11" or null
tag.color1Hex // "#FF3232"
tag.tdValue // 12.5 (HueForge Transmission Distance)
tag.manufacturingDate // Date (UTC)
tag.stockPercent // 75.0 or null
tag.productPageUrl // "https://tigertag.io/products/..." or null
tag.apiUrl // "https://api.tigertag.io/..." or null
tag.imgUrls // { icon16, icon32, thumbnail, small, medium, large, original }
// CDN image URLs — TigerTag+ only; null for Maker / Init tagsconst { TigerTag } = require('tigertag');
// Build a new tag from scratch
const tag = TigerTag.create({
uid: Buffer.from('04A1B2C3D4E5F6', 'hex'),
idMaterial: 38219, // PLA
idBrand: 19961, // Rosa3D
nozzleTempMin: 195,
nozzleTempMax: 230,
color1R: 255, color1G: 0, color1B: 0, color1A: 255,
measure: 1000, idUnit: 21,
// measureAvailable: 750, // optional — partial spool; defaults to measure (full)
});
// Blank TigerTag Init chip (ready for programming)
const initTag = TigerTag.asInit(Buffer.from('04A1B2C3D4E5F6', 'hex'));
// Erase a chip — write the returned 80 bytes to the NFC chip
const blankBytes = TigerTag.erase();
// Immutable surgical update — returns a new TigerTag, original unchanged
const patched = tag.patch({ nozzleTempMin: 200, dryTemp: 55 });
// TigerTag+ cloud sync
const apiData = await tag.rawApi(); // fetch live product data
const diffs = await tag.diffApi(apiData); // what differs chip vs cloud?
const [patchedTag, applied] = await tag.patchFromApi(); // apply all cloud values
console.log(`${applied.length} field(s) updated from cloud`);Protected fields — patch() throws if you try to modify:
idTigertag, idProduct, uid, signatureR, signatureS.
fromCloudDoc() maps the Firestore document format to chip fields.
Combine with patchFromRawDict() for surgical overrides before writing.
// Full write: Firestore doc → chip bytes (80 bytes, pages 0x04–0x17)
const tag = TigerTag.fromCloudDoc(firestoreDoc);
const bytes = tag.toBytes(); // → Buffer(80) ready for NFC write
// Surgical patch: only override td_raw and message, keep all other fields
const patched = TigerTag.fromCloudDoc(firestoreDoc)
.patchFromRawDict({ td_raw: 150, message: 'Opened 2025-06' });
const bytes = patched.toBytes();
// From a stored toRawDict() snapshot — same snake_case shape
const tag2 = TigerTag.fromRawDict(storedRawDict);
const patched2 = tag2.patchFromRawDict({ measure_available: 650 });Firestore field mapping used by fromCloudDoc():
| Firestore field | Chip field |
|---|---|
data1 |
idDiameter |
data2 |
nozzleTempMin |
data3 |
nozzleTempMax |
data4 |
dryTemp |
data5 |
dryTime |
data6 |
bedTempMin |
data7 |
bedTempMax |
TD |
tdRaw (float × 10 → integer, e.g. 1.5 → 15) |
weight_available / measure_gr |
measureAvailable |
ApiDiff is a plain object { field, chipValue, apiValue }:
const { TigerTag } = require('tigertag');
const tag = TigerTag.fromPages(uid, payload);
const diffs = await tag.diffApi();
for (const d of diffs) {
console.log(`${d.field}: chip=${d.chipValue} → api=${d.apiValue}`);
}Fields compared: nozzle_min, nozzle_max, bed_min, bed_max, dry_temp, dry_time,
type, material, brand, diameter, aspect_1, aspect_2, color_1, color_2,
color_3, measure_g, measure_unit.
const result = tag.verify(); // fully autonomous — finds the public key from the bundled DB
result.ok // true only for VALID
result.status // "valid" | "invalid" | "unsigned" | "no_key" | "no_uid"
String(result) // "✅ VALID" | "❌ INVALID" | "⬜ NOT SIGNED" | "🔑 NO KEY" | …
result.toDict() // { status: "valid", ok: true, detail: "…" }| Status | Meaning |
|---|---|
valid |
Signature matches — chip is authentic |
invalid |
Signature present but does not match UID + data |
unsigned |
No signature bytes — Maker tag or unverified |
no_key |
No matching public key in database for this protocol version |
no_uid |
UID not provided — cannot verify (use fromPages(uid, payload)) |
ECDSA-P256 verification uses the public key bundled in database/id_version.json — works
fully offline, no external dependencies (Node.js built-in crypto module).
const { TigerTagDB } = require('tigertag');
const db = new TigerTagDB(); // bundled database (offline, no network)
const db = new TigerTagDB({ autoSync: true }); // check for updates on init
const db = new TigerTagDB({ dbPath: '/path' }); // custom database path
db.material(38219) // { id: 38219, label: "PLA", density: 1.24, ... }
db.brand(1) // { id: 1, label: "Generic", ... }
db.version(0x01000001) // { id: ..., label: ..., public_key: "-----BEGIN..." }
TigerTagDB.label(entry) // safe label extraction helperThe SDK ships with bundled reference databases — works fully offline after npm install tigertag.
| Mode | Behavior |
|---|---|
| Default | Uses bundled JSONs — no network, always works |
new TigerTagDB({ autoSync: true }) |
Checks timestamps on init, downloads only changed files |
tag.syncDb(null, true) |
Forces full re-download |
tigertag --sync-only |
CLI sync, updates bundled database in place |
| Network failure | Caught silently — bundled databases used as fallback |
Sources: TigerTag API → GitHub mirror (automatic fallback).
fromPages() accepts exactly what NFC SDKs provide:
// Node.js — nfc-pcsc (ACR122U / PN532)
reader.on('card', async (card) => {
const uid = Buffer.from(card.uid, 'hex'); // 7 bytes
const payload = await reader.read(4, 144, 4); // pages 4–39, 144 bytes
const tag = TigerTag.fromPages(uid, payload);
console.log(tag.pretty());
console.log(String(tag.verify())); // ✅ VALID / ⬜ NOT SIGNED
});npm install nfc-pcsc tigertagconst { NFC } = require('nfc-pcsc');
const { TigerTag } = require('tigertag');
const nfc = new NFC();
nfc.on('reader', (reader) => {
reader.autoProcessing = false;
reader.on('card', async (card) => {
try {
const uid = Buffer.from(card.uid, 'hex'); // 7 bytes
const payload = await reader.read(4, 144, 4); // pages 4–39, 144 bytes
const tag = TigerTag.fromPages(uid, payload);
console.log(tag.pretty());
console.log(String(tag.verify())); // ✅ VALID / ⬜ NOT SIGNED / ❌ INVALID
} catch (err) {
console.error(err);
}
});
});See examples/integrate_nfc_sdk.js for patterns covering
Android, iOS, Flutter, Arduino, and Electron/nfc-pcsc.
Pages 0x04–0x27 (144 bytes: user data + ECDSA signature)
Offset Size Field
──────────────────────────────────────────────────────
0x00 4 idTigertag u32 BE — version identifier
0x04 4 idProduct u32 BE — 0xFFFFFFFF=Maker, 0=Init, else cloud ID
0x08 2 idMaterial u16 BE
0x0A 1 idAspect1 u8
0x0B 1 idAspect2 u8
0x0C 1 idType u8
0x0D 1 idDiameter u8
0x0E 2 idBrand u16 BE
0x10 4 color1 RGBA u8×4
0x14 3 measure u24 BE
0x17 1 idUnit u8
0x18 2 nozzleTempMin u16 BE
0x1A 2 nozzleTempMax u16 BE
0x1C 1 dryTemp u8
0x1D 1 dryTime u8 hours
0x1E 1 bedTempMin u8
0x1F 1 bedTempMax u8
0x20 4 timestamp u32 BE — seconds since 2000-01-01 UTC
0x24 3 color2 RGB u8×3 + 0x00 padding
0x28 3 color3 RGB u8×3 + 0x00 padding
0x2C 2 tdRaw u16 BE — HueForge TD × 10
0x2E 2 (padding)
0x30 28 customMessage UTF-8, zero-padded
0x4C 3 measureAvailable u24 BE
0x4F 1 (padding)
── Signature (only in 144-byte payload) ──────────────
0x50 32 signatureR ECDSA-P256 R component
0x70 32 signatureS ECDSA-P256 S component
Dump formats:
| Size | Format |
|---|---|
| 180 bytes | Full chip dump (pages 0–44) — UID auto-extracted |
| 144 bytes | User data + signature (pages 0x04–0x27) |
| 80 bytes | User data only (pages 0x04–0x17) |
Signed message = SHA-256( uid_bytes(7) + id_tigertag_BE(4) + id_product_BE(4) )
Signature = 64 bytes raw: R(32) + S(32)
Algorithm = ECDSA-P256 (prime256v1)
Public key = PEM in database/id_version.json[].public_key
Uses Node.js built-in crypto — no node-forge, no OpenSSL wrappers, no external dependencies.
# Parse a dump file
tigertag dump.bin
# JSON output
tigertag dump.bin --json
# Raw protocol fields (no DB lookup)
tigertag dump.bin --raw
# Use a custom database folder
tigertag dump.bin --db /path/to/db
# Update reference databases and exit
tigertag --sync-only
# Show version
tigertag --version
# Also available as a module runner:
node -e "require('tigertag')"// main.js — replace parseTigerTag() subprocess dependency
const { TigerTag, TigerTagDB } = require('tigertag');
// Called from your NFC reader callback
function parseTigerTag(payload, uid) {
const tag = TigerTag.fromPages(Buffer.from(uid), Buffer.from(payload));
const db = new TigerTagDB();
return {
dict: tag.toDict(db),
raw: tag.toRawDict(),
sig: tag.verify(db).toDict(),
};
}const {
TigerTag,
TigerTagDB,
SignatureResult,
ApiDiff,
syncDatabases,
ID_TIGERTAG,
ID_TIGERTAG_PLUS,
ID_TIGERTAG_INIT,
MAKER_PRODUCT_ID,
INIT_PRODUCT_ID,
} = require('tigertag');| File | Description |
|---|---|
examples/basic_parse.js |
Parse a TigerTag payload, inspect fields |
examples/verify_signature.js |
Full ECDSA sign → verify round-trip |
examples/integrate_nfc_sdk.js |
Integration patterns for all major NFC SDKs |
npm test75 tests across all SDK features. Uses fixtures from test/fixtures/ — no NFC hardware needed.
To regenerate fixtures:
node scripts/generate_fixtures.js- Node.js 18+ (uses built-in
fetchandcrypto) - No external runtime dependencies
| Device | Description | Cost |
|---|---|---|
| TigerTag Pod | Plug-and-play NFC reader/writer — connects to desktop (Studio Manager) or phone (RFID Connect app). Read and write chips with no soldering, no setup. | — |
| Tiger Scale | Open-source DIY ESP32 smart scale — reads the tag on scan, weighs the spool, and updates measureAvailable on the chip in real time. Full BOM and firmware available. |
~30 € in parts |
| Tool | Platform | Description |
|---|---|---|
| TigerTag-RFID-Guide | Spec | Open protocol specification |
| TigerTag-SDK-JS | Node.js | This SDK — parse, verify, write, diff |
| TigerTag-SDK-Python | Python | Python port — parse, verify, write, diff |
| TigerTag Studio Manager | Windows / macOS / Linux | Open-source desktop inventory manager — works with TigerTag Pod and ACR122U |
| TigerTag RFID Connect | iOS | Official mobile app — read/write using the phone's built-in NFC |
| TigerTag RFID Connect | Android | Official mobile app — read/write using the phone's built-in NFC |
| TigerTag Firebase Integration | Cloud | Firebase backend integration example |
| Tiger Scale | ESP32 firmware | Open-source firmware for the DIY smart scale |
Community integrations: OpenRFID · Home Assistant · Snapmaker U1 firmware · TD1s by Ajax
Open source: GNU General Public License v3.0 — see LICENSE.md
Commercial OEM licensing: contact licensing@tigertag.io
Protocol spec: github.com/TigerTag-Project/TigerTag-RFID-Guide