<feed xmlns='http://www.w3.org/2005/Atom'>
<title>coinosv3.git/src/features/nostrlogin.js, branch master</title>
<subtitle>coinos v3 — bitcoin, Ark, Lightning and nostr wallet (web + Android)</subtitle>
<id>https://git.coinos.io/coinosv3.git/atom?h=master</id>
<link rel='self' href='https://git.coinos.io/coinosv3.git/atom?h=master'/>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/'/>
<updated>2026-10-12T02:51:45Z</updated>
<entry>
<title>Feed: a wheel scroll through a loading feed no longer stutters on a laptop. The phone was fine (its scroll is the compositor's) but the desktop's wheel coast is driven from the main thread, so every long task froze it, and a feed that was still loading ran dozens of them: each ready post went in through its own admitFeed → holdScroll → full render (200–450 ms apiece; a Popular switch did 49 in a few seconds, one of them 1.3 s). Inside those renders the window's list getter — run on every scroll frame too — filtered every note through the mute/spam/report checks, and each check re-derived the login identity (two parses of the stored state plus a bech32 encode): 11 s of a 2-minute profile. Now ready posts are admitted together, one render per 120 ms, and while the reader is moving the page the batch waits for a quiet moment (up to 1.5 s); nostrLoginIdentity keeps its answer for 250 ms (dropped by its own saves, a signer change or another wallet's key), npubOf is memoized, modOn reads the cached messages state, the foreign-language guess is remembered per post, and the window list is reused between renders. Measured on Adam's dev tab (tools/feed-scroll-jank.js: real CDP wheel notches, chip clicks mid-load, rAF frame gaps, long tasks, a CPU profile that names each busy stretch): stalled time during a switch 1.9–2.5 s → 0–83 ms, no frame over 50 ms on the wheel path, main thread busy 33% → 17%; settled scrolling was already clean. feed-streaming's "tap reveals the oldest ready new post" fails on the previous commit too.</title>
<updated>2026-10-12T02:51:45Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-12T02:51:45Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=fdaf7137139540d2f8de50b2a42d6ff3c036328b'/>
<id>urn:sha1:fdaf7137139540d2f8de50b2a42d6ff3c036328b</id>
<content type='text'>
Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Lock with fingerprint or face. The padlock's "Protect this device?" now offers a passkey instead of a password, and Settings → Security can turn it on or off. The saved-wallets vault is sealed under a secret only that passkey's PRF produces (behind the phone's fingerprint/face check, the passkey kept by the platform's secure hardware or password manager), so nothing in this browser's storage opens it. Opening the app on a locked device asks for the passkey by itself; a cancel leaves an Unlock button. The prompts that need the vault opened (save, forget, lock a stray wallet) take the passkey too; there's no Change password for a vault that has none; the recovery words remain the way back. The lock passkey has its own PRF salt and a marked user handle, not discoverable where the platform allows — passkey sign-in refuses it rather than derive a stranger's key from it. English + Italian strings. tools/passkey-lock-test.js (Chrome's virtual authenticator with PRF).</title>
<updated>2026-10-11T05:01:32Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-11T05:01:32Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=8b6d2837716aeb04bedbfdd9c00ab9071be63f90'/>
<id>urn:sha1:8b6d2837716aeb04bedbfdd9c00ab9071be63f90</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Messages: the Amber notice no longer waits for Amber to wake. "Messages sent to you can't be opened here · Connect Amber" showed only once the Amber signer had resumed, and the DM drain is what resumes it — on a phone whose every wrap the old code had already logged as opened, nothing is pending, so nothing might ever ask. nostrLoginIdentity now says how the account came in (`via`), and an Amber account counts as one whether or not its signer is live yet (the notice, and the flag that re-opens written-off wraps once a decrypting signer takes over). tools/amber-dm-upgrade-test.js runs that case with ALL_LOGGED=1.</title>
<updated>2026-10-10T15:35:36Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T15:35:36Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=2338345063b0f1c816469401264b0cd0d7b7636b'/>
<id>urn:sha1:2338345063b0f1c816469401264b0cd0d7b7636b</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Messages: Amber users can read their DMs. Signed in with Amber by intent (NIP-55), nothing sealed to the login npub was ever decrypted — each decrypt would be a trip through the app — so isolabellart's thread with Adam showed only what that phone had sent. Worse, decryptorsComplete() counted the Amber signer as complete, so those wraps were tried on the wallet key alone and logged as opened, hidden for good. Now an interactive signer counts as incomplete (wraps wait, uncounted); Messages and the DM thread say "messages sent to you can't be opened here" with Connect Amber, which opens a nostrconnect:// link for the SAME account over NIP-46 (Amber's bunker mode; a different npub is refused, a callbackUrl reload resumes the handshake). Once a signer that can decrypt takes over, a wallet that ran with Amber re-runs the heal pass over every wrap the relays hold, so messages the old code wrote off come back. tools/amber-dm-upgrade-test.js (plays the remote signer over a shimmed relay).</title>
<updated>2026-10-10T06:54:43Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T06:54:43Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=a70f34f2a5d35352f99bae8f10cbab12637e08ee'/>
<id>urn:sha1:a70f34f2a5d35352f99bae8f10cbab12637e08ee</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
</content>
</entry>
<entry>
<title>Amber (NIP-55) signs you in on Android: one intent per request, the answer read off the clipboard or pasted.</title>
<updated>2026-09-27T15:29:00Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-27T15:29:00Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=b06bbb48e6c6c7f81a9592ab9562bb8e3042b2b6'/>
<id>urn:sha1:b06bbb48e6c6c7f81a9592ab9562bb8e3042b2b6</id>
<content type='text'>
Closes #7. A "Sign in with Amber" door on Android opens nostrsigner: intents for
get_public_key, sign_event and nip44 encrypt/decrypt, with a waiting card that
reads the clipboard when the tab returns (or takes a paste). Requests queue one
at a time — a sign-in fires the backup, relay list and follows together — and
each answer is checked for its type so a stale clipboard is never mistaken for
one. An interactive signer is not asked to decrypt DMs or private lists that
arrive on their own; each trip through Amber is a deliberate one.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
</content>
</entry>
<entry>
<title>New identities start with something to read: Adam and coinos followed, a Bitcoin feed, and #gardenstr.</title>
<updated>2026-09-21T23:55:02Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-21T23:55:02Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=2fefb01640a0d4c353ac0dd351f2059688b7d73a'/>
<id>urn:sha1:2fefb01640a0d4c353ac0dd351f2059688b7d73a</id>
<content type='text'>
A wallet the wizard made from fresh words, or a sign-in that found no
wallet on its identity, gets one kind-3 update following the two coinos
accounts (a sign-in identity only if it follows nobody yet), plus two
starter feeds. The Bitcoin feed has a follow-pack slot (STARTER_BITCOIN_PACK)
and reads the #bitcoin topic until a pack is chosen; #gardenstr shows what
a topic feed is. Once per identity. The feed test now runs through the
real first run, reading the words off the screen.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
</content>
</entry>
<entry>
<title>Seed words read down each column; the Google central is a parameter with a door to the previous one.</title>
<updated>2026-09-21T19:16:17Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-21T19:16:17Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=8dbbe74b2bf9714370197816928c93fe3a34cbd4'/>
<id>urn:sha1:8dbbe74b2bf9714370197816928c93fe3a34cbd4</id>
<content type='text'>
Word grids flow down a column then the next, with the row count from the
word count — on a phone the first column is 1–6, the way a hand copies a
list. pomegranate.js takes the central as an option (CENTRALS.coinos is
auth.coinos.io, now deployed; the default stays auth.njump.me until its
Google client is live), and the sign-in card grows a "previous server"
line once the default moves, so accounts made under njump keep working.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01Tmfg8k5f3TbNJS34qiqHAp
</content>
</entry>
<entry>
<title>A remote signer that drops its socket comes back by itself.</title>
<updated>2026-09-15T01:00:19Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-15T01:00:19Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=cb4e155ae6fb72282d5f186389d2ecca6db6a0f0'/>
<id>urn:sha1:cb4e155ae6fb72282d5f186389d2ecca6db6a0f0</id>
<content type='text'>
"My signer keeps disconnecting": a phone freezing the tab cut the relay
socket the bunker session talks through, every request after that hung
to its deadline, and the app declared the signer gone — a Reconnect
button, tapped over and over. Three changes: the bunker client now talks
through a pool that pings and reconnects on its own, so a dropped socket
is re-opened and the subscription re-fired without anyone noticing; a
request that does fail on a dead connection drops the session and dials
again silently, so the notice only appears if that resume fails; and
coming back from the background probes the signer with a quick question,
replacing a dead session before the next real request runs into it.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
</content>
</entry>
<entry>
<title>Each account in the switcher wears its own face, and its own renewal warning.</title>
<updated>2026-09-11T04:05:58Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-11T04:05:58Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=3cda220c2432181b5f2dfc0c47e28c3e3cb54f18'/>
<id>urn:sha1:3cda220c2432181b5f2dfc0c47e28c3e3cb54f18</id>
<content type='text'>
Two accounts could show the same name and avatar: the identity list read a
key stamped onto the account record, and the stamp was taken during the gap
in a wallet switch where the new account is already loaded and saved but the
features haven't re-inited — so the PREVIOUS account's signer was still
answering "who are you". The live signer now re-checks on every read that it
still belongs to the open wallet, not just at init(); and the switcher reads
each account's own persisted login (or its seed's nostr key) instead of the
stamped mirror, so a record stamped wrong before this still lists right.

The Spending renewal warning belonged to the wallet that computed it and
stayed on screen after switching — the old wallet's amount and deadline over
the new wallet's balance, with a 30-minute throttle keeping it there. It now
resets with the ark manager, and the next connect re-decides.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
</content>
</entry>
<entry>
<title>Passkey identities stay signed in across reloads: the passkey-derived nostr key is now kept in the wallet's nostrlogin state and resumeLogin reattaches from it instantly, so opening chat no longer greets passkey users with "Your nostr signer isn't connected". Kept deliberately and only for the passkey door: a pasted nsec keeps its used-once-never-stored promise, but a passkey key exists only through this app's PRF derivation and re-deriving it costs a WebAuthn ceremony (system dialog + biometric) on every open — and the same storage already holds the wallet seed, so this is the same exposure class. Existing passkey sessions heal on their next reconnect: that ceremony saves the key, and none is asked for again</title>
<updated>2026-09-04T15:24:04Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-04T15:24:04Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=53b283837f6b8341602b17ff9210cb85d1ec8ae0'/>
<id>urn:sha1:53b283837f6b8341602b17ff9210cb85d1ec8ae0</id>
<content type='text'>
Co-Authored-By: Claude Fable 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
</content>
</entry>
</feed>
