Tala v2: betting on local-first

The vehicle details page.
The vehicle details page.

I built the first version of Tala, my digital car maintenance logbook, like a small SaaS. Authentication, vehicle CRUD, job CRUD, photo uploads. A Flutter app talking to a Go/PostgreSQL backend across two repos. To develop and use it I had to run two services, commit in two places, debug in two places, and host the backend somewhere I could reach from the garage.

I was the only user. I had one vehicle. It was over-engineered.

Tala was supposed to be a fun side project I'd actually use while restoring my '72 MGB GT, but every time I wanted to log a job I had to ensure it was deployed and running. So I didn't log jobs, and I stopped working on the app. It was heading to the projects-graveyard.

Except I still wanted the tool. I just needed to build the version of it that matched what I was actually doing.

Three options

I had three paths going forward:

A. Keep the split, self-host cheaply. Containerise with Docker, deploy on a small VPS, point Flutter at it. Cheap and simple to deploy and run, but still a server to maintain.

B. Go local-first, drop the backend. Swap the Go backend for SQLite (via drift) inside the Flutter app. Fully offline. Backend code shelved for later.

C. Monorepo with sync. One repo, frontend and backend together. Write locally first, push to the server when reachable.

C is perhaps the right long-term architecture, but it's also the most work and "long-term architecture" was exactly the trap that killed v1. A would have changed very little about what was actually broken. B was the smallest thing that could be useful. All the data lives on one phone with no backup or sync, but that problem only exists if I'm actually using the app, which I wasn't.

So: B now, C later if usage demands it.

Phase 0: Strip it back

No new features, just re-plumbing. The Flutter side already had repository interfaces (VehicleRepository, JobRepository, PhotoRepository), so the migration was mostly writing SQLite-backed implementations and swapping them in via dependency injection in config/.

A few specific calls:

  • Drift for the database. Type-safe queries, generated code, built-in migrations. The migration support matters if I want to ingest sensor data from the battery monitor later.
  • Photos go to the app documents directory. Store the file path in SQLite instead of a server URL.
  • Auth removed. Login and register screens gone, JWT handling gone. The app bootstraps a local profile on first launch. User model is still there (name, email) in case I want it back.

Phase 1: Make it usable in the garage

I'm in the middle of a job and realise something that needs to be done, I want to log it while I remember it. But I have dirty hands, bad lighting, and about 30 seconds of attention before I forget and keep doing what I'm doing. How do I make sure I use that time and actually log the job?

The main change was a quick-add job button. One tap, then title, category, date (defaulting to today). Description, cost, odometer, status are all optional, all fillable later when I'm at a desk. Alongside it I had predefined categories. Maintenance, repair, restoration, inspection, upgrade, electrical, bodywork, with an "other" escape hatch. Not sure yet whether the category field should be mandatory; trying it required for now.

These were fundamental changes to ensure I would be able to add jobs quickly. A few other improvements followed. Jobs can have photos attached or taken inline. Filters on jobs by status (planned / in progress / completed), category, and date range make it easy to see what's actually going on.

Lastly I updated the vehicle detail page to include photo, basic info, plus some simple job stats (counts by status and total cost so far).

What I'm not building yet

There's a list of things I could add: projects to group related jobs, parts tracking, receipts and document attachments, export and backup, search across job content (AI, maybe), and a sync layer back to the Go backend still sitting in the other repo. I'd also like the design to lean more vintage, maybe British racing green, or something with character. I also want to eventually pull in data from the battery monitor, GPS, and other sensors I've been building.

But none of that matters until I'm actually using the thing.

That's the lesson I'm learning here: the right architecture is whatever lets the project survive long enough to tell you what it needs. v1 of Tala wanted to be ready for users and usage it didn't have or need. v2 just needs to be ready for me, in the garage, this Sunday.