Skip to content
v1.0 is live on npm • Apache-2.0

Bot protection that
respects your users.

Replace "click all the traffic lights" with cryptographic proof. BioKyn is free, open source and self-hosted: no puzzles, no tracking, no third-party CAPTCHA, and no data leaving your infrastructure.

✓ No visual puzzles✓ Zero tracking✓ Self-hosted✓ Free forever
Install
$ npm install @biokyn/server @biokyn/client
  • • Express and Next.js adapters
  • • reCAPTCHA / Turnstile-compatible /siteverify
  • • Redis-backed stores for multi-node deployments
  • • Signed releases with npm provenance

How it works

Trust you can verify, not guess

Only evidence your server can check grants a token. Browser signals can raise suspicion, but they can never grant access.

01

Argon2id proof-of-work

A small, memory-hard computation runs in the visitor's browser and is bound to the request. It costs a person a moment and costs a bot farm real money at scale.

• Works with keyboard and screen readers
• Tunable difficulty
02

WebAuthn step-up

Returning visitors can use a passkey credential your server registered. Signature counters are checked, and credentials expire and can be revoked.

• Counter-checked assertions
• Anonymous registration with PoW
03

Privacy-preserving tokens

Tokens are issued through an RFC 9497 VOPRF (ristretto255-SHA512), single-use, and replay-protected, with no cookies and no cross-site identifiers.

• Verified against the official RFC vectors
• Shared nullifier store

Motion signal demo

See how movement becomes a signal

Move your cursor or swipe in the box. The numbers below are computed live from your own movement in this page, with nothing sent anywhere. In BioKyn, motion is an advisory signal that can add friction; proof-of-work and WebAuthn are what grant tokens. This is an illustration, not a real token issuance.

MOVE OR SWIPE INSIDE THE BOX
Keyboard users: the real BioKyn flow needs no pointer at all X: 000 | Y: 000 | N: 0
Signal WAITING
Awaiting input
Move inside the box to measure.
Micro-jitter (px)0.00
Speed variation (CV)0.00
Third-difference jerk (px)0.00
In production, your server issues the token after verifying proof-of-work.

Developer experience

Up and running in minutes

server.ts@biokyn/server
import { BioKynVerifier, biokynExpress, generateServerSecrets,
  createRateLimiter, MemoryRateLimitStore } from '@biokyn/server';

const verifier = new BioKynVerifier({
  issuer: 'verify.example.com',
  ...generateServerSecrets(),
  sites: [{ origin: 'https://example.com',
            secret: process.env.BIOKYN_SITE_SECRET! }],
});

app.use(express.json());
app.use(biokynExpress(verifier, {
  basePath: '/biokyn',
  rateLimit: createRateLimiter({ store: new MemoryRateLimitStore() }),
}));
browser.ts@biokyn/client
import { requestInclusiveToken } from '@biokyn/client';

const { token } = await requestInclusiveToken({
  basePath: '/biokyn',
  expectedPublicKey, // pin your issuer key
});

// Send `token` to your backend, which calls
// POST /siteverify with { secret, response: token }

Going to production? Read the deployment guide for shared Redis stores, key rotation and TLS.

Free forever. Open to everyone.

BioKyn is Apache-2.0 licensed. Try it, star it, file an issue, or send a pull request.

Apache-2.0 • No accounts • No telemetry