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
| Mistake | Issue | Fix |
|---|---|---|
| Variable-length or wrong-size ID | "Invalid ID size" error | Use exactly 64 hexadecimal characters |
| Unicode in name/description | "Invalid name" or "Invalid description" | Use ASCII only (no emojis, smart quotes, etc.) |
Missing url field | Wallet rejects connection | Always include url: "http://localhost:" + location.port |
url without port | Connection may fail | Use location.port not just origin |
Delaying send() | Connection closes | Send immediately in onopen |
| Invalid JSON | Parse error | Use JSON.stringify() |
Related Pages
- Official Demo - Complete app1 source code
- XSWD Protocol - Complete connection and protocol reference
- API Call Patterns - Making RPC calls after connection