# How Cursor Secrets Enable Secure Pagination in agentsview

> Learn how agentsview uses cursor secrets for secure pagination. Discover how HMAC-SHA256 protects tokens without server-side storage.

- Repository: [Kenn Software/agentsview](https://github.com/kenn-io/agentsview)
- Tags: how-to-guide
- Published: 2026-06-20

---

**The cursor secret in agentsview is a persistent 32-byte random value that cryptographically signs pagination cursors using HMAC-SHA256, ensuring tamper-evident token validation without server-side session storage.**

agentsview implements stateless pagination for its list-type APIs using cryptographically secured cursor tokens. The cursor secret provides the foundation for this security model by enabling the server to detect forged or manipulated pagination requests while eliminating the need to store per-client pagination state.

## What Are Cursor Secrets in agentsview?

A **cursor secret** is a random 32-byte value generated once during the application lifecycle and stored in the user's TOML configuration file. This secret acts as the cryptographic key for all cursor-based pagination operations in agentsview, including session lists and search results.

The secret remains exclusively server-side and is never exposed to API clients. When the server generates a pagination cursor, it embeds an HMAC signature derived from this secret. When a client returns that cursor to fetch the next page, the server recomputes the HMAC and rejects the request with a `400 Bad Request` response if the signatures do not match.

## Generation and Storage Mechanism

The [`internal/config/config.go`](https://github.com/kenn-io/agentsview/blob/main/internal/config/config.go) file handles the complete lifecycle of cursor secret creation and persistence.

### Automatic Generation via ensureCursorSecret

During configuration loading, the `ensureCursorSecret` method checks whether `cursor_secret` exists in the config. If the field is empty, the function generates a new cryptographically secure random value, creates the data directory if needed, and persists the secret to disk.

```go
func (c *Config) ensureCursorSecret() error {
    if c.CursorSecret != "" {                 // already set → nothing to do
        return nil
    }

    // generate 32 random bytes → base‑64 string
    b := make([]byte, 32)
    if _, err := rand.Read(b); err != nil {
        return fmt.Errorf("generating secret: %w", err)
    }
    secret := base64.StdEncoding.EncodeToString(b)
    c.CursorSecret = secret

    // make sure the data directory exists
    if err := os.MkdirAll(c.DataDir, 0o700); err != nil {
        return fmt.Errorf("creating data dir: %w", err)
    }

    // persist the new secret back to the config file
    existing, err := c.readConfigMap()
    if err != nil {
        return err
    }
    existing["cursor_secret"] = secret
    return c.writeConfigMap(existing)
}

```

This initialization happens transparently when `config.Load()` is invoked, ensuring every agentsview installation has a unique secret without manual intervention.

### Persistent Storage Location

The secret is stored as a base64-encoded string in the `cursor_secret` field of the TOML configuration file. The process ensures the data directory exists with `0o700` permissions before writing, protecting the secret from unauthorized access at the filesystem level.

## Cryptographic Implementation for Pagination

### Creating Signed Cursors in Request Handlers

When generating pagination tokens in [`internal/server/search.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/search.go) and the generated OpenAPI routes under `internal/server/huma_routes_*.go`, the server combines the last-seen record identifier with an HMAC-SHA256 signature using the loaded `cfg.CursorSecret`.

```go
func encodeCursor(lastID string, secret string) string {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(lastID))
    sig := mac.Sum(nil)
    // cursor format: <lastID>:<base64(sig)>
    return fmt.Sprintf("%s:%s", lastID, base64.RawURLEncoding.EncodeToString(sig))
}

```

This produces a stateless cursor string containing both the pagination offset and a cryptographic proof of authenticity.

### Validating Client-Supplied Cursors

Upon receiving a cursor from a client, agentsview validates the signature before processing the request. The validation logic splits the cursor into its components and uses `hmac.Equal` to prevent timing attacks.

```go
func validateCursor(cur string, secret string) (string, error) {
    parts := strings.SplitN(cur, ":", 2)
    if len(parts) != 2 {
        return "", fmt.Errorf("malformed cursor")
    }
    id, sigB64 := parts[0], parts[1]

    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(id))
    expected := mac.Sum(nil)

    sig, err := base64.RawURLEncoding.DecodeString(sigB64)
    if err != nil || !hmac.Equal(sig, expected) {
        return "", fmt.Errorf("invalid cursor")
    }
    return id, nil
}

```

If validation fails, the API returns an error immediately, preventing clients from traversing arbitrary offsets or accessing unauthorized data slices.

## Security Architecture Benefits

The cursor secret provides **tamper-evidence** for all cursor-based pagination. Because the HMAC signature binds the cursor data to the server-side secret, clients cannot forge cursors to jump to arbitrary positions or modify pagination parameters without detection.

This design enables a fully **stateless pagination architecture**. The server does not maintain cursor state in memory or databases; all necessary pagination context travels with the cursor token itself, signed by the secret stored in [`internal/config/config.go`](https://github.com/kenn-io/agentsview/blob/main/internal/config/config.go). The implementation in [`internal/server/cursor_dir.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/cursor_dir.go) relies on this guarantee for secure cursor handling across the filesystem layout for cursor-based agents.

## Summary

- **Cursor secrets** are 32-byte random values generated once via `ensureCursorSecret` in [`internal/config/config.go`](https://github.com/kenn-io/agentsview/blob/main/internal/config/config.go) and persist for the installation lifetime.
- The secret enables **HMAC-SHA256 signing** of pagination tokens, providing cryptographic proof that cursors originate from the server and have not been modified.
- **Stateless architecture**: Cursors carry all pagination state, eliminating server-side storage while maintaining security through signature validation in request handlers like [`internal/server/search.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/search.go).
- Invalid or forged cursors trigger immediate rejection with `400 Bad Request`, protecting list APIs against unauthorized data access.

## Frequently Asked Questions

### What happens if the cursor_secret is deleted from the configuration?

If the `cursor_secret` field is removed or corrupted, agentsview automatically generates a new secret on the next configuration load via `ensureCursorSecret`. However, existing pagination cursors held by clients will become invalid because their HMAC signatures will no longer validate against the new secret, effectively resetting all active pagination sessions.

### Is the cursor_secret exposed to API clients or users?

No. The cursor secret remains exclusively in the server's on-disk configuration and memory. It is never transmitted to clients, logged, or exposed through any API endpoint. Only the HMAC signatures derived from the secret travel to clients within cursor tokens.

### Why does agentsview use HMAC signing instead of encrypting the cursor data?

HMAC provides **integrity and authenticity** without requiring encryption. The cursor data (typically record IDs or offsets) does not need confidentiality—it needs tamper-proofing. HMAC-SHA256 is computationally efficient for verifying that the cursor was genuinely issued by this server instance and has not been altered, whereas encryption would add unnecessary overhead for non-sensitive pagination state.

### Where is the cursor validation logic implemented in the codebase?

Cursor validation occurs in the request handlers defined in [`internal/server/search.go`](https://github.com/kenn-io/agentsview/blob/main/internal/server/search.go) for search results and within the generated OpenAPI route handlers under `internal/server/huma_routes_*.go`. These handlers reference the `CursorSecret` from the loaded configuration to verify signatures on incoming pagination requests.