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.

RavenTag.com
Public brand registry
Docs · Downloads
register
Brand Backend (Docker)
BRAND_MASTER_KEY · ADMIN_KEY
Verify · Revoke · Chip keys · Metadata cache
verify / keys / cache
Brand Manager App
HD wallet (BIP44) · IPFS CID on-device
NFC chip programming (APDU)
Admin / Operator roles
issue ▶
Ravencoin
ElectrumX · public
pin ▶
Pinata
IPFS · metadata + images
pin ▶
Kubo IPFS
optional · self-hosted node
Consumer Verify App
Your branded white-label app · NFC scan → brand backend → authentic / revoked
Once infrastructure is running, you are independent. RavenTag.com is used only for docs, app downloads, and the mandatory public brand registry (RTSL-1.0 attribution).

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
Write down all generated keys and store them in a password manager before continuing. brand_master_key and brand_salt cannot be changed after the first chip is programmed.
Key Security

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_ORIGINSCORS 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
Once the keys are saved, delete the plaintext files from disk: rm -f secrets/brand_master_key secrets/brand_salt secrets/admin_key secrets/operator_key. Docker reads them at container startup only. Leaving plaintext keys on disk is a security risk.
To upgrade: git pull origin master, then docker compose up -d --build backend. The ./secrets/ directory and the database volume are never touched by upgrades.

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.com
Use Certbot (Let's Encrypt) for a free TLS certificate. After enabling HTTPS, update ALLOWED_ORIGINS in .env to your HTTPS frontend URL and restart the backend.

2. 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.

RTSL-1.0, Mandatory. Registration in the public brand registry is required by the RavenTag license. The list only shows: brand name + registration date. No private data is shared.
# 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

500 RVN
Root asset
e.g. FASHIONX
100 RVN
Sub-asset
e.g. FASHIONX/BAG01
5 RVN
Unique token
e.g. BAG01#SN0001

Get the wallet address from the Brand App (Wallet → Receive) or admin dashboard. The app uses a local BIP44 HD wallet.

The wallet connects directly to ElectrumX public nodes and does not depend on the RavenTag backend. The consumer can send and receive RVN, and transfer assets to any Ravencoin address, even if the brand's backend is offline or unreachable.

4. Roles & Permissions

Two access levels allow separation between brand managers and factory operators who program tags.

Admin

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
Operator

Factory worker, warehouse staff. Limited.

  • Register NFC chips
  • List chips
  • View chip details
Cannot issue assets
Cannot revoke
No wallet
OPERATOR_KEY=op-key
X-Operator-Key: op-key
Configure the Operator Key in Brand Manager App Settings and distribute the app to factory workers. They can program chips without access to asset management. The Admin Key is stored encrypted on the device (Android Keystore, AES-256-GCM).

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

1
Open Brand App → Brand → Program NFC Tag
Select asset from wallet or type asset name. Verify URL is shown.
2
Tap NTAG 424 DNA chip (Step 1 of 3)
App reads 7-byte UID. Calls POST /api/brand/derive-chip-key (HTTPS, admin auth). Backend derives 4 per-chip AES-128 keys using slot-prefixed diversification: Key_n = AES_ECB(BRAND_MASTER_KEY, slot_prefix_n || UID || padding). Slots: 0=AppMasterKey, 1=SDMInputKey, 2=SDMEncKey (encrypts PICCData), 3=SDMMacKey (base for session MAC). Returns nfc_pub_id = SHA-256(UID || BRAND_SALT).
3
Keys written to chip (Step 3 of 3)
AES keys + SUN URL written via ISO 7816-4 APDUs. Chip locked.
4
Auto-registration on backend
App calls POST /api/brand/register-chip. Backend recomputes nfc_pub_id using BRAND_SALT, consistent with the value stored in IPFS metadata.
The SUN URL written to the chip uses the Verification Server URL set in Settings. Change it to your own server before programming chips in production. BRAND_MASTER_KEY and BRAND_SALT must be set on the backend before programming any chip.

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
The consumer app shows your brand name, logo, colors. The RavenTag attribution appears in Settings → About (required by RTSL-1.0).

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%23SN0001
Backend revocation is instant and reversible. Use un-revoke to restore an asset to authentic status.

Product 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)
CID computed on-device, no external IPFS node required. Configure a Kubo node URL in Settings to also push images there. At issuance, the metadata is automatically mirrored to the backend SQLite cache so verifications never need to call IPFS.

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
There is no cloud backup. The mnemonic is the only way to recover your wallet and all associated assets. Store it in multiple secure offline locations.

Troubleshooting

Common issues and solutions.

1
NFC tag is not detected
Check that NFC is enabled in phone settings. For NTAG 424 DNA, ensure the phone supports ISO 14443-4 (ISO-DEP). On some phones, the NFC antenna is at the top, try different tag placement.
2
WriteData / ChangeFileSettings fails
The tag must be a factory-fresh NTAG 424 DNA. A previously programmed tag needs to be reset (not possible without the original keys). Check that the app has the correct current master key (default: 16 zero bytes for factory tags).
3
Verification returns "MAC failed"
The backend AES keys (BRAND_MASTER_KEY) must match the keys programmed on the chip. If you changed BRAND_MASTER_KEY after programming chips, those chips cannot be verified. Keys are derived per-chip at verify time, regenerating with the same master key always gives the same chip key.
4
Asset issuance fails with 502
The Ravencoin node wallet has insufficient balance. Check with GET /api/brand/wallet. Ensure the node is fully synced (ravend getblockchaininfo). Allow 30+ minutes for initial sync on testnet.
5
Product image not visible when consumers verify
The CID is computed on-device and the metadata is cached in the backend SQLite. If no Kubo node URL is set in Settings, the image bytes exist only on the device that issued the asset. Configure a Kubo node URL in Brand App Settings so the image bytes are pushed to a node that pinning services and gateways can retrieve.
6
Backend health check fails or port 3001 is not reachable
Check Docker is running: docker compose ps. View logs: docker compose logs backend. Ensure port 3001 is not blocked by the server firewall (ufw allow 3001 for local testing, or use nginx on 443 for production). The health endpoint is GET /api/health.
7
Browser shows CORS error when the frontend calls the backend
Add your frontend URL to ALLOWED_ORIGINS in .env, then restart the backend: docker compose restart backend. Example: ALLOWED_ORIGINS=https://verify.yourbrand.com,https://yourbrand.com. CORS must also allow the exact origin used by the NFC chip SUN URL redirect.