summaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* chore(release): 0.9.0, the fixes from the first real-watch roundv0.9.0cirakg4 days4-3/+96
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(ui): second UI scenario, and the fixes it foundcirakg4 days208-89/+772
| | | | | | | | | | | | | | | | | | | A second UI-test build (uitest-extra.jungle) covers the legacy PIN and the forced new PIN, Change and Reset PIN, a queued Require PIN, UPC-A, Code 39, the code error screen, the encoding progress, auto-lock on a barcode, the light theme and the glance without a code; the main flow adds the Steam and Library details and the menu's text screens. Fixed from what it found: Entry IDs tripped the watchdog with 110 entries; auto-lock didn't close a locked barcode; a queued Require PIN showed "Confirm in menu"; locked barcodes lacked the padlock; the PIN keypad's bottom row, the Reset PIN question, text-screen bottoms, the glance title and the light-theme warning colour. Goldens approved on all seven watches; only the Forerunner 70 was re-verified after the version bump. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(codes): encoding progress follows the workcirakg4 days8-8/+214
| | | | | | | | | | The QR percentage counted phases and the display froze at 90 % once encoding ended, so the first opening sat at 90 % while the slow stages ran. Each 2D symbology now weights its phases by measured cost, interpolates inside the long ones, and the display splits encode, pack and runs; a cached code counts its runs from 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: 4-digit PIN, the offline-guessing limit, Manage needs Applycirakg4 days5-21/+29
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): vertical swipes and drags scroll the list and text screenscirakg4 days6-9/+206
| | | | | | | | | The Forerunner 70 sends no page behaviour for a swipe on a custom view, so the list and info screens only scrolled with the buttons. Swipes and drags now scroll them directly; one gesture scrolls once even when the device also sends the page behaviour, and a drag's release opens no row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(pin): exactly 4 digits, entered on the 4th digitcirakg4 days18-57/+381
| | | | | | | | | | | | The keypad has no OK key; the 4th digit submits, so a tap that also arrives as START can no longer submit a partial PIN (the likely cause of "Wrong PIN" before the code opened). PINs set by earlier versions still unlock and are then replaced by a 4-digit one. Opening an entry ignores a second request from the same tap. A Require PIN sent from the phone while the watch is locked now applies at the next PIN instead of waiting for Confirm all. The linter skips .claude. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record the settings spike and the Forerunner 70 benchmarkcirakg4 days14-16/+126
| | | | | | | | | | Spike (2026-10-21, Android): 8000 characters per field and 64,000 in total arrive; URIs with & save; the mixed special-character string is refused, so the test strings now split it into one group each plus a fake Aegis export. Benchmark: SHA-1 TOTP 18 ms, SHA-512 732 ms, PBKDF2 2,000 iterations per second. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: the Forerunner 70 is AMOLED; barcodes go to full brightnesscirakg4 days1-4/+4
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: how a sideloaded build is removed from the watchcirakg4 days2-3/+6
| | | | | | | The watch removes the .prg after installing it; the app is removed from the watch's own menu and its fr70.* storage files deleted by hand. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: your step-by-step guide to the on-watch tests before 1.0.0cirakg8 days3-1/+303
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: user guide, known limitations and M8 results; bump to 0.8.0v0.8.0cirakg8 days9-23/+386
| | | | | | | 0.8.0 is the release candidate: 1.0.0 is tagged after the on-watch checklist (docs/TESTING.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(build): add make watchdog, the glance memory budget and the glance ↵cirakg8 days26-31/+425
| | | | | | | | | | | | | | | screenshot - make watchdog (in make ci): a stress build encodes, packs, turns into runs and draws the largest code of every 2D symbology at two steps per event on the Forerunner 255S and 70; it fails on a watchdog trip, and the last code left on the screen must decode (bitmap path on the 70). - make memtest checks the glance (DIAG glance, budget 70 % of 64 KB). - screenshots.sh launches the app again after the flow and compares the glance with the favourite's code (glance_code.png on 7 watches); the About goldens show 0.8.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(glance): show the favourite code in the glance (opt-in)cirakg8 days25-54/+581
| | | | | | | | | | | | | | | With "Show the favourite code in the glance" on, the glance shows the code of the first favourite TOTP or Steam entry without the PIN, with a countdown bar. The glance can't open the vault, so the About scan seals that entry under "g" (device key, aad OTPW-GL-1) after every change, and removes it when the option is off or no entry qualifies; the glance also reads the option itself. HKDF moves to its own module so the glance loads only the crypto it needs (37 % of its 64 KB). Manage entry gains "Mark as favourite (glance)" and "Remove favourite", queued for locked entries like the other changes. Lines are inset on round screens so the Instinct 3's curved glance corner doesn't cut them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(codes): bound every 2D encoding step under the watchdog; draw large ↵cirakg8 days13-777/+2262
| | | | | | | | | | | | | | | | | | | | | | | | | | | | codes from runs M6's step sizes were estimates, only exercised with small codes. With the largest inputs the app accepts, every 2D encoder tripped the watchdog on the Forerunner 255S (and the biggest steps were far over the 768 KB watches' limit), so a large code added from the phone would have stopped the app during settings processing at every start. - QR: resumable segmentation, Reed-Solomon, interleaving, function patterns and final mask; mask scoring on rows and columns packed 16 modules per word (same penalties, checked against the per-module rules). - Reed-Solomon: resumable field tables and generator, encoder shifting with native slice. - Data Matrix, Aztec, PDF417: resumable encodation, stuffing, compaction, placement and build; Data Matrix's grid as a byte array (90 KB -> 35 KB); PDF417 refuses text that can't fit the screen before compacting. - Cache format 2 with 16-bit sizes (wide PDF417 was cached wrong); packing in steps; codes drawn as runs, and from a bitmap painted in steps when a frame can't draw all runs. - The job runner does two steps per tick on 768 KB watches; gen_products --check verifies each product's watchdogCount fits that. - CodeView renews full brightness (AMOLED) or the backlight (MIP) for up to a minute from its single ticker; a tap restarts the minute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(import): keep parsed items packed and the pasted text oncecirakg8 days10-56/+248
| | | | | | | | | | | | | | A parsed import item with its Entry took about 760 bytes as objects on the Forerunner 255S; packed into one Codec ByteArray it takes about 110, and the importer unpacks one item per store step (wiping the packed secret). Aegis group names are resolved when unpacking, so no step repacks all items. The JSON is parsed from the untrimmed text's bytes and the string is dropped; an incomplete part is saved from those bytes. The form hash is computed before the importer copies the text. The peak of a 100-entry import on the 255S falls from 56 % to 45 %. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M7 (settings forms) results; bump to 0.7.0v0.7.0cirakg9 days8-15/+139
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* build(lint): validate resource XML against the SDK schema and cross-check ↵cirakg9 days5-0/+250
| | | | | | | | | | the settings form tools/check_resources.py checks every resource file against the SDK's resources.xsd and that settings, properties and strings agree. Adds the pure-Python, MIT-licensed xmlschema to the dev image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(settings): cover every form, symbology and Manage action; send the ↵cirakg9 days57-6/+1061
| | | | | | | | | | | | forms end to end in the UI flow 34 Monkey C tests (154 per device): validation messages, idempotency, the queue, the About scan, "Encrypt names" and the properties contract. The UI-test build fills in Add OTP, Add code and Manage like the phone; the flow checks the new TOTP (309 848 at the frozen time), decodes the Code 128 card and confirms the queued change to the locked entry with the PIN. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(settings): add the Add OTP, Add code and Manage entry forms with a PIN ↵cirakg9 days18-120/+2369
| | | | | | | | | | | | | | | | | | | | queue for locked entries The full phone settings form (README 10.2) and its processing (10.3) as one resumable job: Import, Add OTP, Add code, Manage, then the About fields. Forms are applied only when their switch is on, cleared afterwards (secret first; only the secret after a problem), deduplicated by keyed hash, and report into their Result field and the results screen. - Add OTP validates per README 10.4; Add code runs the real encoder (CodeCheck, a job for 2D codes) and caches 2D codes on the way in. - Manage entry: edit, move, require/drop PIN, replace, counter, delete. Changes to locked entries are sealed under "q" and applied only after the PIN is entered under "Phone changes" in the menu. - About: catalog (800 characters max), version, storage use. - "Encrypt names" now applies to new and existing entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M6 (all 12 symbologies) results; bump to 0.6.0v0.6.0cirakg9 days4-7/+38
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(codes): compare Aztec with aztec_code_generator, decode it and its ↵cirakg9 days19-5/+161
| | | | | | screenshot Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(codes): add an Aztec encoder (compact and full range) as a resumable jobcirakg9 days2-0/+525
| | | | | | | | Upper/Lower/Digit modes with Binary Shift, bit stuffing, Reed-Solomon over GF(2^6..2^12) in steps, geometry ported from aztec_code_generator; symbols match it for single-class text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M6c (Data Matrix, PDF417) resultscirakg9 days4-1/+45
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(codes): compare Data Matrix and PDF417 with references and decode them, ↵cirakg9 days31-17/+544
| | | | | | also from screenshots Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): show Data Matrix and PDF417 codes; fit 2D codes inside round screenscirakg9 days2-31/+66
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(codes): add Data Matrix (ECC 200, 10x10-144x144) and PDF417 encoders as ↵cirakg9 days10-20/+951
| | | | | | | | | | | | jobs Data Matrix: ASCII with digit pairs, Base256 or Upper Shift for UTF-8, interleaved Reed-Solomon in steps, utah placement; matches ppf.datamatrix. PDF417: pdf417gen's compaction, automatic or fixed EC level, columns fitted to the screen, pattern tables as generated JSON resources; matches pdf417gen. 2D matrices gain a row height and 8-at-a-time packing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* build(docker): add pdf417gen, ppf-datamatrix and aztec-code-generator as ↵cirakg9 days3-1/+27
| | | | | | reference encoders Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M6b (QR) resultscirakg9 days5-4/+46
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(codes): compare QR with segno, decode QR dumps and the QR screenshotcirakg9 days29-3/+459
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): show QR codes, encoded once with progress and cached sealed per entrycirakg9 days3-25/+195
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(codes): add a QR Code encoder (versions 1-40, levels L-H) as a ↵cirakg9 days4-0/+1025
| | | | | | | | | | | resumable job Optimal numeric/alphanumeric/byte segmentation, Reed-Solomon over GF(256), all eight masks scored like segno, steps sized for the 120,000-bytecode watchdog; block tables generated from segno; a generic Matrix type with a compact form for caching. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M6a (linear barcodes) resultscirakg10 days4-4/+36
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(codes): compare with zxing-cpp reference rows and decode encoder output ↵cirakg10 days16-4/+757
| | | | | | and screenshots Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): show barcode entries black on white at the largest whole module sizecirakg10 days3-4/+184
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(codes): add EAN/UPC, Code 128, Code 39, ITF and Codabar encoderscirakg10 days6-0/+661
| | | | | | | | Check digits added, verified, fixed or off; canonical UPC-E zero suppression; Code 128 sets B and C with zxing's switching rules; module rows match zxing-cpp (Code 128 by length, the tie-breaking differs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M5 results; bump to 0.5.0v0.5.0cirakg10 days6-5/+111
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(import): cover parsers, importer and a seeded fuzz corpus; import 100 ↵cirakg10 days23-5/+1050
| | | | | | entries in the UI flow Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(settings): apply the Import form from the phone and show resultscirakg10 days13-16/+411
| | | | | | | | Import group in the app settings; the processor runs after the first frame and on settings changes, clears the fields, writes the status, remembers form hashes and opens the results screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(import): parse otpauth URIs and unencrypted Aegis JSON in resumable stepscirakg10 days8-0/+1609
| | | | | | | | | | Streaming JSON tokenizer with depth and length limits and native skipping of long strings; Aegis reader for database versions 1-3 with groups and encrypted-export detection; otpauth parser with ';' separators and Steam; import job with Replace mode, duplicate and replacement checks, a sealed multi-part buffer and a report. Google Authenticator exports deferred. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(storage): add keyed secret fingerprints to record headerscirakg10 days4-1/+53
| | | | | | | | An 8-byte HMAC of kind, type and secret (key derived from the device key) in header tag 13 lets imports find duplicates of locked entries while the watch is locked. Vault gains anyHeader() and storage(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M4 results and simulator findings; bump to 0.4.0v0.4.0cirakg10 days7-20/+132
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(ui): drive the simulator with a scripted flow and check every screencirakg10 days79-32/+808
| | | | | | | | | tools/ui_flow.py clicks buttons and taps from the device files, waits for DIAG shown lines and compares 10 screens per watch with goldens on four test and three spot-check devices. Adds the fixture-wrap generator, UI logic tests and driver tests; the launcher opens apps from their glance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): add the list, OTP detail, PIN entry, menu, info screens and glancecirakg10 days23-16/+2575
| | | | | | | | | | | | List with live codes and countdowns, adaptive rows and lazy headers; OTP detail with ring or bar, next code and HOTP next; PIN keypad or button selector with create, unlock, change, lockout display and pending promotion; native menu; glance with the entry count; self-test after the first frame; option properties with defaults. Codes are computed one per timer event, apart from drawing, for the watchdog. A UI-test build (uitest.jungle) starts with fake entries, PIN 123456 and a frozen clock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(crypto): XOR PBKDF2 blocks as 32-bit words and step 100 iterationscirakg10 days7-76/+76
| | | | | | | | 200 byte-wise iterations in one event tripped the 120,000-bytecode watchdog on the Forerunner 255S profile. Word-wise XOR makes PBKDF2 2.3x faster (22 ms per 1000 iterations in the simulator). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: document the storage format and record M3 results; bump to 0.3.0v0.3.0cirakg10 days12-4/+218
| | | | Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(storage): cover records, vault, PINs, lockout and migrationscirakg10 days7-1/+1065
| | | | | | | | | | 34 new tests (65 per device): round trips per entry type, every tampered byte, swapped records, the pending queue, the entry cap, a full store at 67 fill levels, the lockout schedule on a fake clock including restarts, erase, PIN change and reset, auto-lock wiping, and v0 to v1 migration. Adds an in-memory store with a size limit and storage benchmarks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(storage): add the encrypted vault, PIN keys, lockout, session and ↵cirakg10 days12-0/+1605
| | | | | | | | | | | | | | migrations Records are one ByteArray each: a readable TLV header plus an encrypt-then-MAC box whose aad binds the entry ID and the header. The device key seals unlocked entries and pending items; a random master key, wrapped under a PBKDF2 PIN key, seals locked ones. Wrong PINs are counted before the check, with delays on the trusted clock and an uptime fallback. Bytes.toUtf8 validates UTF-8 first, because the native conversion crashes on invalid input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: record M2 results and bump version to 0.2.0v0.2.0cirakg11 days7-4/+84
| | | | | | | | | CHANGELOG 0.2.0; PERFORMANCE (simulator benchmarks and what they mean for the watch); TESTING (vector sources, M2 results, watchdog caveat); FEASIBILITY (verified: no native HMAC-SHA1, AES-256 works, Long semantics); README status and targets; developer-guide Monkey C facts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(spike): add the M1 settings spike app and make targetscirakg11 days37-5/+861
| | | | | | | | | | A separate throwaway app (own app ID, no permissions) that shows what the Garmin Connect app delivers per settings field and writes values back, to measure the undocumented settings limits on the real phone and watch. Includes length-marked test strings, `make spike` / `make spike-sim`, `make vectors`, and step-by-step instructions in docs/SETTINGS_SPIKE.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test: add cross-checked crypto/OTP vectors, unit tests and stricter screenshotscirakg11 days14-2/+1193
| | | | | | | | | | | | | - gen_vectors.py: RFC/FIPS/NIST vectors recomputed with Python's stdlib (openssl for AES) before use, plus seeded cases around SHA padding boundaries, 2038 and 2100; generates test/data/Vectors.mc. - 31 Monkey C tests (hashes, HMAC, PBKDF2, HKDF, AES, sealed records incl. every-byte tamper detection, Base32 errors, 100 OTP and 12 Steam vectors, clock, jobs) and a benchmark; pass on all four test devices. - Screenshot tolerance 0.01 %: one changed character no longer passes. - Golden images updated for version 0.2.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>