first commit

This commit is contained in:
Chris Troutner
2025-11-09 16:16:37 -08:00
commit ba2afd47a4
61 changed files with 13847 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
# Developer Documentation
- [prompt.md](./prompt.md) - This is the original prompt [trout](https://github.com/christroutner) gave the AI to build the app. He uses Cursor 2.0 in Planning mode and the Composer 1 LLM.
- [plan](./rest2nostr-poxy-api.plan.md) - This is the plan created by the Cursor AI from the prompt and support files it was given. This is the plan it used to build the app.
+34
View File
@@ -0,0 +1,34 @@
## Build a REST API implementing REST2NOSTR Proxy
Your task is to plan out the building of a REST API server that implements the idea for a REST2NOSTR proxy. The code for this REST API server app should be placed in the `/app` directory. You can edit any files in the `/app` directory. The REST API server should be built using node.js JavaScript and the express.js library. It should also use dotenv to manage the use of environment variables. It should use [api-doc](https://www.npmjs.com/package/api-doc) to generate API documentation for the REST API.
### Nostr
Nostr is a social media protocol. It is defined by nip-01.md in the `nostr/` directory. This is the primary specification that defines Nostr, but there are many other NIPS for other features. If you need to dig deeper into the Nostr standards you can explore the [NIPs on Github](https://github.com/nostr-protocol/nips/tree/master).
The `nostr-sandbox/` directory contains a series of small example code snippets for common social media use-cases using Nostr. You can study this code understand how a Nostr Client would interact with a Nostr Relay.
[This discussion thread on GitHub](https://github.com/nostr-protocol/nips/issues/1549) introduces a concept called REST2NOSTR. It solves an common issue experienced by JavaScript web developers: a high preference for REST APIs over Websockets. The proposal is to create a REST API server that operates as a proxy for the Websockets protocol used by Nostr Relays. This would allow Client developers to interact with a Nostr Relay over a familiar REST API, instead of using Websockets. Here is a summary of the API endpoints the discussion proposes:
| REST Method | Endpoint | Nostr WebSocket Equivalent | Purpose |
| --- | --- | --- | --- |
| `POST` | `/event` | `["EVENT", <event>]` | Publish a signed Nostr event to the relay. |
| `GET` | `/req/` | `["REQ", <sub_id>, <filters>]` | Retrieve a list of events based on filters (stateless query). |
| `POST`/`PUT` | `/req/` | `["REQ", <sub_id>, <filters>]` | Establish a subscription (for long-polling or SSE). |
| `DELETE` | `/req/` | `["CLOSE", <sub_id>]` | Close an existing subscription. |
Your task is to plan out the building of a REST API server that implements the idea for a REST2NOSTR proxy. All code examples in the `nostr-sandbox/` directory that interact with a Relay over Websockets should be able to be implemented using the new REST API. The code examples are a good benchmark to use as a frame of reference as to weather the REST API has been implemented correctly. It would be a good idea to create an `examples/` directory that contain many of these code example, refactored for use with the new REST API.
There is a similar implementation of REST2NOSTR available at [https://nostr-api.com/](https://nostr-api.com/). While the API and documentation are available, the source code for that implementation is not available. However, it is good to study as an example of the kind of output we are looking for.
### Follow the Clean Architecture code pattern
As you plan out the code layout, you should follow the Clean Architecture patterns. Here is background information you can follow to ensure you follow the Clean Architecture pattern:
* [Clean Architecture Summary](https://raw.githubusercontent.com/christroutner/trouts-blog/refs/heads/master/blog/2021-07-06-clean-architecture/index.md) - This is a markdown document that summarizes the Clean Architecture pattern, and links to additional support information.
* [ipfs-service-provider](https://github.com/Permissionless-Software-Foundation/ipfs-service-provider) - This is a node.js JavaScript code base that follows the Clean Architecture patterns. Notice that within the `src` directory, the sub-directories are split up according to the guidance in the Clean Architecture Summary article. This is the primary pattern you should follow.
* A copy of the ipfs-service-provider repository has been copied to the `clean-architecture/` directory, so that you can study the code locally.
### Summary
Your task is to plan out the building of a REST API server that implements the idea for a REST2NOSTR proxy. The code for this REST API server app should be placed in the `/app` directory. You can edit any files in the `/app` directory. The REST API server should be built using node.js JavaScript and the express.js library. It should also use dotenv to manage the use of environment variables. It should use [api-doc](https://www.npmjs.com/package/api-doc) to generate API documentation for the REST API.
+163
View File
@@ -0,0 +1,163 @@
# REST2NOSTR Proxy API Implementation Plan
## Overview
Build a REST API server in `/app` that proxies Nostr WebSocket protocol to REST endpoints, enabling JavaScript developers to interact with Nostr relays via familiar REST APIs instead of WebSockets.
## Architecture
Follow Clean Architecture pattern with these layers:
- **Entities** (`src/entities/`): Domain models (Event, Subscription, etc.)
- **Use Cases** (`src/use-cases/`): Business logic (PublishEvent, QueryEvents, ManageSubscription)
- **Adapters** (`src/adapters/`): External interfaces (NostrRelay WebSocket client, logger)
- **Controllers** (`src/controllers/rest-api/`): Express.js route handlers
## Directory Structure
```
/app
├── src/
│ ├── entities/
│ │ └── event.js
│ ├── use-cases/
│ │ ├── index.js
│ │ ├── publish-event.js
│ │ ├── query-events.js
│ │ └── manage-subscription.js
│ ├── adapters/
│ │ ├── index.js
│ │ ├── nostr-relay.js
│ │ └── wlogger.js
│ ├── controllers/
│ │ ├── index.js
│ │ └── rest-api/
│ │ ├── index.js
│ │ ├── event/
│ │ │ ├── controller.js
│ │ │ └── index.js
│ │ └── req/
│ │ ├── controller.js
│ │ └── index.js
│ └── config/
│ ├── index.js
│ └── env/
│ ├── common.js
│ ├── development.js
│ └── production.js
├── examples/
│ └── [refactored sandbox examples]
├── bin/
│ └── server.js
├── .env.example
├── .env
├── index.js
├── package.json
└── apidoc.json
```
## Core Endpoints
### POST /event
- Maps to: `["EVENT", <event>]`
- Publish a signed Nostr event to relay
- Body: JSON event object
- Response: `{"accepted": true/false, "message": ""}` (maps to `["OK", ...]`)
### GET /req/:subId
- Maps to: `["REQ", <sub_id>, <filters>]`
- Stateless query - returns events immediately
- Query params: filters (JSON encoded or separate params)
- Response: Array of events
### POST /req/:subId
- Maps to: `["REQ", <sub_id>, <filters>]`
- Establish subscription for Server-Sent Events (SSE)
- Body: filters object
- Response: SSE stream of events
### DELETE /req/:subId
- Maps to: `["CLOSE", <sub_id>]`
- Close an existing subscription
- Response: Confirmation
## Implementation Details
### 1. Package Dependencies
- `express`: REST API framework
- `dotenv`: Environment variable management
- `apidoc`: API documentation generation
- `ws` or `nostr-tools`: WebSocket client for Nostr relays
- `winston`: Logging (following example pattern)
### 2. Adapter Layer (`src/adapters/`)
- **nostr-relay.js**: WebSocket client wrapper
- Connect to configured relay(s)
- Send `EVENT`, `REQ`, `CLOSE` messages
- Handle relay responses (`EVENT`, `OK`, `EOSE`, `CLOSED`, `NOTICE`)
- Manage connection pooling for multiple relays
- **wlogger.js**: Winston-based logger
### 3. Use Cases (`src/use-cases/`)
- **publish-event.js**: Validate event, send to relay, return OK response
- **query-events.js**: Stateless query - send REQ, collect events until EOSE, return results
- **manage-subscription.js**: Create/close subscriptions, handle SSE streaming
### 4. Controllers (`src/controllers/rest-api/`)
- **event/controller.js**: Handle POST /event
- **req/controller.js**: Handle GET/POST/DELETE /req/:subId
- Express middleware for validation, error handling
- SSE support for subscription endpoint
### 5. Configuration (`src/config/`)
- Environment-based config (development, production, test)
- Default relay URL(s) from environment variables
- Port, logging level, etc.
### 6. Subscription Management
- Store active subscriptions in memory (Map with subId as key)
- Map subscription IDs to WebSocket connections
- Handle cleanup on DELETE /req/:subId
- Support SSE streaming for POST /req/:subId
### 7. Examples (`examples/`)
Refactor sandbox examples to use REST API:
- `01-create-account/` - Use REST API to publish kind 0 event
- `02-read-posts/` - Use GET /req/:subId for stateless query
- `03-write-post/` - Use POST /event
- `04-read-alice-posts/` - Use GET /req/:subId with author filter
- `14-get-follow-list/` - Use GET /req/:subId with kind 3 filter
- Additional examples as needed
### 8. API Documentation
- Use api-doc annotations in controller files
- Generate docs with `npm run docs`
- Follow pattern from clean-architecture example
## Environment Variables
- `NOSTR_RELAY_URL`: Default relay WebSocket URL (e.g., `wss://nostr-relay.psfoundation.info`)
- `PORT`: Server port (default: 3000)
- `NODE_ENV`: Environment (development, production, test)
- `LOG_LEVEL`: Logging level (info, debug, error)
## Key Implementation Notes
- WebSocket connections: Maintain persistent connections to relay(s) in adapter
- Error handling: Map Nostr relay errors to appropriate HTTP status codes
- Validation: Validate Nostr events before forwarding (event structure, signature)
- SSE: Use Express response.write() for Server-Sent Events in subscription endpoint
- Stateless queries: For GET /req/:subId, collect events until EOSE, then close subscription automatically
+161
View File
@@ -0,0 +1,161 @@
# Test Plan for REST2NOSTR Application
## Overview
Create unit and integration tests for the REST2NOSTR Express.js application following patterns from `tests/testing-example-code/`. Tests will use mocha, chai, and sinon, and cover all code paths exercised by the examples in `/app/examples/`.
## Test Structure
Create test directory structure:
- `app/test/unit/` - Unit tests with mocked dependencies
- `app/test/integration/` - Integration tests with real dependencies
- `app/test/unit/mocks/` - Mock data for unit tests
## Unit Tests
### 1. Entity Tests (`test/unit/entities/`)
- **event-unit.js**: Test Event entity validation and serialization
- Test `isValid()` with valid events
- Test `isValid()` with invalid events (missing fields, wrong types, wrong lengths)
- Test `toJSON()` serialization
- Use mock event data from `mocks/event-mocks.js`
### 2. Use Case Tests (`test/unit/use-cases/`)
- **publish-event-unit.js**: Test PublishEventUseCase
- Mock NostrRelayAdapter.sendEvent()
- Test successful event publishing
- Test invalid event rejection
- Test adapter error handling
- Use mocks from `mocks/nostr-relay-mocks.js`
- **query-events-unit.js**: Test QueryEventsUseCase
- Mock NostrRelayAdapter.sendReq() and sendClose()
- Test successful query with events returned
- Test query timeout handling
- Test CLOSED message handling
- Test EOSE handling
- **manage-subscription-unit.js**: Test ManageSubscriptionUseCase
- Mock NostrRelayAdapter.sendReq() and sendClose()
- Test subscription creation
- Test subscription closure
- Test duplicate subscription prevention
- Test handler callbacks (onEvent, onEose, onClosed)
### 3. Controller Tests (`test/unit/controllers/`)
- **event-controller-unit.js**: Test EventRESTControllerLib
- Mock UseCases.publishEvent.execute()
- Test POST /event with valid event data
- Test POST /event with missing event data
- Test error handling
- Use Express request/response mocks
- **req-controller-unit.js**: Test ReqRESTControllerLib
- Mock UseCases.queryEvents.execute() and manageSubscription methods
- Test GET /req/:subId with filters (JSON string and parsed)
- Test GET /req/:subId with individual query params
- Test POST /req/:subId for SSE subscription
- Test DELETE /req/:subId for closing subscription
- Test error handling for invalid filters
- Test missing subscription ID validation
### 4. Adapter Tests (`test/unit/adapters/`)
- **nostr-relay-unit.js**: Test NostrRelayAdapter
- Mock WebSocket connections
- Test connection establishment
- Test sendEvent() and OK response handling
- Test sendReq() and EVENT/EOSE/CLOSED message handling
- Test sendClose()
- Test message queuing when disconnected
- Test reconnection logic
- Test error handling
### 5. Server Tests (`test/unit/bin/`)
- **server-unit.js**: Test Server class
- Mock Express app, Controllers, and adapters
- Test server initialization
- Test middleware attachment
- Test route attachment
- Test error handling
- Test health check endpoint
## Integration Tests
### 1. API Endpoint Tests (`test/integration/api/`)
- **event-integration.js**: Test POST /event endpoint
- Create a test server instance
- Test publishing kind 0 event (profile metadata) - covers example 01
- Test publishing kind 1 event (text post) - covers example 03
- Test publishing kind 3 event (follow list) - covers example 06
- Test publishing kind 7 event (reaction/like) - covers example 07
- Test invalid event rejection
- Use real Nostr relay connection (may need test relay or mock relay)
- **req-integration.js**: Test GET /req/:subId endpoint
- Create a test server instance
- Test querying kind 1 events (posts) - covers examples 02, 04
- Test querying kind 3 events (follow list) - covers example 05
- Test with various filter combinations
- Test with filters as JSON string query param
- Test with individual query params
- Test error handling
- **subscription-integration.js**: Test POST /req/:subId SSE subscription
- Create a test server instance
- Test SSE subscription creation
- Test event streaming
- Test EOSE handling
- Test subscription closure
- Test DELETE /req/:subId endpoint
### 2. Use Case Integration Tests (`test/integration/use-cases/`)
- **publish-event-integration.js**: Test PublishEventUseCase with real adapter
- **query-events-integration.js**: Test QueryEventsUseCase with real adapter
- **manage-subscription-integration.js**: Test ManageSubscriptionUseCase with real adapter
## Mock Data Files
Create mock data files in `test/unit/mocks/`:
- **event-mocks.js**: Mock event data for various event kinds (0, 1, 3, 7)
- **nostr-relay-mocks.js**: Mock responses from Nostr relay (OK, EVENT, EOSE, CLOSED)
- **controller-mocks.js**: Mock Express request/response objects
## Key Considerations
1. **ES Modules**: The app uses ES modules (import/export) while the example uses CommonJS. Tests should use ES modules with `.js` extension and proper import syntax.
2. **Test Environment**: Integration tests will need a running Nostr relay. Consider:
- Using a test relay URL from config
- Or creating a mock WebSocket server for integration tests
- Or documenting that tests require a relay URL in environment
3. **Test Coverage**: Ensure tests cover:
- All endpoints used by examples (POST /event, GET /req/:subId)
- All event kinds (0, 1, 3, 7)
- Error paths and edge cases
- Validation logic
4. **Test Data**: Use the same test keys and data patterns from examples where applicable (e.g., Alice's private key, Bob's public key)
5. **Async Handling**: Properly handle async/await in tests, especially for WebSocket operations and SSE streams
6. **Cleanup**: Ensure proper cleanup of WebSocket connections and subscriptions in tests
## Test Execution
Tests will run using existing npm scripts:
- `npm test` - Runs unit tests with linting and coverage
- `npm run test:integration` - Runs integration tests with extended timeout
- `npm run coverage` - Generates coverage report
+13
View File
@@ -0,0 +1,13 @@
The express.js app in the `app/` directory needs unit and integration tests. The scope of this task is to create a plan for adding these
When considering the creation of tests, follow the patters provided in the example available in the `testing/testing-example-code/` directory. This is simple node.js application which uses the following testing libraries:
- mocha as a test runner
- chai as an assertion library
- sinon as a stubing library
The code example show common patterns for writing both unit and integration tests.
The `/app/examples/` directory contains working code that I've tested and verified works correctly. The unit and integration tests you create should cover the same code paths exercised by these examples.
The testing dependencies are already installed and listed in the package.json file under the `app/` directory. The package.json file also contains the `test`, `test:integration`, and `coverage` scripts that we'll use to run tests and measure test coverage.