- home schema: home.links (seeded, owner-editable) and home.snapshots (written by a host collector)
- deploy/home-collector: stdlib Python + systemd user timer (60s) that snapshots /proc stats,
docker containers, systemd timers, service HTTP checks and ntfy topics via a limited role
- extras/newtab-extension: MV3 new-tab override, zipped to static/newtab-extension.zip
- owner-only pages skip Google Analytics; /trackers/health#body anchor for weigh-ins
The nightly scraper and worker API now take over an existing row (e.g. source=manual)
whose link is the same listing (normalized URL, or the Pararius key / Funda, Kamernet,
Vesteda id in it) instead of inserting a copy; typed values are kept where the scrape has
none. house-listing update/add give manual rows with such links the site key unless
another row has it, update accepts --source/--source-id, and lookups by link ignore
tracking params. scripts/house-rekey.ts backfills existing manual rows.
Lists contracts/pension docs with written summaries, an inline viewer and
download at /trackers/money/documents. Files and summaries live on the host
(MONEY_DOCUMENTS_ROOT, bind-mounted read-only by deploy-remote.sh), never in
git or static/; the file endpoint is admin-only (401/403) and private, no-store.
- scripts/house-scrape.ts: polite nightly scrape (honest UA, 6-10 s apart, stops a source on 403/challenge), ingest + price history + removals, ntfy alerts on a separate topic (max 5 + summary, first run seeds)
- address_key + duplicate_of: copies on another source link to one listing, manual wins
- /projects/house-tracker -> /trackers/houses (308), owner only (404 otherwise); filters for min m2, rooms, source, Gouda region default; notes, add-by-hand form, total rent, also-on links
- scripts/house-db.sh: run the house-listing CLI as the limited house_cli role
- deploy/systemd/house-scrape.{service,timer}
scripts/house-listing.ts (bun run house add|update|list|remove|restore) upserts
house.listings on (source, source_id) derived from the listing URL or address,
derives city_slug, records price_history, and accepts flags or JSON.
pdf.js 6 removed PDFDocumentProxy.destroy(), so the PdfReader teardown threw
"destroy is not a function" on every unmount, breaking the client-side
navigation back to the book page (and sometimes showing the error page).
Close the document through its loading task instead (closePdf), which also
cancels a load still in flight. Test covers the pdf.js API and closePdf.
- Zoom buttons + Ctrl/Cmd +/-/0, Ctrl+wheel/trackpad pinch, touch pinch on PDFs;
PDF scales the pdf.js render (sharp re-render after it settles), EPUB scales text.
Remembered per format, and per book.
- One/two pages side by side: PDF rows with the cover alone, foliate max-column-count.
- Scroll vs Pages mode for both engines; PDF paged shows one page/spread fitted to the
screen, prev/next move a whole spread; spreads stack when scrolling.
- Reading position is kept across zoom/spread/mode changes; progress keeps saving.
- layout.ts helpers with unit tests; old flow/fontSize/pdfZoom settings migrate.
Adds money.accounts.include_in_total (default true). Excluded accounts keep their own balance and line, but are left out of net worth, invested, this month, the projection and the total/All accounts lines.
Run #79 failed at "sudo tee /opt/personal-website/compose.books.yml": the deploy
account may only sudo-tee .env. Write the override to ~deploy instead; the
compose CLI runs as root and reads it from there.
Run #78 skipped the /books mount because [ -d /home/stan/books/library ] runs as
the deploy account, which has no access to /home/stan (0750). Check inside a
container with the dir bind-mounted instead; the mount itself is done by dockerd.
Run #77 failed in db-migrate with "permission denied for function sync": in prod
meta.sync() is granted to personal_app/personal_ddl, not to the schema owner
(personal) that runs migrations. The migration still registers the books tables in
meta.tables and syncs when it can (dev); deploy-remote.sh now runs
scripts/db-meta-sync.ts as DB_URL after migrating. Verified on a prod clone with
SET ROLE personal / personal_app.
The Docker build runs vitest via bun (no Node in oven/bun), where the named
`{ z }` import from zod 4 resolved to undefined; the rest of the repo already
uses `import * as z from 'zod'`.
- Knab statement exports: per-account ids (knab:<account>:<Referentie>), routed by IBAN,
transfers between own accounts merged into one tracker account are dropped (interest kept)
- Import: multiple files, hand-entered twins get the import id, hand entries equal to the
sum of 2-3 payments within a day are replaced by them
- bank-link --bank revolut|abn|knab with per-bank default account; ASPSP notes printed
- bank-sync: longest history on an account first sync (--full to force), sum reconcile,
per-session status so one bank failing does not mark the others
- Timeline: 1 year / 3 years / All history range
Sync stores the bank-reported balance (ITAV preferred) on the account and keeps
one Bank balance adjustment row so the tracker total equals the bank balance.
Accounts panel shows the bank balance and flags any drift.