Self-hosted Nostr indexer for NIP-35 torrent events with federated curation
Guide to deploying Lighthouse in a federated multi-node configuration.
Lighthouse can operate in multiple modes:
| Mode | Description |
|---|---|
| Standalone | Single node, simple WoT |
| Curator | Content moderation node |
| Consumer | Trusts external curators |
| Federated | Full mesh of curators |
┌─────────────────────────────────────────────────────────────────┐
│ Public Nostr Relays │
└────────────────────────────┬────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Curator │ │ Curator │ │ Curator │
│ Node A │ │ Node B │ │ Node C │
│ (Movies) │ │ (TV) │ │ (Music) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────────────┼───────────────────┘
│
▼
┌──────────────────┐
│ Community Relay │
│ (Curated only) │
└────────┬─────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Consumer │ │ Consumer │ │ Consumer │
│ Node 1 │ │ Node 2 │ │ Node 3 │
└──────────┘ └──────────┘ └──────────┘
| Role | Responsibility |
|---|---|
| Explorer | Collects events from relays |
| Curator | Applies rulesets, signs decisions |
| Indexer | Stores curated content |
| Consumer | Trusts curators, serves users |
# Curator node configuration
server:
port: 9999
trust:
depth: 0 # Strict whitelist mode
indexer:
tag_filter_enabled: true
tag_filter:
- movies
- 4k
curator:
enabled: true
publish_decisions: true
publish_relays:
- "wss://community.relay.example"
./lighthouse
# Consumer node configuration
server:
port: 9999
trust:
depth: 0 # Use curators only
nostr:
relays:
- url: "wss://community.relay.example"
enabled: true
Via UI:
Via API:
curl -X POST http://localhost:9999/api/trust/curators \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"pubkey": "npub1curator...",
"name": "Movie Curator",
"weight": 1.0
}'
Configure how multiple curator decisions combine:
curl -X PUT http://localhost:9999/api/trust/aggregation \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"mode": "quorum",
"quorum_required": 2
}'
A community relay serves curated content only.
# Community relay policy
relay:
enabled: true
policy:
accept_only_curated: true
allowed_kinds:
- 2003 # Torrent metadata
- 30172 # Verification decisions
curator_pubkeys:
- "npub1curator_a..."
- "npub1curator_b..."
Nodes can discover each other via Nostr relay announcements.
relay:
enable_discovery: true
Nodes advertise themselves with Kind 30166 events:
{
"kind": 30166,
"tags": [
["d", "wss://my-relay.example.com"],
["r", "wss://my-relay.example.com"],
["name", "My Lighthouse Node"]
],
"content": "{\"name\":\"My Node\",\"supported_nips\":[1,35]}"
}
Curators publish trust policies to Nostr.
{
"kind": 30173,
"content": "{policy_json}",
"tags": [
["d", "trust-policy"],
["version", "1.0.0"]
],
"pubkey": "curator_pubkey",
"sig": "signature"
}
{
"version": "1.0.0",
"curator_pubkey": "npub1...",
"allowlist": [
{"pubkey": "npub1trusted...", "added_at": "2024-01-01T00:00:00Z"}
],
"denylist": [
{"pubkey": "npub1blocked...", "reason": "spam", "added_at": "2024-01-01T00:00:00Z"}
],
"rulesets": [
{"type": "censoring", "version": "1.0.0", "hash": "sha256..."}
]
}
Curators should rotate keys periodically:
If a key is compromised:
{
"kind": 30174,
"content": "",
"tags": [
["d", "key-revocation"],
["p", "revoked_pubkey"],
["reason", "Key compromised"]
],
"pubkey": "new_pubkey",
"sig": "signature"
}
┌─────────────────┐
│ Load Balancer │
└────────┬────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Instance │ │ Instance │ │ Instance │
│ 1 │ │ 2 │ │ 3 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────┼─────────────┘
│
┌──────┴──────┐
│ Shared DB │
│ (Postgres) │
└─────────────┘
For high-read workloads:
# Node health
curl http://localhost:9999/health
# Indexer status
curl http://localhost:9999/api/indexer/status
# Relay status
curl http://localhost:9999/api/relay/status
# Prometheus scrape config
scrape_configs:
- job_name: 'lighthouse'
static_configs:
- targets: ['localhost:9999']
| Metric | Description |
|---|---|
lighthouse_events_processed |
Events processed |
lighthouse_decisions_total |
Decisions made |
lighthouse_relay_connections |
Connected relays |
lighthouse_torrents_indexed |
Indexed torrents |
# SQLite backup
sqlite3 lighthouse.db ".backup backup.db"
# PostgreSQL backup
pg_dump lighthouse > backup.sql
1 Curator + 1 Community Relay + N Consumers
3 Curators + 2 Relays + N Consumers
Aggregation: quorum=2
10+ Curators (specialized) + 5+ Relays + Many Consumers
Aggregation: weighted by specialty