Foundation slice for making Tor a wallet-level user setting (Phase 2, slice 1 of the per-user-tor plan). Transport/config only; no onboarding/settings UI, relay-set swap, or nprofile in this slice. Wallet-level tor_enabled (tri-state): - NostrConfig gains tor_enabled: Option<bool> with the existing Option + serde(default) pattern. tor_enabled() resolves None -> ON, so every legacy nostr.toml keeps Tor with no migration; new wallets write an explicit value at onboarding (later slice). Adds tor_enabled_is_set() + set_tor_enabled() and a back-compat/tri-state unit test. Clearnet path (the big lift, rebuilt after the Nym removal took it out): - ClearnetWebSocketTransport parallel to TorWebSocketTransport: direct TLS websocket, honoring the user's AppConfig SOCKS5 proxy (tokio-socks, already in the tree) so a VPN/proxy user can front their traffic. Shares ws_config/ split_ws with the Tor transport. - Transport selection moved into run_service: the wallet's resolved tor_enabled() picks Tor vs clearnet when the one pool is built; the Tor-bootstrap wait is skipped for clearnet wallets. restart() re-enters here, so a settings toggle rebuilds the pool on the new transport. - Process-global route flag (tor::set_route_over_tor/route_over_tor), mirrored from the open wallet like set_home_domain, so free-function HTTP callers pick the matching transport off-thread. - tor::http_request_bytes takes a clearnet branch when the wallet is Tor-off (no more hard-fail on "Tor not bootstrapped"); new http::clearnet_request_bytes reuses the existing HttpClient (proxy-aware) with the same redirect behavior and browser-like UA. NIP-05, price, relay-pool gist and NIP-11 probes all ride it unchanged. Status/readiness: - New TransportStatus enum + NostrService::transport_status()/tor_routing(): exposes a "Connected (direct)" clearnet state so a Tor-off wallet does not read as "connecting over Tor" forever. The three UI status lines are rewired to it in a later slice; the state is exposed cleanly now. Grin node path unchanged (stays clearnet always). cargo check --all-targets green; targeted config/nostr/tor tests pass.
Goblin
Goblin is a private, pay-by-username wallet for GRIN ツ - confidential digital cash on Mimblewimble, with no amounts or addresses on the chain.
Instead of passing slatepack files back and forth, you pay a username (or an npub) and the payment is delivered for you as an end-to-end encrypted message over nostr, routed through Tor. Relays only ever see ciphertext - never the amount, the sender, or the recipient. Tor hides your IP from the relay; the relay and encryption hide the rest - content, sender, timing.
Goblin is a fork of the Grim egui GRIN wallet: it keeps Grim's full GRIN node/wallet engine and layers a Nostr-native, mobile-first payments experience on top.
What it does
- Send to people - pay a
usernameornpub; the GRIN slatepack travels as a NIP-17 gift-wrapped DM (kind 1059) over Tor and is applied automatically by the recipient's wallet. No files to swap, no need to both be online at once. - Manual slatepacks too - when you need to pay or get paid without a handle, Settings → Wallet → Slatepacks exposes the classic by-hand flow: create a slatepack to send, or paste one to receive, finalize, or pay.
- Open-to-pay links - a
goblin:ornostr:pay link, or a scanned checkout QR, opens the wallet straight to a prefilled review screen (recipient, amount and note filled in, ready to hold-to-send) on desktop, macOS and Android. - Proofs on request - payments can include a native Grin payment proof when the payment request asks for one, off by default, shown on the review screen. An ordinary person-to-person send carries none.
- In-app identity - a nostr payment key that is deliberately not part of your seed, so you can rotate it any time to stay unlinkable without touching your funds. An optional human-readable
namecomes from the goblin.st identity service. - Private by construction - GRIN's address-less, confidential chain; your payments and identity (nostr relays, NIP-05 lookups, price) are routed through Tor, so who-pays-whom never touches the clear net. The GRIN node connection - block sync and broadcasting your transaction - is direct: public chain data, the same for everyone, and not tied to your identity. Keys, names and history stay on your device.
- Configurable amount pairing - show balances against a world currency, Bitcoin, or sats (rates fetched over Tor), or turn the preview off.
- News on Home - the latest post from the official Goblin news key (a kind 30023 long-form article) appears on the Home screen in your wallet's language, falling back to English; it stays hidden when there is nothing to show, and only ever shows the newest article.
- Cross-platform - Linux, macOS, Windows, Android, built in pure Rust on egui.
How a payment travels
you ──slatepack──▶ NIP-17 gift wrap (kind 1059, NIP-44 encrypted)
│
Tor
│
┌─────────────┴─────────────┐
your relays recipient's DM relays (kind 10050)
└─────────────┬─────────────┘
▼
recipient ◀──unwrap, verify seal author, apply slatepack
The wrap is NIP-44-encrypted, and delivery uses the recipient's DM relay list (kind 10050). Tor hides your IP from the relay; the relay and the encryption above hide the rest - content, sender, timing.
Both parties only need one relay in common. The default set is the Floonet relay (relay.floonet.dev) plus Tor-friendly public relays (relay.0xchat.com, offchain.pub) that accept connections from Tor exits, and the set is editable in Settings → Relays.
Build
Desktop (Linux / macOS / Windows)
Goblin links Tor in-process via arti - the wallet is a single self-contained binary, no sidecar, nothing separate to install:
git submodule update --init --recursive
cargo build --release
./target/release/goblin
Goblin's identity and payment traffic (nostr relays, NIP-05 lookups and price fetches) rides Tor: every relay, the money-path relay included, is reached over a Tor exit to its ordinary clearnet host. The GRIN node connection (block sync and transaction broadcast) is not routed through Tor: it connects directly, as it carries only public chain data that isn't linked to your wallet.
Android
Install the Android SDK / NDK, then from the repo root:
./scripts/android.sh build|release v7|v8|x86
v7/v8/x86 is the device CPU architecture for build; for release pass a version in major.minor.patch form.
Identity service (goblin-nip05d)
The optional name service lives in goblin-nip05d/ (axum + SQLite) and is deployed at goblin.st. It implements NIP-05 resolution, NIP-98-authenticated registration and release (names are never transferred - on a key rotation you release the old name and re-register, or import your existing identity). The wallet is fully usable - and fully anonymous - without it. Avatars aren't stored or served - clients render them from the pubkey (an npub gradient with the username's first letter, else the Grin mark).
License
Apache License v2.0.
Credits
🤖 Built with AI pair-programming assistance (Claude)
The underlying cross-platform GRIN wallet engine is the upstream Grim project.
Simplified and Traditional Chinese translations by jasperli2026 (https://github.com/jasperli2026).
