Ranked play
Arena
A ban a new account can't shake.
- Nescio tells it
- A per-game player ID, and whether this person already plays here.
- It keeps
- That ID and its ban list. No name, no email.
Nescio ID confirms each player is a real, unique person and gives your game one stable, anonymous ID for them. Only a zero-knowledge proof leaves the phone: a unique nullifier and the facts your game asks for, like a minimum age. We turn it into a one-way pseudonym. No name, birthdate, document number or photo.
Self-serve, with mock passports. Live keys are switched on after a quick review.
Nescio is Latin for “I do not know.” That's how the system works: the fields above are never collected, so there is nothing identifying to leak.* Only if your game sets a minimum age of 18+; otherwise null. Age is optional, per game: none, 13, 16, 18 or 21.
Three steps. Only the first one involves the player, and it happens on their phone.
In a passport app, Self or ZKPassport, they hold their passport's chip to the phone. The app creates a zero-knowledge proof: math that confirms facts like “real and unique” without revealing the data behind them.
We verify it and derive a one-way pseudonym from its nullifier. We keep that pseudonym and a few claims: human, nationality, and the minimum age proven if your game asked for one. No name, birthdate, document number or face image is ever stored.
Your backend receives a stable subjectId, can look up claims such as verified_human, and gates whatever it wants on them.
Gate the stakes, not the sign-up. Ask for verification at ranked play, rewards or early access, so players who can't verify can still play everything else.
Bans attach to the subjectId, not the login. A new email, device or IP with the same passport maps back to the same subjectId, so a second account needs a different valid passport. A renewed or second passport can count as a new player.
No passport data or ID scans to store, secure or explain to a regulator. Nationality, and age when your game asks for it, arrive as simple claims.
Each game gets its own ID for the same person, so players can't be tracked from one studio to the next. That comes from how the IDs are derived, not from a policy.
You get one passport. Swap your email, device, IP or name and try to sneak a second account in. Then take the same person to a second game and see if anyone can link them.
Runs in your browser: no signup, no passport, nothing sent. About 60 seconds.
In this example run there were 5 attempts: 1 got in and 4 were blocked. A new email, a new device, a new IP and a new name all failed. One passport, one account.
Each one is an ordinary customer of the public API, built the way any studio would build it. They run in sandbox, so you can open one, verify with a mock passport and see exactly what the app gets back.
Ranked play
A ban a new account can't shake.
Fair queue
One place in line per real person, with no sign-up.
Age gate · 18+
An adults-only game that learns “18 or older” and nothing else.
All three are sandbox demos: use a mock passport, not a real one. Make one in the Self app or the ZKPassport app.
Your backend holds the API key and talks to a small REST API over plain HTTP, in any language. The key never reaches the browser.
subjectId.subjectId any time to read its claims.subjectId, and ban, gate or reward the identity, not the account.# 1 · start a verification (server-side key)
curl -X POST \
https://api.nescio.dev/v1/game/verify/sessions \
-H "authorization: Bearer $NESCIO_API_KEY"
# → { "sessionId": "…", "minAge": null, "client": { …QR… } }
# show `client` as a QR for the player; minAge is
# your game's setting: null, 13, 16, 18 or 21
# 2 · poll until verified
curl \
https://api.nescio.dev/v1/game/verify/sessions/$SESSION_ID \
-H "authorization: Bearer $NESCIO_API_KEY"
# → { "status": "verified", "subjectId": "…",
# "duplicate": false, "mode": "sandbox", … }
# same passport → same subjectId, in this game only.
# "duplicate": true = this passport already has a
# player in YOUR game. Never a cross-game signal.
# 3 · look up a player any time
curl \
https://api.nescio.dev/v1/game/players/$SUBJECT_ID \
-H "authorization: Bearer $NESCIO_API_KEY"
# → { "subjectId": "…", "claims": [ { "verified_human": 1, … } ] }
// gate ranked play on a verified, unique human
const r = await fetch(
`${API}/v1/game/verify/sessions/${sessionId}`,
{ headers: { authorization: `Bearer ${apiKey}` } },
);
const { status, subjectId } = await r.json();
if (status === "verified") {
if (bans.has(subjectId)) return deny("banned");
if (seats.has(subjectId)) return deny("already has an account");
seats.add(subjectId); // one passport, one seat
}
// subjectId is stable for this player, in THIS game only.
Your backend gets one small object per player. The dashboard shows verified players and blocked duplicate accounts in aggregate.
// GET /v1/game/players/a91f… (game min. age 18)
{
"subjectId": "a91f…",
"claims": [{
"method": "document_nfc",
"assurance_level": "substantial",
"verified_human": 1,
"age_proven": 18, // or null: no age set
"over_18": 1, // 1 or null, never 0
"issuing_country": "ESP", // nationality
"verified_at": "2026-09-27T18:04:11Z"
}]
}
Nescio ID is pre-production. The sandbox is free and self-serve; live keys are switched on after a quick review by our team.
Founding studios get a direct line to the people building it and a say in what we ship next. Write to hello@nescio.dev.