Faster loads with SharedWorker & ServiceWorker

When CryptPad was first created, the only thing to load was the CryptPad code itself and the pad which you were editing. Recently edited pads were remembered in the browser’s localStorage which was not portable between computers but allowed some recent history to be kept.

However, we wanted to allow people to login and manage their pads on all of their devices so we created the CryptDrive. Anyone who has used CryptPad but never logged in is encouraged to register and login (it’s free) and check out their drive. CryptDrive is basically just a realtime pad containing a JSON structure with links to all of your pads as well as their titles and other information. When you update the title of a pad, it changes the pad itself but it also changes your drive so that you can see the title of the pad in your drive. This is of course not perfect because if someone else changes the title of a pad, your drive will not be updated until you look at the pad again, but doing everything with the server completely blind to the content isn’t easy, and this works reasonably well.

However, CryptDrive causes an additional delay when loading CryptPad because whenever you load a document, you are actually loading two realtime instances. Since the drive is loaded over and over for every pad you view, it was obvious to us that we could make it more efficient using communication inside the browser.

First idea: Messages between tabs

Tabs (or windows) in a browser which are on the same website are able to communicate using localStorage and so it seemed like a good solution to just have one tab claim the role of managing the drive and then when another tab is opened, it would message the first. However, this seemingly easy solution becomes a nightmare when you consider what happens when that tab is closed. The drive that everyone is relying on goes away and all of the other tabs are without a drive so they need to flip a coin to decide which tab should become the keeper of the drive, then that tab needs to download the drive before it can service events, all the while any of the buttons which affect the drive (for instance deleting the pad) cannot possibly function. This idea was soon scrapped…

Enter ServiceWorker

Recently, the HTML5 working group created a new standard called ServiceWorker which for someone making a webapp seems like a dream come true. ServiceWorkers:

  • Are side-processes which are created one-per-website and live in the background.
  • Can intercept HTTP requests from your main javascript (excellent for caching!).
  • Is suspended when the last tab is closed, and re-launched when the user returns.
  • Are stored in suspended state even when the browser is turned off.
  • Supported by every modern browser.

When the website loads a worker, if the worker is already running it will not load and will instead defer to the existing worker. Communicating between workers and tabs which are on the website is possible via a postMessage() API and then the ServiceWorker can postMessage() which will reach all tabs that are navigated to the website.

One limitation of this design is versioning. Because the ServiceWorker would stay alive potentially forever, we needed to identify a way to upgrade it if a new version of CryptPad is released. This is quite important for CryptPad as version mismatches can lead to catastrophic conflicts between different browsers working on the same document.

Though updating is non-trivial, we were able to solve it by sending version messages between different components of CryptPad and informing them whether they need to update (or even if they can optionally update). Since this seemed like a solvable problem, we tried creating an experimental implementatiton of ServiceWorkers in CryptPad, and then the fun started…

ServiceWorkers in Firefox

Since Firefox 48, Firefox has begun following Chrome’s model and running different processes for rendering the different tabs in the browser. However, this isolation has a side-effect that when you attempt to launch a ServiceWorker, it may launch even though one already exists for the same website, because the other one exists in a different tab which happens to be operating in a different process. However, the postMessage() requests from tabs go out to all of the ServiceWorkers so this bug can be worked around.

Unfortunately we encountered some more issues with Firefox which we found not worth debugging. Our CryptDrive is a Javascript object which is represented in the encrypted realtime document as JSON and there is an ES6 Proxy object in order to allow every change to the drive to be propagated to the underlying realtime object. In Firefox the proxy did not work when used in a ServiceWorker and at times our ServiceWorker simply stopped running.

Reporting bugs in browsers

One might think these are great opportunities to report issues with Firefox, and when we find an issue which we think will affect lots of people especially if it is a regression, we don’t hesitate to report it, but usually it is not clear how to reproduce the issue, whether there might be some error in our usage of the API which is being smoothed over by Chrome, or whether the component being reported on is a priority, in that case rather rain low quality bug reports down upon the poor browser developers, we spend our limited time trying to make CryptPad better. In the case of ServiceWorker in Firefox, our conclusion is that in effect the technology remains experimental and shouldn’t be relied upon.

SharedWorker to the rescue

Fortunately there is another technology called SharedWorker which is essentially identical to a plain vanilla WebWorker but can communicate with all tabs which are navigated to the site. Unfortunately this technology is only supported in Chrome and Firefox, but the support for this technology, we found, really works!

However, Firefox still has the issue with of multiple SharedWorkers being created for multiple tabs, but since this issue is fixable we were able to go ahead with it anyway. For browsers which have no SharedWorker, they would fallback to plain old WebWorker. Though this seems like it would be a problem, it is in fact quite fine because actions done in the drive by another WebWorker are the same as actions done in the drive by another device, they need to be encrypted and sent to the server in order to persist anyway.

Faster CryptPad

Coming in the release on Tuesday June 26, 2018, we will have a new SharedWorker based CryptPad instance, that means when you use Chrome or Firefox, the first time you open CryptPad, it will load your drive, but then every tab you open after that will communicate with the SharedWorker managing the drive and therefore pads will load nearly twice as fast.

Signing CryptPad

CryptPad was designed with a view that privacy should be default and cryptography should be invisible. In order to do this, we made use of the web-app model so people could just go to and immediately begin using the app, no installation necessary. However, this model has a known flaw, the server can decide what client-side code it will send to any given user, allowing a compromized server to serve code with a back-door vulnerability.

Recently, I did an experiment to make CryptPad more secure against these types of attacks by signing the code. CryptPad is a unique webapp, even without considering the encryption aspect. There is no build system, the code we write is exactly the same as what your web browser runs. All of the CryptPad html, javascript and resources are static files which are served by a plain old web server. The data persistance is managed by an API server which the web browser communicates with using an HTML5 WebSocket. Finally, in order to add a layer of security against possible Cross Site Scripting attacks, CryptPad makes use of a cross domain iframe, protecting your encryption keys from the majority of the CryptPad code in the same way that your online bank is protected from that sketchy porn site open in another tab.

Since CryptPad has no build system, there are many small javascript files which must be loaded. To do this, CryptPad uses RequireJS. While many small files are generally considered to be bad for website performance, RequireJS uses the HTML5 async attribute to tell the browser not to block loading of other things while waiting for the scripts to load. Secondly, RequireJS also allows version numbers to be added to the script URLs which allows us to cache almost everything in the browser. Finally, we use the HTTP/2 protocol to serve resources because it allows multiple requests to be sent at the same time, while HTTP/2 is incompatible with WebSocket, this is ok because the web-app is served from a different server from the API server.

Chain of Trust

Just one corrupted script is enough to render the security of an entire web-app useless, so in CryptPad we needed the signing to cover all javascript files. Fortunately there is a new HTML5 technology called Subresource Integrity which allows putting the hash of a script in a script tag attribute and makes the browser verify the script before executing it.

Insecure, can serve you anything:


Secure, only one possible script can be sent by or else the web browser will throw an error and refuse to run the script:


So rather than signing every script, I only needed to make a list of hashes of every script, and sign that. What I needed was a way to generate a manifest, and so I developed a small program which could hash all of the javascript files in CryptPad and generate a manifest file. The content of the manifest looks something like this:

"files": {
"assert": {
"frame": {
"frame.js": "BrN2JNnK4QJCztw3PyRRPAsEwSq5lczTBrRkzdLAFow=",
"respond.js": "yO0KFMHiCdE1fXFWPVaFB+Mmh37OCl/UNPpXrYtWF7A="
"main.js": "ABf3uhmYVHWaHX6vhK8K2jAUY8XqRjjMJ2FqXVGLZE0=",
"translations": {
"main.js": "50Ami2eghyXcGKGYTaDK1vUeEuAEG7kcpvUoCKbUaUU="

It contains a JSON tree which mirrors the files that are part of the CryptPad codebase and the hashes of the files for the Subresource Integrity check. Once the manifest.js file was created, then I needed a javascript file which would load and verify it. Since the manifest is different every time a new release is made, the verification of the manifest needed to be via signing. The manifest hash was signed along with a version number and those were placed in a file called version.txt and version.txt is loaded using a file called sboot.js. The hash of sboot.js was included directly into the html files which are cached, so sboot.js can never be changed at all.

Loading process


First, the browser loads the html file, the html file contains a single script tag loading sboot.js


There is a custom attribute called data-bootload which indicates which javascript file should be loaded for that html file.


When sboot.js gets loaded, it downloads and then verifies version.txt which is a signed message containing the CryptPad version number and the hash of manifest.js. The content that is signed looks something like this:


The version number (85) is not the CryptPad version but rather an auto-incrementing number which is stored in the browser localStorage and prevents the server from downgrading the version of CryptPad. After the signature/version check completes successfully, sboot.js loads manifest.js like the following:


You will notice that the hash is used also in the URL of manifest.js, this allows the server to signal that the files are immutable and can be cached by the browser forever which makes CryptPad load faster next time.

After manifest.js loads, sboot.js finds the hash of require.js in the manifest and then manually loads require.js in the same way. Once require.js is loaded, sboot.js configures require to use the hashes from the manifest for every file it loads, then it uses require to load boot2.js.


This file is not needed for security, but unlike sboot.js, it can easily be changed from release to release and it contains any code which should be run before the main CryptPad code. Things such as additional requirejs configuration and shims for missing browser APIs are placed here. After boot2.js is complete, it reads the data-bootload attribute from the html file and invokes require to load that.

Further development

While this system provides excellent security, it is still not perfect. If the root html file is compromized then it can alter the chain of trust, or scrap it completely. With a very long cache header, the browser will store the html file essentially forever, but if the user triggers a hard reload with the F5 key, then the cache will be flushed.

The root html file can be signed using pgp and then verified using the signed pages chrome extension. But signed pages is not able to prevent the loading of the website even if the signature is invalid and it only takes 1 second for the keys in localStorage to be leaked.

If the root html file was generated by the server each load, it could contain a secret key which is used to encrypt the keys in the localStorage, thus rendering them unusable if the html file is re-loaded, and meaning that the user must re-enter their password and would then be able to see that the signature on the html file is invalid, however unless signed pages can ignore the key inside of the html file when verifying the signature, it would have to be re-signed every time, pushing the pgp key onto the server, which we are worried about being compromized.

There are also a number of configuration files in the CryptPad project which are in fact javascript files and would thus be signed by the release manager, preventing anyone hosting CryptPad from changing them so it may be a long time before this project is merged into CryptPad mainline, however it is available and you can experiment with it by checking out the code-integrity branch of the CryptPad project.

One year of biweekly releases

Approximately one year ago, we published an article about our intention to follow a biweekly release schedule. Since then, we’ve thought of every second Tuesday as release day. Starting with the letter A on February 21st, 2017 and ending with the letter Z on January 30th, 2018, we went through the whole latin alphabet.

Throughout this self-imposed regimen, we managed to meet each deadline, though on occasion we had to stay at the office later than usual. Any time a release was more difficult than expected, we considered what went wrong and added a counter-measure to our release checklist (in CryptPad, naturally). Each release became easier than the one before, but even so, we found there were some drawbacks to this rigid schedule.

Changing our pace

Having a regular rhythm for our releases trained us to break up complex features into components that could be implemented within a two week period. With that in mind, not all tasks fit neatly within two weeks, and those larger tasks have had a tendency to get pushed to the next release. So, now that we’ve finished one full year of releases, we’ve started to look more closely at those larger features which we’ve inadvertently neglected. The tendency to procrastinate on especially difficult features is just another challenge to approach, but it’s been one which is somewhat more difficult to summarize within a to do list.

As of CryptPad v1.26.0, we’re no longer following the strict biweekly schedule. To be clear, this doesn’t mean we’re slowing down development. We’re still working on CryptPad consistently. We still plan to deliver features to users as soon as they are stable. We still plan to deploy on Tuesdays, since it’s as early in the week as possible without falling on a Monday and deploying on a Friday is a terrible idea.

Should I release on a Friday?

Some releases might happen in a single week. Others might take stretch to three weeks or a full month, but we’ll do our best not to take any longer than that. We try to be as transparent as possible with our plans, and so users should expect each release to also specify the projected date for the following release.

What’s coming next…

In the last year, we tried to find a balance between improving user security through the use of our sandboxing techniques, and implementing the productivity features necessary for people to consider the security improvements an added bonus rather than an impractical ideology. The core of our philosophy is that security and ease of use must be packaged together in order for tools like CryptPad to benefit users.

While CryptPad is considerably more than a proof-of-concept, we don’t consider it anywhere close to being finished. As heavy users of CryptPad ourselves, we are aware of its rough edges. Our community has been very supportive with our continued development, though, so we’re excited to be able to improve the following areas!


We spend a lot of time passing links between each other when collaborating on a project. In order to do so privately, the sender and the receiver must both use an encrypted messenger, or else the message could be intercepted by a malicious third-party. The necessity of having to use a second tool makes it so that CryptPad must always be used as part of the solution, rather than solving a user’s problem outright.

We’d like to approach this problem with two improvements:

  1. integrate our existing encrypted messenger better with the rest of CryptPad’s functionality (as per this issue)
  2. develop a method of sharing entire folder structures from a users drive, so that sharing can scale to support large-scale projects, a feature we call Workgroups

Layered protection

Even if the link for a pad is shared securely, there is the possibility that somebody discovers that link through other (possibly malicious) means. We’re interested in developing CryptPad’s basic two-tier permission system (edit/view) to address such concerns. This could be accomplished in very different ways:

  • add the ability to protect a pad with a password, so that anyone who finds the link must also know a secret value in order to retrieve the pad’s history from the server
  • add the ability to encrypt messages such that only a designated set of users can decrypt them using public-key cryptography

So far CryptPad only features a very limited set of cryptographic techniques. Going forward, we hope to find ways to implement a more granular permission system which does not rely on the server behaving correctly. Some of these features are more in the realm of applied-cryptography research than web application development, so it’s very likely they’ll be implemented later, but we’re excited about them nevertheless.

Increased trustworthiness

Even though CryptPad performs its encryption in your browser, there is always the possibility that a compromised server could send malicious code to the browser which would cause it to reveal its secrets. We’re interested in developing features which would mitigate these kinds of attacks against users, through a combination of modern browser features like sub-resource integrity, and perhaps a registry of verified code signatures signed by us, the developers.

It’s very difficult to make a web application secure against those responsible for delivering its source to your browser. We’re actively searching for any kind of technique for securing that makes CryptPad a more robust platform for storing your data privately, even against ourselves.

Password usability

CryptPad’s login system looks quite conventional at first glance, since it has a field for a user-name and password like most other web applications. Unlike those other applications, we never learn your user-name and password. Instead, your browser uses those fields to generate a unique secret value which you use to encrypt your drive, and accomplish a range of other tasks. The downside of this approach is that if a user forgets their username or password, we can’t help them gain access to their documents. If we could, we’d be able to access their documents ourselves.

Fortunately, we’re not the only team building web applications that use cryptography, and so there has been some research into how to improve password-based workflows without revealing secrets data to the server’s host (us). We’ve already received emails from users who’ve inadvertently locked themselves out of their accounts with no way to recover their data, and that’s a situation we’d like to help people avoid, so this is a feature we’re looking forward to offering.

More applications

Lots of users have requested that we add a few more applications to CryptPad, and we’ve been listening! We plan to add support for spreadsheets, and possibly other applications so that people don’t have to fall back to using an unencrypted CryptPad alternative.


One advantage CryptPad has over other conventional collaboration platforms is that a lot of the difficult computation is run in the users’ browsers, rather than on our server. This means that we can support a very large number of clients without a noticeable decrease in performance. Even so, the number of people using and the amount they use it has increased dramatically.

Though excessive popularity is a wonderful problem to have, we’d like to avoid a situation where users have difficulty accessing our service, so improved scalability is something we’d like to work on.


We haven’t yet decided how long we intend for this release cycle to last, and we don’t have a set list of the exact goals we’d like to accomplish. We’re taking a step back to decide where we’d like to go next.

We recognize that CryptPad’s growth over the last year was driven largely by word of mouth between friends and colleagues who care about privacy. By moving towards a more flexible schedule, we hope to make it easier to adapt to the frequent feedback we receive from people using CryptPad to help them accomplish a multitude of goals.

We’re very interested in hearing what you think about this change. Feel free to reach out to us through any of the methods listed on our contact page.

CryptPad's new Secure Cross-Domain Iframe

CryptPad version 1.14 (Codename Ouroboros) has been released and the most exciting new feature is one you cannot even see. As you may remember from the Security Growing Pains post, Content Security Policy is a significant part of CryptPad’s security model, and it is unfortunately incompatible with CKEditor, the Open Source text editor used in CryptPad.

With this release, we have done a significant re-architecture of the CryptPad codebase. Starting with the /pad/ application, the CryptPad UI has begun a process of moving into an iframe which is hosted on a different domain: Moving the visual content to a different domain means that even in the event of a Cross-site scripting security vulnerability, most of your private information such as the pads in your CryptDrive, will not be at risk.

In this version we updated only the /pad/ application to use the cross-domain iframe because it is the only app which requires inline script. This prevented us from using Content Security Policy to block the most significant vector for Cross Site Scripting attacks but now with the cross-domain iframe, such attacks are mitigated.

Going forward, we plan to implement a standardized CryptPad application API so that new applications can be developed, installed and used in CryptPad. Today, the CryptPad API which is exposed to apps such as /pad/ and /code/ is not standardized and there is no clear line between the apps themselves and the CryptPad internals. As we move toward the a standard app API, we will define a standard representation of a CryptPad application with such additional aspects as the app’s color-scheme and icons.

Fundementally, this unexciting change to CryptPad begins a new phase in development, we plan to move from a set of integrated prepackaged applications to an ecosystem of applications for collaborating on different types of content with the same encryption under the hood.

But Wait, There’s more

The pictures in this blog post are not hosted on the blog, they are in fact Zero Knowledge files uploaded on CryptPad. They can be seen on this blog because it is using the Media Tag which was developed as part of the UCF Project with the support of Systematic, BPIFrance and the City of Paris.

Media Tag allows files on CryptPad to be included in any website (such as this blog). All you have to do to include a file from CryptPad is simply include the Media Tag loader and then add a Media Tag to your document, just like the following:

<!-- At the top of your HTML file -->
<script src=""></script>

<!-- Where you'd like the image to be located -->
<media-tag src="" data-crypto-key="cryptpad:VE4raHL5VFReAXxioTaFZwt6q2jpxX+bdFHAFeoivZQ="></media-tag>

With this you can embed files from your CryptDrive into any website you want.

CryptPad's New Direction

If you are hosting CryptPad, please make sure you are up to date. CryptPad 1.13.0 (Naiad) fixed a major security issue.

CryptPad was born on Halloween 2014, at that time it was a skunkworks project inside of XWiki SAS. The UI was hidious green and white and the only feature was the CKEditor based pad. We have come a long way.

Old CryptPad Main Page

As was mentioned in Building Mututally Beneficial Relationships, CryptPad cannot ever be great without people developing the software as their daily job. We have been able to develop this project with the generous support of BPI France and the OpenPaaS::NG but that support only finances a small team and it will not continue indefinitely.

Starting with this release, we are adopting a new look and a reinforced dedication to making a quality product for people whose time is valuable. We’re starting this by upgrading the logo and the informational pages.

New CryptPad Main Page

Our lovable fist logo was created one night by grabbing a screen shot of an ascii generator. It was a time when I was racing to get something working to prove that CryptPad was an idea worth pursuing. Now times have changed. CryptPad is finally something that I’m starting to feel proud of, and the logo represented the last reminents of a time when everything was a rush and quality was an afterthought.

We also have introduced a lot of new features such as:

  • New front page which allows creating a pad in 1 click
  • Clickable links in pads when viewed in read-only mode
  • File-picker for embedding media in a pad in Markdown mode
  • You can now have your preference between tabs and spaces, when editing in the code editor

Registered users can uploading and view PDF Files

But more than features, we have focused on making CryptPad easier to use. It’s now easier to paste text into the pad without breaking the formatting and we have additional tool-tips to help explain different features of CryptPad.

Going forward from today we plan to make CryptPad easier to use, more secure and more extensible. We are rewriting the pad logic in order to run in a cross-domain iframe which will make use of the browser’s Same Origin Policy as a sandbox to block most of the code from accessing the decryption keys.

This will open the door to 3rd party applications developed for CryptPad which can be protected by the same cryptography as CryptPad and which have limited access to the CryptPad system.