> ## 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

> Persistent device pairing system using encrypted room secrets stored in IndexedDB for cross-network device discovery.

# 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:

```javascript theme={null}
// 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:

```javascript theme={null}
// 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

```javascript theme={null}
// From ui.js lines 1954-1956
_pairDeviceInitiate() {
    Events.fire('pair-device-initiate');
}
```

#### Server-Side Initiation

```javascript theme={null}
// 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

```javascript theme={null}
// 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;
}
```

#### Key Format

* **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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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:

1. **Local Network**: Automatic discovery via IP-based rooms
2. **Remote Networks**: Discovery via shared room secrets
3. **Network Switching**: Seamless transition between networks
4. **Offline Support**: Pairing information persists offline

### Room Secret Synchronization

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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.
