| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
| |
name="theme-color"> existed, so the bar colour stayed #15171a in light mode (a dark status bar over a light page, and the Android bars of the installed web app). It now runs after the PWA head, and light pages get #eef0f3
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MgAFLnkF7KrdhuyCLMNgtL
|
| |
|
|
|
|
| |
gone from build.js and the README (the cs deploy hook now passes COINOS_TARGET; the F-Droid 1.9 recipe keeps HAL_NO_SW because that tag's own build.js is what builds it, and the recipe says so for the next tag)
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
| |
icon: it was a copy of the full logo, so Chrome's home-screen Coinos showed an extra outer ring
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
| |
(self-hosted, Unsplash) cycled with arrows or a swipe, or upload your own; untouched, each pubkey gets its own default, published as the kind-0 banner beside the punk
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
| |
still accepted); silent-payment leftovers removed
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01YRC6aKhwAud231ot9FPmhi
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The server renders /nevent1 and /note1 pages with the thread inlined
(post, root, replies, kind 0s) and Open Graph tags; the client seeds its
thread cache from it and opens the thread on the first paint. Without
it, a pending frame holds the screen instead of the front door. Profile
lookups ask our relays and the profile indexes at once, streamed,
instead of one after the other behind five-second waits. A thread
opened straight from a link has no Back button.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
| |
With no files beside it, relative punks/, locales/, nip46.js and jsqr.js
looked on the disk. From file:// they now come from v3.coinos.io (images
and scripts need no CORS; /locales/ now sends Access-Control-Allow-Origin
on the server), and standalone.html carries Public Sans inline.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VP5Ljqk7EYcm5QQAbzDwog
|
| |
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
host.
No public Blossom host transcodes (nostr.build's and primal's BUD-05 /media
return the same bitrate), so a 15-20 Mbps phone clip stalled on playback.
src/video-compress.js re-encodes on the device with WebCodecs via
Mediabunny (a lazily loaded chunk, left out of the SW precache and the
standalone file): H.264, short edge 720, ~2.5 Mbps, moov first, audio
passed through, rotation baked in. Anything short of a clear win uploads
the original. A toast counts the shrinking down.
Media hosts: blossom.nostr.build first, nostr.download second (it was
unreachable for hours); a HEAD probe skips a host that doesn't answer
within 5 s instead of waiting out the whole upload timeout.
video-compress-test: 1080p 20 Mbps 5 s (landscape and rotated portrait) →
1.6 MB H.264 720p 2.49 Mbps with audio, moov first, in 2-3 s.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Du1qT1wbkYwYK6zkGH79Rp
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
colour follows the app's.
One dark colour for both bars gave white navigation icons over a light page
under gesture navigation. The wrapper (1.9, versionCode 10) now uses the
page background in light mode and the dark theme colour in dark mode, for
the status bar and the navigation bar alike. The page updates its
theme-color meta when its own theme is applied, so a PWA's status bar and
the wrapper's follow the same switch.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| |
|
|
|
|
|
|
|
| |
One variable-weight woff2 each for upright and italic (41 KB + 45 KB, OFL),
shipped with the build and precached by the service worker; the system stack
paints first and stays when the file never arrives.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VhUKyXKtsJ67QAFfb7QR9D
|
| | |
|
| |
|
|
|
|
| |
the registry, not features/index.js.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Android builds so far were shells around the live site: a verified
APK proved nothing about the code that ran. The new `fdroid` flavor
(io.coinos.fdroid) shares the WebView shell and carries the built web
app in its assets. The page still lives at the v3.coinos.io origin — same
storage, links and gift URLs — but every file under it is answered from
the APK (Assets.java); only the site's live API paths reach the network,
and the bundled page registers no service worker (HAL_NO_SW=1).
Two clean unsigned builds give the same APK. COINOS_UNSIGNED=1 builds
without the keystore; Bun is pinned via packageManager; the Gradle wrapper
properties pin 8.12. fdroid/io.coinos.fdroid.yml is the fdroiddata recipe
(checksum-pinned Bun in sudo, prebuild web build, scandelete, our signing
key allowed for the Binaries match) and docs/REPRODUCIBLE.md the how-to.
Verified on a phone: the bundled build cold-starts in airplane mode.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JuwQqNWtDJcxPWtEm9134z
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
The push notifier now watches kind 1 for the user's pubkey beside DMs and
zap receipts, and pushes who, what, and an excerpt. The worker shows
"New reply to your post" or "You were mentioned" with the text, and a tap
lands in the Notifications list — as ?open=notifs when a window has to
be opened, or as a worker message when one already is. It has its own
switch, "Replies and mentions on nostr", in Settings → Notifications,
on by default like payments and DMs.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QYJfZ4A1TDCEY4Foq3CApR
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The reply box gets the same paperclip the post composer has: one uploader
now, handing the URL to whichever draft asked for it, and the reply's own
imeta tag describing what it carries.
And the UnifiedPush build is graphene.apk — what people will actually call
it, and short enough to read down a phone line.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The TWA can't. Chrome renders the site, so the wallet's keys live in Chrome's
storage for its own origin, and nothing outside Chrome can reach them — a
UnifiedPush message could raise a notification and no more. On a phone with
Google Play Services none of this matters; without it, background answering
was simply unavailable.
So: a second flavour, io.coinos.webview, "Coinos (UnifiedPush)". The wallet
runs in a WebView this app owns, which means a push can start it off-screen
and let it answer for itself. WakeService loads it with ?wake=<payload> into
the same storage the visible app uses, the page subscribes and answers
whatever the payload was about, then calls CoinosHost.done() so the service
stops — capped at 25 seconds from the page and 30 from the service, because
nothing here is allowed to sit in the background costing battery. Its
foreground notification says what it's doing rather than apologising for
existing.
The WebView carries what the wallet actually needs: DOM storage and
IndexedDB, the camera for the QR scanner (granted only once Android has
granted it to us), a file chooser for attaching pictures, and links to
anywhere but coinos.io handed to a real browser.
The trade is named in the code and worth naming here: a separate app is a
separate wallet. Its storage is its own, so signing in there is signing in
again. That's why this is a second build rather than a replacement — the TWA
is untouched, still the better browser for anyone whose phone has push.
The shared push receiver now asks the system for whatever front door this
build has, and asks a per-flavour Wake class whether the wallet can be woken
at all — no flavour checks in the middle of it. Both APKs build and ship:
coinos.apk as before, coinos-unifiedpush.apk beside it.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The art is 240x240 (20KB each, 1.5MB for the set) because that's what a punk
published as someone's nostr picture should be — but the circles the app
draws are 18 to 64px, so a new wallet with nothing cached was fetching eight
times the pixels it could show, one request per face.
punks-sm carries a 128px copy of each: 4KB instead of 20KB, indistinguishable
at 64px. Small circles and the wizard's picker tiles load those; only the 64px
profile avatar and the value the wizard publishes stay full-size.
The bigger win is that a punk picture is our own art wherever it turns up.
Everyone who keeps the default publishes v3.coinos.io/punks/N.webp as their
nostr picture, so those faces now paint straight off disk at the size being
drawn — no request, no thumbnail to make, and no broken circle when the
legacy coinos.io copies 502 (which they do for a third of the set).
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
|
| |
|
|
|
|
|
| |
gone (no sp in the feature registry) but its mentions lingered: the zapstore description now says plain "full BIP84 bitcoin wallet" (fresh kind 32267 republished, verified clean on the relay), the README loses the sp feature section and its mentions in the E2E-suite and dev-proxy lists, the dead receiveSpTab i18n key is removed, and two stale comments (app.js seams, build.js HAL_FEATURES list) stop naming it. Deliberately KEPT: silentPaymentNote — the send review still labels an output paying someone else's sp1 address, which remains supported
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
whole reason to exist is intent registration — lightning:/bitcoin:/nostr:/lnurl:/lnurlw:/lnurlp: VIEW intents and the same schemes via NFC NDEF dispatch (bolt cards, address stickers; NFC declared required=false), plus verified https links to the site. LauncherActivity forwards a scheme URI into the web app as ?u=<encoded>; the web app reads it BEFORE features construct — a nostr npub/nprofile rewrites itself onto the existing /npub profile deep link, payment URIs stash and dispatch through handleScanned (the QR path) once a wallet is open, and the param is stripped from the URL either way. assetlinks.json (static/.well-known/, copied by build.js) vouches for the signing cert so the TWA runs URL-bar-less; the built APK is served at /coinos.apk for sideloading. Signing keystore lives in android/ GITIGNORED (keystore.properties beside it) — the assetlinks fingerprint pins it, so losing it means re-shipping assetlinks too. Verified: headless ?u= routing (lightning → Send with invoice filled + URL cleaned; nostr → profile page), APK installed on a real phone via adb, schemes registered (chooser appears where other wallets also claim lightning:)
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
|
| |
|
|
|
|
|
| |
synchronous verifyEvent (a schnorr check per event, ~560ms on a relay backlog — the profiled carousel-stutter cause) is switched off, and every event is now gated through a Web Worker before any caller sees it. Nothing unverified is ever trusted — the worker runs the same nostr-tools verifyEvent, just on another thread, with a synchronous fallback if the worker is unavailable (standalone file://) or stalls. Bundled to dist/verify-worker.js, precached in the SW
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
|
|
| |
emits standalone.html (~950KB, everything inlined, boots from a bare file:// with no server), How it works grows a 'Keep a copy' card linking the download, and the tab strip's phantom double-tap dies — the pointerdown's paired click was arriving after the render had already swapped in fresh handlers, and with toggling tabs that ghost click un-did the tap it belonged to. The timestamp guard now lives on the DOM node, which survives the morph
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
|
| |
|
|
|
| |
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
|
| |
|
|
|
|
|
| |
one, present from the static boot shell's very first paint so it never pops in
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
|
| |
|
|
|
|
|
| |
manifest, service worker, touch icon) are root-absolute — a page served from /ag/… was resolving them into the subpath, getting index.html back, and dying silently on module MIME checking before a single render
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
|
| |
|
|
|
|
|
| |
shared chunks alongside, 61KB of gifts/NWC/hats deferred past first paint into slots that keep their hook seniority), the lazy scripts wear IIFE coats so their names can't collide with the page's — which is what silently broke the remote-signer button — nip98 moves out of the login module so minimal builds stop hauling backup relays, and the smoke test learns the front door's new steps while the regtest stack rises from its IBD nap
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
|
| |
|
|
|
|
|
| |
static boot shell baked in — brand row, and the profile frame when the path names one — while the 789KB bundle moves to a content-addressed app-<hash>.js the browser keeps (a repeat visit re-downloads kilobytes, not the app); the nostr-tools barrel import goes subpath, and the NIP-46 signer client ships as its own lazy file fetched on first bunker login
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
|
| |
|
|
|
|
|
| |
knows, and a link to it — the build token stays where it belongs, inside the service worker
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
|
| |
|
|
|
|
|
| |
esplora, sp, lnurl, well-known — go straight to network, never the cache that froze a refresh on a stale unconfirmed
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
|
| |
|
|
|
|
|
| |
HAL_TARGET=staging stamps the brand, renames the PWA, and starts fresh wallets on Mutinynet — one master, no diverging branch
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
|
| |
|
|
|
|
|
| |
avatar everywhere it appears, from 21-sat beanie to 2.1M-sat top hat; the crown stays on the founder
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
|
| |
|
|
|
|
|
| |
honours server-side — and the status bar gets a real coinos badge instead of a black blob
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_014tE4U5nEjGTQy8PwYqamfv
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The recipient candidate list was hiding under the on-screen keyboard:
scroll the field to the top on focus and when fresh candidates land,
give the list scroll room to get there, and let the keyboard resize the
viewport (interactive-widget) so the scroll has somewhere to go.
The wizard now shows whenever the device has no wallet — the one-time
onboarded flag is gone; deleting every wallet brings the wizard back.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The app is inlined in the page, so when a new service worker took
control the page reloaded — otherwise it would keep running the old
build. But navigations are network-first: the refresh that triggered
the update had already fetched the new page, so the reload landed a
second or two later as an unasked-for second refresh.
The page and the worker now carry the same build token and the page
asks which build took over, reloading only on a real mismatch. The
token is hashed before substitution, since a page cannot contain a
hash of itself.
tools/sw-reload-test.js pins all three cases against a swappable
server: a first visit doesn't reload itself, a refresh after a deploy
is the only refresh, and a page left sitting open across a deploy is
still reloaded — exactly once. The previous build takes two loads for
one manual refresh; this one takes one.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
sw.js threw "NWC_WAKE_FALLBACK is not defined" on every load: the
template substitution used String.replace with a string pattern, which
replaces only the FIRST match — and that was the mention inside the
comment above, leaving the real placeholder literal in the output
(parsed as a block plus a bare identifier). replaceAll fixes it, the
comment no longer spells the token, and {{VERSION}} got the same
treatment. Both build profiles now emit a service worker that parses
and activates cleanly.
The relay noise: a browser logs failed WebSocket connections itself and
JS cannot suppress that line, so the fix is to stop making the doomed
connection. nostr.js now probes each new relay once, remembers
unreachable ones for ten minutes, and filters them out of every
subscribe/query/publish (never down to an empty set). Publish
rejections that look like connection failures mark the relay sick
rather than logging a scary "refused" line. relay.damus.io — down all
week — also leaves the profile relay list.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
coinos.io serves 502 for a third of the punk set (both the numbered and
hash endpoints), which is why a default avatar could come up blank. The
64 webps now ship with the app (53 salvaged, 11 gaps filled with
donors) and every reference is same-origin. Skipping the avatar step
publishes the deterministic punk as the kind-0 picture — your face is
the same on every nostr client. Success screen: checkmark now sits
below the title, bigger.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The app already presents as Coinos; this finishes the job in the repo:
package name, README, GitHub link, code comments, and the stale locale
lines that still explained the old "Hal Wallet" name (they fall back to
the English tribute line). Kept: the hal-nwc-pouch IndexedDB name (an
upgrade-compat identifier), the halwallet.app domain in the BIP-353
names feature (live infrastructure), and the Hal Finney tribute itself.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Implements the Concord protocol (CORD-01..04) — private streams, the
control-plane fold, guestbook membership, owner-rooted authority — and
ships a built-in "coinos" community founded by Adam's npub (owner/admin).
Messages sign with the nostr login identity when a signer is live, else
the wallet's NIP-06 key; all encryption rides derived stream keys so
remote signers only ever sign seals.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Consistency fix with teeth: logging in with a pasted key derived one
wallet while an extension login for the SAME identity found no backup
and generated a different one. Now every signer kind runs one flow —
look for the published association first (that's where the money is),
then derive (key in hand) or generate (signer only) — and a derived
seed is published too. That costs nothing, since the derivation is
public and anyone holding the key could compute it anyway, and it makes
the identity open the same wallet however you log in.
Registrar auth is now NIP-98 (HTTP Auth, kind 27235) instead of a
homemade envelope: the signature binds the URL, method and body hash,
so a captured header can't be replayed elsewhere. NIP-78 already
covered the encrypted association; NIP-26 delegation is deprecated, so
owner→manager nomination lives in the registrar's own model: an
npub-shaped name may only be claimed by that identity, which nominates
the wallet key to keep the record updated afterwards (the wallet key is
the only one always available — offline, in the worker).
Payment addresses therefore default to the user's REAL npub in all
three login methods, falling back to the wallet's own identity when no
Nostr account is linked.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three ways in: a NIP-07 browser extension, a NIP-46 remote signer
(Amber, Amethyst — paste its bunker:// address), or a pasted
nsec/hex key that lives in memory for the session and is never stored.
How an account gets a wallet depends on what we can see, and the two
cases are genuinely different:
* We hold the key (pasted nsec): the seed is DERIVED from it —
sha256("coinos:wallet-seed:v1" | privkey) as BIP-39 entropy. The
same Nostr key always opens the same wallet, on any device, with
nothing stored anywhere.
* We only have a signer: deriving is impossible (we never see the
key, and neither signatures nor NIP-44 ciphertexts reproduce), so
the seed is ASSOCIATED instead — generated once and published as a
replaceable event encrypted to the user's own key, which only their
signer can decrypt. Later logins find and decrypt it.
The second path means the Nostr key can recover the money; the UI says
so before publishing anything. Linking an already-open wallet uses the
same publish path and refuses to overwrite an account's existing wallet.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
| |
Icons, favicon, and PWA assets come from the coinos brand set; the
accent goes monochrome (black in light mode, white in dark, with a
proper on-accent text variable so buttons stay legible both ways) and
the inline bitcoin mark inherits the text color.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The wallet is the next version of coinos, deployed at v3.coinos.io
(same build as halwallet.app) ahead of an eventual migration of
coinos.io itself. App name, manifest, notifications, and the NWC alias
all say Coinos; the Hal Finney tribute stays.
The registrar is multi-domain (per-domain Cloudflare zones; existing
halwallet.app names keep working) and new claims default to @coinos.io.
Names belonging to existing coinos users are reserved for them — the
registrar checks the live coinos API before granting a claim — until
the migration gives those users a way to claim their own.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Receiving: a Settings card claims a username, signed with the wallet's
nostr key; the new names registrar (names/server.js, deployed at
names.halwallet.app) enforces first-come ownership by that key and
mirrors the claim into a DNSSEC-signed TXT record at
<name>.user._bitcoin-payment.halwallet.app whose BIP-21 URI carries the
wallet's reusable ark address. The record re-registers itself if the
ark address ever changes, and a collapsible hint shows the single CNAME
a user adds to serve the same name from their own domain (BIP-353
supports delegation when both zones are DNSSEC-signed).
Sending: a pasted name@domain resolves over DNS first — two independent
DoH resolvers must agree and both report the DNSSEC AD flag (full
client-side chain validation is the stated follow-up) — an ark=
instruction pays natively, an on-chain path fills the send form, and a
name with no DNS record falls back to the Lightning-address zap flow.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Dropped: silent payments (feature, wallet chains, scan worker, indexer
config), Boltz submarine/reverse swaps (feature + swap.js + tools), the
third-party ark swap bridge (config, client flows, settings card — the
ASP's own Lightning send is the one path now), and the Consolidate
coins settings action.
The NWC pouch is gone with them: background answering now spends the
WHOLE ark balance. The IndexedDB record mirrors the real manager state
plus the chain-3 keys for its live coins and a change-index window; the
worker pays through the ASP exactly like the open app, and the app
folds the worker's spends, change, and key allocations back in on
connect (additive merge + reconcile against the server). A leftover
chain-6 pouch from the short-lived pouch era is swept home
automatically using the keys stored in its own record. The stated
tradeoff: same-origin script access now reaches the ark balance, not a
carve-out — the ark balance is walking-around money, the on-chain
wallet's keys are never mirrored, and per-connection NWC budgets remain
the enforced ceiling.
Bundle: 726KB -> 602KB.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The no-tap half of background wallet-connect. The push payload carries
the (encrypted, client-signed) request event; a bundled service-worker
responder (src/sw-nwc.js -> nwc-respond.js) finds the matching pouch in
IndexedDB, decrypts with the connection's service key, and answers
get_info/get_balance/list_transactions from pouch state. pay_invoice
pays through the swap bridge with an ArkManager running over the
pouch's chain-6 key window (one HTLC lock, one HTTP swap), enforcing
per-payment and daily budgets from the pouch's own spend log, checking
the notifier's /answered endpoint so it never double-pays or shouts
over an open device, and persisting the swap before handing it off so
the app can refund a crashed flight. Anything it can't handle falls
back to the wake-the-user notification.
The notifier grows /publish (verified 23195s only, rate-limited) as the
worker's reply pipe and /answered for race avoidance. The app gains
pouch funding (mailbox-less arkoor self-send whose recipient bytes we
keep), emptying, spend/budget reconciliation on open, and refund
recovery with the pouch's own keys.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Step 3, first half. Android Chrome freezes backgrounded PWAs and closes their
relay sockets, so a phone with hal "open" answers nothing — which is what made
Amethyst spin. Push is the only way to reach a frozen app.
- service worker gains a `push` handler. It deliberately does NOT spend: if a
window is open it nudges it to re-listen, otherwise it raises a notification
that opens hal on tap. Putting a spend path in the worker is the remaining
half and wants its own testing.
- hal-side: request permission, subscribe with the notifier's VAPID key, and
register its service pubkeys with nwcpush.coinos.io. Turning it off
deregisters, unsubscribes, and clears the pouch — no reason to leave
spendable keys where a worker can read them if nothing will.
- a Settings toggle, and the ark feature exposes the ASP context the pouch
record needs.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
build.js swaps src/features/index.js for a generated module listing only the
features in its own ALL_FEATURES registry, so registering nwc in index.js
alone was not enough: its i18n strings shipped (they live elsewhere) but its
code never entered the bundle, and the settings card silently never rendered.
- add nwc to build.js ALL_FEATURES, after ark since it drives ark's headless
pay/balance seam
- tools/feature-registry-check.js asserts the two lists agree on membership
AND on construction order, since hook precedence depends on the order
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Send a Lightning zap to a nostr npub, a lightning address (name@domain),
or an lnurl. New src/lnurl.js does the resolution half — target → LNURL-pay
params → signed kind-9734 zap request → callback → bolt11 — and the new
zaps feature (src/features/zaps.js) drives the amount/comment UI, then hands
the invoice to the existing Boltz submarine-swap flow via a new
swaps.startLnPay hook, so there's one tested pay/review/confirm/success path.
Routing: a bolt11 is still caught by swaps; an npub still prefers an instant
free Ark zap when Ark can serve it, and only falls through to a Lightning
zap otherwise — ark's "no Ark address" screen also offers an explicit
"Zap over Lightning" fallback (gated on the zaps feature's canLnZap). A real
zap receipt is attributed only when the server advertises allowsNostr and we
know the recipient's pubkey; otherwise it degrades to a plain LNURL-pay.
Nostr seam gains nostrSign (finalize without publishing), nostrProfile
(kind-0 with lud16/lud06), and nostrRelays. Lightning addresses are
debounced in matchSendText since their regex matches mid-typing.
Verified: unit tests for parse/encode/zap-request; a live resolve→signed
9734→payable mainnet bolt11 against coinos; and an in-browser DOM smoke
(zap form + LNURL params over CORS + allowsNostr hint + handoff).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
layout
- Gifts now sync across devices: gift records + reserved/reclaimed coin
sets ride the encrypted snapshot as a merge-safe extension (like ark),
and every gift state change (create/reclaim/revoke) triggers a
saveCache so it actually publishes. A gift made on one device now
shows on the others.
- Cap the published snapshot at ~40KB JSON (relay rejects >64KB events,
nip44 expands ~1.5x): trim oldest on-chain txs when oversized; another
device backfills via rescan. Typical wallets untouched.
- Service worker: register with updateViaCache:'none' so a new build is
detected immediately (not held by the 4h HTTP cache), and reload once
on controllerchange so the fresh app runs without a manual second
refresh.
- Gift detail: View/Reclaim share a row, Revoke on its own full-width
line so 'Revoke now' stops wrapping.
Co-Authored-By: Claude Fable 5 <[email protected]>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
wallet.js drops from 2,374 to ~1,970 lines, holding only BIP84 on-chain
logic plus generic feature seams. New seams added this pass:
registerBalanceExtra extra confirmed/pending buckets
registerInputProvider extra spendable inputs for buildTx
registerSendPreparer destinations needing post-selection derivation
registerInputSigner non-BIP84 input signing
registerHistoryProvider / registerScanHook / registerRealtimeHook /
registerLoadHook / registerCacheExtension / registerCacheSavedHook
src/features/sp-wallet.js: the whole BIP-352 engine — key derivation,
worker-accelerated indexer scanning, the live indexer WebSocket, SP
outputs as spendable inputs, sp1 send preparation, and per-output-key
signing — installs via those seams (installSpWallet).
src/features/sync.js: nostr identity + encrypted cross-device state sync
plus its Settings card, installed via load/cache-saved/realtime hooks.
profileChip/nostrProfile move here-adjacent (into gifts, the only other
nostr consumer), so core app.js no longer imports nostr at all.
buildTx/sign are now feature-agnostic (input providers + preparers +
signers); _recomputeBalance* use the balance-extra registry, so a core
build with no SP no longer calls a missing spBalance() (that regression
dropped the minimal build into offline mode — caught by the smoke test).
New coverage: tools/sp-test.js drives two browser "devices" through an
SP send, indexer discovery, and an SP-input spend on the ark regtest
chain (needs tools/sp-indexer.js pointed at bitcoind:18543). Minimal
smoke now also asserts no SP/nostr code ships.
Minimal profile: 314KB, down from 412KB (full: 580KB). All suites green:
minimal smoke, sp-test, ark UI, gift lifecycle.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QFAcD4V284DvYZZHP7Rxxy
|