Most of the common operating systems, be they mobile or desktop, have default folders like Gallery, Documents etc. One important reason behind these default folders is to have a standard place (based on the type of files to be stored) that other applications can easily collaborate with. For example, a camera application can capture images and save them to the Gallery folder, while an image editing application can look for files from the Gallery folder. Having default folders brings more organised content and also makes it easier to collaborate and share data with other applications.
Default Containers
The SAFE Network follows a similar pattern of providing default containers. Default containers can be shared by applications when the user grants the needed permission. The default containers are:
- _documents - to store document related data
- _downloads - to store downloaded content
- _music - to store music files
- _pictures - to store images
- _videos - to store videos
- _public - to store unencrypted data
- _publicNames - to store public name/identity which can be looked up for public information
The data stored in the containers must be encrypted, except for the _public container.
In addition to the default containers, applications can create their own root container. Applications can store metadata related to the application or any other data based on the needs.
Data Types
The SAFE Network provides two data types to store and retrieve data. The amount of data that a user can store depends on the Safecoin balance that the user account has. During test networks, the user account limits are regulated based on the number of mutation requests permitted for the account.
Data deduplication is a unique feature of the SAFE Network which is achieved by the process of Self-Encryption. ImmutableData types are subjected to Self-Encryption and stored in the network to avoid data duplication. Binary data and other types of files can be good candidates for storing in the network as Immutable Data. Immutable Data types are cached by the clients and fetching the same can be quicker.
On the other hand, we need a defined structure to store and retrieve data which can be mutated. The second data type, MutableData facilitates in allowing data to be mutated based on the need. MutableData can be visualised as a simple key-value store where data can be stored, retrieved, updated and deleted. In addition, MutableData provides fine-grained access control.
The default containers are built using MutableData and the files are stored as ImmutableData chunks in the network. This is just another use case of using MutableData and ImmutableData.
Client Ecosystem
The authenticator is an important application that users need to create an account and get started on the SAFE Network. The authenticator is packaged with the SAFE Browser.
The SAFE Browser is used to surf safe://
websites and web applications, while the authenticator is used for creating the user account and managing the application authorisations. Applications must be authorised by the user to be able to connect to the SAFE Network on behalf of the user. The user can revoke the granted access at any time from the authenticator.
SAFE Application Development
Application development for the SAFE Network is not any different from the standard practices followed. Applications can easily integrate the safe_app library based on the platform the application is being built on. Node.js and Java are two platforms supported at present. We are eager to expand support to other platforms with time. Web applications can be built using the DOM API of the SAFE Browser.
Similar to the OAuth process, the application sends a request using the library for authorisation. When the authorisation is approved by the user, the application gets a token which is used to connect to the SAFE Network to build decentralised applications.
Node.js API
Npm Dependency
Include safe_app dependency in the package.json. safe_app is not released in the npm repository yet. Should be linked from the git repository for the time being.
Example: "safe-app": "git+https://github.com/maidsafe/safe_app_nodejs/"
The documentation for Node.js safe_app API is hosted here: Home - Documentation
Authorisation
The application must authorise with the authenticator with the needed access permissions. When the user approves the request, application specific encryption keys are generated. The application will be identified in the network using its keys. When the user grants or denies authorisation, the application will receive a URI. Applications can connect to the SAFE Network on behalf of the user by using the URI received.
The application can create its own container, and request access to default containers or other applicationsā containers through the authorisation request.
READ, WRITE, UPDATE, DELETE, MANAGE permissions can be requested for every container.
Cipher Opts
The safe_app library provides crypto API functions. Likewise, CipherOpts is a type which specifies the type of cipher technique to be applied while storing the data in the network.
There are three types of CipherOpts:
- Plain - Data will not be encrypted.
- Symmetric - Data is encrypted with a symmetric key.
- Asymmetric - Data is encrypted using a key pair.
Immutable Data
As the name suggest, the data saved as ImmutableData can not be mutated. Binary content can be written and read from the network using the ImmutableData API. ImmutableData API uses self-encryption to store the data in the network to avoid data deduplication. When the data is stored in the network, a XOR name is returned which is used to reference the data while reading it.
Mutable Data
MutableData can be compared to a simple key-value store. MutableData allows fine-grained access control for collaboration. Permission to insert, update, delete, manage can be requested or permitted for collaboration.
MutableData can be used in different flavours:
- Public Mutable Data - Data is unencrypted.
- Private Mutable Data - Data is encrypted.
- Shared Mutable Data - Data is encrypted and can be used for collaborative purpose. E.g. an inbox where anyone can insert data or allow only whitelisted users to insert.
DOM API
A web application can communicate with the SAFE Network and Authenticator by interacting directly with the SAFE Browserās DOM API, i.e. window.safe* functions.
This API is very similar to the Node.js API, the main difference is that the web application receives handles for each of the objects that are instantiated when interacting with the API, e.g. SAFEApp and MutableData instances. The web app is required to release the handles provided by calling a specific āfreeā function on each of the tokens received (please refer to the examples below for more details).
The full documentation of the SAFE Network DOM API is published here: http://docs.maidsafe.net/beaker-plugin-safe-app
Webapp Example #1
This example code demonstrates how a web app can connect to the authenticator to request for authorisation and receive the auth URI.
function SAFE_Tutorial_Example_1() {
console.log('SAFE Example #1: Authorising web application from: ', window.location.href);
let app = {
name: 'Example safe-app web page #1',
id: 'net.maidsafe.tutorial.webapp.example1',
version: '0.1.0',
vendor: 'MaidSafe Ltd.',
}
let access = {
_public: ['Read']
};
window.safeApp.initialise(app)
.then((appToken) => {
console.log("Application Token received:", appToken);
window.safeApp.authorise(appToken, access)
.then((authURI) => {
console.log("Application was authorised by user. Auth URI received:", authURI);
window.safeApp.connectAuthorised(appToken, authURI)
.then(_ => {
console.log("Application is now registered in the network");
// Make sure that SAFEApp instance is freed from memory.
window.safeApp.free(appToken);
console.log("SAFEApp instance freed.");
});
}, (err) => {
console.warn("Application authorisation was rejected")
});
})
.catch((err) => {
console.error("Error from webapp: ", err);
});
};
Webapp Example #2
This example code demonstrates how a web app can connect to the authenticator to request for authorisation and receive the auth URI, use the auth URI to then connect to the SAFE Network, and finally create a MutableData.
function SAFE_Tutorial_Example_2() {
console.log('SAFE Example #2: Creating a MutableData');
let appInfo = {
name: 'Example safe-app web page #2',
id: 'net.maidsafe.tutorial.webapp.example2',
version: '0.1.0',
vendor: 'MaidSafe Ltd.',
}
let access = {
'_public': ['Read', 'Insert', 'Delete', 'ManagePermissions']
};
const typeTag = 15000;
const MUTABLE_DATA_ENTRIES = { key1: 'value1', key2: 'value2' };
window.safeApp.initialise(appInfo)
.then((appToken) => window.safeApp.authorise(appToken, access)
.then((authURI) => window.safeApp.connectAuthorised(appToken, authURI))
.then(_ => window.safeMutableData.newRandomPublic(appToken, typeTag)
.then((mdata) => {
console.log('MutableData created, handle: ', mdata);
return window.safeMutableData.quickSetup(mdata, MUTABLE_DATA_ENTRIES)
.then(_ => {
console.log('MutableData setup succeeded');
return window.safeMutableData.get(mdata, 'key1')
.then((val) => {
console.log('Value successfully retrieved: ', val.buf.toString());
// Make sure that SAFEApp instance is freed from memory.
window.safeApp.free(appToken);
console.log("SAFEApp instance freed.");
// Make sure that MutableData instance is freed from memory.
window.safeMutableData.free(mdata);
console.log("MutableData instance freed.");
});
});
})
))
.catch((err) => {
console.error("Error from webapp: ", err);
});
};
Webapp Example #3
This example code demonstrates how a web app can connect to the authenticator to request for authorisation and receive the auth URI, use the auth URI to then connect to the SAFE Network, after connected, it creates a MutableData, and finally it iterates over the entries, key and values of the created MutableData.
function SAFE_Tutorial_Example_3() {
console.log('SAFE Example #3: Iterating over MutableData entries');
let appInfo = {
name: 'Example safe-app web page #3',
id: 'net.maidsafe.tutorial.webapp.example3',
version: '0.1.0',
vendor: 'MaidSafe Ltd.',
}
let access = {
'_public': ['Read', 'Insert', 'Delete', 'ManagePermissions']
};
const typeTag = 15000;
const MUTABLE_DATA_ENTRIES = { key1: 'value1', key2: 'value2' };
window.safeApp.initialise(appInfo)
.then((appToken) => window.safeApp.authorise(appToken, access)
.then((authURI) => window.safeApp.connectAuthorised(appToken, authURI))
.then(_ => window.safeMutableData.newRandomPublic(appToken, typeTag))
.then((mdata) => window.safeMutableData.quickSetup(mdata, MUTABLE_DATA_ENTRIES)
.then(_ => window.safeMutableData.getKeys(mdata))
.then((keys) => keys.map((key) => console.log("Key:", key.toString())))
.then(_ => window.safeMutableData.getValues(mdata))
.then((values) => values.map((value) => console.log("Value:", value.buf.toString())))
.then(_ => window.safeMutableData.getEntries(mdata)
.then((entries) => window.safeMutableDataEntries.forEach(entries,
(key, value) => console.log("Entry: (", key.toString(), ",", value.buf.toString(), ")"))
.then(() => {
console.log("Entries iteration finished");
// Make sure that MutableData Entries instance is freed from memory.
window.safeMutableDataEntries.free(entries);
console.log("MutableData Entries instance freed.");
}))
)
.then(_ => {
// Make sure that SAFEApp instance is freed from memory.
window.safeApp.free(appToken);
console.log("SAFEApp instance freed.");
// Make sure that MutableData instance is freed from memory.
window.safeMutableData.free(mdata);
console.log("MutableData instance freed.");
})
)
)
.catch((err) => {
console.error("Error from webapp: ", err);
});
};