A fresh advisory names nanoid below 3.3.17: a custom generator can loop
forever when asked for a zero-size id. It reaches the tree only through
next and postcss, but the audit gate rightly blocks on a known HIGH with
a fix available. Bounded override in the established pattern; the audit
drops from one high to none.
pdfjs-dist 6.1.200 and a transitive 5.6.205 both sit in the affected range
of a flaw that lets a crafted PDF run JavaScript inside the renderer. The
document vault feeds it every uploaded PDF, so pin the direct dependency to
6.2.108 and add a bounded override so the transitive copy lands there too.
The override stops short of pdf-parse's 5.4.296, which predates the flaw and
drives the local text-extraction path.
The runtime image ships every pdfjs-dist copy in the store, not just the one
that gets loaded, so an unused transitive copy still counted.
Hoisting in the image picked whichever copy the filesystem listed first, and
more than one copy reaches it, so the version named in package.json was not
reliably the one the renderer resolved. Carry the resolved version forward
from the build stage, select it by name, and fail the build when it is
missing rather than leaving another copy in its place.
Address4 decoded a leading-zero octet as decimal, so `010.0.0.1` parsed as
10.0.0.1 instead of being rejected. That is precisely the alternate-notation
class `isPublicUrl` was written to catch, and this copy arrives transitively
under the MCP SDK's rate limiter, where nothing of ours screens the value
first. The production tree resolved 10.2.0 while the dev tree already had a
fixed one, so the override names the floor rather than trusting resolution.
`undici` moves to 8.10.0 and the `fast-uri` override rises to 3.1.5. Both were
published as high after the last run on the trunk, so this is new information
rather than neglect, and both matter here: undici is what backs `safeFetch`,
which is the one documented egress boundary and the place the connect-time
DNS-rebinding pin lives.
The undici advisory is a cross-user information disclosure and a parse-time
crash through degenerate private cache directives. `fast-uri` arrives four ways
through transitive dependencies and the existing override was pinned one patch
short of the fix.
The safe-fetch dispatcher suite passes against the new client, which is the
part that had to be checked rather than assumed: the pin reaches into undici's
connect hook and a client bump is exactly where that could quietly stop being
enforced.
Bumps pg-boss 12.20 → 12.26 (schema-drift detection, self-healing background
index builds, correctness fixes). Some of those fixes surface errors earlier
versions silently swallowed. Verified safe: the app attaches an error listener
to the boss instance (reminder-worker.ts) and uses none of the direct calls the
change affects (insert/fetch/deleteQueue) — only send/work/start/stop/schedule/
getJobById, and the one getJobById is already wrapped in try/catch. The full
integration suite (101 files, 497 tests) exercising the queue against real
Postgres passes unchanged under 12.26. No config change.
Adds an OpenID Connect relying-party login path (PKCE authorization-code
flow) against a single env-configured identity provider — additive to
password/passkey login by default, with an operator opt-in (OIDC_ONLY,
enforced server-side on every auth route) to disable them entirely.
First-time sign-in auto-provisions or links an account by verified email.
Closes an open redirect in the post-login `next` parameter (both the new
OIDC path and the pre-existing password/passkey flow shared the same
backslash-normalization bypass) by resolving the target through a real
URL parse and comparing origins instead of a prefix check.
Only Anthropic accepts a native PDF block; codex and other image-only-wire
providers dropped it. Add a bounded, best-effort server-side rasterizer that
renders a PDF's pages to JPEG images those providers read as ordinary image
input, so a scanned/image-only PDF is legible on the auto-read path.
- @napi-rs/canvas promoted to a direct dependency; its musl prebuilts + the
pdfjs loader pinned into the standalone trace (next.config.ts)
- rasterize-pdf.ts: lazy pdfjs import (keeps DOMMatrix out of the eager chunk),
first 10 pages, 1600px long edge, JPEG q80, never throws
- prepareVisionInput rasterizes a PDF for a non-Anthropic vision provider and
keeps the native PDF block for Anthropic; the document capability reports
PDFs readable via raster so the UI offers the read
The timeline windows a flat item list (month labels + chunked card rows)
with @tanstack/react-virtual over the shell's scroll container — single
scroll owner, bounded DOM at four-digit corpus sizes. Columns are
measured by a ResizeObserver so the row model and the painted grid never
disagree. Cards are compact-density: kind glyph, foreground title, one
muted meta line, condition pills, a download-only badge for attachment-
class formats, and a hover/focus selection checkbox above the whole-card
click target. In-flight and failed uploads share the same footprint with
a live progress ring and translated failure copy.
Plan-Task: W2-T2, W2-T4 (upload cards)
next 16.2.10, @tanstack/react-query 5.101.2, nodemailer 9.0.3,
react-hook-form 7.81.0, @types/node 26.1.0; docker buildx/metadata/
build-push action pins to their current releases.
The date field opened the browser's native date picker, which rendered by
the browser and kept its intrinsic width on mobile. Replace it with the
shadcn calendar in a popover: a fully app-controlled react-day-picker
calendar that looks the same everywhere and never overflows a phone-width
sheet. The field keeps its preference-formatted, typeable display and its
yyyy-MM-dd value contract, so the date-time field, the time field and
every form that uses them are unchanged. The calendar's month and weekday
labels follow the UI language.
Combine the remote MCP server (stdio + remote /mcp + OAuth 2.1 + read
tools + deep-value reads + a confirmed, audience-bound write surface)
with the v1.23 account-security line (MFA, known/trusted devices,
free-text note encryption).
- schema: keep every v1.23 User relation (MFA, known/trusted devices)
alongside the McpOAuthConnection relation; the MeasurementSource enum
carries the MCP value.
- api-handler: the MCP audience guard (a token scoped to
health:read/health:write is bound to /mcp and refused a REST write)
and the step-up MFA helpers coexist.
- maintenance queues: register the MCP token-cleanup sweep next to the
note-encryption backfill and key-rotation queues; shift it to 04:00
so the two daily discovery ticks do not collide.
- migrations: renumber the MCP migrations to 0206/0207 so they follow
the v1.23 chain (0199-0205).
- MCP mood writes route the note through the v1.23 at-rest cipher.
Add the TOTP enroll/confirm/disable surface and Argon2id-hashed recovery
codes as the v1.23 second factor.
- totp.ts wraps otpauth (zero-dep) pinned to the interop floor (SHA-1, 6
digits, 30 s) with a +/-1 drift window. Replay protection persists the last
accepted time-step per account (User.totpLastStep, migration
0202_v123_totp_laststep) and rejects any step that is not strictly newer,
so a captured code dies the instant it is accepted once.
- recovery-codes.ts issues ten single-use, high-entropy codes, Argon2id-
hashed at rest, shown once; regeneration replaces the entire prior set in a
transaction and verification burns the matched code with a guarded update.
- The TOTP secret is stored AES-256-GCM encrypted (already registered in the
encrypted-columns registry + rotation script); decrypt is fail-closed.
- Routes: setup (pending secret), confirm (activate + issue recovery codes),
disable (step-up + current factor), recovery-codes/regenerate (step-up).
Setup/confirm are cookie-only; disable/regenerate add requireFreshMfa.
- scripts/disable-mfa.ts is the operator escape hatch for a locked-out
self-hoster, audited, run via pnpm dlx tsx.
Introduce a read-only Model Context Protocol surface built on the official
@modelcontextprotocol/sdk so a user can query their own HealthLog data from an
MCP-capable assistant over local stdio with a pasted, scoped Bearer token — no
extra infrastructure.
The tool and resource layer in src/lib/mcp is transport-agnostic: five read
tools — list_metrics, get_metric_series, get_medication_compliance, get_labs,
get_correlations — each a thin wrapper over the existing server-authoritative
retrieval paths, returning structured values, units, reference bands, and
provenance with an explicit { present: false } for absence and no prose
verdicts. Two resources expose the user's profile (data-minimised) and
medication list. A stdio entrypoint (src/cli/mcp-stdio.ts, pnpm mcp:stdio)
resolves the token through the shared Bearer path and binds the session to
<user>:<token>; with no token it refuses to start.
Read-only and admin-unreachable by construction: only read tools are
registered, and requireAdmin stays cookie-only so the Bearer/MCP wire can never
elevate. Covered by unit tests for every tool and resource, the auth/scope
model, and the cookie-only admin boundary. The server ships in the existing
container; docker compose up is unchanged.
Add an opt-in in-browser OCR path so a user whose provider cannot read
images (ChatGPT-OAuth/Codex, a text-only model) can still scan a lab
report. The capability probe gains a `mode` field: `vision` when the
provider reads the image directly, `text` when local OCR is enabled and
any provider is configured, or null. The extract route grows a text
branch that accepts the in-browser-OCR'd text as JSON and structures it
through the existing schema + review/commit flow; the raw image never
reaches the server in text mode. New `labsLocalOcrEnabled` user column
(migration 0184, default off) gates it, with a dedicated PATCH endpoint.
Bump undici 8.4.1 -> 8.5.0 (GHSA-vmh5-mc38-953g, TLS certificate
validation bypass in the SOCKS5 ProxyAgent) and nodemailer 9.0.0 -> 9.0.1
(GHSA-p6gq-j5cr-w38f), both direct dependencies. Clears the production
high-severity dependency audit; no API change.
Pin hono to >=4.12.25 (GHSA-88fw-hqm2-52qc, CORS middleware reflects
any Origin) and picomatch to >=4.0.4 (CVE-2026-33671, ReDoS) through
pnpm overrides scoped to the vulnerable ranges. Both are build- and
dev-tooling transitives — hono via Prisma's dev tooling and the shadcn
CLI, picomatch via the build chain — and neither is on a runtime path:
the app is Next.js and sets no CORS headers. The prod audit drops from
one high to zero.
Alerts and reminders over email — the one channel every self-hoster
already has. The SMTP transport (host/port/auth/from) is operator-supplied
via SMTP_* env and loaded once per process like the APNs key; the only
per-user value is the recipient address, so SMTP credentials never enter a
user's encrypted channel blob. Absent SMTP env disables the channel
cleanly: the dispatcher skips it and the Settings card hides itself.
Bodies are plain text — HTML is stripped and routine reminders run through
the emoji-strip path; no markdown library is added. SMTP 5xx maps to a
hard reject (permanent — auto-disable); connection/4xx errors are
transient and ride the backoff schedule. Adds nodemailer (the standard,
well-audited mail transport) and its types.
The v6 major leaves the createDocument API and the .meta() registry
annotations unchanged for this codebase; the generated
docs/api/openapi.yaml stays byte-identical and openapi:check passes
with no registry adjustment.
Published manifests are now cosign-signed keyless with a documented
digest-pinning path, a lefthook pre-commit runs gitleaks over staged
changes, responses opt into a credentialless embedder policy, the
deploy webhook can require a fresh timestamp, and a monthly drill
decrypts and parses the newest offhost backup so a broken key or format
surfaces before an emergency does.
Users can now tell the assistant about themselves in an encrypted
free-text profile that feeds the coach and briefing prompts as clearly
fenced context, with the coach allowed one targeted follow-up question
when the picture is incomplete. Operators on closed registrations can
mint expiring invite links with a QR code from the admin area; a valid
invite opens registration exactly as many times as it allows and records
who redeemed it. A nightly pass watches three deterministic signals —
low adherence, blood pressure above its target band, a falling recovery
trend — and sends at most one coach nudge per week through the existing
notification channels, gated behind the same opt-outs as every other
assistant surface.