API Reference
This document provides a comprehensive reference for all API endpoints exposed by Shugur Relay.
Overview
Shugur Relay exposes several types of APIs:
- WebSocket API - Core Nostr protocol implementation
- HTTP REST API - Management and monitoring endpoints
- Metrics API - Prometheus metrics endpoint
- Web Dashboard - Browser-based interface
WebSocket API (Nostr Protocol)
The main relay functionality follows the Nostr protocol specification.
Endpoint
ws://localhost:8080/wss://relay.example.com/
Protocol Overview
The WebSocket API implements the Nostr protocol using JSON messages. All messages are JSON arrays where the first element is a command string.
Client to Relay Messages
EVENT
Submit a new event to the relay.
["EVENT", <event>]
Example:
[ "EVENT", { "id": "4376c65d2f232afbe9b882a35baa4f6fe8667c4e684749af565f981833ed6a65", "pubkey": "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93", "created_at": 1673347337, "kind": 1, "tags": [], "content": "Walled gardens became prisons, and nostr is the first step towards tearing down the prison walls.", "sig": "908a15e46fb4d8675bab026fc230a0e3542bfade63da02d542fb78b2a8513fcd0092619a2c8c1221e581946e0191f2af505dfdf8657a414dbca329186f009262" }]
Response:
["OK", <event_id>, <accepted>, <message>]
REQ
Start a subscription to receive events.
["REQ", <subscription_id>, <filters>...]
Example:
[ "REQ", "my-subscription", { "authors": ["6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93"], "kinds": [1], "limit": 10 }]
Response: Stream of EVENT messages followed by EOSE.
CLOSE
Close a subscription.
["CLOSE", <subscription_id>]
Example:
["CLOSE", "my-subscription"]
COUNT (NIP-45)
Request event count for filters.
["COUNT", <subscription_id>, <filters>...]
Example:
[ "COUNT", "count-sub", { "authors": ["6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93"], "kinds": [1] }]
Response:
["COUNT", <subscription_id>, {"count": <number>}]
Relay to Client Messages
EVENT
Event matching a subscription.
["EVENT", <subscription_id>, <event>]
EOSE
End of stored events for a subscription.
["EOSE", <subscription_id>]
CLOSED
Subscription was closed by the relay.
["CLOSED", <subscription_id>, <message>]
NOTICE
Informational message from relay.
["NOTICE", <message>]
OK
Response to EVENT submission.
["OK", <event_id>, <accepted>, <message>]
Event Filters
Filters are used in REQ and COUNT messages to specify which events to retrieve.
Filter Fields
Field | Type | Description |
---|---|---|
ids | array of strings | Event IDs |
authors | array of strings | Pubkeys of event authors |
kinds | array of integers | Event kinds |
#<tag> | array of strings | Tag values (e.g., #e , #p ) |
since | integer | Events after this timestamp |
until | integer | Events before this timestamp |
limit | integer | Maximum number of events |
search | string | Full-text search (NIP-50) |
Filter Examples
Latest 10 text notes:
{ "kinds": [1], "limit": 10}
Events by specific author:
{ "authors": ["pubkey_hex"], "limit": 50}
Events with specific tag:
{ "#p": ["referenced_pubkey"], "limit": 20}
Search for text:
{ "kinds": [1], "search": "nostr protocol", "limit": 10}
HTTP REST API
Relay Information (NIP-11)
GET /
Returns relay metadata when accessed with appropriate headers.
Request Headers:
Accept: application/nostr+json
Response:
{ "name": "Shugur Relay", "description": "High-performance Nostr relay", "pubkey": "relay_pubkey_hex", "contact": "admin@example.com", "supported_nips": [1, 2, 3, 4, 9, 11, 15, 16, 17, 20, 22, 23, 24, 25, 26, 28, 33, 40, 44, 50, 59, 65, 78], "software": "https://github.com/Shugur-Network/relay", "version": "1.0.0", "limitation": { "max_message_length": 2048, "max_subscriptions": 10, "max_filters": 100, "max_event_tags": 100, "auth_required": false, "payment_required": false }}
API Endpoints
GET /api/info
Returns relay information in JSON format.
Response:
{ "name": "Shugur Relay", "description": "High-performance Nostr relay", "version": "1.0.0", "contact": "admin@example.com", "pubkey": "relay_pubkey_hex", "supported_nips": [1, 2, 3, 4, 9, 11, 15, 16, 17, 20, 22, 23, 24, 25, 26, 28, 33, 40, 44, 50, 59, 65, 78]}
GET /api/stats
Returns current relay statistics.
Response:
{ "stats": { "active_connections": 42, "messages_processed": 1234567, "events_stored": 98765 }, "uptime": "2d 5h 30m"}
GET /api/cluster
Returns CockroachDB cluster information.
Query Parameters:
type=health
- Returns cluster health information
Response (cluster info):
{ "is_cluster": true, "node_count": 3, "cluster_version": "v23.1.0", "nodes": [ { "node_id": 1, "address": "node1.example.com:26257", "is_live": true, "is_available": true } ]}
Response (health check):
{ "healthy": true, "details": { "nodes_live": 3, "nodes_total": 3, "replication_status": "healthy" }}
Metrics API (Prometheus)
Endpoint
GET http://localhost:2112/metrics
Available Metrics
Connection Metrics
relay_active_connections
- Current number of active WebSocket connectionsrelay_total_connections
- Total connections since startrelay_connection_duration_seconds
- Connection duration histogram
Event Metrics
relay_events_received_total
- Total events receivedrelay_events_stored_total
- Total events storedrelay_events_rejected_total
- Total events rejectedrelay_event_processing_duration_seconds
- Event processing time
Request Metrics
relay_http_requests_total
- Total HTTP requestsrelay_http_request_duration_seconds
- HTTP request durationrelay_websocket_messages_total
- Total WebSocket messages
Database Metrics
relay_db_connections_total
- Database connection statusrelay_db_operations_total
- Database operations by typerelay_db_errors_total
- Database errors by type
System Metrics
relay_uptime_seconds
- Relay uptime in secondsrelay_memory_usage_bytes
- Memory usagerelay_goroutines_total
- Number of goroutines
Web Dashboard
Endpoint
GET http://localhost:8080/
The web dashboard provides a browser-based interface for monitoring the relay.
Features
- Real-time Statistics - Live connection and event counters
- Relay Information - Configuration and metadata display
- Performance Metrics - Charts and graphs (if enabled)
- Cluster Status - CockroachDB cluster information
- Recent Events - Latest events processed
Error Responses
WebSocket Errors
NOTICE Messages
Informational messages sent to clients:
["NOTICE", "rate limit exceeded"]["NOTICE", "event rejected: invalid signature"]["NOTICE", "subscription limit reached"]["NOTICE", "connection will be closed due to policy violation"]
CLOSED Messages
Subscription closed by relay:
["CLOSED", "subscription_id", "rate limit exceeded"]["CLOSED", "subscription_id", "invalid filter"]["CLOSED", "subscription_id", "relay overloaded"]
OK Messages (Event Rejection)
["OK", "event_id", false, "invalid: signature verification failed"]["OK", "event_id", false, "blocked: pubkey is blacklisted"]["OK", "event_id", false, "rate-limited: too many events"]
HTTP Error Responses
400 Bad Request
{ "error": "invalid request format", "message": "request body must be valid JSON"}
429 Too Many Requests
{ "error": "rate limit exceeded", "message": "too many requests, try again later", "retry_after": 60}
500 Internal Server Error
{ "error": "internal server error", "message": "database connection failed"}
Rate Limiting
Connection Limits
- Maximum concurrent connections per IP
- Progressive banning for violations
- Burst allowance for brief traffic spikes
Request Limits
- Events per second per connection
- Requests per second per connection
- Content length limits
Rate Limit Headers
HTTP responses include rate limit information:
X-RateLimit-Limit: 100X-RateLimit-Remaining: 45X-RateLimit-Reset: 1640995200
Client Libraries
JavaScript/TypeScript
const relay = new WebSocket('ws://localhost:8080');
// Send subscriptionrelay.send(JSON.stringify([ "REQ", "sub1", {"kinds": [1], "limit": 10}]));
// Handle messagesrelay.onmessage = (event) => { const message = JSON.parse(event.data); console.log('Received:', message);};
Python
import websocketimport json
def on_message(ws, message): data = json.loads(message) print("Received:", data)
ws = websocket.WebSocketApp("ws://localhost:8080", on_message=on_message)ws.run_forever()
Go
package main
import ( "github.com/gorilla/websocket" "github.com/nbd-wtf/go-nostr")
func main() { relay, err := nostr.RelayConnect(context.Background(), "ws://localhost:8080") if err != nil { panic(err) }
// Subscribe to events sub := relay.Subscribe(context.Background(), nostr.Filters{ {Kinds: []int{1}, Limit: 10}, })
for ev := range sub.Events { fmt.Printf("Received event: %s\n", ev.Content) }}
Testing
WebSocket Testing
# Install wscatnpm install -g wscat
# Connect to relaywscat -c ws://localhost:8080
# Send a REQ message["REQ", "test", {"kinds": [1], "limit": 1}]
HTTP Testing
# Test relay infocurl -H "Accept: application/nostr+json" http://localhost:8080/
# Test statisticscurl http://localhost:8080/api/stats
# Test cluster infocurl http://localhost:8080/api/cluster
Best Practices
Client Implementation
- Handle reconnections - Implement automatic reconnection logic
- Respect rate limits - Monitor rate limit headers and back off when needed
- Use appropriate filters - Limit subscriptions to reduce server load
- Close unused subscriptions - Send CLOSE messages when done
Server Integration
- Monitor metrics - Use Prometheus metrics for monitoring
- Configure rate limits - Set appropriate limits for your use case
- Use HTTPS/WSS - Always use TLS in production
- Regular backups - Backup your database regularly
Performance Optimization
- Use specific filters - Avoid overly broad subscriptions
- Limit event size - Keep event content reasonable
- Monitor connections - Track connection patterns and adjust limits
- Database tuning - Optimize database configuration for your workload
Related Documentation
- Installation Guide: Choose your deployment method
- Configuration Guide: Configure your relay settings
- NIPs Support: Comprehensive NIP implementation details
- Performance Guide: Optimize for production workloads