# ShredReplay

ShredReplay captures raw Solana shred packets and lets you replay them to any UDP target with precise timing control. The service retains approximately the last 24 hours of shred data, so you can replay any slot or time window from the past day.

Use it to replay historical network data for testing validators, debugging slot behavior, or analyzing shred propagation.

## Authentication

All API endpoints (except health check) require an API key.

Include your key in every request:

```
X-API-Key: YOUR_API_KEY
```

Your API key is provided by the service administrator. Keep it secret.

***

## Browsing Available Data

### List Available Slots

```bash
# List slots (default: first 100)
curl https://shredreplay.thornode.io/v1/slots \
  -H 'X-API-Key: YOUR_KEY'

# Filter by slot range
curl 'https://API_HOST/v1/slots?from=405445570&to=405445600' \
  -H 'X-API-Key: YOUR_KEY'

# Increase limit (max 1000)
curl 'https://API_HOST/v1/slots?from=405445570&limit=500' \
  -H 'X-API-Key: YOUR_KEY'
```

```json
[
  { "slot": 405445570, "segment_count": 1 },
  { "slot": 405445571, "segment_count": 1 },
  { "slot": 405445572, "segment_count": 1 }
]
```

### Inspect a Slot

```bash
curl https://shredreplay.thornode.io/v1/slots/405445570 \
  -H 'X-API-Key: YOUR_KEY'
```

```json
{
  "slot": 405445570,
  "packet_count": 1536,
  "first_recv_ns": 1773335147575703921,
  "last_recv_ns": 1773335147898216408,
  "first_seq_no": 2115839,
  "last_seq_no": 2117374,
  "variant_histogram": {
    "merkle_data": 768,
    "merkle_coding": 768
  }
}
```

| Field                            | Description                                |
| -------------------------------- | ------------------------------------------ |
| `packet_count`                   | Total shred packets captured for this slot |
| `variant_histogram`              | Breakdown by shred type                    |
| `first_recv_ns` / `last_recv_ns` | Capture time window (unix nanoseconds)     |

***

## Replaying Shreds

### Creating a Replay Job

```bash
curl -X POST https://shredreplay.thornode.io/v1/replay/jobs \
  -H 'X-API-Key: YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "selector": { ... },
    "timing": { "mode": "original" },
    "target": "YOUR_IP:PORT",
    "dry_run": false
  }'
```

```json
{
  "job_id": "87549772-7f81-4425-92ee-01b49d6ac076",
  "status": "queued"
}
```

### Packet Selection

Use the `selector` object to choose which packets to replay.

#### By Slot Range

```json
{
  "selector": {
    "slot_range": [405445570, 405445580]
  }
}
```

#### By Specific Slots

```json
{
  "selector": {
    "slot_list": [405445570, 405445575, 405445580]
  }
}
```

#### By Time (Last N Seconds)

```json
{
  "selector": {
    "last_seconds": 300
  }
}
```

Replays the last 5 minutes of captured data.

#### By Exact Time Range

```json
{
  "selector": {
    "time_range": [1773335147575703921, 1773335149898216408]
  }
}
```

Time values are unix nanoseconds. You can find these from the slot inspect or stats endpoints.

#### With Slot Padding

Include neighboring slots around your selection:

```json
{
  "selector": {
    "slot_range": [405445600, 405445600],
    "padding_slots": 5
  }
}
```

Selects slots 405445595 through 405445605.

#### Filter by Shred Type

```json
{
  "selector": {
    "slot_range": [405445570, 405445580],
    "variant_filter": "data_only"
  }
}
```

| Value           | Description               |
| --------------- | ------------------------- |
| `"all"`         | All shred types (default) |
| `"data_only"`   | Only data shreds          |
| `"coding_only"` | Only coding/FEC shreds    |

#### Only Valid Shreds

```json
{
  "selector": {
    "slot_range": [405445570, 405445580],
    "valid_shred_only": true
  }
}
```

Excludes packets that failed shred header parsing.

### Timing Modes

#### Original Timing

```json
{ "timing": { "mode": "original" } }
```

Preserves the original inter-packet delays. Gaps larger than 1ms are accurate within 10%. Use this to simulate real network conditions.

#### Scaled Timing

```json
{ "timing": { "mode": "scaled", "speed": 2.0 } }
```

| Speed  | Effect                   |
| ------ | ------------------------ |
| `2.0`  | 2x faster than original  |
| `0.5`  | Half speed (slow motion) |
| `10.0` | 10x faster               |

#### Max Speed

```json
{ "timing": { "mode": "max_speed" } }
```

Sends all packets as fast as possible while preserving order. No inter-packet delay.

### Dry Run

Preview how many packets would be replayed without sending anything:

```json
{
  "selector": { "slot_range": [405445570, 405445580] },
  "timing": { "mode": "max_speed" },
  "target": "127.0.0.1:9999",
  "dry_run": true
}
```

### Checking Job Status

```bash
curl https://shredreplay.thornode.io/v1/replay/jobs/JOB_ID \
  -H 'X-API-Key: YOUR_KEY'
```

```json
{
  "job_id": "87549772-7f81-4425-92ee-01b49d6ac076",
  "status": "completed",
  "timing_mode": "max_speed",
  "target": "YOUR_IP:PORT",
  "dry_run": false,
  "selected_count": 8000,
  "sent_count": 8000,
  "time_span": [1773335147575703921, 1773335149898216408],
  "started_at": 1773335200000000000,
  "completed_at": 1773335200500000000,
  "error": null
}
```

| Status      | Meaning                            |
| ----------- | ---------------------------------- |
| `queued`    | Waiting to start                   |
| `running`   | Sending packets                    |
| `completed` | All packets sent                   |
| `failed`    | Error occurred (see `error` field) |
| `cancelled` | Cancelled by user                  |

### Cancelling a Job

```bash
curl -X POST https://shredreplay.thornode.io/v1/replay/jobs/JOB_ID/cancel \
  -H 'X-API-Key: YOUR_KEY'
```

***

## Ordering Guarantees

* Packets are sent in the exact order they were originally captured (sorted by receive timestamp, then sequence number)
* Payloads are byte-identical to the original UDP packets — no modification

***

## Typical Workflow

{% stepper %}
{% step %}

### Browse available slots

GET /v1/slots?from=X\&to=Y → see what's captured
{% endstep %}

{% step %}

### Inspect the slots you care about

GET /v1/slots/405445570 → packet count, variant breakdown
{% endstep %}

{% step %}

### Dry run to preview

POST /v1/replay/jobs → dry\_run: true, see selected\_count
{% endstep %}

{% step %}

### Replay for real

POST /v1/replay/jobs → dry\_run: false, packets sent to your target
{% endstep %}

{% step %}

### Monitor progress

GET /v1/replay/jobs/{id} → watch sent\_count increase
{% endstep %}
{% endstepper %}

***

## Rate Limits

API requests are rate-limited per key. If you exceed the limit, you'll receive a `429 Too Many Requests` response. Wait briefly and retry.

## Health Check

```bash
curl https://shredreplay.thornode.io/healthz
```

Returns `{"ok": true}` — no authentication required.

***

## API Reference

| Method | Path                          | Auth    | Description                                   |
| ------ | ----------------------------- | ------- | --------------------------------------------- |
| `GET`  | `/healthz`                    | No      | Health check                                  |
| `GET`  | `/v1/slots`                   | API Key | List available slots (`?from=X&to=Y&limit=N`) |
| `GET`  | `/v1/slots/{slot}`            | API Key | Inspect a specific slot                       |
| `POST` | `/v1/replay/jobs`             | API Key | Create a replay job                           |
| `GET`  | `/v1/replay/jobs/{id}`        | API Key | Get job status                                |
| `POST` | `/v1/replay/jobs/{id}/cancel` | API Key | Cancel a job                                  |
