Passive captcha combining motion attestation, navigator attestation, and SHA-256 balloon proof-of-work. Attestation scripts are compiled to encrypted bytecode and executed inside a polymorphic QuickJS WASM sandbox that automatically encrypts and signs responses.
The attestation code does not run directly in the browser.
It is compiled to QuickJS bytecode, encrypted with ChaCha20, and
delivered as a .vmbc bundle. The WASM binary is regenerated every
10 minutes with fresh keys, dead code, and renamed symbols β making
static analysis and replay attacks impractical.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Server (every 10 min) β
β β
β 1. node scripts/build.js β vm.wasm + manifest.json β
β 2. node scripts/compile.js β attestation.vmbc β
β (source: attestation expression β encrypted bundle) β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
POST /challenge β { challengeId, nonce, pow, vmbc }
β
ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β Browser (parallel) β
β β
β ββ WASM VM βββββββββββββββββββββββββββββββββββ β
β β vm_init() β β
β β vm_exec_bytecode(attestation.vmbc) β β
β β ChaCha20 decrypt β QuickJS eval β β
β β β collects motion + navigator signals β β
β β ChaCha20 encrypt + HMAC-SHA256 sign β β
β β β encrypted + signed response β β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β ββ Web Workers βββββββββββββββββββββββββββββββ β
β β sha256-balloon PoW mining β β
β ββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
POST /challenge/:id/verify β { vmResponse, nonce }
β
ββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β Server β
β β
β 1. Verify HMAC signature (KEY_SIGN from manifest) β
β 2. Decrypt attestation result (KEY_ENCRYPT) β
β 3. Verify PoW (balloon hash β₯ difficulty) β
β 4. Analyze motion data (behavioral biometrics) β
β 5. Validate navigator signals (24 categories) β
β 6. Compute weighted score β issue signed token β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The attestation source is a JavaScript expression compiled with
node scripts/compile.js. The VM evaluates it and returns the
result as the response payload:
JSON.stringify({
m: __motion_data__,
s: __navigator_signals__,
ts: __vm_ts(),
integrity: __vm_integrity(),
});This follows the same pattern as any QuickJS bytecode source:
'Hello from compiled bytecode!';The compile step converts JS β QuickJS bytecode β encrypted .vmbc:
ββββββββββββ¬βββββββββββββββ¬βββββββββββ¬βββββββββββββ
β 4B magic β 4B bc_len LE β 12B nonceβ ciphertext β
β "VMBC" β β β β
ββββββββββββ΄βββββββββββββββ΄βββββββββββ΄βββββββββββββ
The VM wraps the execution result in an encrypted signed response:
ββββββββββββ¬βββββββββββββββ¬ββββββββββββ¬βββββββββββββ¬βββββββββββ
β 4B magic β 4B total_len β 12B nonce β ciphertext β 32B HMAC β
β "VMRP" β LE β β β β
ββββββββββββ΄βββββββββββββββ΄ββββββββββββ΄βββββββββββββ΄βββββββββββ
The server holds manifest.json with the matching keys to verify
the HMAC and decrypt the ciphertext.
Every 10 minutes the server rebuilds the WASM binary. Each build:
- Generates fresh 32-byte ChaCha20/HMAC keys
- Injects 20-40 dead code functions with realistic bodies
- Renames all exported and internal symbols
- Shuffles key material across randomized decoy arrays
- Applies control flow flattening and opaque predicates
- Recompiles the attestation source against the new keys
This means every client receives a structurally unique binary. Reverse engineering one build does not help with the next.
| Package | Role | Usage |
|---|---|---|
motion-attestation |
Behavioral biometrics collection | dependency |
navigator-attestation |
Browser environment fingerprinting | dependency |
sha256-balloon |
Memory-hard proof-of-work | dependency |
quickjs-wasm |
Polymorphic WASM sandbox | reference |
npm install silent-challengeThe three attestation/PoW packages are runtime dependencies.
The quickjs-wasm package is a build-time reference for
compiling and serving the WASM sandbox.
import express from 'express';
import { silentMiddleware } from 'silent-challenge/server';
const app = express();
const silent = silentMiddleware({
secret: process.env.SILENT_SECRET,
pow: { difficulty: 10, spaceCost: 512 },
thresholds: { combined: 0.5, motion: 0.3, navigator: 0.3 },
debug: false,
});
app.use(express.json({ limit: '64kb' }));
silent.mountRoutes(app);
app.get('/protected', silent.requireToken, (req, res) => {
res.json({
message: 'Access granted',
score: req.silentPayload.score,
});
});
app.listen(3000);| Method | Path | Description |
|---|---|---|
| POST | /challenge |
Issue challenge + PoW |
| POST | /challenge/:challengeId/verify |
Submit solution |
silentMiddleware({
secret: string, // HMAC secret (auto-generated)
debug: boolean, // include full analysis detail
weights: {
motion: 0.4, // behavioral biometrics weight
navigator: 0.35, // environment attestation weight
pow: 0.25, // proof-of-work weight
},
pow: {
difficulty: 10, // leading zero bits required
spaceCost: 512, // balloon memory blocks (Γ 32B)
timeCost: 1, // balloon mixing rounds
},
thresholds: {
combined: 0.5, // minimum weighted score
motion: 0.3, // minimum motion score
navigator: 0.3, // minimum navigator score
},
});import { createClient } from 'silent-challenge/client';
const client = createClient({
baseUrl: '',
workerUrl: './worker.js',
onProgress: ({ hashes, hashRate }) => {
console.log(`${hashes} hashes @ ${hashRate} H/s`);
},
});
// Start passive collection
const collector = client.attach(document, window);
collector.bind(document.getElementById('submit'), 'submit');
// When ready (e.g. form submit)
const result = await client.verify();
// result.cleared, result.token, result.scoreThe client runs three tasks in parallel:
- Motion collection β mouse, clicks, keystrokes, scroll,
touch, sensors, event order (via
motion-attestation) - Navigator signals β 24 categories: automation markers,
headless detection, VM indicators, canvas/WebGL fingerprints,
prototype tampering (via
navigator-attestation) - PoW mining β multi-worker balloon hashing
(via
sha256-balloon)
The combined score is a weighted sum:
score = motion Γ 0.40 + navigator Γ 0.35 + pow Γ 0.25
PoW score is binary (1.0 if valid, 0.0 if not). Motion and navigator scores range from 0.0 to 1.0 based on anomaly penalties. A token is issued only when all three individual thresholds and the combined threshold are met.
npm start
# β http://localhost:3000Interactive demo with step indicators, live stats, and score visualization.
npm testsrc/
index.js Barrel exports
index.d.ts TypeScript definitions
challenge.js Challenge issuance + combined verification
server.js Express middleware (mountRoutes, requireToken)
client.js Browser orchestrator (collect + solve + submit)