mirror of
https://github.com/bitvora/wot-relay.git
synced 2025-06-05 17:02:02 +00:00
cleaner logic, fix segfault, update readme
This commit is contained in:
parent
dd9763e967
commit
0505740f1e
12
.env.example
12
.env.example
@ -1,7 +1,17 @@
|
|||||||
|
# Relay Metadata
|
||||||
RELAY_NAME="utxo WoT relay"
|
RELAY_NAME="utxo WoT relay"
|
||||||
RELAY_PUBKEY="e2ccf7cf20403f3f2a4a55b328f0de3be38558a7d5f33632fdaaefc726c1c8eb"
|
RELAY_PUBKEY="e2ccf7cf20403f3f2a4a55b328f0de3be38558a7d5f33632fdaaefc726c1c8eb" # not your npub!
|
||||||
RELAY_DESCRIPTION="Only notes in utxo WoT"
|
RELAY_DESCRIPTION="Only notes in utxo WoT"
|
||||||
RELAY_URL="wss://wot.utxo.one"
|
RELAY_URL="wss://wot.utxo.one"
|
||||||
|
|
||||||
|
# where we should store the database
|
||||||
DB_PATH="db"
|
DB_PATH="db"
|
||||||
|
|
||||||
|
# where we should store the index.html and static files
|
||||||
INDEX_PATH="templates/index.html"
|
INDEX_PATH="templates/index.html"
|
||||||
STATIC_PATH="templates/static"
|
STATIC_PATH="templates/static"
|
||||||
|
|
||||||
|
# relay behavior
|
||||||
|
|
||||||
|
# how often to refresh the relay's view of the WoT in HOURS
|
||||||
|
REFRESH_INTERVAL_HOURS=24
|
68
README.md
68
README.md
@ -1,6 +1,6 @@
|
|||||||
# WOT Relay
|
# WoT Relay
|
||||||
|
|
||||||
WOT Relay is a Nostr relay that saves all the notes that people you follow, and people they follow are posting.
|
WOT Relay is a Nostr relay that saves all the notes that people you follow, and people they follow are posting. It's built on the [Khatru](https://khatru.nostr.technology) framework.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ RELAY_DESCRIPTION="Your relay description"
|
|||||||
DB_PATH="/home/ubuntu/wot-relay/db" # any path you would like the database to be saved.
|
DB_PATH="/home/ubuntu/wot-relay/db" # any path you would like the database to be saved.
|
||||||
INDEX_PATH="/home/ubuntu/wot-relay/templates/index.html" # path to the index.html file
|
INDEX_PATH="/home/ubuntu/wot-relay/templates/index.html" # path to the index.html file
|
||||||
STATIC_PATH="/home/ubuntu/wot-relay/templates/static" # path to the static folder
|
STATIC_PATH="/home/ubuntu/wot-relay/templates/static" # path to the static folder
|
||||||
|
REFRESH_INTERVAL=24 # interval in hours to refresh the web of trust
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Build the project
|
### 4. Build the project
|
||||||
@ -65,10 +66,9 @@ Description=WOT Relay Service
|
|||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/path/to/wot-relay
|
ExecStart=/home/ubuntu/wot-relay/wot-relay #change this to your path
|
||||||
WorkingDirectory=/path/to/wot-relay
|
WorkingDirectory=/home/ubuntu/wot-relay #change this to your path
|
||||||
Restart=always
|
Restart=always
|
||||||
EnvironmentFile=/path/to/.env
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
@ -94,7 +94,63 @@ sudo systemctl start wot-relay
|
|||||||
sudo systemctl enable wot-relay
|
sudo systemctl enable wot-relay
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Start the Project with Docker Compose (optional)
|
#### Permission Issues on Some Systems
|
||||||
|
|
||||||
|
the relay may not have permissions to read and write to the database. To fix this, you can change the permissions of the database folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chmod -R 777 /path/to/db
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Serving over nginx (optional)
|
||||||
|
|
||||||
|
You can serve the relay over nginx by adding the following configuration to your nginx configuration file:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name yourdomain.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:3334;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `yourdomain.com` with your actual domain name.
|
||||||
|
|
||||||
|
After adding the configuration, restart nginx:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Install Certbot (optional)
|
||||||
|
|
||||||
|
If you want to serve the relay over HTTPS, you can use Certbot to generate an SSL certificate.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install certbot python3-certbot-nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
After installing Certbot, run the following command to generate an SSL certificate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo certbot --nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Follow the instructions to generate the certificate.
|
||||||
|
|
||||||
|
### 8. Access the relay
|
||||||
|
|
||||||
|
Once everything is set up, the relay will be running on `localhost:3334` or your domain name if you set up nginx.
|
||||||
|
|
||||||
|
## Start the Project with Docker Compose
|
||||||
|
|
||||||
To start the project using Docker Compose, follow these steps:
|
To start the project using Docker Compose, follow these steps:
|
||||||
|
|
||||||
|
95
main.go
95
main.go
@ -7,6 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ type Config struct {
|
|||||||
RelayURL string
|
RelayURL string
|
||||||
IndexPath string
|
IndexPath string
|
||||||
StaticPath string
|
StaticPath string
|
||||||
|
RefreshInterval int
|
||||||
}
|
}
|
||||||
|
|
||||||
var pool *nostr.SimplePool
|
var pool *nostr.SimplePool
|
||||||
@ -36,19 +38,27 @@ var mu sync.Mutex
|
|||||||
var trustNetworkFilter *blobloom.Filter
|
var trustNetworkFilter *blobloom.Filter
|
||||||
var trustNetworkFilterMu sync.Mutex
|
var trustNetworkFilterMu sync.Mutex
|
||||||
var seedRelays []string
|
var seedRelays []string
|
||||||
|
var booted bool
|
||||||
|
var oneHopNetwork []string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
booted = false
|
||||||
green := "\033[32m"
|
green := "\033[32m"
|
||||||
reset := "\033[0m"
|
reset := "\033[0m"
|
||||||
|
|
||||||
art := `
|
art := `
|
||||||
__ __ ___. _____ ___________ __
|
888 888 88888888888 8888888b. 888
|
||||||
/ \ / \ ____\_ |__ _____/ ____\ \__ ___/______ __ __ _______/ |_
|
888 o 888 888 888 Y88b 888
|
||||||
\ \/\/ // __ \| __ \ / _ \ __\ | | \_ __ \ | \/ ___/\ __\
|
888 d8b 888 888 888 888 888
|
||||||
\ /\ ___/| \_\ \ ( <_> ) | | | | | \/ | /\___ \ | |
|
888 d888b 888 .d88b. 888 888 d88P .d88b. 888 8888b. 888 888
|
||||||
\__/\ / \___ >___ / \____/|__| |____| |__| |____//____ > |__|
|
888d88888b888 d88""88b 888 8888888P" d8P Y8b 888 "88b 888 888
|
||||||
\/ \/ \/ \/
|
88888P Y88888 888 888 888 888 T88b 88888888 888 .d888888 888 888
|
||||||
`
|
8888P Y8888 Y88..88P 888 888 T88b Y8b. 888 888 888 Y88b 888
|
||||||
|
888P Y888 "Y88P" 888 888 T88b "Y8888 888 "Y888888 "Y88888
|
||||||
|
888
|
||||||
|
Y8b d88P
|
||||||
|
powered by: khatru "Y88P"
|
||||||
|
`
|
||||||
|
|
||||||
fmt.Println(green + art + reset)
|
fmt.Println(green + art + reset)
|
||||||
log.Println("🚀 booting up web of trust relay")
|
log.Println("🚀 booting up web of trust relay")
|
||||||
@ -104,7 +114,6 @@ func main() {
|
|||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
|
||||||
go refreshTrustNetwork(relay, ctx)
|
go refreshTrustNetwork(relay, ctx)
|
||||||
go archiveTrustedNotes(relay, ctx)
|
|
||||||
|
|
||||||
mux := relay.Router()
|
mux := relay.Router()
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -138,6 +147,13 @@ func main() {
|
|||||||
func LoadConfig() Config {
|
func LoadConfig() Config {
|
||||||
godotenv.Load(".env")
|
godotenv.Load(".env")
|
||||||
|
|
||||||
|
if os.Getenv("REFRESH_INTERVAL_HOURS") == "" {
|
||||||
|
os.Setenv("REFRESH_INTERVAL_HOURS", "24")
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshInterval, _ := strconv.Atoi(os.Getenv("REFRESH_INTERVAL_HOURS"))
|
||||||
|
log.Println("🔄 refresh interval set to", refreshInterval, "hours")
|
||||||
|
|
||||||
config := Config{
|
config := Config{
|
||||||
RelayName: getEnv("RELAY_NAME"),
|
RelayName: getEnv("RELAY_NAME"),
|
||||||
RelayPubkey: getEnv("RELAY_PUBKEY"),
|
RelayPubkey: getEnv("RELAY_PUBKEY"),
|
||||||
@ -146,6 +162,7 @@ func LoadConfig() Config {
|
|||||||
RelayURL: getEnv("RELAY_URL"),
|
RelayURL: getEnv("RELAY_URL"),
|
||||||
IndexPath: getEnv("INDEX_PATH"),
|
IndexPath: getEnv("INDEX_PATH"),
|
||||||
StaticPath: getEnv("STATIC_PATH"),
|
StaticPath: getEnv("STATIC_PATH"),
|
||||||
|
RefreshInterval: refreshInterval,
|
||||||
}
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
@ -174,10 +191,10 @@ func updateTrustNetworkFilter() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) []string {
|
func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) {
|
||||||
|
|
||||||
runTrustNetworkRefresh := func() {
|
runTrustNetworkRefresh := func() {
|
||||||
timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
timeoutCtx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
filters := []nostr.Filter{{
|
filters := []nostr.Filter{{
|
||||||
@ -188,25 +205,22 @@ func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) []string {
|
|||||||
log.Println("🔍 fetching owner's follows")
|
log.Println("🔍 fetching owner's follows")
|
||||||
for ev := range pool.SubManyEose(timeoutCtx, seedRelays, filters) {
|
for ev := range pool.SubManyEose(timeoutCtx, seedRelays, filters) {
|
||||||
for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) {
|
for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) {
|
||||||
appendPubkey(contact[1])
|
appendOneHopNetwork(contact[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
follows := make([]string, len(trustNetwork))
|
|
||||||
copy(follows, trustNetwork)
|
|
||||||
|
|
||||||
log.Println("🌐 building web of trust graph")
|
log.Println("🌐 building web of trust graph")
|
||||||
for i := 0; i < len(follows); i += 200 {
|
for i := 0; i < len(oneHopNetwork); i += 100 {
|
||||||
timeout, cancel := context.WithTimeout(ctx, 3*time.Second)
|
timeout, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
end := i + 200
|
end := i + 100
|
||||||
if end > len(follows) {
|
if end > len(oneHopNetwork) {
|
||||||
end = len(follows)
|
end = len(oneHopNetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
filters = []nostr.Filter{{
|
filters = []nostr.Filter{{
|
||||||
Authors: follows[i:end],
|
Authors: oneHopNetwork[i:end],
|
||||||
Kinds: []int{nostr.KindContactList, nostr.KindRelayListMetadata, nostr.KindProfileMetadata},
|
Kinds: []int{nostr.KindContactList, nostr.KindRelayListMetadata, nostr.KindProfileMetadata},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@ -231,18 +245,11 @@ func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) []string {
|
|||||||
log.Println("🔗 relays discovered:", len(relays))
|
log.Println("🔗 relays discovered:", len(relays))
|
||||||
}
|
}
|
||||||
|
|
||||||
runTrustNetworkRefresh()
|
for {
|
||||||
updateTrustNetworkFilter()
|
|
||||||
|
|
||||||
ticker := time.NewTicker(24 * time.Hour)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for range ticker.C {
|
|
||||||
runTrustNetworkRefresh()
|
runTrustNetworkRefresh()
|
||||||
updateTrustNetworkFilter()
|
updateTrustNetworkFilter()
|
||||||
|
archiveTrustedNotes(relay, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return trustNetwork
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendRelay(relay string) {
|
func appendRelay(relay string) {
|
||||||
@ -274,10 +281,25 @@ func appendPubkey(pubkey string) {
|
|||||||
trustNetwork = append(trustNetwork, pubkey)
|
trustNetwork = append(trustNetwork, pubkey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendOneHopNetwork(pubkey string) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
for _, pk := range oneHopNetwork {
|
||||||
|
if pk == pubkey {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pubkey) != 64 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oneHopNetwork = append(oneHopNetwork, pubkey)
|
||||||
|
}
|
||||||
|
|
||||||
func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
|
func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
|
||||||
log.Println("⏳ waiting for trust network to be populated")
|
timeout, cancel := context.WithTimeout(ctx, time.Duration(config.RefreshInterval)*time.Hour)
|
||||||
time.Sleep(1 * time.Minute)
|
|
||||||
timeout, cancel := context.WithTimeout(ctx, 24*time.Hour)
|
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
filters := []nostr.Filter{{
|
filters := []nostr.Filter{{
|
||||||
@ -297,7 +319,8 @@ func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
log.Println("📦 archiving trusted notes...")
|
log.Println("📦 archiving trusted notes...")
|
||||||
var i int64
|
var trustedNotes uint64
|
||||||
|
var untrustedNotes uint64
|
||||||
trustNetworkFilterMu.Lock()
|
trustNetworkFilterMu.Lock()
|
||||||
for ev := range pool.SubMany(timeout, seedRelays, filters) {
|
for ev := range pool.SubMany(timeout, seedRelays, filters) {
|
||||||
if trustNetworkFilter.Has(xxhash.Sum64([]byte(ev.Event.PubKey))) {
|
if trustNetworkFilter.Has(xxhash.Sum64([]byte(ev.Event.PubKey))) {
|
||||||
@ -305,9 +328,11 @@ func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
relay.AddEvent(ctx, ev.Event)
|
relay.AddEvent(ctx, ev.Event)
|
||||||
i++
|
trustedNotes++
|
||||||
|
} else {
|
||||||
|
untrustedNotes++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
trustNetworkFilterMu.Unlock()
|
trustNetworkFilterMu.Unlock()
|
||||||
fmt.Println("📦 archived", i, "trusted notes")
|
log.Println("📦 archived", trustedNotes, "trusted notes and discarded", untrustedNotes, "untrusted notes")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user