Documentation
Everything you need to install, configure, integrate and secure your self-hosted GateCHA server — the open-source ALTCHA CAPTCHA management layer.
Introduction
GateCHA is a self-hosted, open-source (MIT) alternative to ALTCHA Sentinel. It wraps the ALTCHA proof-of-work CAPTCHA protocol with API-key management, multi-site support, replay protection, adaptive difficulty, per-key rate limiting, a privacy-first statistics dashboard, and a DIY Human Interaction Signature — all in a single Docker container.
- ALTCHA-compatible — works with the official ALTCHA widget; no proprietary client.
- Self-contained — one container, embedded SQLite (or MySQL), no external services.
- Privacy-first — no client IP is ever stored or logged; only aggregated, anonymous counters.
Installation
Docker Compose (recommended)
mkdir -p /opt/docker/GateCHA && cd /opt/docker/GateCHA
wget https://raw.githubusercontent.com/Upellift99/GateCHA/refs/heads/main/docker-compose.yml
docker compose up -d
Open http://localhost:8080 and log in. If you did not set GATECHA_ADMIN_PASSWORD, a random one is printed to the container logs on first boot.
Docker Run
docker run -d -p 8080:8080 \
-v gatecha_data:/app/data \
-e GATECHA_ADMIN_PASSWORD=your-password \
ghcr.io/upellift99/gatecha:latest
From source
# Prerequisites: Go 1.26+, Node.js 20+
git clone https://github.com/Upellift99/GateCHA.git
cd GateCHA
make build # SQLite only (default)
./gatecha
Configuration
GateCHA is configured entirely through environment variables. All values are optional; sensible defaults apply.
| Variable | Default | Description |
|---|---|---|
GATECHA_LISTEN_ADDR | :8080 | Listen address. |
GATECHA_DB_DRIVER | sqlite | sqlite (always available) or mysql (mysql build variant). |
GATECHA_DB_DSN | ./data/gatecha.db | SQLite file path, or MySQL connection string. |
GATECHA_SECRET_KEY | auto | JWT signing secret. Set it to keep sessions valid across restarts. |
GATECHA_ADMIN_USERNAME | admin | Admin username. |
GATECHA_ADMIN_PASSWORD | auto | Admin password (printed once to stderr if generated). |
GATECHA_LOG_LEVEL | info | debug / info / warn / error. |
GATECHA_CLEANUP_INTERVAL | 10 | Expired-challenge cleanup interval, in minutes. |
GATECHA_HIS_SAMPLE_RETENTION_DAYS | 30 | Retention for opted-in raw HIS calibration samples. |
GATECHA_CORS_ALLOW_ALL | false | Allow CORS from any origin (default: per-key domain check). |
GATECHA_TRUST_PROXY | false | Trust X-Forwarded-For / X-Real-IP. Set to true behind a reverse proxy (see note). |
GATECHA_ENABLE_HSTS | false | Send Strict-Transport-Security (HTTPS-only deployments). |
GATECHA_MAX_BODY_BYTES | 1048576 | Maximum accepted request body size, in bytes. |
GATECHA_RATE_LIMIT_ENABLED | true | Enable per-IP rate limiting. |
GATECHA_RATE_LIMIT_LOGIN | 5 | Admin login requests per minute, per IP. |
GATECHA_RATE_LIMIT_API | 60 | Public API requests per minute, per IP. |
GATECHA_TRUST_PROXY=true. Otherwise per-IP rate limiting keys off the proxy's IP — every visitor shares one bucket, which exhausts immediately and breaks the public challenge endpoint (and the login captcha). Only enable it behind a trusted proxy that sets the forwarding headers.
Widget integration
Add the official ALTCHA widget (v3) to your form and point its challenge attribute at your GateCHA challenge endpoint, authenticated with the site's API key.
<script async defer src="https://cdn.jsdelivr.net/npm/altcha/dist/altcha.min.js" type="module"></script>
<form action="/your-endpoint" method="POST">
<!-- your fields -->
<altcha-widget
challenge="https://your-gatecha-host/api/v1/challenge?apiKey=gk_your_key_id"
></altcha-widget>
<button type="submit">Submit</button>
</form>
Verify on your backend
The widget posts an altcha payload with your form. Forward it to GateCHA's verify endpoint and check ok before trusting the submission. The challenge is single-use — replays are rejected.
# Example: Python
import requests
altcha_payload = request.form.get('altcha')
resp = requests.post(
'https://your-gatecha-host/api/v1/verify?apiKey=gk_your_key_id',
json={'payload': altcha_payload},
)
if resp.json().get('ok'):
pass # valid submission
challenge attribute, not the v2 challengeurl. The exact integration snippet for each key is shown on its detail page in the dashboard.
API reference
Public API — authenticated with ?apiKey=gk_…
{ "payload": "…", "his_signals"?: {…} }.Admin API — JWT via Authorization: Bearer
Security levers
Each API key carries its own protection settings, all editable from the dashboard. PoW alone deters casual automation; combine these for layered defense.
Per-key difficulty
max_number sets the proof-of-work cost (higher = harder = slower for the client). Pick per site based on abuse pressure vs. UX. expire_seconds bounds challenge validity; algorithm selects SHA-256 (default) or SHA-512.
Domain restrictions
A key can be locked to one or more origins. List one domain per line (commas also work); requests whose Origin/Referer host matches any entry are allowed. Use *.example.com to allow every subdomain and the bare example.com. Leave the field empty to allow any origin.
Per-key rate limiting
rate_limit_per_min caps challenge + verify calls for a key across all clients (0 = unlimited). This is in addition to the global per-IP limiter (GATECHA_RATE_LIMIT_API).
Adaptive difficulty
When enabled, GateCHA raises the PoW cost above the key's base for any source (by IP) requesting challenges at an abusive rate — escalating an attacker without penalizing normal visitors. The configured difficulty stays the floor; it never blocks.
Human Interaction Signature (HIS)
An open-source take on the concept behind ALTCHA Sentinel's proprietary HIS. The client collects privacy-preserving aggregates of interaction behaviour (counts, pointer distance, durations, timing variance — never coordinates, timestamps or key contents) and the server scores the automation probability.
- Monitor mode (default) records and counts bot-suspected samples but never blocks.
- Sampling (opt-in per key) stores the raw aggregates so you can calibrate a threshold on real traffic, with a configurable retention window. The dashboard shows the score distribution against the suspect threshold.
Privacy & data
GateCHA is privacy-first by construction. It is designed so you can run a CAPTCHA without becoming a tracking vector.
- No IP is ever stored or logged. The client IP is used only in memory for rate limiting and adaptive difficulty.
- Country-only geolocation. The traffic-by-country panel resolves the IP to a country code at request time (via an embedded, offline database) and immediately discards the address — only the aggregated country counter is persisted.
- HIS stores aggregates only — counts, distances, durations and timing variance; never coordinates, timestamps or what was typed.
- Self-contained. No third-party CDN for fonts or scripts in the admin UI; nothing phones home.
Resources
- GateCHA on GitHub — source, issues, releases.
- GateCHA WordPress plugin — drop-in protection for WordPress forms.
- ALTCHA project — the underlying open CAPTCHA protocol and widget.