The DLUX Device Connection System enables secure wallet functionality across multiple devices, perfect for VR headsets, mobile phones, tablets, and public computers. This system allows one device to request transactions while another device with wallet access provides secure signing.
The device connection system works by:
- Pairing: A device with wallet access creates a 6-digit pairing code
- Connection: Another device uses this code to establish a secure connection
- Remote Signing: The requesting device can send transactions to the signing device
- User Confirmation: All operations require explicit user approval on the signing device
- VR Headsets: Use your phone to sign transactions while in VR
- Public Computers: Keep your wallet on your phone, use public terminals safely
- Shared Devices: Multiple users can request signing from their personal devices
- Kiosks: Interactive displays can request wallet operations from users' phones
Creates a 6-digit pairing code that other devices can use to connect.
// Create pairing code (requires logged in user)
try {
const pairCode = await dluxWallet.requestDevicePairing();
console.log('Share this code:', pairCode); // e.g., "A3X9K2"
} catch (error) {
console.error('Pairing failed:', error.message);
}Returns: Promise<string> - The 6-digit pairing code
Requirements: User must be logged in with any supported wallet method
Events Triggered: dlux-wallet-device-pairing-created
Connect to another device using a 6-digit pairing code.
// Connect to device using pairing code
try {
const success = await dluxWallet.connectToDevice('A3X9K2');
if (success) {
console.log('Connected successfully!');
}
} catch (error) {
console.error('Connection failed:', error.message);
}Parameters:
pairCode(string): 6-character pairing code from signing device
Returns: Promise<boolean> - True if connection successful
Events Triggered: dlux-wallet-device-connected
Send a transaction to the connected signing device for approval and signing.
// Example vote transaction
const transaction = [
'username',
[
[
'vote',
{
voter: 'signer_username',
author: 'disregardfiat',
permlink: 'post-permlink',
weight: 10000
}
]
],
'posting'
];
// Request remote signing
try {
const result = await dluxWallet.requestRemoteSign(transaction, {
broadcast: true, // Default true
timeout: 60000 // 60 seconds, default
});
if (result.signed) {
console.log('Transaction signed and broadcasted!');
console.log('TX ID:', result.id);
}
} catch (error) {
console.error('Remote signing failed:', error.message);
}Parameters:
transaction(Array): Standard Hive transaction formatoptions(Object, optional):broadcast(boolean): Whether to broadcast after signing (default: true)timeout(number): Timeout in milliseconds (default: 60000)
Returns: Promise<Object> - Transaction result with signed flag and potential id
Send a challenge/buffer to the connected signing device for signing.
// Sign a challenge
try {
const signature = await dluxWallet.requestRemoteSignChallenge(
'challenge-string-123',
'posting', // or 'active', 'memo'
{ timeout: 30000 }
);
console.log('Challenge signed:', signature);
} catch (error) {
console.error('Remote challenge signing failed:', error.message);
}Parameters:
challenge(string): Challenge string to signkeyType(string): Key type to use ('posting', 'active', 'memo')options(Object, optional):timeout(number): Timeout in milliseconds (default: 60000)
Returns: Promise<string> - The signature
Disconnect from the paired device.
// Disconnect from device
await dluxWallet.disconnectDevice();
console.log('Device disconnected');Returns: Promise<void>
Events Triggered: dlux-wallet-device-disconnected
Get current device connection status.
const status = dluxWallet.getDeviceStatus();
console.log('Device Status:', {
isConnected: status.isConnected,
role: status.role, // 'signer' or 'requester'
sessionId: status.sessionId,
pairCode: status.pairCode // Only on signing device
});Returns: Object - Device connection status
The device connection system emits several events you can listen for:
Fired when a pairing code is successfully created.
dluxWallet.on('device-pairing-created', (data) => {
console.log('Pairing code:', data.pairCode);
console.log('Expires in:', data.expiresIn, 'seconds');
});Fired when a device successfully connects.
dluxWallet.on('device-connected', (data) => {
console.log('Connected! Session:', data.sessionId);
console.log('Signer info:', data.signerInfo);
});Fired when device connection is terminated.
dluxWallet.on('device-disconnected', () => {
console.log('Device disconnected');
});Fired on the signing device when a request is received.
dluxWallet.on('device-request-received', (data) => {
console.log('Request type:', data.request.type);
console.log('Request data:', data.request.data);
});- Pairing requires an authenticated user with any supported wallet method
- Each pairing code is unique and expires in 5 minutes
- Sessions are cryptographically secured and expire automatically
- Every transaction requires explicit user approval on the signing device
- Users see full transaction details before confirming
- Users can cancel any operation at any time
- All communication is routed through the secure data.dlux.io backend
- No direct device-to-device communication
- Session tokens prevent unauthorized access
- Device names and identifying information are minimal
- Session data is temporary and automatically cleaned up
- No transaction data is permanently stored on backend
The following endpoints are used by the client (implemented at data.dlux.io):
For real-time communication, connect to the WebSocket endpoint instead of polling:
WebSocket URL: wss://data.dlux.io/ws/payment-monitor
Subscribe to device events:
// Connect to WebSocket
const ws = new WebSocket('wss://data.dlux.io/ws/payment-monitor');
// Subscribe to device session events
ws.send(JSON.stringify({
type: 'device_subscribe',
sessionId: 'your-session-id',
userType: 'signer' // or 'requester'
}));
// Listen for device events
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// Send acknowledgment for critical messages
if (data.requiresAck && data.messageId) {
ws.send(JSON.stringify({
type: 'device_ack',
messageId: data.messageId
}));
}
switch (data.type) {
case 'device_pairing_created':
console.log('Pairing code:', data.pairCode);
break;
case 'device_connected':
console.log('Device connected:', data.signerInfo);
break;
case 'device_signing_request':
console.log('New signing request:', data.requestData);
// This is a critical message that requires acknowledgment
break;
case 'device_signing_response':
console.log('Signing response:', data.response);
// This is a critical message that requires acknowledgment
break;
case 'device_disconnected':
console.log('Device disconnected');
break;
case 'device_request_timeout':
console.log('Request timed out:', data.requestId);
break;
case 'device_delivery_failed':
console.error('Message delivery failed:', data.originalMessage);
break;
}
};WebSocket Events:
device_pairing_created- Pairing code createddevice_connected- Device successfully connecteddevice_disconnected- Device disconnecteddevice_signing_request- New signing request received (signer only) [REQUIRES ACK]device_signing_response- Signing request completed (requester only) [REQUIRES ACK]device_session_expired- Session expireddevice_request_timeout- Request timed outdevice_delivery_failed- Message delivery failed after retries
Message Acknowledgment:
Critical messages (signing requests and responses) require acknowledgment to ensure delivery. When you receive a message with requiresAck: true, you must send:
ws.send(JSON.stringify({
type: 'device_ack',
messageId: data.messageId
}));If no acknowledgment is received within 5 seconds, the system will retry up to 3 times before giving up.
Create a device pairing code.
Headers Required:
x-account: [hive_username]
x-challenge: [unix_timestamp]
x-pubkey: [public_key]
x-signature: [signature]
Response:
{
"success": true,
"pairCode": "A3X9K2",
"sessionId": "session_uuid",
"expiresIn": 300
}Connect to a device using a pairing code.
Body:
{
"pairCode": "A3X9K2"
}Response:
{
"success": true,
"sessionId": "session_uuid",
"signerInfo": {
"username": "signer_username",
"deviceName": "Phone"
}
}Send a transaction request to the paired signing device.
Body:
{
"sessionId": "session_uuid",
"type": "sign-transaction",
"data": {
"transaction": [...],
"broadcast": true
},
"timeout": 60000
}Response:
{
"success": true,
"requestId": "request_uuid"
}Poll for pending requests (signing device). Note: WebSocket subscription is recommended instead of polling.
Response:
{
"success": true,
"requests": [
{
"id": "request_uuid",
"type": "sign-transaction",
"data": {...},
"deviceInfo": {...},
"timestamp": 1234567890
}
]
}Send response to a transaction request.
Body:
{
"sessionId": "session_uuid",
"requestId": "request_uuid",
"response": {...},
"error": null
}Disconnect device session.
Body:
{
"sessionId": "session_uuid"
}Get WebSocket connection information and supported events.
Response:
{
"success": true,
"websocketUrl": "/ws/payment-monitor",
"supportedEvents": [...],
"instructions": {...}
}// In VR application
async function setupVRWallet() {
// Check if we can connect to an existing session
const deviceStatus = dluxWallet.getDeviceStatus();
if (!deviceStatus.isConnected) {
// Show pairing code input in VR interface
const pairCode = await showVRPairingInput();
await dluxWallet.connectToDevice(pairCode);
}
// Now we can request signatures remotely
dluxWallet.on('device-connected', () => {
showVRNotification('Wallet connected! You can now sign transactions.');
});
}
async function voteInVR(author, permlink, weight) {
const transaction = [
'', // Username will be filled by signer
[['vote', { voter: '', author, permlink, weight }]],
'posting'
];
try {
showVRNotification('Check your phone to approve transaction...');
const result = await dluxWallet.requestRemoteSign(transaction);
showVRNotification('Vote successful!');
} catch (error) {
showVRNotification('Vote failed: ' + error.message);
}
}// On mobile phone (signing device)
async function startMobileSigner() {
if (!dluxWallet.currentUser) {
// Prompt user to login first
await promptUserLogin();
}
// Create pairing code
const pairCode = await dluxWallet.requestDevicePairing();
// Show pairing code to user
showPairingCodeModal(pairCode);
// Listen for incoming requests
dluxWallet.on('device-request-received', (data) => {
// The v3-nav component will handle showing confirmation dialogs
console.log('Incoming request from connected device');
});
}- Always check connection status before attempting operations
- Provide clear feedback about waiting for signing device
- Implement reasonable timeouts for user experience
- Handle disconnections gracefully
- Only create pairing codes when user explicitly requests
- Display pairing codes clearly and securely
- Automatically disconnect after period of inactivity
- Provide clear indication of connected devices
- Use HTTPS only for all communications
- Validate all transaction data before presenting to users
- Implement session timeouts appropriate for your use case
- Log security-relevant events for audit purposes
"No master domain available"
- Ensure device has internet connection
- Check that dlux.io, vue.dlux.io, or www.dlux.io are accessible
"Failed to create pairing code"
- User must be logged in with a supported wallet method
- Check network connectivity to data.dlux.io
"Connection failed"
- Verify pairing code is correct (6 characters)
- Ensure pairing code hasn't expired (5 minute limit)
- Check that signing device is still online
"Remote signing timeout"
- User may have dismissed confirmation dialog
- Check signing device is still connected
- Verify signing device user is logged in
Enable debug logging to troubleshoot issues:
// Enable debug mode
window.dluxWalletDebug = true;
// Check connection status
console.log('Wallet Status:', dluxWallet.getStatus());
console.log('Device Status:', dluxWallet.getDeviceStatus());Planned improvements to the device connection system:
- WebSocket support for real-time communication
- Multi-device support (connect multiple requesting devices)
- Device management interface (see all connected devices)
- Enhanced security with device fingerprinting
- Biometric authentication integration
- QR code pairing as alternative to text codes