The app checks itself

Notes on shipping a zero-backend app: give the client a static set of public links, let every install do its own fetching — and know exactly where that pattern stops working.

I built a small app for a small problem: I swim with my dogs, and before we drive to the water I want to know whether the water is warm enough to be pleasant but cold enough to actually cool a husky down, whether the tide will be where we want it, and whether a storm cell is going to end the trip early. That's Nauti Husky — a SwiftUI app for iPhone, Mac, and watch that picks nearby swim spots and renders one glanceable verdict per spot.

The interesting part isn't the app. It's what the app doesn't have: a backend. There is no server, no API key, no account, no analytics, and no monthly bill. The client ships with a static catalog of public endpoints and checks them itself.

The four sources

Everything the app knows comes from four sources, all fetched directly from the device:

Each spot's conditions are gathered concurrently, and every source is allowed to fail on its own — a dead tide station doesn't take down the weather, and a missing lightning feed falls back to a heuristic derived from the forecast. Scoring (which hour is best, how well the water will cool a dog) happens on-device. The "architecture diagram" is a list of URLs and a for-loop.

Why this works at all

This only works because every feed in that list is a free, keyless, public API that's designed to be consumed directly. NOAA and NWS are public-money data with generous, anonymous access. Overpass is community infrastructure with documented fair-use expectations. Nobody issues you a credential, so there's no credential to protect, and each install's traffic is its own — a thousand users don't share a quota, because there's no account for a quota to hang off of.

What you get in exchange for choosing your data sources this carefully:

Where the line is

Here's the part that makes this worth writing down rather than just pleasant: the pattern has a hard boundary, and it's exactly one feature away.

You cannot ship a secret inside a client. Anything in the binary is public — so the moment an app wants a keyed API, the key has to live on a server you run, and the whole zero-backend property evaporates. For Nauti Husky that moment has a name: real lightning-strike data. The good strike feeds are commercial; a paid key can't ride in the app. The repo already carries the scar tissue — an api/ directory with a small FastAPI service, scaffolded and unused, whose README says plainly what it's for: hide a future paid key, merge feeds, cache across users. The backend grows back the instant you want something the public feeds don't give away.

So the design discipline isn't "never have a backend." It's knowing which side of the line every feature lives on, and noticing when a feature request is actually a request to cross it. Cross it deliberately, for something worth running a server for — not accidentally, because a library made it easy.

This is the same instinct as the token piece, pointed at a different bill. There, the discipline was spending premium tokens only on what a frontier model is uniquely good at. Here, it's spending server-side infrastructure only on what can't run on the device. In both cases the architecture question is the same one: what's the cheapest place this work can happen, and what's stopping it from happening there? Surprisingly often, the answer to the second question is "nothing."


The app is free and MIT-licensed at github.com/botelle/nauti-husky — a single xcodegen generate away from building. The curated spots are seeded for coastal Connecticut, where the dogs are.