back 2017-04-25 JavaScriptNode.jsVue.jsPsiTransfer
  1. PsiTransfer
    1. Why?
    2. Implementation
      1. Server
      2. Client
      3. Password protection / Encryption
    3. Open Source

PsiTransfer

Simple open source self-hosted file sharing solution.
It’s an alternative to paid services like Dropbox, WeTransfer.

Why?

There are many file sharing solutions around but none of them fits our current needs:

  • Support of big files, >1 Gigabyte
  • Robust and resumable uploads, especially on bad network conditions
  • Ability to brand the layout with a company logo and colors
  • Retention settings to delete the files after some time
  • Easy handling and no login required
  • Self-hosted due to privacy guidelines

A long time ago I’ve played around with tus.io, an “open protocol for resumable file uploads” and it did a good job. So I decided to take this code and extended it with the required features.

PsiTransfer

Implementation

One of the main requirements was simplicity. So I did not want any login-based system. Hiding the content behind hard to guess tokens is a common practice in this case and I’ve decided to isolate the upload sessions by stripped, MD5-hashed UUIDv4 tokens. The names of the uploaded files are also replaced by full UUIDv4 tokens. To make it even harder to catch the files there should be an optional password protection. This could be implemented as end-to-end encryption or at least in a protected download list.

Server

The Server is, of course, written in NodeJS. It uses Express.js to provide some REST-like endpoints and delivers the frontend applications for up- and downloading the files.

I’ve decided to use an in-memory object database. Assuming that there are 500 concurrent file-buckets, each with 20 files with not too long comments - it requires about 10 MiB memory. The metadata for every upload gets stored in a related JSON file which guarantees the persistence between server restarts.

To be able to resume broken downloads PsiTransfer supports requests with range-headers.

Client

To provide a nice user experience the frontend applications leverages the Vue.js framework. The reactivity and component based approach suits perfect for this kind of applications. The download and upload apps are decoupled and uses a REST-like interface for the server communication.

By using the tus.io protocol the client is able to resume broken uploads as soon as the connection gets re-established. PsiTransfer knows whether you’re online or offline. Resuming is possible as long as the browser is not closed.

Password protection / Encryption

The preferred goal is full end-to-end encryption and I’ve started some experiments on this topic. It must support big files. En-/Decrypt the whole file in-memory is not possible. The solution must support streams or at least chunks.

The HTML5 File API can be used to read a file bit by bit. Fetching a chunk, encrypt it in-memory and upload it to the server is a usable solution. So uploading an encrypted file is possible.

Download a file, decrypt the payload and save it seems to be the more challenging task. Most browsers don’t provide enough capabilities to do this. Especially providing the “save as” dialog for streaming data is challenging. That’s why I put in-browser end-to-end encryption on hold for now. See StreamSaver.js.

As weak compromise, I’ve implemented encryption for the download list. If the download app requests the files from the server it will receive a list of AES encrypted strings. After entering the key the decryption gets computed by the browser. But consider, the key goes over the wire at upload time and the files are unencrypted on the server’s filesystem.

Open Source

PsiTransfer is licenced under BSD which means it’s free as in free speech, not free as in free beer. :)

Sourcecode and release-packages on Github: https://github.com/psi-4ward/psitransfer


Interesting posts

Dynamic, event based reverse proxy for Docker containers with custom resolving algorithm
Installation and configuration of PsiTransfer
Nginx reverse proxy setup to host multiple applications using Docker