Repository navigation
v1.5.0-dev02
Pre-release
Pre-release
Second dev pre-release of the 1.5.0 minor
First Day of Week, plain HTTP to a server on your own network in the Android app, Health Connect catching up on past days, Share to OFF with your own account, correct USDA and CookTrace recipe values, more of the app translatable, many fixes, and security updates worth installing.
Heads-up before installing:
- If you show NutriTrace inside a dashboard (Homarr, Organizr, Home Assistant), it shows blank until you set
FRAME_ANCESTORSto the dashboard's address. - If your reverse proxy doesn't pass on the address you open (
HostorX-Forwarded-Host), setPUBLIC_URLto it. - Share to OFF now needs your own free Open Food Facts account.
- Recipes from CookTrace show correct ingredient values once CookTrace is updated too.
Added
- Other apps can read your weight and body measurements with an API token (relates to #111). A token with the new
read:body-measurementsscope reads the owner's weight and body-composition readings fromGET /api/v1/body-measurements, by date range and source, without turning on the Public API. Each source's readings stay separate. Guide Thanks @kgenerozov. - The Android app connects over plain HTTP to a server on your own network (#194). Enter an
http://address such ashttp://192.168.1.20:3001orhttp://nutritrace.local:3001, read the warning that the connection isn't encrypted, and choose Connect Anyway; the answer is kept for that server, and Settings > Server Connection shows Not Encrypted. Only private, Tailscale, loopback and link-local addresses, one-word names, and names ending in.local,.home.arpa,.internalor.lanqualify; any otherhttp://address is refused before anything is sent. HTTPS works as before. Coming from a debug build that used plain HTTP, the app asks before it contacts the server. Debug builds now follow the same rule. Everything else the app reaches directly (Open Food Facts, USDA, update checks, ntfy, Gotify, Apprise, AI providers, pictures) is still HTTPS-only, as before. Guide Thanks @systems-monitor for the request. - First Day of Week (#232). Settings > Regional & Units starts the week on Monday, Sunday or Saturday. The Diary's week strip, every calendar, and the weekday lists (goals and coach targets by weekday, fasting days, the weekly summary day) follow it; a value set for a weekday stays on that weekday. With a day chosen, the weekly summary covers the last whole week. Automatic, the default, follows the device's region: on a device set to the US nothing changes, but on one set to a Monday region (most of Europe, for example) weeks now start on Monday without touching the setting. Works offline and syncs to your other devices. Thanks @fatman00 for the request and @adam-sesese for seconding it.
Changed
- More of the app can be translated. The desktop Diary side panel, the Diary nutrition bar, Goals, Statistics, the food editor, and the names of nutrients, body measurements and wellness goals now come from the language files. Weekday names, dates, Today and Yesterday in the Diary follow the app language instead of the browser's. Thanks @tarzan77cz.
Fixed
- The app no longer reports a database error on start while a language file is still loading. In a language other than English, the first screen could mount before its words had arrived. Thanks @tarzan77cz.
- Recipes from CookTrace show the right values for each ingredient (#296, reported by @JohannesKi). Each ingredient showed the values of its CookTrace pantry serving, usually 100 g, whatever the recipe used, so 250 g of flour counted as 100 g. Once CookTrace is updated too, each ingredient comes with the values for its amount, and an amount CookTrace can't convert, like a pinch, shows at its serving with a note to edit it. Update the recipe from CookTrace to correct ones already imported: opening one offers Refresh.
- The Android app reads past days from Health Connect (#294, reported by @saha-kiranmoy). Only today was ever read, so a night of sleep or a day of steps that reached Health Connect later never reached NutriTrace. Now the first read after updating takes in the last 30 days (all Health Connect lets an app read from before it was given access), and after that the app catches up on the last 7 days at most once an hour: when it syncs with your server (as it opens or comes back to the front), when you tap Sync, and in the background sync when Health Connect lets it read; after a longer absence it reads back to where it left off, up to 30 days. Each day's values go to that day, and a night to the day you woke up. Sync on a past day of the Wellness page now reads that day. Only values that changed are sent to the server. Guide
- The Android app's hourly Health Connect sync reads while the app is closed (found with #294). Health Connect answers an app only while it's open unless the app may read in the background, and NutriTrace never asked, so on recent Android the background sync read nothing. Turning Health Connect on now also asks to access data in the background; if you already use it, Settings > Wellness > Background Sync says it isn't allowed and has an Allow button. Without it everything still syncs when you open the app, and on a phone whose Health Connect can't read in the background, Settings says so. Guide
- Workouts, floors and water from Health Connect come in when you open the app (found with #294). Only the background sync could read them, so without background reading allowed they never arrived. Now that reader also runs once when the app opens or comes back to the front, and when you tap Health Connect Sync on Wellness or in Settings, and Wellness refreshes when it's done. It runs one at a time, not again within a few minutes, and sends only what changed; a workout it sends is no longer sent a second time by the app's own sync.
- The Android app no longer turns off its background Health Connect sync or reminders after it was closed mid-save. When Android stopped the app while it was writing, the next start couldn't read the settings (the database had an unfinished write to undo) and took that for "off": the hourly Health Connect sync was cancelled until the app's next start, and reminder alarms could be cancelled too. The settings are now read in a way that finishes undoing the write first, and a setting that can't be read leaves the schedule as it was.
- Share to OFF works with your own Open Food Facts account (#284, reported by @fatman00). It failed with "Unexpected token '<', "<!-- start"... is not valid JSON" because, with no account set in Settings, the app used a shared login that Open Food Facts refuses. Sharing now needs your own free Open Food Facts account: with none set, or a wrong one, Share to OFF asks for it right there in the food editor and shares once it's saved (it's the same account as in Settings > Food Sources). The product and its photo go through your server (the Android app with no server sends them to Open Food Facts itself), and a wrong username or password, a product Open Food Facts refuses, or Open Food Facts being unreachable each get a plain message. The photo on the food is now sent too.
- Foods added from USDA keep USDA's values (#290, reported by @wallinb). Foods added from USDA with a serving size other than 100 g were saved with wrong values: USDA gives every value per 100 g, and the app read them as per serving, so popcorn at 385 kcal per 100 g with a 13 g serving was saved at 2,962. Open one and tap Refresh from USDA, or add it again. Diary entries already logged keep their values, and a saved meal or recipe that uses the food keeps the old ones until you add the food to it again. Drinks USDA lists in milliliters are now kept per 100 ml.
- Scheduled backups run at the time you picked, in your time zone, and are named in it (found with notetrace#44, thanks @marinierb). The time in Settings > Backup was read in the server's zone, UTC on a container without
TZ, so a 2:00 AM backup in New York ran at 10:00 PM the evening before, and files were named in UTC. The schedule now keeps the zone of the browser that set it and shows it next to the time; a schedule set before this runs as it did, with Use My Time Zone to switch. Runs stay on that time through clock changes, and one missed while the server was down happens once, then the schedule carries on. Backup files, on the server and the phone, are named in local time with its UTC offset (nutritrace-backup-2026-10-21T22-00-00-0400.zip).TZon the container sets the server's own zone. - Trace works with Gemini when a tool answers with a list (found with lifttrace#147, thanks @wretchedxx). With a Google Gemini key, questions that looked up your workouts or your activity log, or a coach's clients, client foods or recommendations, failed with "Invalid JSON payload received. Unknown name "response" ... Proto field is not repeating, cannot start list." Gemini takes a tool's answer only as an object, so a list now goes to it inside one, in the app and through the server when Trace is set by environment variables.
- Deleting a meal or recipe from the Foods page on a wide screen deletes it (#282, reported by @perkons). With the preview pane beside the list (1440 px and wider), Delete hid the food that shared the meal's number instead, said "Food deleted", and left the meal. A food hidden this way is not erased: it stays in the server's database, where the admin can restore it. The confirmation now names meals and recipes, its button says Delete, a delete the server refuses says so, and someone else's shared meal no longer offers Delete there.
- Add to Diary from that pane counts the meal's use, not a food's (#282). Logging a saved meal there moved the food that shared its number up Most Used and Recently Used, and left the meal where it was.
- In the Android app, logging someone else's shared meal no longer counts a use of one of your own (#282). The phone read the shared meal's server number as one of its own and moved that meal up Most Used and Recently Used. A shared item's use isn't counted on the phone now.
- Add to Diary from that pane logs a saved meal's foods (#282), at their saved portions, as adding the meal from the Diary does. It went in as one row named after the meal. A recipe still goes in as one row.
- ntfy pushes from the Android app carry their message. Sent from the phone, they came with the title only and "triggered" as the message, because Android sends no body without a content type; the app now sends one.
- The app never opens with nobody signed in when your server can't be reached. If the server was down, behind a gateway error or a sign-in proxy when the app started, the web app opened as if the server had no accounts, and on Android, after Log Out, opening the app again showed the Diary with no account. Now the sign-in page shows instead, on the web saying the server can't be reached (or that something else answered) with Retry. A server that runs without accounts still opens straight to the app, and an account kept on the device still works offline.
- The Android app shows your server's single sign-on, or says why it can't (found with notetrace#36, thanks @harikiran-lab). Behind a sign-in proxy such as an Authentik or Authelia outpost or Cloudflare Access, the proxy's login page answered the app instead of the server. Connecting took that page for the server and showed a password form without single sign-on and without a word, and signing in then failed with "Unexpected token '<'". The app now says that something other than NutriTrace answered with a web page (a sign-in proxy, or not your server's address) and that it has to let the app reach
/api. Sign-in options that couldn't be loaded are said, with a Retry, on the setup screen, the sign-in page and Settings, and the password form stays. Settings > Server Connection offers single sign-on too (with a NutriTrace 1.4 server), so moving from Use Locally to a server that signs in that way works; its sign-in options load as you type the address. - Trace can go through many days at once (found with notetrace#34, thanks @harikiran-lab). Asked something like "which days last month did I have pizza", Trace gave up with "Too many tool call rounds": it had 5 rounds of looking things up (8 for a coach), read the diary one day per round, and saw at most 50 saved meals. Now it reads up to 31 days of the diary at once, goes through the meals library page by page, one answer can take 12 rounds, and when that isn't enough Trace answers from what it found and says what's left, so you can ask for the rest. This works the same when Trace is set up on the server.
- Trace's replies are no longer cut off at 4,096 tokens, and a reply that stops short says so (found with notetrace#23, thanks @harikiran-lab). A reasoning model's thinking counts toward a reply's length, and 4,096 tokens could go to thinking alone, so Trace said it returned nothing. Trace now asks for up to 16,384 tokens, with every provider, and asks again with 4,096 when a server or an older model won't take that many (vLLM before 0.32 serving a small context, Claude 3 Haiku). A reply cut off at its limit, or one with reasoning but no answer, is said and never used, so a rewrite never replaces your text with half of it and a cut-off tool call never runs.
- Trace shows its review card for each food it estimates for you. While one card waited for Add or Discard, no other card could appear, so a second food got its numbers in the chat instead; and the Quick Calories card came only with a photo. Now a new food gets its own card in place of the waiting one, the Quick Calories card also works in text when you ask for quick calories, and a food Trace can't find gets an offer to estimate it on a card. Named foods are still searched first, an estimate still keeps its full nutrition on the food card, numbers you type still log straight away, and a card you already added or discarded doesn't come back by itself. Thanks to the Reddit user who reported it.
- Saving your Profile works again (#269). In the Android app every save failed with "Could not save profile" before anything reached the server. In the browser a save could fail the same way when the browser refused this site storage, or when a change your server would not accept was waiting and storage then stopped working; diary, food, meal, activity and fasting saves had the same gap. With a connection, saving no longer depends on the browser's storage. With neither a connection nor storage, Profile says which, and a save kept for later no longer makes the app forget who is signed in. Thanks @jsapede.
- Diary photos in the Android app come from the right food after a reinstall (#277, reported by @Chrristin). The app matched a diary item to its food or recipe by an id that, once a reinstall renumbered the phone's ids, could belong to another one, so items logged in the browser, in the app before the reinstall, or through MCP or the API showed another photo or none. A food or recipe saved with a space before or after its name lost its photo too. Meals logged through MCP or the API, and items added again from Yesterday's Meals in the app, now keep which food each item came from, and the browser finds the photo of an item logged in the Android app by its food's server id.
- Diary items stay with their food after Upload or Merge in the Android app (#277, reported by @Chrristin). Connecting the app to a server with Upload or Merge sends your foods, meals and recipes there as new ones, but diary items kept the ids those had before, so on that server and back on the phone an item could show another food's photo, units or barcode, or none. Items now follow their food, meal or recipe, including one that only goes up with the next sync, and an item whose food isn't on the phone is left as it was.
- Saved meals and recipes keep their foods after Upload or Merge in the Android app (#277, reported by @Chrristin). Their ingredients kept the ids their foods had before, so logging such a meal or splitting such a recipe into the diary later gave its items another food's units or barcode, or none. They now follow their foods and recipes the way diary items do.
- Diary items no longer take units or a barcode from an unrelated food (#277, reported by @Chrristin). An item whose food was deleted before Upload or Merge in the Android app kept that food's old id, which on the new server could belong to another food, and the item showed that food's units and barcode. A food now lends them only when it has the item's name, or was already there when the item was logged (a food renamed since keeps working).
- Another device no longer puts a diary item's old food back (#277, reported by @Chrristin). When the Android app pointed a diary item at its food again (after Upload or Merge, or once the food reached the server), the browser or another phone sending its older copy of that day could put the old food back. The item now keeps its food over an older copy, and an edit made elsewhere since still wins, splitting a recipe and moving a meal included.
- Yesterday's Meals shows the same photos as the diary (#277, reported by @Chrristin). A recipe could show the photo of a food that happened to share its number, and a food saved with a space after its name, or in other capitals, showed none.
- Foods of the same name show the same photo in the browser and the Android app. When several foods or recipes share a name and nothing else says which one a diary item came from, the server usually picked the oldest one's photo and the Android app another; both now show the newest one's (on the server, your own before ones shared with you).
- Yesterday's Meals offers the day before the one you're adding to (#280, reported by @fatman00). Adding food to another day of the diary, such as tomorrow when you meal prep, still offered the meals of the day before today. It now offers the day before the day you're adding to, named after that day (From Today, From Sunday, or its date) unless it's yesterday.
- Birthday and gender from first-run setup show in Profile (#269). Setup kept them only as settings, so Profile showed both empty, and in the Android app with no server Gender showed blank. They now go on the account, and accounts set up before this are filled in once when the server starts, never replacing a birthday or gender the account already has. Turning on user accounts also no longer clears the email you just entered for the new admin. Thanks @jsapede.
- Reminders and the weekly summary set just after midnight fire. The server read the hour after midnight as 24, so a bedtime, meal or weigh-in reminder, a daily device sync or a weekly summary set between 00:00 and 00:59 never came, and a device sync window starting at 00:00 skipped its first hour. The wind-down before a bedtime just after midnight now comes the evening before. Reminders set just before midnight still come when the check runs just after it.
- Trace's diary averages cover the days before today. In the evening west of UTC the window took in today and left out its oldest day.
- Scheduled fasts start at your time and are marked on your day. On a server in another time zone, a scheduled fast followed the server's clock (a New York 23:00 fast started at 19:00 on a UTC server), and the day it fired was marked with the UTC date, so after an evening start west of UTC the next day's fast was skipped.
POST /api/settings/force-syncalso fetches your today, not the UTC date's. - Weekly Summary can be turned on with email alone. Settings > Notifications showed it only with Device Notifications or a Push Service on. It now also shows when the server sends email and you have an address, and its description says whether it comes by push, email or both.
- Scheduled Fitbit syncs and the smart bedtime reminder use the right days at UTC+13 and +14. On a server in New Zealand (in summer), Tonga or Samoa, the scheduled sync fetched each day a day early and never today, and the bedtime reminder used the night before last as last night.
- The weekly summary email reaches people with no push service. With Weekly Summary on and an email address but no ntfy, Gotify or Apprise set up, the email never came. It now goes on your Delivery Day to each channel you have: email, push, or both.
- The weekly summary push comes once a week, on your Delivery Day. A Fitbit sync of several days that ran on a Sunday sent it again every time, whatever day was set in Settings > Notifications.
- Signing in with SSO finishes on phones with an older Android System WebView. On older WebView versions (seen on 124), the app didn't recognize the sign-in return from the browser, so the sign-in page closed and the app stayed signed out.
- OpenAI Compatible works without an API key (found with notetrace#18). Settings marks the key optional, but the connection test asked for one, the status stayed blank, and the chat panel said "API key required" over the conversation.
- OpenAI Compatible accepts a Base URL with or without
/v1, or the whole/v1/chat/completionsaddress, in Settings and inAI_BASE_URL. Given with/v1, every request went to/v1/v1/chat/completions. - Test Connection is always in Settings > AI Assistant. It was only in the status line, which showed nothing until every setting was filled in, and never for OpenAI Compatible without a key. A setting filled in last tests on its own once everything it needs is there, and a test only counts for the settings it tested.
- When Trace gets no answer from your provider, it says what to check instead of "Failed to fetch": the Base URL, or a server that has to allow this site on every answer, errors included, or an https:// address for a page on HTTPS. A reverse proxy that adds CORS headers only to successful answers hid the provider's real error, such as a model that can't use tools.
Security
- The server log no longer shows your USDA API key when USDA answers with an error. The server logged the full address of a failed food lookup, key included. Keys, tokens, passwords and a
user:password@part in an address are now shown as***in every log line that prints one: food lookups, the CookTrace connection, the Open Food Facts mirror download and photo downloads. - Pages on lookalike addresses can no longer read or change your account. The server let any web page whose address contained its own name call it with your sign-in and read the answer:
nutrition.example.com.attacker.example,xnutrition.example.com, any subdomain of it, the same name on another port, or on a home network:30011for a server on:3001. Such a page under your own domain or on your server's machine could read and change your account while you were signed in; a page anywhere else got nothing, unless the server runs without accounts (single-user mode), where it could read what your browser can reach. Someone would need a page on such an address and you to open it. Only the Android app's own addresses andPUBLIC_URLare allowed now, compared exactly; the Android app, older versions included, works as before. - Web pages on other sites can no longer make changes through your browser on a server without accounts. A page can make your browser send a form or a file upload to the server without being able to read the answer. In single-user mode, which has no sign-in, a page on any site could do that, for example restore its own backup over yours, which gave its author an admin account on your server. Someone would need your server's address and you to open their page. The server now refuses any change your browser says came from another address, with accounts too, where a sign-in already needed a token such a page can't get. The web app, the Android app, MCP, the API and scripts work as before; if your reverse proxy doesn't pass on the address you open (the
HostorX-Forwarded-Hostheader), setPUBLIC_URLto it. Over https that matters only for older browsers, such as Safari before 16.4. - Other sites can no longer show NutriTrace inside a frame. A page could put NutriTrace in an invisible frame and trick a click into it; only NutriTrace itself may frame it now. If you show NutriTrace inside a dashboard (Homarr, Organizr, Home Assistant), it shows blank after this update until you set
FRAME_ANCESTORSto the dashboard's address, for exampleFRAME_ANCESTORS=https://home.example.com. Origins only; a path is ignored. A proxy that already sets its own frame-ancestors for the dashboard needsFRAME_ANCESTORStoo. - vite bumped 6.4.3 to 6.4.5, closes GHSA-587h-7wmj-h5pm (high), GHSA-698v-4r3x-3vcv and GHSA-r825-w8mm-5pxw (moderate), all three through the development server's open-in-editor link, where a crafted file path could run a command on the developer's computer on Windows (WSL included). Only the development server (
npm run dev) has that link; the app and server people install never include it. - hono bumped 4.13.8 to 4.13.13 on the server (through the MCP library), closes GHSA-5r4p-p66f-jhc7 (moderate:
serveStaticcould serve a file without running the middleware on its path). NutriTrace wasn't exposed: it never usesserveStatic, the MCP library uses hono only to read requests, and Express serves its files. npm auditreports 0 vulnerabilities for the app and the server.
Verify the download. nutritrace-v1.5.0-dev02.apk: SHA-256 000bb8d665df84a62e09653b5fca4de922f50781442525f2097b25b4ad37a03b, VirusTotal report.