XEY LABS — MYTHIC / 001 ID/ EN/ 中文/ 日本語/ 한국어 ← BUILD LOG PROJECT REPO ↗ GITHUB ↗

Mythic ships public.

Today the lab's first project goes from repo to record: Mythic — adaptive bot mitigation, built as a captcha replacement for the era where captchas lose. AGPL-3.0, fully public, as of today.

Captchas are dead, and the death is measurable

Solving farms sell a thousand answers for less than lunch. Vision models read image puzzles like text. Stealth browsers walk past the fingerprint checks that were supposed to catch them. Every static puzzle shares one failure mode: one solver, discovered once, kills it forever. The economics favor the attacker at every step — so we stopped trying to build a better puzzle and started pricing the abuse instead.

The thesis: never trust the client

Mythic assumes the browser is hostile by default. Every byte that arrives from a client is adversarial input — so the client SDK is a sensor, never a judge. Decisions happen server-side, in a deterministic risk engine that outputs named, auditable reasons.

The pricing of abuse is layered: an adaptive proof-of-work costs a human about one second inside a web worker — and the same cost, per request, forever, for a bot farm. Risky traffic gets harder challenges. Critical actions can demand cryptographic proof. And the verdict travels as a short-lived, Ed25519-signed token your backend verifies locally — no callback needed.

The verdict is a token, not a callback

When the server decides, it signs the decision — risk score, difficulty, expiry — with Ed25519. Your origin verifies it locally with the public key from the JWKS endpoint. No per-request callback to mythicd, no shared secret to leak:

claims, err := xtoken.VerifyToken(pub, token)
if err != nil {
    // reject: malformed, bad signature, or expired
}
// claims.Decision and claims.Risk are yours to enforce;
// verification only proves mythicd signed these claims.

The HTTP surface is two endpoints. Challenge in, proof out:

$ curl -s mythicd:8080/v1/challenge \
    -d '{"site_key":"sk_live_..."}'
# → {"id":"ch_01J…","difficulty":18,…}

# solve the SHA-256 PoW, then:
$ curl -s mythicd:8080/v1/verify \
    -d '{"challenge_id":"ch_01J…","nonce":"48291033"}'
# → decision token (or a harder challenge)

We tried to break it first

Before launch, we ran the attacks ourselves. A ten-thousand-mutation storm over the token format caught a real acceptance bug: distinct strings decoding to the same signed bytes. A live chaos session found that one forged header defeated the per-IP rate limiter twenty times out of twenty. Both are fixed. Both are pinned by regression tests. A fuzzer now grinds the verification path in CI on every push. The bypass reports were the useful part — that's the culture we want this project to reward.

What it honestly can't do yet

v0 loses to a funded operator with a stealth browser and a native solver. We say so in the threat model, in writing. The answer is roadmap, not vibes: behavioral biometrics, cryptographic step-up, a learning engine. Public by default means the gaps are public too.

License

Everything — engine and SDK — is AGPL-3.0-or-later. Read it, clone it, break it. If you break it, tell us first, privately. Contributions are welcome under the CLA in the repo.

Project 001 is live. Project 002 is already queuing up.

— XEY LABS, SHIP DAY