Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add Getting Started with LDK Node guide - #315

Open
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node
Open

Add Getting Started with LDK Node guide#315
Camillarhi wants to merge 2 commits into
lightningdevkit:mainfrom
Camillarhi:docs/getting-started-with-ldk-node

Conversation

@Camillarhi

@CamillarhiCamillarhi commented Jun 12, 2026

Copy link
Copy Markdown

Adds a guide for building a Lightning node with ldk-node in Rust, covering node setup, channel management, BOLT11/BOLT12 payments, and spontaneous payments against a local Polar regtest network.

Adds:

  • docs/getting-started-with-ldk-node.md
  • sidebar/config entries in docs/.vitepress/config.mts and docs/.vuepress/config.js
  • footer link in docs/.vitepress/theme/components/SiteFooter.vue

🤖 Kotlin code examples generated with Claude Code

@netlify

netlifyBot commented Jun 12, 2026

Copy link
Copy Markdown

Deploy Preview for lightningdevkit ready!

NameLink
🔨 Latest commitca55ab8
🔍 Latest deploy loghttps://app.netlify.com/projects/lightningdevkit/deploys/6a90d1b284f2da00088fd3d6
😎 Deploy Previewhttps://deploy-preview-315--lightningdevkit.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 5ca6ec4 to 16a8ba1CompareJune 13, 2026 02:08

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated code review (Claude Code) — 3 inline comments below on the guide's code samples, plus one issue that can't be anchored inline:

PR body promises a footer link that isn't in the diff. The description says "footer link in docs/.vitepress/theme/components/SiteFooter.vue", but the diff doesn't touch that file and it has no getting-started entry. The footer entry that was added lives in the legacy docs/.vuepress/config.js, which the live VitePress build doesn't read (the build workflow publishes docs/.vitepress/dist). Either add { text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node' } to the Docs column in SiteFooter.vue, or amend the PR description.

Verdict: ready with fixes — structure, flow, and sidebar wiring are sound; the three inline items are small, localized edits to the code samples.

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 16a8ba1 to 004377cCompareJuly 24, 2026 10:31
@ConorOkus

Copy link
Copy Markdown
Contributor

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 004377c to de79bfeCompareJuly 24, 2026 20:36
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from b71b3d3 to 765b70bCompareJuly 24, 2026 20:40
@Camillarhi

Copy link
Copy Markdown
Author

One remaining issue from review, in the persistence guidance:

docs/getting-started-with-ldk-node.md line 310 recommends build_with_sqlite_store() and build_with_postgres_store(), but neither method exists on ldk-node 0.7's Builder — the available options are build() (SQLite is the default backend), build_with_fs_store(), build_with_store(), and the build_with_vss_store* variants. There's no Postgres persistence support.

Suggested rewrite for line 310:

On persistence: build_with_fs_store() persists node state to the filesystem. For production, you would likely prefer the default build() (which uses SQLite) or build_with_vss_store() depending on your storage requirements.

Line 17 repeats the claim ("State is persisted to SQLite, Postgres or the filesystem") — Postgres should be dropped there too.

Everything else from the earlier review round looks resolved in 004377c — thanks!

Thanks. This has been updated

@ConorOkus

Copy link
Copy Markdown
Contributor

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

@Camillarhi

Copy link
Copy Markdown
Author

To avoid confusion, I think it's worth changing the title to "Building on mobile with LDK Node"

I see where you're coming from, but I'd lean away from a mobile-specific title. The article builds against a local Polar regtest setup, and the Kotlin examples use JVM with Gradle and a terminal flow, not Android. A mobile dev could adapt it, but they wouldn't get the mobile-specific setup like Android project setup or the .aar dependency. I think something like 'Getting Started with LDK Node' fits the content better, but open to other ideas.

@ConorOkusConorOkus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this — it's a genuinely useful guide and clearly the product of actually running the thing. I checked every API call in it against the ldk-node 0.7.0 rustdoc and the v0.7.0 UniFFI definitions, and the signatures, arities and units all check out in both languages. The three comments from the last round are all addressed too.

Most of what's below isn't about the API usage — it's about what the guide tells the reader. Since this is official docs, readers will copy both the code and the prose into nodes that hold real funds, so I've weighted it that way.

The things I'd most like to see before merge:

  1. set_storage_dir_path is never called, so the wallet seed and every channel monitor land in /tmp/ldk_node. That's the one with actual funds risk.
  2. The BOLT12 walkthrough can't be completed as written — no onion-message peer is ever connected, and then the flow asks the reader to pay the offer in Polar, which the page itself says Polar can't do. Worth settling the intended Polar topology once; that answers both.
  3. Two statements are factually inverted — the push-amount liquidity direction, and what actually happens when you skip event_handled().

The rest are smaller. Nothing here is a reason not to land it — happy to re-review whenever.

Comment threaddocs/getting-started-with-ldk-node.md Outdated

Note: [LDK Server](https://github.com/lightningdevkit/ldk-server) is being added to Polar ([PR #1374](https://github.com/jamaljsr/polar/pull/1374)). Once merged, you will be able to use it directly as your local node backend, since LDK Server is essentially ldk-node with an RPC interface. It will also work as an onion-message-capable peer for BOLT12 offer creation.

Note: To create BOLT12 offers, your ldk-node needs to connect to an onion-message-capable peer. In Polar, this means adding a CLN node to your network. LND does not currently support onion messages, so offer creation will fail if CLN is not present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This prerequisite is correct, but nothing in the guide ever satisfies it. The code only opens a channel to Node B and never calls node.connect(...), so a reader who follows along lands in create_offer's error branch with no way forward beyond the message "make sure your node is connected."

Two ways out, depending on what your Polar setup actually looked like:

  • If Node B was the CLN node, say so here — then the channel peer is also the onion-message peer and it just works.
  • If CLN was a separate node, add the connect before create_offer:
node.connect(cln_pubkey, cln_addr,true).unwrap();
node.connect(clnPubkey, clnAddr, true)

Related to the comment on line 1247 — one decision about the topology resolves both.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The prerequisite already covers this. Readers are told before any code that offers need an onion-message-capable peer, and in Polar that means a CLN node, and that it'll fail without one. And open_channel connects to the peer anyway, so if they set it up according to this guide, there's nothing extra to add.

If someone is running LND, create_offer hits the error branch rather than panicking, so it prints and carries on. I'd rather leave it like that than hardcode a CLN node into the example, since not everyone runs Polar, and LDK Server works as the peer too once PR #1374 is in.

I also kept the error wording generic on purpose. CLN won't be the only option for long, LDK Server relays onion messages as well since it's ldk-node underneath, so naming CLN in the code would just go stale. The Polar-specific bit also lives in the prerequisite.

println!("Press enter to create a BOLT12 offer for Node B to pay (inbound payment)...");
std::io::stdin().read_line(&mut String::new()).unwrap();
create_offer(Arc::clone(&node));
println!("\nPay the offer from Node B in Polar, then press Enter when done...");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This contradicts the page's own note at line 778 ("Paying a BOLT12 offer end to end through Polar's UI is not currently supported"), which the screenshot caption at line 975 repeats. A reader gets here, is told to pay the offer in Polar, and can't. Same prompt in the Kotlin listing at line 1494, and in the incremental listings at 871 / 930.

Simplest fix is to end the BOLT12 receive demo at offer creation and say plainly that paying it back needs a payer outside this setup — which is what the note already says. Alternatively, drop in a CLN command-line pay flow if you've run one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Dropped that prompt. The receive side now ends at creating the offer, and the send step takes an offer if you have one and skips if you just hit enter.

Kept send_bolt12_payment in because Polar isn't the only way people run this. If you're driving CLN directly it works both ways, so it seemed worth showing even though Polar can't demo it.

What do you think?

Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md Outdated
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md
Comment threaddocs/getting-started-with-ldk-node.md Outdated
children: [
{ text: 'Introduction', link: '/introduction/' },
{ text: 'Building a node with LDK', link: '/building-a-node-with-ldk/introduction/' },
{ text: 'Getting Started with LDK Node', link: '/getting-started-with-ldk-node/' },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, and honestly pre-existing rather than yours: the trailing slash makes this 404.

https://lightningdevkit.org/running-a-sample-ldk-node/ -> 404
https://lightningdevkit.org/running-a-sample-ldk-node -> 200

Every entry in this footer list has the same trailing slash, so it's a site-wide thing, not something this PR introduced — but this adds one more instance. Your sidebar entry in config.mts uses the correct bare form already. Note ignoreDeadLinks: true means the build won't flag it.

Happy for this to be out of scope; just flagging since you're touching the file.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I fixed the pre-existing as a separate commit

@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from 765b70b to 80c1f3dCompareAugust 6, 2026 18:21
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch 2 times, most recently from 4638a73 to f566741CompareAugust 28, 2026 00:02
Walkthrough for building a Lightning node with ldk-node in Rust and kotlin
covering node setup, channel management, BOLT11 and BOLT12 payments, and
spontaneous payments using Polar for a local regtest environment.
@Camillarhi
Camillarhiforce-pushed the docs/getting-started-with-ldk-node branch from f566741 to ca55ab8CompareAugust 28, 2026 00:09
Sign up for freeto 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.

2 participants

@Camillarhi@ConorOkus