Every captured commit receives deterministic security triage and a separate communication-quality score. Security candidates and broader second-pass signals receive full-patch Ollama analysis.
Message quality measures whether a commit identifies its scope, purpose, rationale, testing, and supporting references. It does not change the security-severity score.
This commit tightens how LND processes invoice payments, especially for newer multi-path (MPP) and AMP invoices, keysend payments, and replayed payments. It adds checks that ensure the right payment preimage is used for each invoice type a…
Preimage validation added for AMP and regular invoice replaysAMP invoices forced into MPP update path, preventing legacy-path processingLegacy settlement now fails when invoice-level preimage is missing
This commit tightens how LND handles invoice payment updates, especially for newer 'AMP' invoices and old-style 'keysend' payments. It adds checks so that the wrong kind of payment cannot be processed against the wrong kind of invoice, and…
Added preimage/hash mismatch checks for both regular and AMP invoice replaysAMP records now require an MPP payload, preventing AMP processing on the legacy pathLegacy path now rejects AMP invoices and invoices missing invoice-level preimages
This commit adds support in LND for a new Lightning protocol message called InvoiceError. It is used to politely tell another node why their payment invoice or invoice request was rejected, sent privately through an onion-routed message. T…
New unsigned onion message type added with no cryptographic signature or bech32 formWriter-side validation prevents empty or non-UTF-8 error strings and disallowed suggested_value without erroneous_fieldReader-side BOLT 1 must-understand rule enforced: unknown even TLVs rejected, unknown odd TLVs tolerated
This commit adds validation checks for BOLT 12 invoices in the LND Lightning node software. It ensures invoices contain required fields (creation time, amount, payment hash, node ID, payment paths), match their originating invoice requests…
New validation gate added to Invoice.Encode() to reject malformed invoices before serializationReader rejects unknown even invoice TLV types and unknown even feature bitsReader enforces chain compatibility against activeChain
This commit adds new code to support BOLT 12 invoices in the LND Lightning node. It introduces a data structure, encoding/decoding logic, and helper functions to filter fallback addresses and blinded payment paths. There is no bug fix or s…
This commit is a hardening and refactoring change to a GitHub Actions workflow that detects duplicate issues. It splits the workflow into two jobs: one that only reads issue data and uses an AI model to find duplicates, and a second that o…
Principle of least privilege: AI/model job no longer holds issues:write or id-token:writeAction dependency pinned to full commit SHA instead of mutable tagpersist-credentials: false set on checkout steps
This commit is a hardening and cleanup of a GitHub Actions workflow that automatically labels pull requests by severity. It does not change any LND node code, wallet logic, or network protocol. Instead, it splits the workflow into two jobs…
Principle of least privilege: write token moved out of the model-bearing jobUntrusted input (model-generated comment) sanitized before privileged API useExternal action pinned to immutable commit SHA instead of mutable tag
This commit updates a GitHub Actions workflow for an optional code-review bot called 'gateway' from version 0.4.4 to 0.5.0. It adds support for replying to inline review comments (not just regular PR comments) and pins the new action and r…
Workflow-only change with no modifications to LND application codeAction and runtime pinned to immutable commit SHAs (supply-chain mitigation)New pull_request_review_comment trigger added; commit message asserts same fork-PR secret safety as issue_comment
This commit is a straightforward internal code cleanup: it changes the lnwallet package to use the OpenChannel type from a dedicated chanstate package instead of getting it indirectly through the channeldb package. There is no change to us…
This commit is a straightforward internal code cleanup in LND's channel-opening machinery. It swaps one internal type name (channeldb.OpenChannel) for another (chanstate.OpenChannel) across function signatures in the funding manager and it…
This is a small internal code cleanup in LND's wallet RPC server. It changes one helper function to use a newer internal package type (chanstate.OpenChannel) instead of an older compatibility alias (channeldb.OpenChannel). There is no user…
This commit fixes a bug in how the Lightning Network Daemon (LND) copies payment channel data. When the program made a copy of an HTLC (a pending payment in a Lightning channel), it failed to copy several important fields and did not prope…
Incomplete deep copy of security-relevant channel stateMissing fields in HTLC clone (RHash, OnionBlob, HtlcIndex, LogIndex)Nil-slice copy bug for Signature and ExtraData
This commit is a routine internal code cleanup in the LND Lightning node software. It removes temporary generic type parameters from channel-state database interfaces and replaces them with direct references to the concrete OpenChannel typ…
This commit is a routine code reorganization: it moves two helper functions and a constant related to Taproot channels from one internal package (channeldb) to another (chanstate), and leaves aliases in the old location so existing callers…
This commit is a code cleanup: it removes a forwarding-package helper object from the in-memory channel state and instead creates it on demand inside database methods. There is no direct security fix or vulnerability being patched. It main…
No security-relevant keywords in commit title or messageNo changes to cryptographic operations, authentication, or network parsingRefactoring only: field removal and localized object construction
This commit is a pure internal code reorganization. It moves several small channel-related data types (like channel configuration, status flags, close summaries, and helper types) from the channeldb package into a new chanstate package, th…
This commit adds a new RPC called SubmitPackage to LND's WalletKit. It lets users submit a group of related Bitcoin transactions together so a zero-fee parent can be accepted because a later child transaction pays its fee. This is a featur…
New RPC endpoint gated by onchain:write macaroon permissionPackage size bounded to 25 transactions to limit deserialization workFee-rate ceiling passed through to backend; explicit 0 disables limit
This commit adds a new command-line tool called `lncli wallet submitpackage` that lets users hand one or more raw Bitcoin transactions to LND's wallet service so they can be submitted to the network as a group (a "package"). The change onl…
No security-relevant signals present in the diff or commit message.New CLI command is a thin wrapper around an existing RPC.No changes to validation, authentication, authorization, or network handling.
This commit only adds a new integration test for an existing LND WalletKit RPC called SubmitPackage. It does not change production code, wallet logic, or network behavior. The test verifies that a zero-fee Bitcoin transaction can be accept…
This commit adds validation checks for BOLT 12 invoice requests in the LND Lightning node. It ensures that invoice requests follow protocol rules when being created (written) and received (read), rejecting malformed or non-compliant reques…
New input validation functions added for protocol messagesValidation now runs before encoding, preventing malformed outbound messagesOverflow guard added for amount*quantity calculation
waitForWalletSync used time.Tick inside the poll loop, leaking a new goroutine on every iteration. Over 5 reorg cycles with ~300 polls each this accumulated up to 1500 leaked goroutines, adding measurable system load that made the 30s timeout too tight, especially when running against a postgres backend where block-processing writes carry more overhead.
Fix the leak by using a single time.NewTicker (deferred Stop), and raise the timeout to 2 minutes to give the neutrino P2P layer and the address-manager transaction walk enough headroom under load.
Also improve the timeout error messages to identify which of the two sync layers was stuck: - Layer 1 (header/P2P): ChainIO.GetBestBlock height has not yet caught up to the miner tip — neutrino is still fetching headers. - Layer 2 (transaction walk): heights matched but IsSynced() never returned true — the chain-sync notification or the address-manager DB write (undo+redo on reorg) did not complete in time.
Add a detailed doc comment to waitForWalletSync explaining the three pipeline stages (header sync, compact-filter/block fetch, transaction walk) and why each stage is relevant, so a future timeout can be diagnosed from the error message alone.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification
Why it was queued
signing or wallet pathsecond-pass: security-sensitive path
AI analysis · Informational 15/100
This commit fixes a flaky automated test helper in the LND codebase. It replaces a goroutine-leaking timer with a proper reusable ticker and increases a test timeout from 30 seconds to 2 minutes so the test is less likely to fail under heavy load. It also improves error messages and adds explanatory comments. There is no user-facing security issue or production bug being fixed.
lntest: pin pre-v30 mempool policy defaults in itest bitcoind
Bitcoind v30 lowered the default minrelaytxfee and incrementalrelayfee from 1000 sat/kvB (1 sat/vB) to 100 sat/kvB. The itest suite was written against the old defaults and the lower values cascade into:
- integer sat/vByte assertions losing precision below 1 sat/vB, and - RBF bump thresholds that alter sweeper/bumpfee replacement timing.
Pin the old defaults in the itest bitcoind backend so the existing tests keep passing without per-test adaptation. Running against the new defaults is still worth doing, but that is a separate exercise that should not be bundled with the v30 version bump.
83/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Mentions testing or verification
Why it was queued
access control
AI analysis · Informational 14/100
This change only adjusts the settings used for internal testing of LND with a Bitcoin Core backend. It pins older Bitcoin Core fee defaults so that existing automated tests continue to produce the same results after Bitcoin Core v30 changed its defaults. It does not change production LND code, user-facing behavior, or network consensus rules, and it does not fix a security vulnerability.
Lower-prioritypeer: register the rbfCloseActor, have RPC route fee bumps to itby Olaoluwa Osuntokun · fa2d0f99 · Apr 23, 2026 · 3 filesMessage 73 · AdequateTriage 0Details
Commit message · Olaoluwa Osuntokun
peer: register the rbfCloseActor, have RPC route fee bumps to it
In this commit, we now register the rbfCloseActor when we create the rbf chan closer state machine. Now the RPC server no longer neesd to traverse a series of maps and pointers (rpcServer -> server -> peer -> activeCloseMap -> rbf chan closer) to trigger a new fee bump.
Instead, it just creates the service key that it knows that the closer can be reached at, and sends a message to it using the returned actorRef/router. We also hide additional details re the various methods in play, as we only care about the type of message we expect to send and receive.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
protofsm: implement the actor.ActorBehavior interface for StateMachine
In this commit, we implement the actor.ActorBehavior interface for StateMachine. This enables the state machine executor to be registered as an actor, and have messages be sent to it via a unique ServiceKey that a concrete instance will set.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
AI review queuedlnwallet/chancloser: create unique ServiceKey for the RBF chan closerby Olaoluwa Osuntokun · 07f54ae1 · Apr 23, 2026 · 1 fileMessage 73 · AdequateInformational 15Details
Commit message · Olaoluwa Osuntokun
lnwallet/chancloser: create unique ServiceKey for the RBF chan closer
This can be used to allow any system to send a message to the RBF chan closer if it knows the proper service key. In the future, we can use this to redo the msgmux.Router in terms of the new actor abstractions.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Why it was queued
signing or wallet pathsecond-pass: security-sensitive path
AI analysis · Informational 15/100
This commit adds a single type alias (a nickname for an existing type) in the Lightning Network Daemon's channel closer code. It does not change any behavior, fix any bug, or alter how messages are processed. It is a small code-cleanup/refactoring step to prepare for future architectural changes.
Lower-prioritypeer: create new rbfCloseActor to decouple RPC RBF close bumpsby Olaoluwa Osuntokun · 2a3ae1ef · Apr 22, 2026 · 2 filesMessage 73 · AdequateTriage 0Details
Commit message · Olaoluwa Osuntokun
peer: create new rbfCloseActor to decouple RPC RBF close bumps
In this commit, we create a new rbfCloseActor wrapper struct. This will wrap the RPC operations to trigger a new RBF close bump within a new actor. In the next commit, we'll now register this actor, and clean up the call graph from the rpc server to this actor.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
docs: add release note for SIMPLE_TAPROOT_FINAL follow-ups
See https://github.com/lightningnetwork/lnd/pull/10763.
85/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Uses a recognizable type or scope✓ Provides an explanatory body✓ Links an issue, advisory, or supporting reference
Why it was queued
documentation-only discount
Lower-priorityserver: do not auto-enable RBF coop close for overlay channelsby George Tsagkarelis · 6d951548 · Apr 20, 2026 · 1 fileMessage 85 · StrongTriage 0Details
Commit message · George Tsagkarelis
server: do not auto-enable RBF coop close for overlay channels
An earlier commit added an auto-enable that forces RbfCoopClose=true whenever either taproot channel flag is set. This breaks taproot-overlay channels, because the RBF coop close state machine in lnwallet/chancloser/rbf_coop_*.go does not integrate the AuxCloser (or any other aux) hook that overlay channels depend on to build aux-aware close transactions. A node that enables --protocol.simple-taproot-overlay-chans ends up with RBF force-on and its overlay channel closes silently fail, leaving the aux closer unable to finalize on-chain.
Narrow the auto-enable so it only fires for TaprootChans (staging / final taproot) and explicitly skips it when TaprootOverlayChans is set. Operators that positively want RBF can still opt in via --protocol.rbf-coop-close; this change only removes the forced path that silently breaks overlay closes.
85/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode
chanacceptor: map SIMPLE_TAPROOT_FINAL in rpc acceptor
The feature-bits-to-lnrpc-enum switch in sendAcceptRequests covered every commitment type the RPC acceptor can be asked about, except the production taproot variant introduced alongside the prod-taproot-chans work. For a channel open using SimpleTaprootChannelsRequiredFinal (with any combination of the scid-alias / zero-conf modifiers), the switch fell through to the default branch, which logs a warning and leaves commitmentType at its zero value -- lnrpc.CommitmentType_UNKNOWN_COMMITMENT_TYPE. External acceptor clients then see UNKNOWN rather than the actual commitment type and either reject or misclassify the channel.
Add the four missing cases so the new commitment type is reported to acceptor clients correctly.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
docs: add new contributors section to contribution guidelines
Advise new contributors to build a PR review track record before submitting code, or to open a detailed issue when they spot a bug.
85/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Uses a recognizable type or scope✓ Provides detailed explanatory context
Why it was queued
documentation-only discount
Lower-prioritydocs: add release note for same-peer onion message cycle dropby Gijs van Dam · 84ec3cb2 · Apr 17, 2026 · 1 fileMessage 93 · StrongTriage 0Details
Commit message · Gijs van Dam
docs: add release note for same-peer onion message cycle drop
Record the onion-message same-peer cycle drop from #10754 under a new Robustness subsection in the 0.21.0 release notes.
93/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Uses a recognizable type or scope✓ Provides detailed explanatory context✓ Links an issue, advisory, or supporting reference
Why it was queued
documentation-only discount
Lower-priorityonionmessage: drop onion messages cycling back to the sending peerby Gijs van Dam · 261babcf · Apr 17, 2026 · 3 filesMessage 73 · AdequateTriage 0Details
Commit message · Gijs van Dam
onionmessage: drop onion messages cycling back to the sending peer
Block forwarding of an onion message when the resolved next hop is the same peer that delivered it. Such a forward would immediately bounce the message back over the very connection it arrived on, which is never useful and can be abused to amplify traffic against a peer.
The check runs after the routing action is resolved, so both direct next-node-ID and SCID-resolved paths are covered. A new `ErrSamePeerCycle` is returned (and logged at warn level) when a cycle is detected.
73/100 · AdequateMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context
Security candidatepeer: gate onion message ingress on having an open channelby Olaoluwa Osuntokun · c0827e8e · Apr 15, 2026 · 9 filesMessage 95 · StrongHigh 76Details
Commit message · Olaoluwa Osuntokun
peer: gate onion message ingress on having an open channel
Onion message forwarding is an unpaid side channel. Without any peer qualification the byte-bucket limiters added in the previous commits are our only defense against a Sybil attacker: an attacker that can cheaply spin up N identities and burn a full per-peer byte budget on each one saturates the global bucket and converts the aggregate cap into a service-denial primitive against legitimate channel peers. This was raised on PR review — the per-peer cap is good, but the global cap on its own is a Sybil multiplier if peer identity is free. The proper fix is to make new identities cost real capital, which is what requiring a funded channel does.
This commit adds a channel-presence gate as the first check in allowOnionMessage, ahead of both the per-peer and the global rate limiters. Messages from peers that do not have at least one fully open channel with us are dropped with a new dropReasonNoChannel sentinel and never allocate any rate limiter state — the gate runs before either limiter is consulted, so no-channel peers cannot burn tokens on any bucket. Pending channels are deliberately excluded from the check: they are represented as nil values in the activeChannels map, are cheap to open and prone to getting stuck, and so do not provide the capital-cost guarantee the Sybil defense depends on. Existing Brontide cleanup paths (StopOnionActorIfExists, OnionPeerLimiter.Forget) already handle teardown on peer disconnect; nothing new is needed there because the gate keeps no-channel peers from ever allocating per-peer state in the first place.
For the hot path we cannot afford to iterate the activeChannels registry on every incoming onion message, so Brontide now carries a numActiveChans atomic.Int32 that shadows the count of non-pending entries in activeChannels. hasActiveChannels is a single atomic Load and is therefore O(1). The counter is maintained in lockstep with activeChannels at every mutation site: loadActiveChannels increments it as it populates the registry during Start(); addActiveChannel uses a new lnutils.SyncMap.Swap method (a thin typed wrapper around sync.Map.Swap) to atomically replace any prior entry so that both brand-new channels and pending-to-active promotions bump the counter by exactly one; WipeChannel and handleRemovePendingChannel both use LoadAndDelete so they can inspect the prior value and only decrement when the removed entry was non-nil. Under race, this keeps the counter and the map consistent even when RPC WipeChannel races with the channelManager goroutine.
The accompanying unit tests cover: the no-channel drop path at the allowOnionMessage level, asserting that neither the global stub counter nor the per-peer limiter's dropped counter move when the gate fires; the subsequent channel-gained path on the same peer, asserting the same message is accepted once hasChannel flips; and a focused Brontide-level test that walks the counter through initial emptiness, a pending-only state (counter must stay at zero), a pending-to-active promotion via direct Store + Add, the pending delete path through handleRemovePendingChannel (must not underflow), and the active delete path through LoadAndDelete + Add(-1) that WipeChannel uses internally. Running with -race confirms the Swap/LoadAndDelete patterns keep the counter and the map in sync under concurrent access.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification
This change closes a denial-of-service weakness in LND's onion-message forwarding. Previously, an attacker could create unlimited free peer identities and burn through the global byte-budget reserved for onion messages, starving real peers. The patch now requires a peer to share at least one funded, fully open Lightning channel before any onion message is accepted, so each attacker identity must lock up real bitcoin. It also adds a fast O(1) atomic counter so this check does not slow down every incoming message.
peer: enforce onion message rate limits at ingress
This commit plumbs the combined IngressLimiter (per-peer + global) through peer.Config and consults it from the readHandler's *lnwire.OnionMessage case. The decision is factored into a small allowOnionMessage helper so that the ingress policy is directly unit-testable without standing up a full Brontide harness. Per-peer is checked first inside the IngressLimiter: if we consulted the global limiter first, a peer whose own bucket was already empty would still get to burn a global token on each attempt, letting a single hostile peer drain the shared budget and starve legitimate peers.
peer.Config carries a single OnionLimiter field of IngressLimiter type; the brontide readHandler calls a single AllowN per incoming onion message and dispatches on sentinel errors via errors.Is for the first-drop log path. Nil limiter values are treated as "disabled" throughout, which both preserves the pre-change behavior when onion messaging is entirely turned off and keeps the brontide test harness from needing to construct real limiters. Per-peer bucket state is retained across disconnect at the IngressLimiter layer so a peer cannot cycle the connection to reset its per-peer allowance.
OnionMessage also gains a WireSize method that computes the on-the-wire size directly from the in-memory fields (no round-trip through Encode) so the hot ingress path can charge the right number of byte tokens without paying for a full serialization.
The accompanying unit tests cover the nil/disabled path, the per-peer-rejects-first ordering invariant (asserting the global limiter is not consulted when the per-peer bucket is empty), the global rejection path, per-peer isolation across distinct pubkeys, and a small concurrent stress test that asserts every attempt is accounted for as either accepted or dropped and that the total accepted count equals the configured burst under -race. A property-based rapid test on WireSize guards against silent divergence from WriteMessage if the OnionMessage wire format ever gains a TLV extension.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification
The existing per-peer actor mailbox (capacity 50, RED from depth 40) only bounds in-flight queue depth. It does not cap throughput: a peer that drains its actor quickly can saturate our Sphinx unwrap CPU, replay-DB writes, and outbound forwarding bandwidth without ever tripping RED. At spec-max onion message sizes (~32 KiB per sphinx packet) a single well-behaved-draining peer is enough to push multiple Mbps of unpaid forwarded traffic through us, and aggregate fan-in from many peers multiplies that into tens of Mbps — an amount of bandwidth that is very much out of proportion for a side channel on a payment routing node.
This commit adds the building blocks for two token-bucket limiters that will be wired into the onion message ingress path in a follow-up commit: a process-wide global limiter and a per-peer registry. Both drop (rather than wait) on over-limit so that a hostile peer cannot grow our goroutine or memory footprint simply by sustaining above-threshold traffic. The per-peer registry keys buckets on the peer's compressed pubkey, creates them lazily, and retains them for the lifetime of the process so a peer cannot reset its burst by cycling the connection; cardinality is bounded by the live channel-peer count (the ingress call site gates on having a channel before allocating per-peer state), so no time-based GC is needed.
A minimal RateLimiter interface is introduced so that callers and tests can substitute noop or alternate implementations without reaching into x/time/rate directly, and a small countingLimiter wrapper keeps an atomic drop counter plus a one-shot first-drop flag for observability. A rate of zero (or a non-positive burst) yields a noop limiter, providing a clean "disabled" mode without branching at the call site.
On top of those, a single IngressLimiter interface composes the per-peer and global buckets behind one surface so that callers — notably the peer readHandler — only thread one object through Config and call one method per incoming onion message. Drop reasons are surfaced as sentinel errors (ErrPeerRateLimit, ErrGlobalRateLimit) wrapped in fn.Result[fn.Unit] so callers match on them with errors.Is rather than comparing free-form strings. The stock implementation encodes the load-bearing ordering — per-peer first, then global — inside AllowN so that a hostile peer whose own bucket is already empty cannot burn global tokens on every rejected attempt and starve legitimate peers.
Default constants targeting roughly ~5 Mbps worst-case ingress at spec-max message sizes are added alongside the existing mailbox defaults.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification
Why it was queued
secure hardware boundary
AI analysis · Moderate 61/100
This commit adds new rate-limiting building blocks for LND's onion message handling. It does not yet wire them into live message processing, so by itself it cannot stop an attack. The code is clearly preparing to fix a denial-of-service risk: a single peer (or many peers together) could currently flood a routing node with large, unpaid onion messages and consume CPU, database writes, and outbound bandwidth. The new primitives cap per-peer and total incoming onion-message bytes using token buckets, and they drop excess traffic immediately rather than queuing it.
In this commit we surface the onion message rate limiter thresholds as ProtocolOptions so that operators can tune them from lnd.conf or the command line. Four options are added — onion-msg-peer-rate, onion-msg-peer-burst, onion-msg-global-rate, and onion-msg-global-burst — and are documented such that a rate of zero disables the corresponding limiter entirely. The default values are seeded from the constants added in the previous commit via DefaultConfig, following the same pattern that the Gossip sub-config already uses for its own rate limiter knobs.
The fields are duplicated into protocol_integration.go so that the integration build tag sees the same surface; this mirrors how the existing NoOnionMessagesOption and related fields are declared.
85/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode
Why it was queued
parser or protocol pathsecond-pass: security-sensitive path
AI analysis · Informational 12/100
This commit adds user-facing configuration options for rate-limiting onion messages in LND. It does not change any runtime behavior by itself; it only exposes knobs that operators can tune and adds startup validation to catch configuration mistakes. There is no vulnerability introduced or fixed in this diff.
lncfg+peer+server: add protocol.onion-msg-relay-all to bypass channel gate
Add a new protocol option, protocol.onion-msg-relay-all, that controls whether incoming onion messages are required to come from peers with a fully open channel. The default is false, which preserves the existing behavior: the channel-presence gate drops messages from peers with no channel before the rate limiters are consulted, so a new no-cost identity cannot burn any per-peer byte budget and saturate the global bucket. Setting the flag to true skips the gate so that onion messages from any peer are admitted into the per-peer + global IngressLimiter pipeline.
The flag is plumbed through ProtocolOptions in both the default and integration build variants of lncfg/protocol*.go, threaded into the peer subsystem as peer.Config.OnionRelayAll, and wired by the server from s.cfg.ProtocolOptions.OnionMsgRelayAll alongside the existing OnionLimiter field. allowOnionMessage gains a relayAll bool parameter; the gate check becomes "if \!relayAll && \!hasChannel { drop }" so the semantics of hasChannel stay pure — it still means "this peer has a channel" — and the policy toggle lives entirely in the caller's configuration rather than being spread across gate-state and flag state.
sample-lnd.conf gains a commented-out entry for the new option with the default value and an operator-facing note that enabling it trades the Sybil-resistance property of the gate for reachability to peers with whom we have no channel.
A new TestAllowOnionMessageRelayAll unit test exercises the four (hasChannel, relayAll) combinations at the helper level, including the key new behavior — a peer with hasChannel=false being rejected under relayAll=false and admitted into the limiter under relayAll=true — and the nil-limiter path under relayAll=true, which must still accept. The existing allowOnionMessage tests were extended with the new parameter set to false so they continue to assert the gate semantics unchanged.
95/100 · StrongMessage clarity
✓ Specific, descriptive subject✓ Names a concrete action or component✓ Provides detailed explanatory context✓ Explains rationale or failure mode✓ Mentions testing or verification
Why it was queued
parser or protocol pathsecond-pass: security-sensitive path
AI analysis · Informational 15/100
This commit adds a new optional configuration flag, protocol.onion-msg-relay-all, that lets a node operator choose whether to accept onion messages only from peers that already have a payment channel (the default, safer behavior) or from any peer. It does not change the default behavior and does not fix a vulnerability; it is a feature addition that preserves the existing security gate unless explicitly disabled.
Introduce docs/onion_message_rate_limiting.md, a prose explainer for operators and contributors that covers the two-layer defense on the onion message ingress path: the channel-presence gate that turns peer identity into a capital cost, and the byte-denominated per-peer and global token-bucket rate limiters that run behind it. The doc walks through the adversary first so that each layer has a concrete thing to defend against, then covers the knobs, the startup-time validation rules, the default sizing, and the protocol.onion-msg-relay-all escape hatch with its explicit tradeoff against Sybil resistance. A short operator recipes section collects the common "I want to ..." configurations so readers do not have to reconstruct them from the principles.
No code change; documentation only.
92/100 · StrongMessage clarity
✓ Descriptive subject✓ Names a concrete action or component✓ Uses a recognizable type or scope✓ Provides detailed explanatory context✓ Explains rationale or failure mode