<feed xmlns='http://www.w3.org/2005/Atom'>
<title>coinosv3.git/src/nostr.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>Messages: NIP-42 inbox relays. isolabellart's kind-10050 inbox is auth.nostr1.com and a personal relay, both of which answer a REQ for their gift wraps with CLOSED "auth-required" — we never authenticated, so DMs delivered only there (anything sent from Amethyst and other 10050-only clients) never reached v3. The DM subscription on our own inbox relays now authenticates as the DM identity (subscribeAuthed: per relay, AUTH then ask again, still closable; the AUTH event is signed before nostr-tools' cached auth promise so a failing signer can't wedge the socket), sends to a peer's inbox authenticate when a relay refuses the write with auth-required, and our sent copy also goes to our own inbox relays so other clients reading that list see it. Never Amber by intent (an app trip per connection). Checked live: auth.nostr1.com accepts the handshake. tools/dm-nip42-test.js</title>
<updated>2026-10-10T14:03:30Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-10T14:03:30Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=572361e2bd1ab16b4618517c2d9531689c6f46a8'/>
<id>urn:sha1:572361e2bd1ab16b4618517c2d9531689c6f46a8</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>Gentler on relays: a relay that says rate-limited (CLOSED/OK/NOTICE) gets no one-shot reads for 30 s, doubling to 10 min; zap/reply tallies flush at most every 2 s in one REQ (two filters) and the live #e sub swaps at most every 10 s; quoted-note lookups batch per relay over 150 ms — scrolling a 300-follow feed went from ~21 to ~8.5 REQs per 10 s on the busiest relay, primal's "too many concurrent REQs" from 19 to 0</title>
<updated>2026-10-08T04:27:40Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-08T04:27:40Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=654e489cb438c62c09d291cc781707323914f8f1'/>
<id>urn:sha1:654e489cb438c62c09d291cc781707323914f8f1</id>
<content type='text'>
Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01PCGjEEMD23CweKrefbTDJp
</content>
</entry>
<entry>
<title>Stream ready feed posts without waiting for catch-up batches</title>
<updated>2026-10-01T22:31:10Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-01T22:31:10Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=a7806172f7ede9be345750c93e5fa72e10e9b953'/>
<id>urn:sha1:a7806172f7ede9be345750c93e5fa72e10e9b953</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Serve a shared snapshot for the public feed</title>
<updated>2026-10-01T13:55:14Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-01T13:55:14Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=5d441ebbce6066c3636168ec5332ae9c2bdb8607'/>
<id>urn:sha1:5d441ebbce6066c3636168ec5332ae9c2bdb8607</id>
<content type='text'>
Build the default Popular feed once on the server and let browsers verify and paint its signed events before slow media finishes. Keep custom relays, pagination, filtering, and relay fallback on their existing paths.\n\nAdd stale-while-refresh persistence, service-worker bypass, development integration, and unit/browser coverage.
</content>
</entry>
<entry>
<title>A profile shows all of someone's posts, not just the two primal still holds.</title>
<updated>2026-10-01T13:45:07Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-10-01T13:45:07Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=5ff909d37fe32ba04a841941593d95c7b3498ec3'/>
<id>urn:sha1:5ff909d37fe32ba04a841941593d95c7b3498ec3</id>
<content type='text'>
The phone had written off relay.coinos.io, nos.lol and damus after a
network blip, so only primal answered. Home relays are now skipped for a
minute at most and forgiven on resume/online; failures while hidden or
offline don't count. Profiles also ask history-keeping relays, merge
answers instead of replacing the page, and re-ask on reopen.

Co-Authored-By: Claude Opus 5.5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
</content>
</entry>
<entry>
<title>Console: NWC chatter behind a debug switch; dead relays remembered longer and dropped from the pool.</title>
<updated>2026-09-27T05:51:52Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-27T05:51:52Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=b3df6e629a8d165ac2d6f9fb6c4962d47da0f0cf'/>
<id>urn:sha1:b3df6e629a8d165ac2d6f9fb6c4962d47da0f0cf</id>
<content type='text'>
`coinosDebug(true)` in the console (or ?debug=1) turns the nwc: listener
logs back on; they are off by default. Warnings and errors stay.

The red WebSocket lines were the pool's own reconnect loop redialling relays
from other people's relay lists that have been dead for weeks — the sick
list only stopped NEW dials, for ten minutes, and forgot on reload. A dead
relay is now dropped from the pool (no reconnect loop), skipped twice as
long each time it fails (up to a day), keyed by normalized URL, and the
verdict survives a reload. A relay the pool has failed to reconnect to
three times counts as dead.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
</content>
</entry>
<entry>
<title>Feeds: follow packs from following.space (NIP-51 kind 39089) as a feed's people.</title>
<updated>2026-09-21T06:14:41Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-21T06:14:41Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=19ac1c46091e969075d998af95d304d82cbba259'/>
<id>urn:sha1:19ac1c46091e969075d998af95d304d82cbba259</id>
<content type='text'>
A feed can name packs as well as people: the pack's current members are
its authors, read from the pack event itself and cached on disk, refreshed
hourly when a feed using it is opened — so a curator's change reaches the
feed by itself. The editor takes a pasted following.space link or naddr, or
searches packs by title (NIP-50 on relay.nostr.band). parseNostrRef learned
naddr.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
</content>
</entry>
<entry>
<title>Chat keeps its relay subscriptions alive while the app is on screen.</title>
<updated>2026-09-15T18:56:52Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-15T18:56:52Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=0ebef05d3850d312ec8098584525b22ec1b9a0de'/>
<id>urn:sha1:0ebef05d3850d312ec8098584525b22ec1b9a0de</id>
<content type='text'>
New messages rang in Vector but sat unseen in v3 until a reload. The
resume hook only rebuilds subscriptions after an absence it was told
about; a socket that errors while the page stays visible (a wifi hop, a
laptop lid, a phone freezing the tab without the away timer noticing,
another tab clearing that timer first) is dropped from the nostr-tools
pool with every subscription it carried, and nothing dials it again.

A watchdog now runs whenever the app is on screen: on focus, on becoming
visible, and every ten seconds between. It asks the pool whether each
relay the inbox and the rooms listen on still holds a live socket (a dial
hung past 20s counts as dead, and a relay that closed a subscription on us
counts too) and rebuilds the lot when one is missing, dropping a stuck
relay first so the rebuild dials afresh. Rebuilds retry on a growing
backoff while a relay keeps refusing. tools/chat-watchdog-test.js errors
every socket under an open room and checks the recovery.

Co-Authored-By: Claude Fable 5.1 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
</content>
</entry>
<entry>
<title>The feed was showing half the posts it should.</title>
<updated>2026-09-13T05:15:10Z</updated>
<author>
<name>Adam Soltys</name>
<email>adam@coinos.io</email>
</author>
<published>2026-09-13T05:15:10Z</published>
<link rel='alternate' type='text/html' href='https://git.coinos.io/coinosv3.git/commit/?id=d57962cb896b360295ec107afd50d2ce8701c401'/>
<id>urn:sha1:d57962cb896b360295ec107afd50d2ce8701c401</id>
<content type='text'>
Compared against a wider net over the same six hours and the same follow
list — 862 people, read off this wallet:

  before:  165 notes from 38 people
  after:   327 notes from 86 people, and nothing the wider net still finds

Four things were losing posts, in order of how much:

THE AUTHOR CAP. feedAuthors() stopped at 500. On an 862-follow list that
silently dropped 362 people, and 164 of the 166 posts the wider net found
were theirs. Gone: it takes everyone now, up to a couple of thousand, with
each REQ carrying at most 400 authors so no single filter is absurd.

THE DEFAULTS. relay.damus.io is the second-biggest relay this follow list
writes to — 249 of them — and it wasn't in our set. 232 follows publish no
relay list at all and lean entirely on those defaults, so they were being
asked for on two relays instead of three.

THE SOCKET CAP. Ten relays covered all but 66 follows; twenty-five covered
all but 26. It's 24 now — Amethyst runs near a hundred, and this is the same
idea kept to a number a phone can hold.

MALFORMED RELAY LISTS. One list in these follows crams three URLs into a
single r tag, space-separated. It passed our "starts with wss://" check,
reached the pool, threw Invalid URL — and every author covered by that relay
quietly returned nothing. Each URL has to parse now.

And a quote-post is no longer mistaken for a reply. NIP-10 marks a reply's e
tags 'root' or 'reply'; a quote's are 'mention', and NIP-18 quotes carry a q
tag. We were hiding every note with an e tag, which included the quote-posts
that now render as cards.

Co-Authored-By: Claude Opus 5 (1M context) &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
</content>
</entry>
</feed>
