Skip to content

Expose Postgres storage backend - #243

Open
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres
Open

Expose Postgres storage backend#243
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman

@benthecarmanbenthecarman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Allow ldk-server to configure LDK Node wallet and channel state storage through PostgreSQL without a feature-gated server build. Keep local disk storage for server-owned files and history, and document the backup boundary.

Refuse switching between SQLite and PostgreSQL after initialization so the same node identity cannot silently start without its persisted channel state. Clarify the backup and per-network isolation boundaries.

@ldk-reviews-bot

ldk-reviews-bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarmanforce-pushed the postgres branch 2 times, most recently from c79c1a4 to a4378eaCompareJuly 12, 2026 19:41
@benthecarman
benthecarman requested review from tankyleo and removed request for valentinewallaceJuly 28, 2026 00:44

@tankyleotankyleo 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.

skimmed briefly one question

Comment threaddocs/configuration.md

When `[storage.postgres]` is configured, LDK Node wallet/channel state is stored in
PostgreSQL instead of `ldk_node_data.sqlite`. The storage directory remains required for
the mnemonic, API key, TLS material, logs, and `ldk_server_data.sqlite`.

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.

Would big routing nodes want ldk_server_data.sqlite to be in the postgres db too, given that they'll be forwarding a lot of payments ? Or is that for a later PR / out-of-scope here ?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

It will be after lightningdevkit/ldk-node#772, just keeping the docs accurate of the local state of the repo. Will update once we merge and upgrade here

@tankyleo
tankyleo self-requested a review July 28, 2026 02:58

# Optional LDK Node PostgreSQL storage for wallet and channel state.
#[storage.postgres]
#connection_string = "postgresql://postgres:postgres@localhost:5432" # PostgreSQL connection string. Do not include dbname if db_name is set.

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.

If it works I find the key value format to be cleaner, less error prone ie host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

tbh i always hate these, but can switch if we really want

Comment threaddocs/configuration.md
```

Only `connection_string` is required. `db_name`, `kv_table_name`, and `certificate_path`
are optional. If `db_name` is set, do not also include a database name in the connection

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.

Don't remember us explicitly rejecting a database name in the connection string anywhere in this patch ? Would it be good to add test coverage for this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

that's in ldk-node, added a test for this

Comment threaddocs/configuration.md

```toml
[storage.postgres]
connection_string = "postgresql://postgres:postgres@localhost:5432"

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.

Similar here, the key-value format would be better if we can have it.

Comment threadldk-server/src/util/config.rs Outdated
|| args.storage_postgres_kv_table_name.is_some()
|| args.storage_postgres_certificate_path.is_some()
{
let mut postgres = self.ldk_node_postgres.take().unwrap_or_default();

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.

Here we can do a get_or_insert_default, so we don't need the assignment at the end of the block.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

Comment threadldk-server/src/util/config.rs Outdated

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.

Similar here can do a get_or_insert_default if you care to :)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

LdkNodeStorageConfig::Postgres {
connection_string: postgres
.connection_string
.ok_or_else(|| missing_field_err("storage_postgres_connection_string"))?,

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.

For this field I wonder if this is a sensible default:
host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Imo probably better to not have a defualt connection string

@joostjager

Copy link
Copy Markdown
Contributor

Posting some AI comments that look sensible. Second one is in flight in lightningdevkit/ldk-node#1000, but maybe it should be a prerequisite.

🤖 Requesting changes for two fund-safety blockers.

  1. The backend is selected only after reusing the existing mnemonic, with no migration or continuity check. Adding [storage.postgres] to an existing SQLite deployment therefore preserves the same Lightning identity while an empty PostgreSQL target creates fresh wallet and channel-manager state and silently ignores ldk_node_data.sqlite. For a node with live channels, starting without its channel monitors can lose channel funds. Please require an explicit verified migration, persist and validate backend identity, or at minimum refuse this switch when local SQLite LDK state exists and the PostgreSQL destination is empty.

  2. The PostgreSQL build path does not acquire exclusive ownership of the database and table. Two deployments with the same mnemonic and target can both pass descriptor and network validation, read the same snapshot, and start the same node identity. The pinned PostgreSQL store uses process-local ordering locks and unconditional upserts, so overlapping instances can interleave stale channel-manager and monitor writes. Please hold a database advisory lock or equivalent lease for the node lifetime and fail the second writer before building the node.

The previous cross-network concern is fail-closed in the pinned dependency because the persisted BDK network is checked during wallet load. The target is still not network-isolated, however, so the operations claim that multiple networks can share one storage root without conflicts should state that PostgreSQL deployments need distinct database or table targets per network.

@benthecarman

Copy link
Copy Markdown
CollaboratorAuthor
  1. Made it so we forbid migrating. As this is 0.1, i think this is fine for now and we can make a proper implementation in the future.
  2. Made Lock Postgres stores on initialization ldk-node#1012

Allow ldk-server to configure LDK Node wallet and channel state
storage through PostgreSQL without a feature-gated server build.
Refuse switching between SQLite and PostgreSQL after initialization so
the same node identity cannot silently start without its persisted
channel state. Clarify the backup and per-network isolation boundaries.
AI-assisted-by: OpenAI Codex
@joostjager

Copy link
Copy Markdown
Contributor

I’m not sure we should rush PostgreSQL support into ldk-server with an interim locking mechanism, only to replace it with a different approach later. That could also leave us with migration or compatibility concerns between deployed versions? lightningdevkit/ldk-node#1012 does not look entirely trivial either.

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.

4 participants

@benthecarman@ldk-reviews-bot@joostjager@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Expose Postgres storage backend by benthecarman · Pull Request #243 · lightningdevkit/ldk-server · GitHub
Skip to content

Expose Postgres storage backend - #243

Open
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres
Open

Expose Postgres storage backend#243
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman

@benthecarmanbenthecarman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Allow ldk-server to configure LDK Node wallet and channel state storage through PostgreSQL without a feature-gated server build. Keep local disk storage for server-owned files and history, and document the backup boundary.

Refuse switching between SQLite and PostgreSQL after initialization so the same node identity cannot silently start without its persisted channel state. Clarify the backup and per-network isolation boundaries.

@ldk-reviews-bot

ldk-reviews-bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarmanforce-pushed the postgres branch 2 times, most recently from c79c1a4 to a4378eaCompareJuly 12, 2026 19:41
@benthecarman
benthecarman requested review from tankyleo and removed request for valentinewallaceJuly 28, 2026 00:44

@tankyleotankyleo 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.

skimmed briefly one question

Comment threaddocs/configuration.md

When `[storage.postgres]` is configured, LDK Node wallet/channel state is stored in
PostgreSQL instead of `ldk_node_data.sqlite`. The storage directory remains required for
the mnemonic, API key, TLS material, logs, and `ldk_server_data.sqlite`.

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.

Would big routing nodes want ldk_server_data.sqlite to be in the postgres db too, given that they'll be forwarding a lot of payments ? Or is that for a later PR / out-of-scope here ?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

It will be after lightningdevkit/ldk-node#772, just keeping the docs accurate of the local state of the repo. Will update once we merge and upgrade here

@tankyleo
tankyleo self-requested a review July 28, 2026 02:58

# Optional LDK Node PostgreSQL storage for wallet and channel state.
#[storage.postgres]
#connection_string = "postgresql://postgres:postgres@localhost:5432" # PostgreSQL connection string. Do not include dbname if db_name is set.

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.

If it works I find the key value format to be cleaner, less error prone ie host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

tbh i always hate these, but can switch if we really want

Comment threaddocs/configuration.md
```

Only `connection_string` is required. `db_name`, `kv_table_name`, and `certificate_path`
are optional. If `db_name` is set, do not also include a database name in the connection

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.

Don't remember us explicitly rejecting a database name in the connection string anywhere in this patch ? Would it be good to add test coverage for this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

that's in ldk-node, added a test for this

Comment threaddocs/configuration.md

```toml
[storage.postgres]
connection_string = "postgresql://postgres:postgres@localhost:5432"

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.

Similar here, the key-value format would be better if we can have it.

Comment threadldk-server/src/util/config.rs Outdated
|| args.storage_postgres_kv_table_name.is_some()
|| args.storage_postgres_certificate_path.is_some()
{
let mut postgres = self.ldk_node_postgres.take().unwrap_or_default();

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.

Here we can do a get_or_insert_default, so we don't need the assignment at the end of the block.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

Comment threadldk-server/src/util/config.rs Outdated

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.

Similar here can do a get_or_insert_default if you care to :)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

LdkNodeStorageConfig::Postgres {
connection_string: postgres
.connection_string
.ok_or_else(|| missing_field_err("storage_postgres_connection_string"))?,

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.

For this field I wonder if this is a sensible default:
host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Imo probably better to not have a defualt connection string

@joostjager

Copy link
Copy Markdown
Contributor

Posting some AI comments that look sensible. Second one is in flight in lightningdevkit/ldk-node#1000, but maybe it should be a prerequisite.

🤖 Requesting changes for two fund-safety blockers.

  1. The backend is selected only after reusing the existing mnemonic, with no migration or continuity check. Adding [storage.postgres] to an existing SQLite deployment therefore preserves the same Lightning identity while an empty PostgreSQL target creates fresh wallet and channel-manager state and silently ignores ldk_node_data.sqlite. For a node with live channels, starting without its channel monitors can lose channel funds. Please require an explicit verified migration, persist and validate backend identity, or at minimum refuse this switch when local SQLite LDK state exists and the PostgreSQL destination is empty.

  2. The PostgreSQL build path does not acquire exclusive ownership of the database and table. Two deployments with the same mnemonic and target can both pass descriptor and network validation, read the same snapshot, and start the same node identity. The pinned PostgreSQL store uses process-local ordering locks and unconditional upserts, so overlapping instances can interleave stale channel-manager and monitor writes. Please hold a database advisory lock or equivalent lease for the node lifetime and fail the second writer before building the node.

The previous cross-network concern is fail-closed in the pinned dependency because the persisted BDK network is checked during wallet load. The target is still not network-isolated, however, so the operations claim that multiple networks can share one storage root without conflicts should state that PostgreSQL deployments need distinct database or table targets per network.

@benthecarman

Copy link
Copy Markdown
CollaboratorAuthor
  1. Made it so we forbid migrating. As this is 0.1, i think this is fine for now and we can make a proper implementation in the future.
  2. Made Lock Postgres stores on initialization ldk-node#1012

Allow ldk-server to configure LDK Node wallet and channel state
storage through PostgreSQL without a feature-gated server build.
Refuse switching between SQLite and PostgreSQL after initialization so
the same node identity cannot silently start without its persisted
channel state. Clarify the backup and per-network isolation boundaries.
AI-assisted-by: OpenAI Codex
@joostjager

Copy link
Copy Markdown
Contributor

I’m not sure we should rush PostgreSQL support into ldk-server with an interim locking mechanism, only to replace it with a different approach later. That could also leave us with migration or compatibility concerns between deployed versions? lightningdevkit/ldk-node#1012 does not look entirely trivial either.

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.

4 participants

@benthecarman@ldk-reviews-bot@joostjager@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Expose Postgres storage backend by benthecarman · Pull Request #243 · lightningdevkit/ldk-server · GitHub
Skip to content

Expose Postgres storage backend - #243

Open
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres
Open

Expose Postgres storage backend#243
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman

@benthecarmanbenthecarman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Allow ldk-server to configure LDK Node wallet and channel state storage through PostgreSQL without a feature-gated server build. Keep local disk storage for server-owned files and history, and document the backup boundary.

Refuse switching between SQLite and PostgreSQL after initialization so the same node identity cannot silently start without its persisted channel state. Clarify the backup and per-network isolation boundaries.

@ldk-reviews-bot

ldk-reviews-bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarmanforce-pushed the postgres branch 2 times, most recently from c79c1a4 to a4378eaCompareJuly 12, 2026 19:41
@benthecarman
benthecarman requested review from tankyleo and removed request for valentinewallaceJuly 28, 2026 00:44

@tankyleotankyleo 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.

skimmed briefly one question

Comment threaddocs/configuration.md

When `[storage.postgres]` is configured, LDK Node wallet/channel state is stored in
PostgreSQL instead of `ldk_node_data.sqlite`. The storage directory remains required for
the mnemonic, API key, TLS material, logs, and `ldk_server_data.sqlite`.

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.

Would big routing nodes want ldk_server_data.sqlite to be in the postgres db too, given that they'll be forwarding a lot of payments ? Or is that for a later PR / out-of-scope here ?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

It will be after lightningdevkit/ldk-node#772, just keeping the docs accurate of the local state of the repo. Will update once we merge and upgrade here

@tankyleo
tankyleo self-requested a review July 28, 2026 02:58

# Optional LDK Node PostgreSQL storage for wallet and channel state.
#[storage.postgres]
#connection_string = "postgresql://postgres:postgres@localhost:5432" # PostgreSQL connection string. Do not include dbname if db_name is set.

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.

If it works I find the key value format to be cleaner, less error prone ie host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

tbh i always hate these, but can switch if we really want

Comment threaddocs/configuration.md
```

Only `connection_string` is required. `db_name`, `kv_table_name`, and `certificate_path`
are optional. If `db_name` is set, do not also include a database name in the connection

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.

Don't remember us explicitly rejecting a database name in the connection string anywhere in this patch ? Would it be good to add test coverage for this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

that's in ldk-node, added a test for this

Comment threaddocs/configuration.md

```toml
[storage.postgres]
connection_string = "postgresql://postgres:postgres@localhost:5432"

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.

Similar here, the key-value format would be better if we can have it.

Comment threadldk-server/src/util/config.rs Outdated
|| args.storage_postgres_kv_table_name.is_some()
|| args.storage_postgres_certificate_path.is_some()
{
let mut postgres = self.ldk_node_postgres.take().unwrap_or_default();

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.

Here we can do a get_or_insert_default, so we don't need the assignment at the end of the block.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

Comment threadldk-server/src/util/config.rs Outdated

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.

Similar here can do a get_or_insert_default if you care to :)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

LdkNodeStorageConfig::Postgres {
connection_string: postgres
.connection_string
.ok_or_else(|| missing_field_err("storage_postgres_connection_string"))?,

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.

For this field I wonder if this is a sensible default:
host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Imo probably better to not have a defualt connection string

@joostjager

Copy link
Copy Markdown
Contributor

Posting some AI comments that look sensible. Second one is in flight in lightningdevkit/ldk-node#1000, but maybe it should be a prerequisite.

🤖 Requesting changes for two fund-safety blockers.

  1. The backend is selected only after reusing the existing mnemonic, with no migration or continuity check. Adding [storage.postgres] to an existing SQLite deployment therefore preserves the same Lightning identity while an empty PostgreSQL target creates fresh wallet and channel-manager state and silently ignores ldk_node_data.sqlite. For a node with live channels, starting without its channel monitors can lose channel funds. Please require an explicit verified migration, persist and validate backend identity, or at minimum refuse this switch when local SQLite LDK state exists and the PostgreSQL destination is empty.

  2. The PostgreSQL build path does not acquire exclusive ownership of the database and table. Two deployments with the same mnemonic and target can both pass descriptor and network validation, read the same snapshot, and start the same node identity. The pinned PostgreSQL store uses process-local ordering locks and unconditional upserts, so overlapping instances can interleave stale channel-manager and monitor writes. Please hold a database advisory lock or equivalent lease for the node lifetime and fail the second writer before building the node.

The previous cross-network concern is fail-closed in the pinned dependency because the persisted BDK network is checked during wallet load. The target is still not network-isolated, however, so the operations claim that multiple networks can share one storage root without conflicts should state that PostgreSQL deployments need distinct database or table targets per network.

@benthecarman

Copy link
Copy Markdown
CollaboratorAuthor
  1. Made it so we forbid migrating. As this is 0.1, i think this is fine for now and we can make a proper implementation in the future.
  2. Made Lock Postgres stores on initialization ldk-node#1012

Allow ldk-server to configure LDK Node wallet and channel state
storage through PostgreSQL without a feature-gated server build.
Refuse switching between SQLite and PostgreSQL after initialization so
the same node identity cannot silently start without its persisted
channel state. Clarify the backup and per-network isolation boundaries.
AI-assisted-by: OpenAI Codex
@joostjager

Copy link
Copy Markdown
Contributor

I’m not sure we should rush PostgreSQL support into ldk-server with an interim locking mechanism, only to replace it with a different approach later. That could also leave us with migration or compatibility concerns between deployed versions? lightningdevkit/ldk-node#1012 does not look entirely trivial either.

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.

4 participants

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

Expose Postgres storage backend - #243

Open
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres
Open

Expose Postgres storage backend#243
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman

@benthecarmanbenthecarman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Allow ldk-server to configure LDK Node wallet and channel state storage through PostgreSQL without a feature-gated server build. Keep local disk storage for server-owned files and history, and document the backup boundary.

Refuse switching between SQLite and PostgreSQL after initialization so the same node identity cannot silently start without its persisted channel state. Clarify the backup and per-network isolation boundaries.

@ldk-reviews-bot

ldk-reviews-bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarmanforce-pushed the postgres branch 2 times, most recently from c79c1a4 to a4378eaCompareJuly 12, 2026 19:41
@benthecarman
benthecarman requested review from tankyleo and removed request for valentinewallaceJuly 28, 2026 00:44

@tankyleotankyleo 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.

skimmed briefly one question

Comment threaddocs/configuration.md

When `[storage.postgres]` is configured, LDK Node wallet/channel state is stored in
PostgreSQL instead of `ldk_node_data.sqlite`. The storage directory remains required for
the mnemonic, API key, TLS material, logs, and `ldk_server_data.sqlite`.

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.

Would big routing nodes want ldk_server_data.sqlite to be in the postgres db too, given that they'll be forwarding a lot of payments ? Or is that for a later PR / out-of-scope here ?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

It will be after lightningdevkit/ldk-node#772, just keeping the docs accurate of the local state of the repo. Will update once we merge and upgrade here

@tankyleo
tankyleo self-requested a review July 28, 2026 02:58

# Optional LDK Node PostgreSQL storage for wallet and channel state.
#[storage.postgres]
#connection_string = "postgresql://postgres:postgres@localhost:5432" # PostgreSQL connection string. Do not include dbname if db_name is set.

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.

If it works I find the key value format to be cleaner, less error prone ie host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

tbh i always hate these, but can switch if we really want

Comment threaddocs/configuration.md
```

Only `connection_string` is required. `db_name`, `kv_table_name`, and `certificate_path`
are optional. If `db_name` is set, do not also include a database name in the connection

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.

Don't remember us explicitly rejecting a database name in the connection string anywhere in this patch ? Would it be good to add test coverage for this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

that's in ldk-node, added a test for this

Comment threaddocs/configuration.md

```toml
[storage.postgres]
connection_string = "postgresql://postgres:postgres@localhost:5432"

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.

Similar here, the key-value format would be better if we can have it.

Comment threadldk-server/src/util/config.rs Outdated
|| args.storage_postgres_kv_table_name.is_some()
|| args.storage_postgres_certificate_path.is_some()
{
let mut postgres = self.ldk_node_postgres.take().unwrap_or_default();

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.

Here we can do a get_or_insert_default, so we don't need the assignment at the end of the block.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

Comment threadldk-server/src/util/config.rs Outdated

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.

Similar here can do a get_or_insert_default if you care to :)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

LdkNodeStorageConfig::Postgres {
connection_string: postgres
.connection_string
.ok_or_else(|| missing_field_err("storage_postgres_connection_string"))?,

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.

For this field I wonder if this is a sensible default:
host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Imo probably better to not have a defualt connection string

@joostjager

Copy link
Copy Markdown
Contributor

Posting some AI comments that look sensible. Second one is in flight in lightningdevkit/ldk-node#1000, but maybe it should be a prerequisite.

🤖 Requesting changes for two fund-safety blockers.

  1. The backend is selected only after reusing the existing mnemonic, with no migration or continuity check. Adding [storage.postgres] to an existing SQLite deployment therefore preserves the same Lightning identity while an empty PostgreSQL target creates fresh wallet and channel-manager state and silently ignores ldk_node_data.sqlite. For a node with live channels, starting without its channel monitors can lose channel funds. Please require an explicit verified migration, persist and validate backend identity, or at minimum refuse this switch when local SQLite LDK state exists and the PostgreSQL destination is empty.

  2. The PostgreSQL build path does not acquire exclusive ownership of the database and table. Two deployments with the same mnemonic and target can both pass descriptor and network validation, read the same snapshot, and start the same node identity. The pinned PostgreSQL store uses process-local ordering locks and unconditional upserts, so overlapping instances can interleave stale channel-manager and monitor writes. Please hold a database advisory lock or equivalent lease for the node lifetime and fail the second writer before building the node.

The previous cross-network concern is fail-closed in the pinned dependency because the persisted BDK network is checked during wallet load. The target is still not network-isolated, however, so the operations claim that multiple networks can share one storage root without conflicts should state that PostgreSQL deployments need distinct database or table targets per network.

@benthecarman

Copy link
Copy Markdown
CollaboratorAuthor
  1. Made it so we forbid migrating. As this is 0.1, i think this is fine for now and we can make a proper implementation in the future.
  2. Made Lock Postgres stores on initialization ldk-node#1012

Allow ldk-server to configure LDK Node wallet and channel state
storage through PostgreSQL without a feature-gated server build.
Refuse switching between SQLite and PostgreSQL after initialization so
the same node identity cannot silently start without its persisted
channel state. Clarify the backup and per-network isolation boundaries.
AI-assisted-by: OpenAI Codex
@joostjager

Copy link
Copy Markdown
Contributor

I’m not sure we should rush PostgreSQL support into ldk-server with an interim locking mechanism, only to replace it with a different approach later. That could also leave us with migration or compatibility concerns between deployed versions? lightningdevkit/ldk-node#1012 does not look entirely trivial either.

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.

4 participants

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

Expose Postgres storage backend - #243

Open
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres
Open

Expose Postgres storage backend#243
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman

@benthecarmanbenthecarman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Allow ldk-server to configure LDK Node wallet and channel state storage through PostgreSQL without a feature-gated server build. Keep local disk storage for server-owned files and history, and document the backup boundary.

Refuse switching between SQLite and PostgreSQL after initialization so the same node identity cannot silently start without its persisted channel state. Clarify the backup and per-network isolation boundaries.

@ldk-reviews-bot

ldk-reviews-bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarmanforce-pushed the postgres branch 2 times, most recently from c79c1a4 to a4378eaCompareJuly 12, 2026 19:41
@benthecarman
benthecarman requested review from tankyleo and removed request for valentinewallaceJuly 28, 2026 00:44

@tankyleotankyleo 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.

skimmed briefly one question

Comment threaddocs/configuration.md

When `[storage.postgres]` is configured, LDK Node wallet/channel state is stored in
PostgreSQL instead of `ldk_node_data.sqlite`. The storage directory remains required for
the mnemonic, API key, TLS material, logs, and `ldk_server_data.sqlite`.

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.

Would big routing nodes want ldk_server_data.sqlite to be in the postgres db too, given that they'll be forwarding a lot of payments ? Or is that for a later PR / out-of-scope here ?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

It will be after lightningdevkit/ldk-node#772, just keeping the docs accurate of the local state of the repo. Will update once we merge and upgrade here

@tankyleo
tankyleo self-requested a review July 28, 2026 02:58

# Optional LDK Node PostgreSQL storage for wallet and channel state.
#[storage.postgres]
#connection_string = "postgresql://postgres:postgres@localhost:5432" # PostgreSQL connection string. Do not include dbname if db_name is set.

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.

If it works I find the key value format to be cleaner, less error prone ie host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

tbh i always hate these, but can switch if we really want

Comment threaddocs/configuration.md
```

Only `connection_string` is required. `db_name`, `kv_table_name`, and `certificate_path`
are optional. If `db_name` is set, do not also include a database name in the connection

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.

Don't remember us explicitly rejecting a database name in the connection string anywhere in this patch ? Would it be good to add test coverage for this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

that's in ldk-node, added a test for this

Comment threaddocs/configuration.md

```toml
[storage.postgres]
connection_string = "postgresql://postgres:postgres@localhost:5432"

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.

Similar here, the key-value format would be better if we can have it.

Comment threadldk-server/src/util/config.rs Outdated
|| args.storage_postgres_kv_table_name.is_some()
|| args.storage_postgres_certificate_path.is_some()
{
let mut postgres = self.ldk_node_postgres.take().unwrap_or_default();

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.

Here we can do a get_or_insert_default, so we don't need the assignment at the end of the block.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

Comment threadldk-server/src/util/config.rs Outdated

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.

Similar here can do a get_or_insert_default if you care to :)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

LdkNodeStorageConfig::Postgres {
connection_string: postgres
.connection_string
.ok_or_else(|| missing_field_err("storage_postgres_connection_string"))?,

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.

For this field I wonder if this is a sensible default:
host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Imo probably better to not have a defualt connection string

@joostjager

Copy link
Copy Markdown
Contributor

Posting some AI comments that look sensible. Second one is in flight in lightningdevkit/ldk-node#1000, but maybe it should be a prerequisite.

🤖 Requesting changes for two fund-safety blockers.

  1. The backend is selected only after reusing the existing mnemonic, with no migration or continuity check. Adding [storage.postgres] to an existing SQLite deployment therefore preserves the same Lightning identity while an empty PostgreSQL target creates fresh wallet and channel-manager state and silently ignores ldk_node_data.sqlite. For a node with live channels, starting without its channel monitors can lose channel funds. Please require an explicit verified migration, persist and validate backend identity, or at minimum refuse this switch when local SQLite LDK state exists and the PostgreSQL destination is empty.

  2. The PostgreSQL build path does not acquire exclusive ownership of the database and table. Two deployments with the same mnemonic and target can both pass descriptor and network validation, read the same snapshot, and start the same node identity. The pinned PostgreSQL store uses process-local ordering locks and unconditional upserts, so overlapping instances can interleave stale channel-manager and monitor writes. Please hold a database advisory lock or equivalent lease for the node lifetime and fail the second writer before building the node.

The previous cross-network concern is fail-closed in the pinned dependency because the persisted BDK network is checked during wallet load. The target is still not network-isolated, however, so the operations claim that multiple networks can share one storage root without conflicts should state that PostgreSQL deployments need distinct database or table targets per network.

@benthecarman

Copy link
Copy Markdown
CollaboratorAuthor
  1. Made it so we forbid migrating. As this is 0.1, i think this is fine for now and we can make a proper implementation in the future.
  2. Made Lock Postgres stores on initialization ldk-node#1012

Allow ldk-server to configure LDK Node wallet and channel state
storage through PostgreSQL without a feature-gated server build.
Refuse switching between SQLite and PostgreSQL after initialization so
the same node identity cannot silently start without its persisted
channel state. Clarify the backup and per-network isolation boundaries.
AI-assisted-by: OpenAI Codex
@joostjager

Copy link
Copy Markdown
Contributor

I’m not sure we should rush PostgreSQL support into ldk-server with an interim locking mechanism, only to replace it with a different approach later. That could also leave us with migration or compatibility concerns between deployed versions? lightningdevkit/ldk-node#1012 does not look entirely trivial either.

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.

4 participants

@benthecarman@ldk-reviews-bot@joostjager@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Expose Postgres storage backend by benthecarman · Pull Request #243 · lightningdevkit/ldk-server · GitHub
Skip to content

Expose Postgres storage backend - #243

Open
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres
Open

Expose Postgres storage backend#243
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman

@benthecarmanbenthecarman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Allow ldk-server to configure LDK Node wallet and channel state storage through PostgreSQL without a feature-gated server build. Keep local disk storage for server-owned files and history, and document the backup boundary.

Refuse switching between SQLite and PostgreSQL after initialization so the same node identity cannot silently start without its persisted channel state. Clarify the backup and per-network isolation boundaries.

@ldk-reviews-bot

ldk-reviews-bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarmanforce-pushed the postgres branch 2 times, most recently from c79c1a4 to a4378eaCompareJuly 12, 2026 19:41
@benthecarman
benthecarman requested review from tankyleo and removed request for valentinewallaceJuly 28, 2026 00:44

@tankyleotankyleo 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.

skimmed briefly one question

Comment threaddocs/configuration.md

When `[storage.postgres]` is configured, LDK Node wallet/channel state is stored in
PostgreSQL instead of `ldk_node_data.sqlite`. The storage directory remains required for
the mnemonic, API key, TLS material, logs, and `ldk_server_data.sqlite`.

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.

Would big routing nodes want ldk_server_data.sqlite to be in the postgres db too, given that they'll be forwarding a lot of payments ? Or is that for a later PR / out-of-scope here ?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

It will be after lightningdevkit/ldk-node#772, just keeping the docs accurate of the local state of the repo. Will update once we merge and upgrade here

@tankyleo
tankyleo self-requested a review July 28, 2026 02:58

# Optional LDK Node PostgreSQL storage for wallet and channel state.
#[storage.postgres]
#connection_string = "postgresql://postgres:postgres@localhost:5432" # PostgreSQL connection string. Do not include dbname if db_name is set.

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.

If it works I find the key value format to be cleaner, less error prone ie host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

tbh i always hate these, but can switch if we really want

Comment threaddocs/configuration.md
```

Only `connection_string` is required. `db_name`, `kv_table_name`, and `certificate_path`
are optional. If `db_name` is set, do not also include a database name in the connection

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.

Don't remember us explicitly rejecting a database name in the connection string anywhere in this patch ? Would it be good to add test coverage for this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

that's in ldk-node, added a test for this

Comment threaddocs/configuration.md

```toml
[storage.postgres]
connection_string = "postgresql://postgres:postgres@localhost:5432"

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.

Similar here, the key-value format would be better if we can have it.

Comment threadldk-server/src/util/config.rs Outdated
|| args.storage_postgres_kv_table_name.is_some()
|| args.storage_postgres_certificate_path.is_some()
{
let mut postgres = self.ldk_node_postgres.take().unwrap_or_default();

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.

Here we can do a get_or_insert_default, so we don't need the assignment at the end of the block.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

Comment threadldk-server/src/util/config.rs Outdated

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.

Similar here can do a get_or_insert_default if you care to :)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

LdkNodeStorageConfig::Postgres {
connection_string: postgres
.connection_string
.ok_or_else(|| missing_field_err("storage_postgres_connection_string"))?,

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.

For this field I wonder if this is a sensible default:
host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Imo probably better to not have a defualt connection string

@joostjager

Copy link
Copy Markdown
Contributor

Posting some AI comments that look sensible. Second one is in flight in lightningdevkit/ldk-node#1000, but maybe it should be a prerequisite.

🤖 Requesting changes for two fund-safety blockers.

  1. The backend is selected only after reusing the existing mnemonic, with no migration or continuity check. Adding [storage.postgres] to an existing SQLite deployment therefore preserves the same Lightning identity while an empty PostgreSQL target creates fresh wallet and channel-manager state and silently ignores ldk_node_data.sqlite. For a node with live channels, starting without its channel monitors can lose channel funds. Please require an explicit verified migration, persist and validate backend identity, or at minimum refuse this switch when local SQLite LDK state exists and the PostgreSQL destination is empty.

  2. The PostgreSQL build path does not acquire exclusive ownership of the database and table. Two deployments with the same mnemonic and target can both pass descriptor and network validation, read the same snapshot, and start the same node identity. The pinned PostgreSQL store uses process-local ordering locks and unconditional upserts, so overlapping instances can interleave stale channel-manager and monitor writes. Please hold a database advisory lock or equivalent lease for the node lifetime and fail the second writer before building the node.

The previous cross-network concern is fail-closed in the pinned dependency because the persisted BDK network is checked during wallet load. The target is still not network-isolated, however, so the operations claim that multiple networks can share one storage root without conflicts should state that PostgreSQL deployments need distinct database or table targets per network.

@benthecarman

Copy link
Copy Markdown
CollaboratorAuthor
  1. Made it so we forbid migrating. As this is 0.1, i think this is fine for now and we can make a proper implementation in the future.
  2. Made Lock Postgres stores on initialization ldk-node#1012

Allow ldk-server to configure LDK Node wallet and channel state
storage through PostgreSQL without a feature-gated server build.
Refuse switching between SQLite and PostgreSQL after initialization so
the same node identity cannot silently start without its persisted
channel state. Clarify the backup and per-network isolation boundaries.
AI-assisted-by: OpenAI Codex
@joostjager

Copy link
Copy Markdown
Contributor

I’m not sure we should rush PostgreSQL support into ldk-server with an interim locking mechanism, only to replace it with a different approach later. That could also leave us with migration or compatibility concerns between deployed versions? lightningdevkit/ldk-node#1012 does not look entirely trivial either.

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.

4 participants

@benthecarman@ldk-reviews-bot@joostjager@tankyleo
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Expose Postgres storage backend by benthecarman · Pull Request #243 · lightningdevkit/ldk-server · GitHub
Skip to content

Expose Postgres storage backend - #243

Open
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres
Open

Expose Postgres storage backend#243
benthecarman wants to merge 1 commit into
lightningdevkit:mainfrom
benthecarman:postgres

Conversation

@benthecarman

@benthecarmanbenthecarman commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Allow ldk-server to configure LDK Node wallet and channel state storage through PostgreSQL without a feature-gated server build. Keep local disk storage for server-owned files and history, and document the backup boundary.

Refuse switching between SQLite and PostgreSQL after initialization so the same node identity cannot silently start without its persisted channel state. Clarify the backup and per-network isolation boundaries.

@ldk-reviews-bot

ldk-reviews-bot commented Jul 7, 2026

Copy link
Copy Markdown

👋 Thanks for assigning @tankyleo as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@benthecarman
benthecarmanforce-pushed the postgres branch 2 times, most recently from c79c1a4 to a4378eaCompareJuly 12, 2026 19:41
@benthecarman
benthecarman requested review from tankyleo and removed request for valentinewallaceJuly 28, 2026 00:44

@tankyleotankyleo 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.

skimmed briefly one question

Comment threaddocs/configuration.md

When `[storage.postgres]` is configured, LDK Node wallet/channel state is stored in
PostgreSQL instead of `ldk_node_data.sqlite`. The storage directory remains required for
the mnemonic, API key, TLS material, logs, and `ldk_server_data.sqlite`.

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.

Would big routing nodes want ldk_server_data.sqlite to be in the postgres db too, given that they'll be forwarding a lot of payments ? Or is that for a later PR / out-of-scope here ?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

It will be after lightningdevkit/ldk-node#772, just keeping the docs accurate of the local state of the repo. Will update once we merge and upgrade here

@tankyleo
tankyleo self-requested a review July 28, 2026 02:58

# Optional LDK Node PostgreSQL storage for wallet and channel state.
#[storage.postgres]
#connection_string = "postgresql://postgres:postgres@localhost:5432" # PostgreSQL connection string. Do not include dbname if db_name is set.

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.

If it works I find the key value format to be cleaner, less error prone ie host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

tbh i always hate these, but can switch if we really want

Comment threaddocs/configuration.md
```

Only `connection_string` is required. `db_name`, `kv_table_name`, and `certificate_path`
are optional. If `db_name` is set, do not also include a database name in the connection

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.

Don't remember us explicitly rejecting a database name in the connection string anywhere in this patch ? Would it be good to add test coverage for this?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

that's in ldk-node, added a test for this

Comment threaddocs/configuration.md

```toml
[storage.postgres]
connection_string = "postgresql://postgres:postgres@localhost:5432"

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.

Similar here, the key-value format would be better if we can have it.

Comment threadldk-server/src/util/config.rs Outdated
|| args.storage_postgres_kv_table_name.is_some()
|| args.storage_postgres_certificate_path.is_some()
{
let mut postgres = self.ldk_node_postgres.take().unwrap_or_default();

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.

Here we can do a get_or_insert_default, so we don't need the assignment at the end of the block.

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

Comment threadldk-server/src/util/config.rs Outdated

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.

Similar here can do a get_or_insert_default if you care to :)

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

done

LdkNodeStorageConfig::Postgres {
connection_string: postgres
.connection_string
.ok_or_else(|| missing_field_err("storage_postgres_connection_string"))?,

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.

For this field I wonder if this is a sensible default:
host=localhost port=5432 user=postgres sslmode=disable

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

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

Imo probably better to not have a defualt connection string

@joostjager

Copy link
Copy Markdown
Contributor

Posting some AI comments that look sensible. Second one is in flight in lightningdevkit/ldk-node#1000, but maybe it should be a prerequisite.

🤖 Requesting changes for two fund-safety blockers.

  1. The backend is selected only after reusing the existing mnemonic, with no migration or continuity check. Adding [storage.postgres] to an existing SQLite deployment therefore preserves the same Lightning identity while an empty PostgreSQL target creates fresh wallet and channel-manager state and silently ignores ldk_node_data.sqlite. For a node with live channels, starting without its channel monitors can lose channel funds. Please require an explicit verified migration, persist and validate backend identity, or at minimum refuse this switch when local SQLite LDK state exists and the PostgreSQL destination is empty.

  2. The PostgreSQL build path does not acquire exclusive ownership of the database and table. Two deployments with the same mnemonic and target can both pass descriptor and network validation, read the same snapshot, and start the same node identity. The pinned PostgreSQL store uses process-local ordering locks and unconditional upserts, so overlapping instances can interleave stale channel-manager and monitor writes. Please hold a database advisory lock or equivalent lease for the node lifetime and fail the second writer before building the node.

The previous cross-network concern is fail-closed in the pinned dependency because the persisted BDK network is checked during wallet load. The target is still not network-isolated, however, so the operations claim that multiple networks can share one storage root without conflicts should state that PostgreSQL deployments need distinct database or table targets per network.

@benthecarman

Copy link
Copy Markdown
CollaboratorAuthor
  1. Made it so we forbid migrating. As this is 0.1, i think this is fine for now and we can make a proper implementation in the future.
  2. Made Lock Postgres stores on initialization ldk-node#1012

Allow ldk-server to configure LDK Node wallet and channel state
storage through PostgreSQL without a feature-gated server build.
Refuse switching between SQLite and PostgreSQL after initialization so
the same node identity cannot silently start without its persisted
channel state. Clarify the backup and per-network isolation boundaries.
AI-assisted-by: OpenAI Codex
@joostjager

Copy link
Copy Markdown
Contributor

I’m not sure we should rush PostgreSQL support into ldk-server with an interim locking mechanism, only to replace it with a different approach later. That could also leave us with migration or compatibility concerns between deployed versions? lightningdevkit/ldk-node#1012 does not look entirely trivial either.

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.

4 participants

@benthecarman@ldk-reviews-bot@joostjager@tankyleo