# How to Use the Dev Capture Feature in DS2API for Debugging

> Learn to use DS2API dev capture for debugging. Enable capture, wrap responses, and inspect requests/responses via the admin endpoint for efficient troubleshooting.

- Repository: [CJACK./ds2api](https://github.com/CJackHwang/ds2api)
- Tags: how-to-guide
- Published: 2026-04-26

---

**Enable the dev capture feature by setting `DS2API_DEV_PACKET_CAPTURE=true`, wrap upstream HTTP responses using `session.WrapBody()`, and inspect the full request/response pairs via `GET /admin/dev/captures`.**

The **dev capture feature** in DS2API provides a development-packet-capture system that records HTTP request/response pairs for downstream API calls, making it invaluable for debugging complex LLM integrations like Deepseek and Claude. This article explains how to use the dev capture feature in DS2API for debugging, based on the actual implementation in the `CJackHwang/ds2api` repository.

## What Is the Dev Capture Feature?

DS2API’s dev capture system uses an **in-memory circular buffer** to store `Entry` logs for every HTTP transaction you choose to instrument. According to the source code in [[`internal/devcapture/store.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/devcapture/store.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/devcapture/store.go), the system consists of three core components:

- **`devcapture.Store`** – Manages the circular buffer, enforces entry limits, and provides snapshot/clear APIs
- **`Session.WrapBody`** – Intercepts response streams, buffers data up to `maxBodyBytes`, and automatically records entries when the body is fully consumed
- **Admin HTTP routes** – Expose capture data via REST endpoints defined in [[`internal/httpapi/admin/devcapture/routes.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/devcapture/routes.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/devcapture/routes.go) and [[`handler_dev_capture.go`](https://github.com/CJackHwang/ds2api/blob/main/handler_dev_capture.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/devcapture/handler_dev_capture.go)

The Deepseek client demonstrates real-world usage in [[`deepseek/client/client_completion.go`](https://github.com/CJackHwang/ds2api/blob/main/deepseek/client/client_completion.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/deepseek/client/client_completion.go), where response bodies are automatically wrapped for capture.

## Enabling Dev Capture via Environment Variables

Configure the feature using these environment variables before starting the DS2API server:

| Variable | Description | Default |
|----------|-------------|---------|
| `DS2API_DEV_PACKET_CAPTURE` | Master toggle (`true`/`false`) | `true` (disabled on Vercel) |
| `DS2API_DEV_PACKET_CAPTURE_LIMIT` | Maximum entries stored (hard cap at 50) | `20` |
| `DS2API_DEV_PACKET_CAPTURE_MAX_BODY_BYTES` | Maximum bytes retained per response | `5 MiB` |

**Vercel Detection:** If `VERCEL` or `NOW_REGION` environment variables are detected, capture defaults to **disabled** regardless of the toggle setting.

```bash

# Example configuration

export DS2API_DEV_PACKET_CAPTURE=true
export DS2API_DEV_PACKET_CAPTURE_LIMIT=30
export DS2API_DEV_PACKET_CAPTURE_MAX_BODY_BYTES=1048576

```

## How Dev Capture Works: Technical Implementation

Understanding the internal flow helps you implement custom captures correctly.

### Store Initialization

On first use, `devcapture.Global()` creates a singleton `Store` via `NewFromEnv()`. This reads the environment variables and initializes the circular buffer.

### Starting a Capture Session

Before dispatching an upstream HTTP request, call `store.Start(label, url, accountID, requestPayload)`. This returns a `*Session` containing metadata:

```go
store := devcapture.Global()
sess := store.Start(
    "deepseek-chat-call",              // label for identification
    "https://api.deepseek.com/v1/chat/completions",
    "",                                // optional account ID
    requestPayload,                    // map[string]any of the request body
)

```

### Wrapping Response Bodies

Pass the upstream response body to `session.WrapBody(resp.Body, resp.StatusCode)` before reading it. The wrapper streams data while buffering up to `maxBodyBytes`:

```go
resp, err := http.DefaultClient.Do(req)
if err != nil {
    return err
}
// Critical: wrap before reading
resp.Body = sess.WrapBody(resp.Body, resp.StatusCode)

// Consume as normal - capture happens transparently
body, err := io.ReadAll(resp.Body)
resp.Body.Close()

```

When the wrapper encounters `io.EOF` or is closed, it creates a `devcapture.Entry` containing request metadata, HTTP status, the full or truncated response body, and a `ResponseTruncated` boolean flag.

### Retrieving Captured Data

Admin routes expose the captured entries as JSON. Access them via:

```bash
curl http://localhost:8080/admin/dev/captures

```

## Practical Usage Examples

### Automatic Capture with Built-in Clients

The Deepseek client automatically wraps response bodies. Any call to the Deepseek API through DS2API’s internal client is captured without additional code. The implementation in [[`client_completion.go`](https://github.com/CJackHwang/ds2api/blob/main/client_completion.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/deepseek/client/client_completion.go) calls `captureSession.WrapBody` immediately after receiving the HTTP response.

### Manual Capture via Admin API

For arbitrary testing without built-in client integration, use the raw-sample capture endpoint defined in [[`internal/httpapi/admin/rawsamples/handler_raw_samples.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/rawsamples/handler_raw_samples.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/httpapi/admin/rawsamples/handler_raw_samples.go):

```bash
curl -X POST http://localhost:8080/admin/dev/raw-samples/capture \
  -H "Content-Type: application/json" \
  -d '{
    "label": "debug-test",
    "url": "https://api.example.com/v1/data",
    "method": "POST",
    "headers": {"Authorization": "Bearer token"},
    "body": {"key": "value"}
  }'

```

### Programmatic Implementation in Go

Here is a complete example demonstrating manual session management:

```go
package main

import (
	"io"
	"net/http"
	
	"ds2api/internal/devcapture"
)

func makeCapturedRequest() {
	// Initialize from environment variables
	store := devcapture.Global()
	if !store.Enabled() {
		return
	}
	
	// Start session with request metadata
	sess := store.Start(
		"manual-debug",
		"https://api.deepseek.com/v1/chat/completions",
		"account-123",
		map[string]any{
			"model": "deepseek-chat",
			"messages": []map[string]string{
				{"role": "user", "content": "Hello"},
			},
		},
	)
	if sess == nil {
		return
	}
	
	// Execute request
	resp, err := http.Post(
		"https://api.deepseek.com/v1/chat/completions",
		"application/json",
		requestBody,
	)
	if err != nil {
		panic(err)
	}
	
	// Wrap response to enable capture
	resp.Body = sess.WrapBody(resp.Body, resp.StatusCode)
	
	// Reading the body triggers the capture
	io.ReadAll(resp.Body)
	resp.Body.Close()
	
	// Verify capture
	captures := store.Snapshot()
}

```

## Viewing and Clearing Capture Logs

**View all captures:**

```bash
curl http://localhost:8080/admin/dev/captures

```

Sample response:

```json
{
  "enabled": true,
  "limit": 30,
  "max_body_bytes": 1048576,
  "items": [
    {
      "id": "cap_5e2b1ff8-a1c2",
      "created_at": 1714132123,
      "label": "deepseek-chat-call",
      "url": "https://api.deepseek.com/v1/chat/completions",
      "account_id": "account-123",
      "status_code": 200,
      "request_body": "{\"model\":\"deepseek-chat\"}",
      "response_body": "{\"choices\":[...]}",
      "response_truncated": false
    }
  ]
}

```

**Clear the capture log:**

```bash
curl -X DELETE http://localhost:8080/admin/dev/captures

```

Response:

```json
{"success": true, "detail": "capture logs cleared"}

```

## Summary

- **Enable** the feature via `DS2API_DEV_PACKET_CAPTURE=true` environment variable (auto-disabled on Vercel)
- **Initialize** the store using `devcapture.Global()` which reads configuration from environment variables
- **Capture** HTTP traffic by calling `store.Start()` before the request and `session.WrapBody()` on the response body
- **Inspect** captured data through the admin endpoint `GET /admin/dev/captures` or programmatically via `store.Snapshot()`
- **Clear** logs using `DELETE /admin/dev/captures` to free memory
- **Reference** the implementation in [`internal/devcapture/store.go`](https://github.com/CJackHwang/ds2api/blob/main/internal/devcapture/store.go) and usage examples in [`deepseek/client/client_completion.go`](https://github.com/CJackHwang/ds2api/blob/main/deepseek/client/client_completion.go)

## Frequently Asked Questions

### How do I know if a response body was truncated in the capture?

Each capture entry includes a boolean field `response_truncated` that is set to `true` when the response body exceeds the `DS2API_DEV_PACKET_CAPTURE_MAX_BODY_BYTES` limit. As implemented in [[`store.go`](https://github.com/CJackHwang/ds2api/blob/main/store.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/devcapture/store.go), the `captureBody` wrapper silently truncates data beyond this limit while allowing the full stream to pass through to your application.

### Can I use dev capture in production environments?

While technically possible by setting the environment variables, the capture system stores full request and response bodies in memory with no encryption or persistence to disk. According to the source code, this is designed for **development debugging only**. The circular buffer has a hard limit of 50 entries, but sensitive data (API keys, user content) remains in memory until cleared or until the server restarts.

### Why is dev capture disabled on Vercel deployments?

The code explicitly checks for `VERCEL` or `NOW_REGION` environment variables and disables capture regardless of the `DS2API_DEV_PACKET_CAPTURE` setting. This prevents memory accumulation issues on serverless platforms where instance longevity is unpredictable and memory constraints are strictly enforced.

### How do I capture traffic from custom HTTP clients not built into DS2API?

Wrap the response body using the `Session.WrapBody()` method pattern shown in [[`client_completion.go`](https://github.com/CJackHwang/ds2api/blob/main/client_completion.go)](https://github.com/CJackHwang/ds2api/blob/main/internal/deepseek/client/client_completion.go). You must import `ds2api/internal/devcapture`, obtain a session via `store.Start()`, and replace `resp.Body` with the wrapped version before reading. The capture triggers automatically when the body is fully consumed or closed.