mirror of
https://github.com/hanzoskill/botlink.git
synced 2026-10-30 21:01:49 +00:00
142 lines
3.6 KiB
JavaScript
142 lines
3.6 KiB
JavaScript
/**
|
|
* ClawdLink Relay Client
|
|
* Sends and receives messages via the central relay
|
|
*/
|
|
|
|
import crypto from './crypto.js';
|
|
import nacl from 'tweetnacl';
|
|
import util from 'tweetnacl-util';
|
|
|
|
const { decodeBase64 } = util;
|
|
|
|
const RELAY_URL = 'https://clawdlink-relay.vercel.app';
|
|
|
|
/**
|
|
* Convert base64 to hex
|
|
*/
|
|
export function base64ToHex(b64) {
|
|
const bytes = decodeBase64(b64);
|
|
let hex = '';
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
hex += bytes[i].toString(16).padStart(2, '0');
|
|
}
|
|
return hex;
|
|
}
|
|
|
|
/**
|
|
* Convert hex to base64
|
|
*/
|
|
export function hexToBase64(hex) {
|
|
const bytes = new Uint8Array(hex.length / 2);
|
|
for (let i = 0; i < bytes.length; i++) {
|
|
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
}
|
|
return util.encodeBase64(bytes);
|
|
}
|
|
|
|
/**
|
|
* Send an encrypted message via the relay
|
|
*
|
|
* @param {Object} params
|
|
* @param {string} params.to - Recipient's public key (base64)
|
|
* @param {Object} params.content - Message content to encrypt
|
|
* @param {Object} params.identity - Sender's identity
|
|
* @param {Object} params.friend - Friend object with shared secret (base64)
|
|
*/
|
|
export async function sendMessage({ to, content, identity, friend }) {
|
|
// Decode shared secret from base64 to Uint8Array
|
|
const sharedSecretBytes = decodeBase64(friend.sharedSecret);
|
|
|
|
// Encrypt the message with shared secret
|
|
const { ciphertext, nonce } = crypto.encrypt(content, sharedSecretBytes);
|
|
|
|
// Sign the ciphertext with our Ed25519 key
|
|
const signature = crypto.sign(ciphertext, identity.secretKey);
|
|
|
|
// Send to relay (convert to hex for relay API)
|
|
const response = await fetch(`${RELAY_URL}/send`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
from: `ed25519:${base64ToHex(identity.publicKey)}`,
|
|
to: `ed25519:${base64ToHex(to)}`,
|
|
ciphertext,
|
|
nonce,
|
|
signature: base64ToHex(signature)
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new Error(error.error || 'Failed to send message');
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
/**
|
|
* Poll for messages from the relay
|
|
*
|
|
* @param {Object} identity - Our identity
|
|
* @returns {Promise<Array>} Array of encrypted messages
|
|
*/
|
|
export async function pollMessages(identity) {
|
|
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
const message = `poll:${timestamp}`;
|
|
const signature = crypto.sign(message, identity.secretKey);
|
|
|
|
const response = await fetch(`${RELAY_URL}/poll`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-ClawdLink-Key': `ed25519:${base64ToHex(identity.publicKey)}`,
|
|
'X-ClawdLink-Timestamp': timestamp,
|
|
'X-ClawdLink-Signature': base64ToHex(signature)
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new Error(error.error || 'Failed to poll messages');
|
|
}
|
|
|
|
const data = await response.json();
|
|
return data.messages || [];
|
|
}
|
|
|
|
/**
|
|
* Decrypt a message from the relay
|
|
*
|
|
* @param {Object} encryptedMsg - Encrypted message from relay
|
|
* @param {Object} friend - Friend object with shared secret (base64)
|
|
*/
|
|
export function decryptMessage(encryptedMsg, friend) {
|
|
try {
|
|
const sharedSecretBytes = decodeBase64(friend.sharedSecret);
|
|
return crypto.decrypt(
|
|
encryptedMsg.ciphertext,
|
|
encryptedMsg.nonce,
|
|
sharedSecretBytes
|
|
);
|
|
} catch (err) {
|
|
throw new Error('Failed to decrypt message');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check relay health
|
|
*/
|
|
export async function checkHealth() {
|
|
const response = await fetch(`${RELAY_URL}/health`);
|
|
return response.json();
|
|
}
|
|
|
|
export default {
|
|
sendMessage,
|
|
pollMessages,
|
|
decryptMessage,
|
|
checkHealth,
|
|
base64ToHex,
|
|
hexToBase64,
|
|
RELAY_URL
|
|
};
|