The kick message said "verification failed". No stage, no hint.
Sonar sits in front of a Minecraft server and makes you prove you are a real client: a teleport, a fall, two packet echoes, a boat ride. It all happens at protocol level, so there is no config flag to turn it off. I logged both directions until the sequence made sense.
1. The teleport. First packet puts you at y=3019. You confirm with your position, and the position matters. The second teleport arrives as y=39 with the relative flag set, so spawn is 3019 + 39 = 3058, not 39. Treat it as absolute and you confirm a spot inside the ground.
2. The platform. A section block update right before the fall gives the block id (8185) and its height (0.8125). It is a slab, so you land at spawnY - 4 + 0.8125. Miss the half block and you land inside it.
3. Gravity. Eight ticks of dy = (dy - 0.08) * 0.98, one position packet per tick, then the landing packet with onGround.
let dy = 0, y = spawnY;
for (let i = 0; i < 8; i++) {
dy = (dy - 0.08) * 0.98;
y += dy;
sendPos(y, false);
}
sendPos(spawnY - 4 + platformHeight, true);
4. Echo one: animation. The server swings your arm and waits. Swing back once. Twice gets you kicked.
5. Echo two: held slot. The first slot packet is invalid on purpose, then the real one follows. A vanilla client only confirms a valid slot that actually changed, so a bot that answers everything fails here without a clue.
6. The boat. Driven by keepalives: boat, out, minecart, out. No packet tells you it started. And while you are in the boat you send vehicle movement, not player movement.
The two that cost me days
I confirmed the teleport after sending my position. Logs looked perfect. But the server compares the confirm against where you were when it issued the teleport, and by then I had moved. One line of reordering fixed it.
I also forgot the tick end packet. Without it the movement packets never close and the whole fall is discarded - exact same symptom as a wrong position, which is why it ate an evening.
Code
About 600 lines of Node.js, zero dependencies. Framing is the only part worth showing:
function varint(n) {
const out = [];
while (true) {
const b = n & 0x7f;
n >>>= 7;
if (n === 0) { out.push(b); return Buffer.from(out); }
out.push(b | 0x80);
}
}
function frame(id, payload) {
const body = Buffer.concat([varint(id), payload]);
return Buffer.concat([varint(body.length), body]);
}
After that it is just a state machine over the packets you receive - no proxy in the middle, nothing that breaks on a server update.
Tested against Sonar 2.1.52 and 2.1.53, on 1.21.x and 26.1.x, on servers I had permission to test.
One caveat: this is a research tool. Running it on a server you do not own breaks that server's rules, and it will get your account banned. I wrote it to understand the verification, not to farm one.
Repo: github.com/SirYadav1/sonar-bypass
Site: yadav.codes












