Skip to main content

capjs-server

Python server-side implementation of the Cap.js proof-of-work CAPTCHA protocol. Framework-agnostic core with optional Django integration.

Cap.js is a privacy-friendly, cookie-free CAPTCHA that uses proof-of-work challenges instead of image recognition. This package provides the server side — challenge creation, solution verification, and token validation.

Installation

pip install capjs-server

# With Django integration
pip install capjs-server[django]

Quick Start

from capjs_server import CapServer

cap = CapServer(secret_key="your-secret-key")

# Create challenge → return as JSON to the Cap.js widget
challenge = cap.create_challenge()

# Verify solutions from the widget → return as JSON
result = cap.redeem(challenge["token"], solutions)

# Validate verification token in your form handler
if cap.validate(request.POST["cap-token"]):
    process_form()

Django Integration

# settings.py (all optional — sensible defaults)
CAP_SECRET_KEY = SECRET_KEY         # defaults to Django's SECRET_KEY
CAP_CHALLENGE_COUNT = 50            # sub-challenges per solve
CAP_CHALLENGE_DIFFICULTY = 4        # target prefix length (hex chars)

# urls.py
from capjs_server.django.views import CapChallengeView, CapRedeemView

urlpatterns = [
    path("cap/challenge", CapChallengeView.as_view()),
    path("cap/redeem", CapRedeemView.as_view()),
]

# views.py
from capjs_server.django import validate_cap_token

def contact(request):
    if not validate_cap_token(request):
        return HttpResponseForbidden()
    # process form...

Configuration

Parameter Default Description
secret_key (required) HMAC signing key. Keep stable across deploys.
challenge_count 50 Sub-challenges per solve
challenge_size 32 Salt length in hex chars
challenge_difficulty 4 Target prefix length in hex chars
challenge_expiry_ms 600000 Challenge token lifetime (10 min)
token_expiry_ms 300000 Verification token lifetime (5 min)
nonce_store MemoryNonceStore() Pluggable replay protection store

Testing

A brute-force solver is included for writing integration tests without a browser:

from capjs_server import CapServer
from capjs_server.testing import solve

cap = CapServer(secret_key="test", challenge_count=2, challenge_difficulty=1)
challenge = cap.create_challenge()
solutions = solve(challenge["token"], challenge["challenge"])
result = cap.redeem(challenge["token"], solutions)
assert result["success"]

How It Works

All state is encoded in HMAC-signed tokens — no server-side storage. This makes it safe for multi-instance deployments (Kubernetes, Cloud Run, serverless).

  1. Challenge: Server generates a random nonce and signs it with difficulty parameters into a token
  2. Solve: The Cap.js widget finds nonces whose SHA-256 hash starts with a PRNG-derived prefix
  3. Redeem: Server verifies the HMAC, checks solutions, and issues a signed verification token
  4. Validate: Server verifies the verification token's HMAC and expiry

Replay Protection

By default, each challenge can only be redeemed once per server process. This prevents attackers from replaying solved challenges to stockpile verification tokens.

The built-in MemoryNonceStore tracks used nonces in-memory with automatic expiry. For multi-process deployments (e.g. Kubernetes with multiple replicas), a challenge can be redeemed at most once per replica.

For strict single-use enforcement across replicas, provide a shared nonce store:

from capjs_server import CapServer, NonceStore

class RedisNonceStore:
    def __init__(self, redis_client):
        self.redis = redis_client

    def mark_used(self, nonce: str, ttl_seconds: float) -> bool:
        # SET NX returns True only if the key was newly created
        return self.redis.set(f"cap:nonce:{nonce}", 1, nx=True, ex=int(ttl_seconds) + 1)

cap = CapServer(secret_key="...", nonce_store=RedisNonceStore(redis_client))

Django:

# settings.py
CAP_NONCE_STORE = RedisNonceStore(redis_client)

License

Apache-2.0 — same as Cap.js.

Metadata

Release files for capjs-server 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for capjs-server 0.2.1
File Size Uploaded Provenance
capjs_server-0.2.1.tar.gz 44.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for capjs-server 0.2.1
File Interpreter ABI Platform
capjs_server-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 57.6 kB

Release files / capjs_server-0.2.1.tar.gz

Download URL capjs_server-0.2.1.tar.gz
Size 44.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2d8d907c79dcaf46f102b9c410bb63c034e9320417cdc2ab0b083d878ae13c02
BLAKE2b-256 checksum
How to use checksums
f2d53201f3838ead52ea73948eed524f254b0ba445736a43b2241084c88c4257
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.

Transparency log

Release files / capjs_server-0.2.1-py3-none-any.whl

Download URL capjs_server-0.2.1-py3-none-any.whl
Size 13.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0d2224210ea54a23a76c63e783b7d8016b5d37a9f4d34f35928f7c749972dc28
BLAKE2b-256 checksum
How to use checksums
b2498376559238300cac01949b25d74b22efdde2e381437e22a27fc39c67ab4e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.

Transparency log

Security for capjs-server 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for capjs_server-0.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page