File Storage
For file storage, the EDH system leverages the Interplanetary File System (IPFS) network and S3-compatible storage to enable users and organizations to store and back up their data.
IPFS is an open data transfer network. Servers that store files can join the network to host their own files, and users can then retrieve files via a Content Identifier (CID) derived from the file's content. This approach means that file URIs are not tied to any single server but are instead based on CIDs, solving the problem of URLs becoming inaccessible after the original hosting server goes offline.
S3 Backend (filebase.com)
In addition to uploading files directly to IPFS, EDH also supports storage via an S3-compatible API through filebase.com.
How it works:
- The application encrypts the file with NaCl secretbox (client-side)
- The encrypted file is uploaded via the S3 API to Filebase
- Filebase automatically pins the file to IPFS and returns a CID
- The application stores the CID and decryption key in EVFS metadata
Advantages:
- No need to maintain your own IPFS node
- Supports presigned URLs for uploads and downloads
- Files remain accessible via IPFS CID
- Suitable for applications that need simple HTTP-based uploads
End-to-End Encryption
Since files stored on the IPFS network reside on a public network with no built-in read access restrictions, all stored files must be encrypted. Access is then controlled by sharing decryption keys in an end-to-end manner.
To simplify key management for reading files and make file re-keying efficient, EDH manages file storage based on the Cryptree principle, which has been developed into a file system called the Ever File System (EVFS).
Cryptree Encryption Model
The Cryptree file encryption model works as follows:
- When file content is written, a random symmetric key (KEY-C) is generated to encrypt the file, ensuring that every file has a unique encryption key.
- The key is stored in the file's metadata.
- When the file metadata is written, it is encrypted with another newly generated random key (KEY-B).
- The file entry is stored in the metadata of the parent directory.
- When the directory metadata is written, it is encrypted with yet another newly generated random key (KEY-A).
Keys are stored in this layered fashion all the way up to the root directory.
KEY-AKEY-BKEY-C1KEY-C2KEY-B'KEY-C3Ever File System (EVFS)
CID + key per childThe Ever File System (EVFS) manages files on IPFS using Cryptree-based encryption. Its principles are as follows:
- File content (BLOBs) is stored on IPFS, encrypted with a symmetric key that is randomly generated for each file.
- File information is stored in a File Node, which contains:
- File metadata such as file name, file type, and creation date
- The IPFS CID of the file content
- The key used to decrypt the file content
- Each File Node is stored on IPFS, encrypted with a symmetric key that is randomly generated for each node.
- Directory information is stored in a Directory Node, which contains:
- Directory metadata such as directory name and creation date
- A list of child nodes in the directory, where each entry includes:
- The node type (directory or file)
- The IPFS CID of the node metadata
- The key used to decrypt the node metadata
- Each Directory Node is stored on IPFS, encrypted with a symmetric key that is randomly generated for each node.
From this storage model, it is clear that if a user can access the key of a given directory, they can read all files and subdirectories within that directory. This makes partial file access control straightforward -- you only need to share the key of the desired subdirectory.
Revoking read access is accomplished by generating a new key and encrypting future files with it. This means anyone without the new key will be unable to read file content created after the key rotation.
Example of a File Node before encryption:
{
"name": "hello.txt",
"type": "file",
"block": {
"data": {
"/": "QmRpjfVxnZvrjDLKvzaGF4....."
},
"key": "bbfe62d3b458440a1b796d040f...."
}
}
Example of a Directory Node before encryption:
{
"name": "",
"type": "directory",
"directories": [],
"files": [
{
"data": {
"/": "QmVAteSB3ywyKQkg8y......."
},
"key": "f6f77aeaab377409f9e......"
},
{
"data": {
"/": "QmfRdkKcgnnqaQTGRibwxdL......"
},
"key": "29a8e235c5abd65b964de...."
}
]
}
Supported File Types
EVFS can store any file type. The following medical file types have special rendering support:
| Type | Extension | MIME Type | Description |
|---|---|---|---|
| FHIR | .json | application/fhir+json | Structured health data |
| DICOM | .dcm | application/dicom | Medical imaging (CT, MRI, X-ray) |
| VCF | .vcf | text/x-vcard | Genomic variant data |
.pdf | application/pdf | Documents, reports, prescriptions | |
| JPEG | .jpg | image/jpeg | General photographs |
| PNG | .png | image/png | Device-captured images |
| TIFF | .tiff | image/tiff | High-resolution images |
| Text | .txt | text/plain | Notes, memos |
| ZIP | .zip | application/zip | Compressed multi-file archives |
For more details on medical standards, see Medical Data Standards.
File Storage
To store constants related to file storage, the EDHAccount contract can hold the following data:
Account Storage: For each account, the primary storage information is as follows:
- The Account ID of the storage owner
- URI: the address of the top-level EVFS directory on IPFS
- RootKey: the secret key used to decrypt the EVFS root directory, encrypted with a SecretKey that can be derived from the current SEED of the account's master key
- format:
<24 bytes nonce>|<nacl.secretbox data>
- format:
Interface: Account Variable Storage
contract EDHAccount {
bytes32 constant public KEY_ACCOUNT_STORE_URI = keccak256("io.evernetwork.edh.account.uri");
bytes32 constant public KEY_ACCOUNT_STORE_ROOT_KEY = keccak256("io.evernetwork.edh.account.key");
bytes32 constant public KEY_TAGGED_PUBLIC_KEY = keccak256("io.evernetwork.edh.tagged-public-key");
function setDataAddress(
bytes32 key, bytes32 publicKey, address value
) public onlyActiveDevice;
function setDataBytes(
bytes32 key, bytes32 publicKey, bytes memory value
) public onlyActiveDevice;
function setDataString(
bytes32 key, bytes32 publicKey, string memory value
) public onlyActiveDevice;
function setDataBytes32(
bytes32 key, bytes32 publicKey, bytes32 value
) public onlyActiveDevice;
function setDataUint(
bytes32 key, bytes32 publicKey, uint value
) public onlyActiveDevice;
function getDataAddress(bytes32 key) public view returns (bytes32 publicKey, address addressValue);
function getDataBytes(bytes32 key) public view returns (bytes32 publicKey, bytes memory bytesValue);
function getDataString(bytes32 key) public view returns (bytes32 publicKey, string memory stringValue);
function getDataBytes32(bytes32 key) public view returns (bytes32 publicKey, bytes32 bytes32Value);
function getDataUint(bytes32 key) public view returns (bytes32 publicKey, uint uintValue);
function getAddress(bytes32 key) public view returns (address);
function getBytes(bytes32 key) public view returns (bytes memory);
function getString(bytes32 key) public view returns (string memory stringValue);
function getBytes32(bytes32 key) public view returns (bytes32 bytes32Value);
function getUint(bytes32 key) public view returns (uint uintValue);
function getEncryptionKey(bytes32 key) public view returns (bytes32 keyValue);
function setEncryptionKey(bytes32 key, bytes32 value) public onlyActiveDevice;
function setAddress(bytes32 key, address value) public onlyActiveDevice;
function setBytes(bytes32 key, bytes memory value) public onlyActiveDevice;
function setString(bytes32 key, string memory value) public onlyActiveDevice;
function setBytes32(bytes32 key, bytes32 value) public onlyActiveDevice;
function setUint(bytes32 key, uint value) public onlyActiveDevice;
}
KEY_XXX : Constants representing the keys used to store the Account Storage URI and root key variables.
setDataXXX : Transaction functions for setting values by key. These include the account's latest public key to prevent race conditions, and record both the variable value and the current public key at the time of the transaction. This is useful for storing values that depend on the public key or seed for reading (e.g., values encrypted with the public key).
getDataXXX : View functions for reading values by key, along with the public key associated with that variable.
setXXX/getXXX : Functions for reading or writing values by key without public key verification.
File Sharing
To share files, users can send the link to the directory they wish to share along with the decryption key. This can be done through several channels, each with different characteristics:
Direct CID Sharing -- Users can directly send the CID of the directory they wish to share along with its encryption key, via channels such as QR Code or email. This method works without relying on any Ever services and is the platform's most fundamental sharing mechanism. With this approach, anyone who receives the CID and encryption key can access the files at any time, as long as the files remain stored on the network. The shared files are a snapshot taken at the time of sharing.
File Viewer Link Sharing -- Ever provides a File Viewer web service for reading files on EDH. Users who create sharing links can set a time limit for file access, and the Ever system can log file access history (audit trail).
Other Methods -- Third-party developers can create additional sharing channels through the
ever-edh-coreSDK.
Limitations and Performance
| Item | Details |
|---|---|
| Encryption overhead | NaCl secretbox adds ~40 bytes per block (24-byte nonce + 16-byte MAC) |
| IPFS Pinning | Files must be pinned on at least one IPFS node to persist on the network |
| Content Addressing | Files with identical content (after encryption) will have different CIDs because their encryption keys differ |
| Immutability | Files on IPFS cannot be modified -- updates create a new CID |
For large files (e.g., DICOM series), it is recommended to use the S3/Filebase backend, which handles large uploads via presigned URLs more effectively than uploading directly through IPFS.
See Also
- Account -- EDHAccount used to store storage references
- Device -- Key derivation and SEED management
- Medical Data Standards -- FHIR, DICOM, VCF
- Ever Services -- S3/Filebase storage and File Viewer