Getting Started

What is NEXAR?

NEXAR is a Private Intelligence Graph — an infrastructure layer that lets anyone register, encrypt, license, and monetize any file as on-chain intellectual property.

Built on

Story ProtocolIP registry and licensing
CDREncrypted vault storage
Privy MPCNon-custodial wallet infrastructure
Pinata IPFSDecentralized file storage
Aeneid TestnetStory Protocol's EVM chain (Chain ID: 1315)

What NEXAR provides

  • A REST API that wraps all of the above
  • A Telegram bot for zero-friction onboarding
  • iMessage and WhatsApp via Photon Spectrum
  • An operator-pays-gas architecture — users never need ETH
  • Multi-signal plagiarism detection at registration
  • Automatic royalty distribution via Story Protocol PIL terms

What NEXAR does not do

  • Store your private keys (Privy MPC handles this)
  • Charge platform fees on revenue (0% cut)
  • Require users to know anything about blockchain
  • Expose your files publicly (always AES-256 encrypted)
Getting Started

Core Concepts

The building blocks of the NEXAR graph — IP assets, vaults, licenses, and the operator model.

IP Asset

Every registered file becomes an IP Asset on Story Protocol. It gets a unique ipId (Ethereum address), a vaultUuid (CDR), and a licenseTermsId (PIL terms contract).

ipId: 0x6A6d38DA92c9D790e88CA72718d5BFED06ed4bf9 vaultUuid: 4657 licenseTermsId: 2837

CDR Vault

Every IP Asset has an associated CDR (Confidential Data Rail) vault. The vault stores the AES encryption key for the file. Access is controlled by on-chain conditions.

Vault typeBehavior
licenseGatedAnyone with a license token can access
ownerOnlyOnly the registered owner can access
timedAccessAccess expires after a set time
inferenceOnlyAccess limited to compute operations

License Token

When someone pays for access, a license token (ERC-721) is minted to their wallet. This token is their on-chain proof of access. It is checked before any vault decryption.

Operator Pattern

One server wallet (the operator) signs all blockchain transactions and pays all gas. Users own the IP — their address is the registered owner — but never need ETH or a wallet setup.

NEXAR Handle

Every user gets a NEXAR handle (e.g. zaid_01). Separate from their Telegram or iMessage username. One handle → one Privy MPC wallet → works across all platforms.

Access Token

A signed, time-limited download link. No NEXAR account needed to use. Can be single-use (burns after one download) or multi-use (unlimited downloads within expiry).

PIL Terms (Programmable IP License)

Every registered file gets Story Protocol PIL terms attached. These terms define the license type and economics:

commercialUseCan buyers use it commercially?
derivativesAllowedCan buyers build on it?
commercialRevShareWhat % do you earn on derivatives?
defaultMintingFeeWhat does a license cost?
royaltyPolicyLAP (Liquid Absolute Protocol)
Getting Started

Quick Start

Get your first file registered as IP in under 5 minutes — over Telegram or via the API.

STEP 1 — OPEN TELEGRAM

Find @nexar_intel_bot on Telegram and send /start.

STEP 2 — CREATE YOUR HANDLE
register yourhandle

Rules

  • 3–20 characters
  • Letters, numbers, underscores only
  • Lowercase only
  • Must be unique — cannot be changed later

What happens

  • NEXAR checks availability
  • Privy MPC wallet created (you own the keys)
  • Handle linked to your Telegram identity
  • Wallet address returned
STEP 3 — SEND A FILE

Send any file as a Telegram attachment. NEXAR walks you through:

Q: Who can access this? A: anyone / @username / timed Q: License terms? (if "anyone") A: strict (no re-selling) / open (re-selling allowed, you earn 20%) Q: Price in $IP? (or "free") A: 0.1 Q: Name this asset? (or "skip" for filename) A: My Dataset
STEP 4 — REGISTERED
✅ My Dataset registered as your IP! Price: 0.1 $IP Access: Anyone who pays IP ID: 0x6A6d38... Explorer: https://aeneid.explorer.story.foundation/ipa/0x... Your file is encrypted on IPFS. Plagiarism fingerprint stored. 🛡️
STEP 5 — GET PAID

When someone buys access via buy yourhandle/my-dataset, you receive a license token minted to the buyer's wallet and a royalty accrued to your IP vault. Text earnings to check your balance and claim to collect.

Via API

Base URL https://your-domain.com · Content-Type: application/json

1 · Create wallet

POST /api/wallet/create { "label": "alice@example.com" } → { "ok": true, "address": "0x..." }

2 · Register file

POST /api/asset/register { "label": "alice@example.com", "name": "Q3 Dataset", "description": "Financial signals Q3 2026", "tier": "DATASET", "content": "<base64>", "contentType": "text/csv", "commercial": true, "revShare": 20, "basePrice": "0.5" } → { "ok": true, "ipId": "0x...", "vaultUuid": "4657" }

3 · Mint license for buyer

POST /api/license/mint { "licensorIpId": "0x...", "licenseTermsId": "2837", "buyerLabel": "bob@example.com", "mintingFee": "500000000000000000" } → { "ok": true, "licenseTokenIds": ["72707"] }

4 · Access file

POST /api/vault/access { "label": "bob@example.com", "vaultUuid": "4657", "licenseTokenIds": ["72707"] } → { "ok": true, "content": "<base64 decrypted>" }
Getting Started

Architecture Overview

How a file moves from a chat message to encrypted, on-chain IP — across the client, backend, chain, and storage layers.

┌─────────────────────────────────────────────────────────┐ │ CLIENT LAYER │ │ Telegram Bot │ iMessage │ WhatsApp │ REST API │ └────────────────────────┬────────────────────────────────┘ │ ┌────────────────────────▼────────────────────────────────┐ │ NEXAR BACKEND │ │ │ │ Express.js Server (Node 22, TypeScript) │ │ ├── /api/asset Asset registration │ │ ├── /api/vault Vault access + delivery │ │ ├── /api/license Minting + verification │ │ ├── /api/royalty Claims + distribution │ │ ├── /api/wallet Privy MPC wallet management │ │ ├── /webhook/telegram Telegram Bot API │ │ ├── /webhook/spectrum Photon (iMessage + WhatsApp) │ │ └── /access/:token Public download links │ │ │ │ ├── IdentityManager Handle + platform linking │ │ ├── WalletManager Privy MPC signing │ │ ├── AssetRegistry Story Protocol registration │ │ ├── VaultManager CDR vault creation + access │ │ ├── LicensingEngine PIL terms + token minting │ │ ├── RoyaltyEngine Revenue distribution │ │ └── FingerprintEngine pHash + audio + video + PDF │ │ │ │ SQLite Database │ │ ├── wallets Privy wallet IDs + addresses │ │ ├── assets ipId + vaultUuid + licenseTermsId │ │ ├── licenses Token IDs + holder addresses │ │ ├── vault_secrets AES keys (encrypted at rest) │ │ ├── asset_fingerprints SHA256 + pHash + audio/video │ │ ├── conversation_states Telegram flow state machine │ │ ├── platform_identities Cross-platform handle mapping │ │ ├── access_tokens Signed download links │ │ └── pending_shares Invite queue │ └──────────┬─────────────────────┬───────────────────────┘ │ │ ┌──────────▼──────────┐ ┌────────▼────────────────────────┐ │ STORY PROTOCOL │ │ STORAGE LAYER │ │ (Aeneid Testnet) │ │ │ │ │ │ Pinata IPFS │ │ IPAssetRegistry │ │ → Encrypted file storage │ │ LicenseRegistry │ │ → Content-addressed CIDs │ │ RoyaltyModule │ │ │ │ PIL Terms │ │ CDR Vaults │ │ SPGNFTCollection │ │ → AES key storage │ │ │ │ → On-chain access conditions │ │ NEXAR Contracts: │ │ │ │ NEXARRegistry │ │ Privy MPC │ │ DynamicPricingHook │ │ → Per-user wallet creation │ │ TimedAccessCond. │ │ → Server-side signing │ │ InferenceAccessC. │ │ → Key never on our server │ └─────────────────────┘ └─────────────────────────────────┘
Messaging

Commands Reference

Every action is a plain-text message. The same commands work on Telegram, iMessage, and WhatsApp.

register <handle> Create your NEXAR identity wallet View your wallet address my assets List your registered IP my licenses List licenses you own earnings Check claimable royalties claim Collect all royalties buy @handle/asset Purchase a license access @handle/asset Decrypt and download a file link email <email> Link email to your account recover <handle> Recover your account whoami View your full identity help Show all commands
Messaging

Identity & Handles

One handle, one wallet, every platform. Includes cross-platform identity, private sharing, and the pending-shares invite queue.

Handle system

One NEXAR handle = one Privy MPC wallet = works on all platforms.

  • 3–20 characters
  • Lowercase letters, numbers, underscores
  • Globally unique — first come first served
  • Permanent — cannot be transferred or changed

Cross-platform recovery

  1. Open Telegram → type recover alice
  2. NEXAR sends a 6-digit code to the original platform
  3. Reply verify 123456
  4. Telegram linked to alice's wallet

Platform linking

One handle can be linked to multiple platforms. Private sharing sends an asset to a specific @handle; if the target hasn't registered yet, it sits in the pending-shares queue and is auto-minted the moment they do. Commands: link email alice@example.com · recover alice

DB schema

usernames: username TEXT PRIMARY KEY privy_wallet_id TEXT created_at INTEGER platform_identities: username TEXT → references usernames platform TEXT (telegram|imessage|whatsapp|email) platform_id TEXT (phone, chat_id, email address) UNIQUE(platform, platform_id) pending_shares: ip_id TEXT owner_username TEXT target_username TEXT → Auto-minted when target registers access_tokens: token TEXT PRIMARY KEY vault_uuid TEXT single_use INTEGER uses_remaining INTEGER expires_at INTEGER
API Reference

API Overview

A single REST surface wraps Story Protocol, CDR, Privy, and IPFS. Every response follows a consistent envelope.

Base URL: https://your-domain.com All responses follow the shape: { "ok": true, ...data } { "ok": false, "error": "ERROR_CODE", "message": "..." }

Authentication is currently open and rate-limited — API keys are on the roadmap. See Rate Limits and Error Codes for the full envelope details.

API Reference
POST

Wallets

POST /api/wallet/create

Create a Privy MPC wallet for a user label. Idempotent — returns the existing wallet if the label exists.

Body

{ "label": "string" // email, username, or any unique ID }

Response

{ "ok": true, "label": "alice@example.com", "address": "0x358dB4C646C313042E11029BBf76431740374c3B", "mode": "privy" // or "local" in dev mode }
GET /api/wallet/:label Get wallet address for a label GET /api/wallet List all wallet labels
API Reference
POST

Asset Registration

POST /api/asset/register

Register any file as a Story Protocol IP Asset. Creates a Privy wallet if the label doesn't exist. Encrypts the file, uploads to IPFS, registers IP, attaches PIL terms, creates a CDR vault.

Body

{ "label": "string", // owner's NEXAR handle or email "name": "string", // asset display name "description": "string", // asset description "tier": "string", // DATASET | MODEL | INFERENCE | STRATEGY | PROMPT "content": "string", // base64-encoded file bytes "contentType": "string", // MIME type e.g. "image/jpeg", "text/csv" "commercial": boolean, // allow commercial use? "revShare": number, // 0-100, % revenue share on derivatives "basePrice": "string" // price in $IP e.g. "0.1" or "0" for free }

Response

{ "ok": true, "ipId": "0x6A6d38DA92c9D790e88CA72718d5BFED06ed4bf9", "vaultUuid": "4657", "owner": "0x358dB4C646C313042E11029BBf76431740374c3B", "name": "Q3 Dataset", "tier": "DATASET", "licenseTermsId": "2837", "explorer": "https://aeneid.explorer.story.foundation/ipa/0x..." }

Asset Tiers

DATASETTraining data, CSVs, research data
MODELAI model weights, configs, checkpoints
INFERENCEAPI access to a model (no weight exposure)
STRATEGYTrading signals, playbooks, research
PROMPTPrompt templates, system instructions
GET /api/asset/:ipId Get asset details by IP ID GET /api/asset?owner=0x... List all assets for a wallet address
API Reference
POST

Timed Vaults (NDA Mode)

POST /api/vault/timed

Create a time-limited encrypted vault. Access revokes automatically on-chain after ttlSeconds — ideal for NDA screenings and previews.

Body

{ "label": "string", "ipId": "string", "content": "string", // base64 "contentType": "string", "ttlSeconds": number // e.g. 172800 for 48 hours }

Response

{ "ok": true, "vaultUuid": "4660", "expiresAt": "2026-06-03T08:41:23.000Z" }

POST /api/vault/session

Create a reviewer session token for timed vault access.

{ "label": "string", // reviewer's handle "vaultUuid": "string", "ipId": "string", "role": "reviewer" }
API Reference
POST

Vault Access

POST /api/vault/access

Verify license ownership and decrypt file content. Returns base64-encoded decrypted bytes.

Body

{ "label": "string", // accessor's handle "vaultUuid": "string", "licenseTokenIds": ["string"] // array of owned token IDs }

Response

{ "ok": true, "vaultUuid": "4657", "content": "<base64 decrypted file bytes>", "txHash": "0x" }

Error — no valid license

{ "ok": false, "error": "VAULT_ACCESS_DENIED", "message": "No key found for vault 4657" }
API Reference
POST

Licensing

POST /api/license/terms/get-fee

Get the minting fee for a specific license.

{ "licensorIpId": "0x...", "licenseTermsId": "2837" } → { "ok": true, "fee": "100000000000000000" } // in wei

POST /api/license/mint

Mint a license token for a buyer. Transfers the minting fee from buyer to licensor.

{ "licensorIpId": "0x...", "licenseTermsId": "2837", "buyerLabel": "string", "mintingFee": "string" // in wei, must match get-fee response } → { "ok": true, "licenseTokenIds": ["72707"], "txHash": "0xfa8e3c..." }
GET /api/license/held/:label All license tokens held by a user GET /api/license/issued/:ipId All licenses issued for an IP asset
API Reference
GET

Royalties

GET /api/royalty/:ipId/claimable

→ { "ok": true, "ipId": "0x...", "claimable": "1000000000000000" } // wei

POST /api/royalty/pay

Pay royalties to an IP asset (external payment).

{ "receiverIpId": "0x...", "payerLabel": "string", "amount": "string" }

POST /api/royalty/claim

Claim all accrued royalties for an IP asset.

{ "ancestorIpId": "0x...", "childIpIds": [], "claimer": "0x..." }
API Reference
GET

Access Tokens (Public Links)

GET /access/:token

Public download endpoint. No account needed. Validates token → decrypts file → streams to browser.

Headers returned: Content-Type: <file mime type> Content-Disposition: attachment; filename="<asset-name>" X-Nexar-Asset: <ipId> X-Nexar-Owner: <ownerUsername> Token states: valid → can be used expired → past expiresAt used → single-use token already redeemed

GET /access/:token/info

Get token metadata without downloading.

{ "ok": true, "assetName": "Q3 Dataset", "ownerUsername": "alice", "singleUse": true, "usesRemaining": 1, "expiresAt": "2026-06-04T00:00:00.000Z", "expired": false, "used": false, "valid": true }
API Reference
POST

Webhooks

Inbound message webhooks power the Telegram, iMessage, and WhatsApp integrations.

POST /webhook/spectrum

Photon Spectrum webhook (iMessage + WhatsApp). Requires HMAC-SHA256 signature verification.

Headers required: X-Spectrum-Event: messages X-Spectrum-Timestamp: <unix timestamp> X-Spectrum-Signature: v0=<hex> Signature formula: HMAC-SHA256(SPECTRUM_SIGNING_SECRET, "v0:" + timestamp + ":" + rawBody)

Payload

{ "event": "messages", "space": { "id": "any;-;+15551234567", "platform": "iMessage", "type": "dm", "phone": "shared" }, "message": { "id": "msg-001", "platform": "iMessage", "direction": "inbound", "timestamp": "2026-06-01T00:00:00Z", "sender": { "id": "+15551234567", "platform": "iMessage" }, "content": { "type": "text", "text": "help" } } }

Response: 200 OK "ok" (plain text, always immediate). Processing happens async after ack.

POST /webhook/telegram

Headers required: X-Telegram-Bot-Api-Secret-Token: <your secret> Register via Telegram API: POST https://api.telegram.org/bot<TOKEN>/setWebhook { "url": "https://your-domain.com/webhook/telegram", "secret_token": "your-secret" }
API Reference

Rate Limits

Per-endpoint limits while the API is open. Exceeding a limit returns a 429.

EndpointLimit
/api/wallet5 req/min
/api/asset/register10 req/min
/api/vault/access60 req/min
/api/license/*30 req/min
/api/royalty/*30 req/min
/webhook/*300 req/min
API Reference

Error Codes

Errors return { "ok": false, "error": "CODE", "message": "..." }.

VALIDATION_ERRORRequest body missing required fields
WALLET_CREATE_FAILEDPrivy wallet creation failed
ASSET_NOT_FOUNDipId not in DB
VAULT_ACCESS_DENIEDNo valid license token found
LICENSE_MINT_FAILEDStory Protocol minting error
INTERNAL_ERRORUnexpected server error
TOKEN_EXPIREDAccess token past expiry
TOKEN_USEDSingle-use token already redeemed
Guides

Operator-Pays-Gas Pattern

Onboard non-crypto users as IP owners without requiring them to hold ETH, set up a wallet, or understand blockchain. This guide also covers how Privy MPC wallets are created and signed.

How it works

1. User sends "register alice" on Telegram. 2. NEXAR creates a Privy MPC wallet for "alice". Address: 0x358dB4C646...374c3B The private key is split via MPC between Privy's servers and the authorized client. NEXAR never holds the full key. 3. When alice registers an IP asset: → Story Protocol's registerIpAsset() is called → nft.recipient = alice's address ← she OWNS the NFT + IP → Transaction signed by OPERATOR wallet ← operator pays gas → Gas fee paid from operator's $IP balance 4. Alice's address appears as the IP owner on-chain: https://aeneid.explorer.story.foundation/ipa/0x... 5. When someone buys alice's license: → mintLicenseTokens() called with receiver = buyer's address → Minting fee paid by buyer (or operator in free tier) → Royalty accrues to alice's IP vault → alice claims via "claim" command

Required env variables

PRIVATE_KEY=0x... # Operator wallet # Holds $IP for gas # The ONLY key on server PRIVY_APP_ID=... PRIVY_APP_SECRET=...

Operator wallet funding

Faucet: faucet.story.foundation

Estimate: ~0.01 $IP per asset registration · ~0.005 $IP per license mint

Security considerations

  • Operator private key should be in env vars only
  • Never commit PRIVATE_KEY to git
  • Use a dedicated operator wallet (not your personal wallet)
  • Monitor operator balance — if it runs out, registrations fail
  • Rotate key quarterly in production
Guides

File Encryption Deep Dive

Every file goes through a multi-layer encryption pipeline before storage. This covers the encryption architecture, key management, and threat model.

Input file bytes │ ▼ AES-256-GCM encryption → Random 256-bit key generated per file → Random 96-bit IV generated per file → Produces: encrypted bytes + auth tag │ ▼ Pinata IPFS upload → Encrypted bytes uploaded as raw blob → Returns CID (content identifier) → CID is public — but content is unreadable without key │ ▼ CDR vault creation → AES key + CID encoded together as vault payload → Uploaded to CDR with access condition → CDR vault UUID returned │ ▼ SQLite vault_secrets → AES key + CID stored for server-side access → Used for operator-mediated decryption

CDR on-chain key management

Decentralized, trustless key release. CDR validators verify license conditions and release the key only to valid holders. Roadmap: Mini App.

SQLite vault_secrets server-side

MVP approach — operator verifies license on-chain, then serves decrypted content directly. Faster, works without user-side signing.

Decryption flow

  1. User calls POST /api/vault/access
  2. Server checks DB: does user hold a valid license token?
  3. Fetches AES key from vault_secrets
  4. Downloads encrypted blob from Pinata (using CID)
  5. AES-256-GCM decrypt with key + IV from payload
  6. Returns decrypted bytes as base64

File never stored in plain text

Plain file bytes exist only in memory during encryption/decryption (milliseconds) and on the user's device after delivery. They are never written to disk on our server, stored in the database, or logged anywhere.

Guides

Plagiarism Detection

Every file is fingerprinted at registration. Future registrations are checked against the fingerprint database.

Detection methods by file type

TypeAlgorithmThresholdSurvives
Image jpg, png, gif, webpDCT perceptual hash (pHash), 64-bit85%Resize, compression, minor edits, color shifts, format conversion
Audio mp3, aac, wav, ogg, flacEnergy distribution fingerprint, 48-char85%Re-encoding, bitrate change, format conversion, minor pitch/tempo
Video mp4, mov, avi, webm, mkvFrame region sampling (16 keyframes)80%Re-encoding, resolution change, compression, format conversion
PDFText extraction + chunk hashing70%Font changes, margins, page reorder, reformatting
Text / Data csv, json, txt, code20-sample chunk hashing70%Minor modifications, added rows, reformatting
All filesSHA256 exact hash100%Byte-for-byte identical copies blocked instantly

Registration flow with duplicate detection

File received │ ▼ Compute fingerprint (SHA256 + type-specific) │ ▼ Check against all existing fingerprints in DB │ ┌─┴──────────────────────────────┐ │ │ No match Match found │ │ ▼ ▼ Register normally Report to user: "⚠️ Similar content found Match: 94% (perceptual) Original: @alice/photo.jpg Do you still want to register?"

DB schema — asset_fingerprints

ip_id TEXT PRIMARY KEY owner TEXT asset_name TEXT sha256 TEXT -- exact hash phash TEXT -- image perceptual hash audio_hash TEXT -- audio energy fingerprint video_hash TEXT -- video frame fingerprint fingerprints TEXT -- JSON: text/pdf chunk hashes file_type TEXT -- image|audio|video|pdf|text|data|other mime_type TEXT created_at INTEGER
Contracts

Deployed Contracts

All NEXAR and Story Protocol contracts on the Aeneid testnet.

NetworkAeneid Testnet
Chain ID1315
RPChttps://aeneid.storyrpc.io
Explorerhttps://aeneid.explorer.story.foundation

NEXAR Contracts

NEXARRegistry

0x0abf194137a47E8cC6D8773E38c8CD399D674128

Maps ipId → vaultUuid on-chain. Central registry for all NEXAR IP assets. registerAsset(ipId, vaultUuid, tier, basePrice)

DynamicPricingHook

0xeAA90ff07786E2f8Ed7012faB5949a0257cCfb96

Handles license pricing per asset. Implements Story Protocol ILicensingHook. registerAsset(ipId, tier, basePrice)

TimedAccessCondition

0x0c8cE21CE246aaa2601efBB5Eb3Ba22D0924E26b

Enforces time-limited vault access on-chain — access auto-revokes after TTL. checkWriteCondition(caller, conditionData)

InferenceAccessCondition

0x3cAF4AaDcbB9DEB261d4E23A010652cEc03E0d2b

Gates model inference access. Verifies compute units and license terms. Custom condition for INFERENCE tier assets.

ReputationRegistry

0x6Cd86319C6977811432881b08F14dC8E7dc2640F

Tracks operator reputation scores. Used for future trust-based access control.

Story Protocol Contracts (Aeneid)

SPG NFT Collection (NEXAR)0x6901E30ed2a14A78aB50BA13a4eE8a75D19467AE
CDR Proxy0xCCCCCC0000000000000000000000000000000005
CDR Implementation0xDC78a37C28A2d53441B8F09E26237320E0F9C0f9
Deployer Wallet0xb92736aaE34B913497E775dFb52Bb7D334B11B2b

Verifying a NEXAR IP on-chain

Explorer: https://aeneid.explorer.story.foundation/ipa/<ipId> Programmatically: GET https://aeneid.storyrpc.io eth_call → IPAssetRegistry.isRegistered(ipId) Returns: true if registered, false if not