Nostr Integration Proposal

<!---
Please read this!

The Definition of Ready (DoR) can be found at https://developers.minds.com/docs/handbook/how-we-work/#definition-of-ready-dor

Before opening a new issue, make sure to search for keywords in the issues
filtered by the "Type::Feature" label and verify the feature you're about to submit isn't a duplicate.
--->

## Goal

<!---
Clearly outline the goal of the issue and why we are doing it
--->

Enable Minds to publish to the [nostr](https://github.com/fiatjaf/nostr) protocol. 

## What needs to be done

<!---
Clearly detail what needs to be done to achieve the goal set out above
--->

- Extend the DID keypair schema to support **secp256k1** keypairs. Export these to DID too
- Run a nostr relay node
- Implement the [NIP-05](https://github.com/fiatjaf/nostr/blob/master/nips/05.md) protocol


## QA

<!---
Consideration is given to how the issue will be demoed and tested prior to being deployed
--->

Can posts be published to nostr?

## UX/Design

<!---
Sufficient wireframes have been presented and understood by the development team. Full mockups are not required and re-usable components are encouraged.
--->

N/A

## Personas

<!---
Who does this user impact and why (optional)
--->

Decentralized Users


## Experiments

<!---
Experiments are not required, but it should be made clear if one is expected. If experiments are required, the hypothesis should be outlined.
--->

N/A

## Acceptance Criteria

<!--
Everyone should be able to understand what is expected to be delivered
--->

- [ ] Implement a nostr relay PoC

## Definition of Ready Checklist

- [ ] Definition Of Done (DoD)
- [ ] Acceptance criteria
- [ ] Weighted
- [ ] QA
- [ ] UX/Design
- [ ] Personas
- [ ] Experiments

## Notes from Nostr team member

The basic Nostr protocol flow is described here: https://github.com/fiatjaf/nostr/blob/master/nips/01.md

For a basic Nostr interface to work we need 4 simple things:
  1. Each user needs a cryptographic keypair to sign posts with (these are called "events").
  2. Then user' posts must be accessible from a relay that speaks the Nostr protocol.
  3. User metadata is published in the same way as a post is, it just has a different `kind` number and a different structure.
  4. Finally, Nostr currently supports linking readable names ("internet identifiers") like `myname@minds.com` to public keys.

I'm not familiar with the Minds codebase and couldn't figure out much from a quick glance, but from using the website I imagine you have a database with users and each user has an ID and metadata like name, unique username, image and description -- and then you have posts associated with each user.

Considering that, we can solve the above points as following:

1. Minds can just generate a key for each user. This can be done in a single step that populates a database column for each user with a random private key; or it can be generated using a fixed formula that takes the user unique ID from the Minds database itself and applies something like `hmac(secret, id)` to get a private key (in this case if the formula doesn't change and the ID for each doesn't change either the private key will always be the same -- and you can always migrate later to a private key that is saved on the database if you want).
2. Minds would run a Nostr relay that would respond to clients that connect to it from the external world through a websocket with lists of events from Minds users. The queries can vary, but basically they will be requesting a all posts of some kind or multiple kinds for a given user or multiple users -- nothing much different from what the current Minds frontend already uses I believe. This can be done in multiple ways too, but to me it seems like the best way (and the path of least resistance) would be to run a custom relays as a separate instance that will have access to the main Minds database such that upon receiving a request from a Nostr client it would
   1. Query the database for all the requested content;
   2. Process the content from whatever the format it is in the Minds database such that it will become a set of standard Nostr events;
   3. Use the private key associated with the user that created the content to sign the event;
   4. Return the event to the client;
   5. Keep the websocket connection open such that whenever a new event that passes the filter is generated on Minds the relay is notified and can then send this new event to all listening clients that want it (this is so events are received in real-time by Nostr clients without refreshes -- it's slightly more complicated than the rest of the flow, so I imagine it could be done at a later date).
3. Metadata is basically an event like the other ones so it follow the same pattern as above.
4. Along with the metadata each user would also have a "name@minds.com" tag attached to it based on their minds.com URL and there would be a very simple global handler for resolving these names to pubkeys on the minds.com domain (see https://github.com/fiatjaf/nostr/blob/master/nips/05.md) -- mine, for example, would be `fiatjaf@minds.com` and then Nostr clients would query `https://minds.com/.well-known/nostr.json?name=fiatjaf` so the global handler could query the database and figure out the public key associated with the username "fiatjaf" and return that as NIP05 specifies.

I believe the Minds Nostr relay code could (at least initially) make use of https://github.com/fiatjaf/relayer which I wrote as "Relay Framework" (it's a library) that can be instantiated with custom database backends and custom responses etc.

---

There are other ways to do the integration and I believe @jb55 has suggested doing it in a slightly different way, but these are cleanest and more elegant ways to do it with the least resistance in my opinion.

Story points: 13.0