Merge pull request #330 from trails-cool/document-https-dev-special-cases
Document why HTTPS=1 dev exists, and the one case that still needs it
This commit is contained in:
commit
ba3a619160
3 changed files with 111 additions and 2 deletions
34
CLAUDE.md
34
CLAUDE.md
|
|
@ -68,6 +68,40 @@ pnpm db:push # Push Drizzle schema to local PostgreSQL
|
||||||
pnpm db:studio # Open Drizzle Studio (DB browser)
|
pnpm db:studio # Open Drizzle Studio (DB browser)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Local HTTPS dev (rare — most contributors never need this)
|
||||||
|
|
||||||
|
The default dev loop runs the journal on plain HTTP at
|
||||||
|
`http://localhost:3000`. WebAuthn passkeys, magic links, sessions, the
|
||||||
|
Terms gate, SSE — everything works over HTTP because the WebAuthn spec
|
||||||
|
treats `localhost` as a secure context regardless of scheme. CI's e2e
|
||||||
|
suite runs over plain HTTP too.
|
||||||
|
|
||||||
|
There is exactly one feature that requires local HTTPS: **Wahoo OAuth**.
|
||||||
|
Wahoo (and most OAuth providers) reject `http://` redirect URIs, so the
|
||||||
|
`/api/sync/connect/wahoo` callback flow can only complete against an
|
||||||
|
HTTPS dev server. To run that flow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
HTTPS=1 ORIGIN=https://localhost:3000 pnpm --filter @trails-cool/journal dev
|
||||||
|
```
|
||||||
|
|
||||||
|
`HTTPS=1` enables the `@vitejs/plugin-basic-ssl` cert + the ALPN
|
||||||
|
HTTP/1.1 workaround in `apps/journal/vite.config.ts`. `ORIGIN` makes the
|
||||||
|
WebAuthn server expect the HTTPS origin (set this together with HTTPS=1
|
||||||
|
or you'll get origin-mismatch errors). Use `pnpm --filter` (not
|
||||||
|
`pnpm dev`) because turbo doesn't pass `HTTPS` through unless added to
|
||||||
|
its `globalPassThroughEnv` — `pnpm --filter` bypasses turbo entirely.
|
||||||
|
|
||||||
|
**Don't set `ORIGIN=https://localhost:3000` in your `apps/journal/.env`
|
||||||
|
unless you intend to always run with `HTTPS=1`.** Mismatched values
|
||||||
|
break the e2e suite and generic dev. See
|
||||||
|
`apps/journal/.env.example` for what each var means.
|
||||||
|
|
||||||
|
If you find yourself wanting `HTTPS=1` for any reason other than Wahoo
|
||||||
|
testing, write it down here so the assumption stays auditable —
|
||||||
|
"everything but Wahoo works over HTTP locally" is what keeps CI and
|
||||||
|
local config symmetric.
|
||||||
|
|
||||||
## Testing Strategy
|
## Testing Strategy
|
||||||
|
|
||||||
- **Unit tests** (Vitest + jsdom): For packages, components, utilities, and app logic.
|
- **Unit tests** (Vitest + jsdom): For packages, components, utilities, and app logic.
|
||||||
|
|
|
||||||
55
apps/journal/.env.example
Normal file
55
apps/journal/.env.example
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Journal local-dev environment.
|
||||||
|
# Copy to `.env` (gitignored) and edit. Most contributors don't need
|
||||||
|
# anything in this file — the journal app boots on plain HTTP at
|
||||||
|
# http://localhost:3000 with sensible defaults.
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────────────
|
||||||
|
# DO NOT SET unless you know you need it
|
||||||
|
# ────────────────────────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# `ORIGIN` is what the WebAuthn ceremony uses as `expectedOrigin`.
|
||||||
|
# When unset it defaults to `http://localhost:3000`, which is what
|
||||||
|
# Playwright sends and what the browser sees in plain-HTTP dev.
|
||||||
|
#
|
||||||
|
# The ONLY reason to set this is if you're running the dev server
|
||||||
|
# over HTTPS (see HTTPS=1 below) and want passkey registration to
|
||||||
|
# succeed against the HTTPS origin. If you set it to `https://...`
|
||||||
|
# without also running the server over HTTPS, you'll get
|
||||||
|
# "Unexpected registration response origin" errors in dev and the
|
||||||
|
# local e2e suite (which always hits HTTP) will fail registration.
|
||||||
|
#
|
||||||
|
# ORIGIN=https://localhost:3000
|
||||||
|
|
||||||
|
# Flagship marker. Renders the project marketing block on the
|
||||||
|
# anonymous home and gates a few "this is the canonical instance"
|
||||||
|
# behaviors. Self-hosted instances leave this unset; flagship CI
|
||||||
|
# sets it to `true`. Either is fine for local dev — set it if you
|
||||||
|
# want to see the full marketing layout, leave it unset if you
|
||||||
|
# want the self-host home view.
|
||||||
|
# IS_FLAGSHIP=true
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────────────
|
||||||
|
# Wahoo OAuth (only needed when actively testing the Wahoo import)
|
||||||
|
# ────────────────────────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# Wahoo's OAuth flow is the one feature that genuinely needs HTTPS
|
||||||
|
# locally: the provider rejects `http://` redirect URIs. To exercise
|
||||||
|
# the connect/disconnect/import flow end-to-end, you need:
|
||||||
|
#
|
||||||
|
# 1. Local HTTPS dev server: run `HTTPS=1 pnpm --filter
|
||||||
|
# @trails-cool/journal dev` (the basic-ssl plugin in
|
||||||
|
# vite.config.ts wires up the cert; see the comment there for
|
||||||
|
# the ALPN workaround).
|
||||||
|
# 2. ORIGIN=https://localhost:3000 (uncomment above).
|
||||||
|
# 3. Wahoo client credentials below, and a redirect URI of
|
||||||
|
# `https://localhost:3000/api/sync/callback/wahoo` registered
|
||||||
|
# in your Wahoo developer dashboard.
|
||||||
|
#
|
||||||
|
# WAHOO_CLIENT_ID=
|
||||||
|
# WAHOO_CLIENT_SECRET=
|
||||||
|
# WAHOO_WEBHOOK_TOKEN=
|
||||||
|
|
||||||
|
# Integration test secret (only needed if running the integration
|
||||||
|
# test suite that drives the API directly). Generate with
|
||||||
|
# `openssl rand -hex 32`.
|
||||||
|
# INTEGRATION_SECRET=
|
||||||
|
|
@ -11,6 +11,26 @@ export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
reactRouter(),
|
reactRouter(),
|
||||||
|
// Local HTTPS dev is OPT-IN via `HTTPS=1`. Plain HTTP is the default
|
||||||
|
// and the right choice for ~everything: WebAuthn (passkeys) treats
|
||||||
|
// `localhost` as a secure context regardless of scheme, magic links
|
||||||
|
// work over HTTP, sessions / Terms gate / SSE all work over HTTP.
|
||||||
|
// CI doesn't set HTTPS=1 either — the e2e suite runs against the
|
||||||
|
// production `react-router-serve` build over plain HTTP and passes
|
||||||
|
// cleanly. So most contributors should never touch this flag.
|
||||||
|
//
|
||||||
|
// The one case where you DO need HTTPS=1 locally:
|
||||||
|
// - Wahoo OAuth callback testing — Wahoo (and most OAuth
|
||||||
|
// providers) reject `http://` redirect URIs, so the
|
||||||
|
// /api/sync/connect/wahoo flow can only complete against an
|
||||||
|
// HTTPS dev server. See docs/tooling.md for the full
|
||||||
|
// command (`HTTPS=1 ORIGIN=https://localhost:3000 pnpm
|
||||||
|
// --filter @trails-cool/journal dev`) and gotchas.
|
||||||
|
//
|
||||||
|
// If you find yourself wanting HTTPS=1 for any other reason, write
|
||||||
|
// it down in docs/tooling.md — the assumption that "everything but
|
||||||
|
// Wahoo works over HTTP locally" is what keeps the CI/local
|
||||||
|
// configurations symmetric.
|
||||||
...(process.env.HTTPS === "1" ? [import("@vitejs/plugin-basic-ssl").then((m) => m.default())] : []),
|
...(process.env.HTTPS === "1" ? [import("@vitejs/plugin-basic-ssl").then((m) => m.default())] : []),
|
||||||
sentryVitePlugin({
|
sentryVitePlugin({
|
||||||
org: "trails-qq",
|
org: "trails-qq",
|
||||||
|
|
@ -36,8 +56,8 @@ export default defineConfig({
|
||||||
// `singleFetchAction` compares `Origin` against the `Host` header,
|
// `singleFetchAction` compares `Origin` against the `Host` header,
|
||||||
// which HTTP/2 replaces with `:authority` and Node doesn't
|
// which HTTP/2 replaces with `:authority` and Node doesn't
|
||||||
// synthesize back. Restricting ALPN to `http/1.1` keeps the Host
|
// synthesize back. Restricting ALPN to `http/1.1` keeps the Host
|
||||||
// header intact and lets fetcher form submissions through.
|
// header intact and lets fetcher form submissions through. Only
|
||||||
// Plain HTTP dev (the default) is unaffected.
|
// matters when `HTTPS=1` is set — see the plugins comment above.
|
||||||
https: process.env.HTTPS === "1" ? { ALPNProtocols: ["http/1.1"] } : undefined,
|
https: process.env.HTTPS === "1" ? { ALPNProtocols: ["http/1.1"] } : undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue