#

web of trust

(19 articles)

Data Model for a Neo4j Nostr Relay

# Background Nostr: Notes and Other Stuff Transmitted by Relays \[1] is an open source protocol for censorship-resistant social media. Users maintain public key - private key pairs and use the Schnorr signature standard for digital signatures and encodings. Content is stored in objects called *events* (a.k.a. *notes*) which are signed and broadcast to *relays* which act as the backend servers for Nostr. Relays transmit events with users, with clients, and with each other via websockets following the protocol specified in NIP-01 (Nostr Implementation Possibility-01: Basic Protocol) \[2]. Most Nostr relays store events using relational databases or key-value databases such as LMDB. However, as of Nov 2025, no native graph database nostr relays have been built. This is unfortunate, given the performance advantages of native graph databases in social graph analysis. This prompts us at NosFabrica to construct a fully NIP-01 compliant, native graph database nostr relay based on neo4j. This document provides an overview of a proposed *data model* \[3] for a *neo4j nostr relay*. The primary function of this data model is to provide support for NIP-01. However, it also provides support for Follow Lists (NIP-02), Mute Lists (NIP-51), Reports (NIP-56), Replies (NIP-10), Reactions (NIP-25), Reposts (NIP-18), Comments (NIP-22), and Trusted Assertions (NIP-85) \[4]. # Data Model ## Node labels * NostrUser, NostrEvent, NostrEventTag, NostrRelay (NIP-01) * NostrUserWotMetricsCard (NIP-85: Trusted Assertions)) ## Relationship types * AUTHORS, HAS\_TAG, REFERENCES, SUGGESTED\_RELAY (NIP-01) * FOLLOWS (NIP-02), MUTES (NIP-51), REPORTS (NIP-56) * IS\_A\_REPLY\_TO (NIP-10) * IS\_A\_REACTION\_TO (NIP-25) * IS\_A\_REPOST\_OF (NIP-18) * IS\_A\_COMMENT\_ON (NIP-22) ## Properties See graph below. ## Graph ![](https://i.nostr.build/sbVvsTTjFKAt6anB.png) ## Cypher Cypher code for the above graph. CREATE (:NostrUser)<-\[:FOLLOWS {timestamp: ""}]-(n11:NostrUser {pubkey: ""})-\[:AUTHORS]->(:NostrEvent {id: "\<id>", kind: "\<kind>", sig: "\<sig>", content: "\<content>", created\_at: "\<unix\_time>"})-\[:HAS\_TAG]->(n4:NostrEventTag {tag\_name: "\<p, e, a, d, ...>", tag\_value: "\<pubkey, id, naddr, etc>", relay\_url: "\<url>", `<other_optional_keys>`: ""})-\[:REFERENCES]->(:NostrEvent)-\[:SUGGESTED\_RELAY]->(:NostrRelay {url: "\<url>"}), (:NostrUser)<-\[:REPORTS {timestamp: "", report\_type: ""}]-(n11)-\[:MUTES {timestamp: ""}]->(:NostrUser), (n11)-\[:WOT\_METRICS\_CARD]->(:NostrUserWotMetricsCard {customer\_id: "", hops: "", influence: "", average: "", input: "", confidence: "", personalizedPageRank: "", verifiedFollowerCount: "", verifiedMuterCount: "", verifiedReporterCount: "", followerInput: "", muterInput: "", reporterInput: ""}), (:NostrEvent {kind: 1})-\[:IS\_A\_REPLY\_TO {marker: "\<reply vs root>"}]->(:NostrEvent {kind: 1}), (:NostrEvent {kind: 7})-\[:IS\_A\_REACTION\_TO]->(:NostrEvent), (:NostrEvent {kind: 6})-\[:IS\_A\_REPOST\_OF]->(:NostrEvent {kind: 1}), (:NostrEvent {kind: 16})-\[:IS\_A\_REPOST\_OF]->(:NostrEvent), (:NostrEvent {kind: 1111})-\[:IS\_A\_COMMENT\_ON]->(:NostrEvent), (n4)-\[:REFERENCES]->(:NostrUser) ## Data Volume Statistics from [nostr.band](http://nostr.band) \[5] indicate: * \~ 15 thousand daily users * \~ 1.2 million profiles with a bio and contact list * \~ 300 thousand users in the extended Follows network * \~ 25 thousand daily updates of contact lists (kind 3 events) * \~ 500 thousand new events published per day * \~ 600 million total events published as of Nov 2025 ## References \[1] <https://github.com/nostr-protocol/nostr> \[2] <https://github.com/nostr-protocol/nips/blob/master/01.md> \[3]  <https://neo4j.com/docs/getting-started/data-modeling/tutorial-data-modeling/> \[4] <https://nostrhub.io/naddr1qvzqqqrcvypzq3svyhng9ld8sv44950j957j9vchdktj7cxumsep9mvvjthc2pjuqyt8wumn8ghj7un9d3shjtnswf5k6ctv9ehx2aqqzf68yatnw3jkgttpwdek2un5d9hkuuctys9zn> \[5] <https://stats.nostr.band>

Why we don't use NIP-85

When it comes to the Nostr Web of Trust, several NIPs have been proposed, but the one that seemed to gather the most traction was [Trusted Assertion](https://github.com/nostr-protocol/nips/pull/1534) by [Vitor Pamplona](https://npub.world/npub1gcxzte5zlkncx26j68ez60fzkvtkm9e0vrwdcvsjakxf9mu9qewqlfnj5z). The idea is to have `kind:30382` represent *assertions* made about an *entity*, which could be a *pubkey*, for example. The NIP also specifies a tag called `rank`, which can be used to build some basic WoT primitives. Example event: ``` { "kind": 30382, "tags": [ ["d", "e88a6...50411"], ["rank", "89"], ["zap_amt_sent", "1000000"], ], "content": "", //... } ``` However, we quickly realized that this NIP was too limiting for what we wanted to accomplish: *real-time, personalized ranking*. To illustrate our different approaches, we will show how to replicate our [Verify Reputation](https://vertexlab.io/docs/nips/verify-reputation-dvm/) with trusted assertions. ## Verify Reputation [Verify Reputation](https://vertexlab.io/content/docs/nips/verify-reputation-dvm.md) returns: - the rank, follow count and followers count of the given `target` - its top followers by rank. All ranks are personalized to the given `source`. This “batteries included” solution helps users avoid impersonation and fraud with a single request, no matter what application they are using, be it a kind:1 client, chat app, wallet, marketplace, or more. !\[\](https://vertexlab.io/images/verify_reputation_example.png) To get the same result with trusted assertions, you would have to - Get all contact lists that contain the `target` (as a `p` tag) - Build the target’s `follower` list from those event authors - Get the trusted assertions for the target and each of the followers - Sort them by the rank provided by the trusted assertions This operation is computationally expensive, especially on mobile devices. It is clear that if the user is willing to verify and process a large number of events (potentially +100k), he can compute some (rudimentary) ranking directly on the device, without the need to trust a third party. **More generally, trusted assertions are very hard to use when one doesn’t know the pubkeys of the people he’s looking for.** **This goes fundamentally against our approach of Web of Trust, as a powerful mechanism for discovery.** --- Are you looking to add Web of Trust capabilities to your app or project? [Take a look at our website](https://vertexlab.io/) or [send a DM to Pip](https://npub.world/npub176p7sup477k5738qhxx0hk2n0cty2k5je5uvalzvkvwmw4tltmeqw7vgup).

Introducing Vertex—Social Graph as a Service

After many months of ideation, research, and heads-down building, <span data-type="mention" data-id="726a1e261cc6474674e8285e3951b3bb139be9a773d1acf49dc868db861a1c11" data-label="nostr:npub1wf4pufsucer5va8g9p0rj5dnhvfeh6d8w0g6eayaep5dhps6rsgs43dgh9">@nostr:npub1wf4pufsucer5va8g9p0rj5dnhvfeh6d8w0g6eayaep5dhps6rsgs43dgh9</span> and myself are excited to announce our new project called Vertex. **Vertex’s mission is to provide developers and builders with the most up-to-date and easy-to-use social graph tools**. Our services will enable our future customers to improve the experience they provide by offering: - Protection against **impersonation** and **DoS attacks** - Personalized **discovery** and **recommendations**. All in an open, transparent and interoperable way. ## Open and Interoperable We have structured our services as [NIP-90 Data Vending Machines](https://github.com/nostr-protocol/nips/blob/master/90.md). We are currently using [these DVMs](https://vertexlab.io/docs/nips) and we are eager to hear what the community thinks and if anyone has suggestions for improvements. Regardless of their specific structures, using DVMs means one very important thing: **no vendor lock-in**. Anyone can start processing the same requests and compete with us to offer the most accurate results at the best price. This is very important for us because we are well aware that services like ours can potentially become a central point of failure. The ease with which we can be replaced by a competitor will keep us on our toes and will continue to motivate us to build better and better experiences for our customers, all while operating in an ethical and open manner. Speaking of openness, we have released **all of our code under the [MIT license](https://spdx.org/licenses/MIT.html)**, which means that anyone can review our algorithms, and any company or power user can run their own copies of Vertex if they so wish. We are confident in this decision because the value of Vertex is not in the software. It is in the team who designed and implemented it – and now continually improves, manages and runs it to provide the most accurate results with the lowest latency and highest uptime. # What we offer We currently support three DVMs, but we plan to increase our offering substantially this year. 1. `VerifyReputation`: give your users useful and personalized information to asses the reputation of an npub, minimizing the risk of impersonations. 2. `RecommendFollows`: give your users personalized recommendations about interesting npubs they might want who to follow. 3. `SortAuthors`: give your users the ability to sort replies, comments, zaps, search results or just about anything using authors’ reputations. To learn more, watch this 3-minute walk-through video, and [visit our website](https://vertexlab.io/) https://cdn.satellite.earth/6efabff7da55ce848074351b2d640ca3bde4515060d9aba002461a4a4ddad8d8.mp4 We are also considering offering a custom service to help builders clarify and implement their vision for Web of Trust in their own applications or projects. Please reach out if you are interested.

The Pretty Good way to calculate a user's influence within your web of trust

The Influence of a user on your Grapevine, in some given context, is a function of two variables: 1) the Average Score, which is a weighted average of the relevant trust rating (“Alice trusts Bob a certain amount to filter content in the given context”), and 2) the Input, which is the sum of the weight for each individual rating. Think of Input as the “number of ratings,” where more is better, except that in the Grapevine, not every rating is weighted equally. Influence scales linearly with Average Score, as it should, because a high Average Score is a good thing. But the key to the Grapevine is that Influence does not scale linearly with Input. This is one of the central problems with legacy social media: the follower count has no upper limit, and “influencers” are rewarded for high follower count. But the goal of the Grapevine is to seek quality, not chase followers. Input, like follower count, has no upper limit, so we shouldn’t make Influence proportional to Input. The Pretty Good solution is to replace Input with Certainty, which has an upper limit of 100%. As Figure 1 shows, Certainty starts at 0% and increases quickly at first as Input increases, but then levels off at 100% no matter how high Input gets. Influence then equals that user’s Average Score multiplied by the Certainty. Using this system, the key to Influence is not a high follower count, but a high Average Score. The number of trusted ratings is taken into account but does not dominate the Influence score. Users are rewarded, not for their follower count or number of likes or for attracting more and more ratings, but for gaining the respect of the small handful of people who know them best. We now have a tool to ditch the tyranny of the follower count; to cut through the noise of the social media influencer and to find the high-quality needles in the haystacks that we have been looking for.