Files
c-relay/API.md

996 lines
22 KiB
Markdown

# C-Relay API Documentation
Complete API reference for the C-Relay event-based administration system and advanced features.
## Table of Contents
- [Overview](#overview)
- [Authentication](#authentication)
- [Admin API](#admin-api)
- [Event Structure](#event-structure)
- [Configuration Management](#configuration-management)
- [Auth Rules Management](#auth-rules-management)
- [System Commands](#system-commands)
- [Database Queries](#database-queries)
- [Configuration Reference](#configuration-reference)
- [Real-time Monitoring](#real-time-monitoring)
- [Direct Message Admin](#direct-message-admin)
- [Response Formats](#response-formats)
- [Error Handling](#error-handling)
- [Examples](#examples)
## Overview
C-Relay uses an innovative **event-based administration system** where all configuration and management commands are sent as cryptographically signed Nostr events. This provides:
- **Cryptographic security**: All commands must be signed with the admin private key
- **Audit trail**: Complete history of all administrative actions
- **Real-time updates**: Configuration changes applied instantly
- **Standard protocol**: Uses Nostr events, no custom protocols
- **NIP-44 encryption**: All admin commands and responses are encrypted
### Key Concepts
1. **Admin Keypair**: Generated on first startup, used to sign all admin commands
2. **Relay Keypair**: The relay's identity on the Nostr network
3. **Admin Events**: Kind 23456 events with NIP-44 encrypted commands
4. **Response Events**: Kind 23457 events with NIP-44 encrypted responses
5. **Event-Based Config**: All settings stored as events in the database
## Authentication
### Admin Private Key
The admin private key is displayed **only once** during first startup:
```
========================================
IMPORTANT: SAVE THIS ADMIN PRIVATE KEY
========================================
Admin Private Key: nsec1abc123...
Admin Public Key: npub1def456...
========================================
```
**Critical**: Save this key immediately. It cannot be recovered and is required for all administrative operations.
### Secure Storage
Store the admin private key securely:
```bash
# Environment variable
export C_RELAY_ADMIN_KEY="nsec1abc123..."
# Secure file
echo "nsec1abc123..." > ~/.c-relay-admin
chmod 600 ~/.c-relay-admin
# Password manager (recommended)
# Store in 1Password, Bitwarden, etc.
```
### Key Loss Recovery
If you lose the admin private key:
1. Stop the relay
2. Delete the database file (`*.db`)
3. Restart the relay (generates new keys)
4. **Note**: This deletes all events and configuration
## Admin API
### Event Structure
All admin commands use the same event structure with NIP-44 encrypted content.
#### Admin Command Event (Kind 23456)
```json
{
"id": "computed_event_id",
"pubkey": "admin_public_key_hex",
"created_at": 1234567890,
"kind": 23456,
"content": "AqHBUgcM7dXFYLQuDVzGwMST1G8jtWYyVvYxXhVGEu4nAb4LVw...",
"tags": [
["p", "relay_public_key_hex"]
],
"sig": "event_signature"
}
```
**Fields**:
- `kind`: Must be `23456` for admin commands
- `pubkey`: Admin public key (hex format)
- `content`: NIP-44 encrypted JSON array containing the command
- `tags`: Must include `["p", "relay_pubkey"]` tag
- `sig`: Valid signature from admin private key
#### Encrypted Content Format
The `content` field contains a NIP-44 encrypted JSON array:
```json
["command_name", "param1", "param2", ...]
```
Examples:
```json
["config_query", "all"]
["config_update", [{"key": "relay_name", "value": "My Relay", ...}]]
["blacklist", "pubkey", "abc123..."]
["auth_query", "all"]
["sql_query", "SELECT * FROM events LIMIT 10"]
```
#### Admin Response Event (Kind 23457)
```json
{
"id": "response_event_id",
"pubkey": "relay_public_key_hex",
"created_at": 1234567890,
"kind": 23457,
"content": "BpKCVhfN8eYtRmPqSvWxZnMkL2gHjUiOp3rTyEwQaS5dFg...",
"tags": [
["p", "admin_public_key_hex"],
["e", "request_event_id"]
],
"sig": "response_signature"
}
```
**Fields**:
- `kind`: Always `23457` for admin responses
- `pubkey`: Relay public key (hex format)
- `content`: NIP-44 encrypted JSON response object
- `tags`: Includes `["p", "admin_pubkey"]` and optionally `["e", "request_id"]`
- `sig`: Valid signature from relay private key
### Configuration Management
#### Query All Configuration
**Command**:
```json
["config_query", "all"]
```
**Response** (decrypted):
```json
{
"query_type": "config_all",
"total_results": 27,
"timestamp": 1234567890,
"data": [
{
"key": "relay_name",
"value": "C-Relay",
"data_type": "string",
"category": "relay",
"description": "Relay name displayed in NIP-11"
},
{
"key": "auth_enabled",
"value": "false",
"data_type": "boolean",
"category": "auth",
"description": "Enable whitelist/blacklist authentication"
}
]
}
```
#### Update Configuration
**Command**:
```json
["config_update", [
{
"key": "relay_name",
"value": "My Awesome Relay",
"data_type": "string",
"category": "relay"
},
{
"key": "max_subscriptions_per_client",
"value": "50",
"data_type": "integer",
"category": "limits"
}
]]
```
**Response** (decrypted):
```json
{
"query_type": "config_update",
"status": "success",
"total_results": 2,
"timestamp": 1234567890,
"data": [
{
"key": "relay_name",
"value": "My Awesome Relay",
"status": "updated"
},
{
"key": "max_subscriptions_per_client",
"value": "50",
"status": "updated"
}
]
}
```
### Auth Rules Management
#### Add Blacklist Rule
**Command**:
```json
["blacklist", "pubkey", "abc123def456..."]
```
**Response** (decrypted):
```json
{
"query_type": "auth_add",
"status": "success",
"message": "Blacklist rule added successfully",
"timestamp": 1234567890
}
```
#### Add Whitelist Rule
**Command**:
```json
["whitelist", "pubkey", "def456abc123..."]
```
#### Delete Auth Rule
**Command**:
```json
["delete_auth_rule", "blacklist", "pubkey", "abc123def456..."]
```
**Response** (decrypted):
```json
{
"query_type": "auth_delete",
"status": "success",
"message": "Auth rule deleted successfully",
"timestamp": 1234567890
}
```
#### Query All Auth Rules
**Command**:
```json
["auth_query", "all"]
```
**Response** (decrypted):
```json
{
"query_type": "auth_rules_all",
"total_results": 5,
"timestamp": 1234567890,
"data": [
{
"rule_type": "blacklist",
"pattern_type": "pubkey",
"pattern_value": "abc123...",
"action": "deny"
},
{
"rule_type": "whitelist",
"pattern_type": "pubkey",
"pattern_value": "def456...",
"action": "allow"
}
]
}
```
#### Query Specific Rule Type
**Command**:
```json
["auth_query", "whitelist"]
```
#### Query Specific Pattern
**Command**:
```json
["auth_query", "pattern", "abc123..."]
```
### System Commands
#### System Status
**Command**:
```json
["system_command", "system_status"]
```
**Response** (decrypted):
```json
{
"query_type": "system_status",
"timestamp": 1234567890,
"status": "running",
"uptime_seconds": 86400,
"version": "0.6.0",
"relay_pubkey": "relay_public_key_hex",
"database_size_bytes": 10485760,
"total_events": 15432,
"active_connections": 42,
"active_subscriptions": 156
}
```
#### Clear All Auth Rules
**Command**:
```json
["system_command", "clear_all_auth_rules"]
```
**Response** (decrypted):
```json
{
"query_type": "system_command",
"status": "success",
"message": "All auth rules cleared",
"timestamp": 1234567890
}
```
#### Database Statistics
**Command**:
```json
["stats_query"]
```
**Response** (decrypted):
```json
{
"query_type": "stats_query",
"timestamp": 1234567890,
"database_size_bytes": 10485760,
"total_events": 15432,
"database_created_at": 1234567800,
"latest_event_at": 1234567890,
"event_kinds": [
{
"kind": 1,
"count": 12000,
"percentage": 77.8
},
{
"kind": 0,
"count": 2500,
"percentage": 16.2
}
],
"time_stats": {
"total": 15432,
"last_24h": 234,
"last_7d": 1456,
"last_30d": 5432
},
"top_pubkeys": [
{
"pubkey": "abc123...",
"event_count": 1234,
"percentage": 8.0
}
]
}
```
### Database Queries
#### SQL Query Command
Execute read-only SQL queries against the relay database.
**Command**:
```json
["sql_query", "SELECT * FROM events ORDER BY created_at DESC LIMIT 10"]
```
**Response** (decrypted):
```json
{
"query_type": "sql_query",
"request_id": "request_event_id",
"timestamp": 1234567890,
"query": "SELECT * FROM events ORDER BY created_at DESC LIMIT 10",
"execution_time_ms": 45,
"row_count": 10,
"columns": ["id", "pubkey", "created_at", "kind", "content", "tags", "sig"],
"rows": [
["abc123...", "def456...", 1234567890, 1, "Hello world", "[]", "sig123..."],
["ghi789...", "jkl012...", 1234567880, 0, "{\"name\":\"Alice\"}", "[]", "sig456..."]
]
}
```
#### Security Features
- **Read-only**: Only SELECT statements allowed
- **Query timeout**: 5 seconds maximum
- **Result limit**: 1000 rows maximum
- **Logging**: All queries logged with execution time
- **Validation**: SQL injection protection
#### Available Tables and Views
**Core Tables**:
- `events` - All Nostr events
- `config` - Configuration parameters
- `auth_rules` - Authentication rules
- `subscription_events` - Subscription lifecycle log
- `event_broadcasts` - Event broadcast log
**Views**:
- `recent_events` - Last 1000 events
- `event_stats` - Event statistics by type
- `subscription_analytics` - Subscription metrics
- `active_subscriptions_log` - Currently active subscriptions
- `event_kinds_view` - Event distribution by kind
- `top_pubkeys_view` - Top 10 pubkeys by event count
- `time_stats_view` - Time-based statistics
#### Example Queries
**Recent events**:
```sql
SELECT id, pubkey, created_at, kind
FROM events
ORDER BY created_at DESC
LIMIT 20
```
**Event distribution**:
```sql
SELECT * FROM event_kinds_view
ORDER BY count DESC
```
**Active subscriptions**:
```sql
SELECT * FROM active_subscriptions_log
ORDER BY created_at DESC
```
**Database statistics**:
```sql
SELECT
(SELECT COUNT(*) FROM events) as total_events,
(SELECT COUNT(*) FROM subscription_events) as total_subscriptions,
(SELECT COUNT(DISTINCT pubkey) FROM events) as unique_pubkeys
```
**Events by specific pubkey**:
```sql
SELECT id, created_at, kind, content
FROM events
WHERE pubkey = 'abc123...'
ORDER BY created_at DESC
LIMIT 50
```
**Events in time range**:
```sql
SELECT COUNT(*) as count, kind
FROM events
WHERE created_at BETWEEN 1234567000 AND 1234567890
GROUP BY kind
ORDER BY count DESC
```
## Configuration Reference
### Basic Relay Settings
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `relay_name` | string | "C-Relay" | Relay name (NIP-11) |
| `relay_description` | string | "C Nostr Relay" | Relay description |
| `relay_contact` | string | "" | Admin contact info |
| `relay_software` | string | "c-relay" | Software identifier |
| `relay_version` | string | auto | Software version |
| `supported_nips` | string | "1,9,11,13,15,20,33,40,42,45,50,70" | Supported NIPs |
| `language_tags` | string | "*" | Supported languages |
| `relay_countries` | string | "*" | Supported countries |
| `posting_policy` | string | "" | Posting policy URL |
| `payments_url` | string | "" | Payment URL |
### Connection & Limits
| Key | Type | Default | Range | Restart Required |
|-----|------|---------|-------|------------------|
| `max_connections` | integer | 1000 | 1-10000 | Yes |
| `max_subscriptions_per_client` | integer | 25 | 1-100 | No |
| `max_total_subscriptions` | integer | 5000 | 100-50000 | No |
| `max_message_length` | integer | 65536 | 1024-1048576 | No |
| `max_event_tags` | integer | 2000 | 10-10000 | No |
| `max_content_length` | integer | 65536 | 1-1048576 | No |
### Authentication & Access Control
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `auth_enabled` | boolean | false | Enable whitelist/blacklist |
| `nip42_auth_required` | boolean | false | Require NIP-42 auth |
| `nip42_auth_required_kinds` | string | "" | Kinds requiring NIP-42 |
| `nip42_challenge_timeout` | integer | 300 | Challenge timeout (seconds) |
### Proof of Work (NIP-13)
| Key | Type | Default | Values | Description |
|-----|------|---------|--------|-------------|
| `pow_min_difficulty` | integer | 0 | 0-40 | Minimum PoW difficulty |
| `pow_mode` | string | "optional" | disabled/optional/required | PoW enforcement mode |
### Event Expiration (NIP-40)
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `nip40_expiration_enabled` | boolean | true | Enable expiration support |
| `nip40_expiration_strict` | boolean | false | Reject expired events |
| `nip40_expiration_filter` | boolean | true | Filter expired from results |
| `nip40_expiration_grace_period` | integer | 300 | Grace period (seconds) |
### Monitoring
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `kind_24567_reporting_throttle_sec` | integer | 5 | Monitoring event throttle |
### Dynamic vs Restart-Required
**Dynamic (No Restart)**:
- All NIP-11 relay information
- Authentication settings
- Subscription limits
- Event validation limits
- Proof of Work settings
- Expiration settings
**Restart Required**:
- `max_connections`
- `relay_port`
- Database settings
## Real-time Monitoring
C-Relay provides subscription-based real-time monitoring using ephemeral events (kind 24567).
### Activation
Subscribe to kind 24567 events to activate monitoring:
```json
["REQ", "monitoring-sub", {"kinds": [24567]}]
```
### Monitoring Event Types
Subscribe to specific monitoring types using d-tag filters:
```json
["REQ", "event-kinds", {"kinds": [24567], "#d": ["event_kinds"]}]
["REQ", "time-stats", {"kinds": [24567], "#d": ["time_stats"]}]
["REQ", "top-pubkeys", {"kinds": [24567], "#d": ["top_pubkeys"]}]
["REQ", "cpu-metrics", {"kinds": [24567], "#d": ["cpu_metrics"]}]
```
### Event Structure
```json
{
"kind": 24567,
"pubkey": "relay_pubkey",
"created_at": 1234567890,
"content": "{\"data_type\":\"event_kinds\",\"timestamp\":1234567890,...}",
"tags": [
["d", "event_kinds"]
]
}
```
### Monitoring Types
#### Event Distribution (`event_kinds`)
```json
{
"data_type": "event_kinds",
"timestamp": 1234567890,
"total_events": 15432,
"kinds": [
{"kind": 1, "count": 12000, "percentage": 77.8},
{"kind": 0, "count": 2500, "percentage": 16.2}
]
}
```
#### Time Statistics (`time_stats`)
```json
{
"data_type": "time_stats",
"timestamp": 1234567890,
"total_events": 15432,
"last_24h": 234,
"last_7d": 1456,
"last_30d": 5432
}
```
#### Top Publishers (`top_pubkeys`)
```json
{
"data_type": "top_pubkeys",
"timestamp": 1234567890,
"top_pubkeys": [
{"pubkey": "abc123...", "count": 1234, "percentage": 8.0},
{"pubkey": "def456...", "count": 987, "percentage": 6.4}
]
}
```
#### CPU Metrics (`cpu_metrics`)
```json
{
"data_type": "cpu_metrics",
"timestamp": 1234567890,
"cpu_percent": 12.5,
"memory_mb": 45.2,
"uptime_seconds": 86400
}
```
#### Active Subscriptions (`active_subscriptions`) - Admin Only
```json
{
"data_type": "active_subscriptions",
"timestamp": 1234567890,
"total_subscriptions": 156,
"subscriptions_by_client": [
{"client_id": "client1", "count": 12},
{"client_id": "client2", "count": 8}
]
}
```
### Configuration
Control monitoring frequency:
```json
["config_update", [{
"key": "kind_24567_reporting_throttle_sec",
"value": "10",
"data_type": "integer",
"category": "monitoring"
}]]
```
### Performance
- Events are ephemeral (not stored)
- Automatic activation/deactivation based on subscriptions
- Throttling prevents excessive event generation
- Minimal overhead when no clients monitoring
## Direct Message Admin
Control your relay by sending direct messages from any Nostr client.
### Setup
1. The relay has its own keypair (shown on startup)
2. The relay knows the admin public key
3. Send NIP-17 direct messages to the relay
### Available Commands
Send a DM containing any of these keywords:
| Command | Aliases | Response |
|---------|---------|----------|
| Statistics | stats, statistics | Database statistics |
| Configuration | config, configuration | Current configuration |
### Example
Using any Nostr client that supports NIP-17:
1. Find the relay's public key (shown on startup)
2. Send a DM: "stats"
3. Receive a DM with current relay statistics
### Response Format
The relay responds with a NIP-17 DM containing:
**Stats Response**:
```
Relay Statistics
================
Total Events: 15,432
Database Size: 10.5 MB
Active Connections: 42
Active Subscriptions: 156
Uptime: 1 day, 2 hours
```
**Config Response**:
```
Relay Configuration
===================
Name: My Awesome Relay
Description: Community relay
Max Subscriptions: 25
Auth Enabled: false
PoW Difficulty: 0
```
## Response Formats
### Success Response
```json
{
"query_type": "command_name",
"status": "success",
"message": "Operation completed successfully",
"timestamp": 1234567890,
"data": [...]
}
```
### Error Response
```json
{
"query_type": "command_name",
"status": "error",
"error": "Error description",
"timestamp": 1234567890
}
```
### Query Response
```json
{
"query_type": "query_name",
"total_results": 10,
"timestamp": 1234567890,
"data": [...]
}
```
## Error Handling
### Common Errors
| Error | Cause | Solution |
|-------|-------|----------|
| `invalid_signature` | Event signature invalid | Check admin private key |
| `unauthorized` | Wrong admin pubkey | Use correct admin key |
| `invalid_command` | Unknown command | Check command format |
| `validation_failed` | Invalid parameter value | Check parameter ranges |
| `database_error` | Database operation failed | Check database integrity |
| `timeout` | Query took too long | Simplify query or increase timeout |
### Error Response Example
```json
{
"query_type": "config_update",
"status": "error",
"error": "field validation failed: invalid port number '99999' (must be 1-65535)",
"timestamp": 1234567890
}
```
## Examples
### JavaScript/TypeScript Example
```javascript
import { SimplePool, nip44, getPublicKey, finalizeEvent } from 'nostr-tools';
const adminPrivkey = 'your_admin_privkey_hex';
const adminPubkey = getPublicKey(adminPrivkey);
const relayPubkey = 'relay_pubkey_hex';
const relayUrl = 'ws://localhost:8888';
// Create admin command
async function sendAdminCommand(command) {
const pool = new SimplePool();
// Encrypt command with NIP-44
const encryptedContent = await nip44.encrypt(
adminPrivkey,
relayPubkey,
JSON.stringify(command)
);
// Create event
const event = finalizeEvent({
kind: 23456,
created_at: Math.floor(Date.now() / 1000),
tags: [['p', relayPubkey]],
content: encryptedContent
}, adminPrivkey);
// Publish event
await pool.publish([relayUrl], event);
// Subscribe to response
const sub = pool.sub([relayUrl], [{
kinds: [23457],
'#p': [adminPubkey],
since: Math.floor(Date.now() / 1000)
}]);
return new Promise((resolve) => {
sub.on('event', async (event) => {
// Decrypt response
const decrypted = await nip44.decrypt(
adminPrivkey,
relayPubkey,
event.content
);
resolve(JSON.parse(decrypted));
sub.unsub();
});
});
}
// Query configuration
const config = await sendAdminCommand(['config_query', 'all']);
console.log(config);
// Update configuration
const result = await sendAdminCommand(['config_update', [
{
key: 'relay_name',
value: 'My Relay',
data_type: 'string',
category: 'relay'
}
]]);
console.log(result);
// Add blacklist rule
const blacklist = await sendAdminCommand([
'blacklist',
'pubkey',
'abc123...'
]);
console.log(blacklist);
// Execute SQL query
const query = await sendAdminCommand([
'sql_query',
'SELECT * FROM events ORDER BY created_at DESC LIMIT 10'
]);
console.log(query);
```
### Python Example
```python
from nostr_sdk import Keys, Client, EventBuilder, Filter, nip44
admin_privkey = "your_admin_privkey_hex"
relay_pubkey = "relay_pubkey_hex"
relay_url = "ws://localhost:8888"
# Initialize
keys = Keys.parse(admin_privkey)
client = Client(keys)
client.add_relay(relay_url)
client.connect()
# Send admin command
async def send_admin_command(command):
# Encrypt command
encrypted = nip44.encrypt(
keys.secret_key(),
relay_pubkey,
json.dumps(command)
)
# Create event
event = EventBuilder.new(
kind=23456,
content=encrypted,
tags=[["p", relay_pubkey]]
).to_event(keys)
# Publish
await client.send_event(event)
# Wait for response
filter = Filter().kind(23457).pubkey(relay_pubkey).since(int(time.time()))
events = await client.get_events_of([filter], timeout=5)
if events:
# Decrypt response
decrypted = nip44.decrypt(
keys.secret_key(),
relay_pubkey,
events[0].content()
)
return json.loads(decrypted)
# Query configuration
config = await send_admin_command(["config_query", "all"])
print(config)
```
### Bash/curl Example
```bash
#!/bin/bash
# Note: This is a simplified example. Real implementation requires:
# - NIP-44 encryption
# - Event signing
# - WebSocket connection
RELAY_URL="ws://localhost:8888"
ADMIN_PRIVKEY="your_admin_privkey"
RELAY_PUBKEY="relay_pubkey"
# Use nostrtool or similar for proper event creation
nostrtool event \
--kind 23456 \
--content "$(echo '["config_query","all"]' | nip44-encrypt)" \
--tag p "$RELAY_PUBKEY" \
--private-key "$ADMIN_PRIVKEY" \
| nostrtool send "$RELAY_URL"
```
---
## Additional Resources
- **[Configuration Guide](docs/configuration_guide.md)** - Detailed configuration options
- **[Deployment Guide](docs/deployment_guide.md)** - Production deployment
- **[NIP-42 Authentication](docs/NIP-42_Authentication.md)** - Authentication setup
- **[User Guide](docs/user_guide.md)** - End-user documentation
## Support
For API questions or issues:
- Open an issue on GitHub
- Check existing documentation
- Join the Nostr community
---
**API Version**: 0.6.0
**Last Updated**: 2026-01-23