fix(train): no row's ID tells the instance a date: the days' IDs are keyed by the account (ADR 0030, D57)

A day's, night's and heart chunk's ID was a UUID of version 5 of the
person and the date, which the instance can hash for every date itself:
each ID told it which days the band was worn. halkyon_keys' keyedRowId
hashes the name under the account's X25519 secret key (BLAKE2b, a UUID of
version 8), the same on every device of the account and unreadable
without its keys; ContentCipher.rowId and TrainKeys.rowId hand it on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-30 16:12:00 +01:00
commit 839d49c4cc
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -676,8 +676,8 @@ Strava. What was found, 27 Oct:
half hour, and when the person pulls the list; a later read replaces what it brought
of a date (its summary, Recharge, skin temperature, heart rate, night) and keeps the
rest, and rewrites nothing unchanged; a day's heart rate goes in chunks by the three
hours each measurement began in, every row's ID made from the person and the date
(`https://halkyon.app/train/day/<person>/<date>`, `…/night/…`, `…/heart/<index>`); a
hours each measurement began in, every row's ID made from the date under the
account's key (D57: `train/day/<date>`, `train/night/<date>`, `…/heart/<index>`); a
row that doesn't open on a device is left out of *Days*, as its date is sealed with
it; what is kept goes up to the day before the latest read's (`keptUntil`, in
`loop.json` with the last read's time), kept over a new setup of the same band and
@ -1424,6 +1424,23 @@ Strava. What was found, 27 Oct:
learnt from the 2105 mm a sensor starts with. Its quotient may fall between 1 and 3 m,
as one typed may (`measuredWheelMm`, `_gpsCovered`). A tunnel's straight line still
measures nothing. The distance still follows `usesWheel`, against the wheel as learnt.
- **D57 Row IDs that tell the instance no date** (recommended, the developer's; 30 Oct;
revises D37's IDs): a `day`, `night` and `day_series` row's ID was a UUID of version
5 of `https://halkyon.app/train/day/<person>/<date>`, so that every phone named a
day alike offline; but the instance knows the person and can hash every date, so
each ID told it which days the person wore the band, their sealing aside. The ID is
now the account's keyed hash of the name (halkyon_keys' `keyedRowId`: BLAKE2b keyed
with the account's X25519 secret key over a label and `train/day/<date>`, a UUID of
version 8; `ContentCipher.rowId`, `TrainKeys.rowId`), the same on every device of
the account, which all hold its keys, and nothing of the date read from it without
them. Over a key of Train's own (its data keys multiply offline, D15, so two
phones would name one day two ways until they met), random IDs with a sealed index
from date to ID (two phones offline each making a day's row, merged later: more
code for the same), and coarser rows (a week or month each, rewriting more with
every day). What the instance still sees: how many rows there are, their sizes and
when they are written; a reset of the account's keys (ADR 0017) renames every row,
as it leaves what was sealed unreadable anyway. Train had no release, so no row
needed moving.
## Consequences