Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/infra/cap

Self-hosted Cap

Cap is the replacement candidate for mCaptcha. It keeps the same HiveGrid principle as mCaptcha: self-hosted, no Google/Cloudflare CAPTCHA dependency, and no third-party widget CDN. The app loads /assets/widget.js and WASM from the Cap instance itself.

Domain requirement

Cap needs a public HTTPS URL. The browser calls the Cap instance to generate and solve challenges, and the app server calls the same instance for siteverify. A raw private IP is not a good production setup because browser TLS, CORS, Cloudflare proxying, and asset loading all depend on a stable public origin.

Recommended hostnames:

  • Staging: https://captcha-staging.hivegrid.de on node1 (176.9.111.179)
  • Production: https://captcha.hivegrid.de on hivegrid-prod-worker

Staging on node1

node1 runs Cap as a plain docker-compose stack and exposes it through the existing Coolify Traefik proxy (coolify-proxy). The compose service also joins the external coolify network with the stable alias cap-staging, so Traefik can reach it without publishing Cap directly to the public interface.

  1. Create DNS captcha-staging.hivegrid.de -> 176.9.111.179, DNS-only until Traefik has issued the certificate.

  2. Copy docker-compose.node1.yml and .env.example to /opt/cap/ on node1.

  3. Create /opt/cap/.env from .env.example, set ADMIN_KEY, and set:

    CORS_ORIGIN=https://staging-app.hivegrid.de,https://staging-api.hivegrid.de,https://staging.hivegrid.de,https://staging-portal.hivegrid.de
    REDIS_PREFIX=
    

    The supplied stack owns its Valkey instance exclusively. Keep REDIS_PREFIX empty with Cap 3.1.5: its site-key creation path does not prefix HMSET, while the challenge path prefixes reads. A non-empty value therefore creates keys that are visible in the dashboard but always return 404 from /<sitekey>/challenge.

  4. Start Cap:

    docker compose -f docker-compose.node1.yml up -d
    
  5. Install the Traefik dynamic config on node1, then restart the proxy:

    cp traefik-staging.yaml /data/coolify/proxy/dynamic/cap-staging.yaml
    docker restart coolify-proxy
    
  6. Open https://captcha-staging.hivegrid.de, log in with ADMIN_KEY, and create site keys for login, forms, and booking. Keep each public sitekey and its matching site secret. Before rolling them out, prove that POST /<sitekey>/challenge returns 200 for every key and includes the requesting staging origin in Access-Control-Allow-Origin.

  7. Set the staging web app env:

    CAPTCHA_PROVIDER=cap
    CAP_INSTANCE_URL=https://captcha-staging.hivegrid.de
    CAP_SITEKEY_LOGIN=<login sitekey>
    CAP_SITE_SECRET_LOGIN=<login secret>
    CAP_SITEKEY_FORMS=<forms sitekey>
    CAP_SITE_SECRET_FORMS=<forms secret>
    CAP_SITEKEY_BOOKING=<booking sitekey>
    CAP_SITE_SECRET_BOOKING=<booking secret>
    CAPTCHA_FAIL_OPEN=true
    

Production on worker/services VM

Production should run as a Coolify compose service on hivegrid-prod-worker (49.13.24.190, private 10.0.0.5) with FQDN captcha.hivegrid.de.

  1. In Coolify, create a Docker Compose service from infra/cap/docker-compose.coolify.yml.

  2. Set SERVICE_FQDN_CAP_3000=captcha.hivegrid.de.

  3. Create the service .env from .env.example and set:

    CORS_ORIGIN=https://app.hivegrid.de,https://admin.hivegrid.de,https://api.hivegrid.de
    
  4. Deploy and verify https://captcha.hivegrid.de returns the Cap dashboard.

  5. Create the same three site keys (login, forms, booking).

  6. Set the production web env to the CAP_* values and redeploy web.

  7. Keep the old MCAPTCHA_* values available until the Cap rollout is verified. Rollback is CAPTCHA_PROVIDER=mcaptcha plus a web redeploy.

App integration contract

The web app reads:

CAPTCHA_PROVIDER=cap
CAP_INSTANCE_URL=https://...
CAP_SITEKEY_LOGIN=...
CAP_SITE_SECRET_LOGIN=...
CAP_SITEKEY_FORMS=...
CAP_SITE_SECRET_FORMS=...
CAP_SITEKEY_BOOKING=...
CAP_SITE_SECRET_BOOKING=...
CAPTCHA_FAIL_OPEN=true

When Cap is active, the UI renders:

<cap-widget data-cap-api-endpoint="https://<cap-host>/<sitekey>/"></cap-widget>

The widget injects cap-token into the enclosing form. Server-side verification uses:

POST https://<cap-host>/<sitekey>/siteverify
Content-Type: application/json

{ "secret": "<site secret>", "response": "<cap-token>" }

The expected valid response is { "success": true }.

Caddyfile.staging.snippet is kept as a fallback for a non-Coolify node1 setup. The active staging setup uses traefik-staging.yaml.

Cutover checklist

  • Staging Cap dashboard reachable over HTTPS.
  • /assets/widget.js and /assets/cap_wasm_bg.wasm return 200 from the Cap host.
  • Staging web env uses CAPTCHA_PROVIDER=cap.
  • Register/login captcha path verifies with a real browser.
  • Public form with captcha enabled submits successfully once and rejects token replay.
  • Production Cap deploy is healthy behind Cloudflare.
  • Production web deploy completed after CAP_* env is set.
  • mCaptcha rollback env remains available until Cap has run through one live deploy cycle.