#!/usr/bin/env node // Spec-compliant test to send config_query command with proper NIP-44 encryption // Uses nostr-tools for encryption and event creation import { nip44, finalizeEvent, getPublicKey } from 'nostr-tools'; import WebSocket from 'ws'; // Test keys from make_and_restart_relay.sh -t const ADMIN_PRIVATE_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; const ADMIN_PUBLIC_KEY = "6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3"; const RELAY_PUBLIC_KEY = "4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"; const RELAY_URL = "ws://localhost:8888"; async function main() { console.log("=== Testing Admin API Config Query (Spec Compliant) ==="); console.log("Sending config_query command using nostr-tools..."); // Command array as per README.md spec const commandArray = ["config_query", "all"]; const commandJson = JSON.stringify(commandArray); console.log(`Command: ${commandJson}`); return new Promise((resolve, reject) => { const ws = new WebSocket(RELAY_URL); let queryResponseReceived = false; ws.on('open', () => { console.log("WebSocket connected"); try { const adminPubkey = getPublicKey(Buffer.from(ADMIN_PRIVATE_KEY, 'hex')); console.log(`Admin pubkey: ${adminPubkey}`); // Send subscription for response events const subId = "admin_response"; const subMessage = JSON.stringify(["REQ", subId, { kinds: [23457], "#p": [ADMIN_PUBLIC_KEY] }]); ws.send(subMessage); console.log(`Subscription sent: ${subMessage}`); // Send the first command sendCommand(ws, commandArray); } catch (error) { console.error("Failed to prepare/send command:", error.message); ws.close(); reject(error); } }); ws.on('message', (data) => { const message = data.toString(); console.log(`Relay response: ${message}`); try { const parsed = JSON.parse(message); if (parsed[0] === "EVENT" && parsed[1] === "admin_response") { const event = parsed[2]; if (event.kind === 23457) { // Decrypt the response content const conversationKey = nip44.getConversationKey(Buffer.from(ADMIN_PRIVATE_KEY, 'hex'), RELAY_PUBLIC_KEY); const decrypted = nip44.decrypt(event.content, conversationKey); console.log(`Decrypted response: ${decrypted}`); if (!queryResponseReceived) { queryResponseReceived = true; console.log("\n=== Testing Admin API Config Update ==="); console.log("Sending config_update command to change relay_description..."); // Send the second command const updateCommandArray = ["config_update", [{"key": "relay_description", "value": "Bozo the clown", "data_type": "string", "category": "relay"}]]; sendCommand(ws, updateCommandArray); } } } } catch (error) { // Ignore parsing errors } }); ws.on('error', (error) => { console.error("WebSocket error:", error.message); reject(error); }); ws.on('close', () => { console.log("WebSocket closed"); resolve(); }); // Timeout after 20 seconds setTimeout(() => { ws.close(); }, 20000); }); } function sendCommand(ws, commandArray) { try { const commandJson = JSON.stringify(commandArray); console.log(`Command: ${commandJson}`); // Derive conversation key for NIP-44 encryption const conversationKey = nip44.getConversationKey(Buffer.from(ADMIN_PRIVATE_KEY, 'hex'), RELAY_PUBLIC_KEY); // Encrypt the command JSON with NIP-44 const encryptedContent = nip44.encrypt(commandJson, conversationKey); console.log(`Encrypted content: ${encryptedContent.substring(0, 50)}...`); // Create the event template const eventTemplate = { kind: 23456, created_at: Math.floor(Date.now() / 1000), tags: [['p', RELAY_PUBLIC_KEY]], content: encryptedContent }; // Sign the event const event = finalizeEvent(eventTemplate, Buffer.from(ADMIN_PRIVATE_KEY, 'hex')); console.log(`Event sent: ${JSON.stringify(event, null, 2)}`); // Send the event const eventMessage = JSON.stringify(["EVENT", event]); ws.send(eventMessage); console.log("Command sent successfully!"); console.log("Listening for responses..."); } catch (error) { console.error("Failed to send command:", error.message); ws.close(); } } main().catch(console.error);