mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
docs: describe the cycle calendar subscription
A how-to for subscribing from Google, Apple and Outlook and what the feed shows; a section on the reproductive-health page saying that once it is on, the dates also sit with the calendar provider and turning it off does not reach those copies; a reverse-proxy note with Caddy and Nginx examples for keeping path credentials out of access logs; and API_TOKEN_HMAC_KEY rotation named as ending every feed link.
This commit is contained in:
1 parent
d9a31524a2
commit
e8ef590af7
5 files changed
+126
-7
No files matched your search
@@ -45,6 +45,8 @@ notes, and machine-readable specs that ship alongside the source:
|
||||
code. [`using/search.md`](./using/search.md) covers the command palette's
|
||||
record search: opening it, what it finds, how matching works, and its
|
||||
limits on very large and shared records.
|
||||
[`using/cycle-calendar-feed.md`](./using/cycle-calendar-feed.md) covers
|
||||
subscribing to the cycle forecast from Google, Apple or Outlook.
|
||||
- [`migration/`](./migration/) — migration notes. Currently only
|
||||
v1.3 to v1.4; later releases are covered by `CHANGELOG.md` and
|
||||
<https://docs.healthlog.dev>.
|
||||
|
||||
@@ -72,11 +72,11 @@ echo "ENCRYPTION_KEY=$(openssl rand -hex 32)" >> .env
|
||||
echo "API_TOKEN_HMAC_KEY=$(openssl rand -hex 32)" >> .env
|
||||
```
|
||||
|
||||
| Variable | Purpose | Rotation cost |
|
||||
| -------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| `POSTGRES_PASSWORD` | Bundled Postgres password — used by both the `db` service and `DATABASE_URL` | Database restart + connection-string update |
|
||||
| `ENCRYPTION_KEY` | AES-256-GCM key for at-rest secrets (Withings tokens, AI provider keys, VAPID secrets) | Coordinated; see `docs/ops/encryption-key-rotation.md` |
|
||||
| `API_TOKEN_HMAC_KEY` | HMAC-SHA256 key for hashing Bearer API tokens and web session cookies before storage | Invalidates every issued `hlk_*` token and signs out every browser session |
|
||||
| Variable | Purpose | Rotation cost |
|
||||
| -------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `POSTGRES_PASSWORD` | Bundled Postgres password — used by both the `db` service and `DATABASE_URL` | Database restart + connection-string update |
|
||||
| `ENCRYPTION_KEY` | AES-256-GCM key for at-rest secrets (Withings tokens, AI provider keys, VAPID secrets) | Coordinated; see `docs/ops/encryption-key-rotation.md` |
|
||||
| `API_TOKEN_HMAC_KEY` | HMAC-SHA256 key for hashing Bearer API tokens and web session cookies before storage | Invalidates every issued `hlk_*` token, share link and cycle calendar-feed link, and signs out every browser session |
|
||||
|
||||
You do **not** edit `DATABASE_URL` on the bundled `docker-compose.yml`
|
||||
stack: compose constructs the connection string inside the `app`
|
||||
@@ -289,8 +289,8 @@ this server needs:
|
||||
retired id for as long as a stored or off-host backup copy still needs it;
|
||||
- `BACKUP_ENCRYPTION_KEY` and any `BACKUP_ENCRYPTION_PREVIOUS_KEYS`, when
|
||||
off-host copies are set up;
|
||||
- `API_TOKEN_HMAC_KEY`, without which every session, token and share link ends
|
||||
(no data is lost).
|
||||
- `API_TOKEN_HMAC_KEY`, without which every session, token, share link and
|
||||
calendar-feed link ends (no data is lost).
|
||||
|
||||
Each key is shown by id and a fingerprint (the first 12 hex characters of
|
||||
SHA-256 over the key), never the key itself, and **Check my copy** compares a
|
||||
|
||||
@@ -121,6 +121,27 @@ the grace window to 0 or wait it out, remove the old volume, and let the
|
||||
dumps age out or delete them
|
||||
([backup-restore.md](../ops/backup-restore.md)).
|
||||
|
||||
## Calendar subscription
|
||||
|
||||
The cycle settings can publish your forecast as a calendar feed that Google
|
||||
Calendar, Apple Calendar or Outlook subscribes to. It is off by default, and
|
||||
turning it on changes the arrangement this page describes: from then on the
|
||||
feed's dates also sit with that calendar provider, on its servers, under its
|
||||
policies. That is exactly the cloud vendor self-hosting otherwise keeps out of
|
||||
the picture.
|
||||
|
||||
What the feed carries is deliberately small. Each event is an all-day date
|
||||
range and a title, nothing else: no symptoms, notes, flow, temperatures, test
|
||||
results, contraception or intercourse fields. By default the title is a dot
|
||||
and the calendar is named "HealthLog", so a glance at a shared screen says
|
||||
nothing; past cycles are left out unless you ask for them, and the fertile
|
||||
window is only offered when your goal is trying to conceive.
|
||||
|
||||
Turning the subscription off, or rotating its link, stops the provider's next
|
||||
poll. It does not reach the copies the provider already fetched: remove the
|
||||
subscribed calendar there as well. The link itself is a credential; see
|
||||
[keeping it out of access logs](./reverse-proxy.md#credentials-in-the-url-path-keep-them-out-of-access-logs).
|
||||
|
||||
## Practical hardening
|
||||
|
||||
If reproductive-health privacy is the reason you self-host, the
|
||||
|
||||
@@ -336,6 +336,49 @@ A `TRUST_PROXY_HOPS` value that is not a whole number resolves no client
|
||||
address at all (requests keep working) and is named in the `[proxy-trust]`
|
||||
boot line.
|
||||
|
||||
## Credentials in the URL path: keep them out of access logs
|
||||
|
||||
A few HealthLog URLs carry their credential in the path, because the caller
|
||||
cannot send a header: the cycle calendar feed (`/api/cycle/feed/hlf_…`,
|
||||
polled by Google, Apple or Outlook), the clinician share view (`/c/hls_…`)
|
||||
and the provider webhooks. HealthLog scrubs these segments from its own logs
|
||||
and error reports. It cannot reach your proxy's access log, which records the
|
||||
full path of every request by default, so anyone who can read that log can
|
||||
read a live feed link.
|
||||
|
||||
Either drop those requests from the access log or rewrite the path before it
|
||||
is written. For Caddy, skip logging the token-bearing paths:
|
||||
|
||||
```caddyfile
|
||||
your-instance.example.com {
|
||||
@credential_paths path /api/cycle/feed/* /c/*
|
||||
log_skip @credential_paths
|
||||
reverse_proxy app:3000
|
||||
}
|
||||
```
|
||||
|
||||
For Nginx, log a redacted path instead of `$request`:
|
||||
|
||||
```nginx
|
||||
map $request_uri $redacted_uri {
|
||||
~^/api/cycle/feed/ /api/cycle/feed/[REDACTED];
|
||||
~^/c/ /c/[REDACTED];
|
||||
default $request_uri;
|
||||
}
|
||||
log_format redacted '$remote_addr - [$time_local] "$request_method $redacted_uri" '
|
||||
'$status $body_bytes_sent "$http_user_agent"';
|
||||
access_log /var/log/nginx/access.log redacted;
|
||||
```
|
||||
|
||||
Traefik's access log has no per-path filter; turn `accessLog` off, or keep it
|
||||
on a host only you can read. If a feed link has been in a log you do not
|
||||
control, rotate it in the app (Cycle, Settings, Calendar subscription, Rotate
|
||||
link): the old one stops working on the provider's next poll.
|
||||
|
||||
The feed answers with `Cache-Control: private, no-cache` and an `ETag`, so a
|
||||
caching proxy in front must pass `If-None-Match` through rather than cache the
|
||||
body for everyone.
|
||||
|
||||
## Running over plain HTTP on a LAN (no reverse proxy)
|
||||
|
||||
The session cookie carries the `Secure` flag by default under
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Subscribing to your cycle forecast
|
||||
|
||||
HealthLog can publish your cycle forecast as a calendar subscription, so the
|
||||
next few periods show up in the calendar app you already use. The feed is
|
||||
computed fresh every time your calendar app asks for it; HealthLog stays the
|
||||
only place the data is kept.
|
||||
|
||||
## Turning it on
|
||||
|
||||
Open **Cycle**, then **Settings**, and find **Calendar subscription**. Before
|
||||
the button, the card says what it means: your calendar provider stores the
|
||||
dates on its servers, and turning the subscription off later does not remove
|
||||
copies it already holds. HealthLog asks you to confirm it is you (a password or
|
||||
passkey) and then shows the link once. Copy it, or use **Open in calendar app**.
|
||||
|
||||
Only the record's owner, or someone who manages it with a MANAGE grant that
|
||||
covers cycle data, can turn it on. It is managed on the web; the iOS app shows
|
||||
where to find it.
|
||||
|
||||
## Adding it to your calendar
|
||||
|
||||
- **Google Calendar:** Other calendars, From URL, paste the link. Google
|
||||
refreshes subscriptions on its own schedule, roughly every 12 to 24 hours.
|
||||
- **Apple Calendar:** on a Mac, File, New Calendar Subscription. On iPhone,
|
||||
Settings, Calendar, Accounts, Add Account, Other, Add Subscribed Calendar.
|
||||
Apple lets you choose how often it refreshes.
|
||||
- **Outlook:** Add calendar, Subscribe from web, paste the link. Outlook
|
||||
refreshes every few hours.
|
||||
|
||||
## What the feed shows
|
||||
|
||||
- The next period and, once three cycles are logged, a few after it (three
|
||||
by default, up to six). Each event covers the whole window the period may
|
||||
fall in, and the window widens the further out it is. The list stops itself
|
||||
where a date would be more guess than estimate (beyond about ±10 days, or
|
||||
half a cycle).
|
||||
- Optionally, your past cycles (up to twelve).
|
||||
- Optionally, when your goal is trying to conceive, the estimated fertile
|
||||
window of the next cycle. It is an estimate, not a contraceptive method.
|
||||
|
||||
Titles are discreet by default: each event is a dot and the calendar is called
|
||||
"HealthLog". Descriptive titles name the period instead. Events never carry
|
||||
symptoms, notes or any other detail of your log.
|
||||
|
||||
When a period is later than its window, the feed shows no forecast until you
|
||||
log it, instead of guessing a new date. With predictions switched off, only
|
||||
past cycles appear (if you chose to include them).
|
||||
|
||||
## Rotating and turning off
|
||||
|
||||
**Rotate link** replaces the link; calendars using the old one stop updating.
|
||||
**Turn off** ends the link. In both cases, remove the old subscribed calendar
|
||||
in your calendar app too: what it already fetched stays there until you do.
|
||||
Reference in new issue
Block a user