diff --git a/CHANGELOG.md b/CHANGELOG.md index f79137c..d7a2b60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,20 @@ for how releases are cut and published. ## [Unreleased] -## [0.8.1] — 2026-07-05 +## [0.8.2] — 2026-07-05 + +### Fixed +- **`RELAY_URL` now defaults to the hosted relay** (`https://network.temetro.com`) instead of + `http://localhost:8080`. The old default silently failed for anyone who joined the network without + explicitly setting `RELAY_URL` — the backend's hub connection could never reach the relay (inside + Docker `localhost` is the container itself), so it never authenticated and QR pairing generated a + QR pointing at an unreachable `localhost`. Self-hosters running their own relay still override + `RELAY_URL`. Updated `.env.example` accordingly. + +### Changed +- Generating a pairing QR (`POST /api/patients/wallet/pair`) now ensures the clinic's relay hub is + connected before pre-registering the request, so the routing is set up even if the connection was + opened lazily. ### Fixed - **QR "scan to connect" pairing** was broken by the multi-clinic relay routing (v0.8.0): pairing diff --git a/backend/.env.example b/backend/.env.example index 71e9634..e8fcd2e 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -40,7 +40,11 @@ FRONTEND_PORT=3000 # /hub with its own Ed25519 signing key, so no shared secret is needed. # RELAY_TOKEN is OPTIONAL/LEGACY — set it only for a private relay that also # gates on a shared token (then use the SAME value here and on the relay). -RELAY_URL=http://localhost:8080 +# Defaults to the hosted relay (https://network.temetro.com) when unset, so +# "Join Temetro Network" works out of the box; set RELAY_URL only to point at +# your own relay. Do NOT use http://localhost — inside Docker that's the +# container itself and the relay connection will silently fail. +RELAY_URL=https://network.temetro.com RELAY_TOKEN= # (Legacy, pre-relay self-hosting.) A phone-reachable URL for the QR when NOT diff --git a/backend/package.json b/backend/package.json index d763eaf..1a7f98a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "temetro-backend", - "version": "0.8.1", + "version": "0.8.2", "private": true, "type": "module", "description": "temetro backend — Express + Postgres API with Better Auth (email/password, organizations) and org-scoped patient records.", diff --git a/backend/src/env.ts b/backend/src/env.ts index 30ef97e..fb63cb2 100644 --- a/backend/src/env.ts +++ b/backend/src/env.ts @@ -35,7 +35,11 @@ const schema = z.object({ // its own Ed25519 signing key, so no shared secret is needed. RELAY_TOKEN is // now *optional/legacy* — set it only for a private relay that also gates on a // shared token (must then match the relay's RELAY_TOKEN). - RELAY_URL: z.string().min(1).default("http://localhost:8080"), + // + // Defaults to the hosted relay so "Join Temetro Network" works out of the box; + // override only when running your own relay. (A `localhost` default silently + // fails inside Docker, where localhost is the container itself.) + RELAY_URL: z.string().min(1).default("https://network.temetro.com"), RELAY_TOKEN: z.string().default(""), // Public, device-reachable URL of this backend's wallet relay, baked into the // QR a patient scans. Optional — when unset we derive it from the request host diff --git a/backend/src/routes/patients-wallet.ts b/backend/src/routes/patients-wallet.ts index 1b9e536..89dbbc3 100644 --- a/backend/src/routes/patients-wallet.ts +++ b/backend/src/routes/patients-wallet.ts @@ -17,7 +17,7 @@ import { } from "../middleware/auth.js"; import { emitToWallet } from "../realtime.js"; import { recordActivity } from "../services/activity.js"; -import { expectResponse } from "../services/relay-client.js"; +import { connectOrg, expectResponse } from "../services/relay-client.js"; import * as patientService from "../services/patients.js"; import { awaitQuickTunnelUrl } from "../services/relay-url.js"; import { getNetworkEnabled } from "../services/signing.js"; @@ -95,6 +95,9 @@ patientsWalletRouter.post( ); // No wallet number to `wallet:send` to yet, so pre-register the request id // with the relay so the scanning device's response routes back to us. + // Ensure the hub is (re)connected first; if it's still mid-handshake the + // on-auth re-registration of pending requests will catch this one. + await connectOrg(req.organizationId!); expectResponse(req.organizationId!, view.id); res.status(201).json({ ...view, diff --git a/backend/src/services/relay-client.ts b/backend/src/services/relay-client.ts index 726a1dd..7bdf3dd 100644 --- a/backend/src/services/relay-client.ts +++ b/backend/src/services/relay-client.ts @@ -49,7 +49,9 @@ export function expectResponse(orgId: string, requestId: string): void { } // Open (and authenticate) a hub connection for a clinic, if not already open. -// Idempotent — safe to call on startup and again when an org joins the network. +// Idempotent — safe to call on startup, when an org joins the network, and +// before generating a pairing QR. The socket auto-reconnects on its own, so an +// existing entry is left as-is. export async function connectOrg(orgId: string): Promise { if (hubs.has(orgId)) return; diff --git a/frontend/package.json b/frontend/package.json index 56c19a6..3a2805b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "0.8.1", + "version": "0.8.2", "private": true, "scripts": { "dev": "next dev", diff --git a/package.json b/package.json index 69a1474..37c173a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "temetro", - "version": "0.8.1", + "version": "0.8.2", "private": true, "devDependencies": { "shadcn": "^4.11.0"