Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,8 @@ func (api *api) GetInfo(ctx context.Context) (*InfoResponse, error) {

info.NodeAlias, _ = api.cfg.Get("NodeAlias", "")

info.TorEnabled = backendType == config.LDKBackendType && api.cfg.GetEnv().LDKTorEnabled

return &info, nil
}

Expand Down
1 change: 1 addition & 0 deletions api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ type InfoResponse struct {
Relays []InfoResponseRelay `json:"relays"`
NodeAlias string `json:"nodeAlias"`
MempoolUrl string `json:"mempoolUrl"`
TorEnabled bool `json:"torEnabled"`
}

type UpdateSettingsRequest struct {
Expand Down
7 changes: 7 additions & 0 deletions config/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ type AppConfig struct {
AutoUnlockPassword string `envconfig:"AUTO_UNLOCK_PASSWORD"`
LogDBQueries bool `envconfig:"LOG_DB_QUERIES" default:"false"`
BoltzApi string `envconfig:"BOLTZ_API" default:"https://api.boltz.exchange"`
LDKTorEnabled bool `envconfig:"LDK_TOR_ENABLED" default:"false"`
LDKTorControlHost string `envconfig:"LDK_TOR_CONTROL_HOST" default:"127.0.0.1"`
LDKTorControlPort int `envconfig:"LDK_TOR_CONTROL_PORT" default:"9051"`
LDKTorControlPassword string `envconfig:"LDK_TOR_CONTROL_PASSWORD"`
LDKTorTargetHost string `envconfig:"LDK_TOR_TARGET_HOST" default:"127.0.0.1"`
LDKTorSocksHost string `envconfig:"LDK_TOR_SOCKS_HOST"`
LDKTorSocksPort int `envconfig:"LDK_TOR_SOCKS_PORT" default:"9050"`
}

func (c *AppConfig) IsDefaultClientId() bool {
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/layouts/SettingsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ export default function SettingsLayout() {
{info && !info.albyAccountConnected && (
<MenuItem to="/alby/account">Alby Account</MenuItem>
)}
{info?.backendType === "LDK" && (
<MenuItem to="/settings/tor">Tor</MenuItem>
)}
<MenuItem to="/settings/developer">Developer</MenuItem>
<MenuItem to="/settings/debug-tools">Debug Tools</MenuItem>
<MenuItem to="/settings/about">About</MenuItem>
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { ChangeUnlockPassword } from "src/screens/settings/ChangeUnlockPassword"
import DebugTools from "src/screens/settings/DebugTools";
import DeveloperSettings from "src/screens/settings/DeveloperSettings";
import Settings from "src/screens/settings/Settings";
import TorSettings from "src/screens/settings/TorSettings";

import { ImportMnemonic } from "src/screens/setup/ImportMnemonic";
import { RestoreNode } from "src/screens/setup/RestoreNode";
Expand Down Expand Up @@ -266,6 +267,11 @@ const routes: RouteObject[] = [
path: "debug-tools",
element: <DebugTools />,
},
{
path: "tor",
element: <TorSettings />,
handle: { crumb: () => "Tor" },
},
],
},
],
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/screens/channels/Channels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,25 @@ export default function Channels() {
</div>
</DropdownMenuItem>
)}
{nodeConnectionInfo?.torAddress &&
nodeConnectionInfo?.torPort && (
<DropdownMenuItem>
<div
className="flex flex-row gap-2 items-center w-full cursor-pointer"
onClick={() => {
const torUri = `${nodeConnectionInfo.pubkey}@${nodeConnectionInfo.torAddress}:${nodeConnectionInfo.torPort}`;
copyToClipboard(torUri);
}}
>
<div>Tor URI</div>
<div className="overflow-hidden text-ellipsis flex-1 text-muted-foreground text-xs">
{nodeConnectionInfo.pubkey.substring(0, 6)}...@
{nodeConnectionInfo.torAddress?.substring(0, 8)}...
</div>
<CopyIcon className="shrink-0 size-4" />
</div>
</DropdownMenuItem>
)}
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
Expand Down
133 changes: 133 additions & 0 deletions frontend/src/screens/settings/TorSettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { CopyIcon, GlobeIcon } from "lucide-react";
import Loading from "src/components/Loading";
import SettingsHeader from "src/components/SettingsHeader";
import { Badge } from "src/components/ui/badge";
import { Button } from "src/components/ui/button";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "src/components/ui/card";
import { useInfo } from "src/hooks/useInfo";
import { useNodeConnectionInfo } from "src/hooks/useNodeConnectionInfo";
import { copyToClipboard } from "src/lib/clipboard";

export default function TorSettings() {
const { data: info } = useInfo();
const { data: nodeConnectionInfo } = useNodeConnectionInfo();

if (!info) {
return <Loading />;
}

const torAddress = nodeConnectionInfo?.torAddress;
const torPort = nodeConnectionInfo?.torPort;
const hasTor = !!torAddress && torPort !== undefined;
const torUri = hasTor
? `${nodeConnectionInfo?.pubkey}@${torAddress}:${torPort}`
: "";
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return (
<>
<SettingsHeader
title="Tor"
description="Tor hidden service for accepting incoming peer connections."
/>
<div className="mt-6 space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<GlobeIcon className="size-5" />
Tor Hidden Service
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{!info.torEnabled && (
<div className="space-y-2">
<p className="text-muted-foreground text-sm">
Tor is not enabled. To enable incoming peer connections via
Tor, set the{" "}
<code className="bg-muted px-1 py-0.5 rounded text-xs">
LDK_TOR_ENABLED=true
</code>{" "}
environment variable and restart your node.
</p>
<p className="text-muted-foreground text-sm">
You also need a Tor daemon running with the control port
accessible. On Umbrel, Tor is already running. Configure the
control host/port with{" "}
<code className="bg-muted px-1 py-0.5 rounded text-xs">
LDK_TOR_CONTROL_HOST
</code>{" "}
and{" "}
<code className="bg-muted px-1 py-0.5 rounded text-xs">
LDK_TOR_CONTROL_PORT
</code>
.
</p>
</div>
)}

{info.torEnabled && (
<div className="space-y-4">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">Status:</span>
{hasTor ? (
<Badge variant="default">Active</Badge>
) : (
<Badge variant="secondary">Connecting...</Badge>
)}
</div>

{hasTor && (
<>
<div className="space-y-2">
<label className="text-sm font-medium">
Onion Address
</label>
<div className="flex items-center gap-2">
<code className="bg-muted px-3 py-2 rounded text-xs break-all flex-1">
{torAddress}
</code>
<Button
variant="outline"
size="icon"
onClick={() => copyToClipboard(torAddress)}
>
<CopyIcon className="size-4" />
</Button>
</div>
</div>

<div className="space-y-2">
<label className="text-sm font-medium">
Node URI (Tor)
</label>
<p className="text-muted-foreground text-xs">
Share this URI with other Lightning nodes to let them
connect to you directly.
</p>
<div className="flex items-center gap-2">
<code className="bg-muted px-3 py-2 rounded text-xs break-all flex-1">
{torUri}
</code>
<Button
variant="outline"
size="icon"
onClick={() => copyToClipboard(torUri)}
>
<CopyIcon className="size-4" />
</Button>
</div>
</div>
</>
)}
</div>
)}
</CardContent>
</Card>
</div>
</>
);
}
3 changes: 3 additions & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export interface InfoResponse {
nodeAlias: string;
mempoolUrl: string;
bitcoinDisplayFormat: BitcoinDisplayFormat;
torEnabled: boolean;
}

export type BitcoinDisplayFormat = "sats" | "bip177";
Expand Down Expand Up @@ -325,6 +326,8 @@ export type NodeConnectionInfo = {
pubkey: string;
address: string;
port: number;
torAddress?: string;
torPort?: number;
};

export type ConnectPeerRequest = {
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/adrg/xdg v0.5.3
github.com/btcsuite/btcd v0.24.3-0.20250318170759-4f4ea81776d6
github.com/btcsuite/btcd/btcutil v1.1.6
github.com/cretz/bine v0.2.0
github.com/elnosh/gonuts v0.4.2
github.com/getAlby/ldk-node-go v0.0.0-20260106083454-34a77eb123bb
github.com/go-gormigrate/gormigrate/v2 v2.1.5
Expand Down Expand Up @@ -268,3 +269,6 @@ require (
// We want to format raw bytes as hex instead of base64. The forked version
// allows us to specify that as an option.
replace google.golang.org/protobuf => github.com/lightninglabs/protobuf-go-hex-display v1.30.0-hex-display

// Tor SOCKS5 outbound support for .onion peer connections
replace github.com/getAlby/ldk-node-go => github.com/sat-engineer/ldk-node-go v0.0.0-20260207204735-907daba13824
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cretz/bine v0.2.0 h1:8GiDRGlTgz+o8H9DSnsl+5MeBK4HsExxgl6WgzOCuZo=
github.com/cretz/bine v0.2.0/go.mod h1:WU4o9QR9wWp8AVKtTM1XD5vUHkEqnf2vVSo6dBqbetI=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -183,8 +185,6 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/getAlby/ldk-node-go v0.0.0-20260106083454-34a77eb123bb h1:ilevBkWdRmk63ach1UBrPwbMKvbK65njlnS0VO+1daw=
github.com/getAlby/ldk-node-go v0.0.0-20260106083454-34a77eb123bb/go.mod h1:8BRjtKcz8E0RyYTPEbMS8VIdgredcGSLne8vHDtcRLg=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gormigrate/gormigrate/v2 v2.1.5 h1:1OyorA5LtdQw12cyJDEHuTrEV3GiXiIhS4/QTTa/SM8=
github.com/go-gormigrate/gormigrate/v2 v2.1.5/go.mod h1:mj9ekk/7CPF3VjopaFvWKN2v7fN3D9d3eEOAXRhi/+M=
Expand Down Expand Up @@ -578,6 +578,8 @@ github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OK
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
github.com/sat-engineer/ldk-node-go v0.0.0-20260207204735-907daba13824 h1:SKSnK9b/tj21noKdkSuuEHTd6gjQ6/QcDdfS6HmEJOw=
github.com/sat-engineer/ldk-node-go v0.0.0-20260207204735-907daba13824/go.mod h1:8BRjtKcz8E0RyYTPEbMS8VIdgredcGSLne8vHDtcRLg=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
Expand Down Expand Up @@ -753,6 +755,7 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
Expand Down Expand Up @@ -795,6 +798,7 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
Expand Down
Loading