Fix lower case characters at beginning of sentences. Some punctuation and minor clarifications changes

master
Laurin Weger 2 months ago
parent 8c9cb81785
commit 518b89cda1
No known key found for this signature in database
GPG Key ID: 9B372BB0B792770F
  1. 4
      src/pages/en/architecture.md
  2. 16
      src/pages/en/documents.md
  3. 6
      src/pages/en/encryption.md
  4. 2
      src/pages/en/features.md
  5. 18
      src/pages/en/framework/crdts.md
  6. 14
      src/pages/en/framework/nuri.md
  7. 2
      src/pages/en/framework/permissions.md
  8. 8
      src/pages/en/framework/semantic.md
  9. 2
      src/pages/en/framework/transactions.md
  10. 6
      src/pages/en/getting-started.md
  11. 6
      src/pages/en/help.md
  12. 2
      src/pages/en/network.md
  13. 28
      src/pages/en/protocol.md
  14. 6
      src/pages/en/social-network.md
  15. 38
      src/pages/en/specs/format-repo.md
  16. 20
      src/pages/en/specs/protocol-app.md
  17. 14
      src/pages/en/specs/protocol-client.md
  18. 8
      src/pages/en/verifier.md
  19. 8
      src/pages/en/wallet.md

@ -30,11 +30,11 @@ We distinguish 2 roles :
The brokers have 2 interfaces:
- they participate between each other, to the Core Network, which is a P2P network with topology of a **general undirected graph**, using the Core Protocol.
- they expose an interface to Verifiers that use the Client Protocol. this is a typical client-server communication, with star topology.
- they expose an interface to Verifiers that use the Client Protocol. This is a typical client-server communication, with star topology.
A verifier only connects to one broker at a time, and needs to register and authenticate in order to access the broker.
Verifiers do not talk to each other (Except in some rare cases when reconciliation is needed. not implemented yet). Instead they talk to their Broker, and it is the Broker who does all the job of maintaining the pub/sub and forwarding events.
Verifiers do not talk to each other (Except in some rare cases when reconciliation is needed. Not implemented yet). Instead they talk to their Broker, and it is the Broker who does all the job of maintaining the pub/sub and forwarding events.
Eventually, an Application connects to a Verifier (locally or remotely) and exchanges with it using the App protocol. This protocol exchanges plaintext data.

@ -26,7 +26,7 @@ So to recap, in a Document we have :
- the **Graph** part (some RDF triples). This is mandatory and always available in all documents, even if left empty.
- the **Discrete** part (some JSON, XML or plaintext. based on Yjs or Automerge CRDTs). This can be optional, meaning that it is possible to create a purely graph-based document that doesn't hold any discrete part.
- the **Discrete** part (some JSON, XML or plaintext. Based on Yjs or Automerge CRDTs). This can be optional, meaning that it is possible to create a purely graph-based document that doesn't hold any discrete part.
- some optional **binary files** attached to the document.
@ -48,7 +48,7 @@ It follows the **DID** (Decentralized Identifier) format, and an adhoc DID Metho
When you create a new Document, you first have to select its **type**, which is also called "primary class" internally.
There are already several types proposed to you (not all of them are currently implemented. see a list in [features](/en/features) ), and App developers can add new types.
There are already several types proposed to you (not all of them are currently implemented. See a list in [features](/en/features) ), and App developers can add new types.
Here is a short list of the most used types :
@ -108,9 +108,9 @@ To be more precise, this primary class and all those commits, are not stored for
A **branch** is a concept that helps us separate the data within a single document, and it can be used in several ways.
- A document can be composed of several blocks. Imagine the blocks like separate paragraphs in a text document, or separate embeds (video, table, spreadsheet, etc...) of content that come from other documents or that have different types, and you want to assemble all those blocks into one document. So you select those blocks and put them one after another. When the reader opens a document, they will not see that it is made of several blocks. to them it will just appear to be one document that they scroll into.
- A document can be composed of several blocks. Imagine the blocks like separate paragraphs in a text document, or separate embeds (video, table, spreadsheet, etc...) of content that come from other documents or that have different types, and you want to assemble all those blocks into one document. So you select those blocks and put them one after another. When the reader opens a document, they will not see that it is made of several blocks. To them it will just appear to be one document that they scroll into.
- We also use branches when we want to fork the work that is happening on a Document (let's say in collaborative editing). This can be handy if we want to keep the current version of the Document unchanged, while we are working on a newer version. All the work on the newer version is not visible to the reader, until the "working branch" is merged back into the main branch. This is something that programmers will understand very well, but that can be very handy for everybody else too. Please note that we use the terms "fork" and "merge" here as we did when we talked about CRDTs earlier, but it is not exactly the same thing. Here the fork and merge happens only on request from the user. You can decide at some point to fork a document or block. Then you can also decide that it is time to merge it back. But that's a bit different than the automatic and transparent "merge and fork" that is continuously happening every time that someone goes offline. To make things more clear, we don't call the later this way, we just say the document synchronizes itself. fork and merge should be terms used only when the user decides to manually fork.
- We also use branches when we want to fork the work that is happening on a Document (let's say in collaborative editing). This can be handy if we want to keep the current version of the Document unchanged, while we are working on a newer version. All the work on the newer version is not visible to the reader, until the "working branch" is merged back into the main branch. This is something that programmers will understand very well, but that can be very handy for everybody else too. Please note that we use the terms "fork" and "merge" here as we did when we talked about CRDTs earlier, but it is not exactly the same thing. Here the fork and merge happens only on request from the user. You can decide at some point to fork a document or block. Then you can also decide that it is time to merge it back. But that's a bit different than the automatic and transparent "merge and fork" that is continuously happening every time that someone goes offline. To make things more clear, we don't call the later this way, we just say the document synchronizes itself. Fork and merge should be terms used only when the user decides to manually fork.
- Branches and blocks can also be used to store different translation of your content. You do not want to create a new document for each translation. Instead, you create separate blocks within the same document, and each block holds the translated content. NextGraph is designed to help you with managing translation and multi-lingual content, and when the user will open the Document, the most adapted language will be selected for them.
@ -118,11 +118,11 @@ A **branch** is a concept that helps us separate the data within a single docume
- Also, we internally use the branches feature to store some internal data that you don't see, but that needs to have different branches for the good functioning of NextGraph.
One of the advantage of dividing your content into several blocks is also that blocks can have different read permissions. So if you want to share some information with a certain group of people, but not with some other group, but still refer to that as one document, then you can use blocks. By example, let's say you have a Social profile, where you describe yourself, your interest, and where you put some contact information. Maybe the contact details should not be available to everyone (like phone number, postal address, etc..) Still, you do not want to create 2 documents for that purpose. So you will have 2 blocks. one will only contain the minimum public information you want to share to everyone. The other block will contain more details like phone number and address, and it will include also a link to the other block. This way, you don't have to copy paste the general information about you in both blocks. The "include link" is transparent for the privileged reader who got access to the more private profile. they will not see that the document is composed of 2 blocks. to them it will just appear as one document. And of course, all the other readers who only got the link to the generic block, will not see that there exists another block that contains more private details.
One of the advantage of dividing your content into several blocks is also that blocks can have different read permissions. So if you want to share some information with a certain group of people, but not with some other group, but still refer to that as one document, then you can use blocks. By example, let's say you have a Social profile, where you describe yourself, your interest, and where you put some contact information. Maybe the contact details should not be available to everyone (like phone number, postal address, etc..) Still, you do not want to create 2 documents for that purpose. So you will have 2 blocks. One will only contain the minimum public information you want to share to everyone. The other block will contain more details like phone number and address, and it will include also a link to the other block. This way, you don't have to copy paste the general information about you in both blocks. The "include link" is transparent for the privileged reader who got access to the more private profile. They will not see that the document is composed of 2 blocks. To them it will just appear as one document. And of course, all the other readers who only got the link to the generic block, will not see that there exists another block that contains more private details.
So, to recap. Each branch holds a separate list of commits, that can be seen in the History pane.
For now the App does not let you create new blocks and branches, but it will come very soon. internally, the branches are already there, and they work well. What you see for now in the App when you open a document, is called the "main" branch. Later, you will be able to add more blocks and branches.
For now the App does not let you create new blocks and branches, but it will come very soon. Internally, the branches are already there, and they work well. What you see for now in the App when you open a document, is called the "main" branch. Later, you will be able to add more blocks and branches.
Those who are more curious about the internals of the branch, commits, DAG (Directed Acyclic Graph) used in NextGraph, can refer to the [Repo format](/en/specs/format-repo) of the specs.
@ -142,7 +142,7 @@ Those are :
- the **protected** store : is a space where you can share data, documents, and media with other users, but they will need a special link and permission in order to access them and collaborate with you on those documents (or just read them). You can also create Groups here when you want to chat or collaborate with several users. This store also acts as your protected profile for social networks. Only your followers will be able to see this profile, as it is not public.
- the **private** store : this is a place where you put only private and personal information that only you have access to. This store will be accessible to all your devices, so it is useful in order to synchronize private data between them. You can put sensitive data too, as everything is encrypted. Nobody else will ever see this data. it is not possible to share the documents of your private store with anybody else.
- the **private** store : this is a place where you put only private and personal information that only you have access to. This store will be accessible to all your devices, so it is useful in order to synchronize private data between them. You can put sensitive data too, as everything is encrypted. Nobody else will ever see this data. It is not possible to share the documents of your private store with anybody else.
You can also later, move documents from one store to another, if you are the owner of such document.
@ -154,7 +154,7 @@ And your public profile/store can be found with the icon that represents one use
We said that you can create **Groups** when you want to share, exchange and collaborate with other users. In fact, each Group is a separate Store. This is helpful because each group can have its own set of permissions, and then you can configure the store so that all the documents included in this store, inherit the permissions of the store. This way, we can manage the group easily. If we add a member to the group, they immediately get access to all the documents in the group. The same happens when you remove a user from the group: they loose access to all the documents at once.
Because most of the time, collaboration on Documents happens within a group of users, that will probably share more than one document between each other, the Store regroups all the Documents that such Group wants to interact with. Not to confuse this Group with the E2EE group i was referring to in the [Encryption chapter](/en/encryption). In fact, in NextGraph terminology, we never talk about any E2EE group. Instead we call that a Repo, or a Document. A Repo is basically the equivalent of an E2EE group for one and only one Document. So when we talk about a Group, we are referring instead to a Store that gathers all the users and their peers, and where they will be able to share several Documents (or Repos if you prefer).
Because most of the time, collaboration on Documents happens within a group of users that will probably share more than one document between each other, the Store regroups all the Documents that such Group wants to interact with. Not to confuse this Group with the E2EE group i was referring to in the [Encryption chapter](/en/encryption). In fact, in NextGraph terminology, we never talk about any E2EE group. Instead we call that a Repo, or a Document. A Repo is basically the equivalent of an E2EE group for one and only one Document. So when we talk about a Group, we are referring instead to a Store that gathers all the users and their peers, and where they will be able to share several Documents (or Repos if you prefer).
As you will see, a Store can be organized into folders and sub-folders, making it very easy to keep tidy. It is the equivalent of the "drive" that you have been using in cloud-based sharing system.

@ -26,7 +26,7 @@ Here is the breakdown of the problem.
DNS and TLS certificates are not fully decentralized and they both rely on some single points of failure (and domains are expensive, and hard to setup and manage for the end-user). TLS was discarded by the creator of Signal Protocol by example, because his main goal was to offer a secure and totally decentralized chat protocol. We also have to forgo DNS and TLS in our architecture, for similar reasons. That's not an easy task. Matrix, by example, that is just another iteration on the above mentioned Signal Protocol (reimplemented in permissive licensing, and with a slightly different protocol for group encryption), felt the need to wrap all the E2EE protocol within some classical REST apis with TLS and a domain name, so basically they voided the advantage that Signal had created: no need for DNS nor TLS certificates. The result is Synapse protocol and Home Server: a mix of E2EE and plain old TLS APIs. That's not what we did. In our protocol, every peer is known by its IP and a PeerID that is a public key. There is no DNS, there is no TLS certificate. We use the Noise protocol for encryption of communication between peers, which is, for now, channeled into a WebSocket, but could be transported over other protocols, including UDP, QUIC or WebRTC, by example.
### P2P isn't viable. we need 2-Tier network with Brokers
### P2P isn't viable. We need 2-Tier network with Brokers
Pure Peer to Peer (P2P) is not practical in real life. Even though we claim NextGraph is peer-to-peer (and it is, somehow), our network topology is not exactly peer-to-peer. What we do is called “**2-tier topology**". It means that the peers/replicas do not communicate directly with each other. Instead, they send and receive their data from some tiers that we call “**Brokers**”. The broker is a server that is always up and running, and that replicas can always contact in order to sync their data. To the contrary, a truly P2P topology would imply that the replicas are connecting directly to each other. This is what HyperHyperSpace is doing by example, with WebRTC that establishes connections directly between web-browsers. Several other protocols are doing direct P2P communication. While it looks good on the paper, and is attractive because it seems at first glance as a desirable feature, it turns out that true P2P is not practical because it forces the peers/replicas that want to sync and exchange data, to be both online at the very same moment. This condition is not guaranteed at all and will impede synchronization if not met. What are the chances that any participant of a collaborative group are connected to the internet at the very same time? very low in real life cases. If I want to sync between several of my own devices, does that mean that at least 2 of my devices have to be online at the same time? this is not gonna work. For this reason, we opted for a “2-tier” topology. Peers that are intermittently connected to the internet, can still synchronize because they will talk only to a common “broker” that is always up and running and that is there specially for that. In fact each broker is connected to the other brokers in the overlay, so it doesn't matter to which broker the client is connected.
@ -42,9 +42,9 @@ Like with the double ratchet, we want to renew epoch from time to time. Speciall
### A repo has editors, viewers and signers
In NextGraph, **each Document** has its own E2EE group, that we call a **Repo**. Now, a Repo has editors, obviously, and those should be inside the E2EE group. Sure thing. Then, we also have users that are only “readers”. they cannot edit the document. Should they be part of the E2EE group? how are the permissions going to be enforced? who is going to check if this specific user can edit or not, in a local-first setting? NextGraph uses the concept of capabilities (OCAP) for permissions. A user can read the content of a document if they have the capability to do so. Furthermore, the capability system that we have designed, is not a traditional OCAP mechanism where a “controller” eventually decides on what to do with an incoming action and associated capability. Instead, in NextGraph, there is no need for an almighty controller. We have designed a capability mechanism based on cryptography. If the user possesses the decryption key, then they are entitled to read the data. There is no need for a “controller” to verify the signature of the capability/delegation they hold. Instead, the user just gets some encrypted data, and if they have the correct decryption key, then it means they will be able to decrypt and read the data. if they don't, well, they will see garbage instead. About the “write” permission, it is the same. The editor needs to possess the private key of the Pub/Sub topic that is used to propagate the edits (more on the pub/sub later). So basically, if a user wants to modify the data, they can try as much as they want locally, but if they don’t have the private key of the pub/sub topic, it will not work because they will not be able to propagate their updates in the overlay (the brokers will refuse it). Again, there is no need for a “controller” here that will check a capability, or better said: the capability is included in the message. The “write” permission is just enforced with cryptography.
In NextGraph, **each Document** has its own E2EE group, that we call a **Repo**. Now, a Repo has editors, obviously, and those should be inside the E2EE group. Sure thing. Then, we also have users that are only “readers”. They cannot edit the document. Should they be part of the E2EE group? how are the permissions going to be enforced? who is going to check if this specific user can edit or not, in a local-first setting? NextGraph uses the concept of capabilities (OCAP) for permissions. A user can read the content of a document if they have the capability to do so. Furthermore, the capability system that we have designed, is not a traditional OCAP mechanism where a “controller” eventually decides on what to do with an incoming action and associated capability. Instead, in NextGraph, there is no need for an almighty controller. We have designed a capability mechanism based on cryptography. If the user possesses the decryption key, then they are entitled to read the data. There is no need for a “controller” to verify the signature of the capability/delegation they hold. Instead, the user just gets some encrypted data, and if they have the correct decryption key, then it means they will be able to decrypt and read the data. If they don't, well, they will see garbage instead. About the “write” permission, it is the same. The editor needs to possess the private key of the Pub/Sub topic that is used to propagate the edits (more on the pub/sub later). So basically, if a user wants to modify the data, they can try as much as they want locally, but if they don’t have the private key of the pub/sub topic, it will not work because they will not be able to propagate their updates in the overlay (the brokers will refuse it). Again, there is no need for a “controller” here that will check a capability, or better said: the capability is included in the message. The “write” permission is just enforced with cryptography.
So, coming back to the problem of the E2EE group, we have several types of users with different types of access, that interact within the Repo. in addition to the **editors** and the **readers**, we also have **signers**. Those signers have only one task: Verify and Sign the commits, on request. They are not necessarily editors. But they do need read access in order to read what they are going to sign. So we have **3 types of access** (and a 4th type for the owners of the documents). Anyway. it starts to seem obvious that a classical E2EE group like OpenMLS or Matrix will not be enough, because we do not want the readers, by example, to see what the editors are doing. We don't even want them to see the list of editors. Are we going to create 4 different OpenMLS group? one for each access type? No. That would not work in practice, and not even on the paper.
So, coming back to the problem of the E2EE group, we have several types of users with different types of access, that interact within the Repo. In addition to the **editors** and the **readers**, we also have **signers**. Those signers have only one task: Verify and Sign the commits, on request. They are not necessarily editors. But they do need read access in order to read what they are going to sign. So we have **3 types of access** (and a 4th type for the owners of the documents). Anyway, it starts to seem obvious that a classical E2EE group like OpenMLS or Matrix will not be enough, because we do not want the readers, by example, to see what the editors are doing. We don't even want them to see the list of editors. Are we going to create 4 different OpenMLS group? one for each access type? No. That would not work in practice, and not even on the paper.
So here we are, we designed and implemented a new E2EE group protocol, that takes into account the specific needs of Document editing, signing and sharing, together with cryptographic capabilities. And this… did not exist, as far as we know. Add to this the fact that we also do content-addressing and deduplication with convergent encryption on all the content and pub/sub messages, and you will soon understand why a novel protocol was needed.

@ -33,7 +33,7 @@ Here is the list of types of Document you can create in NextGraph, and their cor
| Code / Svelte (YText) | Source Code | Text Editor |
| Code / React (YText) | Source Code | Text Editor |
Note: (\*) The JSON Editor (in read-write or read-only) is very primitive for now, it has been developed in one day, to showcase the easy binding of JSON data to some Svelte components. It lacks important features like ( insert in array at position. remove at position in array. splice range. remove from map. change type or name of property in map ). Also the general UX is ugly. But it is functional and demonstrate the potential of CRDT on JSON data, that will be leveraged by app developers. Once it will be improved, this Editor and Viewer will be reused for all Data types, including the Yjs based ones, and the Graph one. It also showcases that no matter what the App is doing, you own the data and can always go to see it and even modify it, or export it. That's what a data-centric framework is about.
Note: (\*) The JSON Editor (in read-write or read-only) is very primitive for now, it has been developed in one day, to showcase the easy binding of JSON data to some Svelte components. It lacks important features like ( insert in array at position, remove at position in array, splice range, remove from map, change type or name of property in map ). Also the general UX is ugly. But it is functional and demonstrate the potential of CRDT on JSON data, that will be leveraged by app developers. Once it will be improved, this Editor and Viewer will be reused for all Data types, including the Yjs based ones, and the Graph one. It also showcases that no matter what the App is doing, you own the data and can always go to see it and even modify it, or export it. That's what a data-centric framework is about.
Note: (\*\*) The JSON Editor (in read-write or read-only) used here for the Yjs types, is based on svelte-jsoneditor which isn't so easy to use. But it is a good demonstration that the GUIs can be anything. The data layer works fine and that's the most important for now.

@ -1,6 +1,6 @@
---
title: Conflict-Free Replicated Data Types (CRDT)
description: NextGraph supports several CRDTs natively, and is open to integrating more of them in the future. more about the Unified data model and features of our CRDTs
description: NextGraph supports several CRDTs natively, and is open to integrating more of them in the future. More about the Unified data model and features of our CRDTs
layout: ../../../layouts/MainLayout.astro
---
@ -77,13 +77,13 @@ Now let's have a look at what those CRDTs have in common and what is different b
| **sequence** | ❌ \* | ✅ | ✅ |
| <td colspan=3> And like in JSON or Javascript, some keys can be Arrays (aka list), which preserve the ordering of the elements. (\*) In RDF, storing arrays is a bit more tricky. For that purpose, Collections can encode order, but are not CRDT based nor concurrency safe |
| **sets** | 🔺 multiset | ✅ | ✅ |
| <td colspan=3> RDF predicates (the equivalent of properties or keys in discrete documents) are not unique. they can have multiple values. that's the main difference between the discrete and graph models. We will offer the option to enforce rules on RDF data (with SHACL/SHEX) that could force unicity of keys, but that would require the use of **Synchronous Transactions**. Sets are usually represented in JS/JSON with a map, of which the values are not used (set to null or false), because keys are unique, in JSON we use the keys to represent the set. In RDF, keys are not unique, but a set can be represented by choosing a single key (a predicate) and its many values will represent the set, as a pair "key/value" is unique (aka a "predicate/object" pair). |
| <td colspan=3> RDF predicates (the equivalent of properties or keys in discrete documents) are not unique. They can have multiple values. That's the main difference between the discrete and graph models. We will offer the option to enforce rules on RDF data (with SHACL/SHEX) that could force unicity of keys, but that would require the use of **Synchronous Transactions**. Sets are usually represented in JS/JSON with a map, of which the values are not used (set to null or false), because keys are unique, in JSON we use the keys to represent the set. In RDF, keys are not unique, but a set can be represented by choosing a single key (a predicate) and its many values will represent the set, as a pair "key/value" is unique (aka a "predicate/object" pair). |
| **unique key** | ❌ | ✅ | ✅ |
| <td colspan=3> related to the above |
| **conflict resolution** | ✅ | lamport clock ? | [higher actor ID](https://automerge.org/docs/documents/conflicts/) |
| <td colspan=3> because RDF has no unique keys, it cannot conflict. |
| **CRDT strings in property values** | ❌ | ❌ | ✅ 🔥 |
| <td colspan=3> allows concurrent edits on the value of a property that is a string. this feature is only offered by Automerge. Very useful for collaborative forms or tables/spreadsheets by example! |
| <td colspan=3> allows concurrent edits on the value of a property that is a string. This feature is only offered by Automerge. Very useful for collaborative forms or tables/spreadsheets by example! |
| **multi-lingual strings** | ✅ 🔥 | ❌ | ❌ |
| <td colspan=3> Store the value of a string property in several languages / translations |
| **Counter CRDT** | ❌ | ❌ | ✅ 🔥 |
@ -91,25 +91,25 @@ Now let's have a look at what those CRDTs have in common and what is different b
| **link/ref values (foreign key)** | ✅ 🔥 | ❌ \* | ❌ \* |
| <td colspan=3> (\*) discrete data cannot link to external documents. This is the reason why all Documents in NextGraph have a Graph part, in order to enable inter-linking of data and documents across the Giant Global Graph of Linked Data / Semantic Web |
| **Float values** | ✅ | 🟧 | ✅ |
| <td colspan=3> Yjs doesn't enforce strong typing on values. they can be any valid JSON (and Floats are just Numbers). |
| <td colspan=3> Yjs doesn't enforce strong typing on values. They can be any valid JSON (and Floats are just Numbers). |
| **Date values** | ✅ | ❌ | ✅ |
| <td colspan=3> JSON doesn't support JS Date datatype. So for the same reason as above, Yjs doesn't support Dates. |
| **Binary buffer values** | ✅ \* | ✅ | ✅ |
| <td colspan=3> (\*) as base64 or hex encoded. please note that for all purposes of storing binary data, you should use the **binary files** facility of each Document instead, which is much more efficient. |
| <td colspan=3> (\*) as base64 or hex encoded. Please note that for all purposes of storing binary data, you should use the **binary files** facility of each Document instead, which is much more efficient. |
| **boolean, integer values** | ✅ | ✅ | ✅ |
| **NULL values** | ❌ | ✅ | ✅ |
| **strongly typed decimal values** | ✅ | ❌ | ❌ |
| <td colspan=3> signed, unsigned, and different sizes of integers |
| **revisions, diff, revert** | 🟧 | 🟧 | 🟧 |
| <td colspan=3> 🔥 implemented at the NextGraph level. work in progress. You will be able to see the diffs and access all the historical data of the Document, and also revert to previous versions. |
| <td colspan=3> 🔥 implemented at the NextGraph level; work in progress. You will be able to see the diffs and access all the historical data of the Document, and also revert to previous versions. |
| **compact** | ✅ | ✅ | ❓ |
| <td colspan=3> compacting is always available as a feature at the NextGraph level (and will compact Graph and Discrete parts alike). Yjs tends to garbage collect deleted content. not sure if automerge does it. Compact will remove all the historical data and deleted content (you won't be able to see diffs nor revert, for all the causal past happening before the compact operation. but normal CRDT behaviour can resume after) . This is a synchronous operation. |
| <td colspan=3> compacting is always available as a feature at the NextGraph level (and will compact Graph and Discrete parts alike). Yjs tends to garbage collect deleted content; not sure if automerge does it. Compact will remove all the historical data and deleted content (you won't be able to see diffs nor revert, for all the causal past happening before the compact operation. But normal CRDT behaviour can resume after) . This is a synchronous operation. |
| **snapshot** | ✅ | ✅ | ✅ |
| <td colspan=3> take a snapshot of the data at a given HEADs, and store it in a non-CRDT way so it can be opened quickly. Also removes all the historical and deleted data. But a snapshot cannot be used to continue collaborating on the document. See it as something similar to "export as a static file". |
| **isolated transactions** | ✅ | ✅ | ✅ |
| <td colspan=3> A NextGraph transaction can atomically mutate both the Graph and the Discrete data in a single isolated transaction. Can be useful to enforce consistency and keep in sync between information stored in the discrete and graph parts of the same document. but: transactions cannot span multiple documents (for that matter, see **smart contracts**). When a SPARQL Update spans across Documents, then the Transaction is split into several ones (one for each target Document) and each one is applied separately, meaning, not atomically. Also, keep in mind, as explained above in the "Counter" section, that CRDTs are eventually consistent. If you need ACID guarantees, use a synchronous transaction instead. |
| <td colspan=3> A NextGraph transaction can atomically mutate both the Graph and the Discrete data in a single isolated transaction. Can be useful to enforce consistency and keep in sync between information stored in the discrete and graph parts of the same document. But: transactions cannot span multiple documents (for that matter, see **smart contracts**). When a SPARQL Update spans across Documents, then the Transaction is split into several ones (one for each target Document) and each one is applied separately, meaning, not atomically. Also, keep in mind, as explained above in the "Counter" section, that CRDTs are eventually consistent. If you need ACID guarantees, use a synchronous transaction instead. |
| **Svelte5 reactive Store (Runes)** | 🟧 | 🟧 | 🟧 |
| <td colspan=3> 🔥 this is planned. will be available shortly. the store will be **writable** and will allow a bidirectional binding of the data to some javascript reactive variables in Svelte (same could be done for React) and we are considering the use of **Valtio** for a generic reactive store, that would also work on nodeJS and Deno |
| <td colspan=3> 🔥 this is planned and will be available shortly. The store will be **writable** and will allow a bidirectional binding of the data to some javascript reactive variables in Svelte (same could be done for React) and we are considering the use of **Valtio** for a generic reactive store, that would also work on nodeJS and Deno |
| **queries across documents** | ✅ 🔥SPARQL | 🟧 \* | 🟧 \* |
| <td colspan=3> (\*) support is planned at the NextGraph level, to be able to query discrete data too in SPARQL. (GraphQL support could then be added) |
| **export/import JSON** | ✅ JSON-LD | ✅ | ✅ |

@ -24,13 +24,13 @@ Those IDs and Keys will be combined in different ways detailed below, in order t
Some special format that need explanation :
- peers : a serialisation of a vector of BrokerServer that each contain the IP and port and the PeerID of the broker (and a version number). this is the “locator” part of the URI which tells which brokers should be contacted to join the overlay.
- peers : a serialisation of a vector of BrokerServer that each contain the IP and port and the PeerID of the broker (and a version number). This is the “locator” part of the URI which tells which brokers should be contacted to join the overlay.
- ReadCap : an ObjectRef (id+key) towards the current Branch definition commit
- OverlayLink: In case of an Inner Overlay, a ReadCap to the current Branch definition commit of the overlay branch of the store.
- ReadToken : is a hash of a ReadCap, it is mostly used in ExtRequests and SPARQL requests from untrusted apps or federated SPARQL requests. it gives the same level of permission than the hashed ReadCap, without revealing its content.
- ReadToken : is a hash of a ReadCap, it is mostly used in ExtRequests and SPARQL requests from untrusted apps or federated SPARQL requests. It gives the same level of permission than the hashed ReadCap, without revealing its content.
- PermaCap: a durable capability ( for read, write or query)
@ -90,11 +90,11 @@ Otherwise, without those triples, the capability is here because the repo, branc
The decomposition into several triples enables to deduplicate some information (if there are several links to the same overlay, the ng:access and ng:locator triple will be the same, so it will deduplicate) and also enables that the URI used to reference the foreign resource, will only contain the RepoId or ObjectId, which is important, specially for RepoId, as with RDF we want to establish facts between resources, that can be traversed with SPARQL queries. The unique identifier of a resource is `<did:ng:o:[repoid]>` and not the full DID capability. No matter where the resource is actually stored (the locator and overlay parts of the DID capability), which access we possess for that resource (read or write) or which revision we want to use at the moment, the unique identifier of the resource should remain the same. This is why we never use the full DID capability as URI for subject or object in RDF. Instead we use the minimal form `<did:ng:o:[repoid]>` .
We should probably mention here also that NextGraph is fully compatible with `<http(s)://…>` URIs and that they can coexist in any Document. `<ng:includes>` might work for http resources that can be dereferenced online with a GET, but we are not going to implement that right now (not a priority) and `<ng:subscribe>` will never work for those URLs, but we thought of using `<ng:refresh> "3600“ ` and `<ng:cache> “3600"` instead, which would refresh periodically the dereferenced resource in the former case, every hour, or would cache the resource after the first dereferencing for a period of 1h in the later case, but then would delete the cache to save space and would only dereference again if need be (before processing any query on the resource) and cache again for another hour. those 2 addition predicate are not planned for now as they are not a priority.
We should probably mention here also that NextGraph is fully compatible with `<http(s)://…>` URIs and that they can coexist in any Document. `<ng:includes>` might work for http resources that can be dereferenced online with a GET, but we are not going to implement that right now (not a priority) and `<ng:subscribe>` will never work for those URLs, but we thought of using `<ng:refresh> "3600“ ` and `<ng:cache> “3600"` instead, which would refresh periodically the dereferenced resource in the former case, every hour, or would cache the resource after the first dereferencing for a period of 1h in the latter case, but then would delete the cache to save space and would only dereference again if need be (before processing any query on the resource) and cache again for another hour. Those 2 additional predicate are not planned for now as they are not a priority.
### Identifiers
Here are the identifiers that can be used to refer to any resource anywhere in the graph. they are unique global identifiers.
Here are the identifiers that can be used to refer to any resource anywhere in the graph. They are unique global identifiers.
| Identifiers | that can be used in subject or object of any triple |
| ---------------------------------- | --------------------------------------------------- |
@ -155,7 +155,7 @@ when no default graph is included in the Query (with USING, GRAPH, FROM), the ta
| AllGroups | did:ng:g | union of all group Stores and all their documents |
| Group(String) | did:ng:g:[name] | shortname given locally to a Group (i.e. “birthday_party", “trip_to_NL") |
| | did:ng:g:g/o:[storeid] | union of all the documents of a specific group store |
| Identity(UserId) | did:ng:i:[userid] | search inside the User Storage of another Identity of the user, that is present in their wallet and has been opened. all the URI described here (except :i) can be used as suffixes behind `:i:[xxx]` |
| Identity(UserId) | did:ng:i:[userid] | search inside the User Storage of another Identity of the user, that is present in their wallet and has been opened. All the URIs described here (except :i) can be used as suffixes behind `:i:[xxx]` |
| | did:ng:i:n:[name] | same as above but with the shortname for the identity (i.e "work“ ) |
| a document | did:ng:o | |
@ -180,7 +180,7 @@ As shown above, there is a Context branch in each Document.
What is it for ?
the Context branch is an RDF mini file that contains some triples describing the JSON-LD Context. it uses the [JSON-LD Vocabulary](https://www.w3.org/ns/json-ld), and can be retrieved in its usual JSON-LD format.
the Context branch is an RDF mini file that contains some triples describing the JSON-LD Context. It uses the [JSON-LD Vocabulary](https://www.w3.org/ns/json-ld), and can be retrieved in its usual JSON-LD format.
It expresses the mapping of prefixes to the URI of the ontologies used in the document. It is common to all branches of the document. In every Header of a Document, the predicate `<ng:x>` contains as object the full link (DID cap) needed in order to open the branch and read the context. If the same context is shared across documents, then this predicate can point to a common context branch that sits outside of the document. In this case the context branch can be empty.
@ -239,5 +239,5 @@ All the files of a branch (added with the AddFile commit) will be listed by the
| ng:stores | Nuri | did:ng:o | a store stores a document<br/> (auto-generated) |
| ng:site | Nuri | did:ng:o | to link the public store from<br/>the protected store (optional) |
| ng:protected | Nuri | did:ng:o:v:r:l | link to protected store main branch,<br/> by example from a follow request |
| ng:follows | Nuri | did:ng:o | profile that is followed. needs <br/> :access, :overlay, :locator |
| ng:follows | Nuri | did:ng:o | profile that is followed. Needs <br/> :access, :overlay, :locator |
| ng:index | Nuri | did:ng:o:r | entrypoint of a service or app |

@ -14,7 +14,7 @@ Those capabilities are composed of :
- a read capability on the branch.
- If it is the root branch, then access to the whole document and all its branches will be granted.
- If it is a transactional branch (Header, Main, a block, a fork, etc...) then only this specific branch will be accessible.
- the overlay ID. see the [Network](/en/network) chapter about overlays.
- the overlay ID. See the [Network](/en/network) chapter about overlays.
- the locator which is a list of brokers that can be contacted in order to join the overlay and access the topics. Read more on topics in the [sync protocol](/en/protocol) chapter.
If the user receives write access to the repo, then the capability will contain also the ReadCap of the inner overlay.

@ -107,7 +107,7 @@ The 2 main types of queries are `SELECT` and `CONSTRUCT`. Select is similar to S
`CONSTRUCT` are a special type of queries that always return some full triples. They work the same as the SELECT WHERE, but you cannot have arbitrary variable projections. It will always return triples of the form `subject predicate object`. But you can of course tell which filters and patterns you want to follow.
Until now, we explained that each Document can hold some RDF triples. but we didn't explain how they are stored and how the SPARQL engine will be able to run queries that span all the RDF Documents that are present locally.
Until now, we explained that each Document can hold some RDF triples. But we didn't explain how they are stored and how the SPARQL engine will be able to run queries that span all the RDF Documents that are present locally.
There is in fact an option in the "SPARQL Query" tool (in the Document Menu, under "Graph" / "View as ...") that lets you query all the documents that you have present locally at once. If you do not toggle this option, you will only get results about the triples of the current Document. While with this "Query all docs" option activated, the SPARQL engine will search in all your documents, regardless if they are in the Public store, Protected store, Private store or in any Group or Dialog store.
@ -159,19 +159,19 @@ Then it is possible to add new triples in the this Document, and the ID of the d
In this Document/Named Graph, the user can add triples, and most of the time, they will add triples that have this Document ID as subject. That's what we call **authoritative triples** because we know that the subject is the same ID as the named graph, (as the document ID) and because we have signatures on every commit and also threshold signatures too, then we can prove that those triples have been authored by the users that claim to be editors of such document.
In order to facilitate adding those kind of authoritative triples with a SPARQL UPDATE, or to retrieve them with a SPARQL QUERY, the user has access to the BASE shortcut, which is `<>` in SPARQL, and that represents the current document. it will be replaced internally by the exact ID of the current document. This placeholder is handy and helps you manipulate the authoritative triples of your document. The default graph of any SPARQL Query is also the current Document, so you do not need to specify it explicitly (except when you select the "Query all docs" option, in this case, the default graph is the union graph of all the graphs).
In order to facilitate adding those kind of authoritative triples with a SPARQL UPDATE, or to retrieve them with a SPARQL QUERY, the user has access to the BASE shortcut, which is `<>` in SPARQL, and that represents the current document. It will be replaced internally by the exact ID of the current document. This placeholder is handy and helps you manipulate the authoritative triples of your document. The default graph of any SPARQL Query is also the current Document, so you do not need to specify it explicitly (except when you select the "Query all docs" option, in this case, the default graph is the union graph of all the graphs).
If we had stopped here, there would be no real interest in having a named graph mechanism.
But you also are able to add triples in the Document/Named graph, that are not authoritative. Those are the triples that have as subject, some other ID than the current Document.
What is it useful for? RDF lets anybody establish facts about any resources. If there is a foreign Document that I am using in my system, and I want to add extra information about this resource, but I don't have write permission on that foreign Document, I can add the triples in one of the Documents that I own. External people who would see those triples that I added, would immediately understand that they are not authoritative, because they were not signed with the private key of the Document ID that they establish facts about (the subject of the triples). So it is possible to say, by example, that `London -> belongs_to -> African_continent` but of course, this is not the official point of the view of the author that manages the `London` Document. it only is "my point of view", and people who will see this triple, will also be notified that it isn't authoritative (I think they can easily understand that by themselves without the need for signatures).
What is it useful for? RDF lets anybody establish facts about any resources. If there is a foreign Document that I am using in my system, and I want to add extra information about this resource, but I don't have write permission on that foreign Document, I can add the triples in one of the Documents that I own. External people who would see those triples that I added, would immediately understand that they are not authoritative, because they were not signed with the private key of the Document ID that they establish facts about (the subject of the triples). So it is possible to say, by example, that `London -> belongs_to -> African_continent` but of course, this is not the official point of the view of the author that manages the `London` Document. It only is "my point of view", and people who will see this triple, will also be notified that it isn't authoritative (I think they can easily understand that by themselves without the need for signatures).
Then we have other use cases for extra triples in the Document :
- fragments, that are prefixed with the authoritative ID, and followed by a hash and a string (like in our previous example `#label`).
- blank nodes that have been skolemized. They get a Nuri of the form `did:ng:o:...:u:...`. this is because blank nodes cannot exist in a local-first system, as we need to give them a unique ID. This is done with the [skolemization procedure](https://www.w3.org/TR/rdf11-concepts/#section-skolemization). For the user or programmer, skolemization is transparent. You can use blank nodes in SPARQL UPDATE and they will be automatically translated to skolems. For SPARQL QUERY anyway, blank nodes are just hidden variables, so there is no impact.
- blank nodes that have been skolemized. They get a Nuri of the form `did:ng:o:...:u:...`. This is because blank nodes cannot exist in a local-first system, as we need to give them a unique ID. This is done with the [skolemization procedure](https://www.w3.org/TR/rdf11-concepts/#section-skolemization). For the user or programmer, skolemization is transparent. You can use blank nodes in SPARQL UPDATE and they will be automatically translated to skolems. For SPARQL QUERY anyway, blank nodes are just hidden variables, so there is no impact.
But those extra triples (fragments and skolems) are all prefixed with the authoritative ID, so they are considered authoritative too.

@ -26,7 +26,7 @@ It wouldn't make sense that developers using our Framework would need to install
For this reason, we have integrated a specific feature into the Repo mechanism, that lets the developer create a Synchronous Transaction in a document (a document that should be understood as a database). Those types of transaction have higher guarantees than the Asynchronous transactions based on CRDTs, as they guarantee the **finality** of the transaction, which means that once this commit has been accepted by a certain threshold of Signers, it is guaranteed that no other concurrent transaction has happened or will ever happen that would conflict with or invalidate the invariants. Therefor the commit is final.
Basically what we do is that we temporarily prevent any fork in the DAG while the transaction is being signed (we also call that “total order”. it creates a temporary bottle neck in the DAG) and the threshold that must be above 50%, guarantees that any concurrent or future fork will be rejected. Supermajority can be used to calculate the threshold, in cases where Byzantine or faulty users have to be considered. (given N = all users, F = byzantine and/or faulty users, supermajority = (N+F)/2 +1).
Basically what we do is that we temporarily prevent any fork in the DAG while the transaction is being signed (we also call that “total order”. It creates a temporary bottle neck in the DAG) and the threshold that must be above 50%, guarantees that any concurrent or future fork will be rejected. Supermajority can be used to calculate the threshold, in cases where Byzantine or faulty users have to be considered. (given N = all users, F = byzantine and/or faulty users, supermajority = (N+F)/2 +1).
This is synchronous because it requires that the quorum of signers is online at the same time, and can agree on the transaction.

@ -42,7 +42,7 @@ You will be able to change your broker later on. Your data will move to the new
### Use the App
Once your wallet is created and you are entering the App main screen for the first time, you will have to learn how to use it. here is a brief introduction to the App.
Once your wallet is created and you are entering the App main screen for the first time, you will have to learn how to use it. Here is a brief introduction to the App.
You have created a Wallet, and inside this wallet, there is one default `Identity` that represents you. This identity is called **personal** because it represents you as an individual. You can later create more Identities if you want to separate your work life and your personal life, by example, or for other purposes of keeping your identities separated.
@ -54,7 +54,7 @@ Within each Identity, your data is organized into **3 different Stores**.
- the protected store : is a space where you can share data, documents, and media with other users, but they will need a special link and permission in order to access them and collaborate with you on those documents (or just read them). You can also create Groups here when you want to chat or collaborate with several users. This store also acts as your protected profile for social networks. Only your followers will be able to see this profile, as it is not public.
- the private store : this is a place where you put only private and personal information that only you have access to. This store will be accessible to all your devices, so it is useful in order to synchronize private data between them. You can put sensitive data too, as everything is encrypted. Nobody else will ever see this data. it is not possible to share the documents of your private store.
- the private store : this is a place where you put only private and personal information that only you have access to. This store will be accessible to all your devices, so it is useful in order to synchronize private data between them. You can put sensitive data too, as everything is encrypted. Nobody else will ever see this data. It is not possible to share the documents of your private store.
You can also later, move documents from one store to another, if you are the owner of such document.
@ -125,7 +125,7 @@ From the Document Menu, you will see many other options, most of them are not im
Three menu options are implemented for now : History, "Attachments and Files" and "Tools > Signature".
The second one is, as its name indicates, about adding some images or any other attachment file to the Document. Images will be useful when you want to insert some picture in a Rich-Text document (not available yet. but you can already add pictures in the Attachments and Files).
The second one is, as its name indicates, about adding some images or any other attachment file to the Document. Images will be useful when you want to insert some picture in a Rich-Text document (not available yet but you can already add pictures in the Attachments and Files).
### History

@ -16,7 +16,7 @@ You have heard that with NextGraph there will be no more ToS to accept. You are
#### Why is the loading so slow on the web app ?
When you use the web app, we do not store your data locally, because there is not enough space to do so in local storage (the browser limits us to 5MB). Soon we will have a new feature for the web-app, where all your data will be stored locally in the browser, thanks to indexedDB. but this is not ready yet. So for now, every time you open your wallet and start using the web app, all the document's content is fetched from the broker. This can take some time and it explains why the loading is slow. Once the documents have been retrieved, as long as you do not close the tab, we keep it locally in memory and it even works offline, in case you loose connectivity to the internet.
When you use the web app, we do not store your data locally, because there is not enough space to do so in local storage (the browser limits us to 5MB). Soon we will have a new feature for the web-app, where all your data will be stored locally in the browser, thanks to indexedDB but this is not ready yet. So for now, every time you open your wallet and start using the web app, all the document's content is fetched from the broker. This can take some time and it explains why the loading is slow. Once the documents have been retrieved, as long as you do not close the tab, we keep it locally in memory and it even works offline, in case you loose connectivity to the internet.
#### Where is my data stored when I install the native App ?
@ -25,7 +25,7 @@ When you use our native app, all the content of the documents is stored locally
- on Linux: `~/.local/share/org.nextgraph.app`
- on macOS: `/Users/[username]/Library/Application Support/org.nextgraph.app`
- on Windows: `C:\Users\[username]\AppData\Roaming\org.nextgraph.app`
- on Android and iOS: the folder is not accessible to you. it is protected by the OS.
- on Android and iOS: the folder is not accessible to you. It is protected by the OS.
You will soon be able to use the CLI in order to read your local data (on Desktop and Laptop).
@ -37,7 +37,7 @@ This is a temporary limitation that will be lifted in several weeks from now (in
#### Can I create several Wallet for myself ?
We do not recommend you to create more than one Wallet. The app is not designed to handle such case. Instead, the app has been designed so that you can create more separate identities from within the same wallet. identities that are stored in the same wallet are still independent one from another and nobody will be able to tell that you own both identities. This feature is not available yet but will come soon. There is really no need to create several wallets. Specially because you will have difficulties to remember several pazzles.
We do not recommend you to create more than one Wallet. The app is not designed to handle such case. Instead, the app has been designed so that you can create more separate identities from within the same wallet. Identities that are stored in the same wallet are still independent one from another and nobody will be able to tell that you own both identities. This feature is not available yet but will come soon. There is really no need to create several wallets. Specially because you will have difficulties to remember several pazzles.
#### Where is the iOS app ?

@ -90,7 +90,7 @@ We also have a pair of overlay for each Group, and only one Inner overlay for ea
The Outer overlay ID is just the Blake3 hash of the Store ID.
While the Inner overlay ID is a Blake3 keyed hash of the Store ID. the key used for the hash is derived from the ReadCapSecret of the Overlay branch of the Store.
While the Inner overlay ID is a Blake3 keyed hash of the Store ID. The key used for the hash is derived from the ReadCapSecret of the Overlay branch of the Store.
We will now dive more into details about repositories, branches, blocks and commits, that compose the [NextGraph Protocol](/en/protocol)

@ -32,7 +32,7 @@ This new commit is now the current head at the local replica, and will be sent t
It can happen that other editors make concurrent modifications. In this case, they will also publish a commit with a causal past (ACKS) that is similar or identical to the new commit we just published.
This will lead to a temporary “fork” in the DAG, and after the replicas have finished their syncing, they will all have 2 current heads. one for each of the concurrent commits.
This will lead to a temporary “fork” in the DAG, and after the replicas have finished their syncing, they will all have 2 current heads, one for each of the concurrent commits.
The next commit (whoever will make more modification in the document), will “merge” the fork when it will publish a new commit that references the 2 heads as ACKS (direct causal past).
@ -56,7 +56,7 @@ A Repo (repository) is a unit that regroups one or several branches of content,
When a repo is created, it comes with 2 branches by default :
- the root branch, which is used to store all the members information, their permissions, the list of branches, and controls the epochs. it does not hold content. Its branchID cannot change because it is in fact, the same ID as the RepoID itself.
- the root branch, which is used to store all the members information, their permissions, the list of branches, and controls the epochs. It does not hold content. Its branchID cannot change because it is in fact, the same ID as the RepoID itself.
- the main branch, which is a transactional branch (transactional=that holds content) and that will be the default branch if no other branch is specified when a request is made to access the content. It is possible to change the main branch and point it to another branch in the repo.
@ -70,7 +70,7 @@ It is possible to renew the topicID during the lifetime of a branch, even severa
This renew mechanism is used when the capabilities of the branch needs to be refreshed (for read access, when we want to remove read access from some user).
The write access is not controlled by branch, but is controlled more generally at the repo level. it is not possible to give write permission only to one specific branch. When a member is given write permission, it applies to all the branches of the repo at once. The same when write permission is revoked. It is revoked for all the branches of the repo at once.
The write access is not controlled by branch, but is controlled more generally at the repo level. It is not possible to give write permission only to one specific branch. When a member is given write permission, it applies to all the branches of the repo at once. The same when write permission is revoked. It is revoked for all the branches of the repo at once.
It is indeed important that permissions are common to all branches, because we will now see that branches can be merged one into another. And when the merge happens, we consider that all the commits of the branches are valid and have been verified already back then, at the moment of every commit addition. We do not want to have to re-verify a whole branch before it is merged. What was already verified and accepted, is immutably part of the repo. If we had a permission system with different permissions for each branch, then there would be cases when some commits in one branch, cannot be merged into another branch because the permissions are incompatible. In order to prevent this, and also to simplify an already very complex design, we restricted the permission management to be only at the repo level, unlike the previous design of LoFi.Re.
@ -92,11 +92,11 @@ This is due to the fact that a read permission is in fact a cryptographic capabi
That's very handy, if we want to separate a Document into several parts that will have different read access.
Let's say I have a Document that is my personal profile description. it contains my pseudonym, full name, date of birth, postal address, email address, phone number, short biography, profile picture, etc…
Let's say I have a Document that is my personal profile description. It contains my pseudonym, full name, date of birth, postal address, email address, phone number, short biography, profile picture, etc…
Now let's imagine that for some reasons related to my privacy, I do not always want to share my postal address and phone number with everyone, but instead I want to opt-out sometimes and share the rest, but not the postal address and phone number.
I could create two different documents. one with all the info, and one with the reduced profile.
I could create two different documents. One with all the info, and one with the reduced profile.
But that would be cumbersome, as every time I need to update my bio, by example, i would have to copy paste it in both Documents.
@ -106,11 +106,11 @@ Both branches are updatable. If I modify my bio, all the users who subscribed to
And I can even include a link to the main branch, from within the privateProfile branch, so that those trusted people also have access to the main branch, without need for me to share both branches ReadCaps with them.
If at some point in the future, I want to merge those two branches into one, well.. that, I won't be able to do it, because in order to merge two branches, they need to share a common ancestor (one branch has to be a fork from the other).
If at some point in the future, I want to merge those two branches into one, well.. That, I won't be able to do it, because in order to merge two branches, they need to share a common ancestor (one branch has to be a fork from the other).
But here, those 2 branches are completely separated one from another. The only thing they share is that they belong to the same Repo, but they both have zero ancestors in their root DAG commit. those 2 DAGs are unrelated one to another. So we cannot merge them.
But here, those 2 branches are completely separated one from another. The only thing they share is that they belong to the same Repo, but they both have zero ancestors in their root DAG commit. Those 2 DAGs are unrelated one to another. So we cannot merge them.
Another example about how we can use branches to do cool stuff, is for commenting/annotating on someone else's content. Commenting is a kind of editing, as it adds content. But we don't want to have to invite those commentators as editors of the document they want to comment on. Instead the commentator will create a standalone branch somewhere on their own protected store (they are free to proceed as they want on that. They can create a special document on their side, that will have the sole purpose of holding all the branches used for each comment on a specific target Document. or they can even use less Documents, and have one general purpose Document in their protected store that is always used to create branches for commenting, regardless of the target document that is commented upon.) What matters is that they are the only editor on that Document, and they will write one comment by branch. The branch subscription mechanism will let them update/fix typos on that specific comment later on. They can also delete the branch at any time, in order to delete their own comment. Once they have created that branch and inserted some content in it (the comment itself), they will send a link (a DID cap) to the original Document they want to comment upon. (each document has an inbox, which is used in this case to drop the link). A comment can reference previous comment, or quote some part of the document (annotation), thanks to RDF, this is easy to do. The owner of the Document that receives this link that contains a comment, can moderate it, accept, reject, or remove it after accepting it. If accepted, the link (DID cap) is added a the special branch for comments, that every document has by default (more on that below). Any reader of the document that subscribed to this branch, will see the new comment.
Another example about how we can use branches to do cool stuff, is for commenting/annotating on someone else's content. Commenting is a kind of editing, as it adds content. But we don't want to have to invite those commentators as editors of the document they want to comment on. Instead the commentator will create a standalone branch somewhere on their own protected store (they are free to proceed as they want on that. They can create a special document on their side, that will have the sole purpose of holding all the branches used for each comment on a specific target Document. Or they can even use less Documents, and have one general purpose Document in their protected store that is always used to create branches for commenting, regardless of the target document that is commented upon.) What matters is that they are the only editor on that Document, and they will write one comment by branch. The branch subscription mechanism will let them update/fix typos on that specific comment later on. They can also delete the branch at any time, in order to delete their own comment. Once they have created that branch and inserted some content in it (the comment itself), they will send a link (a DID cap) to the original Document they want to comment upon. (each document has an inbox, which is used in this case to drop the link). A comment can reference previous comment, or quote some part of the document (annotation), thanks to RDF, this is easy to do. The owner of the Document that receives this link that contains a comment, can moderate it, accept, reject, or remove it after accepting it. If accepted, the link (DID cap) is added a the special branch for comments, that every document has by default (more on that below). Any reader of the document that subscribed to this branch, will see the new comment.
So, to recap.
@ -122,28 +122,28 @@ So, to recap.
- i can also fork a branch into another branch, and then merge that fork back into the original branch (or into any other branch that shares a common ancestor)
- those forks can be used to store some specific revisions of the document. and then, by using the branchId, it is possible to refer to that specific revision.
- those forks can be used to store some specific revisions of the document. And then, by using the branchId, it is possible to refer to that specific revision.
- a branch can also be given a name, like "rewriting_paragraph_B“.
- any given commit has an ID, and that commit can also be used to refer to a specific revision, which in this case, is just the state of the document at that very specific commit. commits can also be given names like v0_1_0 (equivalent to the tags in GIT), and those names are pointers that can be updated. so one can share the name, and update the pointer later on.
- any given commit has an ID, and that commit can also be used to refer to a specific revision, which in this case, is just the state of the document at that very specific commit. Commits can also be given names like v0_1_0 (equivalent to the tags in GIT), and those names are pointers that can be updated. So one can share the name, and update the pointer later on.
- standalone branches can be used to separate different segments of data that need different read permissions.
- ReadCaps can be refreshed in order to remove read access to some branch (but the historical data they used to have access to, will always remain visible to them, specially because everything is local-first, so they surely have a local copy of that historical data. what they won't see are the new updates).
- ReadCaps can be refreshed in order to remove read access to some branch (but the historical data they used to have access to, will always remain visible to them, specially because everything is local-first, so they surely have a local copy of that historical data. What they won't see are the new updates).
- we use the terms “DID cap”, "ReadCap", “URI", “link” or “[Nuri](/en/framework/nuri)” interchangeably in this document. They all mean the same.
it is also possible to fork a whole repo, if ownership and permissions need to be changed (similar to the “fork me on github” feature) and then there is a mechanism for “pull requests” in order to merge back that forked repo into the original repo. But it doesn't work like merging of branches, as each commit has to be checked again separately and added to the DAG again, using the identity of a user that has write permission in the target repo. Let's leave that for now, as it is not coded yet, and not urgent.
The root branch is a bit complex and has all kind of system commits to handle the internals of permissions etc. We will not dive into that right now. There are also some other hidden system branches (called Store, User, Overlay, Chat, etc..) that contain some internal data used by the system, and that you can imagine a bit what it does, given the reserved names they have. but again, let's keep that for later.
The root branch is a bit complex and has all kind of system commits to handle the internals of permissions etc. We will not dive into that right now. There are also some other hidden system branches (called Store, User, Overlay, Chat, etc..) that contain some internal data used by the system, and that you can imagine a bit what it does, given the reserved names they have. But again, let's keep that for later.
What matters for now is that any transactional branch contains commits that modify the content of the branch, which is a revision of the document.
Those commits are encrypted and sent as events in the pub/sub.
When a commit arrives on a replica, the Verifier is in charge of verifying the integrity of the commit and the branches and repo in general, and this Verifier will need to read the ACLs. it will also verify some signatures and do some checks on the DAG.
When a commit arrives on a replica, the Verifier is in charge of verifying the integrity of the commit and the branches and repo in general, and this Verifier will need to read the ACLs. It will also verify some signatures and do some checks on the DAG.
If something goes wrong, the commit is rejected and discarded. its content is not passed to the application level.
If something goes wrong, the commit is rejected and discarded. Its content is not passed to the application level.
Eventually, all the replicas have a local set of commits for a branch, and they need to read them and process them once, in order to build the materialized state of the doc. That's the job of the [verifier](/en/verifier).

@ -28,7 +28,7 @@ ActivityPods will act as a gateway between the world of HTTP and NextGraph, that
Later on, we also aim at being compatible with Nostr, BlueSky/ATproto and maybe more protocols too.
We believe that offering Social Network features in the heart of our framework is essential. here is a list of the features that will soon be part of our framework and platform.
We believe that offering Social Network features in the heart of our framework is essential. Here is a list of the features that will soon be part of our framework and platform.
### Stream
@ -98,8 +98,8 @@ Maybe one day the European Union will finally force them to open their APIs and
In the meanwhile, we are currently exploring ways to implement this already.
Another important tool would be the option to import all the content and contacts that someone has gathered through the years on a platform of big-tech, so it can be used in NextGraph, when that person decides to switch to our platform. Some big-tech platforms already offer the export option. and we would have to implement the import part.
Another important tool would be the option to import all the content and contacts that someone has gathered through the years on a platform of big-tech, so it can be used in NextGraph, when that person decides to switch to our platform. Some big-tech platforms already offer the export option, and we would have to implement the import part.
It is important to understand that NextGraph does not need to implement an export feature, because with NextGraph, all your data is totally under your control already. it is local and you have it directly on your device, it is saved in some open standards like RDF, JSON or Markdown, and you can do whatever you want with it. Furthermore, if you decide at some point to use a different broker, or to self-host your own broker, all your data at NextGraph is absolutely **portable** and you can move your data to another broker without any bad consequence. Your documents and links have unique IDs that are not related to the broker where you store the data. That's another advantage that no other platform or framework can offer, not even ActivityPub or Solid, which are all based on http and domain names, and that will break as soon as you change hosting provider. (I have experienced this myself. In Mastodon, the list of followers can easily be transferred, but you loose all your posts and DMs).
It is important to understand that NextGraph does not need to implement an export feature, because with NextGraph, all your data is totally under your control already. It is local and you have it directly on your device, it is saved in some open standards like RDF, JSON or Markdown, and you can do whatever you want with it. Furthermore, if you decide at some point to use a different broker, or to self-host your own broker, all your data at NextGraph is absolutely **portable** and you can move your data to another broker without any bad consequence. Your documents and links have unique IDs that are not related to the broker where you store the data. That's another advantage that no other platform or framework can offer, not even ActivityPub or Solid, which are all based on http and domain names, and that will break as soon as you change hosting provider. (I have experienced this myself. In Mastodon, the list of followers can easily be transferred, but you loose all your posts and DMs).
Welcome to NextGraph, that will soon offer all those Social Network features, and that already offers the best [collaborative tools](/en/collaboration) with offline-first and end-to-end encryption.

@ -81,7 +81,7 @@ struct RootBranchV0 {
/// Quorum definition ObjectRef
quorum: Option<ObjectRef>,
/// BEC periodic reconciliation interval. zero deactivates it
/// BEC periodic reconciliation interval, zero deactivates it
reconciliation_interval: RelTime,
/// list of owners
@ -108,7 +108,7 @@ struct RootBranchV0 {
- owners : all of them are required to sign any RootBranch that modifies the list of owners or the inherit_perms_users_and_quorum_from_store field.
- owners_write_cap : when the list of owners is changed, a crypto_box containing the RepoWriteCapSecret should be included here for each owner. This should also be done at creation time, with the UserId of the first owner, except for individual private store repo, because it doesn't have a RepoWriteCapSecret. The vector has the same order and size as the owners one. each owner finds their write_cap here.
- owners_write_cap : when the list of owners is changed, a crypto_box containing the RepoWriteCapSecret should be included here for each owner. This should also be done at creation time, with the UserId of the first owner, except for individual private store repo, because it doesn't have a RepoWriteCapSecret. The vector has the same order and size as the owners one. Each owner finds their write_cap here.
### Branch
@ -152,7 +152,7 @@ struct BranchV0 {
- topic_privkey : a BranchWriteCapSecret, encrypted with a nonce = 0 and a key derived as follow
BLAKE3 derive_key ("NextGraph Branch WriteCap Secret BLAKE3 key", RepoWriteCapSecret, TopicId, BranchId ) so that only editors of the repo can decrypt the privkey. For individual store repo, the RepoWriteCapSecret is zero
- pulled_from : optional: this branch is the result of a pull request coming from another repo. contains a serialization of a ReadBranchLink of a transactional branch from another repo
- pulled_from : optional: this branch is the result of a pull request coming from another repo. Contains a serialization of a ReadBranchLink of a transactional branch from another repo
### AddBranch
@ -183,7 +183,7 @@ struct AddBranchV0 {
}
```
- topic_id : the new topic_id. Will be needed immediately by future readers in order to subscribe to the pub/sub). should be identical to the one in the Branch definition. None if merged_in.
- topic_id : the new topic_id. Will be needed immediately by future readers in order to subscribe to the pub/sub). Should be identical to the one in the Branch definition. None if merged_in.
- branch_read_cap : the new branch definition commit (we need the ObjectKey in order to open the pub/sub Event). None if merged_in
@ -218,7 +218,7 @@ Points to the new Signature Object. Based on the total order quorum (or owners q
**DEPS**: the last signed commit in chain
**ACKS**: previous head before the chain of signed commit(s). should be identical to the HEADS (marked as DEPS) of first commit in chain
**ACKS**: previous head before the chain of signed commit(s). Should be identical to the HEADS (marked as DEPS) of first commit in chain
```rust
enum SyncSignature {
@ -254,7 +254,7 @@ struct SignatureV0 {
/// the content that is signed
content: SignatureContent,
/// The threshold signature itself. can come from 3 different sets
/// The threshold signature itself. Can come from 3 different sets
threshold_sig: ThresholdSignatureV0,
/// Reference to the Certificate that must be used to verify this signature.
@ -272,7 +272,7 @@ struct SignatureContentV0 {
commits: Vec<ObjectId>,
}
// the threshold signature itself. with indication which set was used
// the threshold signature itself, with indication which set was used
enum ThresholdSignatureV0 {
PartialOrder(threshold_crypto::Signature),
TotalOrder(threshold_crypto::Signature),
@ -322,7 +322,7 @@ struct CertificateContentV0 {
readcap_id: ObjectId,
/// PublicKey used by the Owners. verifier uses this PK
/// PublicKey used by the Owners. Verifier uses this PK
// if the signature was issued by the Owners.
owners_pk_set: threshold_crypto::PublicKey,
@ -348,7 +348,7 @@ enum OrdersPublicKeySetsV0 {
- CertificateSignatureV0.Owners : indicates that the owners set signed the certificate. If the previous cert's total order PKset has a threshold value of 0 or 1 (1 or 2 signers in the quorum),then it is allowed that the next certificate (this one) will be signed by the owners PKset instead. This is for a simple reason: if a user is removed from the list of signers in the total_order quorum,then in those 2 cases, the excluded signer will probably not cooperate to their exclusion, and will not sign the new certificate. To avoid deadlocks, we allow the owners to step in and sign the new cert instead. The Owners are also used when there is no quorum/signer defined (OrdersPublicKeySetsV0::None).
- CertificateSignatureV0.Store : in case the new certificate being signed is an update on the store certificate (OrdersPublicKeySetsV0::Store(ObjectRef) has changed from previous cert) then the signature is in that new store certificate, and not here. nothing else should have changed in the CertificateContent, and the validity of the new store cert has to be checked.
- CertificateSignatureV0.Store : in case the new certificate being signed is an update on the store certificate (OrdersPublicKeySetsV0::Store(ObjectRef) has changed from previous cert) then the signature is in that new store certificate, and not here. Nothing else should have changed in the CertificateContent, and the validity of the new store cert has to be checked.
- CertificateContentV0.previous : the previous certificate in the chain of trust. Can be another Certificate or the Repository commit's body when we are at the root of the chain of trust.
@ -359,7 +359,7 @@ enum OrdersPublicKeySetsV0 {
- OrdersPublicKeySetsV0::Repo.0 one for the total_order (first one).
- OrdersPublicKeySetsV0::Repo.1 the other for the partial_order (second one.is optional, as some repos are forcefully totally ordered and do not have this set).
- OrdersPublicKeySetsV0::None : the total_order quorum is not defined (yet, or anymore). there are no signers for the total_order, neither for the partial_order. The owners replace them.
- OrdersPublicKeySetsV0::None : the total_order quorum is not defined (yet, or anymore). There are no signers for the total_order, neither for the partial_order. The owners replace them.
### StoreUpdate
@ -389,7 +389,7 @@ Adds a repo into the store branch.
The repo's `store` field should match the destination store
**DEPS**: to the previous AddRepo commit(s) if it is an update. in this case, repo_id of the referenced rootbranch definition(s) should match
**DEPS**: to the previous AddRepo commit(s) if it is an update. In this case, repo_id of the referenced rootbranch definition(s) should match
```rust
struct AddRepoV0 {
@ -408,7 +408,7 @@ So that a user can share with all its device a new signing capability that was j
The cap's `epoch` field should be dereferenced and the user must be part of the quorum/owners.
**DEPS**: to the previous AddSignerCap commit(s) if it is an update. in this case, repo_ids have to match,
**DEPS**: to the previous AddSignerCap commit(s) if it is an update. In this case, repo_ids have to match,
and the referenced rootbranch definition(s) should have compatible causal past (the newer AddSignerCap must have a newer epoch compared to the one of the replaced cap )
```rust
@ -485,7 +485,7 @@ Add a new binary file in a branch
```rust
struct AddFileV0 {
/// an optional name. does not conflict
/// an optional name, does not conflict
/// (not unique across the branch nor repo)
name: Option<String>,
@ -533,7 +533,7 @@ struct CommitV0 {
/// Commit content
content: CommitContent,
/// Signature over the content by the author. an editor (UserId)
/// Signature over the content by the author; an editor (UserId)
sig: Sig,
}
@ -588,7 +588,7 @@ struct CommitHeaderKeysV0 {
CommitContentV0:
- author : Commit author, a BLAKE3 keyed hash of UserId. key is a BLAKE3 derive_key ("NextGraph UserId Hash Overlay Id for Commit BLAKE3 key", overlayId). Hash will be different than for ForwardedPeerAdvertV0 so that core brokers dealing with public sites wont be able to correlate commits and editing peers (via common author's hash).O nly the brokers of the authors that pin a repo for Outer Overlay exposure, will be able to correlate. It also is a different hash than the OuterOverlayId, which is good to prevent correlation when the RepoId is used as author (for Repository, RootBranch and Branch commits)
- author : Commit author, a BLAKE3 keyed hash of UserId. Key is a BLAKE3 derive_key ("NextGraph UserId Hash Overlay Id for Commit BLAKE3 key", overlayId). Hash will be different than for ForwardedPeerAdvertV0 so that core brokers dealing with public sites wont be able to correlate commits and editing peers (via common author's hash).O nly the brokers of the authors that pin a repo for Outer Overlay exposure, will be able to correlate. It also is a different hash than the OuterOverlayId, which is good to prevent correlation when the RepoId is used as author (for Repository, RootBranch and Branch commits)
- branch : BranchId the commit belongs to (not a ref, as readers do not need to access the branch definition)
@ -627,7 +627,7 @@ struct CommitHeaderV0 {
/// Other objects this commit strongly depends on
deps: Vec<ObjectId>,
/// dependency that is removed after this commit. used for reverts
/// dependency that is removed after this commit; used for reverts
ndeps: Vec<ObjectId>,
compact: bool,
@ -644,7 +644,7 @@ struct CommitHeaderV0 {
}
```
- compact : tells brokers that this is a hard snapshot and that all the ACKs and full causal past should be treated as ndeps (their body removed). brokers will only perform the deletion of bodies after this commit has been ACKed by at least one subsequent commit. but if the next commit is a nack, the deletion is aborted.
- compact : tells brokers that this is a hard snapshot and that all the ACKs and full causal past should be treated as ndeps (their body removed). Brokers will only perform the deletion of bodies after this commit has been ACKed by at least one subsequent commit. But if the next commit is a nack, the deletion is aborted.
### RandomAccessFile
@ -749,14 +749,14 @@ type InternalNode = Vec<BlockKey>
```
- BlockV0.commit_header_key : optional Key needed to open the CommitHeader. can be omitted if the Commit is shared without its causal past, or if the block is not a root block of commit, or that commit is a root commit (first in branch)
- BlockV0.commit_header_key : optional Key needed to open the CommitHeader. Can be omitted if the Commit is shared without its causal past, or if the block is not a root block of commit, or that commit is a root commit (first in branch)
- BlockContentV0.commit_header : Reference (actually, only its ID or an embedded block if the size is small enough)
to a CommitHeader of the root Block of a commit that contains references to other objects (e.g. Commit deps & acks).
Only set if the block is a commit (and it is the root block of the Object).
It is an easy way to know if the Block is a commit (but be careful because some root commits can be without a header).
- BlockContentV0.children : Block IDs for child nodes in the Merkle tree. It is empty if ObjectContent fits in one block or this block is a leaf. in both cases, encrypted_content is then not empty
- BlockContentV0.children : Block IDs for child nodes in the Merkle tree. It is empty if ObjectContent fits in one block or this block is a leaf. In both cases, encrypted_content is then not empty
- BlockContentV0.encrypted_content : contains an encrypted ChunkContentV0, encrypted using convergent encryption with ChaCha20: nonce = 0 and key = BLAKE3 keyed hash (convergence_key, plaintext of ChunkContentV0), with convergence_key = BLAKE3 derive_key ("NextGraph Data BLAKE3 key", StoreRepo + store's repo ReadCapSecret ) which is basically similar to the InnerOverlayId but not hashed, so that brokers cannot do "confirmation of a file" attacks.

@ -194,7 +194,7 @@ enum BrokerServerTypeV0 {
- NuriV0.identity : None for personal identity
- NuriV0.entire_store : If it is a store, will include all the docs belonging to the store. not used otherwise
- NuriV0.entire_store : If it is a store, will include all the docs belonging to the store. Not used otherwise
- NuriV0.objects : used only for FileGet.
@ -469,7 +469,7 @@ struct DocAddFile {
- DocAddFile.object : must be the reference you obtained from the last call to `RandomAccessFilePutChunk`.
- DocAddFile.filename : an optional filename. usually it is the original filename on the filesystem when selecting the binary file to upload.
- DocAddFile.filename : an optional filename. Usually it is the original filename on the filesystem when selecting the binary file to upload.
#### final Response
@ -733,7 +733,7 @@ enum DocQuery {
}
```
- AppRequestV0.nuri.target : represents the default graph. can be NuriV0::UserSite or NuriV0::None and in those cases, the **union graph** of all the graphs is used as default graph.
- AppRequestV0.nuri.target : represents the default graph. Can be NuriV0::UserSite or NuriV0::None and in those cases, the **union graph** of all the graphs is used as default graph.
- DocQuery::V0.base : an optional base to resolve all your relative URIs of resources in the SPARQL Query.
@ -743,7 +743,7 @@ enum DocQuery {
Depending on the type of query, the response differs.
- for SELECT queries: an `AppResponseV0::QueryResult(buffer)` where buffer is a `Vec<u8>` containing a UTF-8 serialization of a JSON string representing a JSON Sparql Query Result. see [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/).
- for SELECT queries: an `AppResponseV0::QueryResult(buffer)` where buffer is a `Vec<u8>` containing a UTF-8 serialization of a JSON string representing a JSON Sparql Query Result. See [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/).
- for CONSTRUCT an `AppResponseV0::Graph(buffer)` where buffer is a `Vec<u8>` containing a BARE serialization of a `Vec<Triple>`.
@ -787,7 +787,7 @@ AppRequestV0 {
nuri: NuriV0 {
target: NuriTargetV0::Repo(repo_id),
overlay: Some(overlay_id),
branch: //not implemented. defaults to main branch
branch: //not implemented. Defaults to main branch
... // all the rest empty
},
payload: None
@ -820,7 +820,7 @@ struct CommitInfo {
- AppHistory.history : in order, with the newest commits first. The first part of the tuple (ObjectId) is the CommitID.
- AppHistory.swimlane_state : can be discarded. it is only used by our GUI representation of the history in the Apps. Same with the x and y values in CommitInfo.
- AppHistory.swimlane_state : can be discarded. It is only used by our GUI representation of the history in the Apps. Same with the x and y values in CommitInfo.
### SignatureStatus
@ -836,7 +836,7 @@ AppRequestV0 {
nuri: NuriV0 {
target: NuriTargetV0::Repo(repo_id),
overlay: Some(overlay_id),
branch: //not implemented. defaults to main branch
branch: //not implemented. Defaults to main branch
... // all the rest empty
},
payload: None
@ -850,7 +850,7 @@ The response is a `AppResponseV0::SignatureStatus(Vec<(String, Option<String>, b
Which is a list of commits at the HEAD. Each commit is represented by a tuple that contains :
- the commit ID printed as a string (44 characters)
- an optional string that is present only if the commit is a signature. in this case, the string represents a list of the signed commits `c:[commit_id]:k:[commit_key]` joined with `:` (at least one commit is present), followed by partial Nuri for the signature object `:s:[signature_object_id]:k:[signature_object_key]`.
- an optional string that is present only if the commit is a signature. In this case, the string represents a list of the signed commits `c:[commit_id]:k:[commit_key]` joined with `:` (at least one commit is present), followed by partial Nuri for the signature object `:s:[signature_object_id]:k:[signature_object_key]`.
- a boolean that indicates if the commit is a snapshot. (in this case, only one commit is present in the HEADs)
### SignatureRequest
@ -867,7 +867,7 @@ AppRequestV0 {
nuri: NuriV0 {
target: NuriTargetV0::Repo(repo_id),
overlay: Some(overlay_id),
branch: //not implemented. defaults to main branch
branch: //not implemented. Defaults to main branch
... // all the rest empty
},
payload: None
@ -894,7 +894,7 @@ AppRequestV0 {
nuri: NuriV0 {
target: NuriTargetV0::Repo(repo_id),
overlay: Some(overlay_id),
branch: //not implemented. defaults to main branch
branch: //not implemented. Defaults to main branch
... // all the rest empty
},
payload: None

@ -120,10 +120,10 @@ enum ClientResponseContentV0 {
- ClientResponseV0.result :
- 0 means success
- 1 means PartialContent (the response is a stream. each element in the stream will have this result code)
- 1 means PartialContent (the response is a stream. Each element in the stream will have this result code)
- 2 means EndOfStream (that's the last response in the stream)
- 3 means False
- 4 and above are errors. for the list, see `ng-repo/src/errors.rs` starting at line 265.
- 4 and above are errors. For the list, see `ng-repo/src/errors.rs` starting at line 265.
When an error occurs (result >= 3), the content is of the type ClientResponseContentV0::EmptyResponse
@ -195,7 +195,7 @@ struct CommitGetV0 {
Request the pinning status for a repo on the broker (for the current user's session).
Returns an error code if not pinned, otherwise returns a RepoPinStatusV0.
The overlay entered in ClientMessage is important. if it is the outer, only outer pinning will be checked.
The overlay entered in ClientMessage is important. Id it is the outer, only outer pinning will be checked.
If it is the inner overlay, only the inner pinning will be checked.
#### Request
@ -316,11 +316,11 @@ enum OverlayAccess {
- PinRepoV0.overlay_root_topic : Root topic of the overlay, used to listen to overlay refreshes. Only set for inner overlays (RW or WO) overlays. Not implemented yet
- PinRepoV0.expose_outer : only possible for RW overlays. not allowed for private or dialog overlay. not implemented yet
- PinRepoV0.expose_outer : only possible for RW overlays. Not allowed for private or dialog overlay. Not implemented yet
- PinRepoV0.peers : Broker peers to connect to in order to join the overlay. If the repo has previously been opened (during the same session) or if it is a private overlay, then peers info can be omitted. If there are no known peers in the overlay yet, vector is left empty (creation of a store, or repo in a store that is owned by user).
- PinRepoV0.max_peer_count : Maximum number of peers to connect to for this overlay (only valid for an inner (RW/WO) overlay). not implemented yet
- PinRepoV0.max_peer_count : Maximum number of peers to connect to for this overlay (only valid for an inner (RW/WO) overlay). Not implemented yet
- PinRepoV0.ro_topics : list of topics that should be subscribed to. If the repo has previously been opened (during the same session) and the list of RO topics does not need to be modified, then ro_topics info can be omitted
@ -365,7 +365,7 @@ struct TopicSubV0 {
#### Response
A `TopicSubRes`. see above in [RepoPinStatus](#repopinstatus) for more details.
A `TopicSubRes`. See above in [RepoPinStatus](#repopinstatus) for more details.
### TopicSyncReq
@ -418,7 +418,7 @@ enum TopicSyncResV0 {
Request to know if some blocks are present locally on the responder
used by a Client before publishing an event with FILES, to know what to push, and save bandwidth if the blocks are already present on the Broker (content deduplication). commits without FILES cannot be deduplicated because they are unique, due to their unique position in the DAG, and the embedded BranchId.
used by a Client before publishing an event with FILES, to know what to push, and save bandwidth if the blocks are already present on the Broker (content deduplication). Commits without FILES cannot be deduplicated because they are unique, due to their unique position in the DAG, and the embedded BranchId.
Replied with `BlocksFound`

@ -48,7 +48,7 @@ The 2 Javascript libraries do not have a User Storage so they only support in-me
As the feature of the “User Storage for Web” will take some time to be coded, so we offer another way to solve the problem of volatile materialized state in JS.
There is also the idea of having a full fledged Verifier running inside nodeJS. this would use the NAPI-RS system which compiles the Rust code of the verifier, together with the RocksDb code, and make it a binary library compatible with nodeJS that would run inside the nodeJS process. This also will take some time to be coded.
There is also the idea of having a full fledged Verifier running inside nodeJS. This would use the NAPI-RS system which compiles the Rust code of the verifier, together with the RocksDb code, and make it a binary library compatible with nodeJS that would run inside the nodeJS process. This also will take some time to be coded.
Instead for both cases (JS in web and in nodeJS) we offer the App API that connects to a remote Verifier.
@ -60,13 +60,13 @@ Usually `ngd` only acts as a broker, but it can be configured and used as a Veri
in which use cases is it useful ?
- when the end-user doesn’t have a supported platform where to install the native app. by example, a workstation running OpenBSD or FreeBSD, doesn't have a native app to download (and cannot be compiled neither as Tauri doesn't support such platform). In this case, the end-user has to launch a local ngd, and open the webapp from their browser (http://localhost:1440). The verifier will run remotely, inside ngd (that isn't very far. it is on the same machine). Because it is on localhost or in a private LAN, we do allow the webapp to be served on http (without TLS) and the websocket is also working well without TLS. But this doesn't work anymore if the public IP of the ngd server is used.
- when the end-user doesn’t have a supported platform where to install the native app. By example, a workstation running OpenBSD or FreeBSD, doesn't have a native app to download (and cannot be compiled neither as Tauri doesn't support such platform). In this case, the end-user has to launch a local ngd, and open the webapp from their browser (http://localhost:1440). The verifier will run remotely, inside ngd (that isn't very far, it is on the same machine). Because it is on localhost or in a private LAN, we do allow the webapp to be served on http (without TLS) and the websocket is also working well without TLS. But this doesn't work anymore if the public IP of the ngd server is used.
- when a nodeJS service needs access to the documents and does not want to use the in-memory Verifier, because it needs quick access (like a headless CMS, Astro, an AI service like jan.ai, or a SPARQL REST endpoint, an LDP endpoint, etc..) then in this case, an ngd instance has to run in the same machine as the nodeJS process, or in the same LAN network (Docker network by example).
- in the headless mode, when a server is using ngd as a quadstore/document store and the full credentials of the user identity has been delegated to that server. This is the case for ActivityPods, by example.
- on the SaaS/cloud of NextGraph, we run some ngd brokers that normally would not have any verifier. But in some cases, at the request of the end-user, we can run some verifiers that have limited access to some documents or stores of the user. if they want to serve their data as REST/HTTP endpoints, by example. The end-user will have to grant access about those resources to this remote verifier, by providing their DID capabilities. A Verifier can see in clear all the data that it manipulates, so obviously, users have to be careful where they run a Verifier, and to whom they give the capabilities.
- on the SaaS/cloud of NextGraph, we run some ngd brokers that normally would not have any verifier. But in some cases, at the request of the end-user, we can run some verifiers that have limited access to some documents or stores of the user. If they want to serve their data as REST/HTTP endpoints, by example. The end-user will have to grant access about those resources to this remote verifier, by providing their DID capabilities. A Verifier can see in clear all the data that it manipulates, so obviously, users have to be careful where they run a Verifier, and to whom they give the capabilities.
What is important to understand is that the Verifier needs to run in a trusted environment because it holds the ReadCaps of the documents it is going to open, and in some cases, it even holds the full credentials of the User Identity and has access to the whole set of documents in all stores of the user.
@ -97,7 +97,7 @@ The Verifier talks to the Broker with the ClientProtocol, and receives the encry
Then, it exposes the AppProtocol to the Application level, this is what is used to access and modify the data.
Sometimes the Verifier and the Broker are on the same machine, in the same process, so they use the LocalTransport which is not even using the network interface. That’s the beauty of the code of NextGraph. it has been thought from the beginning with many use cases in mind.
Sometimes the Verifier and the Broker are on the same machine, in the same process, so they use the LocalTransport which is not even using the network interface. That’s the beauty of the code of NextGraph. It has been thought from the beginning with many use cases in mind.
Sometimes the Verifier and the App are in the same process, sometimes they need a websocket between them. But all of this are implementation details. For the developers, the same API is available everywhere, in nodeJS, in front-end Javascript, in Rust, and similarly, as commands in the CLI, regardless of where the Verifier and the Broker are actually located.

@ -16,13 +16,13 @@ We have designed a new wallet specially for our needs, and we have innovative fe
A wallet should be unique to a physical human being. We cannot strictly enforce that, but we would like this rule to be respected, in order to simplify a bit the use cases. So we recommend each person to create only one Wallet for themselves.
Inside a Wallet, many Identities can coexist. A physical individual can create several identities for themselves inside the same wallet. By default, when the Wallet is created, a Personal identity is also created within it. That's the default identity. Then, the individual can add more identities, that will be untraceable back to the wallet, meaning that each identity is unique and has no link to the wallet or to other identities within it. When interacting with other Users, if distinct Identities are used, there will be no way to correlate those distinct Identities, even if they are stored in the same Wallet. By example, one can use the default Personal identity for friends and family, but then create another identity for their professional interactions. it will be impossible for their coworker or boss to find out what is their personal identity, and vis versa. the number of additional identities is unlimited. They are stored in the wallet and just grows the size of the wallet. This feature guarantees total anonymity and separation of Identities. In NextGraph terminology, an Identity is the same as a User.
Inside a Wallet, many Identities can coexist. A physical individual can create several identities for themselves inside the same wallet. By default, when the Wallet is created, a Personal identity is also created within it. That's the default identity. Then, the individual can add more identities, that will be untraceable back to the wallet, meaning that each identity is unique and has no link to the wallet or to other identities within it. When interacting with other Users, if distinct Identities are used, there will be no way to correlate those distinct Identities, even if they are stored in the same Wallet. By example, one can use the default Personal identity for friends and family, but then create another identity for their professional interactions. It will be impossible for their coworker or boss to find out about their personal identity, and vice versa. The number of additional identities is unlimited. They are stored in the wallet and just grows the size of the wallet. This feature guarantees total anonymity and separation of Identities. In NextGraph terminology, an Identity is the same as a User.
Then we also have the concept of an Organization.
An Organization is another type of Identity. It has the same content as a Personal Identity. But in addition, it has member Users, which are Individual Identities that have been associated with the Organization, in a similar way as with email addresses at individual@organization .
An Organization can also have sub-organization, in a hierarchical manner. as in organization/sub-organization .
An Organization can also have sub-organization, in a hierarchical manner, as in `organization/sub-organization`.
An Organization has Owners, and the ownership can be transferred to other Identities, while this is not the case for Individual Identities that cannot be transferred.
@ -46,7 +46,7 @@ Once the wallet has arrived on a new device, everything works the same in the ne
In order to protect your wallet from unauthorized access, we have decided not to use a password, because that would be too risky. We know very well that users do not choose secure password by themselves, because they need to remember such password, so it has to be simple.
The other opposite behaviour is to create a very secure password with a password generator, by example, and then store this very complex password in a password/keys manager. This is another problem, as it just transfers the security of the whole system to that "password manager" that we know have been found insecure so many times. the question of the transfer of such complex password from one device to another is another problem... and we wouldn't have solved anything if we were to use passwords.
The other opposite behaviour is to create a very secure password with a password generator, by example, and then store this very complex password in a password/keys manager. This is another problem, as it just transfers the security of the whole system to that "password manager" that we know have been found insecure so many times. The question of the transfer of such complex password from one device to another is another problem... And we wouldn't have solved anything if we were to use passwords.
So.. here comes the Pazzle.
@ -62,7 +62,7 @@ There is also a mnemonic "passphrase" that is an alternative way to login into y
We propose this option for those we have special needs (like programmers that need to enter in the wallet very quickly).
In general, we encourage you to use the pazzle instead. in the future, we will also implement the option to login with physical dongle/key that has been paired with the wallet.
In general, we encourage you to use the pazzle instead. In the future, we will also implement the option to login with physical dongle/key that has been paired with the wallet.
The mnemonic can also be seen as a recovery passphrase. But be very careful where you save it. Anybody that finds your mnemonic or pazzle, and also has a hold on the wallet file or on a device that has the wallet already imported, can surely enter your account and read/write all your data.

Loading…
Cancel
Save