How DS2API Implements DeepSeek Proof of Work (PoW): A Complete Technical Guide

DS2API implements DeepSeek Proof of Work through a native Go package that brute-forces SHA-3-256 hashes using a custom 23-round Keccak permutation to generate valid x-ds-pow-response headers for protected API endpoints.

The CJackHwang/ds2api repository provides a production-ready Go implementation of DeepSeek's Proof of Work protocol. This self-contained solution eliminates external cryptographic dependencies while delivering deterministic cross-platform behavior for solving server-issued challenges.

Understanding the Challenge Structure

DeepSeek's API issues PoW challenges as JSON objects containing cryptographic parameters. The pow/deepseek_pow.go file defines the Challenge struct that captures these server requirements:

type Challenge struct {
    Algorithm  string `json:"algorithm"`   // Only "DeepSeekHashV1" is supported
    Challenge  string `json:"challenge"`   // 64-char hex target hash
    Salt       string `json:"salt"`        // Random per-request salt
    ExpireAt   int64  `json:"expire_at"`   // Unix timestamp when challenge expires
    Difficulty int64  `json:"difficulty"`  // Max nonce to try (default 144000)
    Signature  string `json:"signature"`   // Server-signed data
    TargetPath string `json:"target_path"` // Protected endpoint path
}

The prefix construction determines the input data hashed during solving. The BuildPrefix function concatenates the salt, expiration timestamp, and delimiters into a fixed string absorbed before the nonce:

func BuildPrefix(salt string, expireAt int64) string {
    return salt + "_" + strconv.FormatInt(expireAt, 10) + "_"
}

The DeepSeekHashV1 Algorithm

DS2API implements DeepSeekHashV1 in pow/deepseek_hash.go as a modified SHA-3-256 hash that skips the initial Keccak round. This pure-Go implementation mirrors DeepSeek's WebAssembly reference (wasm_deepseek_hash_v1) exactly.

The algorithm operates through four distinct phases:

  1. State initialization: Creates a 25-word uint64 array ([25]uint64) representing the Keccak-f[1600] state
  2. Data absorption: Processes input in 136-byte blocks (the SHA-3-256 rate) using the custom permutation
  3. Custom permutation: Executes the keccakF23 function performing 23 rounds (rounds 1-23, omitting round 0) of the standard Keccak-f transformation
  4. Digest extraction: Applies standard SHA-3 padding (0x06 suffix and 0x80 final byte) before extracting the first four 64-bit words as the 32-byte digest
func DeepSeekHashV1(data []byte) [32]byte {
    // Implementation handles 23-round Keccak-f[1600] without external dependencies
    // Returns deterministic 32-byte hash matching DeepSeek's WebAssembly output
}

Solving the PoW Challenge

The SolvePow function in pow/deepseek_pow.go performs a deterministic brute-force search across the nonce space [0, difficulty) to find a value satisfying:


DeepSeekHashV1(BuildPrefix(salt, expireAt) || decimal(nonce)) == challenge_target

The solver implements several performance optimizations:

  • Pre-absorption: Computes the Keccak state for the prefix once, storing it in baseState to avoid reprocessing fixed data for every nonce attempt
  • Incremental hashing: Appends each candidate nonce's decimal ASCII representation to the absorbed prefix state
  • Cancellation support: Checks the provided context.Context every 1024 iterations to enable responsive timeout handling
  • Little-endian comparison: Decodes the 64-character hex target into four uint64 words (t0 through t3) for direct state comparison

If the search exhausts the difficulty bound without finding a match, the function returns an error indicating no solution exists within the constraints.

Constructing the Response Header

Once a valid nonce is discovered, BuildPowHeader serializes the solution into the Base64-encoded JSON format expected by DeepSeek's API:

func BuildPowHeader(c *Challenge, answer int64) (string, error) {
    payload, err := json.Marshal(map[string]any{
        "algorithm":   c.Algorithm,
        "challenge":   c.Challenge,
        "salt":        c.Salt,
        "answer":      answer,
        "signature":   c.Signature,
        "target_path": c.TargetPath,
    })
    if err != nil {
        return "", err
    }
    return base64.StdEncoding.EncodeToString(payload), nil
}

The convenience wrapper SolveAndBuildHeader orchestrates the entire workflow:

func SolveAndBuildHeader(ctx context.Context, c *Challenge) (string, error) {
    if c.Algorithm != "DeepSeekHashV1" {
        return "", errors.New("pow: unsupported algorithm: " + c.Algorithm)
    }
    difficulty := c.Difficulty
    if difficulty == 0 {
        difficulty = 144000 // Fallback default difficulty
    }
    answer, err := SolvePow(ctx, c.Challenge, c.Salt, c.ExpireAt, difficulty)
    if err != nil {
        return "", err
    }
    return BuildPowHeader(c, answer)
}

The resulting Base64 string must be attached to subsequent requests as the x-ds-pow-response HTTP header.

Practical Implementation Examples

Solving a Real-Time Challenge from DeepSeek API

This example demonstrates fetching a challenge, solving it with context cancellation support, and attaching the header to a protected request:

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"

    "github.com/CJackHwang/ds2api/pow"
)

func main() {
    // Fetch PoW challenge from DeepSeek endpoint
    resp, err := http.Get("https://api.deepseek.com/v0/chat/create_pow_challenge")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    
    body, _ := io.ReadAll(resp.Body)

    var challenge pow.Challenge
    if err := json.Unmarshal(body, &challenge); err != nil {
        panic(err)
    }

    // Solve with 10-second timeout
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    headerValue, err := pow.SolveAndBuildHeader(ctx, &challenge)
    if err != nil {
        panic(err) // Handles timeout or no-solution cases
    }

    // Attach header to protected request
    req, _ := http.NewRequest("POST", "https://api.deepseek.com/v0/chat/completions", nil)
    req.Header.Set("x-ds-pow-response", headerValue)
    req.Header.Set("Content-Type", "application/json")
    
    client := &http.Client{}
    protectedResp, _ := client.Do(req)
    fmt.Println("Protected endpoint status:", protectedResp.Status)
}

Custom Difficulty and Manual Nonce Verification

For servers requiring higher work factors or custom verification:

// Increase difficulty beyond default 144000
challenge.Difficulty = 500_000

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

header, err := pow.SolveAndBuildHeader(ctx, &challenge)
if err != nil {
    fmt.Printf("PoW solving failed: %v\n", err)
    return
}

// The header contains base64-encoded JSON with the answer field
fmt.Printf("Generated PoW header: %s\n", header)

Summary

  • DS2API provides a zero-dependency Go implementation of DeepSeek's Proof of Work in the pow package
  • DeepSeekHashV1 uses a modified SHA-3-256 with 23 Keccak rounds (skipping round 0), implemented in pow/deepseek_hash.go
  • The Challenge struct in pow/deepseek_pow.go captures server requirements including difficulty, salt, and expiration timestamps
  • SolvePow optimizes performance through prefix pre-absorption and context-aware cancellation checks every 1024 iterations
  • SolveAndBuildHeader generates the x-ds-pow-response header by Base64-encoding a JSON payload containing the discovered nonce

Frequently Asked Questions

What makes DeepSeekHashV1 different from standard SHA-3-256?

DeepSeekHashV1 omits the first round of the standard Keccak-f[1600] permutation. While SHA-3-256 uses 24 rounds (rounds 0-23), DeepSeek's implementation executes only 23 rounds (rounds 1-23). This subtle modification requires custom cryptographic code, as standard libraries cannot configure the starting round index. DS2API's pow/deepseek_hash.go implements this precisely to match DeepSeek's WebAssembly reference.

How does DS2API handle high-difficulty PoW challenges efficiently?

The solver optimizes through state pre-absorption. Rather than re-hashing the entire prefix (salt + timestamp + delimiter) for every nonce attempt, SolvePow computes the Keccak state after absorbing the prefix once, then clones this base state for each candidate nonce. This reduces the per-iteration workload significantly when searching large nonce spaces up to the default difficulty of 144,000 or higher.

Can the PoW solver be cancelled mid-computation?

Yes. The SolvePow function accepts a context.Context parameter and checks for cancellation every 1024 iterations. This allows applications to enforce strict timeouts or respond to shutdown signals without leaking goroutines. If the context is cancelled before a solution is found, the function immediately returns the context error.

Where does DS2API validate the PoW challenge signature?

While the Challenge struct includes a Signature field for server-signed data, the core pow package focuses on solving valid challenges rather than cryptographic verification of the signature itself. According to the source structure, signature validation likely occurs in upstream handlers such as api/index.go before the challenge reaches the solver, ensuring only authentic challenges consume computational resources.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →