Collaborative Writing With User-Owned Storage and Encryption
How Graphite manages to enable sharing and collaboration while preserving encryption and user-owned storage.
Fair warning, this is a more technical post than normal. However, it’s an important one. As one of the major concerns Graphite solves for is security, it’s important to understand how Graphite provides that security. It’s also important to note that Graphite has not been audited by security professionals. The code is open source and you can always reach out with questions, but I encourage you to do your own due diligence. With that out of the way, let’s dive into how Graphite allows you to share documents and collaborate in real-time without compromising your security.
Remember, with Graphite, you own your data. Your files are stored in the storage provider of your choice. Admittedly, choosing a storage provider is a bit complicated right now (that will change soon!), so the vast majority of people store their files in the default storage provided by Blockstack. But the fact remains: Graphite doesn’t have access to your stored files, even if you’re using Blockstack’s default Azure storage option. In a centralized app, when you share a file, you are giving someone else access to said file which is stored in the app’s database. With Graphite, each user has their own storage hub, so you can’t just give access to someone else to make updates to a file in your storage hub. So, how do we solve this while ensuring everything remains owned by each user and encrypted with each user’s encryption keys?
Let’s start with the process behind sharing a static file (Graphite allows you to share an uneditable copy of your documents or enable real-time collaboration). When you share a static file, you are really just saving a copy of that file in your own storage hub and encrypting that copied file with the recipients public key. Now, with Blockstack identities there are multiple key pairs, so it’s important to know that each user gets a new key pair for each Blockstack app they log into. Your key pair for Graphite is completely different from the key pair for Stealthy. When you share a file with someone, you are encrypting that file with the recipient’s Graphite-specific public key. But how does that user access and decrypt the file?
When the recipient accesses the file (either by the direct link you provide them to it or by going to Documents, clicking the menu button, and clicking Shared Docs), they are actually making an API call to the sender’s storage hub. That API call finds the document that was shared, loads up the encrypted content, and then after the API call is complete, the recipient’s Graphite-specific private key is used to decrypt the file. Since the file was encrypted with the recipient’s Graphite-specific public key, this works and the file content is displayed.
All of the decryption happens client side after the file is loaded and all network requests are complete. If you feel like getting super nerdy and verifying this, have your friend share a Graphite document with you, then load the document with the network panel of the developer tools open. You’ll see a bunch of network requests, so look for one with the document id (you can find this by looking at the last part of the URL). It’ll look like this:
If you click on the network request with the document id and the 202 code, you’ll be able to scroll down in the Headers tab to see the request payload. It should look like this:
If you were a bad actor trying to intercept network requests and access someone’s Graphite documents, the above is all you’d be able to get. Since each Graphite user’s public and private keys are inextricably linked to their Blockstack ID and authentication, it’s not possible for someone to decrypt your files without you giving them your pGraphite-specific private key or them taking your device.
Ok, so that’s sharing a static file. Let’s diagram this quickly:
The process for sharing a collaborative document is largely the same with one key difference. Websockets are used to enable real-time collaboration. This means a central server is used to relay messages between the two or more devices connected in real-time. To help preserve data ownership and decentralization, Graphite has the websockets server code open source and available here. In the production Graphite web app, a server hosted by Graphite relays these messages, but there’s nothing stopping you from deploying your own websockets server.
The natural follow-up question is how does encryption work when websockets are involved? Admittedly, security is not as tight when you choose real-time collaboration. The real-time updates between the documents are not encrypted with your public key. They are encrypted with a secure websockets connection. When the file is then auto-saved to your storage hub, it is, of course, encrypted with your key. If you’re curious about websocket security and best practices, you can read more here (note: Graphite follows all of Heroku’s suggested best security practices):
For those in need of extreme security, Graphite recommends only sharing static copies of files, but for the vast majority of the rest of the world, real-time collaboration provides the convenience you’d expect from Google Docs with significantly more security.
Hopefully, this article was informative. If you have questions, drop on by Twitter and say hi! If you’ve never used Graphite before, give it a shot here: