<feed xmlns='http://www.w3.org/2005/Atom'>
<title>otpGarmin/source/storage/Record.mc, branch main</title>
<subtitle>OTP Wallet for Garmin Connect IQ: offline one-time passwords and barcodes.</subtitle>
<id>https://git.cirakg.xyz/otpGarmin/atom?h=main</id>
<link rel='self' href='https://git.cirakg.xyz/otpGarmin/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cirakg.xyz/otpGarmin/'/>
<updated>2026-10-27T00:01:18+00:00</updated>
<entry>
<title>fix(pin): the work after a PIN and the change queue run in steps</title>
<updated>2026-10-27T00:01:18+00:00</updated>
<author>
<name>cirakg</name>
<email>cirakg@cirakg.xyz</email>
</author>
<published>2026-10-27T00:01:18+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cirakg.xyz/otpGarmin/commit/?id=bc4cc19df5f53519ca1aabd3cf161b6d78ffc16c'/>
<id>urn:sha1:bc4cc19df5f53519ca1aabd3cf161b6d78ffc16c</id>
<content type='text'>
After a correct or new PIN, promoting pending entries and applying waiting
"Require PIN" changes ran in the event of the last PBKDF2 step; 16 waiting
changes tripped the watchdog on the Forerunner 70. ChangeQueue.LockJob now
does it in steps while the PIN screen shows "Checking...", saves what it did
to the results and refreshes the glance after Set PIN too.

The list title decrypted every waiting change at each start. Queue items
are now [action, box] (aad OTPW-Q-2 + action code), so they are counted
without decrypting; 0.9.0 items are still read. A replacement is checked
when it arrives and its material is sealed apart (qm.&lt;n&gt;). Large buffers
are wiped and UTF-8-checked in pieces (Jobs.Wiper, Bytes.utf8Prefix,
SealedBox.openKeep/sealKeep). "Phone changes" reads behind "Reading...",
and Confirm all / Reject all act only on the changes shown. Storage full
no longer stops the app at the PIN.

Found by the review of b8addc8/096b5a4 and a new make watchdog test; the
diff was reviewed again and its findings fixed. fr70: make lint, make test
(176), make watchdog (19 cases at two steps per event), make screenshots
pass; list_locked and list_reset goldens show "Results ready (menu)".

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(storage): add keyed secret fingerprints to record headers</title>
<updated>2026-10-19T23:51:05+00:00</updated>
<author>
<name>cirakg</name>
<email>cirakg@cirakg.xyz</email>
</author>
<published>2026-10-19T23:51:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cirakg.xyz/otpGarmin/commit/?id=bc228753bfb4a2faea540cddb20fc39ebd265db5'/>
<id>urn:sha1:bc228753bfb4a2faea540cddb20fc39ebd265db5</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>feat(storage): add the encrypted vault, PIN keys, lockout, session and migrations</title>
<updated>2026-10-19T13:27:37+00:00</updated>
<author>
<name>cirakg</name>
<email>cirakg@cirakg.xyz</email>
</author>
<published>2026-10-19T13:27:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cirakg.xyz/otpGarmin/commit/?id=dd5d0f68726fe56ee4d199867d414aea81905cc4'/>
<id>urn:sha1:dd5d0f68726fe56ee4d199867d414aea81905cc4</id>
<content type='text'>
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 &lt;noreply@anthropic.com&gt;
</content>
</entry>
</feed>
