All notable changes to this fork are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (0.x: minor = phase, patch = fixes within a phase).
Entries before v0.1.0 describe the upstream history this fork carries. Written retroactively on 2026-07-19; dates are taken from the git/tag/release record, not reconstructed.
0.2.0 — 2026-07-22
-
Atomic 0600
logindata.jsonwrite —writeLoginDataAtomicinsrc/carelink/token.tsopens 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.tsdetectsbgunits/bgUnitsofMMOL_L(with casing fallbacks) and convertssgto mg/dL at the SGV assignment site viaMath.round(sg * 18.0182). Without this, a mmol/L CareLink account flowing into Nightscout is interpreted as mg/dL by downstream looping clients (Loop, xDrip, AAPS) and over-delivers insulin. Asserted numerically:5.5 mmol/L → 99 mg/dL,2.0 → 36,22.2 → 400. -
P0.2 lastAlarm policy —
src/last-alarm.tsplusNightscoutLastAlarmAnnotationinsrc/types/nightscout.ts. CareLink alarms surface asdevicestatus.last_alarmwith code/datetime/text/severity. Priority-1 codes (paradigm delivery-stopped 4/5/6/16/43/61) hitconsole.warnalways-on, irrespective of verbose mode. No alarm relay to Nightscout/api/v1/treatments.json— verified by an absence-grep test oversrc/. NGP-tier codes are intentionally empty pending a sanitised 780G fixture. -
forceRefresh successive-401 regression fix —
authenticate()now returnsPromise<boolean>(true iffrefreshTokenactually ran).fetch()only clearsforceRefreshwhen authenticate did NOT refresh. The pre-fix code re-sent a dead token across consecutive 401s because the flag was unconditionally reset every iteration. The successive-401 test pins the fix: a 401 immediately after a successful refresh+401 still triggers a secondrefreshTokencall. -
P3 trend
NONE→{trend: 4, direction: 'Flat'}— matches Nightscout convention and every other CGM source's flat (xDrip, nightscout-connect). Tested against the realmissingLastSgvfixture intest/samples.ts(with the trailing sg=0 entry dropped at the test site so the production guard for "trend attaches only when the most recent SG is real" is honoured). -
NoAuth0SSOConfigurationErrornamed class +selectAuth0ConfigUrlhelper — the named error is grep-distinguishable in journald. The helper throws it on missingAuth0SSOConfigurationin the discovery entry, carrying the diagnostic context (region, appVersion) for operators. The helper's behaviour is tested directly against a syntheticDiscoveryCpEntryshaped like the v3.4 / v4.0 no-Auth0 tracks. -
Discovery pinning —
DISCOVERY_APP_VERSIONandbuildDiscoveryUrl(isUS)extracted intosrc/discovery.ts. The version string and the URL template (/connect/carepartner/v13/discover/...) are testable constants; a future contributor who edits either cannot silently regress the bridge to a no-Auth0 track (3.4 / 4.0). -
Refresh-failure classification —
isPermanentRefreshFailurepredicate insrc/refresh-failure.tsdistinguishes permanent (HTTP 400 +invalid_grant/invalid_client) from recoverable (5xx, 429, transport, anything not matching the OAuth contract). The catch inauthenticate()is split: one try forrefreshToken(classified), one forwriteLoginDataAtomic(always retain + rethrow so a local disk failure doesn't nuke the token). 18-test suite covers the three behaviours plus defensive defaults (null, undefined, plain Error, etc.). -
Status-aware capped exponential backoff with jitter, honour
Retry-After—decideRetryinsrc/retry-policy.tsclassifies each failed attempt: permanent 4xx fail fast, 429 honoursRetry-After(numeric or HTTP-date) up to a cap, 5xx and transport errors retry with full-jitter exponential backoff (capped). The pre-fix fixed 2s/4s/8s path is replaced. The 401/403 path is short-circuited beforedecideRetryso the existing force-refresh cycle still runs. Two integration tests pin the wiring: 429 +Retry-After: 25— advance 10s, assert no retry (the fixed 2s/4s/8s path would have retried here); advance 20s more, assert the retry fired. 404 — one call, not three.
- The systemd unit now uses systemd's
%hspecifier instead of a hardcoded home-directory path, so it is portable across machines and accounts with no editing — install into~/carelink-bridgeand it resolves to the running user's home. (This also removes the maintainer's own paths from the shipped artifacts.)
CARELINK_MAX_RETRY_DURATIONenv, theConfig.maxRetryDurationfield, theCareLinkClientOptions.maxRetryDurationfield, and theDEFAULT_MAX_RETRY_DURATIONconstant — the option had no defined unit, the fetch loop never honoured it, and the fix-path is the status-aware policy insrc/retry-policy.ts.
- This is expected to be the last contribution in the current
maintenance window. The 780G-payload-fixture items
(
markers[]for treatments,therapyAlgorithmStatefor auto-mode,limits[]schedule, multi-patient fan-out,reservoirLevelPercentsnap-points, NGP-tier alarm codes) are 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. The token-permission and atomic-write fixes shipped here provide the security baseline the deferred items will inherit; the discovery-pinning and named-error work provides the operational baseline. - The PR references for the new items are intentionally left as
plain bullets rather than
([#N])because the GitHub PR/issue numbers are not yet assigned. When the PR is opened, replace the inline rationale with the assigned number to match the existing convention.
0.1.6 — 2026-07-19
npm run doctor— a pre-flight self-check that validates.envcompleteness, decodes and reports the login token's validity/expiry, and confirms CareLink and Nightscout are reachable with an acceptedAPI_SECRET, without fetching pump data. One request each to two hosts, safe to run repeatedly. Exit code is non-zero on failure so it can gate a deploy. First item of the v0.2.0 operability set (#8). (#28)
- The discovery app-version string (
android/3.6) is now a documented, named constant insrc/login.ts. Live probing showed Medtronic's discovery endpoint returns a different config per version — only 3.6/3.7 carry the Auth0 SSO config this flow needs, while 3.4 and 4.0 return no-Auth0 tracks — so a well-meaning "bump to a newer number" would silently break login. The no-SSO-URL error now names the version string as the likely cause. (#27)
0.1.5 — 2026-07-19
- Data POST bodies (BLE and carepartner) now use the username CareLink reports
from
/patient/users/meinstead ofCARELINK_USERNAMEverbatim, falling back to the configured value. Operators who enter their email while their CareLink username differs no longer send the wrong identifier on every data request. A verbose-mode log notes when the two differ. (#25)
- Round 2 of the auth-flow research validated the automated-login page scraping against Medtronic's live Auth0 Universal Login (field names, hidden fields, submit action all match) and confirmed nightscout-connect uses the same data-endpoint family. Findings: #12 round-2 comment.
0.1.4 — 2026-07-19
- A 401/403 from the CareLink API now forces a token refresh on the next retry. Previously a token invalidated mid-lifetime (most commonly by the CareLink phone app logging into the same account) was retried until its natural expiry, stalling the bridge. (#22, closes #21)
- Token expiry margin widened from 60s to 600s, matching the carelink-python-client reference, so a token passing the check cannot expire mid-fetch. (#22)
- Carepartner data-endpoint fallbacks are now derived from a known-version list (13, 11, 6, 5) instead of a hardcoded v5/v6 replace-chain, adding the v13 generation that Medtronic's app discovery config now advertises and degrading sanely for unknown future versions. (#23)
- Round 1 of the auth-flow research validated the implementation against Medtronic's live discovery/SSO/OpenID configs: the Auth0 migration is complete for US and EU, the config shapes match our types field-for-field, and PKCE S256 is explicitly supported. Findings: #12 round-1 comment.
0.1.3 — 2026-07-19
deploy/directory: hardened user-level systemd unit, idempotentinstall.sh, Nightscout + MongoDB + cloudflared docker-compose stack, and a full deployment runbook. (#6)- Maintainer release checklist in CONTRIBUTING.md so version metadata cannot drift silently again. (#18, closes #17)
- Pump timezone offset now rounds to the nearest 15 minutes instead of whole hours. Users in half/quarter-hour timezones (+05:30 India, +09:30 central Australia, +05:45 Nepal, −03:30 Newfoundland) previously had every SGV timestamp skewed by up to 30 minutes. Pump clocks off by more than 7.5 minutes are no longer silently rounded away. (#20, closes #15)
- systemd unit grants
ReadWritePathsat directory level; the previous per-file bind mounts would have blocked deletion oflogindata.jsonon refresh-token expiry (unlink EBUSY), silently defeating the stale-token recovery path. The.envwrite grant (never needed) was dropped. (#19, closes #16) - CONTRIBUTING.md no longer claims CI runs on Node 18;
package.jsonversion synced with the release tag. (#18, closes #17)
0.1.2 — 2026-07-19
- All fork-specific proxy code (
loadProxyList,createProxyAgent,ProxyRotator, thehttps.txtproxy-list file, and theUSE_PROXYenv var), along with thehttps-proxy-agentandsocks-proxy-agentdependencies. Pure attack-surface reduction: −146/+18 lines, two fewer supply-chain deps. (#4)
- Outbound proxying is now done via the standard
HTTPS_PROXY/ALL_PROXYenv vars, which axios respects natively; documented in the README. (#4)
0.1.1 — 2026-07-19
USE_PROXYdefaults tofalse. A previous version silently routed all CareLink traffic (OAuth tokens, CGM data) through proxies listed in an undocumentedhttps.txtfile if it existed. Undocumentedmy.envconfig lookups removed. SECURITY.md gained a durable security-decisions section explaining each choice and what would justify reversing it.
- Regression test for the cherry-picked BLE device detection fix, locking in
the
deviceFamily || medicalDeviceFamilyfallback from upstream PR #2. - Acknowledgements crediting @terminalcommand and @nraverdy.
- Dependabot security updates, issues, and branch protection on
main(required CI checks,enforce_admins).
- Dropped Node 18 (EOL 2025-04-30); CI matrix is now Node 20 + 22.
- vitest bumped to ^4, clearing five transitive dev-dependency CVEs.
package.jsonversion corrected from the inherited2.0.0to0.1.0to match the tag line.
0.1.0 — 2026-07-18
Minimum viable community fork of domien-f/carelink-bridge, picked up while upstream is quiet.
- Cherry-picked upstream PR #2 by @terminalcommand: BLE device detection
for patient accounts. The patient
monitor/dataendpoint reports the device family asdeviceFamily, notmedicalDeviceFamily; without the fallback, 780G, Guardian 4, and Simplera devices fell through to a legacy endpoint that returns empty data. - CI workflow (
tsc --noEmit+ vitest). - Community fork README notice, ROADMAP.md, CONTRIBUTING.md, SECURITY.md.
Carried in this repository's git history from domien-f/carelink-bridge:
- 2026-06-11 — BLE device detection fix for patient accounts (the change later formalized as upstream PR #2 and credited above).
- 2026-02-16 — BLE device support and country/language configuration (upstream #1).
- 2026-02-13 — Initial upstream implementation: CareLink mobile-app OAuth (three-strategy login), pump/CGM fetch, Nightscout transform and upload.