RetroShare / RetroShare

RetroShare is a Free and Open Source cross-platform, Friend-2-Friend and secure decentralised communication platform.
https://retroshare.cc/
Other
1.72k stars 273 forks source link

About supernode for Retroshare #1599

Open xn-zhao opened 5 years ago

xn-zhao commented 5 years ago

Hi everyone, I have a question related to RS supernode: How can we implement a function for increasing the receiving connections, for improving the connectivities of the RS network? I found the v0.3.x but it was done 11 years ago and even don't see the .pro file for Qt project. Who can give a good idea about this? I want to create a supernode that receive any connections with any cert. But I don't know the v0.3.x is running or stable? Is TOR good for network issue now and a solution for this? Any help? Thank you.

csoler commented 5 years ago

Just create a chat server. That's pretty much what you need. The code needs to be compiled without a few extra qmake options. That's all. See the doc in the chat server sub- repository.

xn-zhao commented 5 years ago

thanks a lot, Csoler!

xn-zhao commented 5 years ago

Hi again, In our chat server, we have a directory of uploaded certificates, then chat server will parse these certificates to sslid and pgpid to addFriend. But if we want to make my server like this:

I tried to do this by following:

I don't know it it ok? In my case, after that, client and server still not see and connect together (and see online). Is there any problem in this flow? Any suggestion for it? Thank you.

sehraf commented 5 years ago

There is AAM / Auto Accept Mode, a tiny script that parses RS' output for incoming friend requests and adds them. Might be a start. Search for AAM, there is a channel and a forum with the script.

xn-zhao commented 5 years ago

Hi Sehraf, I tried to search AAM and read code, but could not find the related thing. There is a accept friend request (with button) but it is on ConnectFriendWizard. And this is retroshare-gui only.

G10h4ck commented 5 years ago

Also take a look at this https://gitlab.com/elRepo.io/Tier1 it is the code of http://micheleangiolillo.elrepo.io/ it uses JSON API to control retroshare-service and add the certificates. One can also remotely call that specific call in JSON API to add it automatically without the user going through the webpage

sehraf commented 5 years ago
#!/usr/bin/ruby

# Upon restart ALL peers get wiped except core AAN's (for now)
# Then they build up again by AAM
# Copy this file to /usr/local/bin/rs_auto_accept and make it executable
# with chmod 555 /usr/local/bin/rs_auto_accept
# Run the wrapper with:
# nohup retroshare -d 0 | rs_auto_accept

require 'rest-client'
require 'json'

# *** ADJUST YOUR RS JSON token user:password ***
x_user='user'
x_password='password'
x_server=ARGV[0]
x_port=9092
x_group_id_1='00000000000000000000000000000004'
x_fake_ssl_id='42eedef688cda0b38bb19c4078d99837'
x_delay=0.3
x_flags=0
x_connectivity=nil
# *** ADJUST to the max peers you want connected ***
x_max_peers=100
x_gets='not_nil'

if x_server==nil

  puts 'Missing server address - exiting RetroShare'
  system('killall -s 9 retroshare')
  puts 'RetroShare terminated... '
  exit
  x_connectivity=' '
  x_gets=nil
else
end

puts 'Waiting for RetroShare JSON service...'
while x_connectivity==nil

  begin

    x_connectivity=RestClient.get("http://#{x_user}:#{x_password}@#{x_server}:#{x_port}/"<<'rsPeers/getFriendList').body
  rescue Errno::ECONNREFUSED
  rescue RestClient::Unauthorized

    puts 'Wrong user:password token... '
    system('killall -s 9 retroshare')
    puts 'RetroShare terminated... '
    exit
  end

  sleep 1
end

puts 'Running RetroShare wrapper... '
while x_gets!=nil

  x_gets=$stdin.gets

  if x_gets!=nil

    if x_gets.index('   GPG id = ')==0 && x_gets.index('   GPG id = 0000000000000000')==nil && \
       (JSON.parse(RestClient.get("http://#{x_user}:#{x_password}@#{x_server}:#{x_port}/"<<'rsPeers/getOnlineList').body)["sslIds"]).size<x_max_peers

      x_gpg_id=x_gets.split('= ')[1].chop
      puts Time.now.to_s+' ... new PGP id detected ... '+x_gpg_id
      RestClient.post("http://#{x_user}:#{x_password}@#{x_server}:#{x_port}/"<<'rsPeers/removeFriendLocation','{"sslId":"'+x_fake_ssl_id+'"}')
      sleep x_delay
      RestClient.post("http://#{x_user}:#{x_password}@#{x_server}:#{x_port}/"<<'rsPeers/addFriend','{"sslId":"'+x_fake_ssl_id+'","gpgId":"'+x_gpg_id+'","flags": '+x_flags.to_s+'}')
      sleep x_delay
      RestClient.post("http://#{x_user}:#{x_password}@#{x_server}:#{x_port}/rsPeers/assignPeerToGroup", '{"groupId":"'+x_group_id_1+'","peerId":"'+x_gpg_id+'","assign":true}')
      sleep x_delay
      RestClient.post("http://#{x_user}:#{x_password}@#{x_server}:#{x_port}/"<<'rsPeers/removeFriendLocation','{"sslId":"'+x_fake_ssl_id+'"}')
      sleep x_delay
    end
  end
end

puts 'RetroShare exiting... '
sleep 4
system('killall retroshare')
sleep 6
system('killall -s 9 retroshare')
puts 'RetroShare terminated... '
xn-zhao commented 5 years ago

Thanks Sehraf, But it seems my task different from yours. We want to setup a supernode (that was available from v0.3.x), that it can receive many ssl connections, then it can get the pgp public key then add to the keyring. But this thing maybe go against with the RS rule now, that every node need to know connection from whom, at least RS will check the 'whitelist' before it allow the connection to continue.

So in v0.3.x there is a supernode that can create the fake connection on the fly.
We want to create again this supernode, and want to get the peer's PGP public key when that peer tries to connect to the supernode in the first connection. We want to save a list of peer certificates that tries to connect to supernode.

Another question that I want to ask: IS there any way to add the PGP public key to the ssl connection info in our code when the peer tries to connect to another peer (or supernode)? Because I saw that, we can send the PGP Id in the SSL connection using OpenSSL lib. (I don't know I am right?) When one peer (A) import a friend (B) certificate, in B side we can get the A's PGPId. How can we do it the same for GPG public key? Any idea? Thank you.

sehraf commented 5 years ago

that it can receive many ssl connections, then it can get the pgp public key then add to the keyring.

so you want to add the pgp cert from incoming connection attempts to your keyring but not add them as friends? (I understand super node as a node that accepts any connections attempt, i don't know RS v0.3.x)

unsenep2pdev commented 5 years ago

Hi Sehraf,

We are helping Unseen.is (centralized solution) to decentralized messenger/email/forum. Most of Unseen Users aren't technical enough to play around with Certificate Exchanges. In order to migrate to decentralized Messenger system like Retroshare Communicator, we need a more user-friendly app. We are facing many challenges with RS solutions.

  1. UX/UI are so different from Unseen.is. Users want to have peer-to-peer solution, but they don't understand the challenges and complicated steps to use it. We spend a great length to adjust GUI to look like Unseen.is.

  2. Finding/discovery a friend is taking much longer than expected. Sometime, it takes up to 30minutes if you cannot reach p2p to each other. This is a challenge for us at the moment. Any help or suggestion to improve the connection between peers with or without NAT would be a great help. Such as setting up relay server or proxyserver, etc.

  3. Bootstrap to P2P Network (Unseen), we would like all Unseen Users can search each others and add friend. So will also host some forum, channel, and discussion in Unseen P2P Network. We are looking a supernode or something a like to help auto accept connection from any client on the network.

I think we will follow your script to try to write inside the C++ Retroshare code as follow:

int pqissllistenbase::Extract_Failed_SSL_Certificate(const IncomingSSLInfo& info) { pqioutput(PQL_DEBUG_BASIC, pqissllistenzone, "pqissllistenbase::Extract_Failed_SSL_Certificate()"); std::cerr << "pqissllistenbase::Extract_Failed_SSL_Certificate() FAILED CONNECTION due to security!"; std::cerr << std::endl; // Get the Peer Certificate.... X509 peercert = SSL_get_peer_certificate(info.ssl); std::cerr << "Extract_Failed_SSL_Certificate: " << std::endl; std::cerr << " SSL = " << (void)info.ssl << std::endl; std::cerr << " GPG id = " << info.gpgid << std::endl; std::cerr << " SSL id = " << info.sslid << std::endl; std::cerr << " SSL cn = " << info.sslcn << std::endl; std::cerr << " addr+p = " << sockaddr_storage_tostring(info.addr) << std::endl; if (peercert == NULL) { std::string out; out += "pqissllistenbase::Extract_Failed_SSL_Certificate() from: "; out += sockaddr_storage_tostring(info.addr); out += " ERROR Peer didn't give Cert!"; std::cerr << out << std::endl; AuthSSL::getAuthSSL()->FailedCertificate(peercert, info.gpgid,info.sslid,info.sslcn,info.addr, true); pqioutput(PQL_WARNING, pqissllistenzone, out); return -1;

}
  pqioutput(PQL_DEBUG_BASIC, pqissllistenzone,
  "pqissllistenbase::Extract_Failed_SSL_Certificate() Have Peer Cert - Registering");
{
    std::string out;
    out += "pqissllistenbase::Extract_Failed_SSL_Certificate() from: ";
    out += sockaddr_storage_tostring(info.addr);
    out += " Passing Cert to AuthSSL() for analysis";
    std::cerr << out << std::endl;
    pqioutput(PQL_WARNING, pqissllistenzone, out);
    std::cerr << out << std::endl;

}

// add friend to this node if(info.gpgid != '0000000000000000' && info.sslid != '00000000000000000000000000000000') { std::cerr << " We are adding GPG id = " << info.gpgid << std::endl; std::cerr << " We are adding SSL id = " << info.sslid << std::endl; rsPeers->addFriend(info.gpgid,info.sslid); } // save certificate... (and ip locations) // false for outgoing.... AuthSSL::getAuthSSL()->FailedCertificate(peercert, info.gpgid,info.sslid,info.sslcn,info.addr, true); return 1; }

================= This add friend doesn't work if a newly create node try to connect to "supernode" for the first time. This work if somehow it has connected to a friend, and that friend is already connect to supernode.

Any help will be greatly appreciate it. Unseen Team

G10h4ck commented 5 years ago

I think the more practical approach to this topic is the one developed by elrepo.io, they expose the JSON API call to add friends publicly on their "supernodes", the app itself then remotely call it when it first start, so the user doesn't need to exchange it manually at first time, look at this https://gitlab.com/elRepo.io/Tier1/blob/master/nginx_site.conf this way no modificaiton to libretroshare code is needed

csoler commented 5 years ago

To help connexions you may use rs over tor. It finds friends within seconds.

unsenep2pdev commented 5 years ago

I think the more practical approach to this topic is the one developed by elrepo.io, they expose the JSON API call to add friends publicly on their "supernodes", the app itself then remotely call it when it first start, so the user doesn't need to exchange it manually at first time, look at this https://gitlab.com/elRepo.io/Tier1/blob/master/nginx_site.conf this way no modificaiton to libretroshare code is needed

We have tried to run ansible for this Tier1 role. We have some issue with PGP Keypair. We tried to expert GPG from RS Profile Manager. We are using PGP commandline to generate new keypair. It's always complaining not a valid keypair. Any suggestion on how to generate a valid keypair?

Below is a response error: changed: [64.62.233.37] => { "access_control_allow_headers": "Authorization,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range", "access_control_allow_methods": "GET, POST, OPTIONS", "access_control_allow_origin": "*", "access_control_expose_headers": "Content-Length,Content-Range", "cache_control": "no-cache", "changed": true, "content": "{\n \"pgpId\": \"0000000000000000\",\n \"errorMsg\": \"PGPHandler::importKeyPair(): file does not contain a valid keypair.\",\n \"retval\": false\n}", "content_length": "143", "content_type": "text/json", "cookies": {}, "cookies_string": "", "elapsed": 0, "invocation": { "module_args": { "attributes": null, "backup": null, "body": { "data": "-----BEGIN PGP PUBLIC KEY BLOCK----- Version: OpenPGP:SDK v0.9\nxsBNBFxLlx8BCACyzicZLuBhPtrsLMn0szNgRbJY3lIFTg26De1QswAJqX+GYj8HLAThG+GKyyHDROLX0q29C5KFWq8eN/oa+3dREJttXD1OflplAuH5GSBXQlhgjn8MzQNMrjVBqBVFI7sMXKVcbMQMzsew9QtwjPSFmVoPoLYollh9Fdjw0i9JUkmkxJ4ElpJn6VU+ldG+n8AQ901xMnBQ7YBOFjcLNa2xa1UsBeM6TgRQeN+yW6Sn3Ze+RKlnk9qS1hdmXAEL/El5yRTb4MA3xR7rkELP42MKozUT1tBtM/31poTLjekIB3mZ0WAxYeblM06cmxjBQycb24GJoX2SPN/Tro1y3X7xABEBAAHNJ3VidW50dURldjAxIChHZW5lcmF0ZWQgYnkgVW5zZWVuUDJQKSA8PsLAXwQTAQIAEwUCXEuXHwkQAEPvylMZlkACGQEAAPAzB/wKlHTn7s0L6EWHPNoTuw+crZoDcwMS9xeRc6wBg2W7W+/xmH7Vh+JLkw+Aod/eOqhxOXD0mMyhRa4Z5LioC3SPvYWB2ozJBFUTiZ6zKfPs/C1mzluJEVNFKOV5+q9thlR06DNpAqLbXVyLljd5ZIq57Bor8J7acyOlAerZNWHUNFe2ipBPwTelmqLQT4zvJN+j+rpKDS1XGbvoidQdH4xUfzo57s0GqMJNumCzNg2F7hBWT2BkvvBd1RR1JDpVyzCp3xIM8WQr3Ovk37JuEv9KMstwG8j9CotfukJs2IZsb8m+7zd7oHoaZCQSnqGi9r2y7kNgkC1ne7kam2FDWVRH=e9E9 -----END PGP PUBLIC KEY BLOCK-----\n-----BEGIN PGP PRIVATE KEY BLOCK----- Version: OpenPGP:SDK v0.9\nxcL9BFxLlx8BCACyzicZLuBhPtrsLMn0szNgRbJY3lIFTg26De1QswAJqX+GYj8HLAThG+GKyyHDROLX0q29C5KFWq8eN/oa+3dREJttXD1OflplAuH5GSBXQlhgjn8MzQNMrjVBqBVFI7sMXKVcbMQMzsew9QtwjPSFmVoPoLYollh9Fdjw0i9JUkmkxJ4ElpJn6VU+ldG+n8AQ901xMnBQ7YBOFjcLNa2xa1UsBeM6TgRQeN+yW6Sn3Ze+RKlnk9qS1hdmXAEL/El5yRTb4MA3xR7rkELP42MKozUT1tBtM/31poTLjekIB3mZ0WAxYeblM06cmxjBQycb24GJoX2SPN/Tro1y3X7xABEBAAH+AwECYRiolVad9ygAAAAAAAAAADp0q2fi+vwjkEDVZR6xqA94Snm1S9AyjFuQX1NZ8rjGRqCiioGnHYQHV8E4uNk8rSdd9EkE7yqeVx+GVxvxh5UATEK2tsieqJfAH0lhqCLC7s1wU9/rqRt0OWLZkaxwX1Mupsq4EH1dFqKwQsw8kpdgsABVgIaoqhTNd4BP8NO57H0BuqnPsOtuD300r1t4vhIBSDEkklwarflzxcpljUrYjmfAAqPUMOFJV9uA1pyIyKZB2GXubiIg71pc5yhTJyVkP9bpSeXE2erIerItkdyCAdQbTZlknWTsHhQnPvALPPfoAs+obceFAB/n/q9v21upFhckC5IsRRbDi6BmiOrbPW/efFZN5f24JQXGw1Ik2CHM6YjjfIkervj0GaU+DtZ6i1EfEVY+8tML4t2TspmLujF/k0iqnUuHUO4vFSzEv6n170cB/I7ZUUnWUD87UdAd+fp9odMMBW9AIqgerRqjqQIqTi7C91XhI/RGJWKb9HTxdoONEIlUebzWRIy+vkEd1AIlloiwlLXqOVW5CyRTgOoZAjw8LZILdgLzlkHZPyeM3b3pgzwU8pXbYoY5y5vCQRBALwlY9IpQxBmsvDbOvY0ly/3Fs9IlKDooRZtIT7A3i/sMofVjDWUCDffQYzFu7K8S1M8eE7OgO5OBxqG0VGKOlSA4WHb6Xxf4Cg4KsiZzHTpxMjHgv8uyBgahDbjTSABAe2rWuBLE8RJ+u78STxclQuFiM6R/lMiNuMuP8RjpcXoSjQosMAAHravQMHrtXfU7kiQg8/swnk65xNJnrwJqJ6DwM1kNNkkOCAoeAFiTTPzZ8NmT4PkwnGrd1xD7IpshDCXsXWSm8nw7tJQzhfpMbvgu9CeT0bKyhHUdlgsALKaIsjeDjoaIzSd1YnVudHVEZXYwMSAoR2VuZXJhdGVkIGJ5IFVuc2VlblAyUCkgPD7CwF8EEwECABMFAlxLlx8JEABD78pTGZZAAhkBAADwMwf8CpR05+7NC+hFhzzaE7sPnK2aA3MDEvcXkXOsAYNlu1vv8Zh+1YfiS5MPgKHf3jqocTlw9JjMoUWuGeS4qAt0j72FgdqMyQRVE4mesynz7PwtZs5biRFTRSjlefqvbYZUdOgzaQKi211ci5Y3eWSKuewaK/Ce2nMjpQHq2TVh1DRXtoqQT8E3pZqi0E+M7yTfo/q6Sg0tVxm76InUHR+MVH86Oe7NBqjCTbpgszYNhe4QVk9gZL7wXdUUdSQ6Vcswqd8SDPFkK9zr5N+ybhL/SjLLcBvI/QqLX7pCbNiGbG/Jvu83e6B6GmQkEp6hova9su5DYJAtZ3u5GpthQ1lURw===025y -----END PGP PRIVATE KEY BLOCK-----" }, "body_format": "json", "client_cert": null, "client_key": null, "content": null, "creates": null, "delimiter": null, "dest": null, "directory_mode": null, "follow": false, "follow_redirects": "safe", "force": false, "force_basic_auth": false, "group": null, "headers": { "Content-Type": "application/json" }, "http_agent": "ansible-httpget", "method": "POST", "mode": null, "owner": null, "regexp": null, "remote_src": null, "removes": null, "return_content": true, "selevel": null, "serole": null, "setype": null, "seuser": null, "src": null, "status_code": [ 200 ], "timeout": 30, "unix_socket": null, "unsafe_writes": null, "url": "http://127.0.0.1:9092/rsAccounts/importIdentityFromString", "url_password": null, "url_username": null, "use_proxy": true, "validate_certs": true } }, "json": { "errorMsg": "PGPHandler::importKeyPair(): file does not contain a valid keypair.", "pgpId": "0000000000000000", "retval": false }, "msg": "OK (143 bytes)", "redirected": false, "status": 200, "url": "http://127.0.0.1:9092/rsAccounts/importIdentityFromString" }

G10h4ck commented 5 years ago

Looks like your keypair get mangled where read from the ansible yaml file, for example here -----BEGIN PGP PUBLIC KEY BLOCK----- Version: OpenPGP:SDK it is missing a \n between BLOCK----- Version YAML have many ways to handle multi-line strings, double check you are using the correct one (aka the one which preserves the line breaks)

unsenep2pdev commented 5 years ago

Thank you, we are able to setup several Tier1s or Supernodes to accept certificate through http restAPI. We have found out that tier1 run normal mode and RS run over TOR will have a very long time in discovery online/offline. Perhaps do TOR look for exit network gateway to normal Internet. I'll look into retroshare-service to find a way to turn hidden mode on RS, but it's not very clear if possible. Can someone share how to run RS in Retroshare-Service over Tor Network.

G10h4ck commented 5 years ago

if tor/i2p daemon are running on the default port on the machine retroshare-service will try to use them by default (only for outgoing connections toward retroshare hidden nodes), to setup a retroshare-service hidden node is a different story, haven't attempted to do that trhout the JSON API yet, so it probably would need some more work into RS code, if you find out something please share the info with us ;)

unsenep2pdev commented 4 years ago

I am able to tweak a bit of retroshare-service and TorControl module to make them work. Since, supernode is only one profile per account. So, I created hidden service outside of the HID_XXX datadirectory. You can take a look here: https://github.com/unsenep2pdev/RetroShare/tree/supernode-0.6.5-hidden/retroshare-service/src See if it's something useful to integrated to your service.

Also, anyone has suggestion how to develop offline messages for one2one chat and groupchat? I would love to hear some suggestions on this. It's possible to leverage GXS with instant chat?

G10h4ck commented 4 years ago

I am able to tweak a bit of retroshare-service and TorControl module to make them work. Since, supernode is only one profile per account. So, I created hidden service outside of the HID_XXX datadirectory. You can take a look here: https://github.com/unsenep2pdev/RetroShare/tree/supernode-0.6.5-hidden/retroshare-service/src See if it's something useful to integrated to your service.

Indeed Tor support for retroshare-service would be great AFAIU the support you wrote is based on Qt while we have been moving in a direction so retroshare-service doesn't depends on Qt (it still does but only on android), do you think it is reasonable to rewrite it to not depends on Qt ?

Also, anyone has suggestion how to develop offline messages for one2one chat and groupchat? I would love to hear some suggestions on this. It's possible to leverage GXS with instant chat?

Yeah we have been thinking on implementing that since a bunch of time, we even lay out some plan with @csoler ( @csoler do you have the link at hand ?), basically the chat service would need to b rewritten from scratch, the idea is that it should use the less costly way to deliver the message, so if possible it should first try direct delivery, if not available tunneled delivery, and if not available (the peer is not online ATM of sending) use GxsTrans, another possibility to explore is to use GXS to have a shared history archiving in chats, with the recent addition of distant sync in GXS it seems it would work well, but probably GXS API would need some love to support things such as paging or retrive messages in an interval of time without loading the entire database

csoler commented 4 years ago

It's totally possible to rewrite TorControl so that it does not depend on Qt. I'm pretty sure that there is a number of projects that would love to have this. At some point I talked with the Tor developers who told me they were interested in having this as well. @unsenep2pdev: if you want to implement this, you're welcome. I dont really have time for it now. I can however work on this after next RS release, meaning some time before end of 2019.

unsenep2pdev commented 4 years ago

I’m more interest in Store & Forward Messages distribution (offline messages). So, I’ll work on modified the chat system. Apparently, Email system is not store and forward offline. So, i’m looking into how to make them like an email (SMTP) within P2P Network.

On Sep 19, 2019, at 4:13 AM, csoler notifications@github.com wrote:

It's totally possible to rewrite TorControl so that it does not depend on Qt. I'm pretty sure that there is a number of projects that would love to have this. At some point I talked with the Tor developers who told me they were interested in having this as well. @unsenep2pdev https://github.com/unsenep2pdev: if you want to implement this, you're welcome. I dont really have time for it now. I can however work on this after next RS release, meaning some time before end of 2019.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/RetroShare/RetroShare/issues/1599?email_source=notifications&email_token=AKI7WPVKVN25MHKIQWK554DQKNND7A5CNFSM4ICHFYYKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7DDNAI#issuecomment-533083777, or mute the thread https://github.com/notifications/unsubscribe-auth/AKI7WPXIO2CPBUPBTQJ2JV3QKNND7ANCNFSM4ICHFYYA.

csoler commented 4 years ago

Current RS email system actually works offline. It uses 2 ways to send mails: tunnels (work online only) and GXS which works offline.

G10h4ck commented 4 years ago

@unsenep2pdev p3chatservice already have a very rough support sending messages asynchronously trough GxsTrans as you can see here https://github.com/RetroShare/RetroShare/blob/master/libretroshare/src/chat/p3chatservice.cc#L366 this feature is disabled at compile time only at GUI level as retroshare-gui is not able to properly handle out of order messages as it is not MVC. To enable async message sending in retroshare-gui pass this option to wmake command line CONFIG+=rs_async_chat

unsenep2pdev commented 4 years ago

Here is a test setup network: A connect B and C, B connect A & C, C connects A &B ( 3 nodes fully mesh). B node is supernode (persistent node). A is ubuntu OS, and C is Mac OSX. Sending Online A <--> C work great! Now, C is offline: A —> C, A will add to draft inbox when C is offline. Once C is online A send again. This doesn’t work if sometime later, A is also offline, when C online will not receive this email from A. C (Mac osx) —> A (offline), the message sent and without stay from draft inbox. But when A (Online), A never received message from C, even though C is still online during the time A offline and Online again. (probably bug on Mac OSX).

So store and forward email message isn’t been relay to B as cache copy of the encrypt(email). It’s that by design or bug on email system? Where as forum, channel, and other services do provide storage and forward…

On Sep 25, 2019, at 12:09 AM, csoler notifications@github.com wrote:

Current RS email system actually works offline. It uses 2 ways to send mails: tunnels (work online only) and GXS which works offline.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/RetroShare/RetroShare/issues/1599?email_source=notifications&email_token=AKI7WPQZEDQT24TQDZYY6Q3QLMFDVA5CNFSM4ICHFYYKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7Q3NVI#issuecomment-534886101, or mute the thread https://github.com/notifications/unsubscribe-auth/AKI7WPUPA5TVVLAO5MOK6LDQLMFDVANCNFSM4ICHFYYA.