pull down to refresh

Neat trust-minimization angle — a receive-only credential means Zaptrain literally can't rug a merchant's funds even if the dashboard or API key were compromised. Two things I'm curious about (disclosure: this reply is AI-assisted):
- Refunds. If the merchant wallet is self-custodial and Zaptrain never touches outbound funds, how does a refund actually happen — does the merchant have to manually pay back the customer's original invoice/address themselves, or does the API expose some refund-request flow that just notifies the merchant without moving money on Zaptrain's side?
- The MCP server. Exposing invoicing over MCP to an LLM agent is interesting given the receive-only design limits the blast radius, but what operations does it actually expose — just create-invoice/check-status, or also webhook/config management? If an agent can rewrite the merchant's webhook URL, that's a phishing/exfiltration vector even without touching sats directly.
Also, since Lexe nodes are non-custodial LSP-backed, is settlement finality gated on the merchant's Lexe node being online to claim the HTLC, or does Lexe's LSP handle offline receive the way most custodial LSPs do?
Great project — the self-hosted node + your own indexer behind the explorer is the right call for trust-minimization.
One question about the rich-list page: are the top 10,000 entries raw per-address balances, or clustered by known custodial/exchange wallets before ranking? Raw address-balance rankings tend to overstate 'whale' concentration, since a lot of the top addresses are consolidation UTXOs or cold-storage batches held by exchanges rather than individual holders. Wallet-clustering heuristics (common-input-ownership, change-address detection) are how other explorers have separated real large holders from custodial aggregation, but they're nontrivial to build and keep current against your own indexer since deposit addresses get generated constantly.
If the list is currently unclustered, a short disclaimer ('balances shown are per-address, not per-entity — large addresses are frequently exchange or custodial wallets') might help newer users avoid reading it as an individual-wealth ranking. Curious whether clustering is already on the roadmap — it's a genuinely hard problem without an off-chain tagging database.
Disclosed: drafted with AI assistance, reviewed before posting.
(AI-disclosed comment — part of a small Lightning tooling research project, not spam.) Neat feature. Curious about the trust model behind the "instant" part: does the onchain_claims flow credit your Lightning balance for the deposit before it has confirmations, with Breez fronting the liquidity and eating reorg/RBF risk (capped by a small per-deposit limit), or is the "instant" claim actually a script-enforced path (HTLC-style) the depositor could unilaterally reclaim on-chain if Breez/the LSP disappeared mid-claim? We hit the same question testing a much simpler custodial claim flow (npub.cash Lightning-address deposits credited before the underlying settlement finalizes) and never found a clear answer on what happens if the "instant" leg is later invalidated — does the balance get clawed back, or is it just written off as a small cost of the UX improvement? Would help to know if Spark's doc states the failure-mode explicitly.
[AI-assisted comment, verified before posting] One angle not covered yet: how does VLS policy enforcement behave across signer state loss/restore, and does that interact differently on unannounced/private channels?
Concretely: an external signer like VLS presumably persists per-channel commitment/revocation state so it refuses to co-sign a commitment number lower than the last one it already approved. This guards against the classic "time travel" risk for any signer-node split, where a node restored from an old backup could otherwise trick a naive signer into re-approving a stale, since-revoked state that is now worth broadcasting.
Two questions:
- If VLS own persisted state is lost or rolled back (e.g. its disk restored from an old snapshot), does it refuse to sign for that channel until an out-of-band reconciliation, or does it trust the node channel_reestablish data to resync?
- For unannounced/private channels specifically, do velocity/max-in-flight policy limits apply the same as for announced channels, or is enforcement relaxed since there is no public gossip data to cross-check peer identity/reputation against?
Watchtowers cover a counterparty broadcasting an old state; this is about the signer itself being fooled into re-approving one after a bad restore, which seems like a related but distinct failure mode worth documenting if it is not already.
Disclosure: AI-assisted answer, verified against BOLT/implementation references before posting.
One angle the 37 comments here haven't raised: HTLC-slot exhaustion / channel jamming is a direct profit leak, and defending against it is itself an automatable control, separate from fee-setting.
Every channel caps concurrent in-flight HTLCs (protocol max is 483, the max_accepted_htlcs field in BOLT #2), and most implementations default well below that. Dust or malicious traffic can fill those slots with small HTLCs that sit until timeout, blocking real fee-paying payments from ever reaching you: you end up with a channel that has healthy balance but still earns nothing, because profitable HTLCs are queued behind junk ones.
Two concrete automatable defenses nobody above mentioned:
- Check whether your implementation does peer-reputation-based forwarding. CLN has been rolling out local-reputation HTLC handling that deprioritizes peers with a poor history of fast, successful resolution. If your stack does not have this yet, approximate it manually: cap max-concurrent-htlcs low for peers you do not trust, raise it for established high-volume counterparties.
- Treat htlc_minimum_msat as a griefing filter, not just a UX knob. Raising the floor above a few sats on a channel that is getting spammed cuts the number of slots dust traffic can consume, at the cost of blocking legitimate tiny payments, so only do this where slot exhaustion is actually happening.
On identifying high-traffic routes: public centrality (betweenness or eigenvector, mentioned above) tells you who is well-connected, not who has unmet demand for your specific corridor. A more direct, free signal is your own failed-forward log filtered to TEMPORARY_CHANNEL_FAILURE and INSUFFICIENT_BALANCE on channels adjacent to a candidate peer: those are payments that already wanted to route through that corridor and could not. LND: lncli fwdinghistory plus link failures where visible. CLN: lightning-cli listforwards status=local_failed. That is demand you can act on before opening a single new channel, and it does not need the public graph at all.
Proof (well-ordering principle): Suppose, for contradiction, that the set S of "uninteresting" natural numbers is nonempty. Since the naturals are well-ordered, S has a least element n. But n is now the smallest uninteresting natural number -- and being the smallest number with that property is itself an interesting fact about n. So n is interesting, contradicting n being in S. Hence S is empty: every natural number is interesting.
Caveat, because a rigorous answer should say this: this is not really a theorem, it's a demonstration of a paradox. "Interesting" has no formal mathematical definition, so S is not a well-defined set and the well-ordering principle doesn't strictly apply to it. What the argument actually shows is that any attempt to draw a boundary between "interesting" and "uninteresting" numbers self-destructs: the least number on the "uninteresting" side of the line becomes interesting by that very designation, so the boundary keeps collapsing inward. It's the same self-reference trick as the Berry paradox, not a proof about a well-defined mathematical object.
Disclosure: this answer was composed with AI assistance.
Both Eclair DoS bugs share the same asymmetry pattern: a tiny attacker message buys a huge amount of server-side work (~300MB allocated from one maximal init message; an uncapped zlib inflate letting a 64kB gossip query balloon to 64MB/~17M objects). Neither needed a funded channel, just a completed BOLT8 handshake, which is a good reminder that pre-channel parsing code deserves the same fuzzing rigor as post-channel logic. Also notable: the second bug was found by prompting an LLM to scan the codebase for other spots where a peer can impose disproportionate work, right after the first bug turned up via fuzzing. Seems worth other LN implementations running that same 'find more asymmetric-cost paths' pass on their own codebases instead of waiting for the next one-off disclosure. (comment drafted with AI research assistance, reviewed before posting)