A lightweight Dockerized ALTCHA challenge/verify service built with Bun + Express. It exposes simple endpoints to generate ALTCHA challenges and verify solutions, and includes an optional demo UI.
- Runtime: Bun
- Ports: 3000 (API), 8080 (optional demo)
- Upstream libs: altcha, altcha-lib
Use Docker Compose (recommended):
# Optionally create a .env file (see below) or set variables in your shell
Copy-Item .env.example .env -ErrorAction SilentlyContinue
# Start the stack
docker compose up --build# Unix/macOS (bash/zsh)
# Optionally create a .env file (see below) or set variables in your shell
[ -f .env ] || cp .env.example .env
# Start the stack
docker compose up --build- API base: http://localhost:3000
- Demo (if enabled): http://localhost:8080
To override the secret temporarily in PowerShell:
$env:SECRET = "your-very-long-random-key"; docker compose up --buildUnix/macOS:
SECRET="your-very-long-random-key" docker compose up --buildThe service reads the following environment variables:
- SECRET (required): HMAC key used to sign/verify challenges. Default is $ecret.key (don’t use this in production).
- PORT: API port, default 3000.
- EXPIREMINUTES: Challenge expiry in minutes, default 10.
- MAXRECORDS: Size of in‑memory single‑use token cache, default 1000.
- CORS_ORIGIN: Allowed CORS origin(s), default *. Use comma-separated values for multiple origins.
- ALGORITHM: ALTCHA v2 algorithm, default PBKDF2/SHA-256.
- MAXNUMBER: ALTCHA v2 proof-of-work cost (difficulty), default 5000.
- API_BASE_URL: Optional demo proxy target for /challenge and /verify. Defaults to http://127.0.0.1:$PORT.
- DEMO: When "true", starts a simple demo UI on port 8080.
You can provide variables via:
- .env file in the project root (Docker Compose reads it automatically)
- compose.yaml environment section
- Directly in your shell (e.g., $env:NAME in PowerShell)
Example .env:
SECRET=change-me-to-a-long-random-string
PORT=3000
EXPIREMINUTES=10
MAXRECORDS=1000
CORS_ORIGIN=*
ALGORITHM=PBKDF2/SHA-256
MAXNUMBER=5000
# API_BASE_URL=http://127.0.0.1:3000
DEMO=falseMAXNUMBERis now the preferred environment variable for ALTCHA v2 proof-of-work difficulty.- Existing setups using
COSTstill work for backward compatibility. - If both are set,
MAXNUMBERtakes precedence.
Recommended update for existing deployments:
# old
# COST=5000
# new
MAXNUMBER=5000-
GET /
- Returns 204 No Content. Liveness probe endpoint.
-
GET /challenge
- Returns a signed ALTCHA challenge JSON produced by altcha-lib.
- 200 OK with challenge payload.
-
GET /verify?altcha=
- Verifies the provided ALTCHA solution.
- 202 Accepted on success.
- 417 Expectation Failed on failure or when a token is reused (single-use enforced with an in-memory cache).
Notes:
- CORS is open (origin: *).
- Record reuse protection is best-effort and stored in-memory; scale-out or restarts will reset the cache. For production, pair with a shared store or upstream protections as needed.
Enable the built-in demo page by setting DEMO=true. It serves a minimal HTML form at http://localhost:8080 and proxies widget requests through the demo server at /challenge (and form verification to /verify via the same proxy target). This avoids hardcoded localhost API URLs and works better when running on remote hosts.
Add the widget to your form and point challengeurl at this service:
<script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha@main/dist/altcha.min.js" type="module"></script>
<form action="/your-submit" method="POST">
<input name="email" placeholder="Email" />
<altcha-widget challengeurl="http://localhost:3000/challenge"></altcha-widget>
<button>Submit</button>
<!-- On submit, include the `altcha` field value in your request body -->
<!-- Example server should call GET /verify?altcha=... and accept 202 as success -->
<!-- 417 means invalid or reused token -->
</form>From PowerShell, you can test verification manually:
# Assuming $payload contains the exact `altcha` value from the client
curl "http://localhost:3000/verify?altcha=$([uri]::EscapeDataString($payload))" -Method GET -UseBasicParsingOn Unix/macOS, you can test verification manually:
# Assuming $payload contains the exact `altcha` value from the client
curl -G \
--data-urlencode "altcha=$payload" \
http://localhost:3000/verify -iExpect 202 on success or 417 on failure/reuse.
You can run locally with Bun (requires Bun installed):
bun install
bun run build
bun start# Unix/macOS
bun install
bun run build
bun startOr for live reload during development:
bun run dev# Unix/macOS
bun run dev- Change SECRET to a strong, unique value. Never use the default.
- Consider terminating TLS in front of the container and restricting access to /verify if needed.
- For horizontal scaling, replace the in-memory token cache with a shared store.
- Pin image versions and consider multi-arch builds if deploying across architectures.
Licensed under the MIT License.
❤️ made with passion in Erlangen by Umami Creative GmbH