Commit graph

4 commits

Author SHA1 Message Date
c9ee76387f Google Auto Backup of the age-encrypted blob, passphrase-gated restore
Per request: re-enable allowBackup but scope it to ONE file via
include-rules — files/gbackup/latest.json.age, an age-encrypted export
written only while a backup passphrase is set (never plaintext to
Google; DB/prefs stay excluded). On a new device the OS restores the
blob, the Today screen detects an empty DB + present blob and offers
'Gjenopprett' — which demands the passphrase typed, since the Keystore
key never migrates. Clearing the passphrase deletes the blob.

scrypt work factor dropped 18→15: age's desktop default needs a 256 MiB
working set and OOM-crashed on-device (found the hard way); 15 = 32 MiB,
still real brute-force cost, and largeHeap covers decrypting foreign
files made at 18. Verified by instrumented round-trip tests (write →
empty DB → restore; wrong passphrase keeps DB empty; cleared passphrase
removes blob) — UI automation couldn't drive the multi-field dialog
reliably, so the proof lives in the test instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 19:20:48 +02:00
90eb15ca0b Restore latest backup straight from the bucket
Settings gains 'Gjenopprett siste backup fra bøtta': lists the bucket
(classic query-less ListObjects — an empty canonical query sidesteps a
whole class of SigV4 encoding mismatches; keys filtered client-side),
picks the lexicographically newest timestamped key, downloads, then
runs the existing import path (format auto-detect, passphrase prompt,
replace-all, alarm re-arm). Closes the circle: after a device loss,
recovery is in-app instead of laptop + age -d. parseListKeys is pure
and unit-tested; first ListObjects page only (1000 keys), which the
bucket's lifecycle expiration keeps us far below.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 17:51:13 +02:00
9b5817fcef Inventory split: InventoryItem entity, schema v2 with tested migration, Lager screen
Stock, package size and prescription state move to inventory_item;
medication is now a regimen (itemId FK RESTRICT — deleting a stock item
can never silently take adherence history with it). Supply warnings
aggregate consumption across all regimens sharing an item. New Lager
screen with one-tap '+1 pakning' restock; med editor picks an existing
item or creates one inline via the shared ItemFormState (one form, no
drift). Backup format v2 mirrors the split and still restores v1 files
by performing the same conversion the DB migration does.

MIGRATION_1_2 statements are copied from the exported 2.json and proven
by an instrumented MigrationTestHelper test on the emulator (validates
against the schema AND asserts data survival) before it ever touches
the phone's medical record. Heads-up: connectedAndroidTest must target
the emulator (ANDROID_SERIAL) — the phone's release signature rejects
debug test APKs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 15:13:28 +02:00
8f5b18cf68 Backup: versioned JSON serializer, SigV4 + S3 client, JCE crypto, daily worker
Milestone 5. One BackupManager entry point so export, import and
auto-backup share the same serializer and crypto by construction.
Explicit DTOs decouple the backup contract from the Room schema.
JCE baseline (PBKDF2-HMAC-SHA256 600k → AES-256-GCM) behind the
BackupCrypto interface with format auto-detection on decrypt; age
lands behind the same interface in milestone 6. SigV4 signer verified
against AWS's published documentation example signature. Settings in
SharedPreferences encrypted under a non-exportable AndroidKeyStore key
— replaces the now fully deprecated androidx security-crypto with the
same construction (~70 lines, zero deps); threat-model comments state
plainly that the stored auto-backup passphrase only protects against
bucket compromise. WorkManager daily periodic job, inexact by design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:54:13 +02:00