Vertex's avatar
Vertex 11 months ago
🚀 Announcing Vertex A fast, reliable and open source Web of Trust DVM service based on the Personalized Pagerank algorithm. After months of ideation, research and building, we (@Pip the WoT guy and @franzap ) are ready to offer developers and builders the most powerful and easy-to-use social graph tools. We also are open sourcing the code under the MIT license and started the conversation around DVM specs to encourage transparency and interoperability. Three DVMs are the starting point: - Verify Reputation - Recommend Follows - Sort Authors Ask us anything and visit our website at

Replies (66)

This little launch is the culmination of 6 months of researches and a lots of hard work. TLDR; Personalized, real-time, freaking fast, reliable WoT computations. OR "Become Primal without running Primal infrastructure" View quoted note →
Gigi's avatar
Gigi dergigi.com 11 months ago
👀
Vertex's avatar Vertex
🚀 Announcing Vertex A fast, reliable and open source Web of Trust DVM service based on the Personalized Pagerank algorithm. After months of ideation, research and building, we (@Pip the WoT guy and @franzap ) are ready to offer developers and builders the most powerful and easy-to-use social graph tools. We also are open sourcing the code under the MIT license and started the conversation around DVM specs to encourage transparency and interoperability. Three DVMs are the starting point: - Verify Reputation - Recommend Follows - Sort Authors Ask us anything and visit our website at
View quoted note →
I have added your relay into my list of relays (settings -> network -> add a relay). I am unsure how to proceed to use Vertex (I am clueless when it comes to coding). I am using Primal on iOS.
Vertex's avatar
Vertex 11 months ago
Hello! This is a product targeted to developers, but @miljan may be interested in integrating it to Primal so you can benefit
Promising!
Vertex's avatar Vertex
🚀 Announcing Vertex A fast, reliable and open source Web of Trust DVM service based on the Personalized Pagerank algorithm. After months of ideation, research and building, we (@Pip the WoT guy and @franzap ) are ready to offer developers and builders the most powerful and easy-to-use social graph tools. We also are open sourcing the code under the MIT license and started the conversation around DVM specs to encourage transparency and interoperability. Three DVMs are the starting point: - Verify Reputation - Recommend Follows - Sort Authors Ask us anything and visit our website at
View quoted note →
I can def see that. I’m planning to look into the DVM spec and possibly using it as a mechanism to access some of the grapevine server’s endpoints. I’ve been thinking about how clients could put NIP-85 to best use. Perhaps: if you can locate 10040 for the end user, use those scores. If not, each client could have its own default set of scores. Personalized WoT scores if they’re available; if not, default to “global” scores, where global means from the perspective of the client dev. This would allow us to bootstrap the WoT service from scratch, since most users will not have generated NIP-85 scores until it catches on.
As I discussed in the PR convo I recently made kind 10040 and 30382 events available which means client devs now have raw WoT data to play with. They could use it to stratify content, such as reactions, based on the author’s WoT score. And the scores are personalized, not global.
Vertex's avatar
Vertex 11 months ago
On the original note here and the website there is even a video showing how to use it... with nak (that I think you are familiar with)
Vertex's avatar
Vertex 11 months ago
If you were able to see the video, that is the kind of UX and speed we are after. Not sure that is possible with NIP-85, but we could certainly output to it. It would be great if you could look at the spec (which has an entry for graperank and enough fields to make it work - I think) and start returning responses even if they are precomputed/cached. We need to figure out how to store user preferences for these DVMs, need to look at kind 10040 more closely
Vertex's avatar
Vertex 11 months ago
Interesting, things like what? And why exactly? You can also generate things in the background with a real time service (except maybe if you are offline)
Takes a minute or two to publish them all. Nostr.band scores are global, unless I am mistaken. My service and Vertex offer personalized scores, which to me is one of the holy grails of WoT.
Vertex's avatar
Vertex 11 months ago
Interesting. Again - what's the usecase you have in mind? Showing relevant who follow? For precomputed to work you'd have to generate ranks for all possible permutations of pubkeys? @Pip the WoT guy do you have any idea how many those would be for Personalized Pagerank? We have a DVM that sorts authors. You give it a bunch of authors of comments, and it returns them sorted by rank. How do you do that with NIP-85?
I just took this discussion to the above thread and I included links to the NIP-85 Trusted Assertions spec and discussion threads. I think it will be valuable to consider specific use cases and ask whether it makes more sense for clients to access WoT scores via Trusted Assertions versus DVMs (or a combo of both).
Suppose Alice is the logged in user, and you have a list of authors A_I that you wish to sort by rank. Step 1: find Alice’s 10040 note, which tells the client where to find her kind 3038x notes. (If not available, use the platform’s default 10040.) Step 2: for each author A_i, fetch one kind 30382 and extract the score using the “rank” tag. Use that score to sort.
Any given service - Grapevine, Vertex, etc - will have customers. For each customer, let’s assume on the order of 10^5 to 10^6 kind 30382 notes. Each note contains multiple WoT scores. Your service updates these notes periodically and stores them in a relay server. Different levels of service exist with higher levels of service meaning more notes per customer, more scores per note, more frequent updates. Future: all of the above, but managed by a personalized WoT relay.
Follower count, for instance. Only needs to update when a new follows are added or deleted from the user. Then the replaceable event is updated automatically. No need to request a DVM for it. It's already on the phone
The kind 30382 event gets updated with the new follower count by the service and the phone sees that automatically. Then if the user wants to see the actual list of followers, the client will access the service via the DVM for the list of pubkeys. That makes sense to me.
Let me see if I understand the demo in your video. You requested a list of Odell’s followers and you ranked them according to personalized PageRank using franzap’s pubkey as the source node (source = the specific node in the graph from which pPR calculation is initiated). The results came back very quickly. Is this because you precomputed franzap’s personalized PageRank? For me that takes about 15 seconds because the graph is very large, approximately 170,000 pubkeys extending out 7 or 8 hops and involving millions of follows. Or did you restrict the graph to be only Odell’s followers? Which would have fewer nodes, fewer edges and calculate much faster.
we requested the rank of Fran his top 5 followers, with the ranks personalizedPagerank from the POV of Odell. The algo runs on the whole graph of about 300k nodes, and it's done in real time. We've designed a very fast probabilistic version of personalized Pagerank. That's why is so fast
yugo's avatar
yugo 11 months ago
Looks promising👀
For WN specifically you could do a number of cool things without having to reinvent the wheel or having to manage graph databases. Whenever a user receives a message request, you can call Verify Reputation with "source" the user key, and "target" the requester key. This will return a rank of the requester as well as the top followers of the requester (similar to primal top followers on profiles). You can then present the information to the user, and if certain criteria are met you can automatically hide the request (e.g. the rank of the target is very low). Also, when a user is searching another one to send a DM, you could improve the search by name by using SortAuthors. Imagine you are searching "Fiatjaf", and your DB returns 420 profiles. You can then sort these very effectively using their reputations, and then paginate the results.
Thanks man! Yes we will definitely explore this exiting direction in the future. Maybe a more scalable approach is proving somehow ( I don't know ZK well), that we run some software on our servers, that matches our open source repo. Similar to what @OpenSecret are doing
it looks ok to me on a brief browse of the codebase... i have written tree walkers before but not full graphs, graphs are more complicated because of the loops i'd need you to specify the packages and types and functions that you want me to review specifically, i mean, it all looks fairly routine worker loops with channel listener, context done and so forth the documentation comments should not have () after the function/method name, but you can use an article (a, an, the) in front if appropriate to make a more concise or natural sentence. also the Database interface is huge, i think it should be broken into several smaller specific interfaces, very often they come as pairs but can be several associated methods that interface with a specific type aspect of the data, you probably know of io.Writer and io.ReadWriterCloser and such, these are composited so you define one interface and then you embed (just put the interface name instead of a method) and it pulls those methods into the interface set one of the biggest errors that you see in a lot of people's especially early code is mixing what should be two things into one, creating a tight coupling between things that should not be tied together, it makes refactoring and bugfixing harder anyhow, as i said, i've not written full graph structures before... but a general rule with Go is to avoid recursion unless you know it won't go deep, it has no optimization for recursion, best to stick with iterators instead, but i think that's how you wrote it looking at the Walk stuff - i might only say that it can offen be convenient to write these so they pass a function that lets you put the work nearby the type definitions they relate to, and can also be a neat way to enable you to fan out to multiple threads without having to concern yourself with the concurrency on the "outside" of the type (like how http and filepath libraries let you define handlers, one for responding to requests, the other for doing tree walks)
Ok, that makes sense. That’s impressive. The neo4j algo that I’m using is iterative. My default personalized PageRank params are 20 iterations, 0.85 damping factor. The neo4j graph data science library is pretty mature and does have an algorithm called eigenvector centrality which I haven’t played with. Could be faster, I don’t know. I’ve made some measurements on my current implementation. Looks like neo4j takes 3 seconds to project the main graph onto a subgraph and only 2 seconds to run the PageRank algo. Interesting. The graph projection step is only necessary because I’m maintaining a bigger graph that is multipurpose, so those 3 seconds could be shaved off. The rest of the 15 seconds is probably my front end being slow. Regardless, the linear algebra method is inherently faster than the iterative method, as we’ve discussed. It’s nice to see stuff starting to get built in this space.
Just shows how fast things are moving and how much is out there. I’d not heard of NIP-85 at all (or if I had I’d completely forgotten). I’ll have a look.
First of all, thanks for taking the time to look into it :) > i'd need you to specify the packages and types and functions that you want me to review specifically, i mean, it all looks fairly routine worker loops with channel listener, context done and so forth I am glad it looks fairly routine, as that was my goal since I've started using Go just 3 months ago. > also the Database interface is huge, i think it should be broken into several smaller specific interfaces. This is exactly the high-level feedback I am more interested in, thanks! Yes, the Database and RandomWalkStore interfaces are huge. First of all, do u think they should be interfaces at all? I've taken this approach for separating testing from performance optimization. I first test that the algorithm and the data structures work together (e.g. the walks have the correct statistical distribution, and the algorithm uses that distribution correctly). For this purpose I found useful and quicker to use in-memory database mocks. The thing is that I need to tests on several graphs, and produce a big number of walks. Doing that on Redis without having it optimized will take a lot of time per test, possibly minutes. By using mocks to test I reduced that to seconds. Once it works, I can test it with redis and optimize the perfomance. The tradeoff is that I am testing on something that is not the production code but a mock, which also requires work to be maintained and so on. Secondly, how would you split the interface? The first idea that comes to mind is to have a DatabaseReader that has NodeByID, NodeByKey, NodeIDs, Pubkeys, Follows, Followers methods. a DatabaseWriter that has AddNode and Update a DatabaseScanner that has ScanNodes and AllNodes Do u think this would be better?
with the interface decomposition, establish the common contexts that overlap between the methods and if you recognise they are independent then they can be made into their own, like for example if a set of methods mainly relate to filtering, then they are filtering methods, if a set of methods are about counting, they are counting methods, if they are iterators (or walkers) they are this type it can be good to make an interface for anything that could have an entirely differently constructed API behind it, thus necessitating a shim or adapter that translates from one API to another (and this is why interfaces are an indispensible construct that is neglected by all but Java programmers other than Go) and, regarding the last parts about the interface splitting there, i mentioned above from the first how to figure it out, it could be 2 or 3, just from what i saw, but i wasn't familiar enough with their semantics to catch that creating a neutral interface for the database makes a lot of sense too, because you have used redis there, which is fine, but by using an interface instead of directly coupling to the other API you retain the flexibility to use a more optimal store, or more distributed, or byzantine fault tolerant even, you can also then too use "gateways" that cache access to other data and so on and so on and i think i mentioned this, but making "iterator/walker function types" that the iterator calls with the data from the current node, you can simplify isolating your per-node scope but also enable concurrent safe access to a shared state, which is essential in order to parallelise the processing, again, you may not need it now but if your API calls methods that contain the node state data they can handle locking the data for teh case that concurrently that might be part of a pool of data objects, so not just for enabling synchronisation, but you can also enable bespoke memory and scheduling independant from the primary calculation work, in order to change the way the data flows through
thanks a lot man. > and i think i mentioned this, but making "iterator/walker function types" that the iterator calls with the data from the current node, you can simplify isolating your per-node scope but also enable concurrent safe access to a shared state... I'm sorry but I don't understand it. Could you please make an example?
http library has several good examples a simple one that can be part of path routers these closures can themselves be interfaces as this very common example: but another version that uses concrete types, and the distinction between them is that the data provided by the closure is - very often, builtin types:
no problem... it's something i've wanted to do for a long time, help people learn how to do this stuff... it all took me a lot of time to get the opportunity to teach myself how to use it but because i have that experience and knowledge now, i can help you find the right methodology sooner because often times that's just knowing it exists