Official Demo
Application Data Handshake

Application Data Handshake

Complete analysis of the application data format from tela_tests/app1 - the verified handshake pattern.

Verified Format: This is the exact application data structure from app1 that successfully connects to Engram and CLI wallets.

Application Data Format

// From tela_tests/app1 - VERIFIED FORMAT (EXACT from main.js line 5-10)
const applicationData = {
    "id": "71605a32e3b0c44298fc1c549afbf4c8496fb92427ae41e4649b934ca495991b",  // 64-character hex string (static)
    "name": "TELA Demo Application",
    "description": "Basic WS connection parts for TELA application",
    "url": "http://localhost:" + location.port  // CRITICAL: Must include port!
};
⚠️

Critical ID Requirement: The id field must be exactly 64 hexadecimal characters (0-9, a-f). IDs that are shorter, longer, or contain invalid characters will cause "Invalid ID size" errors.

Field Requirements

id (Required)

  • Type: String
  • Format: Exactly 64 hexadecimal characters (0-9, a-f)
  • Example: "71605a32e3b0c44298fc1c549afbf4c8496fb92427ae41e4649b934ca495991b" (from app1)
  • Purpose: Application identifier for connection and permission management
  • Important: The ID is essentially arbitrary — any valid 64-char hex string works. The wallet only validates length and hex format.
  • Tip: Reuse the same ID across sessions to maintain permission continuity (store in localStorage or use a static value).

Generation options:

// Option 1: Random (store for reuse)
const appId = Array.from({length: 64}, () => 
    Math.floor(Math.random() * 16).toString(16)).join('');
 
// Option 2: SHA-256 of app name (deterministic)
async function generateAppId(appName) {
    const data = new TextEncoder().encode(appName);
    const hash = await crypto.subtle.digest('SHA-256', data);
    return Array.from(new Uint8Array(hash))
        .map(b => b.toString(16).padStart(2, '0')).join('');
}
 
// Option 3: Static/hardcoded
const appId = "71605a32e3b0c44298fc1c549afbf4c8496fb92427ae41e4649b934ca495991b";
ℹ️

Note: App IDs can be duplicated — nothing prevents reuse across different apps. For TELA applications, the SCID is the true identity, not the applicationData.

name (Required)

  • Type: String
  • Format: 1-255 characters, ASCII only
  • Purpose: Display name shown in wallet approval dialog
  • Example: "My TELA Application"

description (Required)

  • Type: String
  • Format: 1-255 characters, ASCII only
  • Purpose: Explanation of what your app does
  • Example: "Connecting to wallet for DERO operations"
🚨

ASCII Only Requirement: The name and description fields must contain only ASCII characters (bytes 0-127). Unicode characters like smart quotes (" "), emojis, or non-English characters will cause the wallet to reject the connection with "Invalid name" or "Invalid description". This is enforced by the DERO wallet's XSWD server.

url (Required)

  • Type: String
  • Format: Must be full URL including port
  • ✅ CORRECT: "http://localhost:" + location.port
  • ❌ WRONG: window.location.origin (missing port)
  • Purpose: Origin verification for security

Complete Handshake Flow

// Step 1: Create application data
// Generate and store ID for reuse (matches app1 pattern - maintains permission continuity)
function getOrCreateAppId() {
    let appId = localStorage.getItem('xswd_app_id');
    if (!appId) {
        // Generate 64-character hex string
        appId = Array.from({length: 64}, () => 
            Math.floor(Math.random() * 16).toString(16)
        ).join('');
        localStorage.setItem('xswd_app_id', appId);
    }
    return appId;
}
 
const appData = {
    id: getOrCreateAppId(),  // Static ID - maintains permission continuity across connections
    name: "My TELA App",
    description: "App description",
    url: "http://localhost:" + location.port  // Include port!
};
 
// Step 2: Connect to WebSocket
socket = new WebSocket("ws://localhost:44326/xswd");
 
// Step 3: Send data immediately when connection opens
socket.onopen = function(event) {
    socket.send(JSON.stringify(appData));  // Immediate send!
};
 
// Step 4: Wait for wallet response
socket.onmessage = function(event) {
    const response = JSON.parse(event.data);
    
    if (response.accepted) {
        // ✅ Connection approved - can make API calls
        console.log("Connection accepted:", response.message);
    } else if (response.rejected) {
        // ❌ User denied or error
        console.error("Connection rejected:", response.message);
    }
};

Wallet Response Format

Accepted Response

{
  "accepted": true,
  "message": "Connection established"
}

Rejected Response

{
  "rejected": true,
  "message": "Connection rejected by user"
}

Common Mistakes

MistakeIssueFix
Variable-length or wrong-size ID"Invalid ID size" errorUse exactly 64 hexadecimal characters
Unicode in name/description"Invalid name" or "Invalid description"Use ASCII only (no emojis, smart quotes, etc.)
Missing url fieldWallet rejects connectionAlways include url: "http://localhost:" + location.port
url without portConnection may failUse location.port not just origin
Delaying send()Connection closesSend immediately in onopen
Invalid JSONParse errorUse JSON.stringify()

Related Pages