Tare: The Application

This article describes the actual application behind Tare. For the more ancillary stuff, like ops or data ingestion, you can check this page.

Overview

Tare is a pretty straightforward server-side rendered app with good ol' HTML+CSS+JS and a bit of HTMX. The actual application uses Axum (Rust) with a couple of SQLite DBs backed up by Litestream. Things like auth and telemetry were mainly handrolled. And no, things have not exploded. It's a small app. The "scalability" guillotine isn't about to drop. Everything is fine.

Frontend

It's probably best to start with the user-facing stuff.

Rust has some great type-safe templating libraries for server-side rendering. I decided to go with Askama, but really, any one of the Jinja-likes is fine. Interactivity is done with HTMX and a bit of JS for the occasional flourish. HTMX is good enough and lightweight compared to frameworks like React. I also appreciate its respect for vanilla HTML and HATEOAS, but it's not something I really lose (gain?) sleep over. It's a pretty neat library and I'm quite excited about its future. I don't really have any complaints about it that don't reduce to skill issues on my part.

Actually, maybe I have one complaint: hx-boost. I still don't really understand what this does exactly and I'm both scared and ashamed to find out at this point. It's enabled for most of the UI.

UX is an ever-improving story. Tare has gone through a couple of minor redesigns as my skills have improved. I'm fairly satisfied with the current state of things, but I still find myself fidgeting with filters and layout every once in a while.

Also, due to my lack of experience with frontend work, accessibility kind of feels like a sword hanging over my head. I'm aware there are best practices, but a lot of it seems more like art than science. I basically just end up asking LLMs to review my code for accessibility issues. I think they do a good job? I have no way of knowing.

If you're using Tare with a screen reader and it sucks, I'm so sorry. Send me a message!

Design

I would describe Tare's design as minimalist, maybe even retro.

Most apps/sites nowadays all look the same. They have a sort of "VC-backed" smell. The smell that you're being A/B tested on. The smell of early-onset enshittification. Liberal use of rounded corners. A hero section with some vague, almost ominous, punchline. Kind of like this. I'd like to believe we're at a point where most people can feel this, at least subconsciously.

The design of Tare was a personal reaction to this. I wanted to go for a scrappier look. Like, "hey, I'm just a guy trying to make something cool". It fits the "local" vibe. It's a bit naive, but I like to believe naivety shines through to users.

Here are some screenshots of what it currently looks like:


Probably one of the more risqué choices I made early on was using monospace fonts. The very, very early design was inspired by The Monospace Web by Oskar Wickström. Fun, right? I still wonder if this looks weird to "normal" people. Do they even notice? I don't really have a satisfying answer right now.

Monospace does allow me to design in a pared down way, though. Formatting with text-only looks kind of weird on Helvetica. It looks more "in-place" with a monospace font. Screenplays are also a place where monospace was traditionally used, and I based a lot of the design and formatting on them too.

The actual font I settled on is called Commit Mono. I also squash the letters together a bit as I find monospace fonts to be a little too stretched out for everyday reading.


The Tyranny of Mobile

The site looks like this on mobile:


I use my phone a lot. I'd like to believe I can tell a good mobile design from a bad one. But man, mobile is hard to design for. What terrible aspect ratios phones have! The site UI was first implemented for desktop, and you can definitely tell. I should have just started with mobile first; that's what most people use. It's much harder to renovate a house than it is to construct one from scratch, after all.

My strategy for retrofitting my desktop site to mobile was basically just an iterative process of using media queries to shrink stuff, remove margins, shift things around. Like I said, in retrospect this probably wasn't the best way to go about it. Also, WebKit is terrible. iOS Safari is terrible. Nothing looks the same. There are so friggin' many peculiarities among platforms.

RANT START;

So much of the work we do as software developers is just patching over the suboptimal work of other people, with our own suboptimal work. The paint's flaking? Time for another coat! A little bit of this is inevitable; for browser frontend work this borders on the absurd. Apple just NEEDS its own browser engine... It's regular software developers, like you and me, who have to bear the brunt of this adventurism. There are so many inconsistencies between platforms. Why can't we just have one? No, AI can't help here. AI can't see shit, yet. I'm also NOT making a mobile app. Maybe a PWA if the stars align, but that would be for different reasons.

RANT END;

Backend

Here's a simplified diagram for the backend:

Tare app diagram

There's honestly not much to say that's application-specific (that's not covered in another section). It's just an Axum Rust app in front of an Nginx proxy. The parts around it are more interesting, in my opinion.

The DBs

The DB system is one of the nicer bits of Tare. I opted for SQLite (over something like Postgres) for a couple of reasons:

Of course, there are some theoretical downsides to using SQLite. The single writer constraint being the most prominent. I tried to keep most of the server write operations pretty simple as a result. It's easier when you have control over the actual product!

Litestream

I freakin' love Litestream. For the uninitiated, Litestream is a program that reads the SQLite WAL and (efficiently?) replicates the database to S3-compatible storage. We are effectively offloading all the hard parts of disaster recovery and reliability to S3. This is an async process and you can configure the replication frequency. If your app server blows up, you can recover the entire DB file from S3 with a simple Litestream CLI command.

There is the potential for data loss due to the replication frequency. This is acceptable for a local restaurant review site.

Migrations

I just handrolled the migration logic. You can "plan" a migration, "apply" it, or "fix" a broken migration state. It's very Flyway-esque. There are migration frameworks out there, but DB migrations at Tare are pretty simple and don't need much. I decided to just avoid the extra dependencies for about a day's worth of work. It's just me running the migrations; I can afford to be opinionated :).

List of DBs

Tare uses different SQLite DBs:

Looking back, I think we could have merged the Main and Img DBs into one... maybe even the Search DB. But with SQLite, having an extra DB doesn't really cost much, so this is more of a nit.

Rust also has a great SQL library: sqlx, which gives you compile-time guarantees for SQL queries. You just need to point the library to your SQLite file (via an env var), and it should only compile if the SQL is syntactically correct for the DB. A very nice-to-have.

Maps

Tare uses MapLibre GL for frontend mapping functionality. I haven't had any issues with this. In fact, it's actually quite simple and extensible. The documentation could be a bit better, though.

Tiles are served with Martin. No complaints either. Looking back, I could have just handrolled tile serving with a new SQLite DB and some extra server code; one less container process to worry about. The tile data is stored as MBTiles anyway, which is already a SQLite file.

The map data is from OpenStreetMap. Our personal philosophies come out a bit in the styling. Most maps have an (imo over)emphasis on roads and highways. We tried to put the focus on more "natural" elements, like parks and streams, and also public transportation, like subway lines. Other than that, we tried to keep the map clutter-free for the most part.

Misc. Backend

Auth

Auth is done with a custom Axum middleware to handle request session cookies. Logging in is password-based and issues new cookies. Pretty simple stuff. I based the original design off this article: Authentication With Axum.

User Lifecycle Management

The logic for this is also handrolled! The only annoying thing was managing password reset flows. I used an external SMTP service for this.

Rate Limiting

There are per-user rate limits and per-IP rate limits for Tare. Per-IP is handled by Nginx, and per-user is handled with another Axum middleware and the Rust governor crate.

Telemetry/Observability

More on this in a coming article on Tare's deployment and operations!

A Note on External Dependencies

If you've made it this far, you've probably noticed that Tare tends to err toward handrolling functionality instead of leveraging external services. That being said, there are two external dependencies of note:

I would also consider these services "commodities". The S3 and SMTP APIs are well-known and the market for them is competitive. SMTP especially; swapping providers is just a config change. S3 is a bit more annoying because your data is stored in your provider's servers. The provider we use, Cloudflare R2, doesn't charge on egress though. So migrating S3 providers is a bit easier. Hopefully it'll never come to that.

Closing Thoughts

Coming from an Enterprise way of doing things, Tare seems pretty simple. And it is! I had to unlearn some biases and assumptions about engineering that I've developed over my time working on "real", "scalable" systems. The concerns and sensibilities of a big tech company simply aren't relevant to small sites like this. No, I don't need k8s. I don't need OTel. I don't need Passkeys. I have to repeat this stuff to myself every once in a while.

Even within this already "simple" system, I think there's an even simpler one just itching to come out. I probably didn't need a dedicated tile server, and a couple of the SQLite DBs could have been merged. I really like this idea of just having a binary and a SQLite file being your backend. It might not be super scalable, but the developer ergonomics would probably be close to programming a static site. I feel like there's a startup in here somewhere...

Oh well. I'm fairly satisfied with the architecture right now. Maybe in Tare V2!