Releases: NovaLux12/carelink-bridge
Release list
carelink-bridge v0.2.0
Highlights
This is the first minor version bump since v0.1.0. Per the project's convention
(0.x: minor = phase, patch = fixes within a phase), v0.2.0 reflects a new
phase — security and reliability hardening — rather than a fix-level patch.
80 new tests added, all green on Linux (135 passed (15 files)). No
behavioural change is expected on a working account; the changes tighten
the existing 401-refresh, retry-loop, and token-write paths and surface
better signals on failure.
Test progression (v0.1.6 → v0.2.0)
55 v0.1.6 baseline
55 → 61 (+6) Atomic 0600 logindata.json write
61 → 67 (+6) P0.1 mmol/L safety
67 → 79 (+12) P0.2 lastAlarm policy
79 → 80 (+1) forceRefresh successive-401 regression
80 → 81 (+1) P3 trend NONE -> Flat
81 → 92 (+11) NoAuth0SSOConfigurationError + selectAuth0ConfigUrl (6) + Discovery pinning (5)
92 → 92 ( 0) CARELINK_MAX_RETRY_DURATION removal
92 → 110 (+18) Refresh-failure classification
110 → 133 (+23) Status-aware backoff helper (decideRetry unit tests)
133 → 135 (+2) Status-aware backoff integration (429 Retry-After + 404 fail-fast)
135 final
Shipped
Added
- Atomic 0600
logindata.jsonwrite —writeLoginDataAtomicopens the
temp file withO_CREAT|O_EXCL|O_WRONLYandmode(0o600), fsyncs,
then renames atomically. Closes the world-readable window between temp
create and chmod that umask-022 boxes had.tightenLoginDataIfLooseruns
on theloadLoginDataread path so an older bridge with a pre-existing
0644 file is closed without a one-shot migration step. - P0.1 mmol/L safety —
src/transform/index.tsdetects
bgunits/bgUnitsofMMOL_L(with casing fallbacks) and converts
sgto mg/dL viaMath.round(sg * 18.0182). Asserted numerically:
5.5 mmol/L → 99 mg/dL,2.0 → 36,22.2 → 400. - P0.2 lastAlarm policy —
src/last-alarm.tsplus
NightscoutLastAlarmAnnotation. CareLink alarms surface as
devicestatus.last_alarm(code/datetime/text/severity). Priority-1
codes hitconsole.warnalways-on. No alarm relay to Nightscout
/api/v1/treatments.json— verified by an absence-grep test over
src/. NoAuth0SSOConfigurationError+selectAuth0ConfigUrl— named
error is grep-distinguishable in journald. The helper throws it on
missingAuth0SSOConfigurationin the discovery entry, carrying
diagnostic context (region, appVersion) for operators.- Discovery pinning —
DISCOVERY_APP_VERSION = 'android/3.6'and
buildDiscoveryUrl(isUS)extracted intosrc/discovery.ts. Testable
constants; future contributors can't silently regress the bridge to
a no-Auth0 track (3.4 / 4.0). - Refresh-failure classification —
isPermanentRefreshFailure
predicate insrc/refresh-failure.ts. Distinguishes permanent
(HTTP 400 +invalid_grant/invalid_client) from recoverable
(5xx, 429, transport). The catch inauthenticate()is split so
a successful refresh + write failure doesn't nuke the token. - Status-aware capped exponential backoff with jitter, honour
Retry-After—decideRetryinsrc/retry-policy.ts. Permanent
4xx fail fast; 429 honoursRetry-After(numeric or HTTP-date)
capped; 5xx and transport errors retry with full-jitter backoff
(capped). The pre-fix fixed 2s/4s/8s path is replaced.
Removed
CARELINK_MAX_RETRY_DURATIONenv,Config.maxRetryDuration
field,CareLinkClientOptions.maxRetryDurationfield, and
DEFAULT_MAX_RETRY_DURATIONconstant — the option had no defined
unit, the fetch loop never honoured it, and the fix-path is the
status-aware policy above.
Verification
npx tsc --noEmit: PASS
npm test -- --run: 135 passed (15 files), 0 failed, 0 skipped
npm run build: clean emit to dist/ (dist/ is .gitignored)
Risk
- Behavioural changes are bounded to the fetch retry loop and the
refresh-token deletion decision. The two pre-existing 401-refresh
regression tests (should force a token refresh after a 401 and succeed on retry,should refresh again on a 401 immediately after a successful refresh+401) are the canary — both still pass. - The 429 with
Retry-After: 25integration test is the load-bearing
proof that the server-set delay is honoured. - The 404 fail-fast integration test is the load-bearing proof that
the loop doesn't hammer a permanent-failure host. - The named-error class and discovery pinning are observation-only:
no behaviour change for working accounts.
Notes
This is expected to be the last contribution in the current maintenance
window. The 780G-payload-fixture items (markers[] for treatments,
therapyAlgorithmState for auto-mode, limits[] schedule, multi-patient
fan-out, reservoirLevelPercent snap-points, NGP-tier alarm codes) remain
deferred until a real pump arrives (currently expected November 2026)
or another operator contributes sanitised fixtures. Project maintenance
continues passively — issue reports and security advisories are still
monitored.
v0.1.6 — doctor pre-flight check + discovery-version guard
Pre-pump operability, and a compatibility hardening from the public-config research.
Added
npm run doctor(#28) — a pre-flight self-check to run the moment a new pump pairs. Confirms.envis complete, decodes and reports your login token's validity/expiry, and checks that CareLink and Nightscout are both reachable with an acceptedAPI_SECRET— without fetching pump data. One request each to two hosts, safe to run repeatedly; non-zero exit on failure so it can gate a deploy. First item of the v0.2.0 operability set (#8).
Changed
- Discovery app-version pinned as a documented constant (#27). Live probing showed Medtronic's discovery endpoint returns a different config per app-version string — only
android/3.6/3.7carry the Auth0 SSO config this flow needs (3.4 and 4.0 return no-Auth0 tracks) — so bumping it to a "newer" number would silently break login. The failure now names the version string as the likely cause.
55/55 tests on Node 20 + 22. No new dependencies. Still no real-CareLink validation — that waits on the pump (#12).
v0.1.5 — server-reported username for data requests
One fix from round 2 of the auth-flow research (findings), which validated the bridge's login-page scraping against Medtronic's live Auth0 Universal Login and cross-checked data fetching against nightscout-connect.
Fix
- Data POST bodies now use the username CareLink reports from
/users/merather thanCARELINK_USERNAMEverbatim (#25). If you enter your email in.envwhile your CareLink username differs, previous versions sent the wrong identifier on every BLE/carepartner data request. Both reference implementations source the username from the server/token; the bridge now does too, with the configured value as fallback. A verbose-mode log notes when the two differ.
Research conclusions shipped as documentation (no code change)
- Strategy 1 (automated login) assumptions validated against the live Auth0 New Universal Login page: field names, hidden
state,action=defaultsubmit, and the form-action fallback all match. CAPTCHA is present but dormant (no active siteKey); wrong-credential string detection is untestable without a real failed login. - Both CareLink API families are alive; nightscout-connect uses the same patient-portal endpoints with plain Bearer auth — de-risking the endpoint-family watch item.
- US SSO config verified structurally identical (own client_id/audience); v13 confirmed as current API generation; GB→EU mapping confirmed.
42/42 tests on Node 20 + 22. No new dependencies.
v0.1.4 — auth hardening from live-config research
Two fixes from the 2026-07-19 auth-flow research session (full findings: #12 comment), which validated the implementation against Medtronic's live discovery/SSO/OpenID configs and the carelink-python-client reference.
Fixes
- Force token refresh on 401/403 (#22, fixes #21). CareLink can invalidate a token before its
expclaim — most commonly when the CareLink phone app logs into the same account. The bridge previously retried the dead token until natural expiry; it now refreshes and retries. Expiry margin widened 60s→600s to match the reference client. - Endpoint fallback chain now includes v13 (#23). Medtronic's app discovery config advertises the carepartner API at v13; the old hardcoded fallback list only handled v5/v6/v11. Fallbacks are now derived from a version list (13, 11, 6, 5) and degrade sanely for unknown future versions.
Research conclusions (no code change needed)
- Medtronic completed migration to Auth0 for both US and EU; the bridge implements the correct (Auth0) flow with PKCE, which the tenant explicitly supports (
S256). - Token refresh parameters and the primary BLE data endpoint match the live config and reference implementation.
- Remaining validation requires a real account — watch items tracked in #12.
37/37 tests on Node 20 + 22. No new dependencies.
v0.1.3 — deployment artifacts + review fixes
Deployment artifacts (the v0.1.3 phase from ROADMAP.md)
deploy/systemd/carelink-bridge.service— hardened user-level systemd unit (#6)deploy/install.sh— idempotent install/update script (#6)deploy/nightscout-docker-compose.yml— Nightscout + MongoDB + cloudflared stack (#6)deploy/README.md— full deployment runbook (#6)
Fixes from the 2026-07-19 full-repo review
- Pump timezone offset now rounds to the nearest 15 minutes, not whole hours (#20, fixes #15). Previously, users in half/quarter-hour timezones (+05:30 India, +09:30 central Australia, +05:45 Nepal, −03:30 Newfoundland) had every SGV timestamp skewed by up to 30 minutes.
⚠️ Behaviour change: pump clocks off by more than 7.5 minutes are no longer silently rounded away — set the pump clock accurately. - systemd
ReadWritePathsis now directory-level (#19, fixes #16). The previous per-file bind mounts would have silently blocked the stale-token recovery path (deletinglogindata.jsonon refresh-token expiry). Needs on-host verification — see #16's checklist. - Version/docs sync (#18, fixes #17):
package.jsonnow matches the release tag, CONTRIBUTING's Node matrix matches CI (20/22), and a maintainer release checklist prevents recurrence.
Notes
- No new dependencies. 33/33 tests passing on Node 20 + 22; the suite is verified timezone-independent (run under UTC, Asia/Kolkata, America/St_Johns).
- Real-data validation against a live 780G is still pending (#12). Nothing in this release has been tested against a real CareLink account.
v0.1.2 — remove unused proxy code; supply-chain reduction
Removes the proxy code path that became dead code after the v0.1.1 USE_PROXY=false default. Pure hygiene + supply-chain reduction.
What's in this release
Supply-chain reduction
- Removed
https-proxy-agentandsocks-proxy-agentfrom runtime dependencies. Both packages are pure attack surface for a medical-data bridge — neither is referenced anywhere in the production code path now that the default is safe. - Deleted
src/carelink/proxy.ts(94 lines). TheloadProxyList,createProxyAgent, andProxyRotatorhelpers had no remaining callers after the v0.1.1 default change. - Removed
USE_PROXYenv var, thehttps.txtconfig file, and the corresponding.gitignore/.env.exampleentries.
Behavioural fix (incidental)
- The previous
fetch()hadmaxRetry = this.proxyRotator.hasProxies ? 10 : 1— meaning default users got exactly 1 retry attempt, masked by the proxy-aware logic. New code: 3 attempts with exponential backoff (2s, 4s, 8s). This is a strict improvement for everyone; no regressions.
Migration for power users
If you were using https.txt + USE_PROXY=true to route CareLink traffic through a proxy:
# Before
echo "1.2.3.4:8080" > https.txt
USE_PROXY=true npm start
# After
HTTPS_PROXY=http://1.2.3.4:8080 npm start
# (or with auth: HTTPS_PROXY=http://user:pass@1.2.3.4:8080)axios respects HTTPS_PROXY / HTTP_PROXY / ALL_PROXY / NO_PROXY natively. No bridge-specific config required.
Verification
npx tsc --noEmit: cleannpm test: 27/27 passnpm audit --omit=dev: 0 vulnerabilities- Net diff: -146 / +18 lines
Credits
PR #4 by @NovaLux12.
Upstream relationship
No upstream PR — this is a deliberate security choice (removing attack surface). Documented in the PR's "Why" section. If upstream becomes active again, this divergence is a candidate for upstream discussion.
v0.1.1 — post-release hygiene + drop Node 18
Patch release covering post-v0.1.0 review follow-through and a Node 18 EOL decision.
Why v0.1.1?
A post-v0.1.0 review surfaced 2 critical and 5 high-priority issues. This release ships the fixes that don't change runtime behaviour but tighten security defaults, sync version metadata, and rebalance the supported Node versions.
What's in this release
Security hygiene
USE_PROXYdefault flipped fromtruetofalse. The previous default would silently route all CareLink traffic through the proxies listed inhttps.txtif that file existed — a footgun for a medical-data bridge. New users get no proxying; power users opt in explicitly withUSE_PROXY=true. README documents the new default.- Removed undocumented
dotenv.config({ path: 'my.env' })lookups fromsrc/main.tsandsrc/login.ts. These were upstream legacy, never documented, gitignored but confusing for anyone reading the entrypoint. SECURITY.mdcontact clarified. Removed theNovaLux12@users.noreply.github.comemail line (GitHub's auto-generated no-reply addresses do not accept inbound mail). Security Advisories are now the sole contact channel.
Metadata sync
package.jsonversion is now0.1.0to match thev0.1.0git tag. Was inherited from upstream at2.0.0— would have causednpm install carelink-bridgeto report the wrong version.
Test coverage
- Regression test for the cherry-picked BLE device detection fix (upstream PR #2 by @terminalcommand). Extracted
isBleDevicefrom a private method to a module-level export; added 7 cases including the exact bug condition (deviceFamily: 'BLE_MINIMED', medicalDeviceFamily: undefined). - 27/27 tests pass (was 20/20 before v0.1.0).
Platform support
- Node 18 dropped from CI matrix and
engines.node. Node 18 was EOL'd upstream on 2025-04-30. Vitest 4 (the supported line, which fixes 5 transitive dev CVEs) does not support Node 18. Now targeting Node 20+. vitestbumped to^4.1.10. Kills 5 transitive dev CVEs (esbuild GHSA-67mh-4wv8-2f99, vite moderate/high, vitest critical, @vitest/mocker, vite-node). All dev-only — no runtime impact.
Repo hygiene
- Issues enabled on the repo (was disabled by default for forks). The shipped
.github/ISSUE_TEMPLATE/files are now reachable. - Dependabot security updates enabled. Auto-PRs will fire for any future CVE.
- Branch protection on
main— CI required, linear history, no force pushes, no branch deletion, conversation resolution required, admins included.
Credits
All new code in this release is by @NovaLux12. The BLE detection fix is by @terminalcommand, co-authored by Nicolas Raverdy (@nraverdy).
Upstream relationship
No behavioural divergence from upstream in this release. Every change is either (a) a hygiene fix for inherited upstream quirks (USE_PROXY default, my.env lookup, package.json version), (b) additive (tests, docs, repo config), or (c) a CVE fix on the supported dev toolchain. The fork continues to carry only changes that landed in or were submitted upstream — the BLE fix remains cherry-picked from upstream PR #2.
v0.1.0 — community fork
First tagged release of the community fork.
This is a fork of domien-f/carelink-bridge maintained while upstream is quiet. Original code by @domien-f.
What's in this release
- All upstream code (3 commits from domien-f).
- @terminalcommand's upstream PR #2 — fixes BLE device detection for patient accounts. Resolves empty-data / no-SGV-upload issue on 780G, Guardian 4, and Simplera CGM devices. Co-authored by Nicolas Raverdy (@nraverdy). Cherry-picked with full author preservation.
- CI —
vitest+tsc --noEmiton Node 18, 20, 22. - Community repo hygiene — issue templates, PR template, Dependabot, CODEOWNERS, labels.