Skip to content

About

A hormone replacement therapy tracker. Algorithms by Laozhong(小中)

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

124 stars

Watchers

1 watching

Forks

 
 

Latest commit

 

History

888 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Oyama's HRT Tracker on a phone: the current estimated estradiol level, today's dose and the hormone level timeline, next to a month of estradiol levels

English     简体中文

Log your HRT doses and see the hormone levels they produce. Private, open and free.

Open in your browser     macOS and Windows     Self-host with Docker     Buy us a coffee

Table of Contents

Introduction

Oyama's HRT Tracker is a privacy-focused tool for logging hormone replacement therapy and simulating the hormone levels your doses produce. It has a feminizing mode (estradiol, plus cyproterone acetate) and a masculinizing mode (testosterone), and it can log other adjunct drugs too.

Runs in the browser, as an installable app, on macOS and Windows, or on your own server.

Six screens of the app: overview, hormone level timeline, what-if, dose history, lab results and logging a dose

From left to right: the overview with the current estimate, a month of estradiol levels, the what-if planner, the dose history, lab results next to the model, and logging a new dose. The screenshots use sample records; to make them again, see design/readme.

Warning

Not medical advice. This app shows estimates from a pharmacokinetic model. They can differ a lot from your real hormone levels and are no substitute for blood tests or a clinician. Do not start, stop or change any medication based on this app alone.

Support the project

The tracker is free and stays free: no subscriptions, no paid features, no ads, and donations never unlock or add anything. If you find it useful and want to help, you can buy us a coffee. Donations are optional and never required.

Why we ask: running the public instance, the domain, and the AI calls used during development all cost real money. If funding stays short for long, development slows down.

  • Monero: 86fnX9pg9VqD5SjJNDjbxKemf5wEvHz7mDojKztJyov8PVKCGVeC7QKijLQrb9QT7KabaXhXFn3HoPbDvWAk6dYrUBgB9p5
  • Bitcoin: bc1qdgfj0rm0tguna4nk36lxmeq45h59hscusrthk2

See DONATE.md for how donations are split (40% project, 30% @fantuanmtf, 30% @SmirnovaOyama).

Features

Why it is different

  • Private by default: your records stay in your browser unless you turn on cloud backup or make a share link, and both are end-to-end encrypted.
  • Open source: every line is public under the MIT License, so anyone can check how the estimates are made.
  • Free: no subscriptions, no paid features, no ads. Donations are optional and never unlock anything; see DONATE.md.
  • Speaks your language: Simplified Chinese, Traditional Chinese, Cantonese, English, Japanese, Korean, Turkish, Russian and Ukrainian.

Hormone levels

  • See the estimated level over time: estradiol in pg/mL, cyproterone acetate in ng/mL, total testosterone in ng/dL
  • Estradiol by injection (benzoate, valerate, cypionate, enanthate, and undecylate in beta), oral, sublingual, gel and patch
  • Testosterone by injection (cypionate, enanthate, undecanoate) and gel
  • For sublingual doses, pick how long you hold the tablet (2 to 15 minutes) or enter your own time

Doses and plans

  • Write down what you take and how often; the overview shows what is due today and logs it with one tap
  • Compare carrying on as now with a planned change in the what-if view
  • Log spironolactone, bicalutamide, medroxyprogesterone acetate, raloxifene, leuprorelin and triptorelin as well; they have no level model, so they draw no curve

Lab results

  • Log blood test results and see them on the curve next to the estimate
  • Calibrate the estradiol model to you with EKF, OU-Kalman, MIPD or Full Bayes, or edit its parameters by hand

Supplies

  • Keep track of how much of each medicine you have
  • Logged doses are subtracted automatically, and the overview warns you before you run out

Backup and sharing

  • Back up to the cloud with end-to-end encryption, or export a file
  • Share a read-only link to your dose history and curve; the key lives only in the link, and lab results, weight and account details are never included
  • Read the privacy notice for every detail

Where the model comes from

The estradiol pharmacokinetic model is based on PKcore.swift and PKparameter.swift by @LaoZhong-Mihari, from the HRT-Recorder-PKcomponent-Test repository. Its three-compartment structure, two-part injection depot kinetics and sublingual absorption tiers come from there.

This project has since adjusted and extended the model: it adds testosterone (injection esters, gel and patch) for the masculinizing mode, cyproterone acetate on its own curve, and estradiol undecylate injections; it revises the gel and sublingual handling; and it can calibrate the estradiol curve against your lab results. See Algorithm Explanation.md for the details.

Command-line tools

The offline tools live in the separate Oyama-s-HRT-Tracker-Tools repository. hrt-tool is a pre-release command-line client for the same data:

  • status shows the current estimate, the projected curve and recent blood tests;
  • add dose records a dose interactively;
  • proxy socks5://… connects through Tor or I2P, with DNS resolved by the proxy;
  • the vault, sessions and named connections are encrypted at rest.

The CLI is experimental; the website does not depend on it and never handles OpenPGP keys or passphrases. Build and release downloads are in the Tools repository.

Self-hosting

You are very welcome to run this app on your own website or server, and you do not need to ask first. The simplest way is Docker; to use your own Cloudflare account instead, see Cloudflare.

Docker

The Docker image runs the complete app locally, including the Worker API, D1 database, and R2-compatible avatar storage. Data is kept in the project's ./data directory and survives container restarts. The image is built from source on your machine, so you need Git and Docker with Compose.

The fastest path is the two helper scripts in the repository root. They do not install Docker itself.

git clone https://github.com/xunxunProjects/Oyama-s-HRT-Tracker.git
cd Oyama-s-HRT-Tracker
./Dependency-Detection.sh --docker   # read-only preflight
./docker-install.sh                  # asks: loopback, public domain, or LAN

Dependency-Detection.sh --docker checks the app version and release tag, Docker, Compose, the daemon, .env, ./data, and the port; the Docker path does not need Node.js on the host. An app version older than 1.5.2 fails the check, and a checkout that is not a release tag gets a warning. docker-install.sh creates .env with a generated JWT_SECRET (mode 600), prepares ./data for uid 1000, starts the Compose stack, and waits for /api/transparency. It never prints the secret. For a public domain it keeps the app on loopback and prints a Caddy, nginx, Cloudflare, or Tailscale configuration to finish; it does not edit your proxy. Use --dry-run to see the plan, --yes --mode loopback for an unattended loopback install, or --mode lan --port 18787 for a trusted LAN without TLS.

If you prefer the manual path:

cp .env.docker.example .env
# Replace JWT_SECRET in .env with the output of:
openssl rand -base64 48
mkdir -p ./data
sudo chown 1000:1000 ./data
docker compose up -d --build

Then open http://localhost:8787. The port is published on 127.0.0.1 only, so the app is not reachable from the LAN by default. Keep it that way unless a reverse proxy sits in front, or deliberately set HRT_BIND_HOST=0.0.0.0 in .env (or use ./docker-install.sh --mode lan). LAN mode has no TLS; never expose it directly to the internet.

The first build takes a few minutes. To update later, pull the new code and rebuild; your data in ./data is kept:

git pull
docker compose up -d --build

Plain http and https

To open the app over plain http from another device (for example http://192.168.1.5:8787), set HRT_BIND_HOST=0.0.0.0 in .env or run ./docker-install.sh --mode lan --port 18787, or put the container behind a reverse proxy first. Backups, exports and share links stay end-to-end encrypted over plain http too. Browsers do hold back a few features until the page is served over https (or opened as localhost): installing it as an app, offline use, and passkey sign-in. To get those, put the container behind any https reverse proxy, such as Caddy (caddy reverse-proxy --from your.domain --to localhost:8787) or Tailscale Serve.

Caddy

Database schema

The schema is defined only by the files in migrations/. The container applies them on every start, and a volume created by an older image is reconciled first (docker/reconcile.mjs), so upgrading needs nothing from you.

For a Cloudflare deployment, run the same two steps once before deploying a build that includes migration 0000:

npm run wrangler:migrate:reconcile
npm run wrangler:migrate:remote

Web and Docker builds use same-origin /api requests by default. Desktop and other custom-protocol builds should set VITE_API_ORIGIN to their Worker/API origin at build time; the release workflow supplies the hosted origin.

To stop the app without deleting its data:

docker compose down

Without Compose, build and run the image with plain Docker:

docker build -t hrt-tracker:local .
docker run -d --name hrt-tracker \
  -p 127.0.0.1:8787:8787 \
  --env-file .env \
  -v "$(pwd)/data:/data" \
  hrt-tracker:local

The mapping above is loopback-only. Use -p 8787:8787 only if you intend to expose the container to the LAN, and never expose it directly to the internet: put a TLS-terminating reverse proxy (or a Tor/I2P daemon) in front instead.

ADMIN_USERNAME and ADMIN_PASSWORD are optional. When used, set both. The container uses Wrangler's local workerd runtime and local persistent D1/R2 resources; it does not connect to the Cloudflare production database or bucket.

TRUSTED_IP_SOURCE is optional too. The default is none, because a client can set either forwarding header itself. Set cf only when Cloudflare is actually in front and its edge overwrites CF-Connecting-IP; set x-forwarded-for only when exactly one trusted reverse proxy in front appends the client address. With none, each endpoint gets one shared limit instead of one per address. docker-install.sh sets these values for its modes; if you configure a proxy by hand, make it overwrite the matching header rather than passing the client's value through.

PUBLIC_APP_ORIGIN (for example https://hrt.example.com) is optional. Share links use it, and passkey sign-in accepts it when the proxy does not pass the browser's Host header through.

The workflow in .github/workflows/docker-publish.yml validates pull requests and publishes linux/amd64 and linux/arm64 images to GitHub Container Registry for main, version tags, and manual runs. That package is private for now, so pulling it without access fails; build from source as shown above.

Cloudflare

wrangler.toml points at the maintainer's own database and bucket. Those IDs only work inside the maintainer's Cloudflare account, so replace them with resources you create in yours. Everything below runs from your clone of the repository.

  1. Sign in

    npm install
    npx wrangler login
  2. Create the database

    npx wrangler d1 create hrt-tracker-prod

    Copy the database_id it prints into the [[d1_databases]] block of wrangler.toml, replacing the one there. Keep the name hrt-tracker-prod: the npm scripts below use it.

  3. Create the avatar bucket

    npx wrangler r2 bucket create hrt-tracker-avatars

    Set bucket_name in the [[r2_buckets]] block to hrt-tracker-avatars (or whatever name you chose). R2 has to be enabled once in the Cloudflare dashboard before the first bucket can be created.

  4. Remove the preview blocks

    Delete every [[previews.*]] block at the end of wrangler.toml. They point at the maintainer's preview database and are only used by pull request previews in Workers Builds. If you want previews too, create a second database and bucket and put their names and ID there instead.

  5. Create the tables

    npm run wrangler:migrate:remote

    Run this again after pulling updates that add files to migrations/.

  6. Deploy

    npm run build
    npx wrangler deploy

    Wrangler prints a workers.dev address. To use your own domain, add it under the Worker's Settings, Domains & Routes in the Cloudflare dashboard. If share links should use a different address than the one people open, set the variable PUBLIC_APP_ORIGIN (for example https://hrt.example.com).

  7. Set the secrets

    Sign-in needs JWT_SECRET, so set it right after the first deploy.

    openssl rand -base64 48 | npx wrangler secret put JWT_SECRET
    # Optional built-in admin account, set both or neither
    npx wrangler secret put ADMIN_USERNAME
    npx wrangler secret put ADMIN_PASSWORD

To deploy on every push instead, connect your fork in the Worker's Settings, Builds, with build command npm run build and deploy command npx wrangler deploy, after committing the wrangler.toml changes above to your fork.

Attribution

If you deploy this app publicly, please:

  1. Keep the original algorithm credits: Visibly link back to the HRT-Recorder-PKcomponent-Test repository.

  2. Respect the license: Ensure you follow any licensing terms associated with the original algorithm code.

Contributing

Contributions are welcome, and you do not need to be a programmer to help. See CONTRIBUTING.md for running the app locally, a map of the code, adding a translation and making a release, and please follow the Code of Conduct. To report a security problem, see SECURITY.md.

For AI contributors

If you are an AI agent working in this repository, read docs/AI-GUIDE.md before changing code. It maps the repository structure and modules, the data and trust boundaries, the checks CI runs, and the rules for changing the model, database, API, and deployment scripts.

The guide is in English so different agents and tools can share one canonical reference. This README and CONTRIBUTING.md stay bilingual for people.

Maintainers

Maintainers
@SmirnovaOyama
@fantuanmtf

Original model
@LaoZhong-Mihari

Contributors

Contributors

Found a bug or have an idea? Open an issue.

Wishing steady progress and good health to everyone who uses this tool.

Legal

Oyama's HRT Tracker is for information only and is not medical advice. Its estimates come from a model and can be far from your real levels. Use it at your own risk, and talk to a clinician about any change to your treatment.

This project is released under the MIT License; see LICENSE. How your data is handled is described in the privacy notice.

About

A hormone replacement therapy tracker. Algorithms by Laozhong(小中)

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

124 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages