Compare commits

..

8 Commits

Author SHA1 Message Date
fiatjaf
2fac7a23b0 add nip34-related follow lists. 2025-11-20 07:42:05 -03:00
hodlbod
c45f504537 Add self to NIP 11 (#1764)
Co-authored-by: Jon Staab <shtaab@gmail.com>
2025-11-17 10:50:04 -08:00
AsaiToshiya
d8e57865d7 add NIP-BE to README. 2025-11-15 05:27:17 +09:00
Francisco Calderón
f63c00213f Add order expiration support to NIP-69 (#2118) 2025-11-13 13:41:04 -05:00
KoalaSat
a47c460415 NIP-BE: Add BLE messaging and device synchronization (#1979) 2025-11-11 11:05:09 -03:00
AsaiToshiya
45668383e3 add NIP-43 and its kinds to README. (#2110) 2025-11-04 05:46:47 -08:00
Rob Woodgate
62f0b14ae8 Added base "unit" tag to NutZap kind:9321 event (#1915) 2025-10-31 15:59:35 +02:00
hodlbod
3ec830cd23 refine wording of nip 17, include kind 7 reactions (#2098)
Co-authored-by: Jon Staab <shtaab@gmail.com>
2025-10-30 15:53:24 -04:00
8 changed files with 182 additions and 24 deletions

5
11.md
View File

@@ -17,6 +17,7 @@ When a relay receives an HTTP(s) request with an `Accept` header of `application
"banner": <a link to an image (e.g. in .jpg, or .png format)>,
"icon": <a link to an icon (e.g. in .jpg, or .png format>,
"pubkey": <administrative contact pubkey>,
"self": <relay's own pubkey>,
"contact": <administrative alternate contact>,
"supported_nips": <a list of NIP numbers supported by the relay>,
"software": <string identifying relay software URL>,
@@ -60,6 +61,10 @@ An administrative contact may be listed with a `pubkey`, in the same format as N
Relay operators have no obligation to respond to direct messages.
### Self
A relay MAY maintain an identity independent from its administrator using the `self` field, which MUST be a 32-byte hex public key. This allows relays to respond to requests with events published either in advance or on demand by their own key.
### Contact
An alternative contact may be listed under the `contact` field as well, with the same purpose as `pubkey`. Use of a Nostr public key and direct message SHOULD be preferred over this. Contents of this field SHOULD be a URI, using schemes such as `mailto` or `https` to provide users with a means of contact.

36
17.md
View File

@@ -6,9 +6,15 @@ Private Direct Messages
`draft` `optional`
This NIP defines an encrypted direct messaging scheme using [NIP-44](44.md) encryption and [NIP-59](59.md) seals and gift wraps.
This NIP defines an encrypted chat scheme which uses [NIP-44](44.md) encryption and [NIP-59](59.md) seals and gift wraps.
## Direct Message Kind
Any event sent to an encrypted chat MUST NOT be signed, and MUST be encrypted as described in [NIP-59](./59.md) and illustrated below. Omitting signatures makes messages deniable in case they are accidentally or maliciously leaked, while still allowing the recipient to authenticate them.
By convention, `kind 14` direct messages, `kind 15` file messages, and [`kind 7` reactions](./25.md) may be sent to an encrypted chat.
## Kind Definitions
### Chat Message
Kind `14` is a chat message. `p` tags identify one or more receivers of the message.
@@ -31,7 +37,7 @@ Kind `14` is a chat message. `p` tags identify one or more receivers of the mess
`.content` MUST be plain text. Fields `id` and `created_at` are required.
An `e` tag denotes the direct parent message this post is replying to.
An `e` tag denotes the direct parent message this post is replying to.
`q` tags MAY be used when citing events in the `.content` with [NIP-21](21.md).
@@ -39,9 +45,7 @@ An `e` tag denotes the direct parent message this post is replying to.
["q", "<event-id> or <event-address>", "<relay-url>", "<pubkey-if-a-regular-event>"]
```
Kind `14`s MUST never be signed. If it is signed, the message might leak to relays and become **fully public**.
## File Message Kind
## File Message
```jsonc
{
@@ -80,8 +84,6 @@ Kind `15` is used for sending encrypted file event messages:
- `thumb` (optional) URL of thumbnail with same aspect ratio (encrypted with the same key, nonce)
- `fallback` (optional) zero or more fallback file sources in case `url` fails (encrypted with the same key, nonce)
Just like kind `14`, kind `15`s MUST never be signed.
## Chat Rooms
The set of `pubkey` + `p` tags defines a chat room. If a new `p` tag is added or a current one is removed, a new room is created with a clean message history.
@@ -92,7 +94,7 @@ An optional `subject` tag defines the current name/topic of the conversation. An
## Encrypting
Following [NIP-59](59.md), the **unsigned** `kind:14` & `kind:15` chat messages must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.
Following [NIP-59](59.md), the **unsigned** chat messages must be sealed (`kind:13`) and then gift-wrapped (`kind:1059`) to each receiver and the sender individually.
```js
{
@@ -127,7 +129,7 @@ Clients SHOULD randomize `created_at` in up to two days in the past in both the
The gift wrap's `p` tag can be the receiver's main pubkey or an alias key created to receive DMs without exposing the receiver's identity.
Clients CAN offer disappearing messages by setting an `expiration` tag in the gift wrap of each receiver or by not generating a gift wrap to the sender's public key
Clients MAY offer disappearing messages by setting an `expiration` tag in the gift wrap of each receiver or by not generating a gift wrap to the sender's public key. This tag SHOULD be included on the `kind 13` seal as well, in case it leaks.
## Publishing
@@ -145,15 +147,13 @@ Kind `10050` indicates the user's preferred relays to receive DMs. The event MUS
}
```
Clients SHOULD publish the gift-wrapped kind 1059 events that contain the sealed kind 14 (text) or kind 15 (file) rumors to the relays listed in the recipients kind 10050 event. If that is not found that indicates the user is not ready to receive messages under this NIP and clients shouldn't try.
Clients SHOULD publish the gift-wrapped `kind 1059` events that contain the sealed rumors to the relays listed in the recipients kind 10050 event. If that is not found that indicates the user is not ready to receive messages under this NIP and clients shouldn't try.
## Relays
It's advisable that relays do not serve `kind:1059` to clients other than the ones tagged in them.
Relays MAY protect message metadata by only serving `kind:1059` events to users p-tagged on the event (enforced using [NIP 42 AUTH](./42.md)).
It's advisable that users choose relays that conform to these practices.
Clients SHOULD guide users to keep `kind:10050` lists small (1-3 relays) and SHOULD spread it to as many relays as viable.
Clients SHOULD guide users to keep `kind:10050` lists small (1-3 relays) and SHOULD spread them to as many relays as viable.
## Benefits & Limitations
@@ -170,12 +170,6 @@ This NIP offers the following privacy and security features:
The main limitation of this approach is having to send a separate encrypted event to each receiver. Group chats with more than 100 participants should find a more suitable messaging scheme.
## Implementation
Clients implementing this NIP should by default only connect to the set of relays found in their `kind:10050` list. From that they should be able to load all messages both sent and received as well as get new live updates, making it for a very simple and lightweight implementation that should be fast.
When sending a message to anyone, clients must then connect to the relays in the receiver's `kind:10050` and send the events there but can disconnect right after unless more messages are expected to be sent (e.g. the chat tab is still selected). Clients should also send a copy of their outgoing messages to their own `kind:10050` relay set.
## Examples
This example sends the message `Hola, que tal?` from `nsec1w8udu59ydjvedgs3yv5qccshcj8k05fh3l60k9x57asjrqdpa00qkmr89m` to `nsec12ywtkplvyq5t6twdqwwygavp5lm4fhuang89c943nf2z92eez43szvn4dt`.

2
51.md
View File

@@ -39,6 +39,8 @@ For example, _mute list_ can contain the public keys of spammers and bad actors
| DM relays | 10050 | Where to receive [NIP-17](17.md) direct messages | `"relay"` (see [NIP-17](17.md)) |
| Good wiki authors | 10101 | [NIP-54](54.md) user recommended wiki authors | `"p"` (pubkeys) |
| Good wiki relays | 10102 | [NIP-54](54.md) relays deemed to only host useful articles | `"relay"` (relay URLs) |
| Git authors | 10017 | code (people who produce NIP-34 events) follow list | `"p"` (pubkeys -- with optional relay hint and petname) |
| Git repositories | 10018 | [NIP-34](34.md) followed repositories | `"a"` (kind:30617 repository announcement event) |
### Sets

7
60.md
View File

@@ -50,6 +50,7 @@ There can be multiple `kind:7375` events for the same mint, and multiple proofs
"kind": 7375,
"content": nip44_encrypt({
"mint": "https://stablenut.umint.cash",
"unit": "sat",
"proofs": [
// one or more proofs in the default cashu format
{
@@ -69,6 +70,7 @@ There can be multiple `kind:7375` events for the same mint, and multiple proofs
* `.content` is a [NIP-44](44.md) encrypted payload:
* `mint`: The mint the proofs belong to.
* `proofs`: unencoded proofs
* `unit` the base unit the proofs are denominated in (eg: `sat`, `usd`, `eur`). Default: `sat` if omitted.
* `del`: token-ids that were destroyed by the creation of this token. This assists with state transitions.
When one or more proofs of a token are spent, the token event should be [NIP-09](09.md)-deleted and, if some proofs are unspent from the same token event, a new token event should be created rolling over the unspent proofs and adding any change outputs to the new token event (the change output should include a `del` field).
@@ -84,6 +86,7 @@ Clients SHOULD publish `kind:7376` events to create a transaction history when t
"content": nip44_encrypt([
[ "direction", "in" ], // in = received, out = sent
[ "amount", "1" ],
[ "unit", "sat" ],
[ "e", "<event-id-of-created-token>", "", "created" ]
]),
"tags": [
@@ -93,6 +96,7 @@ Clients SHOULD publish `kind:7376` events to create a transaction history when t
```
* `direction` - The direction of the transaction; `in` for received funds, `out` for sent funds.
* `unit` the base unit of the amount (eg: `sat`, `usd`, `eur`). Default: `sat` if omitted.
Clients MUST add `e` tags to create references of destroyed and created token events along with the marker of the meaning of the tag:
* `created` - A new token event was created.
@@ -121,6 +125,7 @@ If Alice spends 4 sats from this token event
"id": "event-id-1",
"content": nip44_encrypt({
"mint": "https://stablenut.umint.cash",
"unit": "sat",
"proofs": [
{ "id": "1", "amount": 1 },
{ "id": "2", "amount": 2 },
@@ -140,6 +145,7 @@ Her client:
"id": "event-id-2",
"content": nip44_encrypt({
"mint": "https://stablenut.umint.cash",
"unit": "sat",
"proofs": [
{ "id": "1", "amount": 1 },
{ "id": "2", "amount": 2 },
@@ -159,6 +165,7 @@ Her client:
"content": nip44_encrypt([
[ "direction", "out" ],
[ "amount", "4" ],
[ "unit", "sat" ],
[ "e", "<event-id-1>", "", "destroyed" ],
[ "e", "<event-id-2>", "", "created" ],
]),

3
61.md
View File

@@ -51,6 +51,7 @@ Clients MUST prefix the public key they P2PK-lock with `"02"` (for nostr<>cashu
"pubkey": "<sender-pubkey>",
"tags": [
[ "proof", "{\"amount\":1,\"C\":\"02277c66191736eb72fce9d975d08e3191f8f96afb73ab1eec37e4465683066d3f\",\"id\":\"000a93d6f8a1d2c4\",\"secret\":\"[\\\"P2PK\\\",{\\\"nonce\\\":\\\"b00bdd0467b0090a25bdf2d2f0d45ac4e355c482c1418350f273a04fedaaee83\\\",\\\"data\\\":\\\"02eaee8939e3565e48cc62967e2fde9d8e2a4b3ec0081f29eceff5c64ef10ac1ed\\\"}]\"}" ],
[ "unit", "sat" ],
[ "u", "https://stablenut.umint.cash" ],
[ "e", "<nutzapped-event-id>", "<relay-hint>" ],
[ "k", "<nutzapped-kind>"],
@@ -62,6 +63,7 @@ Clients MUST prefix the public key they P2PK-lock with `"02"` (for nostr<>cashu
* `.content` is an optional comment for the nutzap
* `.tags`:
* `proof` is one or more proofs P2PK-locked to the public key the recipient specified in their `kind:10019` event and including a DLEQ proof.
* `unit` the base unit the proofs are denominated in (eg: `sat`, `usd`, `eur`). Default: `sat` if omitted.
* `u` is the mint the URL of the mint EXACTLY as specified by the recipient's `kind:10019`.
* `p` is the Nostr identity public key of nutzap recipient.
* `e` is the event that is being nutzapped, if any.
@@ -95,6 +97,7 @@ Multiple `kind:9321` events can be tagged in the same `kind:7376` event.
"content": nip44_encrypt([
[ "direction", "in" ], // in = received, out = sent
[ "amount", "1" ],
[ "unit", "sat" ],
[ "e", "<7375-event-id>", "<relay-hint>", "created" ] // new token event that was created
]),
"tags": [

8
69.md
View File

@@ -41,7 +41,8 @@ Events are [addressable events](01.md#kinds) and use `38383` as event kind, a p2
["name", "Nakamoto"],
["g", "<geohash>"],
["bond", "0"],
["expiration", "1719391096"],
["expires_at", "1719391096"],
["expiration", "1719995896"],
["y", "lnp2pbot"],
["z", "order"]
],
@@ -55,7 +56,7 @@ Events are [addressable events](01.md#kinds) and use `38383` as event kind, a p2
- `d` < Order ID >: A unique identifier for the order.
- `k` < Order type >: `sell` or `buy`.
- `f` < Currency >: The asset being traded, using the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) standard.
- `s` < Status >: `pending`, `canceled`, `in-progress`, `success`.
- `s` < Status >: `pending`, `canceled`, `in-progress`, `success`, `expired`.
- `amt` < Amount >: The amount of Bitcoin to be traded, the amount is defined in satoshis, if `0` means that the amount of satoshis will be obtained from a public API after the taker accepts the order.
- `fa` < Fiat amount >: The fiat amount being traded, for range orders two values are expected, the minimum and maximum amount.
- `pm` < Payment method >: The payment method used for the trade, if the order has multiple payment methods, they should be separated by a comma.
@@ -67,7 +68,8 @@ Events are [addressable events](01.md#kinds) and use `38383` as event kind, a p2
- `name` [Name]: The name of the maker.
- `g` [Geohash]: The geohash of the operation, it can be useful in a face to face trade.
- `bond` [Bond]: The bond amount, the bond is a security deposit that both parties must pay.
- `expiration` < Expiration\>: The expiration date of the order ([NIP-40](40.md)).
- `expires_at` < Expires At\>: The expiration date of the event being published in `pending` status, after this time the event status SHOULD be changed to `expired`.
- `expiration` < Expiration\>: The expiration date of the event, after this time the relay SHOULD delete it ([NIP-40](40.md)).
- `y` < Platform >: The platform that created the order.
- `z` < Document >: `order`.

137
BE.md Normal file
View File

@@ -0,0 +1,137 @@
NIP-BE
======
Nostr BLE Communications Protocol
---------------------------------
`draft` `optional`
This NIP specifies how Nostr apps can use BLE to communicate and synchronize with each other. The BLE protocol follows a client-server pattern, so this NIP emulates the WS structure in a similar way, but with some adaptations to its limitations.
## Device advertisement
A device advertises itself with:
- Service UUID: `0000180f-0000-1000-8000-00805f9b34fb`
- Data: Device UUID in ByteArray format
## GATT service
The device exposes a Nordic UART Service with the following characteristics:
1. Write Characteristic
- UUID: `87654321-0000-1000-8000-00805f9b34fb`
- Properties: Write
2. Read Characteristic
- UUID: `12345678-0000-1000-8000-00805f9b34fb`
- Properties: Notify, Read
## Role assignment
When one device initially finds another advertising the service, it will read the service's data to get the device UUID and compare it with its own advertised device UUID. For this communication, the device with the highest ID will take the role of GATT Server (Relay), the other will be considered the GATT Client (Client) and will proceed to establish the connection.
For devices whose purpose will require a single role, its device UUID will always be:
- GATT Server: `FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF`
- GATT Client: `00000000-0000-0000-0000-000000000000`
## Messages
All messages will follow [NIP-01](/01.md) message structure. For a given message, a compression stream (DEFLATE) is applied to the message to generate a byte array. Depending on the BLE version, the byte array can be too large for a single message (20-23 bytes in BLE 4.2, 256 bytes in BLE > 4.2). In that case, this byte array is split into any number of batches following the structure:
```
[batch index (first 2 bytes)][batch n][is last batch (last byte)]
```
After reception of all batches, the other device can then join them and decompress. To ensure reliability, only 1 message will be read/written at a time. MTU can be negotiated in advance. The maximum size for a message is 64KB; bigger messages will be rejected.
## Examples
This example implements a function to split and compress a byte array into chunks, as well as another function to join and decompress them in order to obtain the initial result:
```kotlin
fun splitInChunks(message: ByteArray): Array<ByteArray> {
val chunkSize = 500 // define the chunk size
var byteArray = compressByteArray(message)
val numChunks = (byteArray.size + chunkSize - 1) / chunkSize // calculate the number of chunks
var chunkIndex = 0
val chunks = Array(numChunks) { ByteArray(0) }
for (i in 0 until numChunks) {
val start = i * chunkSize
val end = minOf((i + 1) * chunkSize, byteArray.size)
val chunk = byteArray.copyOfRange(start, end)
// add chunk index to the first 2 bytes and last chunk flag to the last byte
val chunkWithIndex = ByteArray(chunk.size + 2)
chunkWithIndex[0] = chunkIndex.toByte() // chunk index
chunk.copyInto(chunkWithIndex, 1)
chunkWithIndex[chunkWithIndex.size - 1] = numChunks.toByte()
// store the chunk in the array
chunks[i] = chunkWithIndex
chunkIndex++
}
return chunks
}
fun joinChunks(chunks: Array<ByteArray>): ByteArray {
val sortedChunks = chunks.sortedBy { it[0] }
var reassembledByteArray = ByteArray(0)
for (chunk in sortedChunks) {
val chunkData = chunk.copyOfRange(1, chunk.size - 1)
reassembledByteArray = reassembledByteArray.copyOf(reassembledByteArray.size + chunkData.size)
chunkData.copyInto(reassembledByteArray, reassembledByteArray.size - chunkData.size)
}
return decompressByteArray(reassembledByteArray)
}
```
## Workflows
### Client to relay
- Any message the client wants to send to a relay will be a write message.
- Any message the client receives from a relay will be a read message.
### Relay to client
The relay should notify the client about any new event matching subscription's filters by using the Notify action of the Read Characteristic. After that, the client can proceed to read messages from the relay.
### Device synchronization
Given the nature of BLE, it is expected that the direct connection between two devices might be extremely intermittent, with gaps of hours or even days. That's why it's crucial to define a synchronization process by following [NIP-77](./77.md) but with an adaptation to the limitations of the technology.
After two devices have successfully connected and established the Client-Server roles, the devices will use half-duplex communication to intermittently send and receive messages.
#### Half-duplex synchronization
Right after the 2 devices connect, the Client starts the workflow by sending the first message.
1. Client - Writes ["NEG-OPEN"](/77.md#initial-message-client-to-relay) message.
2. Server - Sends `write-success`.
3. Client - Sends `read-message`.
4. Server - Responds with ["NEG-MSG"](./77.md#subsequent-messages-bidirectional) message.
5. Client -
1. If the Client has messages missing on the Server, it writes one `EVENT`.
2. If the Client doesn't have any messages missing on the Server, it writes `EOSE`. In this case, subsequent messages to the Server will be empty while the Server claims to have more notes for the Client.
6. Server - Sends `write-success`.
7. Client - Sends `read-message`.
8. Server -
1. If the Server has messages missing on the Client, it responds with one `EVENT`.
2. If the Client doesn't have any messages missing on the Server, it responds with `EOSE`. In this case, subsequent responses to the Client will be empty.
9. If the Client detects that the devices are not synchronized yet, jump to step 5.
10. After the two devices detect that there are no more missing events on both ends, the workflow will pause at this point.
#### Half-duplex event spread
While two devices are connected and synchronized, it might happen that one of them receives a new message from another connected peer. Devices MUST keep track of which notes have been sent to its peers while they are connected. If the newly received event is detected as missing in one of the connected and synchronized peers:
1. If the peer is a Server:
1. Client - It writes the `EVENT`.
2. Server - Sends `write-success`.
2. If the peer is a Client:
1. Server - It will send an empty notification to the Client.
2. Client - Sends `read-message`.
3. Server - Responds with the `EVENT`.

View File

@@ -58,6 +58,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [NIP-39: External Identities in Profiles](39.md)
- [NIP-40: Expiration Timestamp](40.md)
- [NIP-42: Authentication of clients to relays](42.md)
- [NIP-43: Relay Access Metadata and Requests](43.md)
- [NIP-44: Encrypted Payloads (Versioned)](44.md)
- [NIP-45: Counting results](45.md)
- [NIP-46: Nostr Remote Signing](46.md)
@@ -104,6 +105,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
- [NIP-A0: Voice Messages](A0.md)
- [NIP-B0: Web Bookmarks](B0.md)
- [NIP-B7: Blossom](B7.md)
- [NIP-BE: Nostr BLE Communications Protocol](BE.md)
- [NIP-C0: Code Snippets](C0.md)
- [NIP-C7: Chats](C7.md)
- [NIP-EE: E2EE Messaging using MLS Protocol](EE.md)
@@ -182,6 +184,8 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `7376` | Cashu Wallet History | [60](60.md) |
| `7516` | Geocache log | [geocaching][geocaching] |
| `7517` | Geocache proof of find | [geocaching][geocaching] |
| `8000` | Add User | [43](43.md) |
| `8001` | Remove User | [43](43.md) |
| `9000`-`9030` | Group Control Events | [29](29.md) |
| `9041` | Zap Goal | [75](75.md) |
| `9321` | Nutzap | [61](61.md) |
@@ -213,6 +217,7 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `10377` | Proxy Announcement | [Nostr Epoxy][nostr-epoxy] |
| `11111` | Transport Method Announcement | [Nostr Epoxy][nostr-epoxy] |
| `13194` | Wallet Info | [47](47.md) |
| `13534` | Membership Lists | [43](43.md) |
| `17375` | Cashu Wallet Event | [60](60.md) |
| `21000` | Lightning Pub RPC | [Lightning.Pub][lnpub] |
| `22242` | Client Authentication | [42](42.md) |
@@ -221,6 +226,9 @@ They exist to document what may be implemented by [Nostr](https://github.com/nos
| `24133` | Nostr Connect | [46](46.md) |
| `24242` | Blobs stored on mediaservers | [Blossom][blossom] |
| `27235` | HTTP Auth | [98](98.md) |
| `28934` | Join Request | [43](43.md) |
| `28935` | Invite Request | [43](43.md) |
| `28936` | Leave Request | [43](43.md) |
| `30000` | Follow sets | [51](51.md) |
| `30001` | Generic lists | 51 (deprecated) |
| `30002` | Relay sets | [51](51.md) |