Running TiddlyWiki on Node.js

TiddlyWiki is an awesome personal wiki, but if you have found this page then you probably already know that. Either you know me personally, and I won’t stop talking about it, or you have searched for TiddlyWiki explicitly and don’t need me to tell you about it.

I have been using TiddlyWiki to keep track of just about everything for the last couple of years, from packing lists to general research notes. Here is my setup, which makes it accessible everywhere, secure, and backed-up.

In the following examples I will use the name “mynewwiki”, but you should replace this with a better name.

Installing TiddlyWiki on Node.js

The official instructions for setting up TiddlyWiki on Node.js are actually very complete, but if you find any gaps let me know. I am running on an Ubuntu Server, so I installed Node and NPM through apt.

$ node -v
v4.2.6

$ npm -v
3.5.2

Next, install TiddlyWiki itself and create an instance.

sudo npm install -g tiddlywiki
tiddlywiki mynewwiki --init server

Daemonize TiddlyWiki

By default TiddlyWiki does not come with any mechanism for starting automatically. As of Ubuntu 15.04, daemons are primarily stared with systemd and the following script will daemonize TiddlyWiki. To use it, set the path to your installation and pick a username and password. Finally, set the system user which should run TiddlyWiki.

Place this file in /etc/systemd/system/mynewwiki.service.

[Unit]
Description=TiddlyWiki 5 Launcher
After=network.target

[Service]
ExecStart=/bin/sh -c "/usr/bin/node /usr/local/lib/node_modules/tiddlywiki/tiddlywiki.js /path/to/mynewwiki --server 8080 $:/core/save/all text/plain text/html <username> <password>"
Type=simple
User=<system username>
Restart=on-failure
RestartSec=5
StartLimitInterval=60s
StartLimitBurst=3

[Install]
WantedBy=multi-user.target

After replacing the four placeholder values and saving the script, you must enable it and manually start it the first time.

sudo systemctl enable mynewwiki.service
sudo systemctl start mynewwiki.service

Proxy through Apache

The above script launches the server with a username and password, however you should absolutely not expose that unsecured like it is now. To secure my connection to the server I proxy through Apache with mod_ssl enabled and a free certificate from LetsEncrypt.

<VirtualHost _default_:443>
    ServerAdmin me@example.com
    ServerName tiddlywiki.example.com

    ProxyRequests On
    ProxyPass / http://localhost:8080/
    ProxyPassReverse / http://localhost:8080/

    Header always add Strict-Transport-Security "max-age=15768000; includeSubDomains; preload"

    AllowEncodedSlashes on

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    SSLEngine on
    SSLHonorCipherOrder on
    SSLProtocol all -SSLv2 -SSLv3
    SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"

    SSLCertificateFile /path/to/letsencrypt/live/tiddlywiki.example.com/fullchain.pem
    SSLCertificateKeyFile /path/to/letsencrypt/live/tiddlywiki.example.com/privkey.pem
    SSLCertificateChainFile /path/to/letsencrypt/live/tiddlywiki.example.com/chain.pem

    <FilesMatch "\.(cgi|shtml|phtml|php)$">
                    SSLOptions +StdEnvVars
    </FilesMatch>
    <Directory /usr/lib/cgi-bin>
                    SSLOptions +StdEnvVars
    </Directory>

    BrowserMatch "MSIE [2-6]" \
                    nokeepalive ssl-unclean-shutdown \
                    downgrade-1.0 force-response-1.0
    # MSIE 7 and newer should be able to use keepalive
    BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost>

Setup Backups

There is a plugin for TiddlyWiki which provides some basic versioning, but I prefer to roll my own versioning and backups. I use cron to automatically tar the full contents of my TiddlyWiki every 15 minutes. The md5 hash of this archive file is then compared to the previous archive and is only kept if the hashes are diffent. Here’s the script:

#!/bin/bash                                                                                                                                          
                                                                                    
backups_dir="/path/to/backups"                                        
                                                                                    
previous=$(ls -t ${backups_dir}|awk 'NR==1')                                        
next="mynewwiki.$(date +%Y%m%d-%H%M).tar.bz2"                                          
                                                                                    
cd /path/containing-mynewwiki                                                                       
tar -cjf ${backups_dir}/${next} mynewwiki                                              
                                                                                    
cd ${backups_dir}                                                                   
                                                                                    
md5_previous=$(md5sum ${previous}|awk '{print $1}')                                 
md5_next=$(md5sum ${next}|awk '{print $1}')                                         
                                                                                    
if [ "${md5_previous}" == "${md5_next}" ]; then                                     
    # Don't store the backup if it is the same                                      
    echo "Removing the old one"                                                     
    rm $next                                                                        
else                                                                                
    echo "Not removing the old one"                                                 
    # Keep the last 300 archives                                                      
    rm -f `ls -t | awk 'NR>300'`                                                    
fi                         

Simply add this to your /etc/crontab at the frequency you want to check for changes.