dcap-node
dcap-node copied to clipboard
Convenient REST API for managing optionally encrypted decentralized documents hosted on IPFS
DCAP
ᴅᴇᴄᴇɴᴛʀᴀʟɪᴢᴇᴅ ᴄᴏɴᴛᴇɴᴛ ᴀɢɢʀᴇɢᴀᴛɪᴏɴ ᴩʟᴀᴛꜰᴏʀᴍ
DCAP allows you to easily create and manage well-structured documents that are stored on IPFS. A simple user account system tied to a PGP keypair allows you to encrypt and decrypt your documents over an easy-to-use REST API.
With DCAP you can create document types for things such as notes, todos, chat messages, web page data, social network updates and more. These documents will be stored on the decentralized IPFS network and pinned by the API to ensure accessibility. You can choose to store your documents in plaintext for all to see or encrypt them for privacy. DCAP's API makes it easier to create and retrieve these decentralized documents in your website and apps.
Setup
Requirements
- Node.js
Installation
Clone this repository and run npm install. Copy the sample configuration file (.env.example) to .env and edit it to match your desired configuration (you should change the TOKEN_SECRET and SERVER_KEY_PASS values to different hard-to-guess passwords). Run npm start to start DCAP on http://localhost:5000. Please use this with SSL on production environments.
Configuration
The main configuration is loaded from .env. You can change the security behavior and whether DCAP should pin IPFS documents from these settings.
In the /src/config/types directory are the type definitions that DCAP uses to validate the content you post. These must be written in JSON Schema to be validated by DCAP. You can use the example types that come bundled with DCAP but will probably want to add some more attributes to make them useful.
Example type configuration
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "note", // Type name as referenced in API requests
"description": "Simple note content type",
"type": "object",
"encrypted": true, // Set to false to make document publicly readable
"properties": {
"text": {
"type": "string"
},
"public": {
"type": "string"
}
},
"public": [ // Array of properties that should appear in the public index
"public"
],
"required": [ // Required field names
"text"
]
}
User Storage
Users are stored encrypted on IPFS. If you don't want public access to your list of usernames, you can use MongoDB as a user storage solution. Ensure a mongo daemon is running and in .env comment out USER_STORAGE=ipfs and uncomment USER_STORAGE=mongo and MONGODB_URI and point it to your mongo instance.
API Reference
POST /user/create: Create New User
POST /user/login: Login User
🔑 POST /user/delete: Delete Account
GET /type/:type: Get Type Index
GET /type/:type/schema: Get Type Schema
🔑 POST /type/:type: Add New Document
🔑 POST /type/:type/:hash: Get Decrypted Document
GET /document/:hash: Get Unencrypted Document
🔑 PUT /type/:type/:hash: Update Document
🔑 DELETE /type/:type/:hash: Delete Document From Index
🔑: Requires authentication token. This token (sent to you on login) can be passed to authorized endpoints via the token attribute in the request body, via a token query parameter, or with an x-access-token HTTP header. This token must be refreshed every 24 hours.
Create New User
POST /user/create
Creates a new user and generates a PGP keypair. The public key will be stored in the database but you must keep your private key secure as it will be used to encrypt and decrypt your documents.
Request Body
{
"username": "testuser",
"password": "abc123"
}
Success Response
HTTP/1.1 200 OK
{
"success": "User created",
"pub_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----....",
"priv_key": "-----BEGIN PGP PRIVATE KEY BLOCK-----..."
}
Error Responses
500Key Creation Failed500User Creation Failed
Login User
POST /user/login
Logs user in and returns a JWT token to submit with authorized requests.
Request Body
{
"username": "testuser",
"password": "abc123"
}
Success Response
HTTP/1.1 200 OK
{
"success": "Login succeeded",
"token": "eyJhbGciOi..."
}
Error Responses
401User not found401Wrong Password401User Login Failed
Delete Account
🔑 POST /user/delete
Deletes your user account. Stored documents are not removed from their types.
Request Body
{
"token": "eyJhbGciOi...", // Token can also be sent as x-access-token header
"password": "abc123"
}
Success Response
HTTP/1.1 200 OK
{
"success": "User successfully deleted"
}
Error Responses
401Password must be supplied401Username not found in token401Incorrect password403User deletion failed
Get Type Index
GET /type/:type[?filter=:filter]
Gets list of documents for a given type. You can also filter documents by username with the username query parameter.
URL Params
:typeType name
Query Params
:filter: Parameter to filter documents by. This can beusername/created/updatedor any parameters defined as public by the type's schema.
Success Response
HTTP/1.1 200 OK
{
"documents": [
{
"created": 1515884391549,
"updated": 1515884391549,
"username": "testuser",
"link": {
"/": "QmcxBLD2MCcqEw1q5L3xqxDiSMkYHnoe4LYLskTn7vhwci"
}
},
...
],
"hash": "QmQ5SBZnsAbZ1BEmsAZe8keBDbUwQMzuc2ZF7njYDE5Bum" // IFPS reference to this index
}
Error Responses
404Type not found
Get Type Schema
GET /type/:type/schema
Retrieve the schema specification for a given type.
URL Params
:typeType name
Success Response
HTTP/1.1 200 OK
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "note",
...
}
Error Responses
404Type not found
Add New Document
🔑 POST /type/:type
Create a new document and store it to the type index. The document data must be passed in the body's data field. This data will be validated against the type's schema. If the type schema indicates the document should encrypted, a password and private key must be passed in the request body.
URL Params
:typeType name
Request Body
{
"data": { "text": "foo bar"}, // Must match type schema
"password": "abc123", // Required if type is encrypted
"priv_key": "-----BEGIN PGP PRIVATE KEY BLOCK-----..." // Required if type is encrypted
}
Success Response
HTTP/1.1 200 OK
{
"success": "Document created",
"hash": "QmaCSJeLMJYpbecr3Qft4w2CQQDmQo9w8Y5DPQmf9ptGVL"
}
Error Responses
401Username not found in token404Type does not exist500Must supply data object in request body500Private key not passed in request body500Password not passed in request body500Document already exists
Get Decrypted Document
🔑 POST /type/:type/:hash
Fetches IPFS document and decrypts it if encrypted (otherwise behaves the same as GET /document/:hash).
URL Params
:typeType name:hashDocument hash
Request Body
{
"token": "eyJhbGciOi...", // Token can also be sent as x-access-token header
"password": "abc123", // Required if type is encrypted
"priv_key": "-----BEGIN PGP PRIVATE KEY BLOCK-----..." // Required if type is encrypted
}
Success Response
HTTP/1.1 200 OK
{
"text": "foo bar"
}
Error Responses
404Type not found404User not found401Password and/or private key not included in request body
Get Unencrypted Document
GET /document/:hash
Gets raw document by hash from IPFS. This is functionally equivalent to ipfs cat <ref>
URL Params
:hash: IPFS document hash
Success Response
HTTP/1.1 200 OK
"-----BEGIN PGP MESSAGE-----..."
Update Document
🔑 PUT /type/:type/:hash
Update an existing document and replace its hash in the type index with the newly saved document.
URL Params
:typeType name:hashDocument hash
Request Body
{
"username": "testuser",
"password": "abc123"
}
Success Response
HTTP/1.1 200 OK
{
"success": "Document updated",
"hash": "QmaCSJeLMJYpbecf5Qft4w2CQQDmQo9w8Y5DPQmf9ptGw4..."
}
Error Responses
401Username not found in token403Invalid username for this document404Document to update not found404Type does not exist500Must supply data document in request body500Private key not passed in request body500Password not passed in request body
Delete Document From Index
🔑 DELETE /type/:type/:hash
Removes document from type index. Does not remove the actual (immutable) IPFS document.
URL Params
:typeType name:hashDocument hash
Success Response
HTTP/1.1 200 OK
{
"success": "Document removed from type index",
"hash": "QmcxBLD2MCcqEw1q5L3xqxDiSMkYHnoe4LYLskTn7vhwci"
}
Error Responses
401Username not found in token403Invalid username for this document404Document to remove not found404Type does not exist
Contributing
Contributions are welcome. Please see github issues for open bugs or feature requests, or to submit your own. DCAP has integration tests which should be run (npm run test) and updated for new code contributions. This project uses Typescript and all code should be accepted by Typescript's linter.
License
Copyright (c) 2018 Matt Crider.
Licensed under the MIT License.