Pair code:
- POST /api/auth/owner/pair-code (owner-gated) mints a single-use,
5-minute, 6-digit code held in memory.
- POST /api/auth/owner/pair-redeem swaps the code for an owner cookie,
with basic IP-based rate limiting (max 8 attempts/min).
- OwnerLoginModal gains a tab: "Use 6-digit code" (default) vs the
long secret. The PIN input is big, monospaced, numeric-only.
- Small smartphone icon next to the connected Tesla chip opens
OwnerPairCodeDisplay, which shows the code in 56px type with a
live countdown so it's tappable on the touchscreen of the other
device while you stare at it.
Responsive layout:
- TopBar wraps when narrower than the chip row. minWidth on OD strip
drops 380→280 and chat composer drops 360→240 so they stack cleanly.
- Main body switches to flex-col below the lg breakpoint (Tailwind
1024px), so the map sits on top of the stops rail on Tesla 3/Y
portrait and any window not maximised. Map gets 40vh min-height
so it never disappears.
- Add OWNER_SECRET-based session: signed HMAC cookie, /api/auth/owner login,
requireOwner middleware. All Tesla routes refuse 401 without it.
- Bind OAuth state to a SameSite=Lax httpOnly cookie at /start, validate
match in /callback with constant-time compare. Refuses unmatched callbacks.
- Token store now mkdir 0700, writeFile + rename atomic, mode 0600 with
defensive chmod. Owner-only on disk.
- VIN masked to last 4 in responses; partner-register no longer echoes raw
Tesla body to clients; coord bounds checked on send-to-nav.
- Client: useTesla also tracks owner status; Connect Tesla button opens an
OwnerLoginModal when not authenticated, then continues to Tesla OAuth.
Conscious deferrals:
- Explicit CSRF tokens on POST routes: mitigated by SameSite=Lax cookies
+ same-origin CORS. Will revisit if cross-origin clients land.
- At-rest token encryption: deferred for single-user app; tokens are on a
0700 Dokku volume readable only by the app uid. Will add AES-GCM if we
multi-tenant.