English · 繁體中文 · 📖 Documentation
A signed challenge the server issues, a client widget that solves it in the browser with native Web Crypto, and a token the enclosing form submits for the server to verify. No external service, no tracking. Three layers: a framework-agnostic PHP Core, framework integrations (Laravel first), and a JS client stack.
composer require cluion/turingphp artisan vendor:publish --tag=turing-config # then set TURING_SECRET| Package | Install | Use |
|---|---|---|
@cluion/turing-core |
pnpm add @cluion/turing-core |
Headless core (plain / any framework) |
@cluion/turing-element |
pnpm add @cluion/turing-element |
<turing-captcha> Web Component |
@cluion/turing-vue |
pnpm add @cluion/turing-vue |
<Turing> Vue 3 component |
@cluion/turing-react |
pnpm add @cluion/turing-react |
<Turing/> React component |
Plain HTML via CDN — pin an exact version and add Subresource Integrity:
<script
src="https://cdn.jsdelivr.net/npm/@cluion/turing-core@1.0.0/dist/turing.global.js"
integrity="sha384-Yr0+3DwmN0hpeaF/LmMvcDSdUptv057stCTNWChQQjcYU/L7JbZFBkbe2BEg6/1D"
crossorigin="anonymous"
defer></script><form method="post" action="/submit">
@csrf
<x-turing type="pow" />
<button type="submit">Send</button>
</form>$request->validate(['turing_token' => 'required|turing']);
// or: Turing::verifyRequest($request);The <x-turing/> component renders a CSP-safe container the client widget mounts
onto. The widget fetches the challenge from data-turing-url, solves the PoW
with native Web Crypto (no WASM), and injects a hidden turing_token for the
form to submit.
import '@cluion/turing-core'; // auto-mounts every [data-turing] containerA runnable plain-HTML page is in
examples/plain-html/index.html. Framework
guides: Laravel · Plain HTML
· Vue · React ·
Security.
Framework-agnostic PHP core under php/src/Core. Token =
base64url(payload).base64url(signature) with canonical (sorted-key) JSON.
HMAC-SHA256 default signing (Ed25519 opt-in). Challenge types: math, text,
pow (PBKDF2-SHA256 default, SHA-256 leading-zero-bit opt-in). Stateless by
default; single-use via a Store.
php/tests/vectors/ is the wire contract — the
authoritative wire-contract reference quotes
them verbatim (a docs build guard fails on drift). Language ports MUST reproduce
these fixtures exactly (token bytes, PoW counters, answer hashes). Binary payload
fields (e.g. keySignature) are always base64url of the raw bytes.
composer install
vendor/bin/phpunit # PHP: Core + Laravel suites (Laravel 10–13 in CI)
cd js && pnpm install
pnpm -r build && pnpm -r test # JS: 4 packages (build before test)
pnpm -r typecheck
cd ../docs && pnpm docs:build # docs site + vector drift guard
cd ../e2e && pnpm exec playwright test # browser round-trip (needs chromium)MIT.