Documentation Index
Fetch the complete documentation index at: https://docsdrop.erikraft.com/llms.txt
Use this file to discover all available pages before exploring further.
Device Pairing System
ErikrafT Drop’s device pairing feature allows devices to maintain persistent connections across different networks. Paired devices can discover each other regardless of their current network location, providing seamless file sharing capabilities.
Pairing Architecture
Room Secret System
Device pairing uses cryptographically secure room secrets:
// From ws-server.js lines 223-224
let roomSecret = randomizer.getRandomString(256);
let pairKey = this._createPairKey(sender, roomSecret);
Persistent Storage
Paired device information is stored using IndexedDB:
// From persistent-storage.js
class PersistentStorage {
async saveRoomSecret(roomSecret) {
return new Promise((resolve, reject) => {
const request = indexedDB.open('pairdrop-db');
request.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(['room-secrets'], 'readwrite');
const store = transaction.objectStore('room-secrets');
store.put({ id: roomSecret, secret: roomSecret });
};
});
}
}
Pairing Process
1. Initiation Phase
Pair Device Dialog
// From ui.js lines 1954-1956
_pairDeviceInitiate() {
Events.fire('pair-device-initiate');
}
Server-Side Initiation
// From ws-server.js lines 222-237
_onPairDeviceInitiate(sender) {
let roomSecret = randomizer.getRandomString(256);
let pairKey = this._createPairKey(sender, roomSecret);
if (sender.pairKey) {
this._removePairKey(sender.pairKey);
}
sender.pairKey = pairKey;
this._send(sender, {
type: 'pair-device-initiated',
roomSecret: roomSecret,
pairKey: pairKey
});
this._joinSecretRoom(sender, roomSecret);
}
2. Key Generation
Secure Pair Key Creation
// From ws-server.js lines 327-340
_createPairKey(creator, roomSecret) {
let pairKey;
do {
// Generate 6-digit numeric key
pairKey = crypto.randomInt(1000000, 1999999).toString().substring(1);
} while (pairKey in this._roomSecrets)
this._roomSecrets[pairKey] = {
roomSecret: roomSecret,
creator: creator
}
return pairKey;
}
- Length: 6 digits
- Format: Numeric string (e.g., “123456”)
- Uniqueness: Guaranteed unique within server instance
- Expiration: Temporary, expires after pairing or timeout
3. QR Code Generation
QR Code Display
// From ui.js lines 1966-1981
_setKeyAndQRCode() {
this.$key.innerText = `${this.pairKey.substring(0, 3)} ${this.pairKey.substring(3, 6)}`
// Display QR code for the url
const qr = new QRCode({
content: this._getPairUrl(),
width: 130,
height: 130,
color: {
dark: "#000000",
light: "#FFFFFF"
},
ecl: "L",
join: true
});
this.$qrCode.innerHTML = qr.svg();
}
Pair URL Generation
// From ui.js lines 1983-2002
_getPairUrl() {
const url = new URL(location.href);
url.searchParams.delete('pair_key');
url.hash = '';
if (this.pairKey) {
url.searchParams.append('pair_key', this.pairKey);
}
return url.href;
}
Joining a Pair
1. Key Entry
// From ui.js lines 2004-2014
_submit() {
let inputKey = this.inputKeyContainer._getInputKey();
this._pairDeviceJoin(inputKey);
}
_pairDeviceJoin(pairKey) {
if (/^\d{6}$/g.test(pairKey)) {
Events.fire('pair-device-join', pairKey);
this.inputKeyContainer.focusLastChar();
}
}
2. Server Validation
// From ws-server.js lines 239-265
_onPairDeviceJoin(sender, message) {
if (sender.rateLimitReached()) {
this._send(sender, { type: 'join-key-rate-limit' });
return;
}
if (!this._roomSecrets[message.pairKey] || sender.id === this._roomSecrets[message.pairKey].creator.id) {
this._send(sender, { type: 'pair-device-join-key-invalid' });
return;
}
const roomSecret = this._roomSecrets[message.pairKey].roomSecret;
const creator = this._roomSecrets[message.pairKey].creator;
this._removePairKey(message.pairKey);
this._send(sender, {
type: 'pair-device-joined',
roomSecret: roomSecret,
peerId: creator.id
});
this._send(creator, {
type: 'pair-device-joined',
roomSecret: roomSecret,
peerId: sender.id
});
this._joinSecretRoom(sender, roomSecret);
this._removePairKey(sender.pairKey);
}
3. Pair Confirmation
// From ui.js lines 2016-2037
_onPairDeviceJoined(peerId, roomSecret) {
// abort if peer is another tab on the same browser
if (BrowserTabsConnector.peerIsSameBrowser(peerId)) {
this._cleanUp();
return;
}
// save pairPeer and wait for it to connect
this.pairPeer = {
"peerId": peerId,
"roomSecret": roomSecret
};
// save roomSecret
PersistentStorage
.saveRoomSecret(roomSecret)
.then(_ => {
Events.fire('notify-user', Localization.getTranslation("notifications.pairing-success"));
this.hide();
})
.catch(_ => {
Events.fire('notify-user', Localization.getTranslation("notifications.pairing-not-persistent"));
});
}
Paired Device Management
Storage Structure
// IndexedDB stores paired device information
{
id: "room-secret-256-char-string",
secret: "256-character-encrypted-room-secret",
displayName: "User-chosen-device-name",
autoAccept: false,
timestamp: 1640995200000
}
Device List Management
// From ui.js lines 2162-2243
async _initDOM() {
const roomSecretsEntries = await PersistentStorage.getAllRoomSecretEntries();
roomSecretsEntries.forEach(roomSecretsEntry => {
let $pairedDevice = document.createElement('div');
$pairedDevice.classList.add('paired-device');
// Device display information
const displayDiv = document.createElement('div');
displayDiv.className = 'display-name';
displayDiv.textContent = roomSecretsEntry.displayName;
// Auto-accept toggle
const input = document.createElement('input');
input.type = 'checkbox';
input.checked = roomSecretsEntry.autoAccept;
input.addEventListener('change', e => {
PersistentStorage.updateRoomSecret(roomSecretsEntry.id, {
autoAccept: e.target.checked
});
});
$pairedDevice.appendChild(displayDiv);
$pairedDevice.appendChild(input);
this.$pairedDevicesWrapper.appendChild($pairedDevice);
});
}
Auto-Accept Feature
// From network.js lines 841-847
_respondToFileTransferRequest(accepted) {
this.sendJSON({type: 'files-transfer-response', accepted: accepted});
if (accepted) {
this._requestAccepted = this._requestPending;
this._totalBytesReceived = 0;
this._filesReceived = [];
}
this._requestPending = null;
}
Security Features
Cryptographic Security
- Room Secrets: 256-character cryptographically random strings
- Hash Validation: SHA3-512 hashing for integrity verification
- Rate Limiting: 10 pairing attempts per 10 seconds
- Temporary Keys: Pair keys expire after use
Privacy Protection
// From peer.js lines 128-136
_setPeerId(request) {
const searchParams = new URL(request.url, "http://server").searchParams;
let peerId = searchParams.get('peer_id');
let peerIdHash = searchParams.get('peer_id_hash');
if (peerId && Peer.isValidUuid(peerId) && this.isPeerIdHashValid(peerId, peerIdHash)) {
this.id = peerId;
} else {
this.id = crypto.randomUUID();
}
}
Cross-Network Discovery
Persistent Connections
Paired devices maintain discoverability across different networks:
- Local Network: Automatic discovery via IP-based rooms
- Remote Networks: Discovery via shared room secrets
- Network Switching: Seamless transition between networks
- Offline Support: Pairing information persists offline
Room Secret Synchronization
// From ws-server.js lines 188-198
_onRoomSecrets(sender, message) {
if (!message.roomSecrets) return;
const roomSecrets = message.roomSecrets.filter(roomSecret => {
return /^[\x00-\x7F]{64,256}$/.test(roomSecret);
});
if (!roomSecrets) return;
this._joinSecretRooms(sender, roomSecrets);
}
Pairing Management
Edit Paired Devices
// From ui.js lines 2141-2252
class EditPairedDevicesDialog extends Dialog {
constructor() {
super('edit-paired-devices-dialog');
this.$pairedDevicesWrapper = this.$el.querySelector('.paired-devices-wrapper');
Events.on('peer-display-name-changed', e => this._onPeerDisplayNameChanged(e));
Events.on('keydown', e => this._onKeyDown(e));
}
}
Unpairing Devices
// From ui.js lines 2221-2240
input.addEventListener('click', e => {
PersistentStorage
.deleteRoomSecret(roomSecretsEntry.id)
.then(roomSecret => {
Events.fire('room-secrets-deleted', [roomSecret]);
Events.fire('evaluate-number-room-secrets');
$pairedDevice.innerText = '';
});
});
Use Cases
Personal Device Ecosystem
- Phone to Computer: Quick photo and document transfer
- Tablet to Laptop: Seamless file synchronization
- Work to Home: Access files across different networks
Family Sharing
- Parent-Child Devices: Easy family file sharing
- Multi-Device Households: Shared family devices
- Guest Access: Temporary pairing for visitors
Professional Use
- Team Collaboration: Persistent team device connections
- Conference Sharing: Quick setup for presentations
- Remote Work: Home-office device integration
Troubleshooting
Common Issues
- Invalid Key: Ensure 6-digit key is entered correctly
- Expired Key: Pair keys expire after use or timeout
- Network Issues: Both devices need internet connection for pairing
- Browser Storage: Ensure IndexedDB is enabled and not cleared
Recovery Options
- Re-pairing: Devices can be re-paired if needed
- Backup: Export/import pairing information (planned feature)
- Manual Discovery: Use local network discovery if pairing fails
This device pairing system provides a robust, secure method for maintaining persistent connections between devices across different networks while ensuring user privacy and security.