Our own implementation of the ALTCHA code challenge ("enter the code from the image", with audio), so the open-source altcha widget works without ALTCHA Sentinel. See SPEC.md for the design.
pkg/codechallenge: the library. Mints a proof of work plus a code image and audio, verifies the typed code, issues a pass token. No HTTP, no global state; banjax links it in.pkg/behavior: features from the widget's human interaction signature and from the code step, for scoring clients later (SPEC.md section 4.6).cmd/demo: a small HTTP demo of the whole flow. Not a production component.web/: demo pages and the pinned [email protected] widget with its UI translations (dist/main/altcha.i18n.min.js, MIT), inlined into the page, no CDN.
docker compose up --buildOpen http://localhost:8080/protected (use localhost: the widget needs a secure context).
The widget solves the proof of work, then asks for the code. A right code sets the
eqc_pass cookie and reloads into the protected page; /reset drops the cookie.
The widget and the audio follow the browser language (voices: en, ru, ja, zh, pt; others
are read in English); /protected?lang=ru forces one.
Settings (env): EQC_SECRET (random per start if unset; never commit one),
EQC_CODE_LENGTH, EQC_TTL_SECONDS, EQC_PASS_TTL_SECONDS, EQC_POW_MAX_NUMBER,
EQC_LISTEN.
The demo records behaviour features (no IP, no User-Agent, nothing typed) to
data/features.jsonl, one line per challenge and per verify. Label a session:
EQC_LABEL=human docker compose up. The compose demo also shows visitors the features
computed from their clicks and typing, in a panel next to the challenge
(EQC_DEMO_SHOW_FEATURES=1); a real deployment must not, it tells bots what they look like. data/ is git-ignored; keep it that way. On Linux,
make data/ writable for the container's user (mkdir -p data && chmod 777 data).
Without Docker: go run ./cmd/demo (Go 1.25+; set EQC_FEATURES_FILE to record).
go test -race ./...