fix: persistent Tor channel messages, bulletproof Tor after deploys

- Messages persisted to disk (messages.json) — survive restarts
- Sent messages stored on backend via node-store-sent RPC
- Message deduplication (same pubkey + message within 30s)
- Max 200 messages in circular buffer
- Direction field (sent/received) for proper UI display
- Container doctor: prefer system Tor, remove archy-tor container
- Deploy torrc generator: read from tor-config/services.json,
  web apps map port 80→local port for clean .onion URLs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-20 08:26:40 +00:00
parent fc1120338d
commit f0a403b224
5 changed files with 145 additions and 36 deletions

View File

@@ -67,7 +67,6 @@ async function sendArchMessage() {
sendError.value = ''
sendingArch.value = true
try {
// Broadcast to all federated peers over Tor
const nodes = await rpcClient.federationListNodes()
const msg = messageText.value.trim()
let sent = 0
@@ -77,16 +76,14 @@ async function sendArchMessage() {
sent++
} catch { /* some peers may be offline */ }
}
// Local echo — show the sent message immediately
archMessages.value.push({
from_pubkey: 'me',
message: msg,
timestamp: new Date().toISOString(),
})
// Persist sent message on backend (survives restarts)
try {
await rpcClient.call({ method: 'node-store-sent', params: { message: msg } })
} catch { /* non-fatal */ }
messageText.value = ''
if (sent === 0) sendError.value = 'No peers reachable — message may arrive when they come online'
// Also reload in background to pick up any replies
setTimeout(loadArchMessages, 5000)
// Reload to show the persisted sent message
await loadArchMessages()
} catch (e) {
sendError.value = e instanceof Error ? e.message : 'Send failed'
} finally {
@@ -322,18 +319,21 @@ const hasActiveChat = computed(() => !!activeChatPeer.value || !!activeChatChann
const chatMessages = computed(() => {
if (archChannelActive.value) {
// Map Tor messages to mesh message format for rendering
return archMessages.value.map((m, i) => ({
id: i,
peer_contact_id: -99,
peer_name: m.from_pubkey === 'me' ? 'You' : (m.from_pubkey.slice(0, 12) + '...'),
direction: (m.from_pubkey === 'me' ? 'sent' : 'received') as 'sent' | 'received',
plaintext: m.message,
timestamp: m.timestamp,
delivered: true,
encrypted: false,
message_type: undefined,
typed_payload: undefined,
}))
return archMessages.value.map((m, i) => {
const isSent = (m as Record<string, unknown>).direction === 'sent' || m.from_pubkey === 'me'
return {
id: i,
peer_contact_id: -99,
peer_name: isSent ? 'You' : (m.from_pubkey.slice(0, 12) + '...'),
direction: (isSent ? 'sent' : 'received') as 'sent' | 'received',
plaintext: m.message,
timestamp: m.timestamp,
delivered: true,
encrypted: false,
message_type: undefined,
typed_payload: undefined,
}
})
}
if (activeChatChannel.value) {
// Channel messages have negative contact_id = -(channel_index + 1)