Skip to content

Make sure we are properly validating Lightning Address according to LUD-16#134

Merged
hsjoberg merged 2 commits intomasterfrom
lnaddressing-spec-incompat
Feb 9, 2026
Merged

Make sure we are properly validating Lightning Address according to LUD-16#134
hsjoberg merged 2 commits intomasterfrom
lnaddressing-spec-incompat

Conversation

@hsjoberg
Copy link
Member

@hsjoberg hsjoberg commented Feb 9, 2026

This PR does two things:

  • Relax the isUsername() check for Lightning Address send used for SendScreen.tsx; it did not allow -, which is allowed in the spec
  • Make the ln_address in the server use custom LNURL LUD-16 validator rather than checking for an email. Email addresses are crazy and allow a lot of insane things. We would otherwise allow spec-incompliant Lightning Addresses.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @hsjoberg, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the validation of Lightning Addresses across both the client and server. The primary goal is to ensure full compliance with the LUD-16 specification, allowing for a broader range of valid usernames (specifically, including hyphens) while simultaneously tightening the overall validation logic on the server to prevent non-compliant addresses from being accepted. This change improves the reliability and correctness of Lightning Address handling within the application.

Highlights

  • Client-side Validation Update: The isUsername() regex on the client-side has been relaxed to permit hyphens (-) in Lightning Address usernames, aligning with the LUD-16 specification.
  • Server-side Validation Overhaul: The server-side validation for Lightning Addresses has been migrated from a generic email validator to a custom, LUD-16 compliant validation logic. This ensures stricter adherence to the Lightning Address specification.
  • New Validation Utilities: New Rust functions (is_valid_lightning_address, is_valid_ln_username, ln_username_regex, validate_lightning_address) have been introduced to encapsulate the LUD-16 validation rules, making the server-side checks robust and spec-compliant.
Changelog
  • client/src/constants.ts
    • Updated the isUsername regular expression to include hyphens (-) as allowed characters for usernames, matching LUD-16.
  • client/src/lib/sendUtils.ts
    • Updated a comment to explicitly refer to 'LNURL LUD-16' when checking Lightning Addresses, improving clarity.
  • server/src/routes/public_api_v0.rs
    • Replaced the validate_email() call for ln_address with a new custom validation function, crate::types::is_valid_lightning_address.
  • server/src/types.rs
    • Added new functions (ln_username_regex, is_valid_ln_username, is_valid_lightning_address, validate_lightning_address) to implement the LUD-16 specification for Lightning Address validation.
    • Modified RegisterPayload and UpdateLnAddressPayload structs to use the new custom validate_lightning_address function instead of the generic email validator for the ln_address field.
Activity
  • No specific activity (comments, reviews, etc.) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly moves towards proper LUD-16 validation for Lightning Addresses by allowing hyphens in usernames and replacing the email validation logic on the server. My review includes a few suggestions to further improve the validation logic on both the client and server. Specifically, I've pointed out issues with case-sensitivity in username validation, handling of empty usernames, and the need for more robust domain validation to fully align with the LUD-16 specification.


fn ln_username_regex() -> &'static Regex {
static RE: OnceLock<Regex> = OnceLock::new();
RE.get_or_init(|| Regex::new(r"^[a-z0-9_.-]+$").expect("valid ln username regex"))
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The LUD-16 spec states that the username part of a Lightning Address is case-insensitive. This regex only matches lowercase letters, which would cause valid addresses with uppercase letters (e.g., Satoshi@domain.com) to be incorrectly rejected. Please update the regex to be case-insensitive by including A-Z.

Suggested change
RE.get_or_init(|| Regex::new(r"^[a-z0-9_.-]+$").expect("valid ln username regex"))
RE.get_or_init(|| Regex::new(r"^[a-zA-Z0-9_.-]+$").expect("valid ln username regex"))
References
  1. The suggested regular expression correctly anchors the pattern to the end of the line with $ to prevent partial matches, aligning with the rule that unanchored regexes can match patterns even with trailing characters.

Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The regex for isUsername has two issues:

  1. It uses * which allows an empty string for a username. An empty username is not valid for a Lightning Address. The server-side validation correctly checks for this, and the client should be consistent.
  2. It only allows lowercase letters, but the LUD-16 spec states that usernames are case-insensitive.

To address these, the regex should enforce at least one character and allow both uppercase and lowercase letters.

Suggested change
const isUsername = (n: string): boolean => /^[a-z0-9_.-]*$/.test(n);
const isUsername = (n: string): boolean => /^[a-zA-Z0-9_.-]+$/.test(n);
References
  1. The suggested regular expression correctly anchors the pattern to the end of the line with $ to prevent partial matches, aligning with the rule that unanchored regexes can match patterns even with trailing characters.

Copy link
Member Author

Choose a reason for hiding this comment

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

It only allows lowercase letters, but the LUD-16 spec states that usernames are case-insensitive.

This is not true actually. Usernames are case sensitive -- they have to be lowercase.
See here:
https://github.com/lnurl/luds/blob/luds/16.md

A user can then type these on a WALLET. The <username> is limited to a-z0-9-_. (and + if the SERVICE supports tags).

(n.b: we are skipping SERVICE support right now. It's an obscure feature.)

Comment on lines +16 to +31
pub(crate) fn is_valid_lightning_address(value: &str) -> bool {
let (username, domain) = match value.split_once('@') {
Some(parts) => parts,
None => return false,
};

if username.is_empty() || domain.is_empty() {
return false;
}

if domain.contains('@') {
return false;
}

is_valid_ln_username(username)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The validation for the domain part of the Lightning Address is very minimal. It only checks that the domain is not empty and does not contain an @ symbol. According to LUD-16, the domain should be a fully-qualified domain name (FQDN) or an IP address. The current implementation would allow invalid domains such as test@- or test@domain-.com. This is also a regression from the previous email validation, which at least ensured the domain contained a dot. Consider adding more robust validation for the domain to better align with the specification and prevent invalid addresses from being accepted.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think handling of the domain part can be covered in another PR. The client shouldn't really send this at all as it's the server that has the domain anyway.

@hsjoberg hsjoberg force-pushed the lnaddressing-spec-incompat branch from 88a560e to 2d83ea2 Compare February 9, 2026 15:02
@hsjoberg hsjoberg merged commit 51208c1 into master Feb 9, 2026
8 checks passed
Sign up for free to 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.

1 participant

Comments