Skip to content

Commit 0d5a33d

Browse files
Use raw WebSocket for reliability, support hex pubkeys
- Replace nostr-tools SimplePool with direct WebSocket (ws) - Use did:nostr:<hex> format for trusted keys - Read privkey from git config or CLI args - Tested end-to-end: daemon receives and verifies 30618 events
1 parent 03383fc commit 0d5a33d

5 files changed

Lines changed: 176 additions & 259 deletions

File tree

package-lock.json

Lines changed: 16 additions & 203 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
},
1414
"dependencies": {
1515
"nostr-tools": "^2.19.4",
16-
"websocket-polyfill": "^0.0.3"
16+
"ws": "^8.18.0"
1717
},
1818
"engines": {
1919
"node": ">=18.0.0"

src/daemon.js

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,79 @@
1-
import 'websocket-polyfill'
2-
import { SimplePool, nip19 } from 'nostr-tools'
1+
import WebSocket from 'ws'
32
import { loadConfig } from './config.js'
43
import { verifyEvent, verifyAnchor } from './verify.js'
54
import { gitSync, getCurrentCommit, runPostSync } from './git.js'
65

76
export async function startDaemon(configPath) {
87
const config = loadConfig(configPath)
9-
const pool = new SimplePool()
10-
118
const repoIds = Object.keys(config.repos)
129

1310
console.log('[daemon] Starting nostr-git-sync')
1411
console.log(`[daemon] Watching ${repoIds.length} repo(s): ${repoIds.join(', ')}`)
1512
console.log(`[daemon] Relays: ${config.relays.join(', ')}`)
1613

17-
// Subscribe to 30618 events for our repos
18-
const sub = pool.subscribeMany(
19-
config.relays,
20-
[{ kinds: [30618], '#d': repoIds }],
21-
{
22-
onevent: async (event) => {
23-
await handleEvent(event, config, nip19)
24-
},
25-
oneose: () => {
26-
console.log('[daemon] Caught up with relay history')
27-
}
28-
}
29-
)
14+
const sockets = []
3015

31-
console.log('[daemon] Subscribed, waiting for events...')
16+
// Connect to each relay
17+
config.relays.forEach((url, index) => {
18+
connectToRelay(url, index, repoIds, config, sockets)
19+
})
3220

3321
// Keep alive
3422
process.on('SIGINT', () => {
3523
console.log('\n[daemon] Shutting down...')
36-
sub.close()
24+
sockets.forEach(ws => ws.close())
3725
process.exit(0)
3826
})
3927
}
4028

41-
async function handleEvent(event, config, nip19) {
29+
function connectToRelay(url, index, repoIds, config, sockets) {
30+
const ws = new WebSocket(url)
31+
32+
ws.on('open', () => {
33+
console.log(`[daemon] Connected to ${url}`)
34+
sockets.push(ws)
35+
36+
// Subscribe to 30618 events for our repos
37+
const subscription = JSON.stringify([
38+
'REQ',
39+
`git-sync-${index}`,
40+
{ kinds: [30618], '#d': repoIds }
41+
])
42+
ws.send(subscription)
43+
})
44+
45+
ws.on('message', async (data) => {
46+
try {
47+
const message = JSON.parse(data.toString())
48+
49+
if (message[0] === 'EVENT' && message[2]) {
50+
await handleEvent(message[2], config)
51+
} else if (message[0] === 'EOSE') {
52+
console.log(`[daemon] Caught up with ${url}`)
53+
} else if (message[0] === 'NOTICE') {
54+
console.log(`[notice] ${url}: ${message[1]}`)
55+
}
56+
} catch (err) {
57+
console.error('[daemon] Error parsing message:', err.message)
58+
}
59+
})
60+
61+
ws.on('error', (err) => {
62+
console.log(`[daemon] Error from ${url}: ${err.message}`)
63+
})
64+
65+
ws.on('close', () => {
66+
console.log(`[daemon] Disconnected from ${url}, reconnecting in 10s...`)
67+
const idx = sockets.indexOf(ws)
68+
if (idx > -1) sockets.splice(idx, 1)
69+
70+
setTimeout(() => {
71+
connectToRelay(url, index, repoIds, config, sockets)
72+
}, 10000)
73+
})
74+
}
75+
76+
async function handleEvent(event, config) {
4277
// Extract repo ID from d tag
4378
const dTag = event.tags.find(t => t[0] === 'd')
4479
if (!dTag) return
@@ -48,7 +83,7 @@ async function handleEvent(event, config, nip19) {
4883
if (!repo) return
4984

5085
console.log(`\n[event] Received 30618 for ${repoId}`)
51-
console.log(`[event] From: ${nip19.npubEncode(event.pubkey).slice(0, 20)}...`)
86+
console.log(`[event] From: ${event.pubkey.slice(0, 16)}...`)
5287

5388
// Find branch ref
5489
const refTag = event.tags.find(t => t[0].startsWith('refs/heads/'))
@@ -69,7 +104,7 @@ async function handleEvent(event, config, nip19) {
69104
}
70105

71106
// Verify pubkey is trusted
72-
const verification = verifyEvent(event, repo, nip19)
107+
const verification = verifyEvent(event, repo)
73108
if (!verification.ok) {
74109
console.log(`[event] ✗ Rejected: ${verification.reason}`)
75110
return
@@ -78,8 +113,7 @@ async function handleEvent(event, config, nip19) {
78113

79114
// Optional: verify Blocktrails anchor
80115
if (repo.requireAnchor) {
81-
const npub = nip19.npubEncode(event.pubkey)
82-
const anchor = await verifyAnchor(npub, commit)
116+
const anchor = await verifyAnchor(event.pubkey, commit)
83117
if (!anchor.ok) {
84118
console.log(`[event] ✗ Anchor required but not found: ${anchor.reason}`)
85119
return

0 commit comments

Comments
 (0)