A graph database backend implementation for the ORLY Nostr relay using Neo4j.
docker run -d --name neo4j \
-p 7474:7474 -p 7687:7687 \
-e NEO4J_AUTH=neo4j/password \
neo4j:5.15
All Neo4j configuration is defined in app/config/config.go and visible via ./orly help:
export ORLY_DB_TYPE=neo4j
export ORLY_NEO4J_URI=bolt://localhost:7687
export ORLY_NEO4J_USER=neo4j
export ORLY_NEO4J_PASSWORD=password
Note: Configuration is centralized inapp/config/config.go. Do not useos.Getenv()directly in package code - all environment variables should be passed via thedatabase.DatabaseConfigstruct.
./orly
See docs/NEO4J_BACKEND.md for comprehensive documentation on:
All tags, including e (event references) and p (pubkey mentions), are stored through intermediate Tag nodes:
Event -[:TAGGED_WITH]-> Tag{type:'e',value:eventId} -[:REFERENCES]-> Event
Event -[:TAGGED_WITH]-> Tag{type:'p',value:pubkey} -[:REFERENCES]-> NostrUser
Event -[:TAGGED_WITH]-> Tag{type:'t',value:topic} (no REFERENCES for regular tags)
Benefits:
#e and #p filter queries work correctlyMigration: Existing databases with direct REFERENCES/MENTIONS relationships are automatically migrated at startup via v3 migration.
This package includes schema support for Web of Trust trust metrics computation:
- NostrUser nodes with trust metrics (influence, PageRank, verified counts) - NostrUserWotMetricsCard nodes for personalized multi-tenant metrics - Social graph relationships (FOLLOWS, MUTES, REPORTS) - Cypher schema definitions and example queries
- Algorithm implementations (GrapeRank, Personalized PageRank) - Event processing logic for kinds 0, 3, 1984, 10000 - Multi-tenant architecture and configuration - Performance considerations and deployment modes
Note: The WoT schema is applied automatically but WoT features are not yet fully implemented. See ADDITIONAL_REQUIREMENTS.md for the roadmap.
neo4j.go - Main database implementationschema.go - Graph schema and index definitions (includes WoT extensions)query-events.go - REQ filter to Cypher translationsave-event.go - Event storage with relationship creationfetch-event.go - Event retrieval by serial/IDserial.go - Serial number managementmarkers.go - Metadata key-value storageidentity.go - Relay identity managementdelete.go - Event deletion (NIP-09)subscriptions.go - Subscription managementnip43.go - Invite-based ACL (NIP-43)import-export.go - Event import/exportlogger.go - Logging infrastructureREADME.md - This fileWOT_SPEC.md - Web of Trust data model specificationADDITIONAL_REQUIREMENTS.md - WoT implementation requirements and gapsEVENT_PROCESSING_SPEC.md - Event-driven vertex management specificationIMPLEMENTATION_SUMMARY.md - Implementation overview and statusTESTING.md - Test guide and troubleshootingThe Brainstorm prototype_ Neo4j Data Model.html - Original Brainstorm specification documentsocial-event-processor_test.go - Comprehensive tests for kinds 0, 3, 1984, 10000tag_model_test.go - Tag-based e/p model tests and filter query testssave-event_test.go - Event storage and relationship tests# Start Neo4j using docker-compose
cd pkg/neo4j
docker-compose up -d
# Wait for Neo4j to be ready (~30 seconds)
docker-compose logs -f neo4j # Look for "Started."
# Set Neo4j connection
export ORLY_NEO4J_URI="bolt://localhost:7687"
export ORLY_NEO4J_USER="neo4j"
export ORLY_NEO4J_PASSWORD="testpass123"
# Run all tests
go test -v
# Run social event processor tests
go test -v -run TestSocialEventProcessor
# Cleanup
docker-compose down -v
The social-event-processor_test.go file contains comprehensive tests for:
See TESTING.md for detailed test documentation, troubleshooting, and how to view the graph in Neo4j Browser.
After running tests, explore the graph at http://localhost:7474:
// View all social relationships
MATCH path = (u1:NostrUser)-[r:FOLLOWS|MUTES|REPORTS]->(u2:NostrUser)
RETURN path
// View event processing history
MATCH (evt:ProcessedSocialEvent)
RETURN evt ORDER BY evt.created_at
MATCH (e:Event {pubkey: "abc123..."})
RETURN e
ORDER BY e.created_at DESC
MATCH (e:Event)-[:TAGGED_WITH]->(t:Tag {type: "t", value: "bitcoin"})
RETURN e
MATCH (e:Event)-[:TAGGED_WITH]->(t:Tag {type: "e"})-[:REFERENCES]->(ref:Event)
WHERE e.id = "abc123..."
RETURN e, ref
MATCH (e:Event)-[:TAGGED_WITH]->(t:Tag {type: "p"})-[:REFERENCES]->(u:NostrUser)
WHERE e.id = "abc123..."
RETURN e, u
MATCH (author:NostrUser {pubkey: "abc123..."})
<-[:AUTHORED_BY]-(e:Event)
-[:TAGGED_WITH]->(:Tag {type: "p"})-[:REFERENCES]->(mentioned:NostrUser)
RETURN author, e, mentioned
EXPLAIN and PROFILE to analyze query performanceNostr is inherently a social graph with heavy relationship queries:
Neo4j excels at these patterns, making it a natural fit for relationship-heavy Nostr queries.
Same as ORLY relay project.