Setup Guide
RavenTag · Protocol RTP-1
System Architecture
RavenTag is fully self-hosted and brand-sovereign. Once deployed, your infrastructure is independent from RavenTag.com.
Docs · Downloads
Verify · Revoke · Chip keys · Metadata cache
NFC chip programming (APDU)
Admin / Operator roles
1. Deploy Your Backend
The brand backend is Node.js + Express. Deploy it on any server or cloud.
Step 1: generate secret keys (once)
git clone https://github.com/ALENOC/RavenTag.git && cd RavenTag mkdir -p secrets openssl rand -hex 16 > secrets/brand_master_key openssl rand -hex 16 > secrets/brand_salt openssl rand -hex 24 > secrets/admin_key openssl rand -hex 24 > secrets/operator_key
Admin Key and Operator Key: 24 bytes (192 bits)
Generated from /dev/urandom via OpenSSL, each key has 2¹⁹² possible values. At one trillion guesses per second, exhausting the entire space would take approximately 10⁴² years, about 10³² times the age of the universe. Even Grover's quantum algorithm, which reduces the effective bit-length by half, leaves 2⁹⁶ combinations: still computationally unreachable for any foreseeable technology, classical or quantum.
BRAND_MASTER_KEY: 16 bytes (AES-128)
Derives a unique AES-128 key for each NFC chip at programming time:
Chip_Key = AES-128-ECB(BRAND_MASTER_KEY, UID || 0x00...)
Every chip carries a different cryptographic key derived from its unique 7-byte UID. Compromising one chip's key reveals nothing about any other chip or the master key itself, since AES is a one-way permutation. The master key never leaves your server.
BRAND_SALT: 16 bytes (128-bit random)
Computes the public chip identifier stored on-chain and in IPFS metadata:
nfc_pub_id = SHA-256(UID || BRAND_SALT)
SHA-256 is a one-way function with 2¹²⁸ preimage resistance when the salt is unknown. An attacker who reads all on-chain data cannot recover the chip UID or clone any tag, because reversing SHA-256 without the salt requires brute-forcing 2¹²⁸ values, beyond the reach of any quantum computer in existence or projected.
Docker Secrets: keys protected in memory
Docker mounts /run/secrets/ as tmpfs (RAM only): secret files never touch the container's disk or filesystem. The backend reads each key once at startup and holds it in process memory. docker inspect exposes only the file path, never the key value, unlike plain environment variables, which appear in clear text in docker inspect output. Once docker compose up -d has started, delete the ./secrets/ directory: from that moment the keys live exclusively in RAM, inside the Node.js process and the tmpfs mount, and disappear automatically when the container stops. No key is ever written to disk, swap, or any log.
Step 2: configure .env
cp .env.example .env # Mandatory: ALLOWED_ORIGINS (your frontend domain)
| Variable (.env) | Description |
|---|---|
| ALLOWED_ORIGINS | CORS origins (comma-separated). Must include your frontend URL. |
Step 3: start
docker compose up -d backend
# Verify the backend is running:
curl http://localhost:3001/api/health
# Expected: {"status":"ok"}
# Print your generated keys and save them in a password manager:
cat secrets/admin_key
cat secrets/operator_key
cat secrets/brand_master_key
cat secrets/brand_salt# Delete plaintext key files after saving them elsewhere: rm -f secrets/brand_master_key secrets/brand_salt secrets/admin_key secrets/operator_key
Step 4: HTTPS reverse proxy (production)
Expose the backend over HTTPS before programming any NFC chip in production. NFC chips write the verification URL into their SUN URL at programming time: that URL must be reachable by consumers.
# /etc/nginx/sites-available/raventag
server {
listen 443 ssl;
server_name api.yourbrand.com;
ssl_certificate /etc/letsencrypt/live/api.yourbrand.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourbrand.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Obtain a free TLS certificate with Certbot:
# apt install certbot python3-certbot-nginx
# certbot --nginx -d api.yourbrand.com2. Brand Registry
The brand is registered automatically in the public RavenTag directory the first time a root asset is issued from the Brand Manager app. No configuration required.
# After issuing a root asset from the Brand Manager app:
# POST /api/registry/notify (sent automatically, no action required)
# { "asset_name": "FASHIONX", "asset_type": "root", "txid": "abc123..." }
#
# Backend verifies the txid on the Ravencoin blockchain, then registers
# the brand in the public RavenTag directory.3. Fund the Ravencoin Wallet
Get the wallet address from the Brand App (Wallet → Receive) or admin dashboard. The app uses a local BIP44 HD wallet.
4. Roles & Permissions
Two access levels allow separation between brand managers and factory operators who program tags.
Brand manager, CEO, IT. Full access.
- Issue assets (root/sub/unique)
- Revoke assets
- Transfer tokens
- Manage wallet
- Register + view chips
ADMIN_KEY=secret-key X-Admin-Key: secret-key
Factory worker, warehouse staff. Limited.
- Register NFC chips
- List chips
- View chip details
OPERATOR_KEY=op-key X-Operator-Key: op-key
5. Issue Assets on Ravencoin
Create your asset hierarchy. The typical structure for serialized products:
FASHIONX ← Root asset (500 RVN) FASHIONX/BAG01 ← Sub-asset (100 RVN) FASHIONX/BAG01#SN0001 ← Unique token (5 RVN) , one per physical item
6. Program NFC Chips
7. Customize the Consumer App
Fully white-label the Consumer App for your brand. Edit one file, replace logos, compile.
Step 1, Edit AppConfig.kt
// android/app/src/consumer/java/io/raventag/app/config/AppConfig.kt
object AppConfig {
const val IS_BRAND_APP = false
const val BRAND_NAME = "Fashionx"
const val DEFAULT_VERIFY_URL = "https://verify.fashionx.com"
const val PRIMARY_COLOR_HEX = "#C9622D"
const val LOGO_DRAWABLE = "raven_logo"
}Step 2, Replace logos
android/app/src/main/res/drawable/raven_logo.jpg # In-app header logo android/app/src/main/res/drawable/ic_app_logo.jpg # App icon source
Step 3, Build & distribute
./gradlew assembleConsumerRelease # Rename output: RavenTag-Verify-v1.1.5.apk
8. Consumer Verification
Consumer taps NFC chip
→ Consumer App reads URL: https://verify.yourbrand.com/verify?asset=...&e=...&m=...
→ Backend decrypts e → UID + counter
→ Verifies MAC: CMAC(sdmMacKey, e)[even_bytes][:4] == m
→ Checks counter > stored (anti-replay)
→ Ravencoin → IPFS → nfc_pub_id match
→ Revocation check
→ Returns: { authentic: true/false, revoked: false }9. Revoke Counterfeits
# Via API (immediate, reversible):
POST /api/brand/revoke
X-Admin-Key: your-key
{ "asset_name": "FASHIONX/BAG01#SN0001", "reason": "Stolen" }
# Un-revoke: DELETE /api/brand/revoke/FASHIONX%2FBAG01%23SN0001Product Images
Attach a product image to an asset by tapping the image picker in the Issue Asset screen. The CID is computed on-device (no IPFS node required). Optionally configure a Kubo node URL in Settings to also push the image there.
// In Brand App → Issue Asset screen:
// 1. Tap image picker → select photo from gallery or camera
// 2. CID computed on-device (IpfsCidV0, pure Kotlin, no external node needed)
// Optional: if Kubo URL is set in Settings, image is also pushed there
// 3. CID stored in asset metadata: { "image": "ipfs://QmXxx..." }
// 4. Ravencoin asset metadata includes IPFS hash on-chain
// 5. Metadata mirrored to backend SQLite cache at issuance (no IPFS calls at verify time)Wallet Backup & Recovery
After creating a wallet, the app displays the 12-word mnemonic phrase exactly once on a mandatory backup screen. Write it down offline and keep it safe, it is the only way to recover your wallet. The copy-to-clipboard button auto-clears the clipboard after 60 seconds.
// BIP44 derivation path: m/44'/175'/0'/0/0 // Coin type 175 = Ravencoin (SLIP-0044) // 12-word BIP39 mnemonic → deterministic private key + address // Private key encrypted in Android Keystore (StrongBox/TEE) // Restore: Brand App → Create/Restore Wallet → Restore // Enter 12 words → same address generated on any BIP39 wallet
Troubleshooting
Common issues and solutions.