# 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
