# Squat Watch: names agents asked for that did not exist

Coding agents sometimes ask to install a package that does not exist: a typo (`reqeusts`), a guess (`colourama`), or a name a model made up. Anyone can register such a name later and put code in it (slopsquatting). KarmaDue already answers `unverified_existence` ("cannot establish that it exists; ask your person") for those names. Squat Watch remembers them and keeps looking.

## What it does

1. **Records the name, nothing else.** When a KarmaDue check (MCP `check`, `check_install`, `check_before_acting`, the preflight API, the hook endpoint) is about an npm or PyPI name, the name is queued. Squat Watch keeps the ecosystem, the normalized name (PyPI names per PEP 503; npm names as written), a SHA-256 of it, first seen, last seen and a request count. **No caller identity, key, agent id, IP or task text is copied into Squat Watch.**
2. **Re-checks the registries every 10 minutes.** Queued names are looked up on registry.npmjs.org and pypi.org. A name the registry does not have is tracked. A name that exists and was registered before KarmaDue first saw it is dropped (it was a real package, not a squat), and is not looked up again for 30 days. Each run takes up to 300 queued names (looking up at most 120 of them on the registry; names that clearly existed before they were asked for are settled from download data without a lookup), 250 tracked names and 30 re-judged flags, within about a minute; the rest wait for the next run. The job runs only from KarmaDue's scheduler: requests to start it without the scheduler's secret are refused.
3. **Flags names registered after agents asked for them.** If a tracked name later appears on the registry with a registration time after the last time KarmaDue saw it missing, it becomes a squat candidate with the evidence `requested by agents while nonexistent since <date>; registered <date>`. If its registry document is too large to read (over 4 MB), the time KarmaDue saw it is used as the registration time, so it is still flagged. If the registry shows an older registration time than that (for example a PyPI project whose old files were deleted), the name is kept as an anomaly and never published. A queued name that KarmaDue never saw missing, but that the registry dates after the first request, is held for your person in the hooks with that evidence, but is not published in the feed or the public lookup, because registry dates can move later when old releases are deleted.
   - Verdict **require_human_approval** (ask your person) by default.
   - Verdict **deny** if the new package matches a known-bad pattern: an OpenSSF/OSV malicious-package (MAL-) report, an npm security placeholder, or (npm only) an install script that downloads or runs code (curl, wget, `node -e`, eval, base64, child_process, powershell and similar). Flags still at ask your person are re-checked on later runs, so a malicious-package report that arrives later turns them into deny.
   - This only ever makes an answer stricter. Squat Watch never relaxes a verdict.
4. **Suggests the likely intended package.** For a name that does not exist, KarmaDue suggests the real package you probably meant, chosen by edit distance (at most 2; at most 1 for names of 5 characters or fewer) plus popularity from the registries' own download data: npm's weekly download counts (api.npmjs.org) and PyPI's 30-day download counts (the top-pypi-packages dataset, built from PyPI's public BigQuery downloads table). It is a suggestion, not a check: check the suggested package before you use it. Suggestions are cached for 10 minutes per server instance, and only when every download count was read; nothing else (existence, flags, verdicts) is cached.

## Public feed (possible squat, unconfirmed)

Only names that were **registered after agents requested them** are published. Names that still do not exist are never published, because that list would be a shopping list for squatters.

- JSON: `https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/squat-watch/feed.json`
- RSS: `https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/squat-watch/feed.rss`
- One name: `https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/squat-watch/lookup?ecosystem=pypi&name=reqeusts` (answers only for flagged names)
- Resolver: `https://ogogoizwsfaduzehkshb.supabase.co/functions/v1/squat-watch/resolve?ecosystem=npm&name=expresss`

Every feed item is labeled **"possible squat, unconfirmed"**. A new package with a name agents once guessed is a reason to look, not proof of malice: the owner may have registered it in good faith, or to protect the name. `request_count` includes KarmaDue's own test checks. Read limit: 60 requests a minute per IP while KarmaDue's rate limiter is reachable (if it is not, reads are served without the limit).

## Where the stricter answer shows up today

- The hook endpoint and `karmadue-guard hook` ([hooks.md](https://karmadue.expo.app/docs/hooks.md)): a flagged name is held for your person (or blocked, for deny), with the evidence line and the likely intended package. If Squat Watch cannot be read in time, the hook says so; with fail-closed the action is held for your person.
- The MCP config check (`karmadue-guard mcp-config`).
- `/lookup` and the feeds above.
- The MCP `check` / `check_install` tools and the preflight API will carry the same overlay in their own answers in a later release; until then they answer `unverified_existence` for names that do not exist, exactly as before.

## Limits

- npm and PyPI only. Other ecosystems are not recorded. npm names with capital letters (which npm no longer accepts for new packages) are not watched.
- Registration times come from the registries: for npm, `time.created`, or the earliest version still published when that is later; for PyPI, the earliest file upload that still exists (a project with releases whose files were all deleted gets no date and is never published). A name registered, deleted and registered again is judged by what the registry shows.
- A clean flagged package can still be malicious; a check is not a safety guarantee and does not watch what code does when it runs.
- Squat Watch never registers or publishes packages, including protective or honeypot names.
