Profiles never loaded when scanning a bare npub (username/avatar stayed
blank) even though the relay stores and serves the kind-0 fine. Two causes:
fetch_profile_blocking ran on a throwaway current-thread runtime that can't
drive the relay connections (which live on the service runtime, behind the
custom Nym mixnet transport), and it only dialed nprofile hints, never the
user's own default relays. Run the fetch on the service runtime via a stored
Handle, and always dial the default relay set (incl relay.goblin.st).
In dark theme the wallet list, app settings, wallet creation and onboarding
leave the bright accent-yellow title_panel_bg showing under the status bar,
but status_bar_white_icons() returned the dark-theme default (white) — white
icons are illegible on yellow. Force dark icons on every non-Goblin-surface
screen (the Goblin surface covers the inset and sets its own per-tab flag).
- Onboarding goes Intro -> wallet setup directly (connected to a public node
instantly); the node-choice step is retired.
- External node settings stay in Settings; 'run your own node' (integrated) now
lives in the Advanced submenu.
- Wallet-list: title reads GOBLIN (was WALLETS), dropped the redundant GOBLIN
wordmark under the mark, build number kept but smaller.
The empty-flavor test compared the literal string 'flavor', so a no-flavor
invocation left the APK output path as apk//debug/app--debug.apk and the
rename failed. Default to 'local' correctly.
The macOS CI job lipo'd the universal binary and zipped it raw, so users got a
naked 'goblin' executable instead of a double-clickable app. Assemble the
universal binary into the existing macos/Goblin.app bundle (Info.plist + icon),
ad-hoc codesign it (required for the arm64 slice to run on Apple Silicon after
lipo strips the per-arch signature), and ditto-zip the bundle. Remove the stale
tracked _CodeSignature that would otherwise make the app read as 'damaged'.
A Goblin payment locks the sender's outputs until the recipient replies (S2)
and we finalize+post. If the recipient never connects to nostr, the funds stay
locked until the 24h auto-expiry. This adds a manual Cancel that reclaims them
on demand (after a 10-min grace, or immediately if the send never reached a
relay), marks the payment Cancelled, and best-effort voids it to the recipient.
- WalletTask::NostrCancelSend: authoritative tx lookup; refuses if already
finalized/confirmed (race); marks meta Cancelled BEFORE cancelling the grin
tx; serialized with nostr_finalize_post via a per-service lock so a cancel and
a concurrent S2 finalize can't both commit.
- nostr_finalize_post returns Ok(false) (skip, no retry/re-post) when the tx is
cancelled or the meta is Cancelled — covers the tx-list cancel path too.
- decide() already drops a late S2 on a Cancelled meta (new unit tests assert
it); recipient-side void marks a received payment Cancelled for display WITHOUT
deleting the output (a malicious sender could void-then-post otherwise).
- Void-before-S1 ordering handled via a (slate,sender)-bound marker.
- Receipt: tap-twice 'Cancel payment' with caveat + outcome notice; honest
'Waiting for X to receive…' label; first-class Cancelled status. 6 locales.
- cancel_grace_secs config (default 600).
The right button copied the grin1 slatepack address and the left copied the
nprofile — both wrong for 'how people pay me'. Now: left = Share a friendly
'Pay me on Goblin (goblin.st) — npub1…' message, right = Copy the bare npub.
New receive.copy_npub / receive.share_message keys across all six locales.
The nip05d authority now enforces a 3..=20 length; align the wallet's claim
validation (onboarding + settings) and the 'Names are 3-20 chars' hint across
all six locales so the wallet never offers a name the server will reject.
The vendored 'openssl' dependency existed only to statically link OpenSSL on Linux/Android (where native-tls uses it). In the global [dependencies] it also forced openssl-src to compile on Windows and macOS, which don't use OpenSSL at all (SChannel / Security.framework). On the Windows MSVC CI runner that build fails: openssl-src's perl Configure runs under Git-Bash's MSYS perl, which lacks Params::Check / Locale::Maketext::Simple. Gating the dependency to cfg(any(linux, android)) removes the pointless, fragile build on Windows/macOS and keeps the self-contained static link on Linux/Android. No behavior change on any platform.
- Drop the displayed @ prefix everywhere (identity picker, slatepacks page, all copy); @ stays internal for lookups/avatars.
- Settings: move wallet management to the foot — Switch wallet (deselect, stays unlocked), Lock wallet, and a new Advanced page mirroring GRIM's recovery tools (repair, restore-from-seed, reveal recovery phrase, delete).
- Restore the wallet-list title header (reachable settings gear, Pay-screen accent yellow).
- Transport: a transient receive/finalize failure no longer marks the gift wrap processed, so an incoming payment is retried on catch-up instead of being silently lost; finalize-post is now retry-safe (re-posts an already-finalized slate).
- Guard a latent panic on a short sender key in ensure_contact.
- Add more healthy public grin nodes (mainnet + testnet) for redundancy.
- Default first-run onboarding to the Instant connection (public node), shown first.
- Tidy leftover Tor remnants left from the fork.
Resolve a counterparty's @username on every interaction, not just incoming
requests — receives, sends and requests all kick off a verify-and-cache, plus a
one-time backfill on wallet open — so activity and the recent strip show names,
not bare npubs. Usernames now render WITHOUT the @ (kept internally for avatar
lookup); the recent row ellipsizes past 8 chars and centers the name under the
avatar, while activity shows the full name.
Pay screen: the numpad now sits above the note so the soft keyboard can't cover
it (and tapping the pad dismisses the note's keyboard), and a no-op key — a
second dot, a 0 on a leading zero, the 9-decimal cap — fires a short error
haptic instead of doing nothing silently.
The balance hero only showed amount_currently_spendable, so a wallet whose
funds were still confirming read 0 while GRIM showed the real total — the source
of the "Goblin says 0" confusion. It now shows the TOTAL (matching GRIM) with an
"X available - Y confirming" breakdown when some isn't spendable yet.
A send or approve that hits NotEnoughFunds (coins from a recent payment still
confirming, ~10 min) now says exactly that instead of a blank "Couldn't send",
and the Approve button no longer stays greyed forever — it un-greys on failure
with the reason shown, so the user can retry once funds clear. The relay-dial
cap for cross-relay delivery drops 12s to 6s so the first send isn't sluggish.
A send/request to a counterparty on relays we don't already hold failed with
"relay not found": NIP-17 publishes to the RECIPIENT's relays, but send_*_to
rejects any relay not in the pool. connect_relays() now adds and dials the
target's relays (from their kind-10050 or an nprofile/QR hint) before sending,
so the gift wrap actually reaches their inbox. Same fix lets profile/@username
lookup find a kind-0 that lives only on the target's own relays — fetch_profile
takes relay hints and dials them first. Sidebar handle font now scales with
length so short @names are legible (was a fixed, too-small 11px).
Settings now says "Manual transaction" and the privacy row reads "Messages &
lookups" opening a new Network privacy page that tells the truth: messages,
names, price and avatars ride the Nym mixnet; the grin node connects directly.
README and lander updated to match.
Requests are messages, payments are final: declining a request now sends the
requester a void control message (NIP-17), a requester can cancel a request they
sent (cancels the local invoice and notifies the payer), and incoming requests
resolve the sender's verified @username instead of a bare npub. The Requested
amount on the success screen is centered. New NostrDecline/NostrCancel tasks and
a goblin-action control message carry it, bound to the stored counterparty.
Localization: every Goblin-screen string moved to t!() keys (370 keys) and
translated into de/fr/ru/tr/zh-CN, guarded by a key/placeholder drift test.
System-locale auto-detect now matches region locales like zh-CN.
- The profile card drops the duplicate npub line and shows two statuses: the
mixnet ("Connected over Nym", ~instant) and under it the nostr relay
("Connected to nostr" / "Connecting to relays…"). The relay connection is the
slower step, not the mixnet.
- New cheap cached nym::is_ready() flag backs the mixnet status (no per-frame
TCP probe), set when the SOCKS5 proxy comes up.
- Sidebar identity chip: smaller handle so the npub stays on one line, with a
matching Nym/relay status.
- Pairing defaults to Off on first run: no conversion is shown anywhere and no
price request leaves the device until the user opts into a pairing. Off is a
picker option and the choice persists.
- The rate fetch goes back over the Nym mixnet (reverting Build 71's clearnet
route) — it's opt-in and infrequent, so it stays private and doesn't get in
the way of slatepacks. CoinGecko's free-tier rate limit (shared exit IP) rules
out live/1s polling, so it keeps the slow cache.
- Small note on the Pairing page that rates fetch over the mixnet.
On the Pay screen the scan QR drops its dark puck (now a bigger black icon
directly on the yellow), the user's profile picture sits to its right as a link
to the settings/profile page, and the goblin mark is sized to match — all three
controls about 40px.
On the Pay surface the 3-item bar drops its floating pill + shadow — Wallet,
the center ツ, and Activity sit dark directly on the yellow, Cash App-style.
Other surfaces keep the floating pill.
Correct the Build 71 misread of the design notes:
- Amount keeps the ツ unit mark (revert the goblin mark next to the number).
- The goblin mark replaces the "Pay" title at the top-left (tinted dark to read
on the yellow surface).
- The bottom tab-bar strip turns yellow on the Pay surface, like the body.
- (Dark status-bar icons on the yellow surface kept from Build 71.)
Following upstream Grim's posture for non-sensitive metadata: the fiat price
preview and the update check now go direct over HTTPS instead of the mixnet, so
the price shows promptly without waiting on the mixnet bootstrap. Payments,
relays and identity stay mixnet-only.
- price.rs: fetch the rate over clearnet (plain reqwest), no proxy.
- update check: on by default like Grim, repointed at Goblin's own GitHub
releases, build-number aware (is_update compares buildNN), Goblin asset names,
GitHub User-Agent header.
- Pay screen: status-bar icons go dark on the bright yellow surface
(status_bar_white_icons honours a per-frame yellow-surface flag); the hero
amount shows the goblin mark as its unit in place of the ツ glyph.
README: drop the removed nym-socks5-client sidecar story (the SDK is linked
in-process now), add the in-process build steps + the manual-slatepack feature.
Comments: replace stale "sidecar" wording with the in-process SOCKS5 proxy,
drop leftover Tor references (Goblin routes over Nym), and trim the chattiest
working-notes to terse rationale.
Point .github/actions/fetch-nym at git.us-ea.st/GRIN/nym (branch goblin =
upstream nymtech/nym @ b6eb391 + our Android webpki-roots patch) and clone it
directly. Drops both the upstream-GitHub fetch and the git-apply patch step —
the last third-party dependency in the build chain is now self-hosted. Removes
the now-unused ci/nym-webpki-android.patch.
Add scripts/toolchain.sh: fetches GRIM's canonical build toolchains (custom
NDK r29, zig, appimagetool + type2 runtime, and optionally the Android SDK /
gradle / osxcross) into a gitignored .toolchains/ and writes env.sh.
linux/build_release.sh and scripts/android.sh now source .toolchains/env.sh,
preferring the DEV toolchains and falling back to system installs:
- Android links against the custom NDK r29 (rebuilt LLVM), producing
16 KB page-aligned .so libraries — required by the Play Store.
- The Linux AppImage cross-builds with the DEV zig + appimagetool; bindgen is
pointed at the host kernel headers so v4l2-sys finds linux/videodev2.h under
zig's glibc-2.17 sysroot.
Also fix the stale .gitignore AppRun entry (Grim.AppDir -> Goblin.AppDir).
Pay: stop reddening the amount while typing — requesting more than you hold is
a valid request, so the digits stay black. Pressing Pay without enough funds
now shakes and briefly flashes the amount red and buzzes the phone, then
settles back. Bigger scan-to-pay puck.
Settings: the username "@" moves inside the field as the "@yourname"
placeholder (a leading "@" the user types is stripped). Mixnet routing is
shortened to "All traffic" and flagged in the privacy color. The Build row
links to GitHub releases. Network reads "MW + Nym mixnet + nostr". The Nym
third-party row shows the linked SDK version instead of "socks5".
Wallet: expose GRIM's native by-hand slatepack flow as an advanced
"Slatepacks" page — paste to receive/pay/finalize, or create a payment
slatepack to hand over. The fallback for when a payment can't ride a @username.
CI: fix the red GitHub builds. nym-sdk is a path dep on ../nym, which the
runners didn't have. A composite action materializes the pinned upstream nym
commit plus Goblin's small Android webpki patch before each build; aws-lc-sys
uses prebuilt NASM on native Windows.
Builds: strip release binaries, dropping ~17 MB of debug symbols from the
desktop build.
Security (audit H-2): the legacy update check is OFF by default. It hit
code.gri.mw (GRIM's gitea) directly over CLEARNET via the old HttpClient —
leaking "this user runs Goblin" metadata on every wallet-list view, which
defeats the nothing-clearnet mixnet model, and it pointed at the wrong
project's releases anyway. Opt-in only until reworked to run over the
mixnet against Goblin's own releases.
Build: with the Nym SDK linked in-process there's no sidecar binary to
embed or bundle. linux/build_release.sh drops the GOBLIN_NYM_UNIX_BIN
embed (AppImage is one self-contained binary); scripts/android.sh stops
bundling nym-socks5-client into jniLibs (the cdylib links nym-sdk
directly); scripts/nym-android.sh deleted.
Goblin now links nym-sdk directly and runs its SOCKS5 client on an
internal tokio runtime exposing 127.0.0.1:1080 — the same loopback seam
the transport already dials. There is no sidecar subprocess and no
bundled/embedded/sideloaded helper binary; the goblin process itself owns
:1080. This mirrors how GRIM links arti/Tor in-process. Verified live: the
mixnet comes up in ~1.4-2s (gateway persisted in ~/.goblin/nym, reused
across launches) and a relay connects in ~2s over it, with no separate
process.
- Cargo.toml: add nym-sdk (path dep on the local nym checkout, which carries
the Android webpki-roots patch) + rustls with the ring feature.
- src/lib.rs: install rustls' ring CryptoProvider at startup. Linking nym-sdk
pulls aws-lc-rs alongside our ring; with two providers present rustls 0.23
won't auto-pick a default and tokio-tungstenite/reqwest panic on the first
TLS handshake. nym uses its own explicit provider, so this only steers our
relay/HTTP TLS.
- src/nym/sidecar.rs: replace the subprocess machinery with an in-process
Socks5MixnetClient (persistent storage; ephemeral fallback) kept alive for
the process lifetime on a dedicated runtime. Drops the binary lookup, embed
extraction, init/launch, and child management.
- build.rs: drop the GOBLIN_NYM_*_BIN embed block (nothing to embed).
- src/nym/{mod,transport}.rs, src/nostr/{mod,avatar}.rs: docs now describe the
in-process client; clear stray "old Tor/arti" wording (no Tor transport code
remains — only grin-core's slatepack OnionV3Address, which is unrelated).
Also: named users now get the pubkey-seeded gradient background with their
initial composited on top (instead of the Grin mark) — gui identicon.rs gains
gradient_bg_svg and widgets.rs gains gradient_letter_avatar; avatar_any routes
named keys to it. Verified live with @nymgoblin.
The Linux release no longer ships a loose nym-socks5-client beside AppRun.
It's baked into the goblin binary the same way the Windows build bakes it
into goblin.exe, and extracted to ~/.local/share/Goblin at first launch
(chmod +x on Unix). The AppImage is now one self-contained file with nothing
loose to misplace.
- build.rs: generalised the Windows-only GOBLIN_NYM_WIN_BIN embed to a
cross-platform path. GOBLIN_NYM_UNIX_BIN embeds the Linux/macOS sidecar;
Android never embeds (its sidecar rides in the APK's jniLibs).
- src/nym/sidecar.rs: the embedded const, extract_embedded_sidecar, and the
binary_path extract branch now cover all non-Android targets, with a Unix
chmod +x on the freshly written file.
- linux/build_release.sh: rewritten to set GOBLIN_NYM_UNIX_BIN, apply the
glibc-2.17 zigbuild + CRoaring-AVX512/vendored-OpenSSL fixes, and assemble
an AppDir with no loose sidecar.
Nym "Connecting..." for ~1 minute — root cause fixed. open() spawned the
wallet sync thread BEFORE init_nostr() created the nostr service, so the
sync loop's first pass at service.start() found no service and skipped
it; the service only started on the next sync cycle, a full SYNC_DELAY
(60s) later. Measured on the dev box: the gap between "identity ready"
and "starting service" was 62s, while the relay itself connects over the
mixnet in ~2s. Initialising nostr before start_sync collapses that gap
to ~1ms (verified 62s -> 1ms). The mixnet sidecar already warms up at app
launch, so the relay connect was the only thing waiting — and it waited
on loop timing, not on Nym. Added a "first relay Connected ~Nms" log line
so the timing is observable from logs going forward.
Pay tab is now painted in the yellow theme (Cash App-style brand surface)
regardless of the active theme, via a scoped theme override held across
the central panel so the fill and every widget pick up the yellow tokens
together.
Gradient avatar now renders for anonymous npubs on the onboarding
identity card and the mobile home header — both hardcoded a flat "N"
letter tile while Settings already showed the gradient. The header falls
back to the short npub so avatar_any takes the gradient branch.
Build 60 moved the scan-to-pay screen behind a new `scan_open` flag (for the
Scan | My Code toggle), but the recipient picker (Send to / Request from) still
opened the scanner by setting only `self.scan` — which no longer gates the
screen. So tapping the QR icon in the search field silently started the camera
and showed nothing. Set `scan_open` + the Scan tab there too (keeping `scan` so
the Scan branch does not double-start the camera).
Anonymous users (no @handle, no kind-0 picture) had a flat colored tile with an
N - meaningless, since their identity IS the key. Replace it with a pubkey-seeded
two-tone gradient + the Grin mark (avatar = f(pubkey)): same key -> identical
avatar on every surface, nothing to upload/store/sync. Ported the shared
reference (identicon.rs, f64 math, SHA-256 of the lowercase hex seed) and rendered
via egui SVG loader (cached per pubkey). avatar_any/activity_row now take the
npub/hex and use the gradient when the display name is an npub, the lettered tile
otherwise.
Cash App-style scan screen: a segmented Scan | My Code control (new w::segmented)
over either the camera or your own payment QR. My Code shows the @handle above a
big nprofile QR with the Goblin mark nested in a YELLOW center (was white — same
19% footprint, yellow reads as light to a scanner so the High-ECC recovery is
unchanged), and a native Share button. Added share_text to PlatformCallbacks
(Android ACTION_SEND text/plain via a new shareText JNI method; desktop falls
back to clipboard) to share the npub + nprofile link.
The Nym/relay connection could take up to a full SYNC_DELAY (60s) to even start:
the nostr service was kicked off deep inside the wallet sync loop, behind the
grin node-sync checks and !sync_error, so opening a wallet left the profile on
"Connecting..." until the next 60s sync tick (or never, while the node errored).
Move the (idempotent) service start to the very top of the loop, right after the
open check - independent of node sync - so the connection comes up right away.
Owner wanted more "black strength" on the puck mark; re-subset the same one
glyph from Noto Sans CJK JP Black (was Regular) for thicker, more confident
strokes. Same noto-tsu family / path, no code change.
Owner disliked the Gamja Flower ツ style on the center Pay puck. Replace it with
Noto Sans JP's cleaner, more geometric katakana tsu — subset to ONLY that one
glyph (~1.7 KB) the same way the old one was, loaded as the "noto-tsu" family and
drawn at the puck (mod.rs). Drop the now-unused Gamja Flower font + its license.
Two fixes from live testing:
1) Stuck on "Connecting…": the nostr service dialed relays without waiting for
the bundled Nym sidecar to be up. On a cold start a fast wallet-open beat the
mixnet bootstrap, every relay failed, and nostr-sdk backed off — so the wallet
sat on "Connecting…" long after the mixnet was ready. Now we wait for the
sidecar SOCKS5 port before connecting (instant once it is warm). Verified:
service start -> relay catch-up in ~5s; the flag (Build 55) reflects it within
2s.
2) On-screen keyboard on desktop: upstream Grim pops its own virtual keyboard on
desktop (no_soft_keyboard = is_android()), which looked wrong in the wallet
flows and competed with physical typing (intermittent dropped/duplicated
chars). Goblin now uses native input on every platform — Android IME via JNI,
physical keyboard on desktop — by defaulting no_soft_keyboard to true. Verified
on desktop: no on-screen keyboard, reliable typing.
The nostr service only refreshed its connected flag inside the select!s
sleep(30s) branch, which restarted on every incoming notification — so the
flag could lag the real relay state by 30s+ (or, under steady event flow,
never update), leaving the UI stuck on "Connecting…" even though a relay
handshake over the Nym mixnet completes in ~2s (measured). Set the flag right
after the startup connect, and poll it on an independent 2s interval; the 30s
heartbeat work (persist last-seen, TTL prune) stays on its own cadence.
Address Windows feedback (visible console window; no diagnostics when the
mixnet stalled):
- Hide the sidecar console: spawn nym-socks5-client.exe with CREATE_NO_WINDOW
so launching it no longer flashes a terminal.
- All-in-one: embed the Windows sidecar into goblin.exe (build.rs, gated on
GOBLIN_NYM_WIN_BIN) and extract it to %LOCALAPPDATA%\Goblin at first run, so
the release is a single self-contained .exe with no loose helper to misplace.
- Log the sidecar to ~/.goblin/nym-sidecar.log (all platforms) instead of a
null sink, so a stalled bootstrap is diagnosable.
Verified under wine: goblin.exe extracts the embedded sidecar, launches it,
and it opens the SOCKS5 proxy on 127.0.0.1:1080.
Ship the bundled nym-socks5-client on Windows and Android, not just Linux:
- sidecar.rs resolves the binary per platform — nym-socks5-client.exe on
Windows; on Android the sidecar rides in the APK jniLibs as
libnym_socks5_client.so and is launched from the native-library dir (the
one exec-allowed path), located via NATIVE_LIBS_DIR.
- Restore a vendored, statically-linked OpenSSL. Upstream Grim got this from
arti's static feature; dropping arti for Nym took it with it, which broke
Android/cross builds (no system OpenSSL for the target) and left desktop
dynamically linked to libssl. Inert on Windows/macOS (SChannel/Security.fw).
- android.sh bundles the sidecar into jniLibs per ABI; scripts/nym-android.sh
cross-builds it. Onboarding copy: Tor -> the Nym mixnet.
Verified end to end on an x86_64 emulator: the sidecar extracts, launches,
initialises, and opens the mixnet SOCKS5 proxy on 127.0.0.1:1080.
- Pay tab keypad sits in the lower third (thumb reach) instead of floating in
the middle with a big empty gap below it (pay_ui computes a drop spacer on
phone layouts).
- Review/Confirm "Delivery" reads "NIP-44 encrypted, over Nym" (naming the
actual gift-wrap encryption), and "Privacy" reads "Mimblewimble + Nym" on
the review and the receipt — surfacing both the chain and network layers.
Verified live at 394px across the Pay tab, review, confirm and receipt.
On-device review surfaced UI overflow/centering issues at phone width:
- Balance hero shrinks to fit: amount_text_centered_ink measures the text and
scales the font down when it would run off the edge (e.g. 0.47520721ツ).
- Send flow sits closer to the screen edges (smaller side gutters) and is
properly centered: the Review/Confirm hero now uses a centered, wrapping
layout instead of a fragile manual offset that clipped a long npub and
threw off the centering. info_row values truncate instead of overflowing.
- Recipient picker: the suggested row shows the full npub in the grey subtitle
(truncated to fit) instead of repeating the same shortened npub as the title.
- Success screen uses the goblin head (goblin-logo2), not the mascot mask.
Verified live at 394px across balance, recipient, review, confirm and the
success screen.
- Bake the always-on us-ea.st network requester into NETWORK_REQUESTER so a
shipped wallet routes through the mixnet out of the box (override with
GOBLIN_NYM_PROVIDER). Verified e2e: real wallet-to-wallet payment across two
mainnet nodes (grincoin.org → main.gri.mw), recipient received the exact
amount, all traffic over Nym.
- Fix the sender's displayed amount: a sent tx debits inputs and credits change,
so debited-minus-credited is amount PLUS fee. Subtract the fee so the activity
feed, receipt and history show the value that reached the recipient (0.01),
not the fee-inclusive net (0.033). The receipt's "Network fee" now reads the
real kernel fee instead of the change.
- Replace the remaining user-facing "Tor" copy with Nym (send/request review
delivery line, connection status chips, onboarding, the Privacy settings row).
38 lib tests pass.
Pending transactions that never complete now auto-cancel/expire after a
configurable window (NostrConfig::expiry_secs, default 24h; lower it in
nostr.toml to test). A sync-loop sweep (NostrService::expire_stale):
- cancels stale outgoing sends + invoices we paid via GRIM's cancel_tx
(WalletTask::Cancel), releasing the outputs they had locked;
- annotates incoming payments / invoices we issued as Cancelled only
(a late on-chain confirmation still wins);
- marks pending incoming requests Expired.
The activity feed and receipt now render "canceled" (this also fixes a
latent bug where a manually-cancelled tx still showed "pending").
Receipt de-duplication: the To/From name rows are shown only when the
counterparty has a real identity (petname or verified NIP-05); a bare
npub now appears once, in the "nostr" row, instead of twice.
38 lib tests pass (2 new for the expiry bucket logic).
Route every relay and HTTP request (nostr relays, NIP-05, price) through
a local nym-socks5-client sidecar on 127.0.0.1:1080, so all traffic
egresses via the 5-hop Nym mixnet and nothing touches the clear net.
- Add src/nym/: SOCKS5 HTTP client (reqwest socks5h), NymWebSocketTransport
for the nostr relay pool (tokio-socks dial + TLS/ws handshake over the
mixnet), and a sidecar launcher that reuses or spawns nym-socks5-client.
- Swap the nostr-sdk transport off ArtiWebSocketTransport; route nip05.rs
and price.rs off Tor; revert the clearnet username-lookup shortcut.
- Remove the embedded arti Tor client wholesale: the onion-service
listener and send-to-onion path in the wallet, the legacy transport
GUI tab, the Tor settings page, src/tor/, the webtunnel pluggable
transport (Go build + submodule), and all arti crates from Cargo.toml.
The Grin node connection is unchanged (chain data, no payment metadata,
and never used Tor). The network requester the sidecar routes through is
configured via GOBLIN_NYM_PROVIDER / NETWORK_REQUESTER at deploy time.
The read-only NIP-05 username lookups (resolve + check_availability) carry no
secret and need no anonymity — routing them over Tor is what made choosing a
username slow. They now use a fast clearnet HTTPS GET (reqwest + rustls/ring,
so it still cross-compiles to Android), falling back to Tor only if clearnet is
blocked. The anonymity-critical paths (slatepack delivery, NIP-98-signed ops)
are untouched.
Every first mention of a NIP or nostr kind in the README now links to
nips.nostr.com (NIPs) or nostrbook.dev (kinds). Add screenshots/ to .gitignore
(local gallery captures for the site).