DEV Community

hao li
hao li

Posted on Originally published at github.com

Every visa slot checker goes silent when the portal fights back. I built one that doesn't.

Every visa slot checker goes silent when the portal fights back. I built one that doesn't.

US visa appointment slots (on usvisascheduling.com) appear at random and vanish in under two minutes. Slot-checker extensions exist — but every one I've seen has the same failure mode: the moment the portal fights back — a Cloudflare "I'm human" checkbox, a waiting room, a rate limit, an expired session — the extension goes quiet and leaves you staring at a spinner, wondering whether it's still checking at all.

visa-slot-guard is a Chrome extension (Manifest V3) written from scratch around one design principle: every portal condition maps to an explicit state, a concrete recovery action, and a plain-language message. No silent spinners.

The state machine is the product

The content script snapshots the portal tab and classifies the page into one of seven states:

READY_CALENDAR / CHALLENGE / QUEUE / RATE_LIMITED
/ SESSION_EXPIRED / LOGGED_OUT / UNKNOWN
Enter fullscreen mode Exit fullscreen mode

A session state machine (HEALTHY / DEGRADED / NEEDS_USER) then decides what to do — check, back off, re-login, or ask you — and logs every transition to a Session health feed in the popup. The toolbar badge shows state at a glance: ON / ~ / ! / II.

Concretely:

  • Cloudflare challenge? Polling stops and you get: "human-verification check — open the tab and click the checkbox, checks resume on their own." Solve it and the extension notices the tab state change and resumes. No popup interaction needed.
  • Rate limited? Per-failure-type exponential backoff with jitter — rate limits back off hardest, successes decay the delay. It never hammers.
  • Session expired? Premium's opt-in Quick Login refills the login form from credentials stored only on your device. Without stored credentials, it tells you to log in manually instead of pretending.
  • Auto-book mid-flow hits a checkbox? The whole booking aborts and tells you. It will never try to auto-solve a human verification.

Architecture

The lib is pure functions (snapshot in, conclusion out) — no chrome.* except storage/notify — so all 35 unit tests run under plain node:test with zero dependencies:

src/lib/          pure, unit-tested logic
  detector.js     page-state classifier over a DOM snapshot
  availability.js date extraction (data-attrs → embedded JSON → text scan,
                  multiple strategies, confidence reported)
  scheduler.js    exponential backoff + jitter
  session.js      HEALTHY/DEGRADED/NEEDS_USER state machine
  tiers.js        the ONLY file that knows Free vs Premium
  billing/        provider interface + Stripe stub
  notify.js       sound + desktop alerts
src/content/portal.js        thin DOM adapter, answers worker messages
src/background/service-worker.js  orchestration (alarms, tabs, badge)
src/popup/         settings UI, tier card, health log
Enter fullscreen mode Exit fullscreen mode

Two details I'm happy with:

  1. chrome.alarms for scheduling, not setInterval. Service workers die; alarms survive. All state lives in chrome.storage.local and is re-derived on every alarm fire.
  2. tiers.js is the only file that knows about Free vs Premium. The service worker re-authorizes settings server-side-style on every message — the popup can display but never grant. Adding a third tier means editing one file.

Billing without lying

Real money needs a server: Stripe secret keys must never live in a browser extension. So billing is a provider interface with a stub:

extension → your tiny server (/api/billing/*) → Stripe
Enter fullscreen mode Exit fullscreen mode

Until you wire it, every billing method throws BillingNotConfiguredError with setup instructions, and Free works fully. The README documents the integration point instead of faking it.

Same honesty policy for mobile push: it's roadmap item #1, not shipped, because push without a backend would be a lie.

What it honestly cannot do

This section is in the README and I'm repeating it here, because every slot-checker landing page I've seen lies about these:

  • Human-verification checkboxes cannot and should not be auto-solved. Anyone whose "auto-book" clicks through Cloudflare is either lying or about to get your account flagged.
  • Session expiry is the portal's decision. Re-login needs your credentials and sometimes your security answers.
  • Rate limits are the portal defending itself. Checking every 30 seconds gets you throttled; the extension backs off instead of fighting it.
  • No tool can guarantee you a slot. First-come-first-served, hundreds of people competing, gone in two minutes. The tool makes you faster and tells the truth about its own state. That's the whole pitch.

MIT licensed. If you've ever watched a slot checker spin silently for six hours, you know why the state machine matters more than the checker.

Repo: https://github.com/hahahahahahahahah6/visa-slot-guard

Top comments (0)