Introducing Tinyban

Published on 2019-08-18

I created a simple kanban style to do list web application called Tinyban.

a tinyban board in dark and light mode

It is rather simple: You get as many boards as you want. Boards have a title and can either be private or public. Private boards can only be handled by their creators. Public boards can be viewed by anyone who knows the link.

After creating boards you are able to put tasks on them. Tasks have a title and a body. The body may contain a limited subset of markdown. Tasks can be in one of the three columns: To Do, Work In Progress and Done.

And that is it. Exactly as much as I need at the moment, no more, no less. And no JavaScript.

Tech stack

The web application is written in Ruby using the Ruby on Rails framework. Styling was done with Bulma. The bulma-prefers-dark theme makes dark mode possible depending on prefers-color-scheme.

Tinyban is hosted on Heroku, because it is the easiest way to get a Rails application online. I wanted to spend my time and energy on developing the application rather than installing and securing some kind of server, providing a ruby runtime on it and setting up PostgreSQL, mail handling etc. pp.

Heroku supports custom domains via CNAMEs. I chose Cloudflare as DNS provider, because they are the only company (I could find) offering CNAME flattening on root domains for free.

CNAMEs can usually only be put on subdomains (subdomain.domain.tld). If you put a CNAME on a root domain (domain.tld) on Cloudflare, they resolve whatever DNS name your CNAME is pointing to and provide that as A records.

cloudflare dns settings

Here is some shortened dig output to show how it works:

user@host: ~$ dig CNAME www.tinyban.com
;; ANSWER SECTION:
www.tinyban.com.  300 IN  CNAME host.herokudns.com.

user@host: ~$ dig CNAME tinyban.com
;; AUTHORITY SECTION:
tinyban.com.    2817  IN  SOA alex.ns.cloudflare.com. dns.cloudflare.com. 2031678281 10000 2400 604800 3600

user@host: ~$ dig A www.tinyban.com
;; ANSWER SECTION:
www.tinyban.com.  101 IN  CNAME host.herokudns.com.
host.herokudns.com. 60 IN A 1.2.3.4
host.herokudns.com. 60 IN A 1.2.3.5
host.herokudns.com. 60 IN A 1.2.3.6
host.herokudns.com. 60 IN A 1.2.3.7
host.herokudns.com. 60 IN A 1.2.3.8
host.herokudns.com. 60 IN A 1.2.3.9
host.herokudns.com. 60 IN A 1.2.3.10
host.herokudns.com. 60 IN A 1.2.3.11

user@host: ~$ dig A tinyban.com
;; ANSWER SECTION:
tinyban.com.    44  IN  A 1.2.3.4
tinyban.com.    44  IN  A 1.2.3.5
tinyban.com.    44  IN  A 1.2.3.6
tinyban.com.    44  IN  A 1.2.3.7
tinyban.com.    44  IN  A 1.2.3.8
tinyban.com.    44  IN  A 1.2.3.9
tinyban.com.    44  IN  A 1.2.3.10
tinyban.com.    44  IN  A 1.2.3.11

I put Cloudflare in DNS only mode, so traffic goes directly to Heroku.

Beginning with the smallest paid plan ($7/month for one Hobby dyno), Heroku handles TLS automatically with Let’s Encrypt.

Outgoing emails are handled by AWS Simple Email Service. They offer a SMTP interface which makes configuration with Rails’ ActionMailer very easy. Open a support ticket and request a sending limit increase to be moved out of the SES sandbox which enables you to send emails to all recipients.

To make logs search- and filterable, I added LogDNA from Herokus’ add-on marketplace to the app.

logdna dashboard

Unhandled exceptions are bad. Not noticing them is worse. Sentry is a nice solution to exception management. It groups them, opens issues and can even tell you which line of code caused the problem.

sentry issues dashboard

sentry exception trace

This setup relies on more external parties than I am accustomed to. But it has enabled me to concentrate on building the app alone. I managed to ship at least one tiny feature every day for the past two weeks because productivity feels very high.

Future features

There are some things I think I will want to have in the future: Task submission by email, task attachments and two factor authentication.

Building task submission by email should be rather easy, thanks to the new ActionMailbox framework in Rails 6. Like all nonessential features, this should be opt-in per board. Tinyban will set up one incoming SES email address for every board the user enables it on. Authentication and authorization will be handled by having a long and randomized username part in the email address. Whitelisting sender addresses will probably be implemented at a later point in time.

Task attachments will be done with ActiveRecord and ActiveStorage. I am going to try to implement an ActiveStorage adapter for Backblaze B2 because it is much cheaper than AWS S3. I am not sure whether B2 supports all operations necessary for ActiveStorage support, so let’s see where that is going.

At the beginning, I decided not to use Devise for user management in Tinyban. Customizing it to be as simple and datensparsam as I wanted it to be would have taken longer than implementing what I needed (:has_secure_password) by myself. This means I will have to do more than just include the devise-two-factor gem to enable two factor authentication, but rotp and attr_encrypted will certainly be of help.