initial commit

This commit is contained in:
Biola 2024-09-08 02:53:52 +01:00
commit ae947e2f1f
22 changed files with 743 additions and 0 deletions

2
.dockerignore Normal file
View File

@ -0,0 +1,2 @@
db
# templates

7
.env.example Normal file
View File

@ -0,0 +1,7 @@
RELAY_NAME="utxo WoT relay"
RELAY_PUBKEY="e2ccf7cf20403f3f2a4a55b328f0de3be38558a7d5f33632fdaaefc726c1c8eb"
RELAY_DESCRIPTION="Only notes in utxo WoT"
RELAY_URL="wss://wot.utxo.one"
DB_PATH="db"
INDEX_PATH="templates/index.html"
STATIC_PATH="templates/static"

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
db/
.env
wot-relay

31
Dockerfile Normal file
View File

@ -0,0 +1,31 @@
# Use Golang image based on Debian Bookworm
FROM golang:bookworm
# Set the working directory within the container
WORKDIR /app
# Copy go.mod and go.sum files
COPY go.mod go.sum ./
# Download dependencies
RUN go mod download
# Copy the rest of the application source code
COPY . .
# Set fixed environment variables
ENV DB_PATH="db"
ENV INDEX_PATH="templates/index.html"
ENV STATIC_PATH="templates/static"
# touch a .env (https://github.com/bitvora/wot-relay/pull/4)
RUN touch .env
# Build the Go application
RUN go build -o main .
# Expose the port that the application will run on
EXPOSE 3334
# Set the command to run the executable
CMD ["./main"]

158
README.md Normal file
View File

@ -0,0 +1,158 @@
# WOT Relay
WOT Relay is a Nostr relay that saves all the notes that people you follow, and people they follow are posting.
## Prerequisites
- **Go**: Ensure you have Go installed on your system. You can download it from [here](https://golang.org/dl/).
- **Build Essentials**: If you're using Linux, you may need to install build essentials. You can do this by running `sudo apt install build-essential`.
## Setup Instructions
Follow these steps to get the WOT Relay running on your local machine:
### 1. Clone the repository
```bash
git clone https://github.com/bitvora/wot-relay.git
cd wot-relay
```
### 2. Copy `.env.example` to `.env`
You'll need to create an `.env` file based on the example provided in the repository.
```bash
cp .env.example .env
```
### 3. Set your environment variables
Open the `.env` file and set the necessary environment variables. Example variables include:
```bash
RELAY_NAME="YourRelayName"
RELAY_PUBKEY="YourPublicKey"
RELAY_DESCRIPTION="Your relay description"
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
STATIC_PATH="/home/ubuntu/wot-relay/templates/static" # path to the static folder
```
### 4. Build the project
Run the following command to build the relay:
```bash
go build
```
### 5. Create a Systemd Service (optional)
To have the relay run as a service, create a systemd unit file. Here's an example:
1. Create the file:
```bash
sudo nano /etc/systemd/system/wot-relay.service
```
2. Add the following contents:
```ini
[Unit]
Description=WOT Relay Service
After=network.target
[Service]
ExecStart=/path/to/wot-relay
WorkingDirectory=/path/to/wot-relay
Restart=always
EnvironmentFile=/path/to/.env
[Install]
WantedBy=multi-user.target
```
Replace `/path/to/` with the actual paths where you cloned the repository and stored the `.env` file.
3. Reload systemd to recognize the new service:
```bash
sudo systemctl daemon-reload
```
4. Start the service:
```bash
sudo systemctl start wot-relay
```
5. (Optional) Enable the service to start on boot:
```bash
sudo systemctl enable wot-relay
```
### 6. Start the Project with Docker Compose (optional)
To start the project using Docker Compose, follow these steps:
1. Ensure Docker and Docker Compose are installed on your system.
2. Navigate to the project directory.
3. Ensure the `.env` file is present in the project directory and has the necessary environment variables set.
4. You can also change the paths of the `db` folder and `templates` folder in the `docker-compose.yml` file.
```yaml
volumes:
- "./db:/app/db" # only change the left side before the colon
- "./templates/index.html:/app/templates/index.html" # only change the left side before the colon
- "./templates/static:/app/templates/static" # only change the left side before the colon
```
5. Run the following command:
```sh
# in foreground
docker compose up --build
# in background
docker compose up --build -d
```
6. For updating the relay, run the following command:
```sh
git pull
docker compose build --no-cache
# in foreground
docker compose up
# in background
docker compose up -d
```
This will build the Docker image and start the `wot-relay` service as defined in the `docker-compose.yml` file. The application will be accessible on port 3334.
### 7. Hidden Service with Tor (optional)
Same as the step 6, but with the following command:
```sh
# in foreground
docker compose -f docker-compose.tor.yml up --build
# in background
docker compose -f docker-compose.tor.yml up --build -d
```
You can find the onion address here: `tor/data/relay/hostname`
### 8. Access the relay
Once everything is set up, the relay will be running on `localhost:3334`.
```bash
http://localhost:3334
```
## License
This project is licensed under the MIT License.

13
docker-compose.tor.yml Normal file
View File

@ -0,0 +1,13 @@
services:
tor:
image: lncm/tor:0.4.7.9@sha256:86c2fe9d9099e6376798979110b8b9a3ee5d8adec27289ac4a5ee892514ffe92
container_name: wot-relay-tor
depends_on:
- relay
volumes:
- ./tor/torrc:/etc/tor/torrc
- ./tor/data:/var/lib/tor
restart: on-failure
stop_grace_period: 10m30s

18
docker-compose.yml Normal file
View File

@ -0,0 +1,18 @@
services:
relay:
container_name: wot-relay
build:
context: .
dockerfile: Dockerfile-optimized
# env_file:
# - .env
environment:
RELAY_NAME: "YourRelayName"
RELAY_PUBKEY: "YourPublicKey"
RELAY_DESCRIPTION: "Your relay description"
volumes:
- "./db:/app/db" # only change the left side before the colon
- "./templates/index.html:/app/templates/index.html" # only change the left side before the colon
- "./templates/static:/app/templates/static" # only change the left side before the colon
ports:
- "3334:3334"

39
go.mod Normal file
View File

@ -0,0 +1,39 @@
module github.com/bitvora/wot-relay
go 1.23.0
toolchain go1.23.1
require (
github.com/PowerDNS/lmdb-go v1.9.2 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/fasthttp/websocket v1.5.7 // indirect
github.com/fiatjaf/eventstore v0.8.1 // indirect
github.com/fiatjaf/khatru v0.8.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.3.1 // indirect
github.com/greatroar/blobloom v0.8.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/nbd-wtf/go-nostr v0.34.14 // indirect
github.com/puzpuzpuz/xsync/v3 v3.4.0 // indirect
github.com/rs/cors v1.7.0 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/tidwall/gjson v1.17.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.20.0 // indirect
)

89
go.sum Normal file
View File

@ -0,0 +1,89 @@
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PowerDNS/lmdb-go v1.9.2 h1:Cmgerh9y3ZKBZGz1irxSShhfmFyRUh+Zdk4cZk7ZJvU=
github.com/PowerDNS/lmdb-go v1.9.2/go.mod h1:TE0l+EZK8Z1B4dx070ZxkWTlp8RG1mjN0/+FkFRQMtU=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/fasthttp/websocket v1.5.7 h1:0a6o2OfeATvtGgoMKleURhLT6JqWPg7fYfWnH4KHau4=
github.com/fasthttp/websocket v1.5.7/go.mod h1:bC4fxSono9czeXHQUVKxsC0sNjbm7lPJR04GDFqClfU=
github.com/fiatjaf/eventstore v0.5.1 h1:tTh+JYP0RME51VY2QB2Gvtzj6QTaZAnSVhgZtrYqY2A=
github.com/fiatjaf/eventstore v0.5.1/go.mod h1:r5yCFmrVNT2b1xUOuMnDVS3xPGh97y8IgTcLyY2rYP8=
github.com/fiatjaf/eventstore v0.8.1 h1:51LchQNy0Hpb0YQHwqYR5pKBpfDs/KjySlWCbbz2pkc=
github.com/fiatjaf/eventstore v0.8.1/go.mod h1:bsp0Ibv0CIcVuFcoM2AEerMWmXRhF8uWXMf+dClhuow=
github.com/fiatjaf/khatru v0.8.0 h1:hofUi4qbSqkJiKD4rC9EyNdi9obzBvp3ykJOBxuu/h8=
github.com/fiatjaf/khatru v0.8.0/go.mod h1:jRmqbbIbEH+y0unt3wMUBwqY/btVussqx5SmBoGhXtg=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.0 h1:u0p9s3xLYpZCA1z5JgCkMeB34CKCMMQbM+G8Ii7YD0I=
github.com/gobwas/ws v1.2.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU=
github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/greatroar/blobloom v0.8.0 h1:I9RlEkfqK9/6f1v9mFmDYegDQ/x0mISCpiNpAm23Pt4=
github.com/greatroar/blobloom v0.8.0/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/nbd-wtf/go-nostr v0.34.14 h1:o4n2LkuAtdIjNYJ23sFbcx68UXLnji4j8hYR1Sd2wgI=
github.com/nbd-wtf/go-nostr v0.34.14/go.mod h1:NZQkxl96ggbO8rvDpVjcsojJqKTPwqhP4i82O7K5DJs=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew=
github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/puzpuzpuz/xsync/v3 v3.4.0 h1:DuVBAdXuGFHv8adVXjWWZ63pJq+NRXOWVXlKDBZ+mJ4=
github.com/puzpuzpuz/xsync/v3 v3.4.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

291
main.go Normal file
View File

@ -0,0 +1,291 @@
package main
import (
"context"
"fmt"
"html/template"
"log"
"net/http"
"os"
"sync"
"time"
"github.com/cespare/xxhash"
"github.com/fiatjaf/eventstore/lmdb"
"github.com/fiatjaf/khatru"
"github.com/greatroar/blobloom"
"github.com/joho/godotenv"
"github.com/nbd-wtf/go-nostr"
)
type Config struct {
RelayName string
RelayPubkey string
RelayDescription string
DBPath string
RelayURL string
IndexPath string
StaticPath string
}
var archivePool *nostr.SimplePool
var fetchingPool *nostr.SimplePool
var relays []string
var config Config
var trustNetwork []string
var mu sync.Mutex
func main() {
fmt.Println("starting")
relay := khatru.NewRelay()
ctx := context.Background()
config = LoadConfig()
relay.Info.Name = config.RelayName
relay.Info.PubKey = config.RelayPubkey
relay.Info.Description = config.RelayDescription
appendPubkey(config.RelayPubkey)
db := lmdb.LMDBBackend{
Path: getEnv("DB_PATH"),
}
if err := db.Init(); err != nil {
panic(err)
}
relay.StoreEvent = append(relay.StoreEvent, db.SaveEvent)
relay.QueryEvents = append(relay.QueryEvents, db.QueryEvents)
relay.RejectEvent = append(relay.RejectEvent, func(ctx context.Context, event *nostr.Event) (bool, string) {
for _, pk := range trustNetwork {
if pk == event.PubKey {
return false, ""
}
}
return true, "you are not in the web of trust"
})
mu.Lock()
relays = []string{
"wss://nos.lol",
"wss://nostr.mom",
"wss://purplepag.es",
"wss://purplerelay.com",
"wss://relay.damus.io",
"wss://relay.mostr.pub",
"wss://relay.nos.social",
"wss://relay.nostr.band",
"wss://relay.snort.social",
"wss://relayable.org",
"wss://pyramid.fiatjaf.com",
"wss://relay.primal.net",
"wss://relay.nostr.bg",
"wss://no.str.cr",
"wss://nostr21.com",
"wss://nostrue.com",
"wss://relay.siamstr.com",
}
mu.Unlock()
go refreshTrustNetwork(relay, ctx)
go archiveTrustedNotes(relay, ctx)
mux := relay.Router()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
tmpl := template.Must(template.ParseFiles(os.Getenv("INDEX_PATH")))
data := struct {
RelayName string
RelayPubkey string
RelayDescription string
RelayURL string
}{
RelayName: config.RelayName,
RelayPubkey: config.RelayPubkey,
RelayDescription: config.RelayDescription,
RelayURL: config.RelayURL,
}
err := tmpl.Execute(w, data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
mux.Handle("/favicon.ico", http.StripPrefix("/", http.FileServer(http.Dir(config.StaticPath))))
fmt.Println("running on :3334")
err := http.ListenAndServe(":3334", relay)
if err != nil {
log.Fatal(err)
}
}
func LoadConfig() Config {
godotenv.Load(".env")
config := Config{
RelayName: getEnv("RELAY_NAME"),
RelayPubkey: getEnv("RELAY_PUBKEY"),
RelayDescription: getEnv("RELAY_DESCRIPTION"),
DBPath: getEnv("DB_PATH"),
RelayURL: getEnv("RELAY_URL"),
IndexPath: getEnv("INDEX_PATH"),
StaticPath: getEnv("STATIC_PATH"),
}
return config
}
func getEnv(key string) string {
value, exists := os.LookupEnv(key)
if !exists {
log.Fatalf("Environment variable %s not set", key)
}
return value
}
func refreshTrustNetwork(relay *khatru.Relay, ctx context.Context) []string {
fetchingPool = nostr.NewSimplePool(ctx)
// Function to refresh the trust network
runTrustNetworkRefresh := func() {
timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
filters := []nostr.Filter{{
Authors: []string{config.RelayPubkey},
Kinds: []int{nostr.KindContactList},
}}
for ev := range fetchingPool.SubManyEose(timeoutCtx, relays, filters) {
for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) {
appendPubkey(contact[1])
}
}
chunks := make([][]string, 0)
for i := 0; i < len(trustNetwork); i += 100 {
end := i + 100
if end > len(trustNetwork) {
end = len(trustNetwork)
}
chunks = append(chunks, trustNetwork[i:end])
}
for _, chunk := range chunks {
threeTimeoutCtx, tenCancel := context.WithTimeout(ctx, 10*time.Second)
defer tenCancel()
filters = []nostr.Filter{{
Authors: chunk,
Kinds: []int{nostr.KindContactList},
}}
for ev := range fetchingPool.SubManyEose(threeTimeoutCtx, relays, filters) {
for _, contact := range ev.Event.Tags.GetAll([]string{"p"}) {
if len(contact) > 1 {
appendPubkey(contact[1])
} else {
fmt.Println("Skipping malformed tag: ", contact)
}
}
}
}
fmt.Println("trust network size:", len(trustNetwork))
getTrustNetworkProfileMetadata(relay, ctx)
}
runTrustNetworkRefresh()
ticker := time.NewTicker(10 * time.Minute)
defer ticker.Stop()
for range ticker.C {
runTrustNetworkRefresh()
}
return trustNetwork
}
func getTrustNetworkProfileMetadata(relay *khatru.Relay, ctx context.Context) {
chunks := make([][]string, 0)
for i := 0; i < len(trustNetwork); i += 100 {
end := i + 100
if end > len(trustNetwork) {
end = len(trustNetwork)
}
chunks = append(chunks, trustNetwork[i:end])
}
for _, chunk := range chunks {
timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
filters := []nostr.Filter{{
Authors: chunk,
Kinds: []int{nostr.KindProfileMetadata},
}}
for ev := range fetchingPool.SubManyEose(timeoutCtx, relays, filters) {
relay.AddEvent(ctx, ev.Event)
}
}
}
func appendPubkey(pubkey string) {
mu.Lock()
defer mu.Unlock()
for _, pk := range trustNetwork {
if pk == pubkey {
return
}
}
trustNetwork = append(trustNetwork, pubkey)
}
func archiveTrustedNotes(relay *khatru.Relay, ctx context.Context) {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
archivePool = nostr.NewSimplePool(ctx)
for range ticker.C {
timeout, cancel := context.WithTimeout(ctx, 58*time.Second)
filters := []nostr.Filter{{
Kinds: []int{
nostr.KindArticle,
nostr.KindDeletion,
nostr.KindContactList,
nostr.KindEncryptedDirectMessage,
nostr.KindMuteList,
nostr.KindReaction,
nostr.KindRelayListMetadata,
nostr.KindRepost,
nostr.KindZapRequest,
nostr.KindZap,
nostr.KindTextNote,
},
}}
nKeys := uint64(len(trustNetwork))
bloomFilter := blobloom.NewOptimized(blobloom.Config{
Capacity: nKeys,
FPRate: 1e-4,
})
for _, trustedPubkey := range trustNetwork {
bloomFilter.Add(xxhash.Sum64([]byte(trustedPubkey)))
}
for ev := range archivePool.SubManyEose(timeout, relays, filters) {
if bloomFilter.Has(xxhash.Sum64([]byte(ev.Event.PubKey))) {
if len(ev.Event.Tags) > 2000 {
fmt.Println("skiping note with over 2000 tags")
continue
}
relay.AddEvent(ctx, ev.Event)
}
}
cancel()
}
}

64
templates/index.html Normal file
View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="theme-color" content="#ffffff" />
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{.RelayName}}</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap");
body {
font-family: "Inter", sans-serif;
min-height: 100vh;
display: flex;
flex-direction: column;
}
</style>
</head>
<body class="bg-gray-900 text-white">
<div class="flex-grow flex flex-col justify-center items-center px-4">
<!-- Container -->
<div class="text-center max-w-2xl">
<!-- Heading (Relay Name) -->
<h1 class="text-5xl md:text-6xl font-bold text-purple-400 mb-6">
{{.RelayName}}
</h1>
<!-- Relay Description -->
<p class="text-xl md:text-2xl text-gray-300 mb-8">
{{.RelayDescription}}
</p>
<!-- Relay URL -->
<a
href="#"
class="text-lg md:text-xl text-purple-300 hover:text-purple-400 underline"
>
{{.RelayURL}}
</a>
</div>
</div>
<!-- Footer -->
<footer class="text-center py-6 bg-gray-800 w-full">
<p class="text-lg text-gray-400">
Proudly powered by
<a
href="https://khatru.nostr.technology/"
target="_blank"
class="text-purple-300 hover:text-purple-400 underline"
>Khatru</a
>
|
<a
href="https://github.com/bitvora/wot-relay"
target="_blank"
class="text-purple-300 hover:text-purple-400 underline"
>Web of Trust Relay on Github</a
>
</p>
</footer>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="274.000000pt" height="274.000000pt" viewBox="0 0 274.000000 274.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,274.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M0 1365 l0 -1285 1370 0 1370 0 0 1285 0 1285 -1370 0 -1370 0 0
-1285z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 603 B

2
tor/data/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

2
tor/torrc Normal file
View File

@ -0,0 +1,2 @@
HiddenServiceDir /var/lib/tor/relay
HiddenServicePort 80 relay:3334