# S012 — login UX + deploy shape Design pivot (mvp.dj decision 8): email/SMS sign-up splits into a **separate app**; linji.at hosts on Fly.io and accepts BYO atproto handles from any PDS. ## What landed - **BYO-handle login**: `beginLogin` resolves an external handle the standard way — `.well-known/atproto-did` → DID document → `#atproto_pds` service — and runs OAuth against that server. Falls back to the configured PDS for handles that don't resolve (the dev rig's `.test` accounts). - `linji serve` flags: `--handle-domain` (bare login names suffix it; default `linji.at`), `--invite-code` / `LINJI_INVITE` (403 at `/api/login` without it), `/api/config` (domain + inviteRequired). - **PWA**: full-handle login with the community suffix as a hint, invite field shown only when required (remembered in localStorage), spinners on composer + new-thread, mobile layout (16px inputs against iOS zoom, horizontal space chips, sticky composer, safe-area padding). - **Service worker rewritten** after a real stale-shell incident (cache-first shell stranded the tab on dead asset hashes): now network-first for navigations, cache-first only for hashed `/assets`, `/api` always network, `skipWaiting` + `clients.claim`. - **Deploy**: `app0/fly.toml` + multi-stage Dockerfile (zig → bun → slim runtime; zds is a separate deploy via `LINJI_PDS` secret). `app0/dinit/` dev services with relative paths: `dinit -d app0/dinit dev` boots zds + seed + serve + vite. ## dinit facts (learned via dinit-check + live boot) - `env-file` paths resolve relative to the **service directory**. - Relative command paths work; `dinit-check` warns but they run. - Logfile directories must exist before the service starts — use flat `/tmp` filenames when a seed script creates the state dir later. ## Verification scenario-serve extended (`/api/config`, invite denials, bare-name login) — 32 checkpoints, PASS; unit + scenario-space green. dinit dev stack booted live: fresh `/tmp/linji-dev` seeded, bare-name `alice` login worked, vite proxy 200, clean shutdown. Spinner confirmed under throttled network; mobile layout screenshot-checked at 390×844.