documentation

Using sardinetracker

The in-app help, on the web: what each view is for, what the numbers mean, and how to log a day without it becoming a chore.

A plain-English guide to using the app and understanding what it's tracking.


What Is This App?

Sardinetracker (formerly biotracker) is a personal health logging app built specifically for people managing photosensitive autoimmune and rheumatic conditions — things like lupus, mixed connective tissue disease, or any illness where symptoms are unpredictable, hard to explain to doctors, and deeply affected by things like sun exposure, sleep, and hormonal cycles.

It was built by Alaric, who has lupus, because she couldn't find a tool that did what she actually needed: connect the dots between daily life and disease activity, and produce something useful to bring to a 15-minute doctor's appointment.

Your data lives on whichever server you're logged into — if you're reading this on someone else's instance, ask them where it's hosted and how it's backed up. If you're running your own, it's in a SQLite file on your own machine. Either way it is not sold, shared, or sent to any third party. UV index data is pulled from a public weather API anonymously using only GPS coordinates — that's the only outside connection the app makes.


What Should I Log Every Day?

The daily entry form is the core of the app. You don't have to fill out every field — log what feels relevant. The more consistently you log, even minimally, the more useful the patterns become over time.

The fields the prediction model leans on most:

  • Symptoms — which categories are active (neurological, cognitive, musculature, migraine, pulmonary, dermatological, mucosal, rheumatic, gastro). For rheumatic, mentioning a specific joint in the notes ("right knee", "jaw") lets the model weight it higher than unspecified aches.
  • Pain and fatigue on the 1-10 scale — the model scores these at every step from 4 upward, not just at 7+. A pain-6 day or a fatigue-5 day counts.
  • Flare occurred? — yes or no, plus severity (minor vs. major). This is the ground truth the model is trying to predict.
  • Sun exposure minutes and UV protection level — how long outside, and whether you wore SPF/hat/full cover/stayed indoors. The model multiplies UV index by exposure time by a protection factor.
  • Sleep hours — short sleep drives up the overexertion score.

Biometrics from your Apple Watch (HRV, RMSSD, resting heart rate, respiratory rate, basal body temperature) fill in automatically if you're using the sardinessync iOS app or the sardinesync-android app, so you don't have to type those.

Everything else adds context and richness over time but won't change your daily score dramatically.

On hard days, there's a simplified quick-entry mode that shows only the fields the model needs. You can reach it by adding ?mode=quick to the end of the daily entry URL, or tap "quick entry" in the daily form.


What the Numbers Mean

Pain Scale (1–10)

  • 1–3: Mild, noticeable but not limiting
  • 4–6: Moderate, affecting what you can do
  • 7–9: Severe, significantly limiting
  • 10: Worst pain imaginable / emergency territory

Fatigue Scale (1–10)

Same idea. A 7 fatigue day is "I cannot get up even though I want to." A 3 is "tired but functional."

UV Index

This is a measure of how strong the ultraviolet radiation from the sun is on a given day, on a scale from 0 to 11+.

UV Index Level
0–2 Low
3–5 Moderate
6–7 High
8–10 Very High
11+ Extreme

For photosensitive conditions, UV exposure on one day can trigger a flare one, two, or even three days later — not necessarily the same day. The app tracks this lag and shows you your personal pattern.


What Is HRV?

HRV stands for Heart Rate Variability. It sounds technical but the concept is simple.

Your heart doesn't beat like a metronome. Even at rest, the time between beats varies slightly — sometimes a bit longer, sometimes a bit shorter. That variation is HRV.

Higher HRV generally means your nervous system is relaxed, recovered, and adaptable. Your body has resources to work with.

Lower HRV generally means your body is under stress — from illness, poor sleep, inflammation, or overexertion.

For people with autoimmune conditions, HRV can drop noticeably in the day or two before a flare becomes obvious. It's your body signaling that something is off before you fully feel it.

You can track HRV with a wearable like an Apple Watch, Garmin, Fitbit, or Whoop. The app lets you log your morning HRV reading and visualize how it trends alongside your symptoms over time.

You don't have to track HRV to use the app — it's an optional field. But if you have a device that measures it, it's one of the more interesting things to correlate with flare activity.


What Is Basal Body Temperature (BBT)?

Basal body temperature is your body temperature at complete rest — typically measured first thing in the morning before getting up, eating, or doing anything. It's usually taken with a special BBT thermometer that reads to two decimal places (like 97.42°F rather than just 97.4°F), though a regular digital thermometer works too.

Why does it matter for autoimmune disease?

BBT does two useful things in this context:

1. It can signal inflammation. When your immune system is active — fighting something, reacting to a trigger — your resting temperature can shift subtly before you feel obviously sick. A pattern of slightly elevated morning temperatures can be an early signal.

2. It tracks your menstrual cycle phase. This matters because hormonal cycles interact with immune activity. After ovulation, progesterone causes BBT to rise by about 0.2–0.5°F and stay elevated until your next period. The app detects this shift to figure out which phase of your cycle you're in — which is much more accurate than just counting days, especially if steroids or disease activity make your cycle irregular.

How to log it

Take your temperature first thing in the morning, before getting out of bed, and log it in the daily entry. Even just a few months of data starts to reveal patterns.


The Forecast Model

After about 7 days of logging, the app starts generating a daily flare risk score. This is not a medical prediction — it's a statistical pattern based on your own historical data.

The score comes from adding up contributions from a dozen or so categories. You can see exactly what fired and how many points each contributed on the Forecast page, and the full breakdown over time on the Model page (/model in the URL).

What the model looks at:

  • UV dose today — weighted UV index times minutes outside times a protection factor. Being in the sun at UV 9 for an hour without protection scores much higher than 30 min at UV 3.
  • UV over the prior 4 days — sun damage accumulates. The model adds up recent days with decay weights so yesterday counts more than 4 days ago, but 4 days ago still counts.
  • Overexertion — steps relative to your personal baseline, adjusted for sleep. A big walk on 4 hours of sleep scores higher than the same walk after 8.
  • Basal body temperature elevation — subtle rises above your personal baseline may signal building inflammation.
  • Active symptoms — each category contributes its own points, with rheumatic (joint) pain scored higher if you name a specific joint vs. an unspecified ache.
  • Pain and fatigue — laddered scoring. A pain of 4 counts a little, 5 counts more, 6 more, 7 more again. This replaces an older "only counts at 7+" threshold that missed function-limiting days where a single high-severity symptom was the whole story.
  • Symptom burden delta — how many more symptom categories are active than your personal 14-day baseline. Captures acceleration rather than raw count, which matters because chronic daily symptoms become constant noise and don't distinguish flare days.
  • RMSSD deviation — if your 7-day rolling RMSSD (a vagal-tone measure) is sitting noticeably below your 30-day baseline, that's "vagal withdrawal." Empirically precedes flares; mechanistically grounded in the cholinergic anti-inflammatory pathway.
  • RMSSD instability — separate signal from the above. Measures how much your RMSSD has been oscillating day-to-day. Autonomic chaos (wild swings) tends to precede major flares more reliably than a simple steady drop. An additive independent signal — both the deviation and instability can fire on the same day.
  • Respiratory rate deviation — if your 3-day rolling respiratory rate is climbing above your 14-day baseline, the model scores it. This is a pending-validation feature based on ICU literature; the dashboard chart shows whether it's actually earning its weight in your own data.
  • Emotional state — a low day (mood ≤ 4) adds points, because emotional state often correlates with inflammatory load.
  • Cycle phase — if cycle tracking is enabled, luteal / PMS windows can add points. Currently weight 0 in defaults because the signal wasn't predictive in the author's data, but the hook is there if yours is different.

It adds everything up into a risk score from 0 to 25. A threshold (default 8.0, tunable per user) divides "probably just a regular day" from "flare likely." All weights are adjustable in the Forecast Lab.

The more data you have, the more accurate it becomes. Early on it will be rough. Over months, with 20+ logged flares under your belt, the within-person patterns get meaningful and the model can actually call it.

The Model page shows you, visually, which days hit high risk and whether a flare actually followed — so you can see where the model is doing well and where it's missing.


The Forecast Lab (tuning the model)

The Forecast Lab is where you change how much each factor counts. Open it with the lab button on the Forecast or Model page, go to /forecast/lab, or search for lab, help, manual, tune or weights.

It's a terminal-style menu. Type a key:

  • 1: view the current weights
  • 2: adjust weights: symptoms, whole categories (0 turns one off, 1 is the default, 2 doubles it), the multi-day signals, and the flare threshold
  • 3: run a simulation, to see how accuracy, recall and precision would change, and which days would flip, before you save anything
  • 4: view the model code, the actual Python that calculates your score
  • 5: export your changes
  • 6: achievements
  • ?: the full manual
  • X: leave the Lab

Apply These Changes saves the new weights to your account. Reset to Default puts the original weights back.


The Clinical Record

This section is for organizing the medical side of your life — not just symptoms.

  • Labs: Log test results with values, reference ranges, and dates
  • Medications: Track what you've taken and when, with start and end dates
  • Clinical Events: Appointments, ER visits, procedures — with notes
  • Clinicians: A directory of your care team
  • ANA Tracking: Specialized tracking for ANA titers and patterns (relevant for lupus diagnosis)

You can export any of these as a CSV file, which is useful for sending to a new provider, requesting records, or just keeping a personal copy.


The Interventions View (/interventions)

This is where you answer the question "did that medication actually help?" in stats rather than vibes.

For each medication you've marked as a primary or secondary intervention (the tickbox on the medication edit form in /clinical), the Interventions page shows you a card with:

  • Flare impact — total flare count before vs. after starting it, broken down by severity (major, minor, ER), plus the average number of days between flares. Delta percentages are color-coded: green for improvement, red for worsening.
  • Autonomic shift — average RMSSD, SDNN, and respiratory rate before vs. after, so you can see whether the medication calmed or riled your nervous system.
  • Duration of effect (for one-time doses like a dex IV or a steroid injection) — how many days until the next flare of each severity, and how long it took your autonomic metrics to settle back to their pre-dose baseline.
  • Rebound flag (also for one-time doses) — if a medication helped initially but flares surged back 2-6 weeks later, an amber banner will call it out so you don't have to eyeball it yourself.

Logging side effects, rebounds, and dose changes

Every intervention card has an "events" section where you can log dated observations about that specific medication:

  • Side effect — with a severity slider 0-10. Example: "2026-02-14, HCQ, severity 3, mild GI upset if taken on empty stomach."
  • Rebound — manually confirming a rebound pattern, or noting an unexpected flare return.
  • Efficacy change — "seems to be helping less lately" or "big improvement this month."
  • Dose change — timestamped dose adjustments separate from editing the medication record itself (so you can see "200mg → 400mg on 2026-02-14" as an event in context).
  • Note — general observation, no severity field.

Why a separate view instead of putting all of this in symptoms

The Interventions view is for medication-attributed observations. Regular symptom logging is for "this happened today, cause unknown." Keeping them separate preserves the distinction between "my body did X" and "I think the pill I took yesterday caused X." When you're talking to a rheumatologist about whether to continue a drug, the attributed events are what they need.


Notifications (ntfy)

The app can nudge your phone: medication doses, an elevated flare risk, a missed day of logging. It uses ntfy, a free, open-source push service with no account to create.

Set it up

  1. Install the ntfy app on your phone (App Store or Google Play).
  2. In the app, tap + and subscribe to a topic. Make the name long and unguessable, like sardines-k7x9qm3p. ntfy topics are public: anyone who knows the name can read what's sent to it, so don't use your name or anything else obvious.
  3. In sardinetracker, open Settings, enter the same name in ntfy topic, and save.

Each account sets its own topic, so everyone on a shared instance gets only their own notifications. If you'd rather not rely on the public ntfy.sh service, you can run your own ntfy server and put its address in the ntfy server field.

What you'll get

Notification When How often
Medication dose At each scheduled dose time from the taper wizard Once per dose
Flare risk Each morning, if your weighted score over the last 3 days is 5.0 or higher, or you enter the luteal / PMS phase tomorrow. At 8.0 or higher it arrives as high priority. Once a day at most
Daily log reminder When it's been 12, 16, 20 or 24 hours since you last logged (you choose, in Settings; off by default) Once a day at most
Period nudge If cycle tracking is on and you logged flow 4 days ago but nothing since Once
UV data unavailable Early afternoon, if the day's UV index couldn't be fetched for your location, so you know to enter it by hand Once a day at most

The flare alert needs at least 3 logged days before it can say anything.

Taper reminders

  1. Go to Clinical → Medications and add the medication (for example, methylprednisolone 4mg).
  2. Click set reminders on its row.
  3. The wizard fills in a standard 6-day Medrol dose pack (18 doses, 6 tablets on day 1 down to 1 on day 6). Change the start date, times and amounts to match what you were prescribed, or clear it and enter your own schedule.
  4. Click activate reminders. Today's doses also show up as a checklist on the Daily Entry page, to tick off as you take them.

If you run your own instance

  • Notifications only go out while the app is running. On a Raspberry Pi that's always. On a laptop that sleeps, a dose or morning alert due while the lid was closed can be missed.
  • The morning flare alert time and the afternoon UV check are set for the whole instance in config.json, as hours in the instance's timezone: "flare_alert_hour": 8 and "uv_alert_hour": 13 are the defaults. Restart the app after changing them.
  • To turn notifications off for yourself, clear your ntfy topic in Settings.

Auto-Sync from Your Phone

Your phone can send biometric data to sardinetracker automatically, with no typing required for steps, HRV, resting heart rate, respiratory rate, SpO2, or basal body temperature. Each platform has its own companion app.

iPhone: sardinessync

sardinessync is a native iOS companion app that:

  • Reads everything HealthKit offers, including RR intervals for overnight RMSSD, Time in Daylight, and respiratory rate
  • Computes RMSSD from raw heartbeat intervals on your phone (better accuracy than Apple's built-in HRV number)
  • Handles background sync automatically, with no manual triggering
  • Gives you a tab with mobile-friendly sardinetracker pages and local push notifications for flare alerts / medication doses

It's not in the App Store (not paying Apple $99/yr for a hobby project's listing). You clone the repo, build it, and install it on your iPhone yourself. With a free personal Apple ID the app expires every 7 days and needs a re-install, about 2 minutes once you're set up. If you pay Apple the $99/yr, it lasts a year between rebuilds.

See the sardinessync repo's README for the full setup walkthrough. The short version: change the bundle ID to something unique to you, set signing team to your personal Apple ID, build, and configure the server URL and API token inside the app on first launch.

Android: sardinesync-android

sardinesync-android reads Health Connect, so it works with any wearable that writes there (Fitbit, Garmin, Samsung, Oura, Pixel Watch), not just an Apple Watch. Setup is in that repo's README.

What doesn't sync

  • Sleep: Apple Health and Health Connect both have trouble with polyphasic sleep and sleepwalking, so sleep is better entered manually
  • Symptoms, flare status, notes: these are personal observations that only you can provide

A note about your API token

Whatever the app uses to reach the server, an API token or a client secret for signed requests, can write your biometrics and read your flare score. Treat it like a password, and don't paste it anywhere you wouldn't paste your sardinetracker login. If it may have leaked, the remote access guide explains how to replace it.


Searching

The Search page looks through your daily entries, labs, clinical events and medications all at once. Results are grouped by type; click one to open it.


Backing Up Your Data

Download your data. On the Search page, under data management, Download All Data saves a zip with a CSV of each of your tables (these open in any spreadsheet) and the documents you've uploaded. It holds only your own records, even on an instance shared with other people. On a single-user instance it also includes the database file itself, config.json and your Forecast Lab weights.

Delete your data. Next to it, Delete removes your account and everything in it, including uploaded documents, and logs you out. Nobody else's account is touched. You have to type DELETE MY DATA to confirm, and there is no undo, so download first.

If you run your own instance, everything lives in the sardinetracker folder (or wherever SARDINE_DATA_DIR points):

  • biotracking.db: the database
  • config.json: settings and secrets
  • documents/: uploaded files
  • config/custom_weights.json: Forecast Lab weights, if you've saved any

Copy the database with SQLite's own backup command rather than cp. The app writes through a side file (a write-ahead log), and a plain copy taken while it's running can miss recent entries:

sqlite3 biotracking.db ".backup biotracking_backup_$(date +%Y%m%d).db"

To start over from nothing, stop the app, back up first, then:

rm biotracking.db config.json
python setup.py

That deletes all your data.


A Note on What This Is (and Isn't)

This app is not a medical device and does not give medical advice. It is a record-keeping and pattern-visualization tool.

What it does well:

  • Helps you notice your own patterns
  • Gives you something concrete to bring to appointments
  • Creates a longitudinal record that would otherwise exist only as fragmented memories

What it can't do:

  • Diagnose anything
  • Replace your doctors
  • Predict the future with certainty

Use it as evidence for conversations with your care team, not as a substitute for those conversations.


Tips for Getting Started

Log daily, even briefly. Consistency matters more than completeness. A 30-second entry every day is worth more than a detailed entry once a week.

Mark your flare days. This is the most important thing for the prediction model. If you're having a bad day, check the flare box.

Don't stress about missing days. Life happens. A gap in the data is fine — the model works around it.

Use the notes fields. You don't have to write much, but "started new medication today" or "out in the sun for two hours" adds context that pure numbers can't capture.

Come back to the Model page. After a month or two of data, the Model dashboard (/model) starts showing you things. UV spikes followed by symptom spikes a day later. Sleep drops before flares. RMSSD wobbling before a bad week. Patterns you couldn't see in the day-to-day. Click any of the expandable trend charts to pop it out to full size.

Check the Interventions page after a medication change. Give it a few weeks of data after starting or stopping a medication, then look at the intervention card. If your flare rate drops 40% post-start, that's information worth taking to your rheumatologist. If it doesn't budge, that's also information.


Built by C. Alaric Moore. Data stays on whatever server you're signed into — ask your host if you don't know.

If something isn't working or you want a feature added, just ask.

← all documentation