Front ends overview

Anything that can speak HTTP is a Pithy front end. There is no SDK you are required to use, and there is no client the kit refuses.

That is worth saying first, because a backend kit’s client story is usually the place where the ownership promise quietly stops. A generated SDK you must use is a dependency on the generator; a client library only published for two languages is a decision about which platforms you may ship on.

What your app talks to is your Worker, over HTTPS, with JSON bodies and an Authorization header. Every capability’s routes are documented, and every one of them is reachable with fetch, URLSession, UnityWebRequest, or curl.

The three things a client needs to know

Which credential to hold is choosing a client credential; what to call is HTTP routes; and the web path is scaffolded by pithy ui.

One origin. Your Worker answers at one address per environment, and it is derived rather than written down — so a client is configured with one base URL and nothing else.

A bearer token. A short-lived JWT access token on Authorization: Bearer, minted from a longer-lived session. The token model is the whole of it, and Choosing a client credential is which one belongs where.

JSON. Every route takes and returns it. Errors have one shape, with a stable domain/reason code to branch on.

The web path is the one the CLI scaffolds

pithy ui add react

That puts a React 19 SPA inside the Worker that serves it — same directory, same build, same deploy, same origin.

No second project. No CORS. No second deploy. The SPA and the API ship as one artifact, and the asset router decides per path whether the Worker answers or the shell does.

It scaffolds the router, the styles, the sign-in screens when the Worker composes auth, and one screen of your own — and it never overwrites anything, ever. A file already on disk is left byte-for-byte alone. Pithy authors a file once, and from that moment it is yours.

What the client knows about your backend comes from virtual modules resolved in memory rather than a generated types file on disk. So there is no artifact to regenerate, no generated file to gitignore, and no moment where the client and the backend disagree because somebody forgot to re-run something.

Add React is the walkthrough; One origin, one deploy is the argument.

Everything else is documented, not scaffolded

A React stub exists because one path has to be the paved one. The rest are documented because they are the reason a backend kit exists at all — an app that only runs in a browser did not need a backend kit, it needed a framework.

ClientThe page
iOSNative iOS — Swift, the session in the Keychain, Apple Sign-In and StoreKit landing on the same entitlement
AndroidNative Android — Kotlin, encrypted storage, Play Billing landing on the same entitlement
UnityUnity — the platform most likely to reach for several capabilities at once, and the one with the most awkward HTTP story
Godot, UnrealGodot and Unreal — the same shape, honest about being less exercised
Electron, TauriDesktop — same-origin rules do not apply, so CORS and the cookie path change
A script, a serverCLI and server-to-server — a bearer token, no browser, and where the credential lives
An agentPithy is agent-drivable

Mobile and web are both first-class

Not a slogan — a design constraint that shows up in the auth model.

Mobile uses bearer. There is no ambient credential to forge, so it is CSRF-exempt. The session lives in secure device storage and is exchanged for a short-lived access token.

Web has two options. The same bearer flow, for a SPA holding the token in memory. Or a cookie session, CSRF-protected — and when cookie mode is on, CSRF protection is on with it, not configurable apart.

The consequence worth knowing: a person who buys on iOS is entitled on the web, because the entitlement is a row in your D1 rather than a fact about a device.

What a client is never asked to do

Validate a token. Verification happens in your Worker, locally against the published JWKS.

Know a SKU. Gating code names an entitlement key, not a store product id.

Name an object key. Storage keys are server-derived and opaque; a client supplies a logical path.

Compute a score, a rating or a balance. All three are server-authoritative by default, because a client that can write them is a client that can invent them.

That list is the same list from the other side: what the server holds is what no client can be trusted with.

Starting from an app you already have

pithy ui add react scaffolds into a Worker that exists. If you have a Vite app already, the adopter path brings it in rather than starting over — Adopt an existing Vite app, including its limits.

If your front end is deployed somewhere else entirely and staying there, that is fine too. It is an HTTP client like any other, and the one thing to get right is the trusted-origins list so CSRF and OAuth redirects work.

ESC