From f685cbe4605afea673c43f1b031eee46a72faf4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 4 Oct 2026 01:26:37 +0200 Subject: [PATCH 1/6] =?UTF-8?q?chore(release):=20v1.40.0=20=E2=80=94=20bac?= =?UTF-8?q?k=20up=20the=20encryption=20key,=20medication=20courses=20and?= =?UTF-8?q?=20categories,=20your=20own=20symptoms,=20document=20reads=20in?= =?UTF-8?q?=20the=20background?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 54 +++++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++-- docs/api/openapi.yaml | 2 +- package.json | 2 +- public/sw.js | 2 +- 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53cb335cb..a98d936ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,59 @@ # Changelog +## [1.40.0] — 2026-10-04 + +Back up the encryption key and refuse a key that cannot open the data; +several courses and custom categories for medications; custom symptoms; +document reads in the background; sick notes; lab unit refusal and charts +that follow the source priority. + +### Added + +- **Encryption key backup step (migration 0365).** Admin step with key id, + fingerprint and per-platform guidance (`HEALTHLOG_PLATFORM`), an optional + "check my copy" that compares and discards the pasted key, a reminder that + returns when the key changes. +- **Boot key check.** A canary per key id, probed oldest values first across + string and Bytes columns; a proven mismatch answers 503 + `encryption.key_mismatch` everywhere except `/api/health` and + `/api/version`, MCP included, and starts no worker; an unproven result + serves, warns and re-checks. `ENCRYPTION_KEY_CHECK=warn` as a last resort. +- **Medication courses and custom categories (#1024, #1041, migration + 0368).** Several courses per medication; compliance, streak, cadence and + dose history count every course and never the gaps; existing dated + medications become one course each. Own categories as `custom:` with + `categoryLabel`; the runtime category table is now in the schema. +- **Custom symptoms (#981, migration 0369).** Up to eight definitions, events + with a 0 to 10 intensity, a capture kind, one correlation channel per + symptom, a Coach block; gated on the Illness module. +- **Document AI as background runs (#1090, migrations 0366 and 0370).** + Index, summary, suggestions and extraction accept `Prefer: respond-async`; + the lab scan always runs in the background; `GET /api/ai-runs/{id}`. +- **`SICK_NOTE` document kind.** +- **Natural durations (#1101)** in the sleep field. + +### Fixed + +- **Lab unit mismatch (#1094).** A reading in another unit than its marker is + refused (`labs.unit.mismatch`) on every write path; a marker's unit is + locked while it has readings (`biomarkers.unit.locked`). +- **Source priority in series (#1108).** Pulse, HRV and SpO2 series read the + ladder's source per day; unranked types keep every source. +- **Sign-in over plain http (#1097).** Refused with + `auth.session.insecure_transport` before a password, code or challenge is + used; the sign-in page says so first. +- **Chart window (#1102).** Ends at the end of today in the profile time zone. +- **Workouts.** The insight cap survives a delete (migration 0367); a changed + `manual:` resubmit updates the workout and its paragraph. +- **Display.** Clinician decimals in the owner's locale, stated dates east of + UTC+12, archive month grouping, lab list columns without a range, icon + picker touch targets and labels, symptom entry load errors. + +### Changed + +- Quick-entry forms load on demand; the four eager route baselines are + lowered to the new measurements. + ## [1.39.9] — 2026-10-03 Workouts can be logged by hand, the body fat tile opens its own page, and diff --git a/README.md b/README.md index 806f6649d..f5a20e17e 100644 --- a/README.md +++ b/README.md @@ -129,9 +129,9 @@ Actively developed — new releases roughly weekly, issue reports and PRs welcom HealthLog started as something I built for myself. That it has turned into a project other people use, care about and help shape still surprises me, and I am grateful for every part of it. -Thank you to everyone who sent code that shipped: [@TimonBed](https://github.com/TimonBed), [@Antiheld86](https://github.com/Antiheld86), [@BernhardBuckel](https://github.com/BernhardBuckel), [@balajiv113](https://github.com/balajiv113), [@aucun6352](https://github.com/aucun6352), [@mathewcsims](https://github.com/mathewcsims) and [@muhdusama](https://github.com/muhdusama). A special thanks to [@BernhardBuckel](https://github.com/BernhardBuckel), who has been working through the lab screens one careful pull request at a time, with tests and a plan before every change. +Thank you to everyone who sent code that shipped: [@TimonBed](https://github.com/TimonBed), [@Antiheld86](https://github.com/Antiheld86), [@BernhardBuckel](https://github.com/BernhardBuckel), [@balajiv113](https://github.com/balajiv113), [@aucun6352](https://github.com/aucun6352), [@mathewcsims](https://github.com/mathewcsims) and [@muhdusama](https://github.com/muhdusama). A special thanks to [@BernhardBuckel](https://github.com/BernhardBuckel), who has gone far beyond one fix: careful reports with the code paths already traced, pull requests with tests and a plan before every change, and fixes that everyone using HealthLog benefits from, from the lab screens to how charts pick a source and average a day. -Thank you to everyone who opened an issue, asked a question in the discussions or wrote me an email. Most of what got better in the last months started with someone describing what went wrong for them, often with a log, a screenshot and a second try when the first fix was not enough. +Thank you to the people whose reports shaped HealthLog, again and again: [@lutzkind](https://github.com/lutzkind), [@HercoGC](https://github.com/HercoGC), [@mills1975](https://github.com/mills1975), [@sreeramachandramurthy](https://github.com/sreeramachandramurthy), [@CritLoren](https://github.com/CritLoren), [@mbreitkreuz](https://github.com/mbreitkreuz), [@k-nacion](https://github.com/k-nacion), [@NebuPookins](https://github.com/NebuPookins), [@Nazza01](https://github.com/Nazza01), [@Cnote43](https://github.com/Cnote43) and [@juliaanvaneijndt](https://github.com/juliaanvaneijndt). And to everyone else who opened an issue, asked a question in the discussions or wrote me an email. Most of what got better started with someone describing what went wrong for them, often with a log, a screenshot and a second try when the first fix was not enough. And thank you to everyone who supported the project with a coffee or by buying the iPhone app. I don't name anyone here, but I read every message that comes with it. It pays for the servers and the Apple developer account, and it tells me that HealthLog is worth the evenings. diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 25431a0bd..8b11dbff7 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: HealthLog API - version: 1.39.9 + version: 1.40.0 description: >- Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest. diff --git a/package.json b/package.json index 8f2a1b5f0..74f0243db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "healthlog", - "version": "1.39.9", + "version": "1.40.0", "description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.", "license": "PolyForm-Noncommercial-1.0.0", "homepage": "https://healthlog.dev", diff --git a/public/sw.js b/public/sw.js index 12e07572b..32351a83e 100644 --- a/public/sw.js +++ b/public/sw.js @@ -36,7 +36,7 @@ try { // v1.4.38.4 → v1.4.42. Do not hand-edit; bump `package.json` and rebuild. const CACHE_VERSION = (typeof self !== "undefined" && self.__APP_VERSION__) || - /* @sw-version-fallback */ "v1.39.9"; + /* @sw-version-fallback */ "v1.40.0"; const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`; const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`; // v1.18.6 — read-only data cache for a curated allowlist of safe GET `/api/*` From b3e18739fb902e1624954245c5ebdb0ccd2d0ca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 4 Oct 2026 08:24:07 +0200 Subject: [PATCH 2/6] docs(readme): name the people whose reports shaped HealthLog --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f5a20e17e..54917888f 100644 --- a/README.md +++ b/README.md @@ -129,9 +129,9 @@ Actively developed — new releases roughly weekly, issue reports and PRs welcom HealthLog started as something I built for myself. That it has turned into a project other people use, care about and help shape still surprises me, and I am grateful for every part of it. -Thank you to everyone who sent code that shipped: [@TimonBed](https://github.com/TimonBed), [@Antiheld86](https://github.com/Antiheld86), [@BernhardBuckel](https://github.com/BernhardBuckel), [@balajiv113](https://github.com/balajiv113), [@aucun6352](https://github.com/aucun6352), [@mathewcsims](https://github.com/mathewcsims) and [@muhdusama](https://github.com/muhdusama). A special thanks to [@BernhardBuckel](https://github.com/BernhardBuckel), who has gone far beyond one fix: careful reports with the code paths already traced, pull requests with tests and a plan before every change, and fixes that everyone using HealthLog benefits from, from the lab screens to how charts pick a source and average a day. +Thank you to everyone who sent code that shipped: [@TimonBed](https://github.com/TimonBed), [@Antiheld86](https://github.com/Antiheld86), [@BernhardBuckel](https://github.com/BernhardBuckel), [@balajiv113](https://github.com/balajiv113), [@aucun6352](https://github.com/aucun6352), [@mathewcsims](https://github.com/mathewcsims) and [@muhdusama](https://github.com/muhdusama). A special thanks to [@BernhardBuckel](https://github.com/BernhardBuckel), who has been working through the lab screens one careful pull request at a time, with tests and a plan before every change. -Thank you to the people whose reports shaped HealthLog, again and again: [@lutzkind](https://github.com/lutzkind), [@HercoGC](https://github.com/HercoGC), [@mills1975](https://github.com/mills1975), [@sreeramachandramurthy](https://github.com/sreeramachandramurthy), [@CritLoren](https://github.com/CritLoren), [@mbreitkreuz](https://github.com/mbreitkreuz), [@k-nacion](https://github.com/k-nacion), [@NebuPookins](https://github.com/NebuPookins), [@Nazza01](https://github.com/Nazza01), [@Cnote43](https://github.com/Cnote43) and [@juliaanvaneijndt](https://github.com/juliaanvaneijndt). And to everyone else who opened an issue, asked a question in the discussions or wrote me an email. Most of what got better started with someone describing what went wrong for them, often with a log, a screenshot and a second try when the first fix was not enough. +Thank you to everyone who opened an issue, asked a question in the discussions or wrote me an email, and especially to [@lutzkind](https://github.com/lutzkind), [@HercoGC](https://github.com/HercoGC), [@mills1975](https://github.com/mills1975), [@sreeramachandramurthy](https://github.com/sreeramachandramurthy), [@CritLoren](https://github.com/CritLoren), [@mbreitkreuz](https://github.com/mbreitkreuz), [@k-nacion](https://github.com/k-nacion), [@NebuPookins](https://github.com/NebuPookins), [@Nazza01](https://github.com/Nazza01), [@Cnote43](https://github.com/Cnote43) and [@juliaanvaneijndt](https://github.com/juliaanvaneijndt), whose reports shaped HealthLog again and again. Most of what got better in the last months started with someone describing what went wrong for them, often with a log, a screenshot and a second try when the first fix was not enough. And thank you to everyone who supported the project with a coffee or by buying the iPhone app. I don't name anyone here, but I read every message that comes with it. It pays for the servers and the Apple developer account, and it tells me that HealthLog is worth the evenings. From 715df7d5d33cbc9281c385b1a3269866e8d6761f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 4 Oct 2026 08:29:43 +0200 Subject: [PATCH 3/6] docs(readme): keep the contributor list without a separate line for one person --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54917888f..a757a5c08 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Actively developed — new releases roughly weekly, issue reports and PRs welcom HealthLog started as something I built for myself. That it has turned into a project other people use, care about and help shape still surprises me, and I am grateful for every part of it. -Thank you to everyone who sent code that shipped: [@TimonBed](https://github.com/TimonBed), [@Antiheld86](https://github.com/Antiheld86), [@BernhardBuckel](https://github.com/BernhardBuckel), [@balajiv113](https://github.com/balajiv113), [@aucun6352](https://github.com/aucun6352), [@mathewcsims](https://github.com/mathewcsims) and [@muhdusama](https://github.com/muhdusama). A special thanks to [@BernhardBuckel](https://github.com/BernhardBuckel), who has been working through the lab screens one careful pull request at a time, with tests and a plan before every change. +Thank you to everyone who sent code that shipped: [@TimonBed](https://github.com/TimonBed), [@Antiheld86](https://github.com/Antiheld86), [@BernhardBuckel](https://github.com/BernhardBuckel), [@balajiv113](https://github.com/balajiv113), [@aucun6352](https://github.com/aucun6352), [@mathewcsims](https://github.com/mathewcsims) and [@muhdusama](https://github.com/muhdusama). Thank you to everyone who opened an issue, asked a question in the discussions or wrote me an email, and especially to [@lutzkind](https://github.com/lutzkind), [@HercoGC](https://github.com/HercoGC), [@mills1975](https://github.com/mills1975), [@sreeramachandramurthy](https://github.com/sreeramachandramurthy), [@CritLoren](https://github.com/CritLoren), [@mbreitkreuz](https://github.com/mbreitkreuz), [@k-nacion](https://github.com/k-nacion), [@NebuPookins](https://github.com/NebuPookins), [@Nazza01](https://github.com/Nazza01), [@Cnote43](https://github.com/Cnote43) and [@juliaanvaneijndt](https://github.com/juliaanvaneijndt), whose reports shaped HealthLog again and again. Most of what got better in the last months started with someone describing what went wrong for them, often with a log, a screenshot and a second try when the first fix was not enough. From 2798cc953fc5d32497ad178c7cb4a1e19e6fd878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 4 Oct 2026 10:18:16 +0200 Subject: [PATCH 4/6] refactor(workouts): drop the unused clock arguments of the blank draft --- src/components/workouts/manual-workout-form.tsx | 2 +- src/lib/workouts/__tests__/manual-entry.test.ts | 2 +- src/lib/workouts/manual-entry.ts | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/workouts/manual-workout-form.tsx b/src/components/workouts/manual-workout-form.tsx index c849056e4..4455c83ae 100644 --- a/src/components/workouts/manual-workout-form.tsx +++ b/src/components/workouts/manual-workout-form.tsx @@ -116,7 +116,7 @@ export function ManualWorkoutForm({ // a submit after an edit updates the row the first submit stored. const [externalId] = useState(newManualWorkoutExternalId); const [draft, setDraft] = useState(() => - emptyManualWorkoutDraft(new Date(), timezone), + emptyManualWorkoutDraft(), ); const [errors, setErrors] = useState< Partial> diff --git a/src/lib/workouts/__tests__/manual-entry.test.ts b/src/lib/workouts/__tests__/manual-entry.test.ts index 0f728fb10..001142455 100644 --- a/src/lib/workouts/__tests__/manual-entry.test.ts +++ b/src/lib/workouts/__tests__/manual-entry.test.ts @@ -242,7 +242,7 @@ describe("buildManualWorkoutEntry — validation", () => { describe("the form's defaults", () => { it("opens with a blank start; the field caps at the current minute", () => { expect(wallClockNow(NOW, "Europe/Berlin")).toBe("2026-09-15T14:00"); - expect(emptyManualWorkoutDraft(NOW, "Asia/Seoul")).toMatchObject({ + expect(emptyManualWorkoutDraft()).toMatchObject({ sportType: "", start: "", hours: "", diff --git a/src/lib/workouts/manual-entry.ts b/src/lib/workouts/manual-entry.ts index 6be35a294..d672ae8ea 100644 --- a/src/lib/workouts/manual-entry.ts +++ b/src/lib/workouts/manual-entry.ts @@ -118,10 +118,7 @@ export function wallClockNow(now: Date, timezone: string): string { return `${p.year}-${pad(p.month)}-${pad(p.day)}T${pad(p.hour)}:${pad(p.minute)}`; } -export function emptyManualWorkoutDraft( - now: Date, - timezone: string, -): ManualWorkoutDraft { +export function emptyManualWorkoutDraft(): ManualWorkoutDraft { return { sportType: "", // Blank on purpose (#1085): a blank start means "it just ended", so the From 5d28d0500406982503e81820e8ba8d5d9e18703e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 4 Oct 2026 11:30:20 +0200 Subject: [PATCH 5/6] docs(changelog): add reasoning effort, blood pressure and previews to 1.40.0 --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2ae8aadc..ee6a1e975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ Back up the encryption key and refuse a key that cannot open the data; several courses and custom categories for medications; custom symptoms; document reads in the background; sick notes; lab unit refusal and charts -that follow the source priority. +that follow the source priority; blood pressure no longer loses its +diastolic value; document previews work again. ### Added @@ -31,6 +32,11 @@ that follow the source priority. the lab scan always runs in the background; `GET /api/ai-runs/{id}`. - **`SICK_NOTE` document kind.** - **Natural durations (#1101)** in the sleep field. +- **Reasoning effort per provider (#1126).** The Local and OpenAI-compatible + forms gain a Reasoning select (Default, Off, Low, Medium, High), stored on + the provider's chain entry; Default sends nothing, Off sends + `reasoning_effort: "none"`. A rejection of that field no longer switches + the endpoint's JSON mode off. ### Fixed From 257d3ef6a578ebdd4aadd520bad81367aadb087b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 4 Oct 2026 11:32:04 +0200 Subject: [PATCH 6/6] docs(changelog): add the portable backup fixes to 1.40.0 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee6a1e975..9048aa07d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,11 @@ diastolic value; document previews work again. PDFs render with pdfjs font, cmap and wasm data and a registered fallback font instead of absent system fonts, and an all-blank render reports `raster-failed`. The thumbnail job logs why a preview is missing. +- **Portable backups.** Cycle free text, custom mood labels, visit notes + and vaccination notes are carried readable and re-encrypted on restore + instead of as the source server's ciphertext; custom mood tags restore + without mood entries; a round-trip guard fails on any ciphertext in a + portable file. A medication update and its course share one transaction. - **Lab unit mismatch (#1094).** A reading in another unit than its marker is refused (`labs.unit.mismatch`) on every write path; a marker's unit is locked while it has readings (`biomarkers.unit.locked`).