Summary
The SAFE CLI (Command Line Interface) provides all the tools necessary to interact with the SAFE Network, including storing and browsing data of any kind, following links that are contained in the data and using their addresses on the network.
Conventions
- The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
Motivation
A CLI is a bare-bones implementation and abstraction of user facing APIs. It is largely platform and system agnostic and can provide a basis, via Rust library interfaces it would be founded upon, for many other languages to interact with the SAFE Network in a clean and useful manner. Here, we want to provide tools for developers to easily work with the SAFE Network, combining these commands to perform complex network tasks, while leaving the commands themselves simple and easy to follow, masking as much of the underlying network complexity as possible.
The end goal of this CLI is to provide SAFE end users with an abstraction which allows them to read data objects using their addresses (XOR names) as a frame of reference. Users should be able to browse and retrieve, mutate or add new versions to data by following links. As well as manage accounts, key pairs and safecoin wallets & transactions.
Thus using the CLI users have access to any type of operation that can be made on SAFE Network data, allowing them to also use it for automated scripts in a piped chain of commands. All without the overhead of GUIs and their platform dependent complexities, which should allow for a CLI to be implemented in a robust, well tested fashion much faster.
Assumptions
This document doesn’t cover how data is represented on the network here, though RDF is assumed. Equally, it doesn’t include details about supporting RDF data management, either from the CLI or from the API being exposed, though these may come later (to be decided as we have APIs exposed from SCL).
Detailed design
This document describes the proposed $ safe
CLI by going through several end-user use cases and showing how the CLI can be used for each of them. This is not meant to be exhaustive but to give enough examples to demonstrate the type of operations to be supported by the CLI app and its API.
The API layer which is to be implemented for this CLI app SHALL also be publicly exposed from this crate, thus any Rust client app can make use of the same type of operations that the CLI exposes, with the exact same type of abstractions to manage the Network data. The SAFE CLI is simply a thin layer on top of this API providing the command line UI.
All commands need a target location (the XOR name) of the object the current operation will be applied to. E.g. if the target location is the XOR name of a Files Container ‘A’, then executing a command to add mypath/subpath/myfile.txt
file will result in “mutating” the ‘A’ container to add a link to the location of the newly uploaded myfile.txt
file, the link added to ‘A’ container will be named mypath/subpath/myfile.txt
.
An additional interactive
mode of operation MAY be provided which allows the user to run commands within a shell where the target location will be kept in memory during that session. It can be set and changed by the user in an analogous way as how the current directory is kept in any console session. In the interactive shell the --target
argument becomes optional (see below for details of --target
).
When executing any command that is creating/mutating data on the network, there is a cost incurred. Such a cost can be paid with a safecoin Wallet
. Before we dive into how safecoin wallets are managed and used further below in the Wallet section, let’s just assume there is a default safecoin Wallet
set in the SAFE account with enough balance to pay for the operations performed by the exemplified commands.
Synopsis
$ safe <command> <subcommand> [options] [parameters]
If the CLI is executed with no arguments it will then run the $ safe
CLI interactive shell:
$ safe
Welcome to SAFE CLI interactive shell!
Type "help" for more information about supported commands.
Type "exit" to exit this shell. Enjoy it!
safe >>
Refer to the CLI Interactive Shell section further below for more details.
Global options
--target
Specifies the target location where the current command will be applied to. This is optional and it behaves according to the context as follows:
- When not provided as argument in a command, then the CLI will take the content read from
stdin
as the target location for the current command. This is what allows the CLI to be used in a piped chain of commands. If thestdin
is empty an error is thrown. - If it’s not provided within the interactive shell, it will use the current target location set in the session, otherwise an error will be thrown.
--root
Special alias for --target Root
. In any command where the target is meant to be the Root Container, --root
can be used instead of something like --target safe://<Root Container XOR-URL>
which is also valid as long as the XOR-URL of the Root Container is already known by the user.
--version
Displays version and release information
--help
Provides contextual help on commands and subcommands
--output <type>
Sets the format for the output, default is plain text which is specially useful for piped chain of commands. Another type which shows tables with human readable messages SHALL be also supported, but other formats MAY be also supported like JSON, YAML, CSV, etc.
-h
Alias to --output human-readable
. This is set as the default in the interactive shell.
--verbose
Increases logging verbosity
--query <query>
Allows to apply filters to the output, perhaps using http://jmespath.org or SPARQL
--dry-run
To test the command without effectively applying the operation
Login / Authorise
$ safe auth
This command simply sends an authorisation request to the Authenticator available, e.g. the safe_auth CLI daemon, and it then stores the authorisation response (credentials) at ~/.safe/credentials
, or if the user prefers, in a environment variable. Any subsequent CLI command will read the ~/.safe/credentials
file, or environment variable, to obtain the credentials and connect to the network for the corresponding operation.
In the case of invoking this command within the CLI interactive shell, the authorisation response (credentials) will be kept in memory only for the current session, and any other command executed within that session will be able to get the credentials from memory.
Root and Named Containers
It is herein also proposed to remove the hard-coded list of default containers, i.e. the default containers SHALL NOT be automatically created when an account is created, not even the Root
Container. The user is free to create them as needed on any account with the corresponding commands/API. To illustrate, this is to allow users to not spend safecoins on creating these containers on an account which may be used only for storing safecoins (just as an example).
Thus, the CLI allows the user to create the Root Container
which is automatically linked from the SAFE account packet, or any number of Named Containers
which are linked from another container with a custom name provided by the user.
By default containers will create unpublished AppendOnlyData
. Users will be able to opt in to create (unpublished) MutableData
via a --non-versioned
command argument. Likewise, by default files will be created as unpublished ImmutableData
.
They will also be able to --publish
their unpublished data, this will warn the user of the cost to copy data to a new AppendOnlyData
or ImmutableData
.
$ safe container
Get help for safe container
subcommands:
$ safe container --help
Create the Root Container:
$ safe container create --root -h
Root Container successfully created at: safe://<Root Container XOR-URL>
We can create a Root’s child Container named music
:
$ safe container create --root --name music -h
'music' Container created at: safe://<'music' Container XOR-URL>
We can create a Root’s child Container named photos
and publish that thus:
$ safe container create --root --name photos --publish -h
'photos' Container created and published at: safe://<'photos' Container XOR-URL>
We can create a Root’s child non-versioned
(aka: MutableData
) Container named my-secret-db
thus:
$ safe container create --root --name my-secret-db --non-versioned -h
Non-versioned 'my-secret-db' Container created at: safe://<'music' Container XOR-URL>
We can also create a Container named my-first-album
as a child of music
Container. Note we are not passing -h
in this example to simply get the new XOR-URL (this command could be piped with any other one which expects a XOR-URL in its stdin):
$ safe container create --name my-first-album --target safe://<'music' Container XOR-URL>
safe://<'my-first-album' Container XOR-URL>
Instead of creating a container as a child of another, we can create it first with no name and at a random location on the network, and then add it as a child to the Root Container, e.g. with name pictures
:
$ safe container create -h
Container created at: safe://<XOR-URL>
$ safe container add --root --name pictures --link safe://<XOR-URL> -h
Container linked from Root Container with name 'pictures' to safe://<XOR-URL>
Note we are inferring the link type from the object being linked, and that’s used for the predicate in the graph being generated in the parent Container. E.g. if the link provided refers to a file the container add
command would add a graph which predicate is a safe:File
instead of safe:Container
(there could be different subtypes as well, e.g. published vs unpublished File/Container).
If the target is an un published non-versioned Container (i.e. a MutableData), we can also edit a specific entry:
$ safe container edit --target safe://<MutableData XOR-URL> --key key1 --value new-value
Reading content
$ safe cat
$ safe cat --help
Fetching any object from the network and streaming out the content, eventually this may need to support different serialisation formats if the content is an RDF doc:
$ safe cat safe://<XOR-URL or PNS-URL>
List the links (children) contained in the Root Container:
$ safe cat --root -h
+-------------------------------------------------------------------------+
| Children of: Root |
+--------------------+-------------+--------------------------------------+
| Name | Predicate/s | Link |
+--------------------+-------------+--------------------------------------+
| 'music' | Container | <XOR-URL of 'music' Container> |
+--------------------+-------------+--------------------------------------+
| 'pictures' | Container | <XOR-URL of 'pictures' Container> |
+--------------------+-------------+--------------------------------------+
But we can also list the links (children) contained in the ‘music’ Container:
$ safe cat safe://<'music' Container XOR-URL> -h
+-------------------------------------------------------------------------+
| Children of: safe://<'music' Container XOR-URL> |
+--------------------+-------------+--------------------------------------+
| Name | Predicate/s | Link |
+--------------------+-------------+--------------------------------------+
| 'my-first-album' | Container | <'my-first-album' Container XOR-URL> |
+--------------------+-------------+--------------------------------------+
If the URL targets a (unpublished) non-versioned Container (i.e. a MutableData), it’s possible to fetch specific entries (we can also support SPARQL here):
$ safe cat safe://<MutableData Container XOR-URL> --key key1 -h
+---------------------------------------------------------+
| Entries of: safe://<MutableData XOR-URL> |
+----------------+---------+------------------------------+
| Key | Version | Value |
+----------------+---------+------------------------------+
| 'key1' | 3 | 'just a value string' |
+----------------+---------+------------------------------+
And if the URL targets a published (therefore versioned) Container, it’s possible to fetch an specific version of it (which by default would be fetching the latest version):
$ safe cat safe://<published Container XOR-URL> --version 234 -h
+--------------------------------------------------------------+
| Children of: safe://<published Container XOR-URL> |
+--------------------+-------------+---------------------------+
| Name | Predicate/s | Link |
+--------------------+-------------+---------------------------+
| 'an-old-item' | Container | <some old XOR-URL> |
+--------------------+-------------+---------------------------+
Files & Files Containers
$ safe files put
Upload all files and subfolders found within the ./to-upload/
local directory, recursively, onto a Files Container on the Network obtaining the XOR-URL of the newly created container (we obtain the XOR-URL because we don’t pass -h
):
$ safe files put --source ./to-upload/ --recursive
safe://<XOR-URL>
Files (ImmutableData
) will also default to unpublished
in the first instance and will require use of a --publish
flag to make them publicly accessible.
$ safe files sync
Given the popularity of rsync
command, it’s proposed to have the SAFE CLI subcommand for uploading files and folders to support a subset of the functionality provided by rsync
. The reasoning being that users knowing how to use rsync
can easily start using the CLI and the SAFE Network, potentially making it also easy to integrate existing automated systems which are currently making use of rsync
, or perhaps integrating eventually having the rsync
command to use the CLI as the gateway for the safe://
protocol, similar to how it supports others like protocols like ssh
, e.g. users in the future could potentially be able to do $ rsync ./myfoler/ safe://mypublicname/myfolder/
.
In addition to this, rsync
seems to cover most/all of the use cases for copying files and directories across networks. The subset of features supported can be gradually expanded with more features. It’s worth noting though, that opposed to rsync
the CLI requires to explicitly define which are the source and target arguments.
Sync up all files and subfolders found within the ./to-upload/
local directory, recursively, with a Files Container on the Network (let’s pass -h
to get a nice description of the files synced instead of the targeted XOR-URL):
$ safe files sync --source ./to-upload/ --target safe://<XOR-URL> --recursive -h
+ index.html safe://<'index.html' file XOR-URL>
+ myfolder/notes.txt safe://<'notes.txt' file XOR-URL>
+ img.jpeg safe://<'img.jpeg' file XOR-URL>
Sent 14.71M bytes, 3.27M bytes/sec, 0.000023 safecoins spent
The +
sign means that the files were added as new items into the target container.
Sync up all files and subfolders found within the ./other-to-upload/
local directory, recursively, and delete those files which are found at the target Container that are not part of the uploaded set of files, e.g. let’s assume that we only have a file named img.jpeg
within ./other-to-upload/
:
$ safe files sync --source ./other-to-upload/ --target safe://<XOR-URL> --recursive --delete -h
- index.html
- myfolder/notes.txt
* img.jpeg safe://<'img.jpeg' file XOR-URL>
Sent 6.12M bytes, 2.87M bytes/sec, 0.000008 safecoins spent
The -
and *
signs mean that the files were removed and updated respectively.
$ safe files add
Add the local ./folder/myfile.txt
file to a Container:
$ safe files add --source ./folder/myfile.txt --target safe://<XOR-URL> -h
+ myfile.txt safe://<'myfile.txt' file XOR-URL>
Sent 587K bytes, 5.9M bytes/sec, 0.0000003 safecoins spent
Add the local ./folder/myotherfile.txt
file to a Container but with name published-file.txt
:
$ safe files add --source ./folder/myotherfile.txt --target safe://<XOR-URL> --name published-file.txt -h
+ published-file.txt safe://<'published-file.txt' file XOR-URL>
Sent 342K bytes, 5.2M bytes/sec, 0.0000005 safecoins spent
Add a file which was already uploaded to a Container with name linked-file.txt
:
$ safe files add --link safe://<XOR-URL> --target safe://<XOR-URL> --name linked-file.txt -h
+ linked-file.txt safe://<'linked-file.txt' file XOR-URL>
Sent 0 bytes, 0.0000005 safecoins spent
Again, we can use cat
command to see the content of a Files Container:
$ safe cat safe://<Files Container XOR-URL> -h
+---------------------------------------------------------------------------------+
| Files of: safe://<Files Container XOR-URL> |
+----------------------+-------------+--------------------------------------------+
| Name | Predicate/s | Link |
+----------------------+-------------+--------------------------------------------+
| 'img.jpeg' | FileItem | safe://<'img.jpeg' file XOR-URL> |
+----------------------+-------------+--------------------------------------------+
| 'myfile.txt' | FileItem | safe://<'myfile.txt' file XOR-URL> |
+----------------------+-------------+--------------------------------------------+
| 'published-file.txt' | FileItem | safe://<'published-file.txt' file XOR-URL> |
+----------------------+-------------+--------------------------------------------+
| 'linked-file.txt' | FileItem | <'img.jpeg' file XOR-URL> |
+----------------------+-------------+--------------------------------------------+
Or indeed, the contents of a given file:
$ safe cat safe://<Files Container XOR-URL>/myfile.txt
hello world
Public Names
Get help for pns
subcommands:
$ safe pns --help
$ safe pns create
Create the Resolvable map for several PNS domains:
$ safe pns create --name mypublicname mypublicname onemorename
mypublicname safe://<1st XOR-URL>
secondpublicname safe://<2nd XOR-URL>
onemorename safe://<3rd XOR-URL>
$ safe pns add
Add mysubname
as a sub name to the mypublicname
public name and automatically creates a container for the data belonging to the new subname:
$ safe pns add --name mysubname --target mypublicname -h
New sub name 'mysubname' added to 'mypublicname' and linked container created successfully at: safe://<Files Container XOR-URL>
Or alternatively we can add myothersubname
as a sub name to the mypublicname
public name, but linking it to a container which was previously created:
$ safe pns add --name mysubname --target mypublicname --link safe://<Files Container XOR-URL> -h
New sub name 'myothersubname' added to 'mypublicname'
We can also set what’s the link to follow by default when a client is resolving a PNS URL with no sub name, e.g. safe://mypublicname
, in such a case we can set to resolve to a specific location:
$ safe pns add --default --link safe://<XOR-URL or PNS-URL> --target mypublicname -h
Default sub name set to resolve to: safe://<XOR-URL or PNS-URL>
We can also take the value for the --link
argument from stdin
if we provide a --target
argument (otherwise, if no --target
is provided then the value from stdin
is by default used as the --target
value). Let’s do that, and let’s add such a link as a sub name with a name but also as the default link:
$ echo safe://<XOR-URL> | safe pns add --default --name anothersubaname --target mypublicname -h
New sub name 'anothersubname' added to 'mypublicname', and set as the default too, to resolve to: safe://<XOR-URL>
Thus in the example above, the same location will be resolved when fetching either safe://mypublicname
or safe://anothersubname.mypublicname
.
$ safe pns remove
If needed we can also remove an existing sub name from a public name:
$ safe pns remove --name mysubname --target mypublicname
Or, perhaps we need to remove the default sub name from a public name:
$ safe pns remove --default --target mypublicname
Piping commands
$ safe pns | safe files
When the --target
is not provided, the command will assume the target location to be read from the stdin
, this allows the user to pipe commands. In some commands like safe container
or safe pns
, if the --target
argument is provided but not the --link
then the stdin
will be used as the value for the --link
.
A nice example of this is by looking at how we can chain commands to upload all the local files of a website, publish it at safe://mypublicname
on the SAFE Network, and finally fetch its content using the PNS-URL:
$ safe pns create --name mypublicname && safe files put --source ./my-website/ --recursive | safe pns add --default --target mypublicname && safe cat safe://mypublicname
<html>
<h1>
BOOM!
</h1>
</html>
Let’s assume we have three music files in the local ./music-files/
directory, we can upload them onto the ‘music’ Named Container that is linked from the Root Container as follows:
$ safe container follow --root --name music | safe files sync --source ./music-files/ -h | wc -l
3
CLI Interactive Shell
$ safe
Set current target location on the network to be the Root
Container:
safe >> locate --root
Obviously, we can set the current target location using a XOR-URL or a PNS-URL:
safe >> locate safe://<XOR-URL or PNS-URL>
The --locate
is a global argument supported in the interactive shell and only valid with commands which create new objects on the Network. It requests the CLI to automatically set the current target location in the session to be the location of the new object being created by the command. E.g. the following command will create the Root Container and set the current target location to be the XOR name of the Root container just created:
safe >> container create --root --locate
Root Container successfully created at: safe://<Root Container XOR-URL>
Current target location was set to: safe://<Root Container XOR-URL>
safe >>
Change the current target location by following a link named music
that is found in current target location:
safe >> container follow --name music
Keys
Key
management allows users to create new sign/encryption key pairs that can be used for different type of operations, like choosing which sign key to use for uploading files (and therefore paying for the storage used), or signing a message posted on some social application when an Key is linked with a public profile (e.g. a WebID/SAFE-ID), or even for encrypting messages that are privately sent to another party so it can verify the authenticity of the sender.
Users can record Keys
in a Container, having friendly names to refer to them, but they can also be created as throw away keys which are not linked from any container or other data on the network.
Note that even that the key pair is generated by the CLI, Keys
don’t hold the secret key but just the public key, and optionally can have a safecoin balance attached to it, thus Keys
can also be used for safecoin transactions (see the wallet section below for more details).
$ safe keys
Get help for keys
subcommands:
$ safe keys --help
Create a new Key
linking it from a Container, the name of the link in this case would be the public key itself. Note the key pair is generated locally by the CLI, and the secret key is not stored as part of the Key
, or anywhere on the network:
$ safe keys create --target safe://<a Container XOR-URL> -h
New Key created at: safe://<Key XOR-URL>, and linked from safe://<a Container XOR-URL>.
Key pair generated is: <key pair info>
You can also optionally use nicknames for easy reference when creating Keys
:
$ safe keys create --name business-time --target safe://<a Container XOR-URL> -h
New Key created at: safe://<Key XOR-URL>, and linked with name 'business-time' from safe://<a Container XOR-URL>.
Key pair generated is: <key pair info>
Or create throw away Key
:
$ safe keys create --anon -h
New Key created at: safe://<Key XOR-URL>. This was not linked from any container.
Key pair generated is: <key pair info>
We can create an asymmetric key pair and create a Key
to enable a friend to set up a SAFE account with it by preloading it with safecoins, assuming the default Wallet
linked from the SAFE account has enough balance to transfer the desired preload amount:
$ safe keys create --preload 55 --anon -h
New Key created at safe://<Key XOR-URL>, preloaded with 55 safecoins, which can be used for account creation/generation
Key pair generated is: <key pair info>
55 safecoins spent
An anonymous Key
that was previously created can also be linked from a Container, using its public key or a nickname for easy reference. Let’s add an existing Key
using a nickname to an existing Container:
$ safe keys add --name mykeys --target safe://<a Container XOR-URL> --link safe://<Key XOR-URL> -h
Key at safe://<Key XOR-URL> was linked with name 'mykeys' from safe://<a Container XOR-URL>
It is also possible that a friend wants you to create a Key
for him but having him to provide you with the public key for it, rather than you generating the asymmetric key pair, in this case we can simply create the Key
passing the public key to be used:
$ safe keys create --pk <BLS pk> --preload 3 -h
safe://<Key XOR-URL> was created for public key <BLS pk>, preloaded with 3 safecoins, which can be used for account creation/generation
No key pair was generated.
3 safecoins spent
Combining with other commands
It may be that you want files created to be owned by a different Key
than the default obtained from the account, and perhaps even pay with a different wallet than the default Wallet
set in the account, you can do that by using the --owner
flag to specify the key for ownership of the data, and --wallet
for specifying a specific wallet to pay for the costs:
$ safe files add --source ./folder/myfile.txt --target safe://<XOR-URL> --owner safe://somekey --wallet safe://somewallet -h
+ myfile.txt safe://<'myfile.txt' file XOR-URL>
Sent 587K bytes, 5.9M bytes/sec, 0.0000003 safecoins spent by safe://somewallet
Files uploaded are owned by safe://somekey
Wallet
A Wallet
is a specific type of Container
holding a set of spendable safecoin balances. A Wallet
effectively contains links to Keys
which have safecoin balances attached to them, but the Wallet
also can store the secret keys needed to spend them. Wallets
are stored encrypted and only accessible to the owner by default.
$ safe wallet
Get help for wallet
subcommands:
$ safe wallet --help
Let’s create a Wallet
:
$ safe wallet create -h
Wallet created at safe://<Wallet XOR-URL>
Check your balance:
$ safe wallet balance --target safe://<some wallet XOR-URL> -h
safe://<some wallet XOR-URL> owns 99 safecoins
Let’s add a new spendable balance to the Wallet
with a friendly name we can later use to explicitly refer to it in a safecoin transaction. The following command would link an existing Key
to a Wallet
:
$ safe wallet add --name weekend-leisure --target safe://<Wallet XOR-URL> --link safe://<Key XOR-URL>
Enter secret key corresponding to public key at safe://<Key XOR-URL>:
New spendable balance generated with name 'weekend-leisure' in wallet located at safe://<Wallet XOR-URL>
Let’s transfer safecoins from Wallet using any available spendable balance to an specific named spendable balance of another Wallet:
$ safe wallet transfer --amount 12 --from safe://<wallet XOR-URL> --to safe://<other wallet XOR-URL>/weekend-leisure
Note that a Key
can also be used as the source (--from
) or destination (--to
) of a safecoin transfer, but since Keys
don’t hold the secret key to spend the balance, when they are used as the source of a transfer the CLI will prompt the user to enter the secret key to submit the transaction:
$ safe wallet transfer --amount 55 --from safe://<Key XOR-URL> --to safe://<destination Wallet or Key XOR-URL>
Enter the BLS-sk to spend from safe://<Key XOR-URL>:
55 safecoins transferred to safe://<destination Wallet or Key XOR-URL>
Sweeping all available safecoins:
$ safe wallet sweep --from safe://<Wallet or Key XOR-URL> --to safe://<Wallet or Key XOR-URL>
Generate transaction target for a specific amount of safecoins. The payee needs to only click the link to generate a transaction to confirm for the correct amount:
$ safe wallet receive --amount 12.23324 --on safe://<wallet or key XOR-URL> --description "Art Coffe Bar"
safe://<wallet or key XOR-URL>?amount=12.23324&desc=Art%20Coffe%20Bar
The generated URL can be opened by a Wallet
-compatible application, like the SAFE Browser, which can render an invoice including a QR code and payment description.
SAFE Ids (WebIDs)
This manages a SAFE-Id profile container. All SAFE-Ids are stored there unless otherwise specified.
SAFE-Ids are based upon the WebID spec, but may have other changes (beyond being a safe://
URL, which means they cannot strictly be WebIDs by the specification).
$ safe safeid
Get help for safeid
subcommands:
$ safe safeid --help
Create a SafeId:
$ safe safeid create
# return safe://<SAFE-ID XOR-URL>
Create a SafeId with name:
$ safe safeid create --name josh
# return safe://<SAFE-ID XOR-URL>
Various standard SAFE-Id fields can be easily populated in this flag based manner, eg: --name
, --email
(Do we want this on safe? probably), --nickname
, --surname
, --website
… (more?)
Update a SafeId field:
$ safe safeid update --name gabriel --target safe://<SAFE-ID XOR-URL>
# return safe://<SAFE-ID XOR-URL>
This can be piped to other commands to link to a PNS name e.g.
Drawbacks
None identified so far.
Alternatives
Getting rid of the predefined and hard-coded list of Default Containers (as detailed in Root and Named Containers section) can be considered optional, and an additional parameter can be supported which allows the user to target each of the default containers by name. However, unless there are strong arguments to automatically create these Containers by default when creating an account, it’s strongly RECOMMENDED to effectively remove them.
Unresolved questions
- There needs to be more details (to be added later) covering aspects of encryption keys and how encrypted content is to be handled.