> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/TecharoHq/Anubis/llms.txt
> Use this file to discover all available pages before exploring further.

# Challenge

> Challenge types and implementation interface

## Overview

The challenge package defines the challenge system for bot detection. Challenges are cryptographic or computational puzzles that legitimate browsers can solve but bots typically cannot.

## Types

### Challenge

Metadata about a single challenge issuance.

```go theme={null}
type Challenge struct {
	IssuedAt       time.Time         // When the challenge was issued
	Metadata       map[string]string // Challenge metadata (IP, User-Agent)
	ID             string            // UUID identifying the challenge
	Method         string            // Challenge method ("fast", "preact", etc.)
	RandomData     string            // Hex-encoded random data to process
	PolicyRuleHash string            // Hash of the policy rule that issued this
	Difficulty     int               // Difficulty level (0-64)
	Spent          bool              // Has challenge been solved?
}
```

<ParamField path="IssuedAt" type="time.Time">
  Timestamp when the challenge was created
</ParamField>

<ParamField path="Metadata" type="map[string]string">
  Additional context stored with the challenge

  **Common keys:**

  * `User-Agent`: Client user agent string
  * `X-Real-Ip`: Client IP address
</ParamField>

<ParamField path="ID" type="string">
  Unique UUID (v7) identifier for this challenge
</ParamField>

<ParamField path="Method" type="string">
  Challenge algorithm name

  **Built-in methods:**

  * `fast`: Proof-of-work (SHA-256 based)
  * `preact`: Interactive JavaScript challenge
  * `metarefresh`: Meta refresh redirect challenge
</ParamField>

<ParamField path="RandomData" type="string">
  Hexadecimal-encoded random bytes (64 bytes) that the client must process
</ParamField>

<ParamField path="PolicyRuleHash" type="string">
  Hash of the bot policy rule that triggered this challenge. Used to detect policy changes.
</ParamField>

<ParamField path="Difficulty" type="int">
  Computational difficulty for proof-of-work challenges (0-64). Higher values require more CPU time.

  **Recommended values:**

  * 15-18: Low security, fast solving (\~100ms)
  * 19-22: Medium security (\~500ms)
  * 23-25: High security (\~2-5s)
  * 26+: Very high security (10s+)
</ParamField>

<ParamField path="Spent" type="bool">
  Whether this challenge has already been successfully solved. Prevents replay attacks.
</ParamField>

**Example**

```go theme={null}
import (
	"time"
	"github.com/google/uuid"
	"github.com/TecharoHQ/anubis/lib/challenge"
)

// Create a challenge
chall := &challenge.Challenge{
	ID:         uuid.NewString(),
	Method:     "fast",
	RandomData: "a1b2c3d4...", // hex encoded
	IssuedAt:   time.Now(),
	Difficulty: 20,
	Metadata: map[string]string{
		"User-Agent": r.Header.Get("User-Agent"),
		"X-Real-Ip":  r.Header.Get("X-Real-Ip"),
	},
}
```

See `lib/challenge/challenge.go:5-15`

***

### IssueInput

Input parameters for the challenge Issue method.

```go theme={null}
type IssueInput struct {
	Impressum *config.Impressum
	Rule      *policy.Bot
	Challenge *Challenge
	OGTags    map[string]string
	Store     store.Interface
}
```

<ParamField path="Impressum" type="*config.Impressum">
  Legal/contact information to display on challenge page
</ParamField>

<ParamField path="Rule" type="*policy.Bot">
  The bot detection rule that triggered this challenge
</ParamField>

<ParamField path="Challenge" type="*Challenge">
  The challenge instance being issued
</ParamField>

<ParamField path="OGTags" type="map[string]string">
  OpenGraph metadata tags for the page being protected
</ParamField>

<ParamField path="Store" type="store.Interface">
  Storage backend for persisting challenge state
</ParamField>

See `lib/challenge/interface.go:45-51`

***

### ValidateInput

Input parameters for the challenge Validate method.

```go theme={null}
type ValidateInput struct {
	Rule      *policy.Bot
	Challenge *Challenge
	Store     store.Interface
}
```

<ParamField path="Rule" type="*policy.Bot">
  The policy rule associated with this challenge
</ParamField>

<ParamField path="Challenge" type="*Challenge">
  The challenge being validated
</ParamField>

<ParamField path="Store" type="store.Interface">
  Storage backend for challenge data
</ParamField>

See `lib/challenge/interface.go:53-57`

***

## Interfaces

### Impl

Interface for challenge algorithm implementations.

```go theme={null}
type Impl interface {
	// Setup registers HTTP routes for challenge assets/APIs
	Setup(mux *http.ServeMux)

	// Issue creates a challenge page component
	Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in *IssueInput) (templ.Component, error)

	// Validate checks if a challenge solution is correct
	Validate(r *http.Request, lg *slog.Logger, in *ValidateInput) error
}
```

#### Setup

Registers any HTTP routes needed by the challenge implementation (e.g., for serving JavaScript bundles or API endpoints).

```go theme={null}
Setup(mux *http.ServeMux)
```

<ParamField path="mux" type="*http.ServeMux">
  HTTP router to register routes with
</ParamField>

**Example**

```go theme={null}
func (impl *MyChallenge) Setup(mux *http.ServeMux) {
	mux.Handle("/api/challenge/assets/script.js", 
		http.HandlerFunc(impl.serveScript))
}
```

***

#### Issue

Generates the challenge page component to display to the user.

```go theme={null}
Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in *IssueInput) (templ.Component, error)
```

<ParamField path="w" type="http.ResponseWriter">
  HTTP response writer (for setting headers)
</ParamField>

<ParamField path="r" type="*http.Request">
  HTTP request being challenged
</ParamField>

<ParamField path="lg" type="*slog.Logger">
  Structured logger with request context
</ParamField>

<ParamField path="in" type="*IssueInput">
  Challenge issuance parameters
</ParamField>

<ResponseField name="component" type="templ.Component">
  Templ component to render as the challenge page
</ResponseField>

<ResponseField name="error" type="error">
  Error if challenge generation fails
</ResponseField>

**Example**

```go theme={null}
func (impl *PoW) Issue(w http.ResponseWriter, r *http.Request, lg *slog.Logger, in *IssueInput) (templ.Component, error) {
	return web.ProofOfWorkPage(in.Challenge, in.Rule), nil
}
```

See `lib/challenge/interface.go:64`

***

#### Validate

Validates that the user correctly solved the challenge.

```go theme={null}
Validate(r *http.Request, lg *slog.Logger, in *ValidateInput) error
```

<ParamField path="r" type="*http.Request">
  HTTP request containing the challenge solution
</ParamField>

<ParamField path="lg" type="*slog.Logger">
  Structured logger with request context
</ParamField>

<ParamField path="in" type="*ValidateInput">
  Challenge validation parameters
</ParamField>

<ResponseField name="error" type="error">
  Returns nil if validation succeeds, or an error describing why validation failed
</ResponseField>

**Validation Errors:**

Return a `*challenge.Error` for user-facing validation failures:

```go theme={null}
if solution != expected {
	return challenge.NewError("validate", "Incorrect solution", challenge.ErrFailed)
}
```

See `lib/challenge/interface.go:67`

***

## Functions

### Register

Registers a challenge implementation with the global registry.

```go theme={null}
func Register(name string, impl Impl)
```

<ParamField path="name" type="string" required>
  Unique name for the challenge algorithm (e.g., "fast", "preact")
</ParamField>

<ParamField path="impl" type="Impl" required>
  Challenge implementation
</ParamField>

**Example**

```go theme={null}
func init() {
	challenge.Register("mychal", &MyChallenge{)
}
```

See `lib/challenge/interface.go:20-24`

***

### Get

Retrieves a registered challenge implementation by name.

```go theme={null}
func Get(name string) (Impl, bool)
```

<ParamField path="name" type="string" required>
  Challenge algorithm name
</ParamField>

<ResponseField name="impl" type="Impl">
  The challenge implementation, if found
</ResponseField>

<ResponseField name="ok" type="bool">
  True if the challenge exists in the registry
</ResponseField>

**Example**

```go theme={null}
impl, ok := challenge.Get("fast")
if !ok {
	log.Fatal("challenge algorithm not found")
}
```

See `lib/challenge/interface.go:27-32`

***

### Methods

Returns a sorted list of all registered challenge algorithm names.

```go theme={null}
func Methods() []string
```

<ResponseField name="methods" type="[]string">
  Sorted slice of challenge algorithm names
</ResponseField>

**Example**

```go theme={null}
for _, method := range challenge.Methods() {
	fmt.Println("Available challenge:", method)
}
// Output:
// Available challenge: fast
// Available challenge: metarefresh
// Available challenge: preact
```

See `lib/challenge/interface.go:34-43`

***

## Error Types

### Error

Challenge validation error with public and private messages.

```go theme={null}
type Error struct {
	PrivateReason error  // Internal error (logged)
	Verb          string // Action being performed
	PublicReason  string // User-facing error message
	StatusCode    int    // HTTP status code
}
```

<ParamField path="PrivateReason" type="error">
  Internal error details (not shown to users)
</ParamField>

<ParamField path="Verb" type="string">
  Action that failed (e.g., "validate", "decode")
</ParamField>

<ParamField path="PublicReason" type="string">
  User-friendly error message displayed on error page
</ParamField>

<ParamField path="StatusCode" type="int">
  HTTP status code for the error response (default: 403)
</ParamField>

See `lib/challenge/error.go:24-29`

***

### NewError

Creates a new challenge error.

```go theme={null}
func NewError(verb, publicReason string, privateReason error) *Error
```

<ParamField path="verb" type="string" required>
  Action being performed when error occurred
</ParamField>

<ParamField path="publicReason" type="string" required>
  User-facing error description
</ParamField>

<ParamField path="privateReason" type="error" required>
  Internal error to log and wrap
</ParamField>

<ResponseField name="error" type="*Error">
  Challenge error with status code 403
</ResponseField>

**Example**

```go theme={null}
if nonce == "" {
	return challenge.NewError(
		"validate",
		"Missing required field: nonce",
		challenge.ErrMissingField,
	)
}
```

See `lib/challenge/error.go:15-21`

***

## Sentinel Errors

```go theme={null}
var (
	ErrFailed        = errors.New("challenge: user failed challenge")
	ErrMissingField  = errors.New("challenge: missing field")
	ErrInvalidFormat = errors.New("challenge: field has invalid format")
)
```

<ParamField path="ErrFailed" type="error">
  User submitted an incorrect solution
</ParamField>

<ParamField path="ErrMissingField" type="error">
  Required field missing from request
</ParamField>

<ParamField path="ErrInvalidFormat" type="error">
  Field has incorrect format or encoding
</ParamField>

See `lib/challenge/error.go:9-13`

***

## Built-in Challenges

Anubis includes three challenge implementations:

### fast (Proof of Work)

SHA-256 based proof-of-work challenge. Client must find a nonce that produces a hash with N leading zero bits.

**Difficulty mapping:** Each difficulty level adds one zero bit requirement.

### preact (Interactive)

React-based interactive challenge requiring user interaction. Tests JavaScript execution and user behavior.

### metarefresh

Meta refresh redirect challenge. Tests basic HTML parsing and redirect following.

***

## Related Types

* [Anubis Server](/api/anubis) - Server integration
* [Policy](/api/policy) - Challenge triggering rules
* [Store](/api/store) - Challenge persistence
