Paying lightning invoices through Nostr instead of http. 🀯 Good bye DNS.

https://vimeo.com/950881613

Reply to this note

Please Login to reply.

Discussion

Nostr uses dns

For now... It doesn't need to.

Its going to be pretty gay trying to identify instances by numbers....even ipv6 numbers lol

Wont ssl not work if we use numbers ?

You can do SSL with IP only just fine. There is no need for domain names.

Also, you will identify relay instances and blossom servers by pubkey, not by IP. Relays and servers will be on a marketplace of their own. You just need to search, select one you like and add the pubkey to your relay list. Clients will figure out which IP the pubkey is currently using to host the relay.

Super easy.

We bitch about ipv6 raw addresses while we go around cutting, pasting and trusting npubs?

I know, right? npubs are easy. nprofiles are massive.

Honestly I love how it scales wonderfully, but the chances of IPv6 not ending up geo'ed is less than a Planck length. And it would all be public, 100%. No thanks.

Exactly πŸ‘

Ah. Cool. Every time I try to use Ip with ssl it fails to get validation and errors are given. Many libs dont like self-signed either.

I cant wait for blossom to develop more.mayvr nextcloud replacement one day? Hehe

Yeah, the server needs to offer IP-based SSL. They usually don't though :(

I smell a new npub-based ssl certificate replacement for relays coming

The idea here is to identify the relay with a uri something like thus:

wsn://npub1234....7890@[1001:1003:1003:1004::abcd]:8443

wsn://npub1234....7890@22.33.44.55:8443

wsn://npub1234....7890@centralizeddomainname.com:8443

wsn://npub1234...7890@torelayabcd...efg.onion:8443

Where the format is

wsn://@:

wsn: Web Sockets Nostr

The @ symbol could be changed to something that doesen't have an alternative meaning. Maybe #, %, &, ~ or whatnot

The only downside is it wouldn't play nice with reverse websocket proxies

This is how nostr:npub1exv22uulqnmlluszc4yk92jhs2e5ajcs6mu3t00a6avzjcalj9csm7d828 works basically.

Chatgpt:

Using Elliptic Curve Cryptography (ECC) is a great way to align with NOSTR's encryption and signing schemes. Here’s how you can create a secure WebSocket communication using ECC for encryption and signing, similar to how NOSTR handles notes.

### Step-by-Step Guide

#### 1. Key Generation

Generate ECC key pairs for the server and client using `cryptography` library in Python.

#### 2. Implement the Relay Server

The relay server will use its private key to decrypt the initial connection request and then use symmetric encryption (AES) for the WebSocket communication.

#### 3. Implement the Client

The client will use the server's public key to encrypt the initial connection request.

### Example Code

#### 1. Key Generation

Use the `cryptography` library to generate ECC key pairs.

```python

from cryptography.hazmat.primitives.asymmetric import ec

from cryptography.hazmat.primitives import serialization

# Generate server private key

server_private_key = ec.generate_private_key(ec.SECP256R1())

server_public_key = server_private_key.public_key()

# Save server private key

with open("server_private_key.pem", "wb") as f:

f.write(server_private_key.private_bytes(

encoding=serialization.Encoding.PEM,

format=serialization.PrivateFormat.PKCS8,

encryption_algorithm=serialization.NoEncryption()

))

# Save server public key

with open("server_public_key.pem", "wb") as f:

f.write(server_public_key.public_bytes(

encoding=serialization.Encoding.PEM,

format=serialization.PublicFormat.SubjectPublicKeyInfo

))

```

#### 2. Relay Server Implementation

Using the `websockets` library and `cryptography` for the relay server.

```python

import asyncio

import websockets

from cryptography.hazmat.primitives.asymmetric import ec

from cryptography.hazmat.primitives import hashes

from cryptography.hazmat.primitives.kdf.hkdf import HKDF

from cryptography.hazmat.primitives import serialization

from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash

from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

from cryptography.hazmat.primitives.ciphers.aead import AESGCM

from base64 import b64encode, b64decode

import os

# Load server private key

with open("server_private_key.pem", "rb") as f:

server_private_key = serialization.load_pem_private_key(

f.read(),

password=None

)

# Function to handle WebSocket connections

async def echo(websocket, path):

try:

# Receive the client's public key

client_public_key_pem = await websocket.recv()

client_public_key = serialization.load_pem_public_key(client_public_key_pem.encode('utf-8'))

# Perform ECDH key exchange

shared_key = server_private_key.exchange(ec.ECDH(), client_public_key)

# Derive AES key from the shared key

aes_key = HKDF(

algorithm=hashes.SHA256(),

length=32,

salt=None,

info=b'handshake data'

).derive(shared_key)

while True:

encrypted_message = await websocket.recv()

nonce, ciphertext, tag = [b64decode(x) for x in encrypted_message.split(":")]

# Decrypt the message

aesgcm = AESGCM(aes_key)

message = aesgcm.decrypt(nonce, ciphertext + tag, None).decode('utf-8')

print(f"Received: {message}")

response = f"Echo: {message}"

# Encrypt response

nonce = os.urandom(12)

aesgcm = AESGCM(aes_key)

ciphertext = aesgcm.encrypt(nonce, response.encode('utf-8'), None)

encrypted_response = b64encode(nonce).decode('utf-8') + ":" + b64encode(ciphertext[:-16]).decode('utf-8') + ":" + b64encode(ciphertext[-16:]).decode('utf-8')

await websocket.send(encrypted_response)

except websockets.exceptions.ConnectionClosed as e:

print(f"Connection closed: {e}")

# Start the WebSocket server

start_server = websockets.serve(echo, "localhost", 8765)

asyncio.get_event_loop().run_until_complete(start_server)

asyncio.get_event_loop().run_forever()

```

#### 3. Client Implementation

The client will use the server's public key to perform ECDH key exchange and then use the derived AES key for encryption.

```python

import asyncio

import websockets

from cryptography.hazmat.primitives.asymmetric import ec

from cryptography.hazmat.primitives import hashes

from cryptography.hazmat.primitives.kdf.hkdf import HKDF

from cryptography.hazmat.primitives import serialization

from cryptography.hazmat.primitives.kdf.concatkdf import ConcatKDFHash

from cryptography.hazmat.primitives.ciphers.aead import AESGCM

from base64 import b64encode, b64decode

import os

# Load server public key

with open("server_public_key.pem", "rb") as f:

server_public_key = serialization.load_pem_public_key(f.read())

async def hello():

uri = "ws://localhost:8765"

# Generate client private key

client_private_key = ec.generate_private_key(ec.SECP256R1())

client_public_key = client_private_key.public_key()

# Perform ECDH key exchange

shared_key = client_private_key.exchange(ec.ECDH(), server_public_key)

# Derive AES key from the shared key

aes_key = HKDF(

algorithm=hashes.SHA256(),

length=32,

salt=None,

info=b'handshake data'

).derive(shared_key)

async with websockets.connect(uri) as websocket:

# Send client's public key to the server

client_public_key_pem = client_public_key.public_bytes(

encoding=serialization.Encoding.PEM,

format=serialization.PublicFormat.SubjectPublicKeyInfo

).decode('utf-8')

await websocket.send(client_public_key_pem)

message = "Hello, Server!"

print(f"Sending: {message}")

# Encrypt the message

nonce = os.urandom(12)

aesgcm = AESGCM(aes_key)

ciphertext = aesgcm.encrypt(nonce, message.encode('utf-8'), None)

encrypted_message = b64encode(nonce).decode('utf-8') + ":" + b64encode(ciphertext[:-16]).decode('utf-8') + ":" + b64encode(ciphertext[-16:]).decode('utf-8')

await websocket.send(encrypted_message)

encrypted_response = await websocket.recv()

nonce, ciphertext, tag = [b64decode(x) for x in encrypted_response.split(":")]

# Decrypt the response

aesgcm = AESGCM(aes_key)

response = aesgcm.decrypt(nonce, ciphertext + tag, None).decode('utf-8')

print(f"Received: {response}")

asyncio.get_event_loop().run_until_complete(hello())

```

### Summary

1. **Key Generation**: Generate ECC key pairs for the server and client.

2. **Relay Server**: Implement the server to handle WebSocket connections, perform ECDH key exchange, and use AES-GCM for symmetric encryption.

3. **Client**: Implement the client to perform ECDH key exchange and use AES-GCM for encryption.

This setup ensures that WebSocket communication is encrypted using keys derived from the public-private key pairs, aligning with NOSTR's encryption scheme and avoiding centralized CAs.

Love it!

Could argue it also uses http (sort of) most/all of the components need to be there for proper websocket connections.

Is nomen still active?

I thought the NIP47 nwc spec was expanded to allow receiving (i.e. being requested an invoice, generating one and sending it) over nostr.

Sad that no wallets nor clients picked that up...

Nwc is just a service that uses lnurl behind the scenes. This one inverts everything and brings the Lnurl exchange to Nostr.

I'm talking about this: https://github.com/nostr-protocol/nips/pull/685

These NWC extension has a "make_invoice" request that can entirely replace lnurl. You could just use this to "pay-to-npub" directly if the npub is running a NWC wallet with this capability

I think that is to make an invoice in your own wallet for others to pay. Currently, there is no way for my pubkey to access your NWC server to make an invoice for me to pay. That part still happens via http

Uff you're right, that was a lost opportunity then... I can't watch the vid right now, but why using nostr DMs instead of using NWC-like events?

I also wonder about what are the spam defences of this. On a webserver you control, you can add ratelimits, etc. What's stopping someone from hitting 300 different relays asking for 300 different invoices from your LN node?

Yeah the use of the DM is unnecessary. They can just use any other event kind.

Ok the spam side, you can apply the same DoS protections on the relay you are going to use to communicate OR on the service that is replying to those messages. DVMs already have some of those defenses up.

So you rely on your chosen relays to not relay spam to your LN node? Seems reasonable

You can give someone a NWC connection with only make_invoice permissions. LNURL is not needed

We need to figure out a way to make that foolproof so that no dev can make the mistake of sharing the same link that has permission to spend.

Kinda like sharing an npub and not an nsec.

Yeah, exactly!

LNURL is completely separate from NWC. NWC does not know about or use LNURL

Interesting for sure without pinning to domain names

How exactly does DNS become un-used? I have a hard time believing that there’s not one dependency that requires a domain lookup call in some form or fashion. Thanks in advance, really interesting.

dns is still used for connecting to the nostr relay. but at least you dont need your own domain to host your lnurl any more

You can connect to relays using IP directly. And the IP can be taken from the relay's pubkey metadata. It's super easy.

Ok, I see. Thanks for the explanation. It’s an incremental improvement.

πŸ‘€

Who is going to telll him πŸ˜…

stop stealing my women you fucking cucklefuck

🀣

I have a no-coiner friends group that won't shut up about the current thing. Trump vs Biden (orange man bad). I just sent them this with the line:

"respectfully, gentlemen, the revolution is not being televised"

This is fucking crazy.

I’m open for questions and zaps.

Here is the NIP:

https://github.com/nostr-protocol/nips/pull/1276

It's very cool

Is this the start oft the

"Good bye DNS - day" story. ?

This doesn't fix the fact legacy lnurl relies on SSL and isn't meant for bi-directional

Lightning.Pub is the nostr successor to LNURL

i need alby code pls

nostr:npub14wxtsrj7g2jugh70pfkzjln43vgn4p7655pgky9j9w9d75u465pqvagcye do you have any? #getalby

LFG!