prepare crate for publishing. with examples. more docs

pull/19/head
Niko PLP 9 months ago
parent fc4924ac87
commit bada481432
  1. 88
      Cargo.lock
  2. 10
      README.md
  3. 12
      nextgraph/Cargo.toml
  4. 32
      nextgraph/README.md
  5. 15
      nextgraph/examples/README.md
  6. 13
      nextgraph/examples/in_memory.md
  7. 133
      nextgraph/examples/in_memory.rs
  8. 13
      nextgraph/examples/persistent.md
  9. 28
      nextgraph/examples/persistent.rs
  10. BIN
      nextgraph/examples/wallet-security-image-demo.png
  11. 61
      nextgraph/src/lib.rs
  12. 178
      nextgraph/src/local_broker.rs
  13. 118
      ng-app/src-tauri/src/lib.rs
  14. 16
      ng-app/src/App.svelte
  15. 26
      ng-app/src/api.ts
  16. 2
      ng-app/src/routes/User.svelte
  17. 2
      ng-app/src/routes/WalletCreate.svelte
  18. 12
      ng-app/src/wallet_emojis.ts
  19. 4
      ng-broker/README.md
  20. 5
      ng-broker/src/server_ws.rs
  21. 4
      ng-client-ws/README.md
  22. 4
      ng-net/README.md
  23. 2
      ng-net/src/actor.rs
  24. 2
      ng-net/src/actors/mod.rs
  25. 2
      ng-net/src/broker.rs
  26. 6
      ng-net/src/connection.rs
  27. 7
      ng-net/src/lib.rs
  28. 2
      ng-net/src/server_storage.rs
  29. 1
      ng-net/src/tests/mod.rs
  30. 106
      ng-net/src/types.rs
  31. 3
      ng-repo/Cargo.toml
  32. 4
      ng-repo/README.md
  33. 2
      ng-repo/src/block.rs
  34. 2
      ng-repo/src/commit.rs
  35. 44
      ng-repo/src/errors.rs
  36. 2
      ng-repo/src/event.rs
  37. 2
      ng-repo/src/file.rs
  38. 2
      ng-repo/src/kcv_store.rs
  39. 2
      ng-repo/src/lib.rs
  40. 2
      ng-repo/src/object.rs
  41. 56
      ng-repo/src/os_info.rs
  42. 2
      ng-repo/src/repo.rs
  43. 2
      ng-repo/src/site.rs
  44. 2
      ng-repo/src/store.rs
  45. 79
      ng-repo/src/types.rs
  46. 4
      ng-sdk-js/README.md
  47. 4
      ng-sdk-js/app-react/README.md
  48. 4
      ng-sdk-js/app-web/README.md
  49. 16
      ng-sdk-js/js/node.js
  50. 109
      ng-sdk-js/src/lib.rs
  51. 4
      ng-stores-rocksdb/README.md
  52. 4
      ng-verifier/README.md
  53. 4
      ng-wallet/README.md
  54. 82
      ng-wallet/src/emojis.rs
  55. 100
      ng-wallet/src/lib.rs
  56. 90
      ng-wallet/src/types.rs
  57. 4
      ngcli/README.md
  58. 4
      ngd/README.md
  59. 2
      ngd/src/cli.rs
  60. 6
      ngd/src/main.rs

88
Cargo.lock generated

@ -1220,6 +1220,12 @@ dependencies = [
"rand 0.7.3", "rand 0.7.3",
] ]
[[package]]
name = "current_platform"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a74858bcfe44b22016cb49337d7b6f04618c58e5dbfdef61b06b8c434324a0bc"
[[package]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "3.2.0" version = "3.2.0"
@ -3124,12 +3130,14 @@ dependencies = [
"ng-client-ws", "ng-client-ws",
"ng-net", "ng-net",
"ng-repo", "ng-repo",
"ng-stores-rocksdb",
"ng-verifier", "ng-verifier",
"ng-wallet", "ng-wallet",
"once_cell", "once_cell",
"serde", "serde",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"serde_json",
"web-time", "web-time",
"zeroize", "zeroize",
] ]
@ -3239,6 +3247,7 @@ dependencies = [
"base64-url", "base64-url",
"blake3", "blake3",
"chacha20", "chacha20",
"current_platform",
"curve25519-dalek 3.2.0", "curve25519-dalek 3.2.0",
"debug_print", "debug_print",
"ed25519-dalek", "ed25519-dalek",
@ -3249,10 +3258,12 @@ dependencies = [
"hex", "hex",
"log", "log",
"once_cell", "once_cell",
"os_info",
"rand 0.7.3", "rand 0.7.3",
"serde", "serde",
"serde_bare", "serde_bare",
"serde_bytes", "serde_bytes",
"serde_json",
"slice_as_array", "slice_as_array",
"threshold_crypto", "threshold_crypto",
"time 0.3.23", "time 0.3.23",
@ -3688,6 +3699,17 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
] ]
[[package]]
name = "os_info"
version = "3.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092"
dependencies = [
"log",
"serde",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -6312,6 +6334,15 @@ dependencies = [
"windows-targets 0.48.0", "windows-targets 0.48.0",
] ]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.4",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.42.2" version = "0.42.2"
@ -6342,6 +6373,21 @@ dependencies = [
"windows_x86_64_msvc 0.48.0", "windows_x86_64_msvc 0.48.0",
] ]
[[package]]
name = "windows-targets"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
]
[[package]] [[package]]
name = "windows-tokens" name = "windows-tokens"
version = "0.48.0" version = "0.48.0"
@ -6360,6 +6406,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.42.2" version = "0.42.2"
@ -6372,6 +6424,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.42.2" version = "0.42.2"
@ -6384,6 +6442,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.42.2" version = "0.42.2"
@ -6396,6 +6460,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.42.2" version = "0.42.2"
@ -6408,6 +6478,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.42.2" version = "0.42.2"
@ -6420,6 +6496,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.42.2" version = "0.42.2"
@ -6432,6 +6514,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.4.7" version = "0.4.7"

@ -8,6 +8,8 @@
[![Apache 2.0 Licensed][license-image]][license-link] [![Apache 2.0 Licensed][license-image]][license-link]
[![MIT Licensed][license-image2]][license-link2] [![MIT Licensed][license-image2]][license-link2]
[![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://forum.nextgraph.org) [![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://forum.nextgraph.org)
![Crates.io Version](https://img.shields.io/crates/v/nextgraph)
![docs.rs](https://img.shields.io/docsrs/nextgraph)
Rust implementation of NextGraph Rust implementation of NextGraph
@ -15,9 +17,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)
@ -65,11 +67,11 @@ The crates are organized as follow :
- [ngd](ngd/README.md) : binary executable of the daemon (that can run a broker, verifier and/or Rust services) - [ngd](ngd/README.md) : binary executable of the daemon (that can run a broker, verifier and/or Rust services)
- ng-repo : Repositories common library - ng-repo : Repositories common library
- ng-net : Network common library - ng-net : Network common library
- ng-verifier : Verifier library, that exposes the document API to the app
- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet
- ng-broker : Core and Server Broker library - ng-broker : Core and Server Broker library
- ng-client-ws : Websocket client library - ng-client-ws : Websocket client library
- ng-verifier : Verifier library, that exposes the document API to the app
- ng-stores-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb) - ng-stores-rocksdb : RocksDB backed stores. see also dependency [repo here](https://git.nextgraph.org/NextGraph/rust-rocksdb)
- ng-wallet : keeps the secret keys of all identities of the user in a safe wallet
- [ng-sdk-js](ng-sdk-js/README.md) : contains the JS SDK, with example apps: web app, react app, or node service. - [ng-sdk-js](ng-sdk-js/README.md) : contains the JS SDK, with example apps: web app, react app, or node service.
- [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the web app. - [ng-app](ng-app/README.md) : all the native apps, based on Tauri, and the web app.
- ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency - ngone : server for nextgraph.one. helps user bootstrap into the right app. Not useful to you. Published here for transparency

@ -21,6 +21,7 @@ ng-net = { path = "../ng-net", version = "0.1.0" }
ng-wallet = { path = "../ng-wallet", version = "0.1.0" } ng-wallet = { path = "../ng-wallet", version = "0.1.0" }
ng-client-ws = { path = "../ng-client-ws", version = "0.1.0" } ng-client-ws = { path = "../ng-client-ws", version = "0.1.0" }
ng-verifier = { path = "../ng-verifier", version = "0.1.0" } ng-verifier = { path = "../ng-verifier", version = "0.1.0" }
ng-stores-rocksdb = { path = "../ng-stores-rocksdb", version = "0.1.0" }
async-once-cell = "0.5.3" async-once-cell = "0.5.3"
once_cell = "1.17.1" once_cell = "1.17.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
@ -28,5 +29,14 @@ serde_bare = "0.5.0"
serde_bytes = "0.11.7" serde_bytes = "0.11.7"
base64-url = "2.0.0" base64-url = "2.0.0"
web-time = "0.2.0" web-time = "0.2.0"
async-std = { version = "1.12.0", features = ["attributes","unstable"] } async-std = { version = "1.12.0", features = [ "attributes", "unstable" ] }
zeroize = { version = "1.6.0", features = ["zeroize_derive"] } zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
serde_json = "1.0"
[[example]]
name = "in_memory"
required-features = []
[[example]]
name = "persistent"
required-features = []

@ -7,29 +7,51 @@
![MSRV][rustc-image] ![MSRV][rustc-image]
[![Apache 2.0 Licensed][license-image]][license-link] [![Apache 2.0 Licensed][license-image]][license-link]
[![MIT Licensed][license-image2]][license-link2] [![MIT Licensed][license-image2]][license-link2]
![Crates.io Version](https://img.shields.io/crates/v/nextgraph)
![docs.rs](https://img.shields.io/docsrs/nextgraph)
Rust client library of NextGraph Rust client library of NextGraph framework
This repository is in active development at [https://git.nextgraph.org/NextGraph/nextgraph-rs](https://git.nextgraph.org/NextGraph/nextgraph-rs), a Gitea instance. For bug reports, issues, merge requests, and in order to join the dev team, please visit the link above and create an account (you can do so with a github account). The [github repo](https://github.com/nextgraph-org/nextgraph-rs) is just a read-only mirror that does not accept issues. This library is in active development at [https://git.nextgraph.org/NextGraph/nextgraph-rs](https://git.nextgraph.org/NextGraph/nextgraph-rs), a Gitea instance. For bug reports, issues, merge requests, and in order to join the dev team, please visit the link above and create an account (you can do so with a github account). The [github repo](https://github.com/nextgraph-org/nextgraph-rs) is just a read-only mirror that does not accept issues.
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)
## Support ## Support
This crate has official documentation at [docs.rs](https://docs.rs/nextgraph/0.1.0/nextgraph/)
Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgraph.org) Documentation can be found here [https://docs.nextgraph.org](https://docs.nextgraph.org)
And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org) And our community forum where you can ask questions is here [https://forum.nextgraph.org](https://forum.nextgraph.org)
## How to use the library ## Status
NextGraph is not ready yet. You can subscribe to [our newsletter](https://list.nextgraph.org/subscription/form) to get updates, and support us with a [donation](https://nextgraph.org/donate/). NextGraph is not ready yet. You can subscribe to [our newsletter](https://list.nextgraph.org/subscription/form) to get updates, and support us with a [donation](https://nextgraph.org/donate/).
## Dependencies
Nextgraph library is dependent on [async-std](https://async.rs/). You must include it in your `Cargo.toml`.
A tokio-based version (as a feature) might be available in the future.
```toml
[dependencies]
nextgraph = "0.1.0"
async-std = "1.12.0"
```
## Examples
You can find some examples on how to use the library:
- [in_memory](examples/in_memory)
- [persistent](examples/persistent)
## License ## License
Licensed under either of Licensed under either of

@ -0,0 +1,15 @@
# Examples
Some examples of using `nextgraph` client library
run them with:
```
cargo run -p nextgraph --example in_memory
cargo run -p nextgraph --example persistent
```
See the code:
- [in_memory](in_memory)
- [persistent](persistent)

@ -0,0 +1,13 @@
# in-memory LocalBroker
Example of LocalBroker configured with in-memory (no persistence).
run with:
```
cargo run -p nextgraph -r --example in_memory
```
we assume that you run this command from the root of the git repo (nextgraph-rs)
the `-r` for release version is important, without it, the creation and opening of the wallet will take ages.

@ -0,0 +1,133 @@
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use nextgraph::local_broker::{
init_local_broker, session_start, session_stop, user_connect, user_disconnect, wallet_close,
wallet_create_v0, wallet_get_file, wallet_import, wallet_open_with_pazzle_words,
wallet_read_file, wallet_was_opened, LocalBrokerConfig, SessionConfig,
};
use nextgraph::net::types::BootstrapContentV0;
use nextgraph::repo::errors::NgError;
use nextgraph::repo::types::PubKey;
use nextgraph::wallet::types::CreateWalletV0;
use nextgraph::wallet::{display_mnemonic, emojis::display_pazzle};
use std::env::current_dir;
use std::fs::read;
#[async_std::main]
async fn main() -> std::io::Result<()> {
// initialize the local_broker with in-memory config.
// all sessions will be lost when the program exits
init_local_broker(Box::new(|| LocalBrokerConfig::InMemory)).await;
// load some image that will be used as security_img
// we assume here for the sake of this example,
// that the current directory contains this demo image file
let mut current_path = current_dir()?;
current_path.push("nextgraph");
current_path.push("examples");
current_path.push("wallet-security-image-demo.png");
let security_img = read(current_path)?;
// the peer_id should come from somewhere else.
// this is just given for the sake of an example
#[allow(deprecated)]
let peer_id_of_server_broker = PubKey::nil();
// Create your wallet
// this will take some time !
println!("Creating the wallet. this will take some time...");
let wallet_result = wallet_create_v0(CreateWalletV0 {
security_img,
security_txt: "know yourself".to_string(),
pin: [1, 2, 1, 2],
pazzle_length: 9,
send_bootstrap: false,
send_wallet: false,
result_with_wallet_file: true,
local_save: false,
// we default to localhost:14400. this is just for the sake of an example
core_bootstrap: BootstrapContentV0::new_localhost(peer_id_of_server_broker),
core_registration: None,
additional_bootstrap: None,
})
.await?;
let pazzle = display_pazzle(&wallet_result.pazzle);
let mut pazzle_words = vec![];
println!("Your pazzle is: {:?}", wallet_result.pazzle);
for emoji in pazzle {
println!(" {}:\t{}", emoji.0, emoji.1);
pazzle_words.push(emoji.1.to_string());
}
println!("Your mnemonic is:");
display_mnemonic(&wallet_result.mnemonic)
.iter()
.for_each(|word| print!("{} ", word.as_str()));
println!("");
// at this point, the wallet is kept in the internal memory of the LocalBroker, but it hasn't been saved to disk
// and it hasn't been opened yet, so it is not usable right away.
// now let's open the wallet, by providing the pazzle and PIN code
let opened_wallet =
wallet_open_with_pazzle_words(&wallet_result.wallet, &pazzle_words, [1, 2, 1, 2])?;
let user_id = opened_wallet.personal_identity();
// once the wallet is opened, we notify the LocalBroker that we have opened it.
let _client = wallet_was_opened(opened_wallet).await?;
// if you need the Wallet File again (if you didn't select `result_with_wallet_file` by example), you can retrieve it with:
let wallet_file = wallet_get_file(&wallet_result.wallet_name).await?;
// if you did ask for `result_with_wallet_file`, as we did above, then the 2 vectors should be identical
assert_eq!(wallet_file, wallet_result.wallet_file);
{
// this part should happen on another device. just given here as an example
// on another device, you could use the Wallet File and import it there so it could be used for login.
// first you would read and decode the Wallet File
// this fails here because we already added this wallet in the LocalBroker (when we created it). But on another device, it would work.
let wallet = wallet_read_file(wallet_file).await;
assert_eq!(wallet.unwrap_err(), NgError::WalletAlreadyAdded);
// on another device, we would then open the wallet (here we take the Wallet as we received it from wallet_create_v0, but in real case you would use `wallet`)
let opened_wallet2 =
wallet_open_with_pazzle_words(&wallet_result.wallet, &pazzle_words, [1, 2, 1, 2])?;
// once it has been opened, the Wallet can be imported into the LocalBroker
// if you try to import the same wallet in a LocalBroker where it is already opened, it will fail. So here it fails. But on another device, it would work.
let client_fail = wallet_import(wallet_result.wallet.clone(), opened_wallet2, true).await;
assert_eq!(client_fail.unwrap_err(), NgError::WalletAlreadyAdded);
}
// anyway, now that the wallet is opened, let's start a session.
// we pass the user_id and the wallet_name
let _session = session_start(SessionConfig::new(&user_id, &wallet_result.wallet_name)).await?;
// if the user has internet access, they can now decide to connect to its Server Broker, in order to sync data
let status = user_connect(&user_id).await?;
// The connection cannot succeed because we miss-configured the core_bootstrap of the wallet. it's Peer ID is invalid.
assert_eq!(status[0].3.as_ref().unwrap(), "NoiseHandshakeFailed");
// Then we should disconnect
user_disconnect(&user_id).await?;
// stop the session
session_stop(&user_id).await?;
// closes the wallet
wallet_close(&wallet_result.wallet_name).await?;
Ok(())
}

@ -0,0 +1,13 @@
# persistent LocalBroker
Example of LocalBroker configured with persistence to disk
run with:
```
cargo run -p nextgraph -r --example persistent
```
we assume that you run this command from the root of the git repo (nextgraph-rs).
the `-r` for release version is important, without it, the creation and opening of the wallet will take ages.

@ -0,0 +1,28 @@
// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use nextgraph::local_broker::{init_local_broker, LocalBrokerConfig};
use std::env::current_dir;
use std::fs::create_dir_all;
#[async_std::main]
async fn main() -> std::io::Result<()> {
// get the current working directory
let mut current_path = current_dir()?;
current_path.push("ng");
create_dir_all(current_path.clone())?;
// initialize the local_broker with config to save to disk in a folder called `ng` in the current directory
init_local_broker(Box::new(move || {
LocalBrokerConfig::BasePath(current_path.clone())
}))
.await;
Ok(())
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@ -1 +1,62 @@
#![doc(html_logo_url = "https://file.nextgraph.org/download/1fd175bb6d7d832156bd5ad4abcdee7e")]
#![doc(issue_tracker_base_url = "https://git.nextgraph.org/NextGraph/nextgraph-rs/issues")]
#![doc(html_favicon_url = "https://nextgraph.org/favicon.svg")]
//! # NextGraph framework client library
//!
//! NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
//!
//! This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
//!
//! More info here [https://nextgraph.org](https://nextgraph.org). Documentation available here [https://docs.nextgraph.org](https://docs.nextgraph.org).
//!
//! ## LocalBroker, the entrypoint to NextGraph network
//!
//! `local_broker` contains the API for controlling the Local Broker, which is a reduced instance of the network Broker.
//! This is your entrypoint to NextGraph network.
//! It runs embedded in your client program, and once configured (by opening a Session), it can keep for you (on disk or in memory):
//! - the blocks of the repos,
//! - the connection(s) to your Server Broker
//! - the events that you send to the Overlay, if there is no connectivity (Outbox)
//! - A reference to the verifier (optional)
//!
//! In addition, the API for creating and managing your wallet is provided here.
//!
//! The same API is also made available in Javascript for the browser (and is used by our webapp), nodejs, in the CLI, and for all the Tauri-based Apps.
//!
//! The library requires `async-std` minimal version 1.12.0
//!
//! See [examples](https://git.nextgraph.org/NextGraph/nextgraph-rs/src/branch/master/nextgraph/examples) for a quick start.
//!
//! ## In-memory
//!
//! With this config, no data will be persisted to disk.
//!
//! ```
//! use nextgraph::local_broker::{init_local_broker, LocalBrokerConfig};
//!
//! #[async_std::main]
//! async fn main() -> std::io::Result<()> {
//! // initialize the local_broker with in-memory config.
//! // all sessions will be lost when the program exits
//! init_local_broker(Box::new(|| LocalBrokerConfig::InMemory)).await;
//! Ok(())
//! }
//! ```
pub mod local_broker; pub mod local_broker;
pub mod repo {
pub use ng_repo::*;
}
pub mod net {
pub use ng_net::*;
}
pub mod verifier {
pub use ng_verifier::*;
}
pub mod wallet {
pub use ng_wallet::*;
}

@ -11,9 +11,12 @@ use async_once_cell::OnceCell;
use async_std::sync::{Arc, RwLock}; use async_std::sync::{Arc, RwLock};
use core::fmt; use core::fmt;
use ng_net::connection::{ClientConfig, IConnect, StartConfig}; use ng_net::connection::{ClientConfig, IConnect, StartConfig};
use ng_net::types::ClientInfo; use ng_net::types::{ClientInfo, ClientType};
use ng_repo::os_info::get_os_info;
use ng_wallet::emojis::encode_pazzle;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde_bare::to_vec; use serde_bare::to_vec;
use serde_json::json;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{read, write, File, OpenOptions}; use std::fs::{read, write, File, OpenOptions};
use std::path::PathBuf; use std::path::PathBuf;
@ -34,6 +37,7 @@ type JsStorageReadFn = dyn Fn(String) -> Result<String, NgError> + 'static + Syn
type JsStorageWriteFn = dyn Fn(String, String) -> Result<(), NgError> + 'static + Sync + Send; type JsStorageWriteFn = dyn Fn(String, String) -> Result<(), NgError> + 'static + Sync + Send;
type JsCallback = dyn Fn() + 'static + Sync + Send; type JsCallback = dyn Fn() + 'static + Sync + Send;
#[doc(hidden)]
pub struct JsStorageConfig { pub struct JsStorageConfig {
pub local_read: Box<JsStorageReadFn>, pub local_read: Box<JsStorageReadFn>,
pub local_write: Box<JsStorageWriteFn>, pub local_write: Box<JsStorageWriteFn>,
@ -47,10 +51,15 @@ impl fmt::Debug for JsStorageConfig {
} }
} }
/// Configuration for the LocalBroker. must be returned by a function or closure passed to [init_local_broker]
#[derive(Debug)] #[derive(Debug)]
pub enum LocalBrokerConfig { pub enum LocalBrokerConfig {
/// Local broker will not save any wallet, session or user's data
InMemory, InMemory,
/// Local broker will save all wallets, sessions and user's data on disk, in the provided `Path`
BasePath(PathBuf), BasePath(PathBuf),
#[doc(hidden)]
/// used internally for the JS SDK
JsStorage(JsStorageConfig), JsStorage(JsStorageConfig),
} }
@ -61,12 +70,14 @@ impl LocalBrokerConfig {
_ => false, _ => false,
} }
} }
#[doc(hidden)]
pub fn is_js(&self) -> bool { pub fn is_js(&self) -> bool {
match self { match self {
Self::JsStorage(_) => true, Self::JsStorage(_) => true,
_ => false, _ => false,
} }
} }
#[doc(hidden)]
pub fn js_config(&self) -> Option<&JsStorageConfig> { pub fn js_config(&self) -> Option<&JsStorageConfig> {
match self { match self {
Self::JsStorage(c) => Some(c), Self::JsStorage(c) => Some(c),
@ -78,14 +89,14 @@ impl LocalBrokerConfig {
//type LastSeqFn = fn(PubKey, u16) -> Result<u64, NgError>; //type LastSeqFn = fn(PubKey, u16) -> Result<u64, NgError>;
pub type LastSeqFn = dyn Fn(PubKey, u16) -> Result<u64, NgError> + 'static + Sync + Send; pub type LastSeqFn = dyn Fn(PubKey, u16) -> Result<u64, NgError> + 'static + Sync + Send;
/// peer_id: PubKey, seq_num:u64, event_ser: vec<u8>, // peer_id: PubKey, seq_num:u64, event_ser: vec<u8>,
pub type OutboxWriteFn = pub type OutboxWriteFn =
dyn Fn(PubKey, u64, Vec<u8>) -> Result<(), NgError> + 'static + Sync + Send; dyn Fn(PubKey, u64, Vec<u8>) -> Result<(), NgError> + 'static + Sync + Send;
/// peer_id: PubKey, // peer_id: PubKey,
pub type OutboxReadFn = dyn Fn(PubKey) -> Result<Vec<Vec<u8>>, NgError> + 'static + Sync + Send; pub type OutboxReadFn = dyn Fn(PubKey) -> Result<Vec<Vec<u8>>, NgError> + 'static + Sync + Send;
/// Session Config to initiate a session at a local broker V0 /// used to initiate a session at a local broker V0
pub struct SessionConfigV0 { pub struct SessionConfigV0 {
pub user_id: UserId, pub user_id: UserId,
pub wallet_name: String, pub wallet_name: String,
@ -94,7 +105,7 @@ pub struct SessionConfigV0 {
// pub outbox_read_function: Box<OutboxReadFn>, // pub outbox_read_function: Box<OutboxReadFn>,
} }
/// Session Config to initiate a session at a local broker /// used to initiate a session at a local broker
pub enum SessionConfig { pub enum SessionConfig {
V0(SessionConfigV0), V0(SessionConfigV0),
} }
@ -118,6 +129,14 @@ impl SessionConfig {
Self::V0(v0) => v0.wallet_name.clone(), Self::V0(v0) => v0.wallet_name.clone(),
} }
} }
/// Creates a new SessionConfig with a UserId and a wallet name
/// that should be passed to [session_start]
pub fn new(user_id: &UserId, wallet_name: &String) -> Self {
SessionConfig::V0(SessionConfigV0 {
user_id: user_id.clone(),
wallet_name: wallet_name.clone(),
})
}
} }
impl fmt::Debug for SessionConfigV0 { impl fmt::Debug for SessionConfigV0 {
@ -164,9 +183,7 @@ impl LocalBroker {
} }
} }
//static LOCAL_BROKER: OnceCell<Result<(), NgError>> = OnceCell::new(); static LOCAL_BROKER: OnceCell<Result<Arc<RwLock<LocalBroker>>, NgError>> = OnceCell::new();
static LOCAL_BROKER: OnceCell<Result<Arc<RwLock<LocalBroker>>, NgError>> = OnceCell::new(); //Lazy::new(|| Arc::new(RwLock::new(Broker::new())));
pub type ConfigInitFn = dyn Fn() -> LocalBrokerConfig + 'static + Sync + Send; pub type ConfigInitFn = dyn Fn() -> LocalBrokerConfig + 'static + Sync + Send;
@ -213,7 +230,7 @@ async fn init_(config: LocalBrokerConfig) -> Result<Arc<RwLock<LocalBroker>>, Ng
Ok(Arc::new(RwLock::new(local_broker))) Ok(Arc::new(RwLock::new(local_broker)))
} }
//-> &'static Result<Arc<RwLock<LocalBroker>>, NgError> #[doc(hidden)]
pub async fn init_local_broker_with_lazy(config_fn: &Lazy<Box<ConfigInitFn>>) { pub async fn init_local_broker_with_lazy(config_fn: &Lazy<Box<ConfigInitFn>>) {
LOCAL_BROKER LOCAL_BROKER
.get_or_init(async { .get_or_init(async {
@ -223,6 +240,11 @@ pub async fn init_local_broker_with_lazy(config_fn: &Lazy<Box<ConfigInitFn>>) {
.await; .await;
} }
/// Initialize the configuration of your local broker
///
/// , by passing in a function (or closure) that returns a `LocalBrokerConfig`.
/// You must call `init_local_broker` at least once before you can start to use the broker.
/// After the first call, all subsequent calls will have no effect.
pub async fn init_local_broker(config_fn: Box<ConfigInitFn>) { pub async fn init_local_broker(config_fn: Box<ConfigInitFn>) {
LOCAL_BROKER LOCAL_BROKER
.get_or_init(async { .get_or_init(async {
@ -232,7 +254,8 @@ pub async fn init_local_broker(config_fn: Box<ConfigInitFn>) {
.await; .await;
} }
pub async fn get_all_wallets() -> Result<HashMap<String, LocalWalletStorageV0>, NgError> { /// Retrieves a HashMap of wallets known to the LocalBroker. The name of the Wallet is used as key
pub async fn wallets_get_all() -> Result<HashMap<String, LocalWalletStorageV0>, NgError> {
let broker = match LOCAL_BROKER.get() { let broker = match LOCAL_BROKER.get() {
Some(Err(e)) => { Some(Err(e)) => {
log_err!("LocalBrokerNotInitialized: {}", e); log_err!("LocalBrokerNotInitialized: {}", e);
@ -247,14 +270,32 @@ pub async fn get_all_wallets() -> Result<HashMap<String, LocalWalletStorageV0>,
Ok(broker.wallets.clone()) Ok(broker.wallets.clone())
} }
/// Creates a new Wallet for the user. Each user should create only one Wallet.
///
/// See [CreateWalletV0] for a list of parameters.
///
/// Wallets are transferable to to other devices (see [wallet_get_file] and [wallet_import])
pub async fn wallet_create_v0(params: CreateWalletV0) -> Result<CreateWalletResultV0, NgError> { pub async fn wallet_create_v0(params: CreateWalletV0) -> Result<CreateWalletResultV0, NgError> {
{
// entering sub-block to release the lock asap
let broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.read().await,
};
if params.local_save && broker.config.is_in_memory() {
return Err(NgError::CannotSaveWhenInMemoryConfig);
}
}
let res = create_wallet_v0(params)?; let res = create_wallet_v0(params)?;
let lws: LocalWalletStorageV0 = (&res).into(); let lws: LocalWalletStorageV0 = (&res).into();
wallet_add(lws).await?; wallet_add(lws).await?;
Ok(res) Ok(res)
} }
pub async fn reload_wallets() -> Result<(), NgError> { #[doc(hidden)]
/// Only used by JS SDK when the localStorage changes and brings out of sync for the Rust side copy of the wallets
pub async fn wallets_reload() -> Result<(), NgError> {
let mut broker = match LOCAL_BROKER.get() { let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.write().await, Some(Ok(broker)) => broker.write().await,
@ -274,6 +315,10 @@ pub async fn reload_wallets() -> Result<(), NgError> {
Ok(()) Ok(())
} }
#[doc(hidden)]
/// This should not be used by programmers. Only here because the JS SDK needs it.
///
/// It will throw and error if you use it.
pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> { pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> {
let mut broker = match LOCAL_BROKER.get() { let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
@ -319,6 +364,11 @@ pub async fn wallet_add(lws: LocalWalletStorageV0) -> Result<(), NgError> {
Ok(()) Ok(())
} }
/// Reads a binary Wallet File and decodes it to a Wallet object.
///
/// This object can be used to import the wallet into a new Device
/// with the help of the function [wallet_open_with_pazzle_words]
/// followed by [wallet_import]
pub async fn wallet_read_file(file: Vec<u8>) -> Result<Wallet, NgError> { pub async fn wallet_read_file(file: Vec<u8>) -> Result<Wallet, NgError> {
let ngf: NgFile = file.try_into()?; let ngf: NgFile = file.try_into()?;
if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf { if let NgFile::V0(NgFileV0::Wallet(wallet)) = ngf {
@ -338,7 +388,8 @@ pub async fn wallet_read_file(file: Vec<u8>) -> Result<Wallet, NgError> {
} }
} }
pub async fn wallet_download_file(wallet_name: &String) -> Result<Vec<u8>, NgError> { /// Retrieves the binary content of a Wallet File for the Wallet identified by its name
pub async fn wallet_get_file(wallet_name: &String) -> Result<Vec<u8>, NgError> {
let broker = match LOCAL_BROKER.get() { let broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.read().await, Some(Ok(broker)) => broker.read().await,
@ -350,8 +401,11 @@ pub async fn wallet_download_file(wallet_name: &String) -> Result<Vec<u8>, NgErr
} }
} }
#[doc(hidden)]
/// This is a bit hard to use as the pazzle words are encoded in unsigned bytes.
/// prefer the function wallet_open_with_pazzle_words
pub fn wallet_open_with_pazzle( pub fn wallet_open_with_pazzle(
wallet: Wallet, wallet: &Wallet,
pazzle: Vec<u8>, pazzle: Vec<u8>,
pin: [u8; 4], pin: [u8; 4],
) -> Result<SensitiveWallet, NgError> { ) -> Result<SensitiveWallet, NgError> {
@ -360,6 +414,25 @@ pub fn wallet_open_with_pazzle(
Ok(opened_wallet) Ok(opened_wallet)
} }
/// Opens a wallet by providing an ordered list of words, and the pin.
///
/// If you are opening a wallet that is already known to the LocalBroker, you must then call [wallet_was_opened].
/// Otherwise, if you are importing, then you must call [wallet_import].
///
/// For a list of words, see [list_all_words](crate::wallet::emojis::list_all_words)
pub fn wallet_open_with_pazzle_words(
wallet: &Wallet,
pazzle_words: &Vec<String>,
pin: [u8; 4],
) -> Result<SensitiveWallet, NgError> {
wallet_open_with_pazzle(wallet, encode_pazzle(pazzle_words)?, pin)
}
/// Imports a wallet into the LocalBroker so the user can then access its content.
///
/// the wallet should have been previous opened with [wallet_open_with_pazzle_words].
/// Once import is done, the wallet is already marked as opened, and the user can start a new session right away.
/// There is no need to call wallet_was_opened.
pub async fn wallet_import( pub async fn wallet_import(
encrypted_wallet: Wallet, encrypted_wallet: Wallet,
mut opened_wallet: SensitiveWallet, mut opened_wallet: SensitiveWallet,
@ -374,7 +447,7 @@ pub async fn wallet_import(
let wallet_name = encrypted_wallet.name(); let wallet_name = encrypted_wallet.name();
if broker.wallets.get(&wallet_name).is_some() { if broker.wallets.get(&wallet_name).is_some() {
return Err(NgError::WalletAlreadyOpened); return Err(NgError::WalletAlreadyAdded);
} }
} }
@ -385,6 +458,7 @@ pub async fn wallet_import(
wallet_was_opened(opened_wallet).await wallet_was_opened(opened_wallet).await
} }
/// Must be called after [wallet_open_with_pazzle_words] if you are not importing
pub async fn wallet_was_opened(mut wallet: SensitiveWallet) -> Result<ClientV0, NgError> { pub async fn wallet_was_opened(mut wallet: SensitiveWallet) -> Result<ClientV0, NgError> {
let mut broker = match LOCAL_BROKER.get() { let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
@ -411,6 +485,11 @@ pub async fn wallet_was_opened(mut wallet: SensitiveWallet) -> Result<ClientV0,
Ok(client) Ok(client)
} }
/// Starts a session with the LocalBroker. The type of verifier is selected at this moment.
///
/// The session is valid even if there is no internet. The local data will be used in this case.
/// The returned value is not really useful. Might be removed
//TODO: remove return value?
pub async fn session_start(config: SessionConfig) -> Result<SessionPeerStorageV0, NgError> { pub async fn session_start(config: SessionConfig) -> Result<SessionPeerStorageV0, NgError> {
let mut broker = match LOCAL_BROKER.get() { let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
@ -489,7 +568,7 @@ pub async fn session_start(config: SessionConfig) -> Result<SessionPeerStorageV0
} }
None => { None => {
// the user was not found in the SWS. we need to create a SPS, add it, encrypt and serialize the new SWS, // the user was not found in the SWS. we need to create a SPS, add it, encrypt and serialize the new SWS,
// add the SPS to broker.sessions, and return the newly created SPS and the new SWS encryped serialization // add the SPS to broker.sessions, and return the newly created SPS and the new SWS encrypted serialization
let sps = SessionPeerStorageV0::new(user_id); let sps = SessionPeerStorageV0::new(user_id);
sws.users.insert(user_id.to_string(), sps.clone()); sws.users.insert(user_id.to_string(), sps.clone());
let encrypted = sws.enc_session(&wallet_id)?; let encrypted = sws.enc_session(&wallet_id)?;
@ -551,10 +630,58 @@ fn get_unix_time() -> f64 {
.as_millis() as f64 .as_millis() as f64
} }
/// Attempts a TCP connection to the Server Broker of the User.
///
/// The configuration about which Server to contact is stored in the Wallet.
/// The LocalBroker will be in charge of maintaining this connection alive,
/// cycling through optional alternative servers to contact in case of failure,
/// and will notify the user if connection is lost permanently.
/// Result is a list of (user_id, server_id, server_ip, error, since_date) /// Result is a list of (user_id, server_id, server_ip, error, since_date)
/// If error is None, it means the connection is successful
///
/// Once the connection is established, the user can sync data, open documents, etc.. with the Verifier API
///
/// In a future version, it will be possible to be connected to several brokers at the same time
/// (for different users/identities opened concurrently on the same Client)
// TODO: improve this return value
// TODO: give public access to the API for subscribing to disconnections
pub async fn user_connect( pub async fn user_connect(
user_id: &UserId,
) -> Result<Vec<(String, String, String, Option<String>, f64)>, NgError> {
let os_info = get_os_info();
let info = json!({
"platform": {
"type": "program",
"arch": os_info.get("rust").unwrap().get("arch"),
"debug": os_info.get("rust").unwrap().get("debug"),
"target": os_info.get("rust").unwrap().get("target"),
"arch_uname": os_info.get("uname").unwrap().get("arch"),
"bitness": os_info.get("uname").unwrap().get("bitness"),
"codename": os_info.get("uname").unwrap().get("codename"),
"edition": os_info.get("uname").unwrap().get("edition"),
},
"os": {
"name": os_info.get("uname").unwrap().get("os_name"),
"family": os_info.get("rust").unwrap().get("family"),
"version": os_info.get("uname").unwrap().get("version"),
"name_rust": os_info.get("rust").unwrap().get("os_name"),
}
});
let client_info = ClientInfo::new(
ClientType::NativeService,
info.to_string(),
env!("CARGO_PKG_VERSION").to_string(),
);
user_connect_with_device_info(client_info, &user_id, None).await
}
/// Used internally by JS SDK and Tauri Apps. Do not use "as is". See [user_connect] instead.
#[doc(hidden)]
pub async fn user_connect_with_device_info(
info: ClientInfo, info: ClientInfo,
user_id: UserId, user_id: &UserId,
location: Option<String>, location: Option<String>,
) -> Result<Vec<(String, String, String, Option<String>, f64)>, NgError> { ) -> Result<Vec<(String, String, String, Option<String>, f64)>, NgError> {
let local_broker = match LOCAL_BROKER.get() { let local_broker = match LOCAL_BROKER.get() {
@ -564,7 +691,7 @@ pub async fn user_connect(
let session = local_broker let session = local_broker
.opened_sessions .opened_sessions
.get(&user_id) .get(user_id)
.ok_or(NgError::SessionNotFound)?; .ok_or(NgError::SessionNotFound)?;
let wallet = local_broker.get_wallet_for_session(&session.config)?; let wallet = local_broker.get_wallet_for_session(&session.config)?;
@ -574,7 +701,6 @@ pub async fn user_connect(
match wallet { match wallet {
SensitiveWallet::V0(wallet) => { SensitiveWallet::V0(wallet) => {
let client = wallet.client.as_ref().unwrap(); let client = wallet.client.as_ref().unwrap();
let client_id = client.id;
let client_priv = &client.sensitive_client_storage.priv_key; let client_priv = &client.sensitive_client_storage.priv_key;
let client_name = &client.name; let client_name = &client.name;
let auto_open = &client.auto_open; let auto_open = &client.auto_open;
@ -688,13 +814,14 @@ pub async fn user_connect(
Ok(result) Ok(result)
} }
pub async fn session_stop(user_id: UserId) -> Result<(), NgError> { /// Stops the session, that can be resumed later on. All the local data is flushed from RAM.
pub async fn session_stop(user_id: &UserId) -> Result<(), NgError> {
let mut broker = match LOCAL_BROKER.get() { let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.write().await, Some(Ok(broker)) => broker.write().await,
}; };
if broker.opened_sessions.remove(&user_id).is_some() { if broker.opened_sessions.remove(user_id).is_some() {
// TODO: change the logic here once it will be possible to have several users connected at the same time // TODO: change the logic here once it will be possible to have several users connected at the same time
Broker::close_all_connections().await; Broker::close_all_connections().await;
} }
@ -702,13 +829,14 @@ pub async fn session_stop(user_id: UserId) -> Result<(), NgError> {
Ok(()) Ok(())
} }
pub async fn user_disconnect(user_id: UserId) -> Result<(), NgError> { /// Disconnects the user from the Server Broker(s), but keep all the local data opened and ready.
pub async fn user_disconnect(user_id: &UserId) -> Result<(), NgError> {
let broker = match LOCAL_BROKER.get() { let broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.read().await, Some(Ok(broker)) => broker.read().await,
}; };
if broker.opened_sessions.get(&user_id).is_some() { if broker.opened_sessions.get(user_id).is_some() {
// TODO: change the logic here once it will be possible to have several users connected at the same time // TODO: change the logic here once it will be possible to have several users connected at the same time
Broker::close_all_connections().await; Broker::close_all_connections().await;
} }
@ -716,13 +844,14 @@ pub async fn user_disconnect(user_id: UserId) -> Result<(), NgError> {
Ok(()) Ok(())
} }
pub async fn wallet_close(wallet_name: String) -> Result<(), NgError> { /// Closes a wallet, which means that the pazzle will have to be entered again if the user wants to use it
pub async fn wallet_close(wallet_name: &String) -> Result<(), NgError> {
let mut broker = match LOCAL_BROKER.get() { let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),
Some(Ok(broker)) => broker.write().await, Some(Ok(broker)) => broker.write().await,
}; };
match broker.opened_wallets.remove(&wallet_name) { match broker.opened_wallets.remove(wallet_name) {
Some(mut wallet) => { Some(mut wallet) => {
for user in wallet.sites() { for user in wallet.sites() {
let key: PubKey = (user.as_str()).try_into().unwrap(); let key: PubKey = (user.as_str()).try_into().unwrap();
@ -738,6 +867,7 @@ pub async fn wallet_close(wallet_name: String) -> Result<(), NgError> {
Ok(()) Ok(())
} }
/// (not implemented yet)
pub async fn wallet_remove(wallet_name: String) -> Result<(), NgError> { pub async fn wallet_remove(wallet_name: String) -> Result<(), NgError> {
let mut broker = match LOCAL_BROKER.get() { let mut broker = match LOCAL_BROKER.get() {
None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized), None | Some(Err(_)) => return Err(NgError::LocalBrokerNotInitialized),

@ -17,6 +17,7 @@ use ng_repo::types::*;
use ng_wallet::types::*; use ng_wallet::types::*;
use ng_wallet::*; use ng_wallet::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{read, write, File, OpenOptions}; use std::fs::{read, write, File, OpenOptions};
use std::io::Write; use std::io::Write;
@ -86,14 +87,14 @@ async fn wallet_open_with_pazzle(
app: tauri::AppHandle, app: tauri::AppHandle,
) -> Result<SensitiveWallet, String> { ) -> Result<SensitiveWallet, String> {
//log_debug!("wallet_open_with_pazzle from rust {:?}", pazzle); //log_debug!("wallet_open_with_pazzle from rust {:?}", pazzle);
let wallet = nextgraph::local_broker::wallet_open_with_pazzle(wallet, pazzle, pin) let wallet = nextgraph::local_broker::wallet_open_with_pazzle(&wallet, pazzle, pin)
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
Ok(wallet) Ok(wallet)
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn wallet_download_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> { async fn wallet_get_file(wallet_name: String, app: tauri::AppHandle) -> Result<(), String> {
let ser = nextgraph::local_broker::wallet_download_file(&wallet_name) let ser = nextgraph::local_broker::wallet_get_file(&wallet_name)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
@ -135,34 +136,6 @@ async fn wallet_create(
Ok(cwr) Ok(cwr)
} }
// // TODO: use https://lib.rs/crates/keyring instead of AppLocalData
// async fn save_wallet_locally(
// res: &CreateWalletResultV0,
// app: tauri::AppHandle,
// ) -> Result<SessionWalletStorageV0, ()> {
// let path = app
// .path()
// .resolve("wallets", BaseDirectory::AppLocalData)
// .map_err(|_| ())?;
// let mut wallets: HashMap<String, LocalWalletStorageV0> =
// get_all_wallets().unwrap_or(HashMap::new());
// // check that the wallet is not already present in localStorage
// if wallets.get(&res.wallet_name).is_none() {
// let lws: LocalWalletStorageV0 = res.into();
// wallets.insert(res.wallet_name.clone(), lws);
// let lws_ser = LocalWalletStorage::v0_to_vec(&wallets);
// let r = write(path.clone(), &lws_ser);
// if r.is_err() {
// log_debug!("write {:?} {}", path, r.unwrap_err());
// return Err(());
// }
// let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user, app)?;
// Ok(sws)
// } else {
// Err(())
// }
// }
fn take_some_peer_last_seq_numbers( fn take_some_peer_last_seq_numbers(
peer_id: PubKey, peer_id: PubKey,
qty: u16, qty: u16,
@ -233,41 +206,6 @@ async fn wallet_import(
nextgraph::local_broker::wallet_import(encrypted_wallet, opened_wallet, in_memory) nextgraph::local_broker::wallet_import(encrypted_wallet, opened_wallet, in_memory)
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
// let path = app
// .path()
// .resolve("wallets", BaseDirectory::AppLocalData)
// .map_err(|_| "wallet directory error".to_string())?;
// let mut wallets: HashMap<String, LocalWalletStorageV0> =
// get_all_wallets().unwrap_or(HashMap::new());
// // check that the wallet is not already present in localStorage
// let SensitiveWallet::V0(mut opened_wallet_v0) = opened_wallet;
// let wallet_name = opened_wallet_v0.wallet_id.clone();
// if wallets.get(&wallet_name).is_none() {
// let session = save_new_session(
// &wallet_name,
// opened_wallet_v0.wallet_privkey.to_pub(),
// opened_wallet_v0.personal_site,
// app,
// )
// .map_err(|_| "Cannot create new session".to_string())?;
// let lws = opened_wallet_v0
// .import(previous_wallet)
// .map_err(|e| e.to_string())?;
// //let lws = LocalWalletStorageV0::new(wallet, &client);
// wallets.insert(wallet_name, lws);
// let lws_ser = LocalWalletStorage::v0_to_vec(&wallets);
// let r = write(path.clone(), &lws_ser);
// if r.is_err() {
// log_debug!("write {:?} {}", path, r.unwrap_err());
// Err("Write error".to_string())
// } else {
// Ok(client)
// }
// } else {
// Err("Already present on this device".to_string())
// }
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
@ -281,8 +219,8 @@ async fn get_wallets(
.unwrap(); .unwrap();
init_local_broker(Box::new(move || LocalBrokerConfig::BasePath(path.clone()))).await; init_local_broker(Box::new(move || LocalBrokerConfig::BasePath(path.clone()))).await;
let res = get_all_wallets().await.map_err(|e| { let res = wallets_get_all().await.map_err(|e| {
log_err!("get_all_wallets error {}", e.to_string()); log_err!("wallets_get_all error {}", e.to_string());
}); });
if res.is_ok() { if res.is_ok() {
return Ok(Some(res.unwrap())); return Ok(Some(res.unwrap()));
@ -290,32 +228,6 @@ async fn get_wallets(
Ok(None) Ok(None)
} }
// fn save_new_session(
// wallet_name: &String,
// wallet_id: PubKey,
// user: PubKey,
// app: tauri::AppHandle,
// ) -> Result<SessionWalletStorageV0, ()> {
// let mut path = app
// .path()
// .resolve("sessions", BaseDirectory::AppLocalData)
// .map_err(|_| ())?;
// let session_v0 = create_new_session(wallet_id, user);
// if session_v0.is_err() {
// log_debug!("create_new_session failed {}", session_v0.unwrap_err());
// return Err(());
// }
// let sws = session_v0.unwrap();
// std::fs::create_dir_all(path.clone()).unwrap();
// path.push(wallet_name);
// let res = write(path.clone(), &sws.1);
// if res.is_err() {
// log_debug!("write {:?} {}", path, res.unwrap_err());
// return Err(());
// }
// Ok(sws.0)
// }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn session_start( async fn session_start(
wallet_name: String, wallet_name: String,
@ -465,21 +377,21 @@ async fn doc_get_file_from_store_with_object_ref(
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn session_stop(user_id: UserId) -> Result<(), String> { async fn session_stop(user_id: UserId) -> Result<(), String> {
nextgraph::local_broker::session_stop(user_id) nextgraph::local_broker::session_stop(&user_id)
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn user_disconnect(user_id: UserId) -> Result<(), String> { async fn user_disconnect(user_id: UserId) -> Result<(), String> {
nextgraph::local_broker::user_disconnect(user_id) nextgraph::local_broker::user_disconnect(&user_id)
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
#[tauri::command(rename_all = "snake_case")] #[tauri::command(rename_all = "snake_case")]
async fn wallet_close(wallet_name: String) -> Result<(), String> { async fn wallet_close(wallet_name: String) -> Result<(), String> {
nextgraph::local_broker::wallet_close(wallet_name) nextgraph::local_broker::wallet_close(&wallet_name)
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
@ -500,7 +412,7 @@ async fn user_connect(
) -> Result<HashMap<String, ConnectionInfo>, String> { ) -> Result<HashMap<String, ConnectionInfo>, String> {
let mut opened_connections: HashMap<String, ConnectionInfo> = HashMap::new(); let mut opened_connections: HashMap<String, ConnectionInfo> = HashMap::new();
let results = nextgraph::local_broker::user_connect(info, user_id, None) let results = nextgraph::local_broker::user_connect_with_device_info(info, &user_id, None)
.await .await
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
@ -518,11 +430,14 @@ async fn user_connect(
); );
} }
//BROKER.read().await.print_status();
Ok(opened_connections) Ok(opened_connections)
} }
#[tauri::command(rename_all = "snake_case")]
fn client_info_rust() -> Result<Value, String> {
Ok(ng_repo::os_info::get_os_info())
}
#[derive(Default)] #[derive(Default)]
pub struct AppBuilder { pub struct AppBuilder {
setup: Option<SetupHook>, setup: Option<SetupHook>,
@ -577,7 +492,7 @@ impl AppBuilder {
wallet_was_opened, wallet_was_opened,
wallet_create, wallet_create,
wallet_read_file, wallet_read_file,
wallet_download_file, wallet_get_file,
wallet_import, wallet_import,
wallet_close, wallet_close,
encode_create_account, encode_create_account,
@ -589,6 +504,7 @@ impl AppBuilder {
disconnections_subscribe, disconnections_subscribe,
user_connect, user_connect,
user_disconnect, user_disconnect,
client_info_rust,
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

@ -108,7 +108,7 @@
window.addEventListener("storage", async (event) => { window.addEventListener("storage", async (event) => {
if (event.storageArea != localStorage) return; if (event.storageArea != localStorage) return;
if (event.key === "ng_wallets") { if (event.key === "ng_wallets") {
await ng.reload_wallets(); await ng.wallets_reload();
wallets.set(await ng.get_wallets()); wallets.set(await ng.get_wallets());
} }
}); });
@ -158,12 +158,12 @@
break; break;
case "opened": case "opened":
if (!$opened_wallets[event.data.wallet.id]) { if (!$opened_wallets[event.data.wallet.id]) {
console.log( // console.log(
"ADDING TO OPENED", // "ADDING TO OPENED",
event.data.wallet.id, // event.data.wallet.id,
JSON.stringify($opened_wallets), // JSON.stringify($opened_wallets),
event.data.wallet.wallet // event.data.wallet.wallet
); // );
try { try {
await ng.wallet_was_opened(event.data.wallet.wallet); await ng.wallet_was_opened(event.data.wallet.wallet);
} catch (e) { } catch (e) {
@ -176,7 +176,7 @@
} }
break; break;
case "new_in_mem": case "new_in_mem":
console.log("GOT new_in_mem", event.data); //console.log("GOT new_in_mem", event.data);
if (event.data.lws) { if (event.data.lws) {
if (!$wallets[event.data.name]) { if (!$wallets[event.data.name]) {
await ng.add_in_memory_wallet(event.data.lws); await ng.add_in_memory_wallet(event.data.lws);

@ -20,7 +20,7 @@ const mapping = {
"wallet_was_opened": ["opened_wallet"], "wallet_was_opened": ["opened_wallet"],
"wallet_create": ["params"], "wallet_create": ["params"],
"wallet_read_file": ["file"], "wallet_read_file": ["file"],
"wallet_download_file": ["wallet_name"], "wallet_get_file": ["wallet_name"],
"wallet_import": ["encrypted_wallet","opened_wallet","in_memory"], "wallet_import": ["encrypted_wallet","opened_wallet","in_memory"],
"wallet_close": ["wallet_name"], "wallet_close": ["wallet_name"],
"encode_create_account": ["payload"], "encode_create_account": ["payload"],
@ -62,7 +62,8 @@ const handler = {
} else { } else {
let tauri = await import("@tauri-apps/api/tauri"); let tauri = await import("@tauri-apps/api/tauri");
if (path[0] === "client_info") { if (path[0] === "client_info") {
let from_rust = await tauri.invoke("client_info_rust",{});
let tauri_platform = import.meta.env.TAURI_PLATFORM; let tauri_platform = import.meta.env.TAURI_PLATFORM;
let client_type; let client_type;
switch (tauri_platform) { switch (tauri_platform) {
@ -73,20 +74,25 @@ const handler = {
case 'ios': client_type = "NativeIos";break; case 'ios': client_type = "NativeIos";break;
} }
let info = Bowser.parse(window.navigator.userAgent); let info = Bowser.parse(window.navigator.userAgent);
info.os.type = import.meta.env.TAURI_PLATFORM_TYPE;
info.os.family = import.meta.env.TAURI_FAMILY;
info.os.version_tauri = import.meta.env.TAURI_PLATFORM_VERSION;
info.os.version_uname = from_rust.uname.version;
info.os.name_rust = from_rust.rust.os_name;
info.os.name_uname = from_rust.uname.os_name;
info.platform.arch = import.meta.env.TAURI_ARCH; info.platform.arch = import.meta.env.TAURI_ARCH;
info.platform.tauri = { info.platform.debug = import.meta.env.TAURI_DEBUG;
family: import.meta.env.TAURI_FAMILY, info.platform.target = import.meta.env.TAURI_TARGET_TRIPLE;
os_version: import.meta.env.TAURI_PLATFORM_VERSION, info.platform.arch_uname = from_rust.uname.arch;
type: import.meta.env.TAURI_PLATFORM_TYPE, info.platform.bitness = from_rust.uname.bitness;
debug: import.meta.env.TAURI_DEBUG, info.platform.codename = from_rust.uname.codename || undefined;
target: import.meta.env.TAURI_TARGET_TRIPLE info.platform.edition = from_rust.uname.edition || undefined;
};
info.browser.ua = window.navigator.userAgent; info.browser.ua = window.navigator.userAgent;
let res = { let res = {
// TODO: install timestamp // TODO: install timestamp
V0 : { client_type, details: JSON.stringify(info), version, timestamp_install:0, timestamp_updated:0 } V0 : { client_type, details: JSON.stringify(info), version, timestamp_install:0, timestamp_updated:0 }
}; };
//console.log(res); console.log(info,res);
return res; return res;
} else if (path[0] === "disconnections_subscribe") { } else if (path[0] === "disconnections_subscribe") {
let { getCurrent } = await import("@tauri-apps/plugin-window"); let { getCurrent } = await import("@tauri-apps/plugin-window");

@ -95,7 +95,7 @@
async function download_wallet() { async function download_wallet() {
try { try {
downloading = true; downloading = true;
let file = await ng.wallet_download_file($active_wallet.id); let file = await ng.wallet_get_file($active_wallet.id);
// @ts-ignore // @ts-ignore
wallet_file_ready = "wallet-" + $active_wallet.id + ".ngw"; wallet_file_ready = "wallet-" + $active_wallet.id + ".ngw";
if (!tauri_platform) { if (!tauri_platform) {

@ -156,7 +156,7 @@
invitation = await ng.decode_invitation(param.get("i")); invitation = await ng.decode_invitation(param.get("i"));
window.location.replace(window.location.href.split("?")[0]); window.location.replace(window.location.href.split("?")[0]);
} else if (param.get("i")) { } else if (param.get("i")) {
invitation = await ng.get_local_bootstrap( invitation = await ng.get_local_bootstrap_with_public(
location.href, location.href,
param.get("i") param.get("i")
); );

@ -33,7 +33,7 @@ let face = [
{ {
hexcode: "1f60d", hexcode: "1f60d",
shortcode: "smiling_face_with_heart_eyes", shortcode: "smiling_face_with_heart_eyes",
code: "two_hearts", code: "with_two_hearts",
}, },
{ {
hexcode: "1f618", hexcode: "1f618",
@ -44,12 +44,12 @@ let face = [
{ {
hexcode: "1f61d", hexcode: "1f61d",
shortcode: "squinting_face_with_tongue", shortcode: "squinting_face_with_tongue",
code: "tongue", code: "with_tongue",
}, },
{ {
hexcode: "1f917", hexcode: "1f917",
shortcode: "hugging_face", shortcode: "hugging_face",
code: "two_hands", code: "with_two_hands",
}, },
{ {
hexcode: "1f92d", hexcode: "1f92d",
@ -100,7 +100,7 @@ let face = [
{ {
hexcode: "1f912", hexcode: "1f912",
shortcode: "face_with_thermometer", shortcode: "face_with_thermometer",
code: "thermometer", code: "fever",
}, },
{ {
hexcode: "1f915", hexcode: "1f915",
@ -137,7 +137,7 @@ let face = [
{ {
hexcode: "1f92f", hexcode: "1f92f",
shortcode: "exploding_head", shortcode: "exploding_head",
code: "explosion", code: "exploding",
}, },
{ {
@ -266,7 +266,7 @@ let face = [
{ {
hexcode: "2764", hexcode: "2764",
shortcode: "red_heart", shortcode: "red_heart",
code: "one_heart", code: "red_heart",
}, },
{ {
hexcode: "1f495", hexcode: "1f495",

@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -30,8 +30,6 @@ use async_tungstenite::tungstenite::http::{
use async_tungstenite::tungstenite::protocol::Message; use async_tungstenite::tungstenite::protocol::Message;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use once_cell::sync::Lazy;
use once_cell::sync::OnceCell;
use ng_client_ws::remote_ws::ConnectionWebSocket; use ng_client_ws::remote_ws::ConnectionWebSocket;
use ng_net::broker::*; use ng_net::broker::*;
use ng_net::connection::IAccept; use ng_net::connection::IAccept;
@ -44,6 +42,8 @@ use ng_repo::log::*;
use ng_repo::types::SymKey; use ng_repo::types::SymKey;
use ng_repo::types::{PrivKey, PubKey}; use ng_repo::types::{PrivKey, PubKey};
use ng_repo::utils::generate_keypair; use ng_repo::utils::generate_keypair;
use once_cell::sync::Lazy;
use once_cell::sync::OnceCell;
use rust_embed::RustEmbed; use rust_embed::RustEmbed;
use serde_json::json; use serde_json::json;
use std::collections::HashMap; use std::collections::HashMap;
@ -733,6 +733,7 @@ pub async fn run_server_v0(
for server_type in server_types { for server_type in server_types {
servers.push(BrokerServerV0 { servers.push(BrokerServerV0 {
peer_id: common_peer_id.unwrap_or(peer_id), peer_id: common_peer_id.unwrap_or(peer_id),
can_verify: false,
server_type, server_type,
}) })
} }

@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -9,6 +9,8 @@
* according to those terms. * according to those terms.
*/ */
//! Actor handles messages in the Protocol. common types are here
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use async_std::sync::Mutex; use async_std::sync::Mutex;
use futures::{channel::mpsc, SinkExt}; use futures::{channel::mpsc, SinkExt};

@ -1,3 +1,5 @@
//! List of actors, each one for a specific Protocol message
pub mod noise; pub mod noise;
pub use noise::*; pub use noise::*;

@ -9,6 +9,8 @@
* according to those terms. * according to those terms.
*/ */
//! Broker singleton present in every instance of NextGraph (Client, Server, Core node)
use crate::connection::*; use crate::connection::*;
use crate::errors::*; use crate::errors::*;
use crate::server_storage::ServerStorage; use crate::server_storage::ServerStorage;

@ -9,6 +9,8 @@
* according to those terms. * according to those terms.
*/ */
//! Finite State Machine of the connection/protocol/Noise channel
//static NOISE_CONFIG: &'static str = "Noise_XK_25519_ChaChaPoly_BLAKE2b"; //static NOISE_CONFIG: &'static str = "Noise_XK_25519_ChaChaPoly_BLAKE2b";
use std::any::TypeId; use std::any::TypeId;
@ -28,11 +30,11 @@ use async_std::stream::StreamExt;
use async_std::sync::Mutex; use async_std::sync::Mutex;
use either::Either; use either::Either;
use futures::{channel::mpsc, select, FutureExt, SinkExt}; use futures::{channel::mpsc, select, FutureExt, SinkExt};
use noise_protocol::{patterns::noise_xk, CipherState, HandshakeState};
use noise_rust_crypto::*;
use ng_repo::log::*; use ng_repo::log::*;
use ng_repo::types::{DirectPeerId, PrivKey, PubKey, X25519PrivKey}; use ng_repo::types::{DirectPeerId, PrivKey, PubKey, X25519PrivKey};
use ng_repo::utils::{sign, verify}; use ng_repo::utils::{sign, verify};
use noise_protocol::{patterns::noise_xk, CipherState, HandshakeState};
use noise_rust_crypto::*;
use serde_bare::from_slice; use serde_bare::from_slice;
use unique_id::sequence::SequenceGenerator; use unique_id::sequence::SequenceGenerator;
use unique_id::Generator; use unique_id::Generator;

@ -29,18 +29,25 @@ pub mod actors;
pub mod utils; pub mod utils;
#[doc(hidden)]
pub mod tests; pub mod tests;
#[doc(hidden)]
pub static NG_BOOTSTRAP_LOCAL_PATH: &str = "/.ng_bootstrap"; pub static NG_BOOTSTRAP_LOCAL_PATH: &str = "/.ng_bootstrap";
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
#[doc(hidden)]
pub static WS_PORT: u16 = 14400; pub static WS_PORT: u16 = 14400;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
#[doc(hidden)]
pub static WS_PORT: u16 = 80; pub static WS_PORT: u16 = 80;
#[doc(hidden)]
pub static WS_PORT_ALTERNATE: [u16; 4] = [14400, 28800, 43200, 57600]; pub static WS_PORT_ALTERNATE: [u16; 4] = [14400, 28800, 43200, 57600];
#[doc(hidden)]
pub static WS_PORT_ALTERNATE_SUPERUSER: u16 = 144; pub static WS_PORT_ALTERNATE_SUPERUSER: u16 = 144;
#[doc(hidden)]
pub static WS_PORT_REVERSE_PROXY: u16 = 1440; pub static WS_PORT_REVERSE_PROXY: u16 = 1440;

@ -9,6 +9,8 @@
* according to those terms. * according to those terms.
*/ */
//! Trait for ServerStorage
use crate::{ use crate::{
errors::{ProtocolError, ServerError}, errors::{ProtocolError, ServerError},
types::*, types::*,

@ -1 +1,2 @@
#[doc(hidden)]
pub mod file; pub mod file;

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! P2P network protocol types //! NextGraph network protocol types
//! //!
//! Corresponds to the BARE schema //! Corresponds to the BARE schema
@ -15,6 +15,7 @@ use crate::utils::{
get_domain_without_port_443, is_ipv4_private, is_ipv6_private, is_private_ip, is_public_ip, get_domain_without_port_443, is_ipv4_private, is_ipv6_private, is_private_ip, is_public_ip,
is_public_ipv4, is_public_ipv6, is_public_ipv4, is_public_ipv6,
}; };
use crate::WS_PORT_ALTERNATE;
use crate::{actor::EActor, actors::*, errors::ProtocolError}; use crate::{actor::EActor, actors::*, errors::ProtocolError};
use core::fmt; use core::fmt;
use ng_repo::errors::NgError; use ng_repo::errors::NgError;
@ -109,7 +110,9 @@ impl From<&SocketAddr> for BindAddress {
} }
} }
/// BROKER common types //
// BROKER common types
//
/// Core Broker connection details Version 0 /// Core Broker connection details Version 0
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
@ -144,16 +147,33 @@ pub struct BrokerServerV0 {
/// Network addresses /// Network addresses
pub server_type: BrokerServerTypeV0, pub server_type: BrokerServerTypeV0,
/// is this server capable of running a verifier
pub can_verify: bool,
/// peerId of the server /// peerId of the server
pub peer_id: PubKey, pub peer_id: PubKey,
} }
impl BrokerServerV0 {
pub fn new_localhost(peer_id: PubKey) -> Self {
BrokerServerV0 {
server_type: BrokerServerTypeV0::Localhost(WS_PORT_ALTERNATE[0]),
can_verify: false,
peer_id,
}
}
}
#[doc(hidden)]
pub const APP_ACCOUNT_REGISTERED_SUFFIX: &str = "/#/user/registered"; pub const APP_ACCOUNT_REGISTERED_SUFFIX: &str = "/#/user/registered";
#[doc(hidden)]
pub const NG_ONE_URL: &str = "https://nextgraph.one"; pub const NG_ONE_URL: &str = "https://nextgraph.one";
#[doc(hidden)]
pub const APP_NG_ONE_URL: &str = "https://app.nextgraph.one"; pub const APP_NG_ONE_URL: &str = "https://app.nextgraph.one";
#[doc(hidden)]
pub const APP_NG_ONE_WS_URL: &str = "wss://app.nextgraph.one"; pub const APP_NG_ONE_WS_URL: &str = "wss://app.nextgraph.one";
#[allow(dead_code)] #[allow(dead_code)]
@ -161,16 +181,18 @@ fn api_dyn_peer_url(peer_id: &PubKey) -> String {
format!("https://nextgraph.one/api/v1/dynpeer/{}", peer_id) format!("https://nextgraph.one/api/v1/dynpeer/{}", peer_id)
} }
#[doc(hidden)]
pub const LOCAL_HOSTS: [&str; 3] = ["localhost", "127.0.0.1", "[::1]"]; pub const LOCAL_HOSTS: [&str; 3] = ["localhost", "127.0.0.1", "[::1]"];
fn local_ws_url(port: &u16) -> String { fn local_ws_url(port: &u16) -> String {
format!("ws://localhost:{}", if *port == 0 { 80 } else { *port }) format!("ws://localhost:{}", if *port == 0 { 80 } else { *port })
} }
#[doc(hidden)]
pub fn local_http_url(port: &u16) -> String { pub(crate) fn local_http_url(port: &u16) -> String {
format!("http://localhost:{}", if *port == 0 { 80 } else { *port }) format!("http://localhost:{}", if *port == 0 { 80 } else { *port })
} }
#[doc(hidden)]
pub const LOCAL_URLS: [&str; 3] = ["http://localhost", "http://127.0.0.1", "http://[::1]"]; pub const LOCAL_URLS: [&str; 3] = ["http://localhost", "http://127.0.0.1", "http://[::1]"];
use url::{Host, Url}; use url::{Host, Url};
@ -451,7 +473,12 @@ pub struct BootstrapContentV0 {
} }
impl BootstrapContentV0 { impl BootstrapContentV0 {
pub fn new() -> Self { pub fn new_localhost(peer_id: PubKey) -> Self {
BootstrapContentV0 {
servers: vec![BrokerServerV0::new_localhost(peer_id)],
}
}
pub fn new_empty() -> Self {
BootstrapContentV0 { servers: vec![] } BootstrapContentV0 { servers: vec![] }
} }
pub fn merge(&mut self, with: &BootstrapContentV0) { pub fn merge(&mut self, with: &BootstrapContentV0) {
@ -576,7 +603,7 @@ impl InvitationV0 {
} }
pub fn empty(name: Option<String>) -> Self { pub fn empty(name: Option<String>) -> Self {
InvitationV0 { InvitationV0 {
bootstrap: BootstrapContentV0::new(), bootstrap: BootstrapContentV0::new_empty(),
code: None, code: None,
name, name,
url: None, url: None,
@ -635,7 +662,7 @@ impl Invitation {
pub fn intersects(&self, invite2: Invitation) -> Invitation { pub fn intersects(&self, invite2: Invitation) -> Invitation {
let Invitation::V0(v0) = self; let Invitation::V0(v0) = self;
let mut new_invite = InvitationV0 { let mut new_invite = InvitationV0 {
bootstrap: BootstrapContentV0::new(), bootstrap: BootstrapContentV0::new_empty(),
code: v0.code.clone(), code: v0.code.clone(),
name: v0.name.clone(), name: v0.name.clone(),
url: v0.url.clone(), url: v0.url.clone(),
@ -816,6 +843,7 @@ pub struct ListenerInfo {
} }
/// AcceptForwardForV0 type /// AcceptForwardForV0 type
///
/// allow answers to connection requests originating from a client behind a reverse proxy /// allow answers to connection requests originating from a client behind a reverse proxy
/// Format of last param in the tuple is a list of comma separated hosts or CIDR subnetworks IPv4 and/or IPv6 addresses accepted as X-Forwarded-For /// Format of last param in the tuple is a list of comma separated hosts or CIDR subnetworks IPv4 and/or IPv6 addresses accepted as X-Forwarded-For
/// Empty string means all addresses are accepted /// Empty string means all addresses are accepted
@ -1406,6 +1434,7 @@ pub enum OverlayLeave {
} }
/// Content of PublisherAdvertV0 /// Content of PublisherAdvertV0
///
/// the peer is matched with the InnerOverlayMessageV0.Session -> peerId. /// the peer is matched with the InnerOverlayMessageV0.Session -> peerId.
#[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct PublisherAdvertContentV0 { pub struct PublisherAdvertContentV0 {
@ -1530,6 +1559,7 @@ pub enum BlockSearchTopic {
} }
/// Block search along a random walk in the overlay /// Block search along a random walk in the overlay
///
/// fanout is always 1 /// fanout is always 1
/// if result is none, tries another path if several paths available locally /// if result is none, tries another path if several paths available locally
/// answered with a stream BlockResult /// answered with a stream BlockResult
@ -1556,6 +1586,7 @@ pub enum BlockSearchRandom {
} }
/// Response to a BlockSearch* request /// Response to a BlockSearch* request
///
/// can be a stream /// can be a stream
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockResultV0 { pub struct BlockResultV0 {
@ -1564,6 +1595,7 @@ pub struct BlockResultV0 {
} }
/// Response to a BlockSearch* request /// Response to a BlockSearch* request
///
/// can be a stream /// can be a stream
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BlockResult { pub enum BlockResult {
@ -1613,6 +1645,7 @@ pub enum PeerStatus {
} }
/// ForwardedPeerAdvertV0 /// ForwardedPeerAdvertV0
///
/// peer_advert.forwarded_by is matched with sessionid->peerid /// peer_advert.forwarded_by is matched with sessionid->peerid
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ForwardedPeerAdvertV0 { pub struct ForwardedPeerAdvertV0 {
@ -1636,6 +1669,7 @@ pub enum ForwardedPeerAdvert {
} }
/// ForwardedPeerConflictV0 /// ForwardedPeerConflictV0
///
/// peer_advert.forwarded_by is matched with sessionid->peerid /// peer_advert.forwarded_by is matched with sessionid->peerid
/// When the forwarding broker receives the conflict (or notices it), it sends a notification /// When the forwarding broker receives the conflict (or notices it), it sends a notification
/// In order to avoid conflicts, the highest version of PeerAdvert should win, when the Forwarding Broker is different. /// In order to avoid conflicts, the highest version of PeerAdvert should win, when the Forwarding Broker is different.
@ -1803,6 +1837,7 @@ pub enum OverlayAdvert {
} }
/// CoreBrokerJoinedAdvert V0 /// CoreBrokerJoinedAdvert V0
///
/// Each broker that is already part of an overlay, when receiving the CoreBrokerJoinedAdvert, should answer with one direct message /// Each broker that is already part of an overlay, when receiving the CoreBrokerJoinedAdvert, should answer with one direct message
/// to the joining peer (found in OverlayAdvertPayloadV0.peer) for each overlay, containing an OverlayAdvertMarker containing their current sequence number. /// to the joining peer (found in OverlayAdvertPayloadV0.peer) for each overlay, containing an OverlayAdvertMarker containing their current sequence number.
/// This is sent for each path (in case multiple paths arrive to the same broker). Only the first sequence number received by joining peer is kept locally /// This is sent for each path (in case multiple paths arrive to the same broker). Only the first sequence number received by joining peer is kept locally
@ -1814,6 +1849,7 @@ pub struct CoreBrokerJoinedAdvertV0 {
} }
/// CoreBrokerLeftAdvert V0 /// CoreBrokerLeftAdvert V0
///
/// A broker has disconnected from another broker, and the routes need to be updated /// A broker has disconnected from another broker, and the routes need to be updated
/// this is not used to leave one specific overlay. see OverlayLeave message for that purpose /// this is not used to leave one specific overlay. see OverlayLeave message for that purpose
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -1871,6 +1907,7 @@ pub struct CoreAdvertV0 {
} }
/// OverlayAdvertMarker V0 /// OverlayAdvertMarker V0
///
/// when receiving a marker, the broker saves the ingress edge and the corresponding remote peer and /// when receiving a marker, the broker saves the ingress edge and the corresponding remote peer and
/// overlay that can be reached (the OverlayAdvertPayloadV0.peer and .overlay) in the CoreRoutingTable /// overlay that can be reached (the OverlayAdvertPayloadV0.peer and .overlay) in the CoreRoutingTable
/// It also saves the sessionId and seq number /// It also saves the sessionId and seq number
@ -1905,6 +1942,7 @@ pub struct CoreBlockGetV0 {
} }
/// Core Block Result V0 /// Core Block Result V0
///
/// can be a stream /// can be a stream
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoreBlockResultV0 { pub struct CoreBlockResultV0 {
@ -1980,6 +2018,7 @@ pub struct CoreDirectMessageV0 {
} }
/// CoreBrokerConnect V0 /// CoreBrokerConnect V0
///
/// replied with CoreBrokerConnectResponse /// replied with CoreBrokerConnectResponse
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoreBrokerConnectV0 { pub struct CoreBrokerConnectV0 {
@ -2017,7 +2056,8 @@ impl CoreBrokerConnect {
pub type CoreBrokerDisconnectV0 = (); pub type CoreBrokerDisconnectV0 = ();
/// Content of CoreOverlayJoin V0 /// Content of CoreOverlayJoin V0
/// // replied with an emptyResponse, and an error code if OverlayId not present on remote broker ///
/// replied with an emptyResponse, and an error code if OverlayId not present on remote broker
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum CoreOverlayJoinV0 { pub enum CoreOverlayJoinV0 {
Inner(OverlayAdvert), Inner(OverlayAdvert),
@ -2045,6 +2085,7 @@ pub enum OuterOverlayRequestContentV0 {
} }
/// OuterOverlayRequestV0 V0 /// OuterOverlayRequestV0 V0
///
/// replied with OuterOverlayResponseV0 /// replied with OuterOverlayResponseV0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OuterOverlayRequestV0 { pub struct OuterOverlayRequestV0 {
@ -2053,6 +2094,7 @@ pub struct OuterOverlayRequestV0 {
} }
/// OuterOverlayResponse V0 /// OuterOverlayResponse V0
///
/// reply to an OuterOverlayRequest V0 /// reply to an OuterOverlayRequest V0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OuterOverlayResponseV0 { pub struct OuterOverlayResponseV0 {
@ -2100,6 +2142,7 @@ pub enum TopicSyncResV0 {
} }
/// Topic synchronization response /// Topic synchronization response
///
/// it is a stream of blocks and or events. /// it is a stream of blocks and or events.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum TopicSyncRes { pub enum TopicSyncRes {
@ -2134,6 +2177,7 @@ pub enum CoreRequestContentV0 {
} }
/// CoreRequest V0 /// CoreRequest V0
///
/// replied with CoreResponse V0 /// replied with CoreResponse V0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoreRequestV0 { pub struct CoreRequestV0 {
@ -2153,6 +2197,7 @@ pub enum CoreRequest {
} }
/// CoreBrokerConnectResponse V0 /// CoreBrokerConnectResponse V0
///
/// reply to a CoreBrokerConnect V0 /// reply to a CoreBrokerConnect V0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoreBrokerConnectResponseV0 { pub struct CoreBrokerConnectResponseV0 {
@ -2176,7 +2221,8 @@ pub enum CoreResponseContentV0 {
} }
/// CoreResponse V0 /// CoreResponse V0
/// reply to an CoreRequest V0 ///
/// reply to a CoreRequest V0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CoreResponseV0 { pub struct CoreResponseV0 {
/// Request ID /// Request ID
@ -2467,6 +2513,7 @@ impl AdminResponse {
// //
/// Request to open a repo in a non-durable way (without pinning it). /// Request to open a repo in a non-durable way (without pinning it).
///
/// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed, /// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed,
/// except if a PinRepo occurred before or after the OpenRepo /// except if a PinRepo occurred before or after the OpenRepo
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -2515,6 +2562,7 @@ impl OpenRepo {
} }
/// Request to pin a repo on the broker. /// Request to pin a repo on the broker.
///
/// When client will disconnect, the subscriptions and publisherAdvert of the topics will be remain active on the broker, /// When client will disconnect, the subscriptions and publisherAdvert of the topics will be remain active on the broker,
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PinRepoV0 { pub struct PinRepoV0 {
@ -2570,6 +2618,7 @@ impl PinRepo {
} }
/// Request to refresh the Pinning of a previously pinned repo. /// Request to refresh the Pinning of a previously pinned repo.
///
/// it can consist of updating the expose_outer, the list of ro_topics and/or rw_topics, /// it can consist of updating the expose_outer, the list of ro_topics and/or rw_topics,
/// and in case of a ban_member, the broker will effectively flush the topics locally after all local members except the banned one, have refreshed /// and in case of a ban_member, the broker will effectively flush the topics locally after all local members except the banned one, have refreshed
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -2599,6 +2648,7 @@ pub enum RefreshPinRepo {
} }
/// Request to unpin a repo on the broker. /// Request to unpin a repo on the broker.
///
/// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed on the broker /// When client will disconnect, the subscriptions and publisherAdvert of the topics will be removed on the broker
/// (for that user only. other users might continue to have the repo pinned) /// (for that user only. other users might continue to have the repo pinned)
@ -2623,6 +2673,7 @@ impl UnpinRepo {
} }
/// Request the status of pinning for a repo on the broker. V0 /// Request the status of pinning for a repo on the broker. V0
///
/// returns an error code if not pinned, otherwise returns a RepoPinStatusV0 /// 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. if it is the outer, only outer pinning will be checked.
/// if it is the inner overlay, only the inner pinning will be checked. /// if it is the inner overlay, only the inner pinning will be checked.
@ -2715,6 +2766,7 @@ pub enum TopicUnsub {
} }
/// Request a Block by ID /// Request a Block by ID
///
/// commit_header_key is always set to None in the reply when request is made on OuterOverlay of protected or Group overlays /// commit_header_key is always set to None in the reply when request is made on OuterOverlay of protected or Group overlays
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockGetV0 { pub struct BlockGetV0 {
@ -2775,6 +2827,7 @@ impl BlocksPut {
} }
/// Request to know if some blocks are present locally /// Request to know if some blocks are present locally
///
/// used by client before publishing an event, to know what to push /// used by client before publishing an event, to know what to push
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlocksExistV0 { pub struct BlocksExistV0 {
@ -2841,6 +2894,7 @@ impl ObjectUnpin {
} }
/// Request to delete an object /// Request to delete an object
///
/// only effective if the refcount for this object is zero (basically it removes it from LRU) /// only effective if the refcount for this object is zero (basically it removes it from LRU)
#[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct ObjectDelV0 { pub struct ObjectDelV0 {
@ -3173,6 +3227,7 @@ pub struct ExtRequestV0 {
} }
/// External request are made by clients directly to a core broker of their choice. /// External request are made by clients directly to a core broker of their choice.
///
/// They differ from OuterOverlayRequests in the sense that the broker where the client is attached, is not involved in the request. /// They differ from OuterOverlayRequests in the sense that the broker where the client is attached, is not involved in the request.
/// It is a direct connection that is established between the client and the core broker that will give the response. /// It is a direct connection that is established between the client and the core broker that will give the response.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -3248,11 +3303,12 @@ impl TryFrom<ProtocolMessage> for ExtResponse {
} }
} }
/// //
/// PROTOCOL MESSAGES // PROTOCOL MESSAGES
/// //
#[doc(hidden)]
pub static MAGIC_NG_REQUEST: [u8; 2] = [78u8, 71u8]; pub static MAGIC_NG_REQUEST: [u8; 2] = [78u8, 71u8];
#[doc(hidden)]
pub static MAGIC_NG_RESPONSE: [u8; 4] = [89u8, 88u8, 78u8, 75u8]; pub static MAGIC_NG_RESPONSE: [u8; 4] = [89u8, 88u8, 78u8, 75u8];
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -3392,9 +3448,9 @@ impl ProtocolMessage {
} }
} }
/// //
/// AUTHENTICATION MESSAGES // AUTHENTICATION MESSAGES
/// //
/// Content of ClientAuthV0 /// Content of ClientAuthV0
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
@ -3510,6 +3566,7 @@ impl From<AuthResult> for ProtocolMessage {
// //
/// Link to a repository /// Link to a repository
///
/// Consists of an identifier (repoid), a ReadCap or WriteCap, and a locator (peers and overlayLink) /// Consists of an identifier (repoid), a ReadCap or WriteCap, and a locator (peers and overlayLink)
/// Those capabilities are not durable: They can be refreshed by the members and previously shared Caps will become obsolete/revoked. /// Those capabilities are not durable: They can be refreshed by the members and previously shared Caps will become obsolete/revoked.
/// As long as the user is a member of the repo and subscribes to the root topic (of the repo, and of the store if needed/applicable), they will receive the updated capabilities. /// As long as the user is a member of the repo and subscribes to the root topic (of the repo, and of the store if needed/applicable), they will receive the updated capabilities.
@ -3555,11 +3612,13 @@ impl RepoLink {
} }
} }
/// Link for a Public Repo
///
/// The latest ReadCap of the branch (or main branch) will be downloaded from the outerOverlay, if the peer brokers listed below allow it. /// The latest ReadCap of the branch (or main branch) will be downloaded from the outerOverlay, if the peer brokers listed below allow it.
/// The snapshot can be downloaded instead /// The snapshot can be downloaded instead
/// This locator is durable, because the public site are served differently by brokers. /// This link is durable, because the public site are served differently by brokers.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PublicRepoLocatorV0 { pub struct PublicRepoLinkV0 {
/// Repository ID /// Repository ID
pub repo: RepoId, pub repo: RepoId,
@ -3583,11 +3642,12 @@ pub struct PublicRepoLocatorV0 {
/// Link to a public repository /// Link to a public repository
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum PublicRepoLocator { pub enum PublicRepoLink {
V0(PublicRepoLocatorV0), V0(PublicRepoLinkV0),
} }
/// Read access to a branch of a Public, Protected or Group store. /// Read access to a branch of a Public, Protected or Group store.
///
/// The overlay to join can be the outer or the inner, depending on what was offered in the link. /// The overlay to join can be the outer or the inner, depending on what was offered in the link.
/// The difference between the two is that in the outer overlay, only one broker is contacted. /// The difference between the two is that in the outer overlay, only one broker is contacted.
/// In the inner overlay, all the publisher's brokers are contacted, so subscription to the pub/sub is more reliable, less prone to outage. /// In the inner overlay, all the publisher's brokers are contacted, so subscription to the pub/sub is more reliable, less prone to outage.
@ -3623,8 +3683,9 @@ pub enum ReadBranchLink {
} }
/// Obtains one or more objects of a repo (Commit, File) by their ID. /// Obtains one or more objects of a repo (Commit, File) by their ID.
///
/// On an outerOverlay, the header is always emptied (no way to reconstruct the DAG of commits) except on public overlays or if a topicId is provided /// On an outerOverlay, the header is always emptied (no way to reconstruct the DAG of commits) except on public overlays or if a topicId is provided
/// If the intent is to share a whole DAG of commits at a definite CommitID/HEAD, then ReadBranchLink should be used instead (or PublicRepoLocator if public site) /// If the intent is to share a whole DAG of commits at a definite CommitID/HEAD, then ReadBranchLink should be used instead (or PublicRepoLink if public site)
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ObjectLinkV0 { pub struct ObjectLinkV0 {
/// Repository ID: not used to make the request. but useful for commits, to know which repo they are from without needing to fetch and open the full DAG of commits. /// Repository ID: not used to make the request. but useful for commits, to know which repo they are from without needing to fetch and open the full DAG of commits.
@ -3658,7 +3719,7 @@ pub enum ObjectLink {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum NgLinkV0 { pub enum NgLinkV0 {
Repo(RepoLink), Repo(RepoLink),
PublicRepo(PublicRepoLocator), PublicRepo(PublicRepoLink),
Branch(ReadBranchLink), Branch(ReadBranchLink),
Object(ObjectLink), Object(ObjectLink),
} }
@ -3683,6 +3744,7 @@ mod test {
BootstrapContentV0 { BootstrapContentV0 {
servers: vec![BrokerServerV0 { servers: vec![BrokerServerV0 {
server_type: BrokerServerTypeV0::Localhost(14400), server_type: BrokerServerTypeV0::Localhost(14400),
can_verify: false,
peer_id: PubKey::Ed25519PubKey([ peer_id: PubKey::Ed25519PubKey([
95, 73, 225, 250, 3, 147, 24, 164, 177, 211, 34, 244, 45, 130, 111, 136, 95, 73, 225, 250, 3, 147, 24, 164, 177, 211, 34, 244, 45, 130, 111, 136,
229, 145, 53, 167, 50, 168, 140, 227, 65, 111, 203, 41, 210, 186, 162, 149, 229, 145, 53, 167, 50, 168, 140, 227, 65, 111, 203, 41, 210, 186, 162, 149,

@ -39,6 +39,9 @@ threshold_crypto = "0.4.0"
zeroize = { version = "1.6.0", features = ["zeroize_derive"] } zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
time = { version= "0.3.23", features = ["formatting"] } time = { version= "0.3.23", features = ["formatting"] }
once_cell = "1.17.1" once_cell = "1.17.1"
serde_json = "1.0"
os_info = "3"
current_platform = "0.2.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
debug_print = "1.0.0" debug_print = "1.0.0"

@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! Immutable Block //! Immutable Block, used to store and exchange File and Commit
use crate::errors::*; use crate::errors::*;
use crate::log::*; use crate::log::*;

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! Commit //! Commit that composes the DAG of a Branch
use core::fmt; use core::fmt;
use ed25519_dalek::{PublicKey, Signature}; use ed25519_dalek::{PublicKey, Signature};

@ -28,6 +28,7 @@ pub enum NgError {
InvalidFileFormat, InvalidFileFormat,
InvalidArgument, InvalidArgument,
PermissionDenied, PermissionDenied,
InvalidPazzle,
CommitLoadError(CommitLoadError), CommitLoadError(CommitLoadError),
StorageError(StorageError), StorageError(StorageError),
NotFound, NotFound,
@ -42,7 +43,6 @@ pub enum NgError {
WalletAlreadyOpened, WalletAlreadyOpened,
WalletError(String), WalletError(String),
BrokerError, BrokerError,
LockError,
SessionNotFound, SessionNotFound,
} }
@ -57,6 +57,48 @@ impl fmt::Display for NgError {
} }
} }
impl From<NgError> for std::io::Error {
fn from(err: NgError) -> std::io::Error {
match err {
NgError::InvalidArgument => std::io::Error::from(std::io::ErrorKind::InvalidInput),
NgError::PermissionDenied => std::io::Error::from(std::io::ErrorKind::PermissionDenied),
NgError::CommitLoadError(commit_load_error) => std::io::Error::new(
std::io::ErrorKind::Other,
format!("CommitLoadError: {:?}", commit_load_error),
),
NgError::StorageError(storage_error) => std::io::Error::new(
std::io::ErrorKind::Other,
format!("StorageError: {:?}", storage_error),
),
NgError::NotFound => std::io::Error::from(std::io::ErrorKind::NotFound),
NgError::CommitVerifyError(commit_verify_error) => std::io::Error::new(
std::io::ErrorKind::Other,
format!("CommitVerifyError: {:?}", commit_verify_error),
),
/*NgError::InvalidSignature => ,
NgError::IncompleteSignature =>
NgError::SerializationError => ,
NgError::EncryptionError => ,
NgError::InvalidKey => ,
NgError::InvalidInvitation => ,
NgError::InvalidCreateAccount => ,
NgError::InvalidFileFormat => ,
NgError::LocalBrokerNotInitialized => ,
NgError::JsStorageReadError => ,
NgError::JsStorageWriteError(String) => ,
NgError::CannotSaveWhenInMemoryConfig => ,
NgError::WalletNotFound => ,
NgError::WalletAlreadyAdded => ,
NgError::WalletAlreadyOpened => ,
NgError::WalletError(String) => ,
NgError::BrokerError => ,
NgError::SessionNotFound,
NgError::IoError => ,*/
_ => std::io::Error::new(std::io::ErrorKind::Other, err.to_string().as_str()),
}
}
}
impl From<serde_bare::error::Error> for NgError { impl From<serde_bare::error::Error> for NgError {
fn from(_e: serde_bare::error::Error) -> Self { fn from(_e: serde_bare::error::Error) -> Self {
NgError::SerializationError NgError::SerializationError

@ -6,7 +6,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! Event //! Event, a message sent in the PUB/SUB
use crate::errors::*; use crate::errors::*;
use crate::object::*; use crate::object::*;

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! File and RandomAccessFile objects //! SmallFile and RandomAccessFile objects
use core::fmt; use core::fmt;
use std::cmp::min; use std::cmp::min;

@ -6,6 +6,8 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! KeyColumnValue Store abstraction
use crate::store::StorageError; use crate::store::StorageError;
// TODO:remove mut on self for trait WriteTransaction methods // TODO:remove mut on self for trait WriteTransaction methods

@ -32,6 +32,8 @@ pub mod errors;
pub mod kcv_store; pub mod kcv_store;
pub mod os_info;
#[macro_use] #[macro_use]
extern crate slice_as_array; extern crate slice_as_array;

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! Merkle hash tree of Objects //! Object: Merkle hash tree of Blocks
use core::fmt; use core::fmt;
use std::borrow::BorrowMut; use std::borrow::BorrowMut;

@ -0,0 +1,56 @@
use os_info;
use serde_json::{json, to_string_pretty, Value};
pub fn get_os_info() -> Value {
let arch = std::env::consts::ARCH;
let machine = match arch {
"ia32" => "x86",
"x64" => "x86_64",
"i386" => "x86",
"i686" => "x86",
"amd64" => "x86_64",
"arm64" => "aarch64",
"powerpc" => "ppc",
"powerpc64" => "ppc64",
_ => arch,
};
let info = os_info::get();
let os_type = info.os_type();
let os_name = match os_type {
os_info::Type::Macos => "macOS".to_string(),
_ => format!("{:?}", os_type),
};
let val = json!({
"uname": {
"os_name": os_name,
"version": info.version().to_string(),
"arch": info.architecture().map(|s| s.into()).unwrap_or(Value::Null),
"bitness": format!("{:?}",info.bitness()),
"codename": info.codename().map(|s| s.into()).unwrap_or(Value::Null),
"edition": info.edition().map(|s| s.into()).unwrap_or(Value::Null),
},
"rust": {
"family": std::env::consts::FAMILY,
"os_name": match std::env::consts::OS {
"linux" => "Linux",
"macos" => "macOS",
"ios" => "iOS",
"freebsd" => "FreeBSD",
"dragonfly" => "DragonFly",
"netbsd" => "NetBSD",
"openbsd" => "OpenBSD",
"solaris" => "Solaris",
"android" => "Android",
"windows" => "Windows",
_ => std::env::consts::OS,
},
"arch": machine,
"debug": cfg!(debug_assertions),
"target": current_platform::CURRENT_PLATFORM,
}
});
//println!("{}", to_string_pretty(&val).unwrap());
val
}

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! Repository serde implementation and in memory helper //! Repository
use crate::errors::*; use crate::errors::*;
use crate::event::*; use crate::event::*;

@ -9,6 +9,8 @@
* according to those terms. * according to those terms.
*/ */
//! Site (Public, Protected, Private) of Individual and Org
use crate::errors::NgError; use crate::errors::NgError;
use crate::types::*; use crate::types::*;
use crate::utils::{generate_keypair, sign, verify}; use crate::utils::{generate_keypair, sign, verify};

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! Block store //! Storage of Blocks
use futures::StreamExt; use futures::StreamExt;

@ -7,7 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
//! P2P Repo types //! NextGraph Repo types
//! //!
//! Corresponds to the BARE schema //! Corresponds to the BARE schema
@ -367,14 +367,13 @@ pub type RepoHash = Digest;
/// Topic ID: public key of the topic /// Topic ID: public key of the topic
pub type TopicId = PubKey; pub type TopicId = PubKey;
/// User ID: user account for broker /// User ID: user account for broker and member of a repo
pub type UserId = PubKey; pub type UserId = PubKey;
/// BranchId is a PubKey /// BranchId is a PubKey
pub type BranchId = PubKey; pub type BranchId = PubKey;
/// Block ID: /// Block ID: BLAKE3 hash over the serialized BlockContent (contains encrypted content)
/// BLAKE3 hash over the serialized BlockContent (contains encrypted content)
pub type BlockId = Digest; pub type BlockId = Digest;
pub type BlockKey = SymKey; pub type BlockKey = SymKey;
@ -382,10 +381,10 @@ pub type BlockKey = SymKey;
/// Block reference /// Block reference
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BlockRef { pub struct BlockRef {
/// Object ID /// Block ID
pub id: BlockId, pub id: BlockId,
/// Key for decrypting the Object /// Key for decrypting the Block
pub key: BlockKey, pub key: BlockKey,
} }
@ -454,6 +453,7 @@ pub type ObjectKey = BlockKey;
pub type ObjectRef = BlockRef; pub type ObjectRef = BlockRef;
/// Read capability (for a commit, branch, whole repo, or store) /// Read capability (for a commit, branch, whole repo, or store)
///
/// For a store: A ReadCap to the root repo of the store /// For a store: A ReadCap to the root repo of the store
/// For a repo: A reference to the latest RootBranch definition commit /// For a repo: A reference to the latest RootBranch definition commit
/// For a branch: A reference to the latest Branch definition commit /// For a branch: A reference to the latest Branch definition commit
@ -461,6 +461,7 @@ pub type ObjectRef = BlockRef;
pub type ReadCap = ObjectRef; pub type ReadCap = ObjectRef;
/// Read capability secret (for a commit, branch, whole repo, or store) /// Read capability secret (for a commit, branch, whole repo, or store)
///
/// it is already included in the ReadCap (it is the key part of the reference) /// it is already included in the ReadCap (it is the key part of the reference)
pub type ReadCapSecret = ObjectKey; pub type ReadCapSecret = ObjectKey;
@ -718,20 +719,17 @@ pub struct SiteV0 {
/// Reduced Site (for QRcode) /// Reduced Site (for QRcode)
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ReducedSiteV0 { pub struct ReducedSiteV0 {
pub site_key: PrivKey, pub user_key: PrivKey,
pub private_site_key: PrivKey,
pub private_site_read_cap: ReadCap,
pub private_site_write_cap: RepoWriteCapSecret, pub private_store_read_cap: ReadCap,
pub core: PubKey, pub core: PubKey,
pub bootstraps: Vec<PubKey>, pub bootstraps: Vec<PubKey>,
} }
/// BLOCKS common types //
// BLOCKS common types
//
/// Internal node of a Merkle tree /// Internal node of a Merkle tree
pub type InternalNode = Vec<BlockKey>; pub type InternalNode = Vec<BlockKey>;
@ -746,6 +744,7 @@ pub enum ChunkContentV0 {
DataChunk(Vec<u8>), DataChunk(Vec<u8>),
} }
/// Header of a Commit, can be embedded or as a ref
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CommitHeaderV0 { pub struct CommitHeaderV0 {
/// optional Commit Header ID /// optional Commit Header ID
@ -782,6 +781,7 @@ pub enum CommitHeader {
V0(CommitHeaderV0), V0(CommitHeaderV0),
} }
/// Keys for the corresponding IDs contained in the Header
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CommitHeaderKeysV0 { pub struct CommitHeaderKeysV0 {
/// Other objects this commit strongly depends on (ex: ADD for a REMOVE, files for an nfiles) /// Other objects this commit strongly depends on (ex: ADD for a REMOVE, files for an nfiles)
@ -842,6 +842,7 @@ impl CommitHeaderRef {
} }
} }
/// unencrypted part of the Block
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct BlockContentV0 { pub struct BlockContentV0 {
/// Reference (actually, only its ID or an embedded block if the size is small enough) /// Reference (actually, only its ID or an embedded block if the size is small enough)
@ -913,7 +914,9 @@ pub enum Block {
V0(BlockV0), V0(BlockV0),
} }
/// REPO IMPLEMENTATION //
// REPO IMPLEMENTATION
//
/// Repository definition /// Repository definition
/// ///
@ -1106,7 +1109,7 @@ pub struct QuorumV0 {
} }
/// Quorum definition, is part of the RootBranch commit /// Quorum definition, is part of the RootBranch commit
/// TODO: can it be sent in the root branch without being part of a RootBranch ? // TODO: can it be sent in the root branch without being part of a RootBranch ?
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Quorum { pub enum Quorum {
V0(QuorumV0), V0(QuorumV0),
@ -1199,6 +1202,7 @@ impl fmt::Display for BranchType {
} }
/// Add a branch to the repository /// Add a branch to the repository
///
/// DEPS: if update branch: previous AddBranch commit of the same branchId /// DEPS: if update branch: previous AddBranch commit of the same branchId
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct AddBranchV0 { pub struct AddBranchV0 {
@ -1260,7 +1264,8 @@ pub enum AddMember {
} }
/// Remove member from a repo /// Remove member from a repo
/// An owner cannot be removed ///
/// An owner cannot be removed (it cannot be added even)
/// The overlay should be refreshed if user was malicious, after the user is removed from last repo. See REFRESH_READ_CAP on store repo. /// The overlay should be refreshed if user was malicious, after the user is removed from last repo. See REFRESH_READ_CAP on store repo.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct RemoveMemberV0 { pub struct RemoveMemberV0 {
@ -1412,6 +1417,7 @@ pub enum RepoNamedItem {
} }
/// Add a new name in the repo that can point to a branch, a commit or a file /// Add a new name in the repo that can point to a branch, a commit or a file
///
/// Or change the value of a name /// Or change the value of a name
/// DEPS: if it is a change of value: all the previous AddName commits seen for this name /// DEPS: if it is a change of value: all the previous AddName commits seen for this name
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -1433,6 +1439,7 @@ pub enum AddName {
} }
/// Remove a name from the repo, using ORset CRDT logic /// Remove a name from the repo, using ORset CRDT logic
///
/// DEPS: all the AddName commits seen for this name /// DEPS: all the AddName commits seen for this name
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct RemoveNameV0 { pub struct RemoveNameV0 {
@ -1454,6 +1461,7 @@ pub enum RemoveName {
// //
/// Adds a repo into the store branch. /// Adds a repo into the store branch.
///
/// The repo's `store` field should match the store /// The repo's `store` field should match the 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
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -1471,6 +1479,7 @@ pub enum AddRepo {
} }
/// Removes a repo from the store branch. /// Removes a repo from the store branch.
///
/// DEPS to the previous AddRepo commit(s) (ORset logic) with matching repo_id /// DEPS to the previous AddRepo commit(s) (ORset logic) with matching repo_id
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct RemoveRepoV0 { pub struct RemoveRepoV0 {
@ -1493,6 +1502,7 @@ pub enum RemoveRepo {
// //
/// Adds a link into the user branch, so that a user can share with all its device a new Link they received. /// Adds a link into the user branch, so that a user can share with all its device a new Link they received.
///
/// The repo's `store` field should not match with any store of the user. Only external repos are accepted here. /// The repo's `store` field should not match with any store of the user. Only external repos are accepted here.
/// DEPS to the previous AddLink commit(s) if it is an update. in this case, repo_id of the referenced rootbranch definition(s) should match /// DEPS to the previous AddLink commit(s) if it is an update. in this case, repo_id of the referenced rootbranch definition(s) should match
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -1510,6 +1520,7 @@ pub enum AddLink {
} }
/// Removes a link from the `user` branch. /// Removes a link from the `user` branch.
///
/// DEPS to the previous AddLink commit(s) (ORset logic) with matching repo_id /// DEPS to the previous AddLink commit(s) (ORset logic) with matching repo_id
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct RemoveLinkV0 { pub struct RemoveLinkV0 {
@ -1525,7 +1536,9 @@ pub enum RemoveLink {
V0(RemoveLinkV0), V0(RemoveLinkV0),
} }
/// Adds a SignerCap into the user branch, so that a user can share with all its device a new signing capability that was just created. /// Adds a SignerCap into the user branch,
///
/// so that a user can share with all its device a new signing capability that was just created.
/// The cap's `epoch` field should be dereferenced and the user must be part of the quorum/owners. /// 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 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 ) /// and the 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 )
@ -1544,6 +1557,7 @@ pub enum AddSignerCap {
} }
/// Removes a SignerCap from the `user` branch. /// Removes a SignerCap from the `user` branch.
///
/// DEPS to the previous AddSignerCap commit(s) (ORset logic) with matching repo_id /// DEPS to the previous AddSignerCap commit(s) (ORset logic) with matching repo_id
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct RemoveSignerCapV0 { pub struct RemoveSignerCapV0 {
@ -1560,6 +1574,7 @@ pub enum RemoveSignerCap {
} }
/// Adds a wallet operation so all the devices can sync their locally saved wallet on disk (at the next wallet opening) /// Adds a wallet operation so all the devices can sync their locally saved wallet on disk (at the next wallet opening)
///
/// DEPS are the last HEAD of wallet updates. /// DEPS are the last HEAD of wallet updates.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct WalletUpdateV0 { pub struct WalletUpdateV0 {
@ -1576,6 +1591,7 @@ pub enum WalletUpdate {
} }
/// Updates the ReadCap of the public and protected sites (and potentially also Group stores) /// Updates the ReadCap of the public and protected sites (and potentially also Group stores)
///
/// DEPS to the previous ones. /// DEPS to the previous ones.
/// this is used to speedup joining the overlay of such stores, for new devices on new brokers /// this is used to speedup joining the overlay of such stores, for new devices on new brokers
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -1613,6 +1629,7 @@ pub enum Transaction {
} }
/// Add a new binary file in a branch /// Add a new binary file in a branch
///
/// FILES: the file ObjectRef /// FILES: the file ObjectRef
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct AddFileV0 { pub struct AddFileV0 {
@ -1630,6 +1647,7 @@ pub enum AddFile {
} }
/// Remove a file from the branch, using ORset CRDT logic /// Remove a file from the branch, using ORset CRDT logic
///
/// (removes the ref counting. not necessarily the file itself) /// (removes the ref counting. not necessarily the file itself)
/// NFILES: the file ObjectRef /// NFILES: the file ObjectRef
/// DEPS: all the visible AddFile commits in the branch (ORset) /// DEPS: all the visible AddFile commits in the branch (ORset)
@ -1687,9 +1705,10 @@ pub enum Compact {
V0(CompactV0), V0(CompactV0),
} }
/// Async Threshold Signature of a commit V0 based on the partial order quorum // Async Threshold Signature of a commit V0 based on the partial order quorum
/// Can sign Transaction, AddFile, and Snapshot, after they have been committed to the DAG. //
/// DEPS: the signed commits // Can sign Transaction, AddFile, and Snapshot, after they have been committed to the DAG.
// DEPS: the signed commits
// #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] // #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
// pub struct AsyncSignatureV0 { // pub struct AsyncSignatureV0 {
// /// An Object containing the Threshold signature // /// An Object containing the Threshold signature
@ -1709,7 +1728,10 @@ impl AsyncSignature {
} }
} }
/// Sync Threshold Signature of one or a chain of commits . V0 . based on the total order quorum (or owners quorum) /// Sync Threshold Signature of one or a chain of commits . V0
///
/// points to the new Signature Object
/// based on the total order quorum (or owners quorum)
/// mandatory for UpdateRootBranch, UpdateBranch, some AddBranch, RemoveBranch, RemoveMember, RemovePermission, Quorum, Compact, sync Transaction, RefreshReadCap, RefreshWriteCap /// mandatory for UpdateRootBranch, UpdateBranch, some AddBranch, RemoveBranch, RemoveMember, RemovePermission, Quorum, Compact, sync Transaction, RefreshReadCap, RefreshWriteCap
/// DEPS: the last signed commit in chain /// 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
@ -1718,9 +1740,6 @@ impl AsyncSignature {
// /// An Object containing the Threshold signature // /// An Object containing the Threshold signature
// pub signature: ObjectRef, // pub signature: ObjectRef,
// } // }
/// Threshold Signature of a commit
/// points to the new Signature Object
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum SyncSignature { pub enum SyncSignature {
V0(ObjectRef), V0(ObjectRef),
@ -1746,6 +1765,7 @@ impl fmt::Display for SyncSignature {
} }
/// RefreshReadCap. renew the ReadCap of a `transactional` branch, or the root_branch, or all transactional branches and the root_branch. /// RefreshReadCap. renew the ReadCap of a `transactional` branch, or the root_branch, or all transactional branches and the root_branch.
///
/// Each branch forms its separate chain for that purpose. /// Each branch forms its separate chain for that purpose.
/// can refresh the topic ids, or not /// can refresh the topic ids, or not
/// DEPS: current HEADS in the branch at the moment of refresh. /// DEPS: current HEADS in the branch at the moment of refresh.
@ -1770,6 +1790,7 @@ pub enum RefreshReadCap {
} }
/// RefreshWriteCap is always done on the root_branch, and always refreshes all the transaction branches WriteCaps, and TopicIDs. /// RefreshWriteCap is always done on the root_branch, and always refreshes all the transaction branches WriteCaps, and TopicIDs.
///
/// DEPS: current HEADS in the branch at the moment of refresh. /// DEPS: current HEADS in the branch at the moment of refresh.
/// the chain on the root_branch is : RemovePermission/RemoveMember -> RefreshWriteCap -> RootBranch -> optional AddPermission(s) -> AddBranch /// the chain on the root_branch is : RemovePermission/RemoveMember -> RefreshWriteCap -> RootBranch -> optional AddPermission(s) -> AddBranch
/// and on each transactional branch: RefreshWriteCap -> Branch /// and on each transactional branch: RefreshWriteCap -> Branch
@ -1877,7 +1898,9 @@ pub enum Signature {
V0(SignatureV0), V0(SignatureV0),
} }
/// Enum for "orders" PKsets. Can be inherited from the store, in this case, it is an ObjectRef pointing to the latest Certificate of the store. /// Enum for "orders" PKsets.
///
/// Can be inherited from the store, in this case, it is an ObjectRef pointing to the latest Certificate of the store.
/// Or can be 2 PublicKey defined specially for this repo, /// Or can be 2 PublicKey defined specially for this repo,
/// .0 one for the total_order (first one). it is a PublicKeysSet so that verifier can see the threshold value, and can also verify Shares individually /// .0 one for the total_order (first one). it is a PublicKeysSet so that verifier can see the threshold value, and can also verify Shares individually
/// .1 the other for the partial_order (second one. a PublicKey. is optional, as some repos are forcefully totally ordered and do not have this set). /// .1 the other for the partial_order (second one. a PublicKey. is optional, as some repos are forcefully totally ordered and do not have this set).
@ -2086,7 +2109,8 @@ impl CommitContent {
} }
/// Commit object /// Commit object
/// Signed by branch key, or a member key authorized to publish this commit type ///
/// Signed by member key authorized to publish this commit type
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CommitV0 { pub struct CommitV0 {
/// ID of containing Object /// ID of containing Object
@ -2279,6 +2303,7 @@ impl fmt::Display for PeerId {
} }
/// Content of EventV0 /// Content of EventV0
///
/// Contains the objects of newly published Commit, its optional blocks, and optional FILES and their blocks. /// Contains the objects of newly published Commit, its optional blocks, and optional FILES and their blocks.
/// If a block is not present in the Event, its ID should be present in block_ids and the block should be put on the emitting broker beforehand with BlocksPut. /// If a block is not present in the Event, its ID should be present in block_ids and the block should be put on the emitting broker beforehand with BlocksPut.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]

@ -4,9 +4,9 @@ JS/WASM crate containing the SDK of NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -4,9 +4,9 @@ React example app for NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -4,9 +4,9 @@ Web app client of NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -102,7 +102,11 @@ function osName(platform, release) {
if (platform === 'win32') { if (platform === 'win32') {
return release ? windowsRelease(release) : {name: "Windows"}; return release ? windowsRelease(release) : {name: "Windows"};
} }
if (platform === 'aix') { platform = 'AIX'; }
else if (platform === 'freebsd') { platform = 'FreeBSD'; }
else if (platform === 'openbsd') { platform = 'OpenBSD'; }
else if (platform === 'android') { platform = 'Android'; }
else if (platform === 'sunos') { platform = 'SunOS'; }
return {name:platform, version:release}; return {name:platform, version:release};
} }
module.exports.version = function () { module.exports.version = function () {
@ -111,18 +115,26 @@ module.exports.version = function () {
module.exports.client_details = function () { module.exports.client_details = function () {
const process = require('process'); const process = require('process');
let arch = osnode.machine? osnode.machine() : process.arch;
if (arch=="ia32") {arch="x86"}
else if (arch=="x64") {arch="x86_64"}
else if (arch=="i386") {arch="x86"}
else if (arch=="i686") {arch="x86"}
else if (arch=="amd64") {arch="x86_64"}
else if (arch=="arm64") {arch="aarch64"}
const osnode = require('os'); const osnode = require('os');
let os = osName(osnode.platform(),osnode.release()); let os = osName(osnode.platform(),osnode.release());
if (osnode.version) os.uname = osnode.version(); if (osnode.version) os.uname = osnode.version();
os.type = osnode.type(); os.type = osnode.type();
return JSON.stringify({ return JSON.stringify({
platform: { type: "server", arch: osnode.machine? osnode.machine() : process.arch }, platform: { type: "server", arch },
os, os,
engine: { engine: {
name: "nodejs", name: "nodejs",
version: process.version, version: process.version,
arch : process.arch, arch : process.arch,
machine: osnode.machine? osnode.machine() : undefined,
versions: process.versions versions: process.versions
} }
}); });

@ -125,7 +125,7 @@ pub fn wallet_open_with_pazzle(
.map_err(|_| "Deserialization error of wallet")?; .map_err(|_| "Deserialization error of wallet")?;
let mut pin = serde_wasm_bindgen::from_value::<[u8; 4]>(js_pin) let mut pin = serde_wasm_bindgen::from_value::<[u8; 4]>(js_pin)
.map_err(|_| "Deserialization error of pin")?; .map_err(|_| "Deserialization error of pin")?;
let res = nextgraph::local_broker::wallet_open_with_pazzle(encrypted_wallet, pazzle, pin); let res = nextgraph::local_broker::wallet_open_with_pazzle(&encrypted_wallet, pazzle, pin);
match res { match res {
Ok(r) => Ok(r Ok(r) => Ok(r
.serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true)) .serialize(&serde_wasm_bindgen::Serializer::new().serialize_maps_as_objects(true))
@ -153,7 +153,7 @@ pub fn wallet_update(js_wallet_id: JsValue, js_operations: JsValue) -> Result<Js
pub async fn get_wallets() -> Result<JsValue, JsValue> { pub async fn get_wallets() -> Result<JsValue, JsValue> {
init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await; init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await;
let res = get_all_wallets().await.map_err(|e| { let res = wallets_get_all().await.map_err(|e| {
log_err!("{}", e.to_string()); log_err!("{}", e.to_string());
}); });
if res.is_ok() { if res.is_ok() {
@ -162,25 +162,6 @@ pub async fn get_wallets() -> Result<JsValue, JsValue> {
Ok(JsValue::UNDEFINED) Ok(JsValue::UNDEFINED)
} }
// fn save_new_session(
// wallet_name: &String,
// wallet_id: PubKey,
// user: PubKey,
// ) -> Result<SessionWalletStorageV0, String> {
// let res = create_new_session(wallet_id, user);
// if res.is_ok() {
// let sws = res.unwrap();
// let encoded = base64_url::encode(&sws.1);
// let r = session_save(format!("ng_wallet@{}", wallet_name), encoded);
// if r.is_some() {
// return Err(r.unwrap());
// }
// Ok(sws.0)
// } else {
// Err(res.unwrap_err().to_string())
// }
// }
fn take_some_peer_last_seq_numbers(peer_id: PubKey, qty: u16) -> Result<u64, NgError> { fn take_some_peer_last_seq_numbers(peer_id: PubKey, qty: u16) -> Result<u64, NgError> {
let res = session_get(format!("ng_peer_last_seq@{}", peer_id)); let res = session_get(format!("ng_peer_last_seq@{}", peer_id));
let val = match res { let val = match res {
@ -266,49 +247,11 @@ pub async fn session_start(wallet_name: String, user_js: JsValue) -> Result<JsVa
Ok(serde_wasm_bindgen::to_value(&res).unwrap()) Ok(serde_wasm_bindgen::to_value(&res).unwrap())
} }
// fn save_wallet_locally(res: &CreateWalletResultV0) -> Result<SessionWalletStorageV0, String> {
// let sws = save_new_session(&res.wallet_name, res.wallet.id(), res.user)?;
// let mut wallets: HashMap<String, LocalWalletStorageV0> =
// get_all_wallets().unwrap_or(HashMap::new());
// // TODO: check that the wallet is not already present in localStorage
// let lws: LocalWalletStorageV0 = res.into();
// wallets.insert(res.wallet_name.clone(), lws);
// let lws_ser = serde_bare::to_vec(&LocalWalletStorage::V0(wallets)).unwrap();
// let encoded = base64_url::encode(&lws_ser);
// let r = local_save("ng_wallets".to_string(), encoded);
// if r.is_some() {
// return Err(r.unwrap());
// }
// Ok(sws)
// }
// #[cfg(target_arch = "wasm32")]
// #[wasm_bindgen]
// pub async fn get_wallet_client(
// wallet_id: String,
// wallet_privkey_js: JsValue,
// ) -> Result<JsValue, String> {
// let mut wallet_privkey = serde_wasm_bindgen::from_value::<PrivKey>(wallet_privkey_js)
// .map_err(|_| "Deserialization error of wallet_privkey")?;
// match get_all_wallets().await {
// Ok(wallets) => match wallets.get(&wallet_id) {
// Some(wallet) => {
// let res = wallet
// .to_client_v0(wallet_privkey)
// .map_err(|e| e.to_string())?;
// Ok(serde_wasm_bindgen::to_value(&res).unwrap())
// }
// None => Err("Cannot find this wallet locally".to_string()),
// },
// Err(_) => Err("Cannot find this wallet locally".to_string()),
// }
// }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn reload_wallets() -> Result<(), String> { pub async fn wallets_reload() -> Result<(), String> {
init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await; init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await;
nextgraph::local_broker::reload_wallets() nextgraph::local_broker::wallets_reload()
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
@ -398,10 +341,10 @@ pub async fn wallet_create(js_params: JsValue) -> Result<JsValue, JsValue> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn wallet_download_file(wallet_name: String) -> Result<JsValue, JsValue> { pub async fn wallet_get_file(wallet_name: String) -> Result<JsValue, JsValue> {
init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await; init_local_broker_with_lazy(&INIT_LOCAL_BROKER).await;
let res = nextgraph::local_broker::wallet_download_file(&wallet_name).await; let res = nextgraph::local_broker::wallet_get_file(&wallet_name).await;
match res { match res {
Ok(r) => Ok(serde_wasm_bindgen::to_value(&serde_bytes::ByteBuf::from(r)).unwrap()), Ok(r) => Ok(serde_wasm_bindgen::to_value(&serde_bytes::ByteBuf::from(r)).unwrap()),
Err(e) => Err(e.to_string().into()), Err(e) => Err(e.to_string().into()),
@ -454,36 +397,6 @@ pub async fn wallet_import(
.map_err(|e: NgError| e.to_string())?; .map_err(|e: NgError| e.to_string())?;
Ok(serde_wasm_bindgen::to_value(&client).unwrap()) Ok(serde_wasm_bindgen::to_value(&client).unwrap())
// let SensitiveWallet::V0(mut opened_wallet_v0) = opened_wallet;
// let mut wallets: HashMap<String, LocalWalletStorageV0> =
// get_all_wallets().unwrap_or(HashMap::new());
// // check that the wallet is not already present in localStorage
// let wallet_name = opened_wallet_v0.wallet_id.clone();
// if wallets.get(&wallet_name).is_none() {
// let session = save_new_session(
// &wallet_name,
// opened_wallet_v0.wallet_privkey.to_pub(),
// opened_wallet_v0.personal_site,
// )
// .map_err(|e| format!("Cannot create new session: {e}"))?;
// let (lws, client) = opened_wallet_v0
// .import(previous_wallet, session)
// .map_err(|e| e.to_string())?;
// //let lws = LocalWalletStorageV0::new(wallet, &client);
// wallets.insert(wallet_name, lws);
// let lws_ser = serde_bare::to_vec(&LocalWalletStorage::V0(wallets)).unwrap();
// let encoded = base64_url::encode(&lws_ser);
// let r = local_save("ng_wallets".to_string(), encoded);
// if r.is_some() {
// return Err(r.unwrap());
// }
// Ok(serde_wasm_bindgen::to_value(&client).unwrap())
// } else {
// Err("Wallet already present on this device".to_string())
// }
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@ -497,7 +410,7 @@ pub fn test_create_wallet() -> JsValue {
9, 9,
false, false,
false, false,
BootstrapContentV0::new(), BootstrapContentV0::new_empty(),
None, None,
None, None,
); );
@ -813,7 +726,7 @@ pub async fn session_stop(user_id_js: JsValue) -> Result<(), String> {
let user_id = serde_wasm_bindgen::from_value::<UserId>(user_id_js) let user_id = serde_wasm_bindgen::from_value::<UserId>(user_id_js)
.map_err(|_| "serde error on user_id")?; .map_err(|_| "serde error on user_id")?;
nextgraph::local_broker::session_stop(user_id) nextgraph::local_broker::session_stop(&user_id)
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
@ -824,7 +737,7 @@ pub async fn user_disconnect(user_id_js: JsValue) -> Result<(), String> {
let user_id = serde_wasm_bindgen::from_value::<UserId>(user_id_js) let user_id = serde_wasm_bindgen::from_value::<UserId>(user_id_js)
.map_err(|_| "serde error on user_id")?; .map_err(|_| "serde error on user_id")?;
nextgraph::local_broker::user_disconnect(user_id) nextgraph::local_broker::user_disconnect(&user_id)
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
@ -832,7 +745,7 @@ pub async fn user_disconnect(user_id_js: JsValue) -> Result<(), String> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[wasm_bindgen] #[wasm_bindgen]
pub async fn wallet_close(wallet_name: String) -> Result<(), String> { pub async fn wallet_close(wallet_name: String) -> Result<(), String> {
nextgraph::local_broker::wallet_close(wallet_name) nextgraph::local_broker::wallet_close(&wallet_name)
.await .await
.map_err(|e: NgError| e.to_string()) .map_err(|e: NgError| e.to_string())
} }
@ -860,7 +773,7 @@ pub async fn user_connect(
let mut opened_connections: HashMap<String, ConnectionInfo> = HashMap::new(); let mut opened_connections: HashMap<String, ConnectionInfo> = HashMap::new();
let results = nextgraph::local_broker::user_connect(info, user_id, location) let results = nextgraph::local_broker::user_connect_with_device_info(info, &user_id, location)
.await .await
.map_err(|e: NgError| e.to_string())?; .map_err(|e: NgError| e.to_string())?;

@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -10,9 +10,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -7,6 +7,7 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
use ng_repo::errors::NgError;
use std::collections::HashMap; use std::collections::HashMap;
pub struct EmojiDef<'a> { pub struct EmojiDef<'a> {
pub hexcode: &'a str, pub hexcode: &'a str,
@ -38,7 +39,7 @@ const face: [EmojiDef<'static>; 15] = [
EmojiDef { EmojiDef {
hexcode: "1f60d", hexcode: "1f60d",
shortcode: "smiling_face_with_heart_eyes", shortcode: "smiling_face_with_heart_eyes",
code: "two_hearts", code: "with_two_hearts",
}, },
EmojiDef { EmojiDef {
hexcode: "1f618", hexcode: "1f618",
@ -48,12 +49,12 @@ const face: [EmojiDef<'static>; 15] = [
EmojiDef { EmojiDef {
hexcode: "1f61d", hexcode: "1f61d",
shortcode: "squinting_face_with_tongue", shortcode: "squinting_face_with_tongue",
code: "tongue", code: "with_tongue",
}, },
EmojiDef { EmojiDef {
hexcode: "1f917", hexcode: "1f917",
shortcode: "hugging_face", shortcode: "hugging_face",
code: "two_hands", code: "with_two_hands",
}, },
EmojiDef { EmojiDef {
hexcode: "1f92d", hexcode: "1f92d",
@ -102,7 +103,7 @@ const face_unwell: [EmojiDef<'static>; 15] = [
EmojiDef { EmojiDef {
hexcode: "1f912", hexcode: "1f912",
shortcode: "face_with_thermometer", shortcode: "face_with_thermometer",
code: "thermometer", code: "fever",
}, },
EmojiDef { EmojiDef {
hexcode: "1f915", hexcode: "1f915",
@ -137,7 +138,7 @@ const face_unwell: [EmojiDef<'static>; 15] = [
EmojiDef { EmojiDef {
hexcode: "1f92f", hexcode: "1f92f",
shortcode: "exploding_head", shortcode: "exploding_head",
code: "explosion", code: "exploding",
}, },
EmojiDef { EmojiDef {
hexcode: "2639", hexcode: "2639",
@ -258,7 +259,7 @@ const emotion: [EmojiDef<'static>; 15] = [
EmojiDef { EmojiDef {
hexcode: "2764", hexcode: "2764",
shortcode: "red_heart", shortcode: "red_heart",
code: "one_heart", code: "red_heart",
}, },
EmojiDef { EmojiDef {
hexcode: "1f495", hexcode: "1f495",
@ -1201,13 +1202,13 @@ lazy_static! {
("travel", travel), ("travel", travel),
("sky", sky), ("sky", sky),
("play", play), ("play", play),
("house", play), ("house", house),
] ]
.into_iter() .into_iter()
.collect(); .collect();
} }
pub const EMOJI_CAT: [&str; 15] = [ pub const EMOJI_CAT: [&'static str; 15] = [
"face", "face",
"sport", "sport",
"big_animal", "big_animal",
@ -1224,3 +1225,68 @@ pub const EMOJI_CAT: [&str; 15] = [
"face_costume", "face_costume",
"emotion", "emotion",
]; ];
lazy_static! {
pub static ref EMOJI_CODES: HashMap<&'static str, u8> = generate_tuples();
}
fn generate_tuples() -> HashMap<&'static str, u8> {
let mut tuples = vec![];
for (icat, cat_name) in EMOJI_CAT.iter().enumerate() {
for (iemoji, emoji) in EMOJIS.get(cat_name).unwrap().iter().enumerate() {
let nbr = (icat << 4) + iemoji;
tuples.push((emoji.code, nbr as u8));
}
}
// let mut map = HashMap::new();
// for t in tuples.into_iter() {
// match map.insert(t.0, t.1) {
// Some(double) => log_info!("{} {} {}", t.0, t.1, double),
// None => {}
// }
// }
// map
tuples.into_iter().collect()
}
/// returns a list of tuples of 2 strings (category,emoji)
pub fn display_pazzle(pazzle: &Vec<u8>) -> Vec<(&'static str, &'static str)> {
let mut res = vec![];
for emoji in pazzle {
let cat = (emoji & 240) >> 4;
let idx = emoji & 15;
res.push((
EMOJI_CAT[cat as usize],
EMOJIS.get(&EMOJI_CAT[cat as usize]).unwrap()[idx as usize].code,
));
}
res
}
//use ng_repo::log::*;
/// taking a list of pazzle words, returns a list of u8 codes
pub fn encode_pazzle(words: &Vec<String>) -> Result<Vec<u8>, NgError> {
//assert_eq!(EMOJI_CODES.len(), 15 * 15);
let mut res = vec![];
for word in words {
res.push(
*EMOJI_CODES
.get(word.as_str())
.ok_or(NgError::InvalidPazzle)?,
);
}
//log_info!("{:?}", res);
Ok(res)
}
/// lists all the words available for a pazzle, together with its category and u8 code
pub fn list_all_words() -> Vec<(&'static str, &'static str, u8)> {
let mut tuples = vec![];
for (icat, cat_name) in EMOJI_CAT.iter().enumerate() {
for (iemoji, emoji) in EMOJIS.get(cat_name).unwrap().iter().enumerate() {
let nbr = (icat << 4) + iemoji;
tuples.push((emoji.code, *cat_name, nbr as u8));
}
}
tuples
}

@ -69,6 +69,12 @@ impl Wallet {
} }
} }
/// `nonce` : The current nonce used for encrypting this wallet by the user on this device.
/// It should be incremented BEFORE encrypting the wallet again
/// when some new operations have been added to the log of the Wallet.
/// The nonce is by PeerId. It is saved together with the PeerId in the SessionPeerStorage.
/// If the session is not saved (in-memory) it is lost, but it is fine, as the PeerId is also lost, and a new one
/// will be generated for the next session.
pub fn encrypt( pub fn encrypt(
&self, &self,
wallet_log: &WalletLog, wallet_log: &WalletLog,
@ -208,33 +214,33 @@ pub fn enc_wallet_log(
Ok(buffer) Ok(buffer)
} }
pub fn dec_session(key: PrivKey, vec: &Vec<u8>) -> Result<SessionWalletStorageV0, NgWalletError> { // pub fn dec_session(key: PrivKey, vec: &Vec<u8>) -> Result<SessionWalletStorageV0, NgWalletError> {
let session_ser = crypto_box::seal_open(&(*key.to_dh().slice()).into(), vec) // let session_ser = crypto_box::seal_open(&(*key.to_dh().slice()).into(), vec)
.map_err(|_| NgWalletError::DecryptionError)?; // .map_err(|_| NgWalletError::DecryptionError)?;
let session: SessionWalletStorage = // let session: SessionWalletStorage =
serde_bare::from_slice(&session_ser).map_err(|_| NgWalletError::SerializationError)?; // serde_bare::from_slice(&session_ser).map_err(|_| NgWalletError::SerializationError)?;
let SessionWalletStorage::V0(v0) = session; // let SessionWalletStorage::V0(v0) = session;
Ok(v0) // Ok(v0)
} // }
pub fn create_new_session( // pub fn create_new_session(
wallet_id: PubKey, // wallet_id: PubKey,
user: PubKey, // user: PubKey,
) -> Result<(SessionWalletStorageV0, Vec<u8>), NgWalletError> { // ) -> Result<(SessionWalletStorageV0, Vec<u8>), NgWalletError> {
let peer = generate_keypair(); // let peer = generate_keypair();
let mut sws = SessionWalletStorageV0::new(); // let mut sws = SessionWalletStorageV0::new();
let sps = SessionPeerStorageV0 { // let sps = SessionPeerStorageV0 {
user, // user,
peer_key: peer.0, // peer_key: peer.0,
last_wallet_nonce: 0, // last_wallet_nonce: 0,
}; // };
sws.users.insert(user.to_string(), sps); // sws.users.insert(user.to_string(), sps);
let sws_ser = serde_bare::to_vec(&SessionWalletStorage::V0(sws.clone())).unwrap(); // let sws_ser = serde_bare::to_vec(&SessionWalletStorage::V0(sws.clone())).unwrap();
let mut rng = crypto_box::aead::OsRng {}; // let mut rng = crypto_box::aead::OsRng {};
let cipher = crypto_box::seal(&mut rng, &wallet_id.to_dh_slice().into(), &sws_ser) // let cipher = crypto_box::seal(&mut rng, &wallet_id.to_dh_slice().into(), &sws_ser)
.map_err(|_| NgWalletError::EncryptionError)?; // .map_err(|_| NgWalletError::EncryptionError)?;
Ok((sws, cipher)) // Ok((sws, cipher))
} // }
pub fn dec_encrypted_block( pub fn dec_encrypted_block(
mut ciphertext: Vec<u8>, mut ciphertext: Vec<u8>,
@ -264,6 +270,7 @@ pub fn dec_encrypted_block(
// `ciphertext` now contains the decrypted block // `ciphertext` now contains the decrypted block
//log_debug!("decrypted_block {:?}", ciphertext); //log_debug!("decrypted_block {:?}", ciphertext);
ciphertext.zeroize();
match decrypted_log { match decrypted_log {
WalletLog::V0(v0) => v0.reduce(master_key), WalletLog::V0(v0) => v0.reduce(master_key),
@ -297,7 +304,7 @@ pub fn derive_key_from_pass(mut pass: Vec<u8>, salt: [u8; 16], wallet_id: Wallet
} }
pub fn open_wallet_with_pazzle( pub fn open_wallet_with_pazzle(
wallet: Wallet, wallet: &Wallet,
mut pazzle: Vec<u8>, mut pazzle: Vec<u8>,
mut pin: [u8; 4], mut pin: [u8; 4],
) -> Result<SensitiveWallet, NgWalletError> { ) -> Result<SensitiveWallet, NgWalletError> {
@ -330,9 +337,9 @@ pub fn open_wallet_with_pazzle(
"opening of wallet with pazzle took: {} ms", "opening of wallet with pazzle took: {} ms",
opening_pazzle.elapsed().as_millis() opening_pazzle.elapsed().as_millis()
); );
let cipher = v0.content.encrypted.clone();
Ok(SensitiveWallet::V0(dec_encrypted_block( Ok(SensitiveWallet::V0(dec_encrypted_block(
v0.content.encrypted, cipher,
master_key, master_key,
v0.content.peer_id, v0.content.peer_id,
v0.content.nonce, v0.content.nonce,
@ -445,8 +452,8 @@ pub fn create_wallet_v0(mut params: CreateWalletV0) -> Result<CreateWalletResult
// pazzle_length can only be 9, 12, or 15 // pazzle_length can only be 9, 12, or 15
if params.pazzle_length != 9 if params.pazzle_length != 9
&& params.pazzle_length != 12 //&& params.pazzle_length != 12
&& params.pazzle_length != 15 //&& params.pazzle_length != 15
&& params.pazzle_length != 0 && params.pazzle_length != 0
{ {
return Err(NgWalletError::InvalidPazzleLength); return Err(NgWalletError::InvalidPazzleLength);
@ -565,9 +572,9 @@ pub fn create_wallet_v0(mut params: CreateWalletV0) -> Result<CreateWalletResult
let create_op = WalletOpCreateV0 { let create_op = WalletOpCreateV0 {
wallet_privkey: wallet_privkey.clone(), wallet_privkey: wallet_privkey.clone(),
pazzle: pazzle.clone(), // pazzle: pazzle.clone(),
mnemonic, // mnemonic,
pin: params.pin, // pin: params.pin,
personal_site: site, personal_site: site,
save_to_ng_one: if params.send_wallet { save_to_ng_one: if params.send_wallet {
SaveToNGOne::Wallet SaveToNGOne::Wallet
@ -579,8 +586,8 @@ pub fn create_wallet_v0(mut params: CreateWalletV0) -> Result<CreateWalletResult
//client: client.clone(), //client: client.clone(),
}; };
//Creating a new peerId for this Client and User //Creating a new peerId for this Client and User. we don't do that anymore
let peer = generate_keypair(); //let peer = generate_keypair();
let mut wallet_log = WalletLog::new_v0(create_op); let mut wallet_log = WalletLog::new_v0(create_op);
@ -645,7 +652,15 @@ pub fn create_wallet_v0(mut params: CreateWalletV0) -> Result<CreateWalletResult
let timestamp = now_timestamp(); let timestamp = now_timestamp();
let encrypted = enc_wallet_log(&wallet_log, &master_key, peer.1, 0, timestamp, wallet_id)?; let encrypted = enc_wallet_log(
&wallet_log,
&master_key,
// the peer_id used to generate the nonce at creation time is always zero
PubKey::nil(),
0,
timestamp,
wallet_id,
)?;
master_key.zeroize(); master_key.zeroize();
let wallet_content = WalletContentV0 { let wallet_content = WalletContentV0 {
@ -658,7 +673,7 @@ pub fn create_wallet_v0(mut params: CreateWalletV0) -> Result<CreateWalletResult
enc_master_key_mnemonic, enc_master_key_mnemonic,
master_nonce: 0, master_nonce: 0,
timestamp, timestamp,
peer_id: peer.1, peer_id: PubKey::nil(),
nonce: 0, nonce: 0,
encrypted, encrypted,
}; };
@ -702,9 +717,6 @@ pub fn create_wallet_v0(mut params: CreateWalletV0) -> Result<CreateWalletResult
pazzle, pazzle,
mnemonic: mnemonic.clone(), mnemonic: mnemonic.clone(),
wallet_name: base64_url::encode(&wallet_id.slice()), wallet_name: base64_url::encode(&wallet_id.slice()),
peer_id: peer.1,
peer_key: peer.0,
nonce: 0,
client, client,
user, user,
in_memory: !params.local_save, in_memory: !params.local_save,
@ -761,7 +773,7 @@ mod test {
9, 9,
false, false,
false, false,
BootstrapContentV0::new(), BootstrapContentV0::new_empty(),
None, None,
None, None,
)) ))
@ -816,7 +828,7 @@ mod test {
if v0.content.pazzle_length > 0 { if v0.content.pazzle_length > 0 {
let opening_pazzle = Instant::now(); let opening_pazzle = Instant::now();
let w = open_wallet_with_pazzle(Wallet::V0(v0.clone()), res.pazzle.clone(), pin) let w = open_wallet_with_pazzle(&Wallet::V0(v0.clone()), res.pazzle.clone(), pin)
.expect("open with pazzle"); .expect("open with pazzle");
log_debug!( log_debug!(
"opening of wallet with pazzle took: {} ms", "opening of wallet with pazzle took: {} ms",

@ -144,6 +144,12 @@ impl SessionWalletStorageV0 {
pub struct SessionPeerStorageV0 { pub struct SessionPeerStorageV0 {
pub user: UserId, pub user: UserId,
pub peer_key: PrivKey, pub peer_key: PrivKey,
/// The current nonce used for encrypting this wallet by the user on this device.
/// It should be incremented BEFORE encrypting the wallet again
/// when some new operations have been added to the log of the Wallet.
/// The nonce is by PeerId. It is saved together with the PeerId in the SessionPeerStorage.
/// If the session is not saved (in-memory) it is lost, but it is fine, as the PeerId is also lost, and a new one
/// will be generated for the next session.
pub last_wallet_nonce: u64, pub last_wallet_nonce: u64,
} }
@ -222,7 +228,7 @@ pub struct LocalWalletStorageV0 {
impl From<&CreateWalletResultV0> for LocalWalletStorageV0 { impl From<&CreateWalletResultV0> for LocalWalletStorageV0 {
fn from(res: &CreateWalletResultV0) -> Self { fn from(res: &CreateWalletResultV0) -> Self {
LocalWalletStorageV0 { LocalWalletStorageV0 {
bootstrap: BootstrapContent::V0(BootstrapContentV0::new()), bootstrap: BootstrapContent::V0(BootstrapContentV0::new_empty()),
wallet: res.wallet.clone(), wallet: res.wallet.clone(),
in_memory: res.in_memory, in_memory: res.in_memory,
client_id: res.client.id, client_id: res.client.id,
@ -238,6 +244,7 @@ impl From<&CreateWalletResultV0> for LocalWalletStorageV0 {
} }
impl LocalWalletStorageV0 { impl LocalWalletStorageV0 {
#[doc(hidden)]
pub fn new( pub fn new(
encrypted_wallet: Wallet, encrypted_wallet: Wallet,
wallet_priv_key: PrivKey, wallet_priv_key: PrivKey,
@ -245,7 +252,7 @@ impl LocalWalletStorageV0 {
in_memory: bool, in_memory: bool,
) -> Result<Self, NgWalletError> { ) -> Result<Self, NgWalletError> {
Ok(LocalWalletStorageV0 { Ok(LocalWalletStorageV0 {
bootstrap: BootstrapContent::V0(BootstrapContentV0::new()), bootstrap: BootstrapContent::V0(BootstrapContentV0::new_empty()),
wallet: encrypted_wallet, wallet: encrypted_wallet,
in_memory, in_memory,
client_id: client.id, client_id: client.id,
@ -256,7 +263,7 @@ impl LocalWalletStorageV0 {
.encrypt(client.id, wallet_priv_key)?, .encrypt(client.id, wallet_priv_key)?,
}) })
} }
#[doc(hidden)]
pub fn to_client_v0(&self, wallet_privkey: PrivKey) -> Result<ClientV0, NgWalletError> { pub fn to_client_v0(&self, wallet_privkey: PrivKey) -> Result<ClientV0, NgWalletError> {
Ok(ClientV0 { Ok(ClientV0 {
id: self.client_id, id: self.client_id,
@ -266,7 +273,8 @@ impl LocalWalletStorageV0 {
}) })
} }
fn local_client_storage_v0( /// decrypts the client_storage field, given the wallet PrivKey
pub fn local_client_storage_v0(
&self, &self,
wallet_privkey: PrivKey, wallet_privkey: PrivKey,
) -> Result<LocalClientStorageV0, NgWalletError> { ) -> Result<LocalClientStorageV0, NgWalletError> {
@ -294,16 +302,18 @@ impl LocalWalletStorage {
#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)] #[derive(Clone, Debug, Zeroize, ZeroizeOnDrop, Serialize, Deserialize)]
pub struct ClientV0 { pub struct ClientV0 {
#[zeroize(skip)] #[zeroize(skip)]
/// ClientID
pub id: PubKey, pub id: PubKey,
/// list of users that should be opened automatically (at launch, after wallet opened) on this device /// list of users that should be opened automatically (at launch, after wallet opened) on this device
#[zeroize(skip)] #[zeroize(skip)]
pub auto_open: Vec<PubKey>, pub auto_open: Vec<PubKey>,
/// Device name /// user supplied Device name. can be useful to distinguish between several devices (phone, tablet, laptop, office desktop, etc...)
#[zeroize(skip)] #[zeroize(skip)]
pub name: Option<String>, pub name: Option<String>,
/// contains the decrypted information needed when user is opening their wallet on this client.
pub sensitive_client_storage: LocalClientStorageV0, pub sensitive_client_storage: LocalClientStorageV0,
} }
@ -433,6 +443,9 @@ impl SensitiveWallet {
Self::V0(v0) => v0.wallet_id.clone(), Self::V0(v0) => v0.wallet_id.clone(),
} }
} }
pub fn name(&self) -> String {
self.id()
}
pub fn client(&self) -> &Option<ClientV0> { pub fn client(&self) -> &Option<ClientV0> {
match self { match self {
Self::V0(v0) => &v0.client, Self::V0(v0) => &v0.client,
@ -453,6 +466,11 @@ impl SensitiveWallet {
Self::V0(v0) => v0.sites.get(&user_id.to_string()).is_some(), Self::V0(v0) => v0.sites.get(&user_id.to_string()).is_some(),
} }
} }
pub fn personal_identity(&self) -> UserId {
match self {
Self::V0(v0) => v0.personal_site,
}
}
pub fn import_v0( pub fn import_v0(
&mut self, &mut self,
encrypted_wallet: Wallet, encrypted_wallet: Wallet,
@ -897,15 +915,14 @@ impl WalletOperation {
pub struct WalletOpCreateV0 { pub struct WalletOpCreateV0 {
pub wallet_privkey: PrivKey, pub wallet_privkey: PrivKey,
#[serde(skip)] // #[serde(skip)]
pub pazzle: Vec<u8>, // pub pazzle: Vec<u8>,
#[serde(skip)] // #[serde(skip)]
pub mnemonic: [u16; 12], // pub mnemonic: [u16; 12],
#[serde(skip)]
pub pin: [u8; 4],
// #[serde(skip)]
// pub pin: [u8; 4],
#[zeroize(skip)] #[zeroize(skip)]
pub save_to_ng_one: SaveToNGOne, pub save_to_ng_one: SaveToNGOne,
@ -1068,28 +1085,58 @@ impl AddWallet {
} }
} }
/// Create Wallet Version 0, used by the API create_wallet_v0 /// Create Wallet Version 0, used by the API create_wallet_v0 as a list of arguments
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)]
pub struct CreateWalletV0 { pub struct CreateWalletV0 {
/// a vector containing the binary content of an image file that will be used at every login, displayed (on devices that can)
/// to the user so they can check the wallet is theirs and that entering their pazzle and PIN is safe and there is no phishing attack.
/// an attacker would redirect the user to a clone of the wallet opener app, and would try to steal what the user enters
/// but this attacker would not possess the security_img of the user as it is only present locally in the wallet file.
/// the image should be bigger than 150x150px. There is no need to provide more than 400x400px as it will be scaled down anyway.
/// We accept several formats like JPEG, PNG, GIF, WEBP and more.
/// The image should be unique to the user. But it should not be too personal neither. Do not upload face picture, this is not a profile pic.
/// The best would be any picture that the user recognizes as unique.
/// Please be aware that other users who are sharing the same device, will be able to see this image.
#[zeroize(skip)] #[zeroize(skip)]
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
pub security_img: Vec<u8>, pub security_img: Vec<u8>,
/// A string of characters of minimum length 10.
/// This phrase will be presented to the user every time they are about to enter their pazzle and PIN in order to unlock their wallet.
/// It should be something the user will remember, but not something too personal.
/// Do not enter full name, nor address, nor phone number.
/// Instead, the user can enter a quote, a small phrase that they like, or something meaningless to others, but unique to them.
/// Please be aware that other users who are sharing the same device, will be able to see this phrase.
pub security_txt: String, pub security_txt: String,
/// chose a PIN code.
/// We recommend the user to choose a PIN code they already know very well (unlock phone, credit card).
/// The PIN and the rest of the Wallet will never be sent to NextGraph or any other third party (check the source code if you don't believe us).
/// It cannot be a series like 1234 or 8765. The same digit cannot repeat more than once. By example 4484 is invalid.
/// Try to avoid birth date, last digits of phone number, or zip code for privacy concern
pub pin: [u8; 4], pub pin: [u8; 4],
/// For now, only 9 is supported. 12 and 15 are planned.
/// A value of 0 will deactivate the pazzle mechanism on this Wallet, and only the mnemonic could be used to open it.
pub pazzle_length: u8, pub pazzle_length: u8,
#[zeroize(skip)] #[zeroize(skip)]
/// Not implemented yet. Will send the bootstrap to our cloud servers, if needed
pub send_bootstrap: bool, pub send_bootstrap: bool,
#[zeroize(skip)] #[zeroize(skip)]
/// Not implemented yet. Will send an encrypted Wallet file to our cloud servers, if needed. (and no, it does not contain the user's pazzle nor PIN)
pub send_wallet: bool, pub send_wallet: bool,
#[zeroize(skip)] #[zeroize(skip)]
/// Do you want a binary file containing the whole Wallet ?
pub result_with_wallet_file: bool, pub result_with_wallet_file: bool,
#[zeroize(skip)] #[zeroize(skip)]
/// Should the wallet be saved locally on disk, by the LocalBroker. It will not work on a in-memory LocalBroker, obviously.
pub local_save: bool, pub local_save: bool,
#[zeroize(skip)] #[zeroize(skip)]
/// What Broker Server to contact when there is internet and we want to sync.
pub core_bootstrap: BootstrapContentV0, pub core_bootstrap: BootstrapContentV0,
#[zeroize(skip)] #[zeroize(skip)]
/// What is the registration code at that Broker Server. Only useful the first time you connect to the Server.
/// Can be None the rest of the time, or if your server does not need an Invitation.
pub core_registration: Option<[u8; 32]>, pub core_registration: Option<[u8; 32]>,
#[zeroize(skip)] #[zeroize(skip)]
/// Bootstrap of another server that you might use in order to connect to NextGraph network. It can be another interface on the same `core` server.
pub additional_bootstrap: Option<BootstrapContentV0>, pub additional_bootstrap: Option<BootstrapContentV0>,
} }
@ -1124,25 +1171,32 @@ impl CreateWalletV0 {
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)] #[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize)]
pub struct CreateWalletResultV0 { pub struct CreateWalletResultV0 {
#[zeroize(skip)] #[zeroize(skip)]
/// The encrypted form of the Wallet object that was created.
/// basically the same as what the file contains.
pub wallet: Wallet, pub wallet: Wallet,
#[serde(skip)] #[serde(skip)]
/// The private key of the Wallet. Used for signing the wallet and other internal purposes.
/// it is contained in the opened wallet. No need to save it anywhere.
pub wallet_privkey: PrivKey, pub wallet_privkey: PrivKey,
#[serde(with = "serde_bytes")] #[serde(with = "serde_bytes")]
#[zeroize(skip)] #[zeroize(skip)]
/// The binary file that can be saved to disk and given to the user
pub wallet_file: Vec<u8>, pub wallet_file: Vec<u8>,
/// randomly generated pazzle
pub pazzle: Vec<u8>, pub pazzle: Vec<u8>,
/// randomly generated mnemonic. It is an alternate way to open the wallet.
/// A BIP39 list of 12 words. We argue that the Pazzle is easier to remember than this.
pub mnemonic: [u16; 12], pub mnemonic: [u16; 12],
#[zeroize(skip)] #[zeroize(skip)]
/// a string identifying uniquely the wallet
pub wallet_name: String, pub wallet_name: String,
#[zeroize(skip)] /// newly created Client that uniquely identifies the device where the wallet has been created.
pub peer_id: PubKey,
pub peer_key: PrivKey,
#[zeroize(skip)]
pub nonce: u64,
pub client: ClientV0, pub client: ClientV0,
#[zeroize(skip)] #[zeroize(skip)]
/// UserId of the "personal identity" of the user
pub user: PubKey, pub user: PubKey,
#[zeroize(skip)] #[zeroize(skip)]
/// is this an in_memory wallet that should not be saved to disk by the LocalBroker?
pub in_memory: bool, pub in_memory: bool,
} }

@ -14,9 +14,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -14,9 +14,9 @@ This repository is in active development at [https://git.nextgraph.org/NextGraph
## NextGraph ## NextGraph
> NextGraph brings about the convergence between P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs. > NextGraph brings about the convergence of P2P and Semantic Web technologies, towards a decentralized, secure and privacy-preserving cloud, based on CRDTs.
> >
> This open source ecosystem provides solutions for end-users and software developers alike, wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers. > This open source ecosystem provides solutions for end-users (a platform) and software developers (a framework), wishing to use or create **decentralized** apps featuring: **live collaboration** on rich-text documents, peer to peer communication with **end-to-end encryption**, offline-first, **local-first**, portable and interoperable data, total ownership of data and software, security and privacy. Centered on repositories containing **semantic data** (RDF), **rich text**, and structured data formats like **JSON**, synced between peers belonging to permissioned groups of users, it offers strong eventual consistency, thanks to the use of **CRDTs**. Documents can be linked together, signed, shared securely, queried using the **SPARQL** language and organized into sites and containers.
> >
> More info here [https://nextgraph.org](https://nextgraph.org) > More info here [https://nextgraph.org](https://nextgraph.org)

@ -47,7 +47,7 @@ pub(crate) struct Cli {
#[arg(long, requires("core"))] #[arg(long, requires("core"))]
pub core_with_clients: bool, pub core_with_clients: bool,
/// Quick config to forward all requests to another BROKER. format is "[DOMAIN/IP:PORT]@PEER_ID". An IPv6 should be encased in square brackets [IPv6] and the whole option should be between double quotes. Port defaults to 80 for IPs and 443 for domains /// Quick config to forward all requests to another BROKER. format is "[DOMAIN/IP:PORT]@PEER_ID". An IPv6 should be encased in square brackets `[IPv6]` and the whole option should be between double quotes. Port defaults to 80 for IPs and 443 for domains
#[arg( #[arg(
short, short,
long, long,

@ -49,12 +49,16 @@ use regex::Regex;
//For windows: {846EE342-7039-11DE-9D20-806E6F6E6963} //For windows: {846EE342-7039-11DE-9D20-806E6F6E6963}
//For the other OSes: en0 lo ... //For the other OSes: en0 lo ...
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
lazy_static! { lazy_static! {
#[doc(hidden)]
static ref RE_INTERFACE: Regex = Regex::new(r"^([0-9a-z]{2,16})(\:\d{1,5})?$").unwrap(); static ref RE_INTERFACE: Regex = Regex::new(r"^([0-9a-z]{2,16})(\:\d{1,5})?$").unwrap();
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
lazy_static! { lazy_static! {
#[doc(hidden)]
static ref RE_INTERFACE: Regex = Regex::new( static ref RE_INTERFACE: Regex = Regex::new(
r"^(\{[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}\})(\:\d{1,5})?$" r"^(\{[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}\})(\:\d{1,5})?$"
) )
@ -62,6 +66,7 @@ lazy_static! {
} }
lazy_static! { lazy_static! {
#[doc(hidden)]
static ref RE_IPV6_WITH_PORT: Regex = static ref RE_IPV6_WITH_PORT: Regex =
Regex::new(r"^\[([0-9a-fA-F:]{3,39})\](\:\d{1,5})?$").unwrap(); Regex::new(r"^\[([0-9a-fA-F:]{3,39})\](\:\d{1,5})?$").unwrap();
} }
@ -944,6 +949,7 @@ async fn main_inner() -> Result<(), ()> {
}; };
overlays_config.forward = vec![BrokerServerV0 { overlays_config.forward = vec![BrokerServerV0 {
server_type, server_type,
can_verify: false,
peer_id, peer_id,
}]; }];
} }

Loading…
Cancel
Save