Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,8 @@ WORKER_COUNT=2 # Defaults to CPU count. Use 1 or 2 for local testing.
# TOR_CONTROL_PORT=9051
# TOR_PASSWORD=
# HIDDEN_SERVICE_PORT=80

# --- I2P (Optional) ---
# To enable I2P, use: ./scripts/start_with_i2p
# I2P tunnel configuration lives in i2p/tunnels.conf and i2p/i2pd.conf.
# No application-level env vars are needed; the i2pd sidecar handles everything.
18 changes: 18 additions & 0 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@ The following environment variables can be set:
| DEBUG | Debugging filter | |
| ZEBEDEE_API_KEY | Zebedee Project API Key | |

## I2P

I2P support is provided as a sidecar container (i2pd) via `docker-compose.i2p.yml`, mirroring the Tor setup. No application-level environment variables are needed — the i2pd container creates an I2P server tunnel that forwards traffic to nostream's WebSocket port.

Configuration files live in the `i2p/` directory:

| File | Description |
|------|-------------|
| `i2p/tunnels.conf` | Defines the I2P server tunnel pointing at nostream (port 8008). |
| `i2p/i2pd.conf` | Minimal i2pd daemon configuration. |

Tunnel keys are persisted at `.nostr/i2p/data/` so the `.b32.i2p` address survives container restarts.

The i2pd web console (tunnel status, `.b32.i2p` destinations) is published to the host on **`127.0.0.1:7070`** only. Remove the `ports:` mapping in `docker-compose.i2p.yml` to disable host-side access.

- Start with I2P: `./scripts/start_with_i2p`
- Print hostname hints: `./scripts/print_i2p_hostname`

If you've set READ_REPLICAS to 4, you should configure RR0_ through RR3_.

# Settings
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,16 @@ Print the Tor hostname:
./scripts/print_tor_hostname
```

Start with I2P:
```
./scripts/start_with_i2p
```

Print the I2P hostname:
```
./scripts/print_i2p_hostname
```

### Importing events from JSON Lines

You can import NIP-01 events from a `.jsonl` file directly into the relay database.
Expand Down Expand Up @@ -626,7 +636,7 @@ To observe client and subscription counts in real-time during a test, you can in
```bash
docker compose logs -f nostream
```
=======

## Export Events

Export all stored events to a [JSON Lines](https://jsonlines.org/) (`.jsonl`) file. Each line is a valid NIP-01 Nostr event JSON object. The export streams rows from the database using cursors, so it works safely on relays with millions of events without loading them into memory.
Expand Down
19 changes: 19 additions & 0 deletions docker-compose.i2p.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
services:
i2pd:
image: purplei2p/i2pd:release-2.59.0
container_name: i2pd
depends_on:
- nostream
Comment thread
archief2910 marked this conversation as resolved.
volumes:
- ${PWD}/.nostr/i2p/data:/home/i2pd/data
- ${PWD}/i2p/i2pd.conf:/home/i2pd/data/i2pd.conf:ro
- ${PWD}/i2p/tunnels.conf:/home/i2pd/data/tunnels.conf:ro
ports:
# i2pd web console — bound to 127.0.0.1 on the host so operators can
# look up the .b32.i2p destination without exposing router state
# to the LAN. Remove this mapping to disable host-side access.
- 127.0.0.1:7070:7070
restart: on-failure
networks:
default:
ipv4_address: 10.10.10.252
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ services:
restart: on-failure
networks:
default:
ipv4_address: 10.10.10.2

nostream-db:
image: postgres:15
Expand Down
18 changes: 18 additions & 0 deletions i2p/i2pd.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Minimal i2pd configuration for nostream.
# Data and keys are persisted via the Docker volume mount at /home/i2pd/data.

[http]
# Bind the web console on all container interfaces so Docker port forwarding
# (127.0.0.1:7070 on the host) can reach it. Host binding is restricted in
# docker-compose.i2p.yml.
address = 0.0.0.0
port = 7070
# Accept requests whose Host header is 127.0.0.1 (port-forwarded) or the
# container's IP. Without this, i2pd returns a "host mismatch" error.
strictheaders = false

[limits]
transittunnels = 256

[precomputation]
elgamal = true
5 changes: 5 additions & 0 deletions i2p/tunnels.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[nostream]
type = http
host = 10.10.10.2
port = 8008
keys = nostream.dat
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@
"tor:docker:compose:start": "./scripts/start_with_tor",
"tor:hostname": "./scripts/print_tor_hostname",
"tor:docker:compose:stop": "./scripts/stop",
"i2p:docker:compose:start": "./scripts/start_with_i2p",
"i2p:hostname": "./scripts/print_i2p_hostname",
"i2p:docker:compose:stop": "./scripts/stop",
"docker:integration:run": "docker compose -f ./test/integration/docker-compose.yml run --rm tests",
"docker:test:integration": "npm run docker:integration:run -- npm run test:integration",
"docker:cover:integration": "npm run docker:integration:run -- npm run cover:integration",
Expand Down
23 changes: 23 additions & 0 deletions scripts/print_i2p_hostname
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
set -euo pipefail

PROJECT_ROOT="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.."
KEYS_FILE="${PROJECT_ROOT}/.nostr/i2p/data/nostream.dat"

if [ ! -f "${KEYS_FILE}" ]; then
echo "I2P destination keys not found. Is the i2pd container running?"
echo "Expected: ${KEYS_FILE}"
exit 1
fi

# The .b32.i2p address is derived from a SHA-256 hash of the Destination
# inside nostream.dat, so we cannot compute it portably from the host.
# Query the running i2pd container instead.
echo "I2P destination keys exist at: ${KEYS_FILE}"
echo ""
echo "To find your nostream .b32.i2p address, use one of these methods:"
echo " 1. Open the i2pd web console: http://127.0.0.1:7070/?page=i2p_tunnels"
echo " (published by docker-compose.i2p.yml, bound to 127.0.0.1 only)"
echo " 2. Query the console from inside the container:"
echo " docker exec i2pd wget -qO- 'http://127.0.0.1:7070/?page=i2p_tunnels' \\"
echo " | grep -oE '[a-z2-7]{52}\\.b32\\.i2p' | sort -u"
40 changes: 40 additions & 0 deletions scripts/start_with_i2p
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash
set -euo pipefail

PROJECT_ROOT="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.."
DOCKER_COMPOSE_FILE="${PROJECT_ROOT}/docker-compose.yml"
DOCKER_COMPOSE_I2P_FILE="${PROJECT_ROOT}/docker-compose.i2p.yml"
I2P_DATA_DIR="${PROJECT_ROOT}/.nostr/i2p/data"
NOSTR_CONFIG_DIR="${PROJECT_ROOT}/.nostr"
SETTINGS_FILE="${NOSTR_CONFIG_DIR}/settings.yaml"
DEFAULT_SETTINGS_FILE="${PROJECT_ROOT}/resources/default-settings.yaml"
CURRENT_DIR="$(pwd)"

if [[ ${CURRENT_DIR} =~ /scripts$ ]]; then
echo "Please run this script from the Nostream root folder, not the scripts directory."
echo "To do this, change up one directory, and then run the following command:"
echo "./scripts/start_with_i2p"
exit 1
fi

if [ "$EUID" -eq 0 ]; then
echo "Error: Nostream should not be run as root."
exit 1
fi

if [[ ! -d "${NOSTR_CONFIG_DIR}" ]]; then
echo "Creating folder ${NOSTR_CONFIG_DIR}"
mkdir -p "${NOSTR_CONFIG_DIR}"
fi

if [[ ! -f "${SETTINGS_FILE}" ]]; then
echo "Copying ${DEFAULT_SETTINGS_FILE} to ${SETTINGS_FILE}"
cp "${DEFAULT_SETTINGS_FILE}" "${SETTINGS_FILE}"
fi

mkdir -p "${I2P_DATA_DIR}"

docker compose \
-f "${DOCKER_COMPOSE_FILE}" \
-f "${DOCKER_COMPOSE_I2P_FILE}" \
up --build --remove-orphans "$@"
14 changes: 9 additions & 5 deletions scripts/stop
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#!/bin/bash
PROJECT_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/.."
set -euo pipefail

PROJECT_ROOT="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.."
DOCKER_COMPOSE_FILE="${PROJECT_ROOT}/docker-compose.yml"
DOCKER_COMPOSE_TOR_FILE="${PROJECT_ROOT}/docker-compose.tor.yml"
DOCKER_COMPOSE_I2P_FILE="${PROJECT_ROOT}/docker-compose.i2p.yml"
DOCKER_COMPOSE_LOCAL_FILE="${PROJECT_ROOT}/docker-compose.local.yml"

docker compose \
-f $DOCKER_COMPOSE_FILE \
-f $DOCKER_COMPOSE_TOR_FILE \
-f $DOCKER_COMPOSE_LOCAL_FILE \
down $@
-f "${DOCKER_COMPOSE_FILE}" \
-f "${DOCKER_COMPOSE_TOR_FILE}" \
-f "${DOCKER_COMPOSE_I2P_FILE}" \
-f "${DOCKER_COMPOSE_LOCAL_FILE}" \
down "$@"
2 changes: 2 additions & 0 deletions test/unit/utils/nip05.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import axios from 'axios'
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import Sinon from 'sinon'
import sinonChai from 'sinon-chai'

chai.use(chaiAsPromised)
chai.use(sinonChai)

import {
extractNip05FromEvent,
Expand Down
Loading