Giter Club home page Giter Club logo

rod's Introduction

Rod

Rust Object Database.

The decentralized social networking application Iris-messenger syncs over Rod peers by default.

Use

Install Rust first.

Install & run

cargo install rod
rod start

Library

use rod::{Node, Config, Value};
use rod::adapters::*;

#[tokio::main]
async fn main() {
    let config = Config::default();
    let ws_client = OutgoingWebsocketManager::new(
        config.clone(),
        vec!["ws://localhost:4944/ws".to_string()],
    );
    let mut db = Node::new_with_config(config.clone(), vec![], vec![Box::new(ws_client)]);

    let mut sub = db.get("greeting").on();
    db.get("greeting").put("Hello World!".into());
    if let Value::Text(str) = sub.recv().await.unwrap() {
        assert_eq!(&str, "Hello World!");
        println!("{}", &str);
    }
    db.stop();
}

Status

15/5/2022:

  • Basic
  • CLI for running the server
  • Incoming websockets
  • Outgoing websockets (env PEERS=wss://some-server-url.herokuapp.com/ws)
  • Multicast (currently size limited to 65KB โ€” large photos in messages will not sync over it)
  • In-memory storage
  • TLS support (env CERT_PATH and KEY_PATH)
  • Advanced deduplication of network messages
  • Publish & subscribe (network messages only relayed to relevant peers)
  • Disk storage (sled.rs)
  • Hash verification for content-addressed data (db.get('#').get(data_hash).put(data))
  • Signature verification of user data (db.get('~' + pubkey).get('profile') ...)
  • Encryption & decryption (usually not needed on the server, but used on the client side in js, like iris private messaging)

Issues

  • Multicast doesn't relay large messages like Iris posts with photos

Develop

cargo install cargo-watch
RUST_LOG=debug cargo watch -x 'run -- start'
cargo test

Watch for code changes and re-run tests that contain the word "stats":

RUST_LOG=debug cargo watch -x 'test stats'
cargo bench

Run on Heroku

heroku create --buildpack emk/rust
git push heroku master

or:

Deploy

rod's People

Contributors

esomore avatar gabriel-v avatar mmalmi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rod's Issues

Is it possible to do this with gun-rs?

I want to make a collaborative, crowd-sourced, distributed database of metadata, something like a distributed musicbrainz. When a user changes his metadata the change can be approved by voting on it by the other users, and when changes are approved they propagate to the other databases.

Motivation

What was your motivation to implement gun in Rust?
Maybe you can add a short sentence in the README.

Stats endpoint is not showing data and not sure node is peering

Running rod node on https://cosmic.rocks/stats

CleanShot 2022-07-18 at 12 21 53@2x

running with the following env vars:

env:
        - name: PEERS
          value: wss://gun-rs.iris.to/gun,wss://gun-us.herokuapp.com/gun
        - name: RUST_LOG
          value: debug

logs:

[2022-07-18T19:27:55Z INFO  rod::adapters::ws_conn] WsConn starting
[2022-07-18T19:27:55Z DEBUG rod::adapters::ws_conn] ws_conn in {"#":"LaZLAY3UnNEPjX0n","dam":"hi"}
[2022-07-18T19:27:55Z DEBUG rod::router] incoming message {"#":"LaZLAY3UnNEPjX0n","dam":"hi"}
[2022-07-18T19:27:55Z DEBUG rod::message] get node_id ~Eg13rRFEo6SfRJvOJQBMG_hTzHzuU2bLRU-dxvfDT6Q.C8ogs_7SHyFdjgGXeMhp8ugI8J0zsR8RwzuI6xJO_BA
[2022-07-18T19:27:55Z DEBUG rod::adapters::ws_conn] ws_conn in {"get":{"#":"~Eg13rRFEo6SfRJvOJQBMG_hTzHzuU2bLRU-dxvfDT6Q.C8ogs_7SHyFdjgGXeMhp8ugI8J0zsR8RwzuI6xJO_BA"},"#":"9zViFZ3Ei"}
[2022-07-18T19:27:55Z DEBUG rod::router] incoming message {"get":{"#":"~Eg13rRFEo6SfRJvOJQBMG_hTzHzuU2bLRU-dxvfDT6Q.C8ogs_7SHyFdjgGXeMhp8ugI8J0zsR8RwzuI6xJO_BA"},"#":"9zViFZ3Ei"}
[2022-07-18T19:27:55Z DEBUG rod::router] actor:XLyCBxqpa7LfnFG7cDHNIM2PtNWBqyc0 subscribed to ~Eg13rRFEo6SfRJvOJQBMG_hTzHzuU2bLRU-dxvfDT6Q.C8ogs_7SHyFdjgGXeMhp8ugI8J0zsR8RwzuI6xJO_BA
[2022-07-18T19:27:55Z DEBUG rod::router] send to server peer
[2022-07-18T19:27:55Z DEBUG rod::router] sent get to a random sample of subscribers of size 0
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] SledStorage incoming message Get(Get { id: "9zViFZ3Ei", from: Addr { id: "XLyCBxqpa7LfnFG7cDHNIM2PtNWBqyc0", sender: UnboundedSender { chan: Tx { inner: Chan { tx: Tx { block_tail: 0x7ff16c0f11c0, tail_position: 0 }, semaphore: 0, rx_waker: AtomicWaker, tx_count: 11, rx_fields: "..." } } } }, recipients: None, node_id: "~Eg13rRFEo6SfRJvOJQBMG_hTzHzuU2bLRU-dxvfDT6Q.C8ogs_7SHyFdjgGXeMhp8ugI8J0zsR8RwzuI6xJO_BA", checksum: None, child_key: None, json_str: Some("{\"get\":{\"#\":\"~Eg13rRFEo6SfRJvOJQBMG_hTzHzuU2bLRU-dxvfDT6Q.C8ogs_7SHyFdjgGXeMhp8ugI8J0zsR8RwzuI6xJO_BA\"},\"#\":\"9zViFZ3Ei\"}") })
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] have not ~Eg13rRFEo6SfRJvOJQBMG_hTzHzuU2bLRU-dxvfDT6Q.C8ogs_7SHyFdjgGXeMhp8ugI8J0zsR8RwzuI6xJO_BA
[2022-07-18T19:27:55Z DEBUG rod::message] valid sig
[2022-07-18T19:27:55Z DEBUG rod::message] valid sig
[2022-07-18T19:27:55Z DEBUG rod::adapters::ws_conn] ws_conn in {"put":{"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI":{"_":{"#":"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI",">":{"activity":1658172475585.001}},"activity":"{\":\":{\"#\":\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity\"},\"~\":\"ArVHRYYfnwow5kTto8gFqRTZ30b5Dx67TebR5Wymc1/bkmKl6Yr+UoP4quHLq9Dz5bNYiV9e83aCE+UFlxuNtg==\"}"},"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity":{"_":{"#":"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity",">":{"status":1658172475585.001,"time":1658172475585.001}},"status":"{\":\":\"online\",\"~\":\"s+g7KzJ6SD9hbi6qpWSiJLyafEKPrlxc2fS2y1ymMCbwmfjqsE4EzKRXiTkTBIuoUCX1QDsxk/ZWE+bW33D2Fw==\"}","time":"{\":\":\"2022-07-18T19:27:55.585Z\",\"~\":\"OglRdaCIZfbs5TCSyU8zo6v4SdkJPEycfwDYI7I+q7SYoHr6ZehnB8MVuylv7WAiQU17RdIovqFtw44v7ll5zw==\"}"}},"#":"5aJIIpVvw"}
[2022-07-18T19:27:55Z DEBUG rod::router] incoming message {"put":{"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI":{"_":{"#":"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI",">":{"activity":1658172475585.001}},"activity":"{\":\":{\"#\":\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity\"},\"~\":\"ArVHRYYfnwow5kTto8gFqRTZ30b5Dx67TebR5Wymc1/bkmKl6Yr+UoP4quHLq9Dz5bNYiV9e83aCE+UFlxuNtg==\"}"},"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity":{"_":{"#":"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity",">":{"status":1658172475585.001,"time":1658172475585.001}},"status":"{\":\":\"online\",\"~\":\"s+g7KzJ6SD9hbi6qpWSiJLyafEKPrlxc2fS2y1ymMCbwmfjqsE4EzKRXiTkTBIuoUCX1QDsxk/ZWE+bW33D2Fw==\"}","time":"{\":\":\"2022-07-18T19:27:55.585Z\",\"~\":\"OglRdaCIZfbs5TCSyU8zo6v4SdkJPEycfwDYI7I+q7SYoHr6ZehnB8MVuylv7WAiQU17RdIovqFtw44v7ll5zw==\"}"}},"#":"5aJIIpVvw"}
[2022-07-18T19:27:55Z DEBUG rod::router] sent put to 1 subscribers
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] SledStorage incoming message Put(Put { id: "5aJIIpVvw", from: Addr { id: "XLyCBxqpa7LfnFG7cDHNIM2PtNWBqyc0", sender: UnboundedSender { chan: Tx { inner: Chan { tx: Tx { block_tail: 0x7ff16c0f11c0, tail_position: 0 }, semaphore: 0, rx_waker: AtomicWaker, tx_count: 12, rx_fields: "..." } } } }, recipients: None, in_response_to: None, updated_nodes: {"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI": {"activity": NodeData { value: Text("{\":\":{\"#\":\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity\"},\"~\":\"ArVHRYYfnwow5kTto8gFqRTZ30b5Dx67TebR5Wymc1/bkmKl6Yr+UoP4quHLq9Dz5bNYiV9e83aCE+UFlxuNtg==\"}"), updated_at: 1658172475585.001 }}, "~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity": {"status": NodeData { value: Text("{\":\":\"online\",\"~\":\"s+g7KzJ6SD9hbi6qpWSiJLyafEKPrlxc2fS2y1ymMCbwmfjqsE4EzKRXiTkTBIuoUCX1QDsxk/ZWE+bW33D2Fw==\"}"), updated_at: 1658172475585.001 }, "time": NodeData { value: Text("{\":\":\"2022-07-18T19:27:55.585Z\",\"~\":\"OglRdaCIZfbs5TCSyU8zo6v4SdkJPEycfwDYI7I+q7SYoHr6ZehnB8MVuylv7WAiQU17RdIovqFtw44v7ll5zw==\"}"), updated_at: 1658172475585.001 }}}, checksum: None, json_str: Some("{\"put\":{\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI\":{\"_\":{\"#\":\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI\",\">\":{\"activity\":1658172475585.001}},\"activity\":\"{\\\":\\\":{\\\"#\\\":\\\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity\\\"},\\\"~\\\":\\\"ArVHRYYfnwow5kTto8gFqRTZ30b5Dx67TebR5Wymc1/bkmKl6Yr+UoP4quHLq9Dz5bNYiV9e83aCE+UFlxuNtg==\\\"}\"},\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity\":{\"_\":{\"#\":\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity\",\">\":{\"status\":1658172475585.001,\"time\":1658172475585.001}},\"status\":\"{\\\":\\\":\\\"online\\\",\\\"~\\\":\\\"s+g7KzJ6SD9hbi6qpWSiJLyafEKPrlxc2fS2y1ymMCbwmfjqsE4EzKRXiTkTBIuoUCX1QDsxk/ZWE+bW33D2Fw==\\\"}\",\"time\":\"{\\\":\\\":\\\"2022-07-18T19:27:55.585Z\\\",\\\"~\\\":\\\"OglRdaCIZfbs5TCSyU8zo6v4SdkJPEycfwDYI7I+q7SYoHr6ZehnB8MVuylv7WAiQU17RdIovqFtw44v7ll5zw==\\\"}\"}},\"#\":\"5aJIIpVvw\"}") })
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] saving k-v ~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity: {"status": NodeData { value: Text("{\":\":\"online\",\"~\":\"s+g7KzJ6SD9hbi6qpWSiJLyafEKPrlxc2fS2y1ymMCbwmfjqsE4EzKRXiTkTBIuoUCX1QDsxk/ZWE+bW33D2Fw==\"}"), updated_at: 1658172475585.001 }, "time": NodeData { value: Text("{\":\":\"2022-07-18T19:27:55.585Z\",\"~\":\"OglRdaCIZfbs5TCSyU8zo6v4SdkJPEycfwDYI7I+q7SYoHr6ZehnB8MVuylv7WAiQU17RdIovqFtw44v7ll5zw==\"}"), updated_at: 1658172475585.001 }}
[2022-07-18T19:27:55Z DEBUG sled::meta] allocated pid 11 for root of new_tree [126, 107, 74, 98, 117, 105, 107, 69, 100, 50, 84, 97, 84, 55, 51, 75, 55, 73, 106, 79, 65, 121, 71, 113, 82, 101, 101, 49, 108, 101, 88, 45, 66, 49, 108, 85, 52, 106, 54, 74, 80, 89, 51, 69, 46, 102, 78, 121, 118, 108, 54, 80, 107, 105, 112, 78, 81, 68, 101, 99, 106, 110, 117, 121, 118, 106, 80, 112, 79, 73, 106, 87, 122, 98, 80, 71, 54, 110, 110, 45, 119, 122, 89, 98, 69, 45, 100, 73, 47, 97, 99, 116, 105, 118, 105, 116, 121]
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] ~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity.status new priority 103
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] old 0000000001 new 0000001031
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] ~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity.time new priority 103
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] old 0000000002 new 0000001032
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] saving k-v ~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI: {"activity": NodeData { value: Text("{\":\":{\"#\":\"~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI/activity\"},\"~\":\"ArVHRYYfnwow5kTto8gFqRTZ30b5Dx67TebR5Wymc1/bkmKl6Yr+UoP4quHLq9Dz5bNYiV9e83aCE+UFlxuNtg==\"}"), updated_at: 1658172475585.001 }}
[2022-07-18T19:27:55Z DEBUG sled::meta] allocated pid 13 for root of new_tree [126, 107, 74, 98, 117, 105, 107, 69, 100, 50, 84, 97, 84, 55, 51, 75, 55, 73, 106, 79, 65, 121, 71, 113, 82, 101, 101, 49, 108, 101, 88, 45, 66, 49, 108, 85, 52, 106, 54, 74, 80, 89, 51, 69, 46, 102, 78, 121, 118, 108, 54, 80, 107, 105, 112, 78, 81, 68, 101, 99, 106, 110, 117, 121, 118, 106, 80, 112, 79, 73, 106, 87, 122, 98, 80, 71, 54, 110, 110, 45, 119, 122, 89, 98, 69, 45, 100, 73]
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] ~kJbuikEd2TaT73K7IjOAyGqRee1leX-B1lU4j6JPY3E.fNyvl6PkipNQDecjnuyvjPpOIjWzbPG6nn-wzYbE-dI.activity new priority 103
[2022-07-18T19:27:55Z DEBUG rod::adapters::sled_storage] old 0000000003 new 0000001033
[2022-07-18T19:27:55Z DEBUG rod::message] get node_id ~ms8w_jIFxdB-WVv3Ds7YfOtK_EfTQoW4vBnzWhVwOIc.JwgUO4mMKT42NhWh9MmVFCIeRGBqp0PmQ9ck2scU3oQ
[2022-07-18T19:27:55Z DEBUG rod::adapters::ws_conn] ws_conn in {"get":{"#":"~ms8w_jIFxdB-WVv3Ds7YfOtK_EfTQoW4vBnzWhVwOIc.JwgUO4mMKT42NhWh9MmVFCIeRGBqp0PmQ9ck2scU3oQ"},"#":"kbW9T6RvM"}
[2022-07-18T19:27:55Z DEBUG rod::router] incoming message {"get":{"#":"~ms8w_jIFxdB-WVv3Ds7YfOtK_EfTQoW4vBnzWhVwOIc.JwgUO4mMKT42NhWh9MmVFCIeRGBqp0PmQ9ck2scU3oQ"},"#":"kbW9T6RvM"}
[2022-07-18T19:27:55Z DEBUG rod::router] actor:XLyCBxqpa7LfnFG7cDHNIM2PtNWBqyc0 subscribed to ~ms8w_jIFxdB-WVv3Ds7YfOtK_EfTQoW4vBnzWhVwOI

Plan for implementing encryption?

Iโ€™m wondering if there is a plan in place for implementing encryption (and using it for Iris messaging).

I know Rust has a couple really mature encryption crates. Have they been assessed or chosen for this yet?

Idea: Couple this with rust-lightning?

I noticed that this new Lightning SDK is agnostic about storage. https://lightningdevkit.org/

Our API's let you choose how to backup channel state. This gives you multi-wallet device access and lets you save state locally, to the cloud or an alternative backup scheme.

Wondering if it would make sense to combine gun-rs with https://github.com/lightningdevkit as one deployable solution for messaging/social + storage and money. Iris would provide the context around the economy because you could then also use the Iris pubkey as the identifier for who to open channels with as well (although Iris need not be included to just leverage gun-rs as Lightning state storage).

send before subscribe

question - in node.on() the subscribed receiver is created after message send, here:

https://github.com/mmalmi/rod/blob/aa987583d9ea7d47157fbb3ed69bc3dc59e503dc/src/node.rs#L198C23-L202

if let Some(router) = self.router.read().unwrap().clone() {
    let _ = router.send(Message::Get(get));
}
self.on_sender.subscribe()

The broadcast docs even put in bold the word after for some reason

I know router.send will reach more indirection, more channels, more messages - but it's theoretically possible the race flips and we lose the first value, right?

Any reason not to flip the subscribe before the send?

let subscriber = self.on_sender.subscribe();
if let Some(router) = self.router.read().unwrap().clone() {
    let _ = router.send(Message::Get(get));
}
subscriber

rust library: how to get node value?

In rust, using this as a library, we got get(), put(), on(), map(), but no val().

There's no way to get the value from a node, other than subscribing to it and overwriting something else, thus losing the initial value.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.