Nojus Morkūnas

Blog

When strangers started using my side project

Conatus is a self-hosted task manager I started in July. By September, issues were arriving from people I had never met. They asked for single sign-on, a lighter build on SQLite and a mobile app. One reported that labels couldn't be edited or deleted, which was fixed eight days later. On 18 September Conatus appeared in the selfh.st weekly newsletter, and by early October it had 50 stars on GitHub.

Code from a stranger

On 1 October a pull request arrived from a senior reliability engineer at SpaceX. Yes, that SpaceX! It was the first code anyone other than me had sent to Conatus.

He wanted Conatus to use external S3 storage in his homelab instead of the storage bundled with the project, in his case a Silo instance already running on his NAS behind TLS. It couldn't connect, because the storage client hard-coded an insecure connection. He tracked it down, opened an issue and sent a pull request that turns it into a setting. I merged it the next day.

His change worked when running the app directly, but the published Compose file still hard-coded the bundled storage. My follow-up passes three new variables through Compose. They needed new names because every .env made from the template already sets S3_ENDPOINT=localhost for local development. Reusing that name would have pointed every existing install at localhost instead of its bundled storage after the upgrade.

That last detail is the kind of thing I never had to think about while I was the only user. Self-hosted software has one property that is easy to ignore until other people run it. Everyone runs their own copy on their own server with their own data. A fix I ship today does nothing for them until they upgrade, and an upgrade that breaks their data is worse than no upgrade at all.

Every release has to upgrade cleanly

CI already proved that a fresh install worked. That isn't the path a self-hoster takes. They bring a new release up over a database an older release wrote, and that path had no test at all.

So in August I added an upgrade check. For each published release, it starts that release's own images and Compose file, fills the database using the old version, then brings the new release up over the same volumes. It passes only if the migrations ran, the data survived and the app comes up healthy. Because the old release writes its test data in its own schema, there are no fixtures to keep in step with every past version.

The README promises that any published release upgrades cleanly to the latest one. That sentence is only true because this check runs on every release branch.

Security fixes only help people who upgrade

Two of the most serious fixes so far came from dependencies. In July, npm audit flagged the authentication library as critical. One of its bugs let a configuration error fill the auth object with an error, so a check for whether that object existed would pass even when sign-in had failed. Conatus's request proxy made exactly that check. In September I upgraded Next.js to fix two critical advisories for remote code execution without authentication. Both fixes were version bumps with no code changes.

On a hosted app I would deploy the fix and be done. Here the fix sits in a release until each person pulls it, which is one more reason upgrading has to be safe enough that nobody hesitates.

I also turned off the weekly dependency pull requests. The first run opened ten of them. Security advisories are worth hearing about, but a weekly list of newer versions isn't.

MinIO disappeared

Conatus stored attachments in MinIO, pulled as a Docker image. On 25 September someone opened an issue saying a fresh install failed because the image no longer resolved. MinIO had stopped serving images from Docker Hub and quay.io, so every new install broke on the first docker compose up without a single change on my side.

Four days later Conatus switched to Silo, a maintained MinIO fork with the same on-disk format. Existing data volumes and credentials carried over unchanged.

The code, the issues and the upgrade check are on GitHub. I also wrote about what Conatus lets AI agents do.