Client Message

One of the biggest strengths of the Nostr network is the almost limitless possibilities for interoperable user-facing applications. In protocol terminology these applications are often referred to as Clients. Where relays provide a data housing mechanism the clients get that data in front of users in myriad of wild and wonderful ways. Clients use WebSockets as a means to connect to relays and pass relevant data back and forth around the network. In accordance with the protocol base specification (NIP-01) there are 3 main types of messages which clients construct and pass to relays as JSON arrays. This section is concerned with the construction of these message objects using the Client Message Module.

JSON de/serialization

Rust

TODO

Python

The ClientMessage class easily handles the construction of the 3 main message types EVENT, REQ, and CLOSE. In the examples below we can utilize the relevant class methods event(), req() and close(), respectively, to create the client message objects.

Once we have the ClientMessage objects we can use the as_enum() or as_json() methods to present their content. Note that when using as_enum() we unlock some additional methods associated with the ClientMessageEnum class. These allow for logical tests to be performed to establish the type of message object being assessed (for example, is_req() will return a bool result assessing if the object represents an REQ message type).

# Event client message
print("  Event Client Message:")
message = ClientMessage.event(event)
print(f"     - Event Message: {message.as_enum().is_event_msg()}")
print(f"     - JSON: {message.as_json()}")

Note that when constructing a REQ we want to pass through a Filter object which will allow the relay to return data meeting a given set of criteria. Please jump to the Filter section for more details on how to construct these objects.

# Request client message
print("  Request Client Message:")
f = Filter().id(event.id())
message = ClientMessage.req(subscription_id="ABC123", filters=[f])
print(f"     - Request Message: {message.as_enum().is_req()}")
print(f"     - JSON: {message.as_json()}")
# Close client message
print("  Close Client Message:")
message = ClientMessage.close("ABC123")
print(f"     - Close Message: {message.as_enum().is_close()}")
print(f"     - JSON: {message.as_json()}")

When presented with a client message object as either a JSON or an instance of the ClientMessageEnum class we can parse these data using the from_json() or from_enum() methods, respectively.

# Parse Messages from JSON and/or Enum
print("  Parse Client Messages:")
message = ClientMessage.from_json('["REQ","ABC123",{"#p":["421a4dd67be773903f805bcb7975b4d3377893e0e09d7563b8972ee41031f551"]}]')
print(f"     - ENUM: {message.as_enum()}")
f = Filter().pubkey(keys.public_key())
message = ClientMessage.from_enum(cast(ClientMessageEnum, ClientMessageEnum.REQ("ABC123", filters=[f])))
print(f"     - JSON: {message.as_json()}")
JavaScript

The ClientMessage class easily handles the construction of the 3 main message types EVENT, REQ, and CLOSE. In the examples below we can utilize the relevant class methods event(), req() and close(), respectively, to create the client message objects.

Once we have the ClientMessage objects we can use the asJson() method to present their content.

// Create Event client message
console.log("  Event Client Message:");
let clientMessage = ClientMessage.event(event);
console.log(`     - JSON: ${clientMessage.asJson()}`);

Note that when constructing a REQ we want to pass through a Filter object which will allow the relay to return data meeting a given set of criteria. Please jump to the Filter section for more details on how to construct these objects.

// Create Request client message
console.log("  Request Client Message:");
let f = new Filter().id(event.id);
clientMessage = ClientMessage.req("ABC123", [f]);
console.log(`     - JSON: ${clientMessage.asJson()}`);
// Create Close client message
console.log("  Close Client Message:");
clientMessage = ClientMessage.close("ABC123");
console.log(`     - JSON: ${clientMessage.asJson()}`);

When presented with a client message object as either a JSON using the fromJson() method.

// Parse Messages from JSON
console.log("  Parse Client Messages:");
clientMessage = ClientMessage.fromJson('["REQ","ABC123",{"#p":["421a4dd67be773903f805bcb7975b4d3377893e0e09d7563b8972ee41031f551"]}]');
console.log(`     - JSON: ${clientMessage.asJson()}`);
Kotlin

TODO

Swift

TODO

Flutter

TODO

Authorization and Count Messages

Rust

TODO

Python

As an extension of the client messaging section of the protocol NIP-42 and NIP-45 introduce two new messaging types AUTH and COUNT.

The AUTH type is designed to facilitate a method by which clients can authenticate with a given relay. Whereas the COUNT type offers a method for clients can request simple counts of events from relays. These are constructed in much the same way as the earlier message examples, by using the ClientMessage class in conjunction with the relevant methods auth() and count(). As before the as_enum() method can be used to unlock logical test methods (e.g., is_auth()) associated with these message objects.

# Auth client message  (NIP42)
print("  Auth Client Message:")
message = ClientMessage.auth(event)
print(f"     - Auth Message: {message.as_enum().is_auth()}")
print(f"     - JSON: {message.as_json()}")

Note that COUNT is effectively a specific type of REQ message therefore it utilizes the Filter object in constructing the criteria which should be used by the relay to return the count value.

# Count client message (NIP45)
print("  Count Client Message:")
f = Filter().pubkey(keys.public_key())
message = ClientMessage.count(subscription_id="ABC123", filters=[f])
print(f"     - Count Message: {message.as_enum().is_count()}")
print(f"     - JSON: {message.as_json()}")
JavaScript

As an extension of the client messaging section of the protocol NIP-42 and NIP-45 introduce two new messaging types AUTH and COUNT.

The AUTH type is designed to facilitate a method by which clients can authenticate with a given relay. Whereas the COUNT type offers a method for clients can request simple counts of events from relays. These are constructed in much the same way as the earlier message examples, by using the ClientMessage class in conjunction with the relevant methods auth() and count().

// Create Auth client message  (NIP42)
console.log("  Auth Client Message:");
clientMessage = ClientMessage.auth(event);
console.log(`     - JSON: ${clientMessage.asJson()}`);

Note that COUNT is effectively a specific type of REQ message therefore it utilizes the Filter object in constructing the criteria which should be used by the relay to return the count value.

// Create Count client message (NIP45)
console.log("  Count Client Message:");
f = new Filter().pubkey(keys.publicKey);
clientMessage = ClientMessage.count("ABC123", [f]);
console.log(`     - JSON: ${clientMessage.asJson()}`);
Kotlin

TODO

Swift

TODO

Flutter

TODO

Negentropy Messages

Rust

TODO

Python

Finally, the ClientMessageEnum class also opens up three additional message types NEG_OPEN(), NEG_CLOSE() and NEG_MSG(). These do not form part of the standard protocol specification but instead form part of an additional protocol Negentropy for handling set-reconciliation.

To construct these we need to first create them as instance of the ClientMessageEnum class and then pass these into a ClientMessage object using the from_enum() method.

# Negative Open Message
print("  Negative Client Message (open):")
message = ClientMessage.from_enum(cast(ClientMessageEnum, ClientMessageEnum.NEG_OPEN("ABC123", filter=f, id_size=32, initial_message="<hex-msg>")))
print(f"     - Negative Error Open: {message.as_enum().is_neg_open()}")
print(f"     - JSON: {message.as_json()}")
# Negative Close Message
print("  Negative Client Message (close):")
message = ClientMessage.from_enum(cast(ClientMessageEnum, ClientMessageEnum.NEG_CLOSE("ABC123")))
print(f"     - Negative Error Close: {message.as_enum().is_neg_close()}")
print(f"     - JSON: {message.as_json()}")
# Negative Error Message
print("  Negative Client Message (message):")
enum_msg = ClientMessageEnum.NEG_MSG("ABC123", message="This is not the message you are looking for")
message = ClientMessage.from_enum(cast(ClientMessageEnum, enum_msg))
print(f"     - JSON: {message.as_json()}")
print(f"     - Negative Error Message: {message.as_enum().is_neg_msg()}")
JavaScript

Not currently available in the Javascript Bindings.

Kotlin

TODO

Swift

TODO

Flutter

TODO