Skip to content

Conversation

bleetube
Copy link

@bleetube bleetube commented Jun 21, 2025

Project description

This app was previously nostr-wallet-connect which implemented protocols (nip-47 and lud16, among others) for controlling an lnd lightning wallet over the distributed network protocol called nostr. The Alby "hub" expands the scope of the project to include support for more lightning wallet backends in addition to lnd: pheonixd and an internal ldk node.

While Alby started out providing custodial lightning software, they have since developed more self-custodial software such as this.

In full transparency, I've observed some rough edges in the build process, some of which have been smoothed out by deploying this as a nix package. Many other rough edges were smoothed out by updates over the last six months, so I have a lot more confidence in this project as of late.

  • The upstream build process involves pulling in pre-compiled library modules for ldk-node from separate repositories. By contrast, the new nix package handles this gracefully by creating builds inputs for albyhub and building all library modules from source.
  • Minor auditability concern: I'm not a C or GO developer, but I wish go-secp256k1-zkp imported the C library as a gitmodule, rather than maintaining the entire tree in repo. Then it would've been easier for me to reason about any changes they may have made to secp256k1-zkp. I did mention this concern in an issue and was dismissed summarily (possibly for not explaining it well).

Metadata

@bleetube
Copy link
Author

bleetube commented Jun 22, 2025

My testing todo list (WIP)

  • confirm UI works over tor
  • connect to a nix-bitcoin lnd backend
  • run hub using ldk-node backend and confirm it syncs to the gossip network with tor enabled

One thing I skipped was generating a secret for JWT_SECRET, because I don't quite understand when it would ever be necessary and don't use it myself.

@erikarvstedt
Copy link
Collaborator

Thanks! Alby Hub is a great fit for nix-bitcoin.
But due to its complexity, it adds a good amount of extra maintenance load.
Do you think you'll be around in the foreseeable future to help with maintaining the pkg and the module?
Of course I'm also happy to help out.

@bleetube
Copy link
Author

bleetube commented Aug 28, 2025

Do you think you'll be around in the foreseeable future to help with maintaining the pkg and the module?

Long-term: absolutely yes! I'd say most of the time I should be able to turn around updates or maintenance fixes within a few days. This summer I was inundated with work, side projects, and other commitments which is why the PR has sat as a draft for a few weeks. I actually still use hub as my daily driver for nostr zaps, so I’ve been actively keeping the package up to date over the past couple of months. Getting everything working initially took some effort, but now that it’s stable the ongoing maintenance hasn’t been too heavy. While I do occasionally get pulled into other projects, I’m confident I can continue fitting the maintenance work into my routine. This summer was crazy though.

If it’s helpful, I’ve got some notes on updating the package that I’d be happy to share somewhere. There's still some rough spots, like I think the fuzziest part is having to pick an ldk-node commit based on commit date. It turned into an unexpected rabbit hole at first, but once I finally got the package working, I realized I’m pretty much adopting it for the long haul!

… client (which Hub uses) apparently doesn’t understand the socks5h scheme in HTTP_PROXY/HTTPS_PROXY, so even if I set env vars like `HTTPS_PROXY=socks5://127.0.0.1:9050` this is going to want to leak DNS. I'll have to do some testing to confirm this and then try to think of a workaround. All the other packages in nb seem to have native support for tor. I might need to open an issue upstream.
@naturallaw777
Copy link

Thank you for putting this together!

@bleetube
Copy link
Author

bleetube commented Sep 9, 2025

tl;dr a lot of testing finally yielded (possibly) a little progress here. and I need to do a lot of testing and cleanup

hub's go http client honors HTTP(S)PROXY and supports SOCKS5 proxies configured through env vars.

go-nostr's HTTP/WebSocket stack honors HTTP(S)PROXY but does not support SOCKS5 proxies

I haven't run any tests for ldk-node yet, but it has client libs lightning-net-tokio which supports socks but not http proxies, and reqwest which is an http client. There's also bdk_esplora in ldk-node, but esplora-client should be talking to localhost only anyway.

socks5 env vars like HTTP_PROXY=socks5://127.0.0.1:9050 would presumably work for ldk-node, but not go-nostr

Sep 08 15:22:42 squirtle albyhub[342031]: {"iteration":16,"level":"info","msg":"Connecting to the relay","relay_url":"wss://relay.getalby.com/v1","time":"2025-09-08T15:22:42-07:00"}
Sep 08 15:22:49 squirtle albyhub[342031]: {"error":"error opening websocket to 'wss://relay.getalby.com/v1': failed to WebSocket dial: failed to send handshake request: Get \"https://relay.getalby.com/v1\": connection took too long","iteration":16,"level":"error","msg":"Failed to connect to relay","retry_seconds":60,"time":"2025-09-08T15:22:49-07:00"}

torsocks does not work for hub's go http client:

Sep 08 15:54:29 squirtle torify[361169]: {"error":"could not connect to boltz ws at wss://api.boltz.exchange/v2/ws: dial tcp 143.202.162.204:443: i/o timeout","level":"error","msg":"Failed to connect to boltz websocket, retrying in 2s...","time":"2025-09-08T15:54:29-07:00"}
Sep 08 15:55:02 squirtle torify[361169]: {"error":"Get \"https://getalby.com/api/internal/info\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)","level":"error","msg":"Failed to fetch /info","time":"2025-09-08T15:55:02-07:00"}

Go binaries don’t use glibc for networking, so LD_PRELOAD-based wrappers like torsocks/proxychains can’t reliably intercept sockets

Then I tried to get creative. Since go-nostr doesn't support socks5, tried using an http proxy. I used privoxy, even though tor now also has a HTTPTunnelPort option now (I'm assuming it basically behaves the same way).

using env vars to utilize both tor socks5 + privoxy http proxy (ie. ALL_PROXY=socks5 and HTTPS_PROXY=http) still doesn't work for go-nostr (or probably ldk-node for that matter):

root@squirtle /e/nixos# doas -u nobody env HTTPS_PROXY=http://127.0.0.1:8118 curl -I https://relay.getalby.com/v1
HTTP/1.1 200 Connection established

HTTP/2 200 


root@squirtle /e/nixos# systemctl cat --runtime albyhub.service | grep -E '^ExecStart|^Env|^IPAddress' | grep -v PATH
Environment="ALL_PROXY=socks5://127.0.0.1:9050"
Environment="HTTPS_PROXY=http://127.0.0.1:8118"
Environment="HTTP_PROXY=http://127.0.0.1:8118"
Environment="NO_PROXY=127.0.0.1,localhost,::1"
ExecStart=/nix/store/pvpqkj6qzg36nrpp5x3wyx2612g13mjp-albyhub-1.19.2/bin/albyhub
ExecStartPre=+/nix/store/gib8jj2qfdj6fsi6q00drkcr4smcc3mj-albyhub-setup
IPAddressAllow=127.0.0.1/32
IPAddressAllow=::1/128
IPAddressAllow=169.254.0.0/16
IPAddressDeny=any
root@squirtle /e/nixos# journalctl -fu albyhub | grep -i error
Sep 09 10:32:38 squirtle albyhub[388745]: {"error":"error opening websocket to 'wss://relay.getalby.com/v1': failed to WebSocket dial: failed to send handshake request: Get \"https://relay.getalby.com/v1\": connection took too long","iteration":6,"level":"error","msg":"Failed to connect to relay","retry_seconds":60,"time":"2025-09-09T10:32:38-07:00"}

Regarding ldk-node, Lightning P2P via lightning-net-tokio does not honor ALL_PROXY/HTTP(S)PROXY and won’t be torified by those env vars alone.

so I think both go-nostr and ldk-node either need torsocks, or socks support configured/added directly to their tcp client library.

ldk-node uses several dependencies with uneven support for proxy configuration:

  • reqwest (HTTP client) - Supports HTTP_PROXY/HTTPS_PROXY environment variables
  • lightning-net-tokio - Uses tokio-socks for SOCKS proxy support
  • bdk_esplora and esplora-client - HTTP clients that respect proxy settings (although electrum would always be via localhost and not proxied in nix-bitcoin)

I also tried ugly hacks like using both env vars and torsocks, which can result in double proxying. go-nostr was still broken in that configuration.

ldk-node might work if all proxy vars are configured to use tor socks5, or we use torsocks. But torsocks won't work with Go's http client (in hub), so that leaves us with the best option of using socks5 env vars and leaving go-nostr in a broken state.

I think the next move is to submit a PR to go-nostr to add socks5 support.
After that, the big goal would be to ask for (or build) native tor configuration directly in hub.

Oh wait, maybe part of the issue is that you can't do websocket connections over an http tunnel. I tried another workaround where I bridge the websocket connections to get them to talk over socks5 using this syntax:

websocat -b --ping-interval=30 --exit-on-eof \
  --socks5 127.0.0.1:9050 \
  ws-l:127.0.0.1:8485 \
  wss://relay.getalby.com/v1

That's go-nostr -> websocat -> tor. The big downside being that there's no TLS between hub and websocat, but that's inside the local environment at least.

I stopped seeing those errors, so maybe it works. I'll continue testing as time permits.

quick thoughts on some cleanup I should probably do: I don't think anything cares about all_proxy, so I'll probably remove that. I should probably cleanup the service.albyhub.after properties is probably overkill and should be removed too. the getPublicAddressCmd option is probably pointless in nix-bitcoin too. i should probably dump privoxy and use tor's HTTPTunnelPort option. then I should use that for the boltz api calls instead of websocat.

Apologies for the commit spam, I only realized this morning that I can just test my changes without pushing them somewhere by using nix-bitcoin.url = "path:/etc/nixos/nix-bitcoin"; in my flake (and pushing my changes there instead of here).

@bleetube
Copy link
Author

the websocket bridge workaround adds more complexity (and maintenance overhead) than I like and breaks the UX of NWC setup, since the user gets a localhost relay address rather than the configured relay uri. the right move here is to get PRs merged upstream to add socks5 support for websockets to the GO HTTP client libs for go-nostr.

type = with types; nullOr str;
default =
if config.services.mempool.enable then
"http://${nbLib.addressWithPort config.services.mempool.address config.services.mempool.port}"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing the /api path.

Suggested change
"http://${nbLib.addressWithPort config.services.mempool.address config.services.mempool.port}"
"http://${nbLib.addressWithPort config.services.mempool.address config.services.mempool.port}/api"

Thanks for working on this!

@naturallaw777
Copy link

I created this issue on go-nostr: nbd-wtf/go-nostr#191

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants