Skip to content

Commit 03383fc

Browse files
Initial scaffold: Nostr-based git sync daemon (NIP-34)
Subscribes to kind:30618 repo state events and triggers git pull when trusted publishers announce new commits. Uses did:nostr keys for unified identity across Nostr, Bitcoin (Blocktrails), and ACLs.
0 parents  commit 03383fc

11 files changed

Lines changed: 848 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
repos.json
3+
*.log

README.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# nostr-git-sync
2+
3+
Decentralized git sync daemon using Nostr (NIP-34).
4+
5+
## Overview
6+
7+
```
8+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
9+
│ Publisher │ │ Nostr Relays │ │ Subscribers │
10+
│ (git push) │────▶│ (30618 events) │────▶│ (git pull) │
11+
└─────────────────┘ └─────────────────┘ └─────────────────┘
12+
```
13+
14+
When a publisher pushes to a repo, they broadcast a NIP-34 `kind:30618` event announcing the new state. Subscribers watching for that repo automatically pull the update.
15+
16+
## Key Features
17+
18+
- **Unified identity**: Same secp256k1 key for Nostr, Bitcoin (Blocktrails), and ACLs
19+
- **NIP-34 compatible**: Uses standard `kind:30618` repo state events
20+
- **ACL-based trust**: Only pull from `did:nostr:npub...` keys you trust
21+
- **Optional Blocktrails verification**: Require Bitcoin-anchored commits
22+
23+
## Install
24+
25+
```bash
26+
npm install
27+
cp repos.json.example repos.json
28+
# Edit repos.json with your config
29+
```
30+
31+
## Usage
32+
33+
### Subscriber (daemon)
34+
35+
```bash
36+
# Start watching for updates
37+
npm start
38+
39+
# Or with custom config
40+
node bin/nostr-git-sync.js /path/to/repos.json
41+
```
42+
43+
### Publisher (after git push)
44+
45+
```bash
46+
# Announce new repo state
47+
node src/publish.js <nsec> <repo-id> [repo-path]
48+
49+
# Example
50+
node src/publish.js nsec1... JavaScriptSolidServer .
51+
```
52+
53+
## Configuration
54+
55+
```json
56+
{
57+
"relays": ["wss://relay.damus.io", "wss://nos.lol"],
58+
"repos": {
59+
"JavaScriptSolidServer": {
60+
"path": "/var/www/jss",
61+
"cloneUrl": "https://github.com/...",
62+
"branch": "gh-pages",
63+
"trusted": ["did:nostr:npub1..."],
64+
"requireAnchor": false,
65+
"postSync": "npm install"
66+
}
67+
}
68+
}
69+
```
70+
71+
| Field | Description |
72+
|-------|-------------|
73+
| `path` | Local path to git repo |
74+
| `cloneUrl` | Git remote URL (for future clone support) |
75+
| `branch` | Only sync this branch (optional) |
76+
| `trusted` | Array of `did:nostr:npub...` keys allowed to trigger sync |
77+
| `requireAnchor` | Require Blocktrails verification before sync |
78+
| `postSync` | Command to run after successful sync |
79+
80+
## NIP-34 Event Structure
81+
82+
### Kind 30618: Repository State
83+
84+
```json
85+
{
86+
"kind": 30618,
87+
"pubkey": "<publisher-hex>",
88+
"tags": [
89+
["d", "JavaScriptSolidServer"],
90+
["refs/heads/gh-pages", "7a356be..."]
91+
],
92+
"content": ""
93+
}
94+
```
95+
96+
## Identity Model
97+
98+
The same secp256k1 keypair is used across:
99+
100+
- **Nostr**: Signs 30618 events (`npub1...`)
101+
- **Bitcoin**: Controls UTXOs via Blocktrails key tweaking
102+
- **ACL**: `did:nostr:npub...` format in trusted list
103+
104+
## Roadmap
105+
106+
- [ ] 30617 discovery (fetch clone URLs from Nostr)
107+
- [ ] Blocktrails anchor verification
108+
- [ ] Git hook for auto-publish
109+
- [ ] Systemd service file
110+
- [ ] Multi-branch support
111+
112+
## License
113+
114+
MIT

bin/nostr-git-sync.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env node
2+
3+
import { startDaemon } from '../src/daemon.js'
4+
5+
const configPath = process.argv[2] || 'repos.json'
6+
7+
console.log(`Loading config from: ${configPath}`)
8+
9+
startDaemon(configPath).catch(err => {
10+
console.error('Fatal error:', err.message)
11+
process.exit(1)
12+
})

0 commit comments

Comments
 (0)