| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
| |
notes and replies that say them. "if i submit just a string without selecting one of the suggestions i'd like it to do full text search on kind 1 posts and replies. so if i search fiatjaf i should find posts that are mentioning him without necessarily tagging him" (Adam, 2026-11-11). A session feed with def.search asks NIP-50 relays ({ kinds: [1], search }) — relay.ditto.pub, search.nos.today, nostr.wine, the three that answered a kind-1 search probe (nostr.band and primal answered nothing) — keeps replies as rows of their own, pages back with until, and has no live subscription. Enter in the search page's field or the desktop header field opens it, and the search page lists a “words” row ("Posts and replies that say this") above the topic and people rows. Its address is /feed?q=… so it can be shared. tools/post-search-test.js answers the search relays in the page
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6UKGJyuGyZFxddYRxS
|
| |
|
|
|
|
|
| |
forget the account, and it leaves this device. "someone's asking for a way to delete their v3 account. we allowed that in v2 … we can expose a button for it in settings somewhere" (Adam, 2026-11-10). The page first asks the registrar what stands in the way (coinosv3-server f8c9efa, POST /account/delete with check): sats still on their way here or held in a withdraw link block it, and say so. It lists what goes (the name, free for anyone after; the server's records: payment address, notification registrations, scheduled posts, hats, point-of-sale and receipt records; the wallet on this device) and what does not: the coins, which only the recovery phrase controls, and what was published to nostr relays. Coins still here are named in sats with a warning, and the box to tick then says "I have moved my funds or saved my recovery phrase". On the tap the registrar deletes (signed by the wallet's key, which also answers for a login identity's name it manages), the profile drops a lud16/nip05 that named the freed address (addressGone; publishProfileFields learns `remove`), and the wallet is wiped from the session, the saved vault and its caches. A vault behind a real password not entered this session asks for a lock and unlock first. tools/account-delete-test.js (a stand-in registrar checks the NIP-98 signature)
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6V74NSFWZtNtgCfokN
|
| |
|
|
|
|
| |
they said. "can i have some control as a chat group admin to click on a message and see an option to remove the user and all their messages from the group?" (Adam, 2026-11-10). The message sheet gains "Remove <name> and their messages" for the owner on anyone else's message; a first tap asks ("for everyone? Tap again"), the second does it. It is the owner's ban list (CORD-04 vsk 4): one owner-signed edition naming every removed key, which the app, the public /chat page and other Concord clients already fold: those authors' messages, old and new, drop out and they leave the member list. The list is replaced whole, so setBanned asks the relays for the control stream first and builds on the newest edition (banlistHead/makeBanlist chain version and hash), stopping if the relays hand back nothing. The member list shows the admin who was removed, each with Restore. Offered only where the app holds the control stream's signing key (the built-in community and ones founded here). Removed people no longer light a room's unread dot. And the control fold now lets only the owner's editions compete for a head: any member holds the control stream's key in a public community, and a higher-versioned edition of their own used to shadow the owner's ban list, name or channels (dropped, leaving nothing). tools/chat-remove-member-test.js runs it against a relay inside the test
Co-Authored-By: Claude Fable 5.2 <[email protected]>
|
| |
|
|
|
|
| |
here, and stops telling them to sign in. "andy's having some trouble with the public chat … I can see the feed etc fine, so I am logged in" (Adam relaying Andy, 2026-11-10): /chat read "sign in to join the conversation" to a signed-in reader, and its "Open coinos" button went to the wallet, leaving him to find the chat. With an account on the device the line now reads "open the chat in Coinos to join the conversation" and the button "Join the chat", which goes to /?chat: the app takes the marker off the address and, once the wallet is open, opens the coinos community's room. A saved wallet counts as an account too (a tab opened from a link has no session yet). And /chat, /pos and /feed no longer paint the static profile shell (a card named "chat") while they load. tools/public-chat-join-test.js
Co-Authored-By: Claude Fable 5.2 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
Coinos behind another app answers from its worker. "I'm on V3 … I find nwc zaps a lot more slower than usual" (398ja, 2026-11-10). Measured from the notifier's wakes and the ASP's log over four days: a closed app took about six seconds from the push to its cosign (the Lightning part after that is one and a half), an Android phone fifteen at the median, up to thirty. Where it went: every call to the ASP and the notifier was two round trips (no Access-Control-Max-Age, so Chrome re-asked the CORS preflight after five seconds; a Cloudflare response-header rule now sets 7200 on OPTIONS for ark.coinos.io, ark-staging and nwcpush), and the worker made them one after another: answered?, GetArkInfo, Handshake, claim, a save, the chain tip, then the payment, then status ("pending"), the tip again, the held status call, and status once more for the preimage it had just been handed. Now: what the mirror alone decides (invoice, network, budgets, coins, keys) comes first with no network; the claim (which now also says "already answered", coinosv3-server 3c5e9c3) goes out beside the manager's start, whose info call, handshake and tip run together (init, warmTip/_tipFresh); a send just initiated returns to its caller instead of asking at once, and awaitLnPay settles from the held call's own answer. A worker-style send on regtest: eight calls, six in sequence, where there were thirteen. The app's own answer no longer waits a second on a phone that is on screen (only one behind another app holds back for a computer).
Android freezes an app behind another one, and the worker's nudge to it sat unread until it was opened: on a phone the worker now hands a request only to a window on screen (as it already did on iOS), nudges a hidden one in case it is awake, and answers itself; the request's lock decides between them. For that the mirror is no longer written over the worker's hand: the worker stamps its writes (workerAt), the app writes under the worker's own lock (nwc-bg-work) and folds the worker's state in first (never mid-payment), carries the stamp, absorbs it when it comes back on screen (arkBgAbsorb), and refreshes the mirror as it goes behind another app.
Tests: ln-pay-double-wake (the one held call settles; a failed or mismatched answer doesn't), sw-push-quota (an Android phone's hidden window), nwc-sw-test's notifier stand-in answers /claim as the real one; tools/ark/ln-e2e-test's pay half against regtest captaind 0.8 + CLN
Co-Authored-By: Claude Fable 5.2 <[email protected]>
|
| |
|
|
|
|
| |
button leaves our full screen. "The fullscreen icon on this youtube video worked to make the video fullscreen but not toggle it off … clicking the central pause button didn't pause, it fullscreened. it should just pause" (Adam, 2026-11-10). YouTube's newer player puts its pause button in the middle and its full-screen button above the bottom bar, both under the layer that takes a tap on the picture to full screen: so either press took the box full screen, and once there (the layer aside) the same button asked for full screen again from inside the frame, which shows nothing and leaves no way out. Now the layer is off while a mouse is over the box (by-mouse, set from the pointer's type; a finger gets it back), so the player's buttons do what they say, and a mouse click on the still plays it where it is. A touch's tap still goes full screen with sound; there, when the frame becomes the full-screen element in place of its box (the page sees that much), it's read as "out" and both steps are left. youtube-tap-test covers the touch runs with a finger, the button leaving full screen, and a mouse
Co-Authored-By: Claude Fable 5.2 <[email protected]>
|
| |
|
|
|
|
|
| |
"I refreshed and scanned it again and it put the lnurl in the recipient field and gave me a zap button" (Adam, 2026-11-08): zaps' matchSendText turned everything down until canLnPay, which is false for the first moments after a refresh, so his own withdraw link fell through to the send form's "Zap LNURL…" affordance. A withdraw pays us and needs nothing spendable, and only the GET says which kind an lnurl is, so a scanned or pasted lnurl is taken first and resolved; a pay one meets the pay checks where it's paid. lnurl-withdraw-test covers the scan with nothing spendable
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
opens the app's Zapstore page. "Clicking this franzap zap notification keeps bringing me to his profile not a note" (Adam, 2026-11-07): franzap's 10k "Love it" was a zap on the PAFF listing (kind 32267, a-tagged in the zap request), but Zapstore's receipt carries only p and P, so the row read as a zap on nothing and fell through to the zapper's profile. Rows now take a zap's address from the receipt or, failing that, the zap request inside it (zapAddrOf; streams get that too); a 32267 one is an app zap, named from relay.zapstore.dev once per session, opening zapstore.dev/apps/<id>. A row saved before reads its receipt by id on the first tap and goes where it says; a relay handing the receipt again heals it as well. tools/notif-app-zap-test.js
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
(QR + copy) that anyone can withdraw sats from, like a voucher, with this wallet asleep. "Do we have lnurl withdraw links somewhere? can we add them?" → "Prepaid" (Adam, 2026-11-07); redeeming them already worked (38ce0b4), making them didn't. Amount per withdrawal, how many withdrawals (1–1000), an optional description; the whole amount is paid up front from Spending over Lightning to the coinos registrar (coinosv3-server 912944d, names/withdraw.js), which pays each claim out of it, so it works with any Lightning wallet while the phone is off; "Your withdraw links" lists them (what's left, used up, closed) and a link's page has Take back {what's left}, paid to a fresh Lightning receive of this wallet, which closes the link. Links belong to the wallet's nostr key (NIP-98), so every device with the seed sees them. Lives with the gift link (deferred gifts chunk, ui.giftMode === 'lnurl'). tools/lnurl-withdraw-links-test.js: makes one against the real service behind a stand-in server with a fake node, claims it through our own redeem flow, takes the rest back
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
of it. "When I flick to scroll, it's not getting up to speed very quickly, and clicking around in the app feels a bit sluggish at times" (Adam, 2026-11-07). Measured on his Pixel 9 in the installed PWA: idle is quiet (0.16 s of script in 10 s), but three real flicks through Following spent 1.7 s of 9 on the main thread, frames up to 200 ms, and the work was the avatar thumbnailer finishing faces as they scrolled in: a synchronous canvas.toDataURL WebP encode per face (229 ms), the profile cache JSON.stringified twice per half-second flush (once just to measure it, up to ~2 MB with thumbnails), and a whole-page repaint per finished face that changes nothing on screen (feed rows keep the face they were admitted with). Now: the crop, resize and encode happen off the main thread (createImageBitmap with resizeWidth/Height, OffscreenCanvas.convertToBlob, FileReader), with the old canvas path as the fallback; a finished face is recorded once the page settles; scheduleRepaint holds while a finger is down or the page scrolled in the last 200 ms (at most 1.5 s), and the profile flush likewise (at most 5 s, forced on an account switch) and sizes the blob by summing rows instead of stringifying it. Tests: the create flow's heads-up / passphrase / check steps from earlier today broke every test that made a wallet by waiting for the words and clicking "Skip verification"; tools/puppeteer.js exports seedWords() and skipSeedCheck() and the 96 tests use them. room-faces and avatar-profile fail the same way on 8858591 (row.hover, scrollIntoView), before today's changes
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
to pick. "Reintroduce the seed phrase preamble telling users to write it down but don't give it a warning icon … an intermediary step before we display the seed phrase … instead of verify and skip buttons just have a next and another intermediate step that asks for just the 5th seed word and gives a few options to click and also a skip button" (Adam, 2026-11-07). Creating a wallet (front door, Accounts, signInAnother) now opens on a plain note, "Next you'll see your seed phrase: 12 words … Write them down on paper, in order … make sure no one is looking", with Show seed phrase; the words page has one Next; the check asks "Which one is word #5?" with four of the phrase's own words as buttons (a wrong one turns red and says "Not that one"), Skip, and Show the words again. The gift claim's new-wallet backup gets the same three steps in place of its warning box and typed three-word check, and a sign-in's "Write down your recovery phrase" shows its explanation first and the words behind Show seed phrase (Later still on both). createStep starts at 'intro'; pickConfirm returns { index, options, wrong }
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
Passkey, Google, Nostr. "I think we can remove 'Sign in' from all the sign in buttons since it's at the top in the heading" (Adam, 2026-11-06). frontDoorSignin takes { short } and the sign-in step asks for it; the coinos.io move page (under "or") and the sign-in sheet in Messages keep the full "Sign in with …" labels, where nothing above says it. "Migrate a coinos account" is unchanged. Tests wait for the list by its last door
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
passkey sign-in no longer goes on to make a new one. Coinos Beta's passkey sign-in failed after the account was picked — "CredMan getCredential call failed … RP ID cannot be validated" (Pixel 9, 2026-11-06). An app's association with a site runs both ways: the site's assetlinks.json names the app (it did), and the app's manifest names the site through asset_statements meta-data — which the TWA has and the webview/fcm manifests didn't. They declare it now, and the shared asset_statements string carries get_login_creds beside handle_all_urls. And passkeyLogin rolled any sign-in failure into creating a passkey, so the validation error was followed by a second, failed create dialog: only NotAllowedError (how browsers report "no passkey here", or a dismissed sheet) leads to creating one now; any other error is shown
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
spaced, and setup pages keep only the language picker under them. Adam, 2026-11-06: passkey second, below the seed phrase; "check the spacing on all the buttons, some of them seem a bit different" — the sign-in feature returned its doors in a wrapper of their own (8px apart) inside the page's 20px column, so the gaps ran 20/8/8/20. frontDoorSignin now returns them loose (externalButtons takes passkeyFirst), and the front door's lists sit in one 10px column: measured at 390px, every door 53px tall and 10–11px apart; the welcome's two choices 55px and 10px apart; the sign-in sheet in messages spaces them at its own 12px. And "on all the wizard/setup pages let's hide the footer except for the language selection": the unlock screen and every page the wizard draws (.onb) get a footer with just the language picker, as wide as the language's name; everywhere else keeps the full footer. tools/onboard-backup-test.js checks the front page's footer holds only the picker
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
|
| |
three times over. Profiled on Adam's Pixel 9 at 4x CPU (a mid-range phone), a cold launch — a fresh tab, so the wallet comes out of the device vault — spent ~850 ms of its 1.9 s to the balance stretching keys on the main thread: scrypt (N=2^15) to open the vault under its blank password (~520 ms), then PBKDF2-2048 of the same twelve words once per saved wallet to match it to its watch-only entry and twice more for the open one (the BIP84 account and, inside nostr-tools' nip06, the nostr identity). Now src/seed.js keeps each phrase's BIP-39 seed: the boot stretches the phrases it's about to open with WebCrypto's native PBKDF2 while the stores open (warmBootSeeds, alongside the IndexedDB wait it already had), and the wallet, zaps and the nostr key (NIP-06 m/44'/1237'/n'/0/0, derived from that seed — nostrKeyFromSeed) read it; a reader that comes first stretches it the old way and leaves it for the rest. A lock forgets them. The vault under the BLANK password — the default, which scrypt protects from no one, since anyone with the blob can compute scrypt('') — keeps its key beside the blob and new blank seals reuse that salt (fresh nonce each time); sealing under a real password drops it, and real passwords are stretched every time as before. Same cold launch at 4x: balance 2.2–2.9 s → ~1.5 s, scrypt and the JS PBKDF2 gone from the profile. tools/seed-cache-test.js: WebCrypto's seed = @scure/bip39's for plain, passphrased and NFKD input, warmed or cold; the NIP-06 key = nostr-tools'; a malformed phrase still refused; a blank vault opens without scrypt and reseals with the kept salt, a wrong guess fails and drops nothing, a real password keeps nothing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6Ts5Tktega8VbLg31q
|
| |
|
|
|
|
| |
to seven times on every launch. "Its so slow to load … I'm using the apk. Compared to Minibits or something its way slower" (a user, 2026-11-05): profiled on Adam's Pixel 9 over DevTools, a launch kept the main thread pegged for ~1.7 s, and 1.4 s of it was loadArkState — every arkStateNow() during the first render (balance, history, notices, the Spending figures) structured-cloned the stored state, ~1 MB on a wallet with deep coins, ~220 ms per copy, before the manager connected and its live state took over. ArkStore.peek() hands back the stored object itself (before the store opens, or on localStorage, the parse is kept until the key is next written); wallet.peekArkState() is loadArkState through it, with the misfiled-state repair still going through loadArkState and each object's owner decoded once; arkStateNow and arkWanted read through it — read-only, as they already were with the live ark.state. Same boot on the phone, live tab with the local build: main-thread busy over the first 4 s 3.7 s → 1.8 s, no pegged stretch. tools/ark-store-test.js: peek returns the same object, sees writes at once, and drops a kept localStorage parse on write
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
| |
move over, is offered the door that works: a Lightning receive into Spending. "can you help me" (a user, 2026-11-03, screenshot): 546 sats in Savings, 0 in Spending, a 175-sat invoice — the ask said "Move Savings into Spending first", and that button led to a board panel saying "Needs at least 1,000 sats in Savings" (the coinos server's board minimum), a dead end dressed as a way through. The ask now knows whether Savings can fund a move at all — the server's minimum, from the live manager or the last copy of its info seen, plus the board transaction's chain fee — and when it can't, says what Savings holds and what a move needs and offers "Receive over Lightning instead", which opens Spending's Receive page; a Lightning receive lands in Spending directly, no confirmation, and the invoice can be paid from there. The same question replaces the hardcoded "more than 1,000 sats in Savings" guess behind the pasted-invoice path (its error now carries the same sentence), the Ark send review's move door and the zap card's "Move sats into Spending" button (arkSavingsCanBoard hook), and the board offer's prefill honours the minimum before the manager is connected (it read 0 then, and prefilled 179). tools/ark-ln-savings-small-test.js: 546 sats → the receive door, no move button; 1,000 sats (the minimum, but not the fee on top) → the same; 5,000 → the move, straight to the board panel prefilled at the minimum
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
through. "Any way to stop classifying v3 notifications as spam? I've seen this a few times now" (Adam, 2026-11-03, phone: "Possible spam — Chrome detected possible spam from v3.coinos.io", Unsubscribe / Show notification): Chrome on Android scores every web notification's title and body with an on-device model before showing it, hides it behind that warning above 90 of 100, and — nine warnings in a week on a phone whose engagement with the site is under 50 — revokes the site's notification permission outright (Chromium kAutoRevokeSuspiciousNotification, enabled by default). The model is a text classifier trained on Gemini-made examples of scam notifications, and our lines were their very shape: "Payment received" 95, "Incoming payment / Someone is trying to send you money…" 99, "Payment request / An app is asking your wallet to pay…" 99, "alice is calling / Voice call. Tap to answer." 99, "New message / You have a new private message." 92, "alice / sent you a message." 95, "New reply to your post / <text>" 98, "alice reacted ❤️ / to your post…" 97. Desktop Chrome runs the same model (without the warning) and reports each score to chrome://histograms, so the lines were measured rather than guessed — ~200 candidates on Chrome 156's model: "sats" reads as money without reading as a payment scam, "payment" doesn't; a sender's name in the body of a message or reaction line tips it over, "New chat" / "<name>" doesn't; any "reply" title is 96–99; a reply's own text is out of our hands (an @name or a link in it scores 97+), so it no longer rides along; "Calling" / "<name>" is 11–29 where every "<name> is calling" is 80–99. src/notify-text.js holds every line, with the measurements; the worker, the background responder and the renewal warning draw from it. The new lines score 8–46 (the shortcode reaction 67). tools/notif-spam-score.js re-measures them in a throwaway Chrome (SystemNotifications off, so nothing reaches the desktop) and fails at 90 — run it before changing any of them. On the phone that already warned, "Show notification" then "Always allow" marks the site trusted and stops both the warning and the revocation count
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
Lightning sent to its name arrives the moment it opens. A name whose wallet lives on Second (or a custom server) can only be paid over Lightning — the registrar's float can't arkoor to a server it isn't on — and only while the wallet is open to answer the registrar's offer request; its hourly retry with a six-second window rarely met a short phone session, and manna's 52 sats failed 449 times from 2026-10-18 to 11-03. Once the offer listener is up (nwc listen(), a beat after subscribing), the names feature POSTs /listening (NIP-98, coinosv3-server 14a6dd5) and the registrar sends whatever is queued for the name at once. Once per ten minutes, and never for a wallet on the coinos server: it is paid while asleep and spends no signature on this. tools/names-listening-test.js: nothing on coinos's server, one authed call on Second, no second within ten minutes, a registrar that is down is asked again next time
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
is still sliding in. "I'm still not seeing the Post button right away when I open the feed. It should be there from the start" (Adam, 2026-11-02): measured on his phone over DevTools, the button was in the page 87 ms after the Feed tap — 4109 px down, below an 809 px screen — and in place at 562 ms. It is position: fixed, and it was rendered inside the page, whose entrance animation puts a transform on it; a transform makes fixed descendants position within the page, so the button rode at the foot of the feed card until the slide ended. The feed now hands the button and the new-posts pill to the shell (a pageFloaters hook, taken once per render), which paints them beside the page, outside anything that animates; the pill's CSS no longer depends on sitting inside the page, and its phone-width rule stays after the base one so it still rides above the button. tools/feed-fab-anim-test.js taps Feed from the wallet and samples the button every frame of the slide: on screen from the first. The direct-mount harnesses (feed-new-posts, feed-streaming, feed-bottom-nav) paint the floaters as the shell does
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
server that holds the same blob. "No avatar for karnage?" (Adam, 2026-11-02): his kind 0 names a picture and a banner on media.21media.to, and that host answers "The requested blob was not found" for both — while blossom.primal.net, the server he publishes in his kind 10063, still has them, which is why other clients show his face. A picture named by its sha256 is a Blossom blob, served under the same name by any server holding the bytes: when the thumbnail maker finds the host without it (a 404, or a host that won't answer), the blob is looked for on the person's published servers and then the public ones we know, asked together, and the first that answers stands in — the thumbnail is made from it, the profile page's big face and banner paint from it, the feed's warm-up loads it — and the find is remembered on the device so the search runs once. tools/blob-alt-test.js serves a dead host and a published server holding the copies and checks the thumbnail, the profile page and the post rows
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
beside the heart say 1 if I have 2 heart reactions?" (Adam, 2026-11-02): a thread remembers its counts between visits so that a short relay answer can't erase them, and the bar showed the memory alone — while the tally in hand, which the who-panel reads, had the second heart, brought by the feed's own count fetch before the thread was opened again (the memory is only brought up to date while the thread is on screen). The bar now shows the larger of the remembered number and the live tally, hearts, boosts and sats alike. tools/thread-like-count-test.js opens a thread by its link with one heart on the relay, comes back through the feed when there are two, and checks the thread says 2
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
relays first — the reference's own hints, ours, its author's — and from the wide net of public and archive relays only once those have come back empty, or after 1.2 s if they are slow about it. Asked all at once, as before, every quote and every reply's parent went out to a dozen relays, and in the Conversations view, where each row has a parent, half of all REQs in twenty seconds on the live app — three hundred, to 47 relays — were lookups of notes our own relays answered in the first beat. tools/relay-budget-test.js adds a far relay: the six quotes our relay holds are never asked of it, and the one only it holds is still found there
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
posts NIP-22 comments (kind 1111) on a film's IMDb page: an I tag naming the address, no e or E tag. Adam's own ("Charming and wholesome", 2026-11-02, on relay.primal.net) showed on neither his profile's Replies tab — the profile page never asked for kind 1111 at all — nor in the Conversations view, whose warm-up pass asked for posts and reposts rather than the view's own kinds, and the refresh that asks properly is skipped for half a minute after it. The profile asks for comments now, the warm-up pass asks for what the view shows, and a comment with no post to show above it carries the page it is on where "Replying to" would go — "Comment on imdb.com/title/tt32565993", linked, or the comment's own alt tag when it has one ("Comment on Sheep Detectives (2025) in DaddyTube") — in place of the bare "reply" tag. Also, a report naming a post as 'other' with the author's bare p tag beside it counted as a report of the PERSON (an 'other' e tag didn't count as naming a post), and two such from follows folded everything the author wrote; every post named now counts. tools/web-comment-test.js plays all of it against a fake relay
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
the laptop (2026-11-02, after the resume fix), an open of Following sent some 560 REQs in twenty seconds across 57 relays, over a hundred each to damus and primal, and damus answered the home IP "banned: too many rate-limit violations". Four sources, all cut: a quoted note's author (a face tapped, a reply's target) had their relay list asked for one REQ per author per relay — six quotes on screen sent eighteen — and those asks now wait 80 ms and go out as one REQ through the feed's batched fetch, which also remembers who has no list; the live subscriptions were closed and reopened at the end of every refresh pass (seventeen REQs over eight relays, twice per open) and now stay up while the relays and authors they cover are unchanged, the head they count fresh posts against read at each event; the follows' reports were fetched afresh at every reload (an in-memory clock) and now honour the clock the device saved, asking only for what came since; and every one-shot REQ was held open on our own relay until the slowest relay in the ask had answered — the library closes them all together — so the fast one carried each for up to five seconds. One-shot queries now go out per relay (queryOn, queryStreamOn, queryManyOn, the profile and inbox lookups), each closed on that relay's own EOSE, with no more than ten open on any relay at once: the rest wait their turn, and a turn that comes after the ask's own wait is skipped. tools/relay-budget-test.js runs the feed against a fast and a slow fake relay and checks each of these
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
them anything: one tiny REQ to each; a socket that answers is kept, one that doesn't is closed and redialed at once, its subscriptions refired by the library with since set to the last event each saw, and only then do the DMs resubscribe and the feed refresh. "No new notes in Following since 11:35? It's 9 pm now … it loaded more posts, it just took a while" (Adam, 2026-11-01): after hours in the background a phone's tab is thawed with every socket reading OPEN and long dead on the network, so the catch-up sent on resume vanished, and the library found a dead socket out only after its 29 s ping and that ping's 20 s timeout, then waited 10 s to redial — a minute or more before the backlog arrived through the refired subscriptions. tools/resume-wake-test.js freezes a fake relay's sockets, posts while the app "sleeps", and has the post on screen 2.7 s after resume (never within 15 s before). Also: Primal's "too many concurrent REQs" notice no longer cools the relay off for 30 s at every boot — it serves every REQ past its twenty anyway (measured 60 at once: 60 EOSEs, 12 notices), so the cool-off only lost us its answers while the relay lists, profiles and the feed all asked at once; a CLOSED with that reason still counts
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
with the file's link, instead of sitting as a dead player under "Tap to play". A post's video "won't play" (Adam, 2026-11-01): the mp4 had been deleted from Primal's blossom, which answers a 404 with no CORS headers, so the probe couldn't read the status and the box waited for a tap that loaded nothing — no client renders it (the reply under the post says as much) and no other blossom host has the hash. The player is the first to know: its original failing now tries the server's copy once (a file the browser can't decode fails the same way, and the copy plays that one), and the copy failing too marks the clip gone for a day — the line and the link in the box, its cover, play button and controls off, a short box; the next paint of the post says it at once with no request, the media warm-up and the in-view hook leave it alone, and the videos grid leaves it out as it did a 404 the probe could read. Outside the feed the box used to be hidden on an error before anything was said; it gets the line too. tools/video-gone-test.js serves the clips from a second origin without CORS, one of them a 404, and checks the line, the untouched live clip and the silent repaint
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
|
| |
frame: the Post button, the feed chips and the nav come with the page, not a beat after it. /feed is a public page, so it painted as a visitor's (no button, chips or nav) the moment the app ran, and the wallet's restore — synchronous work: the vault's scrypt on a cold start, the wallet's state, every feature's init, ~0.5 s on a laptop and more on a phone — re-painted it with the button only once it had run ("the post button takes a while to appear", Adam, 2026-10-31). Boot now tells before that work whether a wallet is about to open (a session list in the tab, a vault the last seal or unlock opened without a password — noted, since the vault alone can't say short of the scrypt being avoided — or watch-only accounts) and the feed, the nav and the chips paint for it; taps still go by the wallet actually being open, which it is by the time one lands. The wide-screen wallet column waits for the numbers. A visitor with no wallet on the device gets the visitor's page as before. tools/feed-boot-chrome-test.js boots the built app at /feed behind a session, a noted vault, an unnoted vault (the old order, and the unlock leaves the note) and as a visitor
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TFgisGKUDhdStLUvs
|
| |
|
|
|
|
| |
a long-time nostr user signed in with his key for the first time; our relays had never seen his kind 0 (it lived on ditto, snort and oxtr), the 3 s lookup came back empty, and the payment-address publish went out as {lud16, nip05} alone: name, picture and banner gone from every relay (nicstazzema, shown as npub1yk2lkr7). An empty quick lookup now widens to the same search the follow list got after its own overwrite (indexes, archives, the key's own write relays, Primal's signed copy) before anything is written, once per key per session so a brand-new key's first publish waits once. For anyone already hit, the profile editor offers "Look for older copies": a kind 0 from a history-keeping relay fills only the fields still empty in the editor, and Save publishes it. tools/kind0-overwrite-test.js plays the incident against a fake relay network where only an archive holds the profile
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
| |
pays a Lightning invoice from the coins nearest expiry first, so fewer of them come up for a paid renewal (Stacker News request, 2026-10-29). Lightning sends only: the ASP takes those coins, whereas an Ark-to-Ark send would hand the recipient your short timelocks. The custodial default keeps the smallest coin that covers the payment (expiry costs nothing there), a coin past its timelock still goes last, and a pile of old crumbs that would breach the input cap falls back to largest-first. The closed-app NWC worker follows the mirrored tier the same way
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6VLgQdujavxPsfU7A9
|
| |
|
|
|
|
|
| |
longer means a new connection: "Reconnect your signer" keeps trying the stored session (at once, every 10 s, and on every return to the tab) and closes itself when the signer answers. Before, the screen made one try, or none if a try had failed in the last 20 s (it nearly always had: that failure is what opened it), so the only way out was a fresh nostrconnect, while the stored session answered in 0.7 s whenever anyone asked (Adam's phone, 2026-10-29). A try made while the tab was hidden (frozen on a phone) no longer counts as a failure, a closed signer ("not open anymore") is treated as dead and replaced, and Cordn follows a replaced signer instead of signing with the closed one
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TUeqTyZB8eXLGymUY
|
| |
|
|
|
|
|
| |
who wears what (/hats/worn, a couple of dozen heads, kept per device and refreshed every two minutes), so a face seen for the first time on a message page gets its hat at once instead of after a 400 ms batch and a round trip
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TUeqTyZB8eXLGymUY
|
| |
|
|
|
|
|
| |
ahead, on the quarter hour) and turns Post into Schedule. The note is signed now with that created_at, so a remote signer asks while you're there, and handed to the notifier, which publishes it at that time; nothing reaches a relay before then. What's waiting shows as "Scheduled (n)" under the composer and on your profile, each with Cancel (a kind 5 to the notifier, so it never goes out); one cancelled on another device leaves the list at the next status check
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMan6TUeqTyZB8eXLGymUY
|
| |
|
|
|
|
| |
signed over the gift's code (#f=…, beside the note), so the claim screen shows "From <name>" and claiming follows them. A signature that doesn't match the code names nobody, so a link can't be passed on as someone else's gift. "Show it's from me" is ticked by default when making a gift, and unticking it is remembered; the gift's record keeps the sender, so View and Copy link again keep it too
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
subscription ran out after 19 payments with the app closed, "key window exhausted"), derived once per account and memoized; 60 receive preimages (was 15). When the worker can't act on a request it still wakes the user, and now leaves a "wait, open the app" error with the notifier that goes out only if nobody answers within 20 s, so the other app hears something instead of silence and a sibling's real payment is never shouted over. A request older than its two-minute window (a push that waited at APNs) is ignored quietly instead of showing "Payment request"
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
| |
there; the "[0:03] " prefix other clients read is left out of the words (and out of the marker's line on the bar)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
audio) instead of floating above it, and a reply made at a moment shows as its author's face, not ↩ (the pager's bar and the pop-up too)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
hardware keyboard's Enter still sends
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
| |
the "replying" flag, and the box stays up while a draft has text, so with anything typed it did nothing. Attachments the draft carried are dropped with it
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
| |
on the Lightning line too, and Save in the profile editor repoints it in the kind 0 (a same-domain name that no longer answers); the background heal steps aside for an interactive signer (Amber) instead of waiting forever in the kind-0 queue
Jarryd's page still showed [email protected] over ⚡ [email protected]: the header only substituted the NIP-05 line, and his kind 0 was never republished — the heal of ce96364 needs a background signature, which an Amber sign-in can't give, and Save left a lud16 that wasn't the old address alone.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
| |
(Magnitude Relay, built for gift-wrapped DMs) leads the DM and group-chat relays, with relay.coinos.io and primal still read and written. A wallet key's inbox list that only ever held our old defaults is updated to name it; feeds and zaps never ask it (it takes messages, not posts)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
| |
(ellipsized when it must) and the "in progress" tag sits beside the time, not in the title row where it broke the name mid-word in the narrow wallet column
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
| |
editable field beside @domain with Copy address and Release; typing a different name swaps those for the "old address stops working" line, Cancel and "Change to <name>"
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
fee)"); an on-chain gift still shows its network fee
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
the amount for whoever opens it. It rides the link after the # (#m=…), so no server sees it; the gift's record keeps it, so Copy link again and View include it; with a note the QR is byte mode
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
into) instead of leaving a "This post was deleted by its author" row; threads still keep the deleted post's place
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
the first-run steps: pick a username, then a picture and a cover ("Not now" lands in the account the gift arrived in). Also fixes Verify backup on the gift screen, which crashed (pickConfirm wasn't passed to the gifts feature)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
held in two coins (dust padding split the sender's 886 into 595 + 291) showed as two receives. Receives stamp their mailbox message and the history folds a message's coins together; older rows fold on the same delivery instant
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
player's own controls (its seek bar) stay off, the middle play/pause shows whenever it's paused (touch too), and the sound button stays to turn it down again
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|
| |
|
|
|
|
|
| |
or pauses in place, a click on a playing clip pauses it (a paused one still opens the pager), and a tall clip is held to three quarters of a computer screen with its box narrowed to the clip's shape
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QjVvE4YAcK3AGwV7kZXeN4
|