Desktop

The question that decides everything

It is the same question choosing a client credential asks, answered for a machine that may have no browser: the token model is what you hold, server to server is the answer when nobody is sitting there, and connecting without a browser is the device-code path when somebody is.

Does your app load your origin, or does it call it?

If it loads your origin — a webview pointed at your deployed site — you get a session cookie and the CSRF guard, exactly as a browser does. Nothing special to do.

If it calls your origin from a native shell — a Tauri command, an Electron main process, a Rust or Go binary — you hold a bearer token, exactly as a mobile client does.

Most desktop apps are the second one, even when they render with a webview, because the webview’s origin is a local scheme rather than yours.

Holding a token on a desktop

Use the platform’s credential store. Keychain on macOS, the Credential Manager on Windows, the Secret Service on Linux.

Not a file in the app’s data directory. A refresh credential is long-lived and revocable — which means it is worth stealing until it is revoked.

Rotate on use, and hold the rotation in one place so two windows cannot fire it concurrently.

Signing in without a browser round-trip

An emailed one-time code is the simplest desktop flow. Six digits, five minutes, single-use — no deep link, no custom scheme, no redirect to catch.

A magic link works too, if you can handle the return. The CLI’s own device-code flow is the shape to copy when you cannot: show a short code, open a browser, poll.

What the CLI already does, and you can too

pithy dashboard connect runs a device-code flow against a management client’s origin, prints a user code, opens the browser, and polls.

That is genuine user delegation, which is exactly why a browser authorization flow belongs there and not on a machine-to-machine leg.

If your desktop app is the trusted server

It usually is not.

A desktop binary ships to users, so anything embedded in it is public — a scope you would mint for a trusted server does not become safe because it is compiled.

A score submitted from a desktop client is a score the user can submit. If that matters, the submission has to come from your own server.

Auto-update and the config directory

The kit’s own config directory is a CLI concern, not something your app should write to.

Your app’s credentials belong in your app’s own store, under your own name.

Offline

There is no offline mode. Every route needs the network, and a session resolved server-side cannot be resolved locally.

Your client owns its own offline behavior — a queue of pending writes, a cached read model, whatever fits. The API has no opinion and offers no help.

ESC