In the present day, privacy and data ownership are becoming more and more scarce. As people lean towards using products with these values, companies try to make up for the lack of them with good-sounding marketing and policies. We believe in providing data ownership and privacy by design. In our case, these incentives are aligned as our motivation comes from our own needs, because we were missing them in other available tools for personal knowledge management.
Today’s release marks the final step in fulfilling our vision for full privacy and data ownership by design. We are releasing end-to-end encryption for syncing all new user vaults by default. Every single character and pixel in acreom is fully owned by you, stored on your device in a non-proprietary format, and only accessible by you.
Every single write in acreom is stored on your disk by default, in the markdown format. Think of it as a plain text editor, with an optional sync layer on top. Any changes made to files on the drive are reflected in acreom, and vice-versa. This gives you the flexibility to plug your data into any other tool at any time, as well as the confidence that your data will stay with you, even if acreom ceases to exist.
When designing end-to-end encryption, our main goal was to deliver a complete privacy without any compromises. We defined three main goals for our encryption to fulfill:
No sensitive data can leave your device unencrypted.
Not we, nor anyone else can have any access to the password used for encryption.
acreom can’t access any of your data, even locally, before inputting the encryption password.
By fulfilling these goals, no matter how hard anyone tries, they won’t be able to access your data. Since your password is not saved anywhere, there is no way to access encryption keys. acreom as a 3rd party does not store your password or even secrets derived from your password. This fulfils the concept of zero-knowledge encryption.
We have built our E2EE using @skiff/crypto developed by skiff which made it simple to manage encryption and handle the whole encryption process.
When you first enable end-to-end encryption, you set your encryption password. The password is hashed using argon2id, and then hkdf is derived from the hash. Next, your private keypair is generated. This keypair is then encrypted using
hkdf derived from your password. Encrypted keypair is stored on the user object. This is a necessary step to enable decryption of your data.
Only way to decrypt your encryption keypair is to input the correct password and use it to decrypt your private keypair. There is no communication between the acreom app, and our servers when we decrypt your private keypair, since the object is encrypted on the user object and is only retrieved after successful login. acreom creates hash, derives hkdf, and then tries to decrypt the private keypair object.
Each page, image, and folder is encrypted using aes256-gcm and a unique encryption key. Each encryption key is stored encrypted using the user's private keypair.
When sharing a page, its content alongside the assets gets decrypted and stored as plain text. Since the decision is to make the page public, this was the easiest way to achieve sharing when using e2ee.
We had to decide between two approaches for sharing. The first one was using a different encryption key for sharing and adding it to the URI when you share the document. This way, only people with a link and key would be able to access the document. This would work well if you only wanted to share content with certain people, but would be cumbersome if you wanted to share the page publicly as you would need to include the link with encryption password in it. The second approach was to simply decrypt the data and share the pages the same way we did before.
Long-term goal when it comes to integrations is to have all integrations manage its own data directly from the app. We have already achieved this with github integration, where the only thing that gets stored, or passes through our servers, is the integration configuration, containing credentials and settings for the given integration. That configuration object is now encrypted.
Other integrations currently use our servers as passthrough proxy to request data and get around CORS in the browser. We intend to streamline all integrations to follow the process we created when working on github integration, where the only thing that gets stored or passes through our servers is the encrypted config.
We wrote this piece for transparency and to inspire other makers. The world we want to see is where technical decisions around privacy and data ownerhsip matter more than marketing promises.