mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
Merge remote-tracking branch 'origin/main' into fix/private-notification-origins
# Conflicts: # src/app/api/settings/ntfy/route.ts # src/app/api/settings/webhook/route.ts
This commit is contained in:
commit
71175290af
255 files changed
+12654
-1141
No files matched your search
+1273
-393
File diff suppressed because it is too large.
Load diff
@@ -215,6 +215,42 @@ A run where SOME account got a copy still succeeds, with the rest counted in
|
||||
`offhost_backup_failed`. Failing the whole queue over one account's object
|
||||
would re-upload everybody's on every retry.
|
||||
|
||||
### Which account has no copy
|
||||
|
||||
The counts above say how many accounts were uploaded, never which. An account
|
||||
whose object has failed every night for a month reads as `99 uploaded, 1
|
||||
failed` each time, which looks like weather.
|
||||
|
||||
**Admin console → Backups → Off-host copies** answers it per account: when this
|
||||
host last put that account's encrypted copy in the bucket, how big it was, and
|
||||
how that compares with the nightly schedule. **Fresh** is inside one run,
|
||||
**due** means one run produced nothing for that account, **stale** means two
|
||||
did not, **never** means a run walked the account and no object has ever
|
||||
landed for it, and **no record yet** means no run has recorded the account at
|
||||
all. On a host without `BACKUP_S3_*` the card says off-host backup is not
|
||||
configured and stops there.
|
||||
|
||||
The ledger starts empty on the release that adds it, so on a host that has
|
||||
been uploading nightly for months every account reads **no record yet** until
|
||||
the first run after the upgrade. That is the ledger having no history, not the
|
||||
bucket being empty — which is why it is a separate word from **never**, and
|
||||
why the row says so rather than claiming nothing has ever reached the bucket.
|
||||
|
||||
Each verdict carries six hours of slack on top of the schedule's own 24, and
|
||||
that slack is load-bearing on two nights. The cron runs at 02:30 Europe/Berlin,
|
||||
so on the DST fall-back Sunday two consecutive runs are 25 hours apart; and the
|
||||
timestamp is when _that account's_ object landed, so on a cohort walked one
|
||||
account at a time an account reached later tonight than last night drifts by
|
||||
hours. Without the slack a healthy host would paint its whole cohort **due**
|
||||
once a year.
|
||||
|
||||
The verdicts come from a ledger the worker writes when the object lands
|
||||
(`offhost_backup_state`), not from a listing of the bucket. Keep the worker's
|
||||
grant as documented — PutObject, GetObject, AbortMultipartUpload — and the card
|
||||
still tells the truth. A row that goes stale while the run reports success is
|
||||
that account's object being refused, and the reason is in the run's
|
||||
`offhost_backup_failures` meta.
|
||||
|
||||
## The weekly in-database backup (`data-backup`)
|
||||
|
||||
Separate from the off-host job above, and easy to confuse with it. A second
|
||||
|
||||
@@ -143,6 +143,27 @@ itself still works: it walks the journey against an in-process instance
|
||||
once per leg with that leg broken, and fails if any of them comes back
|
||||
green.
|
||||
|
||||
## Error reporting
|
||||
|
||||
HealthLog ships no default error-reporting target and never will: an
|
||||
operator's crash reports must not leave their host because nobody told them
|
||||
not to. Reporting starts only when someone types a DSN into **Admin →
|
||||
Integrations → GlitchTip**, and that card states plainly whether reports are
|
||||
going anywhere and which host they go to.
|
||||
|
||||
For a maintainer-operated instance, set the DSN there after the first deploy —
|
||||
it lives in the database, not in the environment, so it survives an image
|
||||
upgrade and does not need a compose change.
|
||||
|
||||
The badge on that card tracks delivery, not intent. A DSN that is set and
|
||||
parses is not the same fact as reports arriving: a wrong public key, a wrong
|
||||
project id and a host that stopped answering all parse. So the card is green
|
||||
only while a report has actually left this host inside the last fortnight,
|
||||
amber while a target is set and nothing has come back from it, and it names the
|
||||
reason underneath when the last attempt was refused. Press **Test** to prove
|
||||
the target on demand — that send is recorded like any other, so the badge
|
||||
answers immediately.
|
||||
|
||||
## Verify the image signature
|
||||
|
||||
Every release image (the multi-arch manifest **index**, covering both
|
||||
|
||||
@@ -49,16 +49,16 @@ Either the entire record, which is what sharing has always meant and what
|
||||
every grant written before v1.37.0 carries, or a set of named sections
|
||||
chosen when the invitation is written:
|
||||
|
||||
| Section | What it covers |
|
||||
| ----------------- | -------------------------------------------------------------------------- |
|
||||
| Readings | Weight, blood pressure, pulse, glucose, sleep, workouts and custom metrics |
|
||||
| Medications | The medication list, its schedules, doses taken, side effects and stock |
|
||||
| Lab results | Lab results and the analytes behind them |
|
||||
| Health background | Allergies, family history and the facts in the health profile |
|
||||
| Illness | Illness episodes and the day logs kept under them |
|
||||
| Mood and mind | Mood entries and the mental-health questionnaire history |
|
||||
| Cycle | Cycle tracking |
|
||||
| Documents | Everything in the document vault |
|
||||
| Section | What it covers |
|
||||
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Readings | Weight, blood pressure, pulse, glucose, sleep, workouts and custom metrics |
|
||||
| Medications | The medication list, its schedules, doses taken, side effects and stock |
|
||||
| Lab results | Lab results and the analytes behind them |
|
||||
| Health background | Allergies, family history, the facts in the health profile, visits and the practices behind them, and the vaccination record |
|
||||
| Illness | Illness episodes and the day logs kept under them |
|
||||
| Mood and mind | Mood entries and the mental-health questionnaire history |
|
||||
| Cycle | Cycle tracking |
|
||||
| Documents | Everything in the document vault |
|
||||
|
||||
A section that was not shared is refused exactly the way a record nobody
|
||||
shared is, so the shape of what was held back is not readable from
|
||||
@@ -68,8 +68,12 @@ shared. The invitation screen says this in as many words, because a note
|
||||
or a document name inside one section can be about any other.
|
||||
|
||||
Aggregate surfaces (the dashboard overview, the health score, the daily
|
||||
digest) appear only on a whole-record grant. A figure derived from part of
|
||||
a record reads as a figure about the person, and it would not be one.
|
||||
digest, every insight) read across sections, so a grant naming sections
|
||||
never opens one. A figure derived from part of a record reads as a figure
|
||||
about the person, and it would not be one. Whole-record grants do not all
|
||||
open them equally either: the daily digest and the dashboard's snapshot
|
||||
answer at read, while the health score, the dashboard's summary and the
|
||||
insights need manage.
|
||||
|
||||
A stored scope never grows. A section added by a later release is not in
|
||||
a set somebody ticked before it existed.
|
||||
@@ -80,11 +84,24 @@ Three levels, fixed when the invitation is written. Raising one means
|
||||
revoking and inviting again, because widening a grant in place would carry
|
||||
a consent the other person never gave.
|
||||
|
||||
| Level | Adds |
|
||||
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Read | Read what the grant opens, and change nothing. |
|
||||
| Write | Add entries: a reading, a lab result, an analyte, an illness entry, a side effect, a medication, and marking a dose taken or skipped. Editing, deleting and restoring stay with you. |
|
||||
| Manage | Change and remove entries, including ones the owner wrote; record the health background; read the insights generated from the record. Always the entire record. |
|
||||
| Level | Adds |
|
||||
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Read | Read what the grant opens, and change nothing. |
|
||||
| Write | Add entries: a reading, a lab result, an analyte, an illness entry, a side effect, a medication, marking a dose taken or skipped, a visit, a practice, and a vaccination dose. Editing, deleting and restoring stay with you. |
|
||||
| Manage | Change and remove entries, including ones the owner wrote; record the health background; read the insights generated from the record. Always the entire record. |
|
||||
|
||||
Some entries are creates only **manage** reaches, and the split runs by
|
||||
entry rather than by section: an allergy, a family-history entry, a mood
|
||||
entry, a screener, a cycle day, a water entry and a preventive-care
|
||||
reminder are all created at manage, in sections a write grant can
|
||||
otherwise add to. Nothing in the document vault is writable at any level.
|
||||
|
||||
One entry needs two sections rather than a higher level. Planning the
|
||||
booster a vaccination suggests files it as a preventive-care reminder,
|
||||
which lives under Readings, so a grant that opens Health background alone
|
||||
logs the dose and is refused the booster. Logging the dose is unaffected
|
||||
either way, and the app does not offer the booster to a grant that cannot
|
||||
plan it.
|
||||
|
||||
Offering **manage** asks the owner for a fresh second factor if they have
|
||||
one enrolled, which makes it a browser-only act either way: a Bearer
|
||||
@@ -106,12 +123,19 @@ whole feature is built on, and it is drawn on the server:
|
||||
| API tokens, the MCP connector | A durable credential that outlives the access |
|
||||
| Connected services (Withings, Fitbit, WHOOP, Google Health, Polar, Oura, Strava, Nightscout) | Re-pointing a sync, or feeding somebody else's wearable into this record |
|
||||
| Notification channels and push devices | The owner's health alerts redirected to another phone or chat |
|
||||
| Exports, the health-record archive, clinician share links | A one-click copy of everything, or a new door that outlives the access |
|
||||
| Full exports, the encrypted archive, clinician share links | A one-click copy of everything, or a new door that outlives the access |
|
||||
| Asking an AI provider anything | AI processing of the owner's data, under the owner's own consent |
|
||||
| Modules, thresholds, units, the record's language and timezone | Configuration of the account, not content of the record |
|
||||
| Sharing itself | Inviting somebody else, widening the access, or passing it on |
|
||||
| Locale, theme, dashboard layout | Presentation belongs to the person, not to the record |
|
||||
|
||||
One export is the exception, and it is deliberate: the doctor report
|
||||
(`POST /api/export/health-record`) answers at manage on a whole-record
|
||||
grant. It renders a prepared artefact from a selection the caller declares
|
||||
rather than handing the record over whole, which is the difference the row
|
||||
above is drawing. The full exports and the passphrase-encrypted archive
|
||||
stay with the owner.
|
||||
|
||||
This table is about a grant between two accounts. A managed profile is
|
||||
the one place where some of it is deliberately different, because a record
|
||||
with no login has nobody else to keep its own configuration: see "Records
|
||||
|
||||
Reference in new issue
Block a user