Get started with GenClass.
Add one line and an on-device model watches your app and reports the bugs that never throw, with the evidence. No account and no API key.
Install
GenClass works in any web app that runs in a browser: React, Next.js, Vue, Svelte, Angular, Solid or plain HTML. The package is ESM only.
npm install @genclass/runtime
Or let the CLI detect your stack, show the diff and write it for you:
npx @genclass/runtime init # observe mode
npx @genclass/runtime init --mode guard
npx @genclass/runtime remove # undo exactly what init added
No build step? Add one script tag first in <head>. Keep the version pinned: the unversioned URL may serve an older release from the CDN cache.
<script src="https://cdn.jsdelivr.net/npm/@genclass/runtime@0.2.1"
data-mode="observe" data-devtools="local"></script>
Starter apps for React + Vite, Next.js, Vue and SvelteKit, each with the one line and a racing mock API, are in templates/ on GitHub.
Initialize
The one line, first in your entry file. It starts the runtime in observe mode, discovers React, Redux and Zustand (devtools) state on its own, and loads the model at idle:
import "@genclass/runtime/auto"; // or "@genclass/runtime/auto/guard"
Prefer options in code? Call init() as early as possible, before your app makes requests:
import { GenClass } from "@genclass/runtime";
const rt = GenClass.init();
The model downloads at idle, is cached, and runs in a Web Worker on WebGPU or WASM.
The first five minutes
What the console shows, in order. The notice and the model-ready line are what the runtime prints; the stale-response report is real output copied from the package README (a typeahead in Node, q8 model on WASM, observe mode; the line format has not changed since 0.1.0-beta.0).
[GenClass] Sends anonymous diagnostics (decisions and counts, no page text) to improve the model. Opt out: GenClass.init({ telemetry: false }) or ?genclass=no-telemetry.
[GenClass] Model ready (genclass-runtime-r17, wasm, q8, 0.43s). Mode: observe.
[GenClass] Flagged a stale response: search.results was written twice by other operations since its operation (#6)
started (version 1 → 3), last 0.69s ago by GET /api/search?q=reac (#8), which started 0.09s after #6, from a later
user action (#7). (stale, 0.96)
- The notice appears on public hosts only, not on
localhost. - Then possibly nothing for a while, and that is normal. Observe mode reports only salient situations: a response landing over newer data, a repeat, a failure streak, a broken relation, an error. The first response of each kind after a page load is never a delivery decision, because the runtime first has to see what that request writes.
- Some flags will be wrong: expect 1 to 4 per 100 decisions where nothing was wrong, more in some apps (model 0.1.0; not re-measured for 0.2.0). Identical lines within a minute are folded into one with a count.
- Each flag opens a collapsed group with the evidence.
rt.explain(id)returns the exact situation text the model read. - On localhost, Alt+Shift+G opens the devtools overlay;
initloads it in development only.
Modes
| Mode | What it does |
|---|---|
observe | Default. Reports likely problems with the evidence and never holds, delays or changes anything. |
guard | Opt-in. Acts at the network boundary when the model's gate clears: discard, defer, coalesce, delay. At the shipped setting it acts on 7.5% of the real cases where acting would help (model 0.2.0, balanced). |
heal | Experimental. Adds retry, serve_cached, hedge, rollback, resync and custom actions. Its failure actions are its least precise. |
GenClass.init({ mode: "guard" });
Aggressiveness
Once GenClass can act, the profile sets how sure it must be first. balanced is the default. On held-out simulated apps (model 0.2.0) guard intervenes wrongly in 0.005% of cases at cautious, 0.13% at balanced and 0.54% at eager; heal in 0.26%, 0.59% and 1.84%. The targets are 0.1% and 0.5%, so pick cautious if a wrong intervention costs more than a missed one. On held-out real apps every profile measured 0.00%. The benchmarks put each recall next to its false-intervention rate.
GenClass.init({ mode: "guard", aggressiveness: "cautious" }); // "cautious" | "balanced" | "eager"
Where the reports go
By default a report is a console line in the end user's browser. In production that is not your console. The sinks option hands every detection, intervention, undo, breaker trip or summary to your own code as a structured, redacted record; the anonymous diagnostics (below) go to the GenClass maintainers. An optional free account also shows your app's detections on a dashboard at genclass.dev; runtime 0.2.2 and earlier do not send to it.
Keep your error monitor and session replay. They see exceptions, failed requests, slow spans and what the user saw, but they cannot tell that a response was stale or that a POST was a duplicate, because nothing threw. GenClass decides that on the device, and a sink hands each report to the tools you already use.
GenClass.init({
sinks: [
{ send: (r) => Sentry.addBreadcrumb({ category: "genclass", data: r }) },
{ send: (r) => posthog.capture("genclass", r), kinds: ["detection", "intervention"] },
{ send: (r) => navigator.sendBeacon("/genclass", JSON.stringify(r)), sampleRate: 0.2 },
],
requests: { ignore: ["/genclass"] }, // a sink that ships over fetch sends to an ignored endpoint
});
The generic sink above ships in 0.2.1. Forwarding from the genclass.dev collector to Sentry, PostHog, Datadog, Slack or a signed webhook, with no code in your app, is part of Pro. rt.audit() exports every decision and action as JSON. All sink options.
Explain and undo
Every detection and intervention is logged in plain English. rt.explain(id) returns the exact situation the model read, its facts and every answer. Discards and rollbacks return an ActionRecord with undo(), and the devtools overlay lists everything live. In guard mode the stale-response report above becomes:
[GenClass] Prevented a stale response: search.results was written twice by other operations since its operation (#6)
started (version 1 → 3), last 0.69s ago by GET /api/search?q=reac (#8), which started 0.09s after #6, from a later
user action (#7). Delivered the response to GET /api/search?q=rea (#6) and dropped the state changes it makes over
newer data (search.results). (stale, 0.97; discard 0.98)
Kill switch
Append ?genclass=off to any URL and nothing is installed. ?genclass=observe|guard|heal changes the mode for that page within the limits you set. A breaker downgrades the mode automatically after repeated undos.
What it costs
Measured on a production Next.js ordering site (mobile viewport, 4× CPU throttling, runtime 0.1.0-beta.3; the full table):
- JavaScript: about +98 KB on first load (gzip). No measurable change to the load event, LCP or long tasks.
- First-visit download: about 13 MB at idle on WASM (the 10.2 MB q8 model plus ONNX Runtime), 18 MB on WebGPU, then cached and checked with sha256. Later visits: about 1 KB.
- Memory: 190 to 280 MB in a Web Worker while the model is loaded.
- Device floor: none by default; the default
loadIfonly goes lazy on Save-Data connections. For phones and small devices:
GenClass.init({
model: {
loadIf: { minDeviceMemoryGB: 4, saveData: "skip" }, // or: (env) => !env.mobile && (env.deviceMemoryGB ?? 8) >= 4
preload: "lazy", // download on the first salient situation, not at idle on every page
unloadAfterIdleMs: 60_000, // free the worker's memory after a minute without a decision
},
});
model: false installs the runtime with no download and no worker, and then nothing is detected.
Self-host the model
Strict Content-Security-Policy or no third-party CDNs? Serve the model from your own origin (about 24 MB for the WASM build, 64 MB for every variant). Blocked and not self-hosted, GenClass observes without a model, prints one warning and detects nothing.
npx @genclass/runtime fetch-model public/genclass-model
npx @genclass/runtime init --model-url /genclass-model/
When to skip it
On a mostly static site there are no requests that write state, so there is nothing to decide and the model download is the only effect. GenClass also has little to report where every write is already idempotent on the server and reads are deduplicated in the client, as TanStack Query and SWR do.
On pages used mostly from 2 GB phones, set a device floor (above) or leave GenClass off those pages. If a strict Content-Security-Policy blocks cdn.jsdelivr.net and you cannot self-host the model, it detects nothing. If you need bugs fixed today rather than reported, read the benchmarks first: at the shipped setting guard acts on under 8% of the cases where acting would help (model 0.2.0, balanced), and six demo apps under chaos show the same bug rate with GenClass on or off.
Privacy and telemetry
Decisions are made on the device. From runtime 0.2.1, anonymous diagnostics are on by default on public hosts. From 0.2.2 they are off on local and private hosts (localhost, *.localhost, *.local, *.test, 127.0.0.1, ::1, private IP ranges); 0.2.1 sent from those hosts too, by mistake. They go to the GenClass maintainers' collector to improve the model. By default they contain no text from the page: a random per-page session id, versions, mode, device class, your hostname and id-normalised path, and per decision the trigger, diagnosis and confidence, action, gate, outcome, latency and counts. The situation text the model read is sent only if you opt in with telemetry: { include: { situation: true } }. Turn it off with GenClass.init({ telemetry: false }), npx @genclass/runtime init --no-telemetry, ?genclass=no-telemetry, localStorage genclass.telemetry=off, or Global Privacy Control. Apps that ship GenClass may need to disclose this to their users. See telemetry and privacy.
Dashboard and plans
The runtime needs no account. An optional genclass.dev account shows each app's detections, actions, routes and model load on a dashboard. Pages send to it with the app's token while diagnostics are on; runtime 0.2.2 and earlier do not send to it. The dashboard is free for 3 apps with 7 days of history. Pro ($99 a month) adds unlimited apps, 1 year of history, export of every event and forwarding to the tools you use; Custom ($599 a month) adds a model trained on your app. See pricing.
Visual bugs and the page check
Measured checks for bugs that render without an error are built and in testing: text the same colour as its background, images, fonts and stylesheets that failed to load, pages that scroll sideways on a phone, "undefined" or "[object Object]" on the page, and controls under an invisible layer. They are not in runtime 0.2.1. Measured in CI on October 13, 2026: 35 of 35 planted faults found on fixture pages, 0 false reports in 128 page views of unmodified apps, and on public home pages (bench run 4, 31 of 40 sites), 8 findings in 96 loads, 5 of them confirmed real and 3 unconfirmed. The page check runs the same checks on one public page, except click-blocked, which needs a visitor's clicks. Paste the address into the box on the homepage, or open genclass.dev/ followed by the address. Visual bugs says what they find, how they measure it and what they do not catch yet.
Reference
- API reference
- All options: routes, protected endpoints, breaker, shadow mode, veto, action limits, sinks
- Compatibility: 7 frameworks, 15 data layers, every mode
- Architecture, interception surface and telemetry
- Runtime guide
- How it works and benchmarks
- Found a false detection or a problem? GitHub issues are not enabled yet; write to mehar@daybot.ca with the
rt.explain(id)output. Security reports: security policy.