README.md

# NostrEx

An OTP-compliant Nostr client library for Elixir applications. It provides an interface for connecting to Nostr relays, managing subscriptions, sending and receiving Nostr events.

## Installation

Add `nostr_ex` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:nostr_ex, "~> 0.1.0"}
  ]
end
```

## Usage

### Connecting to Relays

```elixir
# Connect to a relay
iex(1)> NostrEx.connect_relay("wss://relay.example.com")
{:ok, :relay_example_com}
```

Relays are tracked by name as atoms via the `RelayRegistry`. All public facing functions expect this name as input, so you don't have to worry about PIDs. See `RelayManager.registered_names/0`.

### Sending Notes

```elixir
# Create a private key, and send a simple note
iex(2)> privkey = :crypto.strong_rand_bytes(32) |> Base.encode16(case: :lower)
"6dba065ffb6f51b4023d7d24a0c91c125c42ceff344d744d00f3c76e6cb5e03e"
# returns the event ID
iex(3)> NostrEx.send_note("Hello Joe!", private_key)
{:ok, "ed84a5d733a54fc11cb85c4251a007699b223dba1c86b758cdf03a8235bc42ff"}

# Create an event with kind and attrs
iex(4)> NostrEx.create_event(1, %{content: "hello mike"})
{:ok, %Nostr.Event{
  id: nil,
  pubkey: nil,
  kind: 1,
  tags: [],
  created_at: ~U[2025-08-03 15:29:15.261264Z],
  content: "hello mike",
  sig: nil
}}

# Sign the event with your hex-encoded private key
iex(3)> {:ok, signed} = NostrEx.sign_event(event, private_key)
{:ok,
 %Nostr.Event{
   id: "871a08bf8e1b6d286d92238ce44648a94f7397042dd01a4ecc6db0afed745ec3",
   pubkey: "93155d8268a995888fe935ed9de633be690303ab37ba9d698c9f715076a99563",
   kind: 1,
   tags: [],
   created_at: ~U[2025-08-03 15:33:30.652067Z],
   content: "hello mike",
   sig: "60278f60548d5fa49841e0b7518201625aba9a9cf1cdc6d72621290b1943c21971d90c5ca3c2fba49b00ef84f488bac8bc0932c8ccc5ba5e3af2121ce7ad67c9"
 }}

 # send it, returns the event ID
 iex(4)> NostrEx.send_event(signed)
 {:ok, "871a08bf8e1b6d286d92238ce44648a94f7397042dd01a4ecc6db0afed745ec3"}
```

The `send`-type functions take a `send_via` option in `opts` to specify which relays to send the event to.
If not specified, all currently connected relays will be used.

### Subscriptions

NostrEx forwards messages received via a Nostr subscription to the process that created the subscription.
It's up to you to implement how to handle those received events.

You can subscribe to any subscription ID by calling
`Registry.register(NostrEx.PubSub, sub_id, nil)` from the process you want to be subscribed,
and similarly unsubscribe the current process with `Registry.unregister(NostrEx.PubSub, sub_id)`.

```elixir
# Subscribe to a user's notes
NostrEx.subscribe_notes(pubkey)

# Subscribe to a user's profile
NostrEx.subscribe_profile(pubkey)

# Custom subscription with filters
NostrEx.send_subscription([
  authors: [pubkey],
  kinds: [30023],
  since: 1753135689
])
```

### NIP-05 Verification

```elixir
# Verify a NIP-05 identifier
NostrEx.Nip05.verify("user@example.com")
```

## Architecture

NostrEx uses a supervision tree with the following components:

- `RelayManager`: supervises WebSocket connections to relays
- `RelayAgent`: manages subscription state across relays
- `Socket`: handles individual WebSocket connections
- `PubSub`: use Registry to dispatch events to listeners
- `RelayRegistry`: Registry for mapping relay names to connection pids

This library is built on [Sgiath](https://github.com/Sgiath)'s [nostr_lib](https://github.com/Sgiath/nostr-lib) library.
This dependency compiles the libsecp256k1 C library for cryptographic operations,
therefore you will need a C compiler to build this project.

## Contributing

Issues and pull requests are welcome! Please add tests for any new functionality.

## License

MIT License - see LICENSE for details.