1
0
forked from GRIN/grim
Commit Graph

50 Commits

Author SHA1 Message Date
2ro b74d7c1937 goblin: date the home news panel (ISO 8601) and guard title overflow
Show the article date first in the Home news card, formatted YYYY-MM-DD in
UTC (never a US M/D/Y, day only, no time). The date is sourced from the
NIP-23 published_at tag when present, else the 30023 event created_at; a new
optional published_at field on NewsItem threads it through the store and the
news handler.

Guard the title against clipping two ways: a hard NEWS_TITLE_MAX_CHARS cap
(48) that ellipsizes an over-long title predictably, plus measured
shrink-to-fit that steps the semibold font from 16pt down to a 12pt floor so
the title stays on one line within the card width on a 390px phone, backed by
truncate for the pathological narrow case.
2026-07-05 06:34:07 -04:00
2ro 2f7bf1abde goblin: fix activity row amount overlap and truncation target
The recent-activity rows had two layout faults visible on the 390-wide
home surface.

The transaction amount collided with the subtitle text. Root cause was
layout, not position: the title/subtitle column used truncate() against
the full available width, which included the space the right-aligned
amount needed, so the text stretched under the amount and the amount was
pushed on top of it. Reserve the amount as its own right-hand column
first, then bound the text column to the width that is left. Centered
against the two-line stack, the amount now lands between the title and
subtitle lines with clear space above and below. Row height goes 60 to
64 for breathing room.

The wrong subtitle field was being clipped. The subtitle was one
"note · date/time" string truncated at the end, so the date and time
(the part the row exists to show) lost its seconds first. Split the
subtitle: the date/time tail is pinned right and never clipped, and the
note takes the remaining width and gets the ellipsis.

No new user-facing strings. Picker rows reuse the same widget with an
empty tail, so their npub/tag subtitle still truncates as before.
2026-07-05 06:34:07 -04:00
2ro bd351e6a00 harden(nostr): cap relay ws frames and zeroize conversation keys
Two self-contained hardening measures:

- WebSocket frame cap: the Tor relay transport dialed every relay with
  tungstenite's default 64 MiB message / 16 MiB frame ceiling. Pass a
  WebSocketConfig capping both to 4 MiB via client_async_tls_with_config, so
  a hostile or buggy relay can't stream a giant frame into wallet memory. The
  pool only requires max_message_length >= 128 KiB and the wallet's own events
  are far smaller, so 4 MiB keeps ample headroom.

- Secret zeroization: the raw NIP-44 v3 ECDH conversation keys in wrapv3.rs
  are now Zeroizing<[u8; 32]> so they are scrubbed from memory on drop instead
  of lingering. zeroize (already a transitive dep) is pulled in directly.

Adds a ws-config test asserting the caps sit below the tungstenite defaults
and above the pool minimum.
2026-07-05 06:34:07 -04:00
2ro 9ae6117770 fix(send): show the proof-notify target on the review screen
With proof mode on, a notify= npub on the scanned URI makes the wallet
gift-wrap the full signed payment proof (buyer sender address + kernel) to
that key at finalize, but the review screen never showed the target, a
hidden watcher the payer never saw or consented to.

The proof row now renders a second line, Proof shared with: npub1abc...xyz,
whenever a notify target is present, so the recipient is seen and chosen: if
you scanned it, you asked for it. Display only; nothing about what gets sent
changes. Adds the goblin.send.row_proof_shared key in all six locales.
2026-07-05 06:34:07 -04:00
2ro 34315c0a6c fix(payuri): reject non-ASCII and oversized scanned amounts
A crafted pay-URI amount with a multibyte UTF-8 char (e.g. amount=0.EEEE)
reached grin_core's amount_from_hr_string, which slices the fractional tail
at a fixed byte index and panics on a non-char-boundary. The scan/deep-link
thread has no catch_unwind, so a single scanned QR or opened link could crash
the wallet. validate_amount now rejects any non-ASCII amount up front (Grin
amounts are only [0-9.]), degrading to manual entry.

Also cap the whole-GRIN part at 1e9 before the grin parse, below the u64
overflow point of grins * GRIN_BASE. Without this a giant amount wraps in
release to a small atomic value that is what actually gets dispatched while
the review screen still shows the giant figure.

Adds regression tests for both (multibyte to None no panic; over-cap to None).
2026-07-05 06:34:07 -04:00
2ro aae9dc8be3 Goblin: generic browser User-Agent for Tor HTTP, drop duplicate UA
Replace the "goblin-wallet" User-Agent on every Tor-carried request with a
common desktop-browser string so the wallet's traffic is not trivially
classifiable as Goblin at the destination. The default lives in one const in
the Tor client and is applied once per request; price.rs no longer passes its
own User-Agent (which sent the header twice). Also fix a stale nip05 doc
comment ("Nym mixnet" -> "Tor"). No transport change: the update check stays
on its existing clearnet path for GRIM parity.
2026-07-05 06:34:07 -04:00
2ro 16463253c1 Goblin: publish the "payment sent" receipt at dispatch, not finalize
Closes the buyer's double-send window. When a Nostr-rail send carries
order context, the plain kind-17 status=sent receipt used to publish at
finalize alongside the encrypted proof. With an offline merchant,
finalize can be hours away, so the buyer stared at a scannable QR for a
payment already made: maximum double-send temptation.

The receipt now publishes at S1 dispatch, the same moment the payment
envelope is accepted by a relay and the wallet UI flips to "sent",
through the same crash-safe reconcile queue. The event shape is
unchanged (same tags, kind, content); only the timing moves earlier, so
magick's status=sent matcher is unaffected.

The encrypted PROOF delivery stays at finalize (the proof does not exist
before then). Idempotency: a new receipt_sent flag on TxNostrMeta gates
the receipt to exactly one publish per tx, so finalize never duplicates
it. deliver_proof no longer touches the receipt; the reconcile pass gets
an independent receipt-retry path for the crash/offline case.

Decision logic is factored into pure predicates (receipt_due_at_dispatch,
receipt_retry_due, proof_delivery_due) used by the live paths and covered
by three tests: dispatch-publish decision, no-duplicate-at-finalize, and
proof-still-at-finalize. Full lib + i18n drift green.
2026-07-05 06:34:07 -04:00
2ro 69511bac0f README: language-aware news, open-to-pay links, proofs on request
Build 138 surface: the Home news panel now renders in the wallet's language
with an English fallback; goblin:/nostr: pay links and scanned checkout QRs open
straight to a prefilled review screen; and payments can include a native Grin
payment proof when the request asks for one, off by default and shown on review.
2026-07-05 05:31:55 -04:00
2ro 923be3a28a Goblin: fetch fiat rate live on view, kill the 48h price cache
The fiat subline now fetches the CURRENT rate over Tor only while the
balance is actually on screen, and never paints a stale number as if it
were live. No disk cache, no background timer: an idle or payment-
listening wallet never polls (battery).

- price.rs: in-session rate held for a 3-minute freshness window; viewing
  a stale/missing rate kicks a live refetch. New RateState (Fresh/Loading/
  Unavailable) replaces the Option<f64>; failures are tracked so the line
  says "unavailable" instead of spinning or showing an old value. Removed
  the disk seed and the persisted-rate write.
- config.rs: dropped the dead last_rate/last_rate_vs/last_rate_at fields
  and their read/write paths. Old configs still load (serde ignores the
  unknown keys); the fields are simply not written back.
- lib.rs: dropped the cold-start seed_from_disk call.
- GUI: balance hero fiat line renders a subtle "≈ …" while loading and a
  localized "rate unavailable" on failure, requesting a bounded repaint
  while a fetch is pending so the rate pops in on-screen. Pay/send amount
  preview shows nothing until a fresh rate lands.
- New goblin.home.fiat_unavailable string across all six locales.
- Tests: freshness-window bounds, is_fresh boundary, classify state
  machine (fresh/loading/unavailable), and config compat for the removed
  fields.
2026-07-05 04:54:02 -04:00
2ro f9def15306 Goblin: fix macOS build, import objc sel_impl for the deep-link bridge
objc 0.2's sel!/msg_send! macros expand to sel_impl!, which has to be in
scope. The cfg(target_os = macos) apple-events module never compiles on
Linux, so local cargo check missed it and only macOS CI caught it.
2026-07-05 04:01:51 -04:00
2ro c21ad4b7cc Goblin: apply node selection to the running wallet, no silent zero
Selecting a node in Settings > Wallet > Node moved the checkmark and
persisted the choice, but the live session never switched: the node
client is baked into the wallet instance at open time and was only
rebuilt on the next unlock, so a running wallet kept polling the old
(often dead) node. Owners onboarded on an older build still carry the
retired grinnode.live default in connections.toml, which now 502s, so
the wallet showed a stuck zero balance with no way to recover short of
a force-restart.

GRIM's own connection-settings UI reconnects on change (close + reopen);
the Goblin node picker only called update_connection, so this was a
Goblin regression, not inherited upstream.

- Wallet::reconnect_node swaps the live node client's URL + secret in
  place via w2n_client (no close/reopen, so no password re-entry),
  updates the runtime connection so the UI reflects the switch at once,
  clears the stale sync error, and wakes the sync thread to refresh
  against the new node. Wired into all three picker actions.
- node_url_secret factored out of create_node_client so open and live
  reconnect always resolve the same node for a given config.
- balance_hero no longer renders a bare 0 during a node outage: a pure
  balance_subline state machine picks updating / can't-reach-node /
  last-known-balance, so an unreachable node reads honestly instead of
  looking like an emptied wallet. New goblin.home.balance_stale string
  in all six locales (drift green).
- Unit tests for the balance state machine.
2026-07-05 03:44:54 -04:00
2ro 6bf6f3a2e9 Goblin: route macOS goblin: link clicks to the send-review screen
The goblin: scheme was registered everywhere last night, but macOS is the one
platform that delivers a scheme click as a Carbon/Apple Event (kAEGetURL), not on
argv and not through any path winit or eframe surface, so on a Mac the click went
nowhere.

Install a kAEGetURL handler straight on the shared NSAppleEventManager at startup
(macOS only), pull the URL string out of the event, and feed it to on_data, the
exact same entry the desktop argv path uses. The per-frame Goblin router then lands
the pay URI on the prefilled review screen, identical to a scanned checkout QR or a
Linux argv link. This covers both a cold launch (event queued at start-up) and a
warm click (app already running).

The bridge is entirely cfg(target_os = "macos"): zero bytes on Linux, Windows and
Android (Linux binary size byte-for-byte unchanged). It talks to the Objective-C
runtime through the classic objc crate, which is already in the macOS build graph
via nokhwa/cocoa/metal/wgpu, so it adds no new dependency tree, only its own small
handler class. It registers its own Apple Event handler rather than touching the
NSApplicationDelegate winit owns, so nothing winit does is clobbered.
2026-07-05 03:44:54 -04:00
2ro 5352afee4e Goblin: proof-on-request payments (wallet side of the frozen contract)
Implements the Goblin-wallet half of the proof-on-request architecture
(magick payment infra spec section 4/5): the buyer's wallet threads a
native Grin payment proof per transaction, off by default and on only
when the scanned pay URI asks for it, and auto-delivers the proof to the
market and watcher on finalize. Person-to-person sends are unchanged.

W1 pay-URI parser (payuri.rs): PayUri gains proof/order/notify, each
fail-closed (a bad value drops to None and never blocks the payment).
proof is a grin1/tgrin1 slatepack-address shape check, order is an opaque
control-stripped routing key capped at 64 bytes, notify is an npub shape
check. Unknown-param forward-compat and the magick interop tests still
hold. 12 new parser tests.

W2 proof threading: WalletTask::NostrSend carries proof/order/notify; the
handler re-parses proof authoritatively into a SlatepackAddress and calls
w.send(a, Some(addr)) instead of the hard-coded None, setting
payment_proof_recipient_address. The order handle, watcher npub, and
amount persist in TxNostrMeta (new serde-default fields) before dispatch,
so a crash between send and finalize loses nothing.

W3 review indicator: the send-review screen shows a small "proof included"
row when proof mode is active. New t!() keys row_proof / row_proof_val in
all six locales (drift test green).

W4 proof delivery on finalize: when a finalized SEND was sent in proof
mode, the wallet publishes the two contract events: a plain unencrypted
kind-17 "payment sent" receipt to the app relays (payment-request tag =
order handle; proof and kernel deliberately omitted to avoid leaking the
buyer's sender address; flips the page to "detected", never "paid"), and
a NIP-59 gift-wrapped kind-17 rumor to the notify npub carrying the full
proof JSON plus a kernel-excess tag. Both are enrolled in the existing
crash-safe reconcile pass and retried until a relay accepts them.
wrapv3 gains wrap_kind() to gift-wrap an arbitrary rumor kind.

Matching is by the payment-request (invoice number) tag alone; the wallet
never learns magick's internal orderId. No merchant-side auto-receipt is
built: the rewritten spec has the buyer publish on finalize instead.
2026-07-05 03:44:53 -04:00
2ro 9caa7f7ffe Goblin: goblin: payment deep link opens straight to send review
Web "Open in Goblin" pay buttons fired generic nostr: URIs, which the
OS routed to whatever social client claims nostr:. Register a dedicated
goblin: scheme and route it (plus nostr: pay URIs) to the prefilled
send-review screen, the same destination as scanning a checkout QR.

Parser (payuri): accept goblin: alongside nostr: (same payload, either
scheme); add is_pay_uri() to tell a payment link from a slatepack.

Runtime: an argv/intent/socket payload that is a pay URI is stashed and
opened by the Goblin surface via SendFlow::from_deeplink, which shares
the exact apply_scan path the camera uses. Desktop cold + warm start
(argv + single-instance socket) and Android cold + warm (intent-filter)
covered.

Registration: Linux .desktop x-scheme-handler/goblin, Android goblin
intent-filter, macOS CFBundleURLTypes, Windows WiX HKCR URL protocol.
2026-07-05 03:44:53 -04:00
2ro aff0c7c1b2 Goblin: fix desktop clipboard (copy/paste lost the selection)
On desktop the Paste button in the wallet-restore seed entry did nothing, and
the mirror copy on the new-wallet creation screen was equally broken: a copied
recovery phrase never made it to the clipboard.

Root cause (inherited from GRIM, not a Goblin regression — the two clipboard
impls are byte-identical): copy_string_to_buffer / get_string_from_buffer each
created a fresh `arboard::Clipboard` and dropped it the instant the function
returned. On Linux (X11 AND Wayland) the clipboard selection is owned by the
live Clipboard instance, so dropping it immediately released ownership and the
copied text vanished before it could be pasted. Reproduced with arboard 3.6.1
on a Wayland session: fresh-instance-per-call read back empty; one persistent
instance across set+get read back correctly.

Fix: keep ONE long-lived `arboard::Clipboard` on the Desktop platform (behind a
Mutex, since the trait methods take &self and arboard's take &mut self), so our
process stays the durable selection owner. Also stop unwrap()-panicking on a
clipboard backend error — a failed copy/paste now logs instead of crashing.

Every clipboard button in the app routes through this one platform pair
(onboarding paste/copy, GRIM creation paste/copy, proof, slatepack message
entry, tx/receipt copy, receive copy, npub/nsec/backup copy, the shared
TextEdit paste), so this single change repairs the whole class at once.

Verified end-to-end: an #[ignore]d clipboard_roundtrip test drives the real
Desktop impl on the live session clipboard and asserts a 24-word phrase
survives copy -> paste (run with --ignored). It passes on Wayland now and would
fail under the old pattern.
2026-07-05 01:13:49 -04:00
2ro 6543a9a4ea Goblin: GoblinPay QR scans straight to review
When a scanned QR is recognized as a full GoblinPay payment payload (a
recipient key that decodes locally to a concrete pubkey AND a validated
amount), skip the recipient discovery/search step and open the review screen
prefilled with recipient, amount and memo. The merchant checkout QR already
carries everything needed to pay, so re-walking the recipient search was
redundant. The review screen still confirms the send unchanged (the user
holds-to-send); nothing auto-sends.

recognize_scan classifies the payload:
- Review: nprofile/npub/64-hex recipient + amount -> straight to review. A
  cheap LOCAL contact lookup supplies a friendly name; otherwise the short
  npub (no network resolution on this path). nprofile relay hints carry
  through as delivery targets.
- Search: a name/@handle (needs NIP-05 resolution), an amount-less key, or
  non-nostr text -> the pre-existing search-box path, with any amount/memo
  still prefilled.

This covers every scanner entry into the send flow (search-field scan icon
and the header scan-to-pay screen), so it works from anywhere the payment
scanner is reachable.

Unit tests on the pure recognize_scan / decode_recipient_key:
GoblinPay-uri-with-amount -> Review; key-without-amount -> Search;
name+amount -> Search; non-nostr -> Search; key decode variants.
2026-07-05 01:13:49 -04:00
2ro ce6ac5a1e4 Goblin: language-aware news panel
Show the newest news article whose language matches the wallet's active
locale, falling back to the newest English article when none exists.

Language detection per article, in priority order:
1. An event language tag: NIP-32-style ["l","<code>","ISO-639-1"] or the
   bare ["l","<code>"] / ["lang","<code>"] (code is the tag's 2nd element),
   extracted in handle_news and persisted on NewsItem.lang.
2. A trailing "[xx]" marker on the title (case-insensitive, xx = ISO 639-1
   two letters), e.g. "2026-07-05 Welcome to Goblin [de]".
3. No marker anywhere = English.

Selection happens at the data layer (news_latest): the store stays
language-agnostic (still newest-per-d-tag, cap 8, edit-in-place). Among all
cached articles, filter by detected language, take the newest by created_at;
the "[xx]" marker is stripped from the displayed title. The app locale is
folded to its ISO 639-1 primary for matching (zh-CN -> zh).

The GOBLIN_FAKE_NEWS debug hook now injects an English + German sample so the
panel selection can be screenshotted per locale.

Unit tests: tag detection (both shapes), title-suffix detect + strip,
no-marker-means-English, zh-CN folding, locale match + English fallback,
newest-within-language-slice.
2026-07-05 01:13:49 -04:00
2ro 2afea5abc0 i18n: translate lagging fallback strings and localize settings username hint 2026-07-04 23:25:06 -04:00
2ro a9023d0404 README: drop stale onion claim (Tor-exit only since 134), style sweep
Every relay, money path included, is reached over a Tor exit to its
clearnet host; the pinned-onion wording was Build <134. Em dashes
normalized per house style.
2026-07-04 23:25:06 -04:00
2ro e14a16f693 README: Build 136 news panel on Home
Latest article from the official Goblin news key (kind 30023) between
Send/Receive and Recent Activity; hidden when empty; latest-only with
edits-in-place.
2026-07-04 23:25:06 -04:00
2ro 47b095ab88 Goblin Build 136 - home news panel, faster send confirmation
Home now shows the latest post from the Goblin news key (kind 30023) in
a card with tappable links; the panel stays hidden until a post is seen.
Desktop Home widens to use the available space. Send confirms faster.
2026-07-04 20:42:17 -04:00
butler 71570980fe docs: mixnet references updated to Tor (build 134) 2026-07-04 20:42:17 -04:00
2ro 05ee05e2d2 Goblin: dedupe balance sync indicator
Remove the Build 135 balance-hero "Updating…" line. During a routine
sync it duplicated the header node card's "Syncing" status, so a funded
wallet showed two updating indicators at once. Keep the established
"Balance updating…" line (zero-balance / first-sync case) and the header
status as the single sync indicators. Drop the now-orphan
goblin.home.updating key from all six locales (i18n drift green).
2026-07-04 20:42:17 -04:00
2ro 04f8741686 chore: gitignore local release artifacts 2026-07-04 20:42:17 -04:00
2ro 1d7775a037 Goblin Build 135 - Tor credit, balance updating indicator, Nym sweep
- Third-party credits: replace the "Nym mixnet" row with "Tor (arti)" (0.43,
  linking the arti repo), matching the other credit rows' hardcoded style. No
  locale strings existed for it (credit labels are hardcoded, not localized).
- Balance: show a quiet "Updating…" line under the balance while the node is
  still warming, reusing the existing wallet.syncing() signal and the
  balance-updating line's muted style; it clears once the node is synced. New
  t!() key goblin.home.updating added to all six locales (i18n drift green).
- Nym sweep: Cargo.toml package description "Nym mixnet" -> "Tor". The only
  other user-facing Nym reference was the credit above. Remaining references
  are the deliberately-dormant `nym` feature/module and internal code comments,
  left as-is; README and all locale values were already Tor.
2026-07-04 06:21:05 -04:00
2ro 174e387a87 Goblin: retire Nym from the public toolchains + drop internal test harnesses
- CI (release.yml/build.yml): remove fetch-nym + AWS_LC_SYS_PREBUILT_NASM;
  the default cargo build --release is Tor/arti only now (nip44 fetch kept).
- Cargo.toml: nym-sdk is optional behind the `nym` feature so the default
  build never pulls it.
- Delete the dead Nym-era probes (xrelay_smoke, connect_timing,
  tunnel_measure) that referenced the removed Nym API and broke `cargo test`.
- Untrack the internal E2E harnesses (e2e.rs, nostr_e2e.rs) via .gitignore;
  gate `mod e2e` behind the `e2e-internal` feature so clones still compile.
- Refresh stale Nym comments in build.rs and the build scripts.
2026-07-04 06:21:05 -04:00
2ro 7b56855e16 Goblin Build 134 - Tor delivery fixes (drop the flapping onion)
build133 shipped the Tor transport but two delivery bugs remained; this
fixes both so payments resolve through confirmations again.

- Drop the pinned relay .onion. It was a single fragile hidden-service hop
  shared by every wallet; under load it flapped (WebSocket 1006), and when
  it dropped mid-handshake a payment stalled after the first gift-wrap --
  you'd get the incoming alert but the money never confirmed. Every relay is
  now reached over the Tor exit to its clearnet host: your IP still stays
  hidden from the relay, with no fragile onion hop to wedge. The has-onion
  gate that rejected an onion-less relay list is removed, and the pinned
  candidate pool carries no onion/exit fields.

- Tor-friendly relay set. Defaults/fallbacks had included relays that refuse
  Tor-exit connections (damus, nos.lol); wallets that fell back to them
  couldn't send at all. Defaults and the pinned pool are now
  relay.floonet.dev, relay.0xchat.com, offchain.pub.

- confirm-before-sent no longer false-fails across a relay reconnect (a
  confirmed-received wrap is treated as sent-pending instead of re-dispatched,
  which had duplicated wraps and hung the spinner); a v3 gift-wrap unwrap
  failure no longer silently drops the message.

Validated over Tor with two funded wallets on two independent mainnet nodes:
connect in seconds, send to finalize in 8s.
2026-07-04 05:33:48 -04:00
2ro 1e2f41b404 README: update transport description from Nym mixnet to Tor
The README still described the old Nym-mixnet transport, including a
reference to src/nym/sidecar.rs which no longer exists post-switch.
Update the intro, feature list, payment-travel diagram, and build
instructions to describe the current embedded-Tor (arti) transport:
in-process, dialed at the relay's pinned .onion, no sidecar, no sibling
checkout needed for the default build.
2026-07-04 03:52:29 -04:00
2ro 6f1caf4511 Goblin Build 133 - Tor transport (replaces the Nym mixnet)
The wallet's private transport moves from the Nym mixnet to embedded Tor
(arti, copied from GRIM's engine): it dials the relay's pinned .onion, so
the relay never learns your IP, while the relay + NIP-59 gift-wrap hide the
rest - content, sender, and (via a relay-side randomized release) timing.
The Grin node stays on the clear internet as before.

Why leave the mixnet: the Nym free-tier bandwidth this depended on is being
removed upstream (the grant expires at UTC midnight; the paid path requires
holding NYM tokens), so a payments wallet can't stand on it. Tor is
unmetered, embedded in-process on mobile, faster where users wait, and
lighter on the battery.

Preserved intact: the confirm-before-sent guard, relay-gated readiness, and
the lazy warm-on-activity node polling. src/nym/ is feature-gated off (arti
and nym-sdk can't share one binary); full removal is a follow-up.
2026-07-04 03:35:31 -04:00
2ro 1c8d848e18 Goblin Build 132 - payments never fail silently
Adds confirm-before-sent: a payment is not shown as "sent" until the relay
actually confirms receipt. If the path is slow or a send doesn't land, you now
see a clear "not confirmed — retry" instead of a false "sent" that quietly loses
the payment.

Routing is unchanged from prior builds: all wallet nostr traffic — slatepacks,
payment requests, identity, discovery, goblin.st name + relay lookups — rides
the mixnet; the grin node and price/fiat lookups stay direct.
2026-07-03 22:50:02 -04:00
2ro d05e015f98 Goblin Build 131 - connectivity sweep
Connect reliability, measured before release:
- Cold start: the wallet races two mixnet entry-gateway connects and takes the
  first up, so a dead random draw no longer costs the full timeout (dead-round
  rate drops from p to p^2; verified across 30 cold trials).
- Dead exits are condemned in ~10s instead of ~32s (fresh-tunnel probe budget
  5s x 2 rounds, 4.2x margin over the worst measured healthy probe), removing
  the 50-70s worst-case connects. Established-tunnel keepalive unchanged.
- "Connecting relays..." clears in ~2-4s after saving the relay list (the
  Connected flag now tracks the real relay-up signal instead of waiting on a
  30s catch-up fetch); identity publishes are time-boxed; the onboarding
  Claim button un-gates equally sooner.
- Gateway-race and probe timings are now visible in [timing] logs.

Also: the update banner shows Goblin build numbers (not the inherited 0.3.6),
and the nip44 crate is consumed from crates.io (v0.3.0) - no sibling checkout
needed to build.
2026-07-03 17:22:42 -04:00
2ro e8bfe8d76f Goblin Build 130 - Nym connect reliability
Bugfix: the mixnet exit-liveness probe now races two stable targets over two
rounds instead of a single one-shot 1.1.1.1 connect, so a healthy tunnel is no
longer false-condemned and reselected forever. Fixes the intermittent
Connecting to Nym hangs that could take minutes or never resolve (verified:
15/15 cold starts connected, zero hangs).

Also: the Android versionCode/versionName now track the Goblin build number
(were frozen at GRIM's 5 / "0.3.6"), so the OS detects upgrades correctly.
2026-07-03 11:49:01 -04:00
2ro 71f7584800 Goblin Build 129 - relay migration
Bugfix: the default and fallback messaging relay moved from relay.goblin.st
(being retired) to relay.floonet.dev, so new installs and reconnects use the
live relay. Built-in relay fallback list updated to match.
2026-07-03 04:02:23 -04:00
2ro 523d397ad7 Goblin Build 128 - warm connect, instant price, faster reads 2026-07-03 00:09:31 -04:00
2ro ce7008665a Goblin Build 127 - payment request notifications, remove avatar name ring, fix CI 2026-07-02 23:01:49 -04:00
2ro 4ab91c6ece Goblin Build 127 - payment request notifications, remove avatar name ring 2026-07-02 22:31:16 -04:00
2ro 36509c6b9a Goblin Build 127 - payment request notifications 2026-07-02 22:20:51 -04:00
2ro c0981916da Goblin Build 126 - major mixnet transport speedup 2026-07-02 20:54:41 -04:00
2ro c5fbb9fa14 Goblin Build 125 - faster Nostr reads and Nym reconnect 2026-07-02 19:18:35 -04:00
2ro b7d479ede7 Goblin Build 124 - fix in-app update download names 2026-07-02 17:09:33 -04:00
2ro f79bf9d926 Goblin Build 123 - fast relay connect, nostr-app login, scan amounts, name outline 2026-07-02 15:51:37 -04:00
2ro d1070008e7 Goblin Build 122 - drop notification enable-node action; re-enable macOS release CI 2026-07-02 13:38:57 -04:00
2ro 060272626a Goblin Build 121 - restore scoped exit + relay.floonet.dev money-path cutover 2026-07-02 13:27:18 -04:00
2ro 5f90f00d3b Goblin Build 120 - remove dead scoped-exit egress 2026-07-02 10:06:51 -04:00
2ro 3a3cf8fdb0 Goblin Build 119 - paste npub in Send-to (issue #1) 2026-07-02 09:56:40 -04:00
2ro ebe5bc94eb Goblin Build 118 - fast connect (drop blocking exit), avatar ring, ecosystem 2026-07-02 09:48:38 -04:00
2ro 90eb0500c1 Goblin Build 117 - avatar ring gap + magick/floonet ecosystem 2026-07-02 09:35:57 -04:00
2ro 2d2bf326b3 Goblin Build 116
Floonet scoped Nym exit on the money-path relay (dial a relay operator's
co-located exit over a MixnetStream, TLS end to end, no public DNS; anchor +
fallback, never pin-only), NIP-44 v3 gift wraps (G4), mix-DNS, localization,
GUI polish. Verified end to end: two wallets complete a real gift-wrapped Grin
payment over relay.goblin.st, finalized + posted on mainnet.
2026-07-02 04:23:06 -04:00
2ro 5deffbda4c settings: GRIM link now points at the live upstream
The Settings third-party 'GRIM (upstream wallet)' link opened github.com/ardocrat/grim
— a stale personal fork ~106 commits behind. Point it at github.com/GetGrin/grim, the
live GitHub mirror of canonical code.gri.mw/GUI/grim.
2026-06-18 00:24:34 -04:00
2ro 54e1e83bfe Goblin — Build 98 2026-06-17 22:22:21 -04:00