ALTCHA separates challenge issuance from verification. In production, it's important to distinguish who calls each endpoint.
[Browser] [UI Backend] [ALTCHA Server]
| | |
|--- GET /challenge ----------------------------------> | (1) Frontend → ALTCHA
|<-- challenge JSON ----------------------------------| |
| | |
| (PoW solving, browser CPU)| |
| | |
|--- POST /login ----------->| | (2) Frontend → UI Backend
| (form + altcha payload) | |
| |--- POST /verify -------->| (3) Backend → ALTCHA (server-to-server)
| |<-- 202/417 --------------|
|<-- login result ------------| |
The ALTCHA widget in the browser calls GET /challenge directly. This must be a frontend call since the widget solves PoW in the browser.
- Requires public Ingress exposure
- Requires CORS configuration (
CORS_ORIGINenv var)
When the user submits the form, the altcha payload is sent along with it. At this point, the solution has not been verified yet.
The UI backend calls POST /verify with altcha=<payload> server-side.
Why this should be a backend call, not a frontend call:
- Security — Exposing
/verifypublicly allows attackers to exhaust tokens directly - Reliability — Frontend verification results can be tampered with via DevTools. Only backend verification is trustworthy
- Network — Internal network calls avoid traversing the public internet
Call directly via ClusterIP Service from the UI backend:
http://altcha.<namespace>.svc.cluster.local:3000/verify
ALTCHA can be deployed in a shared services VPC or individually per environment.
Option 1: Per-environment deployment (recommended)
Deploy ALTCHA to each EKS cluster. No cross-VPC network dependencies and failures are isolated.
[dev VPC] [stg VPC] [prd VPC]
├─ EKS ├─ EKS ├─ EKS
│ ├─ UI App │ ├─ UI App │ ├─ UI App
│ ├─ ALTCHA │ ├─ ALTCHA │ ├─ ALTCHA
│ └─ Redis/Valkey │ └─ Redis/Valkey │ └─ Redis/Valkey
Option 2: Shared Services VPC
Access a shared ALTCHA service via VPC Peering or Transit Gateway.
[shared VPC]
├─ ALTCHA + Redis
└─ Internal ALB ←── VPC Peering ←── [UI backends in dev/stg/prd VPCs]
The ALTCHA widget uses Web Workers to solve PoW in the browser. Mobile browsers (Chrome, Safari) block blob URL Worker creation in insecure contexts (HTTP), so TLS (HTTPS) is mandatory.
localhostis treated as a secure context by browsers, so HTTP works locallyhttp://<IP>orhttp://<domain>is an insecure context where Workers are blocked- Ingress with TLS provides HTTPS access, resolving this issue
It is recommended to separate API and demo Ingress resources.
- API Ingress (
altcha.example.com) — Exposes/challengeendpoint to browsers./verifyshould only be called via internal cluster Service - Demo Ingress (
altcha-demo.example.com) — For demo page testing. Remove in production
Do not expose /verify or /health/* through the public Ingress. The UI backend should call these via the internal cluster Service address.
The dashboard runs from the same Docker image using the /dashboard command. Deploy it as a separate Deployment and expose it only via an internal Ingress.
[Browser (admin)] ──→ [Dashboard :9000] ──→ [PostgreSQL]
↑
[Browser (user)] ──→ [API Server :3000] ────────┘
- Dashboard and API server share the same PostgreSQL
- API server automatically records events when
POSTGRES_URLis set - Dashboard is accessible only from the internal network via
nginx-internalIngress class - Authenticated via Basic Auth or Keycloak (OIDC)
See the Dashboard documentation for details.
# Production
SECRET=<long-random-string>
CORS_ORIGIN=https://app.example.com,https://login.example.com
STORE=redis
REDIS_URL=redis://valkey-cluster.xxxxx.apne2.cache.amazonaws.com:6379
REDIS_CLUSTER=true
RATE_LIMIT=20