Hello,
I’ve started looking into how a basic email or message-bus design could be built around the Register type, to start getting myself familiar with working with SAFE. After reading some of the safe_network
source code and making little crates that use sn_api::Safe
or sn_client::Client
to play with Registers (against local test-net), I’ve come up with some questions about the SAFE Network design and some ideas about trying to use Registers for email. First I’ll just state my questions, then describe my idea, then get to issues where the idea leads to the questions.
Questions
-
Will the Register API support private registers with write-only access for anyone or for select users?
-
Will the Register API support changing a register’s permissions policy after it’s been created?
-
Who will pay for writing an entry to a register - the owner of the register or the writing user? Will it be configurable, per register, to choose either way?
-
How will denial-of-service attacks against a register be mitigated? How will heavy legit traffic on a register be supported?
Idea
An email address is an NRS name associated with a private register, the currently-active register where new emails are to be sent to. A sender (simply a SAFE user) must have write permission on such a register (which could have a simple single blanket permission for anyone to write). (Some kind of single shared mutable persistent data structure stored on the network is needed: 1) to avoid involving additional platforms, to achieve involving only SAFE; and 2) to enable the sending of emails from an unlimited amount of random new strangers to a single advertised address, similar to traditional email. The Register type (or something built on it) seems to be the only such data type provided by SAFE.)
An email is an entry in such a register. A sender sends by writing a new entry, and a recipient receives by getting the new entries.
With register entries each having a limited size (1 KiB), a larger email will have to be stored on the network as a separate file and its register entry will point to that. The details of how an email entry is structured and serialized are unimportant for now.
An NRS name enables changing the currently-active to different registers progressively, which enables dealing with the limited capacity of registers (max 64 Ki entries) becoming full as more emails are received. (Since registers, being Merkle-DAG CRDT, can only grow (even when “deleting” an entry), some way is needed to deal with this limited capacity while keeping the email address constant.) (And of course an NRS name is also human-friendly.)
A user’s app notices when their address’s currently-active register is full and then creates a new one and changes their NRS name to point to that, as needed.
When trying to send an email, a user’s app notices when the recipient’s currently-active register is full and cannot receive (cannot have another entry added) and so must wait and try to send later when the recipient changes their address’s NRS name to point to a new register.
Senders choose how an email (an entry) links to their previous emails (entries) in the DAG, which would seem to fit various forms of conversation threading, like: starting a new subject/thread (link to none), continuing a thread (link to single), forking multiple threads from one (link to single), and merging multiple threads into one (link to multiple). (Of course a back-and-forth conversation involving multiple addresses would require other kinds of links as well.)
(I’m only focusing on what I thought of for something similar to traditional email. I have different ideas for other applications, e.g. a different design with per-sender registers for some kind of message-bus where the set of senders is prearranged or introduced by some other means.)
Issues
-
How to ensure privacy of a recipient’s register when untrusted senders can write to it? Only the owner (and additionally-permitted users, possibly) should be able to see all its entries. Each sender should only be able to know about their entries but not others’.
If a sender must also have read permission in order to write, then ensuring privacy would be complicated by requiring some kind of extra layer of encryption of entries’ contents or something. Even if that could be figured out, that would still seem undesirable because then others could see how many entries there are, their DAG links, and maybe their approximate timings of being added, and could save all the entries to be possibly cracked in the distant future.
If a sender can have write-only permission, i.e. they do not have read permission and cannot perform any read operations on these registers, then this would seem to have nice properties for ensuring privacy. Senders would only be able to know of their own entries (by recording a separate copy of them themselves). Only the recipient(s) would be able to see the entire DAG state, and senders never can and only ever know of their partial state but that seems ok because their write operations only need to be able to link to their own prior entries. I think this still satisfies the monotonic semilattice property required of a CRDT (it’s like each sender is perpetually partitioned and never gets updates from other replicas and only the recipient does).
Support for such write-only permissions for registers is not provided by the current APIs. The internal types and functions for the permissions look like this could possibly be supported. However, the
Client::write_to_register
implementation callsClient::get_register
which requires read permission, which makes it impossible to use a register created with a write-only policy. The reason it does this is to “get the causality info”, which I can see makes sense for other applications of the Register type. If a similar method was made that didn’t doget_register
but instead took aRegister
as argument (which for my idea would be used to pass a sender’s partial state), this would seem to enable write-only applications. So I’d like to ask what’s been considered in these regards?Will the Register API support private registers with write-only access for anyone or for select users?
-
How to dynamically prevent some users from sending to a register? An email-address owner might want to allow only specific users and block anyone else. It’d be nice to have this, I suppose, in addition to traditional spam filtering performed by a user’s app. We’d want the ability to dynamically change these permissions over time.
The internal types and functions for the permissions look like this could maybe be supported, but
RegisterCmd
and the current APIs don’t provide a way to change the permissions after creating a register. I’d like to ask what’s been considered in these regards?Will the Register API support changing a register’s permissions policy after it’s been created?
If not, I think the ability to dynamically change an email address’s permissions could still be achieved by changing the currently-active register to a new one with the desired different permissions.
-
How to financially afford having an advertised email address that tons of random strangers can send to?
If the register owner must pay for senders’ added entries, then I’d be concerned that some people won’t want to pay to provide such a contact unless it’s still cheap even with a continuously large amount of emails incoming.
If the senders can be made to pay for adding entries (in addition to paying for separated files for larger emails, which is already the case of course), then this seems like a better option for most people, as well as being a spam deterrent. It’d also be nice to still have the option for the register owner to pay instead, so that more well-off organizations can foot the bill and avoid a deterrent to people contacting them. So I’d like to ask what’s been considered in these regards?
Who will pay for writing an entry to a register - the owner of the register or the writing user? Will it be configurable, per register, to choose either way?
-
How to deal with attackers trying to DoS an email address? How to support heavy legit traffic?
A chunk, in contrast to a register, will be cached by many extra nodes, beyond its section, in proportion to the demand for getting it (as I’ve gathered) and so should be more resilient against DoS.
But all attempts to write to a register must be routed to its section (I think) that has only a limited amount of nodes and where the amount of elders (possibly already under heavy load normally) is small. This seems potentially vulnerable to DoS. Even when a register is full, its section will still have to process incoming command messages to some extent to determine that commands to write to a full register should be rejected (I think). What if an attacker sends an endless flood of write commands to a particular register (email address) from very many different users they control? Similarly, what if a particular email address is very popular and has a very high rate of legit emails incoming?
I surmise that reads of the current value of a register should not be cached by extra nodes (because its value can change), and so all such reads must also be routed to its section, and so this also poses similar issues with a single section handling heavy traffic.
(I couldn’t find relevant answers by searching either of the forums (I found discussion about DoS related to other, some outdated, aspects). I could be misunderstanding how these aspects work, since I’m not very familiar.)
How will denial-of-service attacks against a register be mitigated? How will heavy legit traffic on a register be supported?
Thanks for your time. Additional feedback is also welcome (including alternative ideas or saying that I’ve got it all wrong).