# Backup Service API Reference

## Overview

The backup-service exposes RESTful APIs for managing MPC backup shares. All endpoints (except health checks) require service-to-service JWT authentication.

**Base URL:** `http://localhost:3002`

---

## Authentication

All `/backup-share/*` endpoints require a service JWT token in the `X-Service-Token` header.

### Token Format

```json
{
  "service": "identity-service",
  "iat": 1704067200,
  "exp": 1704153600
}
```

### Generating Service Tokens

```typescript
import jwt from 'jsonwebtoken';

const token = jwt.sign(
  { service: 'identity-service' },
  process.env.SERVICE_JWT_SECRET,
  { expiresIn: '24h' }
);
```

### Allowed Services

Only the following services can access the backup-service:
- `identity-service` - Primary service for MPC operations
- `recovery-service` - Account recovery operations

Configure via `ALLOWED_SERVICES` environment variable.

---

## API Endpoints

### 1. Store Backup Share

Store an encrypted MPC backup share for a user.

**Endpoint:** `POST /backup-share/store`

**Headers:**
| Header | Required | Description |
|--------|----------|-------------|
| `X-Service-Token` | Yes | Service JWT token |
| `Content-Type` | Yes | `application/json` |

**Request Body:**

```json
{
  "userId": "12345",
  "accountSequence": 1001,
  "publicKey": "02aabbccdd...",
  "encryptedShareData": "base64-encoded-encrypted-data",
  "threshold": 2,
  "totalParties": 3
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `userId` | string | Yes | User identifier from identity-service |
| `accountSequence` | number | Yes | Account sequence number (min: 1) |
| `publicKey` | string | Yes | MPC public key (66-130 characters) |
| `encryptedShareData` | string | Yes | Pre-encrypted share data (AES-256-GCM by identity-service) |
| `threshold` | number | No | Reconstruction threshold (default: 2, range: 2-10) |
| `totalParties` | number | No | Total MPC parties (default: 3, range: 2-10) |

**Success Response (201 Created):**

```json
{
  "success": true,
  "shareId": "1",
  "message": "Backup share stored successfully"
}
```

**Error Responses:**

| Status | Code | Description |
|--------|------|-------------|
| 400 | `VALIDATION_ERROR` | Invalid request body |
| 401 | `UNAUTHORIZED` | Missing or invalid service token |
| 409 | `SHARE_ALREADY_EXISTS` | Share already exists for this user |

**Example:**

```bash
curl -X POST http://localhost:3002/backup-share/store \
  -H "X-Service-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "12345",
    "accountSequence": 1001,
    "publicKey": "02aabbccddee...",
    "encryptedShareData": "encrypted-share-data-here"
  }'
```

---

### 2. Retrieve Backup Share

Retrieve an encrypted MPC backup share for account recovery.

**Endpoint:** `POST /backup-share/retrieve`

**Headers:**
| Header | Required | Description |
|--------|----------|-------------|
| `X-Service-Token` | Yes | Service JWT token |
| `Content-Type` | Yes | `application/json` |

**Request Body:**

```json
{
  "userId": "12345",
  "publicKey": "02aabbccdd...",
  "recoveryToken": "valid-recovery-token",
  "deviceId": "device-uuid"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `userId` | string | Yes | User identifier |
| `publicKey` | string | Yes | MPC public key (66-130 characters) |
| `recoveryToken` | string | Yes | Recovery token verified by identity-service |
| `deviceId` | string | No | Device identifier for audit logging |

**Success Response (200 OK):**

```json
{
  "success": true,
  "encryptedShareData": "base64-encoded-encrypted-data",
  "partyIndex": 2,
  "publicKey": "02aabbccdd..."
}
```

| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Operation success status |
| `encryptedShareData` | string | Decrypted share data (still encrypted by identity-service) |
| `partyIndex` | number | Party index (always 2 for backup share) |
| `publicKey` | string | MPC public key |

**Error Responses:**

| Status | Code | Description |
|--------|------|-------------|
| 400 | `SHARE_NOT_ACTIVE` | Share has been revoked |
| 401 | `UNAUTHORIZED` | Missing or invalid service token |
| 404 | `SHARE_NOT_FOUND` | No share found for user/publicKey |
| 429 | `RATE_LIMIT_EXCEEDED` | Max 3 retrievals per day exceeded |

**Rate Limiting:**
- Maximum 3 retrieves per user per day
- Configurable via `MAX_RETRIEVE_PER_DAY` environment variable
- Counter resets at midnight UTC

**Example:**

```bash
curl -X POST http://localhost:3002/backup-share/retrieve \
  -H "X-Service-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "12345",
    "publicKey": "02aabbccddee...",
    "recoveryToken": "valid-token-from-identity-service"
  }'
```

---

### 3. Revoke Backup Share

Revoke an existing backup share (for key rotation or account closure).

**Endpoint:** `POST /backup-share/revoke`

**Headers:**
| Header | Required | Description |
|--------|----------|-------------|
| `X-Service-Token` | Yes | Service JWT token |
| `Content-Type` | Yes | `application/json` |

**Request Body:**

```json
{
  "userId": "12345",
  "publicKey": "02aabbccdd...",
  "reason": "ROTATION"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `userId` | string | Yes | User identifier |
| `publicKey` | string | Yes | MPC public key (66-130 characters) |
| `reason` | string | Yes | Revocation reason (see below) |

**Valid Revocation Reasons:**

| Reason | Description |
|--------|-------------|
| `ROTATION` | Key rotation (new share will be stored) |
| `ACCOUNT_CLOSED` | User account permanently closed |
| `SECURITY_BREACH` | Security incident requiring key invalidation |
| `USER_REQUEST` | User requested share removal |

**Success Response (200 OK):**

```json
{
  "success": true,
  "message": "Backup share revoked successfully"
}
```

**Error Responses:**

| Status | Code | Description |
|--------|------|-------------|
| 400 | `VALIDATION_ERROR` | Invalid reason value |
| 401 | `UNAUTHORIZED` | Missing or invalid service token |
| 404 | `SHARE_NOT_FOUND` | No share found for user/publicKey |

**Example:**

```bash
curl -X POST http://localhost:3002/backup-share/revoke \
  -H "X-Service-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "12345",
    "publicKey": "02aabbccddee...",
    "reason": "ROTATION"
  }'
```

---

### 4. Health Check

Basic health check endpoint (no authentication required).

**Endpoint:** `GET /health`

**Response (200 OK):**

```json
{
  "status": "ok",
  "timestamp": "2025-01-15T10:30:45.123Z",
  "service": "backup-service"
}
```

---

### 5. Readiness Probe

Kubernetes readiness probe with database connectivity check.

**Endpoint:** `GET /health/ready`

**Success Response (200 OK):**

```json
{
  "status": "ready",
  "database": "connected",
  "timestamp": "2025-01-15T10:30:45.123Z"
}
```

**Failure Response (503 Service Unavailable):**

```json
{
  "status": "not ready",
  "database": "disconnected",
  "error": "Connection refused",
  "timestamp": "2025-01-15T10:30:45.123Z"
}
```

---

### 6. Liveness Probe

Kubernetes liveness probe.

**Endpoint:** `GET /health/live`

**Response (200 OK):**

```json
{
  "status": "alive",
  "timestamp": "2025-01-15T10:30:45.123Z"
}
```

---

## Error Response Format

All error responses follow this standardized format:

```json
{
  "success": false,
  "error": "Human-readable error message",
  "code": "MACHINE_READABLE_CODE",
  "timestamp": "2025-01-15T10:30:45.123Z",
  "path": "/backup-share/store"
}
```

### Error Codes Reference

| Code | HTTP Status | Description |
|------|-------------|-------------|
| `VALIDATION_ERROR` | 400 | Request validation failed |
| `SHARE_NOT_ACTIVE` | 400 | Share has been revoked |
| `UNAUTHORIZED` | 401 | Authentication failed |
| `FORBIDDEN` | 403 | Service not authorized |
| `SHARE_NOT_FOUND` | 404 | Share not found |
| `SHARE_ALREADY_EXISTS` | 409 | Duplicate share |
| `RATE_LIMIT_EXCEEDED` | 429 | Rate limit exceeded |
| `INTERNAL_ERROR` | 500 | Internal server error |

---

## Data Validation Rules

### Public Key

- Length: 66-130 characters
- Format: Hexadecimal string
- 66 chars = compressed ECDSA public key (02/03 prefix + 64 hex chars)
- 130 chars = uncompressed ECDSA public key (04 prefix + 128 hex chars)

### User ID

- Type: String (numeric format)
- Required: Yes
- Pattern: Positive integer as string

### Account Sequence

- Type: Number
- Minimum: 1
- Required: Yes

### Encrypted Share Data

- Type: String
- Required: Yes
- Format: Base64-encoded encrypted data (from identity-service)

---

## Workflow Examples

### Complete Account Setup Flow

```
1. User creates account on identity-service
2. identity-service generates MPC key shares (3 parties)
3. identity-service stores Party 0 (Server Share) locally
4. identity-service sends Party 1 (Client Share) to user device
5. identity-service calls backup-service to store Party 2 (Backup Share)

POST /backup-share/store
{
  "userId": "12345",
  "accountSequence": 1001,
  "publicKey": "02...",
  "encryptedShareData": "encrypted-party-2-data"
}
```

### Account Recovery Flow

```
1. User loses device and initiates recovery
2. User verifies identity through identity-service
3. identity-service generates recovery token
4. identity-service retrieves backup share

POST /backup-share/retrieve
{
  "userId": "12345",
  "publicKey": "02...",
  "recoveryToken": "valid-recovery-token"
}

5. identity-service combines Party 0 + Party 2 to reconstruct key
6. New Party 1 share generated for new device
```

### Key Rotation Flow

```
1. Security event triggers key rotation
2. New key shares generated
3. Old backup share revoked

POST /backup-share/revoke
{
  "userId": "12345",
  "publicKey": "02...",
  "reason": "ROTATION"
}

4. New backup share stored

POST /backup-share/store
{
  "userId": "12345",
  "accountSequence": 1002,
  "publicKey": "03...",
  "encryptedShareData": "new-encrypted-data"
}
```

---

## Security Considerations

### Double Encryption

Data is encrypted twice:
1. **First layer:** identity-service encrypts share data before sending
2. **Second layer:** backup-service encrypts again using AES-256-GCM

This ensures data remains protected even if one system is compromised.

### Rate Limiting

- Retrieves are limited to prevent brute-force attacks
- Default: 3 retrieves per user per day
- Configurable via environment variable

### Audit Logging

All operations are logged with:
- Timestamp
- User ID
- Action (STORE/RETRIEVE/REVOKE)
- Source service
- Source IP address
- Success/failure status

### Sensitive Data Handling

The following fields are redacted in logs:
- `encryptedShareData`
- `recoveryToken`
- `password`
- `secret`

---

## SDK Examples

### TypeScript/JavaScript

```typescript
import axios from 'axios';
import jwt from 'jsonwebtoken';

const BACKUP_SERVICE_URL = 'http://localhost:3002';
const SERVICE_JWT_SECRET = process.env.SERVICE_JWT_SECRET;

function generateServiceToken(): string {
  return jwt.sign(
    { service: 'identity-service' },
    SERVICE_JWT_SECRET,
    { expiresIn: '1h' }
  );
}

async function storeBackupShare(
  userId: string,
  accountSequence: number,
  publicKey: string,
  encryptedShareData: string
): Promise<string> {
  const response = await axios.post(
    `${BACKUP_SERVICE_URL}/backup-share/store`,
    {
      userId,
      accountSequence,
      publicKey,
      encryptedShareData,
    },
    {
      headers: {
        'X-Service-Token': generateServiceToken(),
        'Content-Type': 'application/json',
      },
    }
  );
  return response.data.shareId;
}

async function retrieveBackupShare(
  userId: string,
  publicKey: string,
  recoveryToken: string
): Promise<{ encryptedShareData: string; partyIndex: number }> {
  const response = await axios.post(
    `${BACKUP_SERVICE_URL}/backup-share/retrieve`,
    {
      userId,
      publicKey,
      recoveryToken,
    },
    {
      headers: {
        'X-Service-Token': generateServiceToken(),
        'Content-Type': 'application/json',
      },
    }
  );
  return {
    encryptedShareData: response.data.encryptedShareData,
    partyIndex: response.data.partyIndex,
  };
}

async function revokeBackupShare(
  userId: string,
  publicKey: string,
  reason: 'ROTATION' | 'ACCOUNT_CLOSED' | 'SECURITY_BREACH' | 'USER_REQUEST'
): Promise<void> {
  await axios.post(
    `${BACKUP_SERVICE_URL}/backup-share/revoke`,
    {
      userId,
      publicKey,
      reason,
    },
    {
      headers: {
        'X-Service-Token': generateServiceToken(),
        'Content-Type': 'application/json',
      },
    }
  );
}
```

### cURL Examples

```bash
# Store a backup share
curl -X POST http://localhost:3002/backup-share/store \
  -H "X-Service-Token: $SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "12345",
    "accountSequence": 1001,
    "publicKey": "02aabbccddee1122334455667788990011223344556677889900112233445566",
    "encryptedShareData": "encrypted-data-here"
  }'

# Retrieve a backup share
curl -X POST http://localhost:3002/backup-share/retrieve \
  -H "X-Service-Token: $SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "12345",
    "publicKey": "02aabbccddee1122334455667788990011223344556677889900112233445566",
    "recoveryToken": "recovery-token-here"
  }'

# Revoke a backup share
curl -X POST http://localhost:3002/backup-share/revoke \
  -H "X-Service-Token: $SERVICE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "12345",
    "publicKey": "02aabbccddee1122334455667788990011223344556677889900112233445566",
    "reason": "ROTATION"
  }'

# Health check
curl http://localhost:3002/health
```
