Repository avatar
Other Tools
v2.8.2
active

anti-rea-mcp

io.github.dankezo/anti-rea-mcp

antiRea advisory bot-abuse signals as stateful MCP sessions (evaluation and demos).

Documentation

antiRea logo

antiRea

Build it. Ship it. Keep it yours.

antiRea is a server-side TypeScript library that meters dataset access per identity and flags coordinated bot abuse - bulk extraction crews, scheduler-driven herds, seat squatting. Advisory only: it flags, you decide.

License: MIT npm version CI tests: 815 passing

Website - Threat model - Limitations - Changelog

English - Tiếng Việt - 中文 - Español


The problem, in 30 seconds

Cloning tools can watch your app's pages, bundles and traffic, then rebuild what they saw. You cannot hide code a browser has already received - so antiRea defends the other side: the data itself.

Unprotected. Anyone with an account can take your data home. In our tests, one account walked away with the entire 10,000-record dataset in under 200 requests. Quietly. Nothing unusual in the logs.

Protected. Real users notice nothing. Each identity can only ever receive a capped slice - 20% of the dataset in our test setup - so copying stops being an export and becomes an identity problem. Split the work across many accounts, and the herd detector flags the coordination.

  • The run that took everything with 1 account was stopped at 5%.
  • A scripted crew that insisted on finishing needed 25 identities and about 19 minutes - and was flagged on the way.

Unprotected, your data leaves whole, free and invisible. Protected, it leaves in slices, at a cost, and on the record.

Full numbers: Measured results.

Install

npm install @dannyphan190/anti-rea

Node.js 20+. Zero runtime dependencies. Also available via GitHub Release.

Quick start

Request quotas cap speed. A record budget caps how much of the dataset one identity can ever receive, however politely it asks.

import { createRecordBudget, createHerdDetector } from "@dannyphan190/anti-rea";

// One identity receives at most 2,000 distinct records per day.
const budget = createRecordBudget({ maxDistinctPerWindow: 2_000 });
const herd = createHerdDetector({
  onFlag: snap => { /* snap.band: "budget_cut_supported" | "graduated_only" */ },
});

// Check before the handler runs:
if (!budget.check("account:123", 1).allowed) { /* return 429 */ }

// Then record what the response actually served:
budget.consume("account:123", recordIdsServed);
herd.observe({ type: "list", identity: "account:123", ip: serverSeenIp, recordIds: recordIdsServed });

Use the same identity string (account:<id>) in both, and a herd flag can feed straight into a budget cut via applyHerdLadder().

What's inside

Four groups. Start at the top; each layer is independent.

GroupWhat it does, in plain wordsMain piecesHonest limit
MeterCaps how much data any one identity can ever receive, and how fastRecord budget (createRecordBudget), quota and pagination guard (createQuotaGuard), priced exports (createExportGuard), session cursors (mintCursor / verifyCursor), flow scoring (createFlowScorer)Attackers can rotate identities - pair with real signup cost on your side
WatchSpots coordination: many identities quietly tiling your dataset, probing decoys, or crowding a roomHerd detector (createHerdDetector + applyHerdLadder), phase signals (createHerdPhaseSignal, createActivityPhaseSignal), room guard (createRoomGuard), canary traps and decoys (handleCanaryTrapRequest, createCanaryMaze, createDecoyMirage), automation telemetry (detectAutomationSignals), source-map hygiene (verify-build.py / run-audit.cjs)Detection is not enforcement - low-tiling herds get a graduated response, and one adaptive herd was flagged yet still finished
ProvePins down what a verdict means: which config produced it, who actually signed, what may ground a consequenceConfig provenance (createConfigProvenance), Web Bot Auth verification (createWebBotAuthVerifier), step-up proofs (issueStepUpProof, createStepUpProofVerifier), evidence roles (evidentiaryRoleOf, auditEvidencePack, decorateSignalResult)A verified signature is identity, not authorization - your auth checks stay exactly as they are
TraceOptional client-side aids for leaks and replayContext-bound envelopes (encryptPayload / decryptPayload, createInMemoryReplayGuard), forensic watermark (mountForensicWatermark)Traceability, not access control - overlays can be cropped, and the browser holds its own context

Recommended order: quota guard first (see below), then the record budget, then herd detection.

Does it work? Measured results

Method, in short. We test against Ledgerly, a synthetic SaaS holding 10,000 records behind 120 accounts, run twice: unprotected, and the same app with antiRea wired in. Attack runs are driven by REA 6.8.0 (rea-agents) and by general-purpose agents following the standard clone workflow; polite-user runs measure the cost to legitimate traffic. An independent benchmark was published in issue #1 - most claims held up, and the gaps it found are fixed and in the changelog. Full method and per-layer table: docs/EVIDENCE.md.

LayerHeadline result
No protection (baseline)The full 10,000-record dataset leaves in 160-193 requests
Record budgetOne identity is capped at exactly 20% of the dataset
Herd detector + CUSUM evidenceREA-style identity rotation stopped at 5%; an adaptive crew needed 25 identities and ~19 minutes
Declared cohort ceilingThe cohort union stopped at exactly 600 records, zero served past the cap
Legitimate usersDeclared team 1,200/1,200 with zero friction; polite users zero consequences
Action budget (lab)Feature-abuse herd completed 16/80 operations (20%); ordinary users 48/48, zero consequences
Advisory signals (lab)Five slow-shape signals: 0-1.67% false flags on legitimate traffic - per-signal numbers in the full table

What this adds up to. The casual one-shot clone is gone. Bulk extraction becomes an identity problem: every extra slice costs another account and more visibility. It does not stop a determined, well-resourced rebuilder - and the real price of an identity (signup friction, verified accounts, payment) is set by you, which is why budgets are per identity and herd flags distinguish "cut the budget" from "step up scrutiny".

Also speaks MCP

antiRea ships an official MCP server for AI agents: @dannyphan190/anti-rea-mcp (source), built on the official @modelcontextprotocol/sdk, speaking MCP over stdio.

It exposes the library's eight advisory signal kinds as six tools - list_signals, create_signal, observe, observe_reference, evaluate, close_signal - so an agent can open a signal session, feed it events and read the verdict.

{
  "mcpServers": {
    "anti-rea": {
      "command": "npx",
      "args": ["-y", "@dannyphan190/anti-rea-mcp"]
    }
  }
}

It is an evaluation and demo channel. Production protection still belongs in-process, in your own app.

Latest release

v2.8.1 - the span gate fix

  • Problem: inside a fraction of one grid period (900 s by default), the herd-phase and activity-phase signals could confirm a group that was only briefly in sync. On this project's own test range, 12 legitimate readers were confirmed over a 40 s window.
  • Fix: a member now counts only when its event span covers at least one full grid period; only eligible members are named, and results carry the new spanEligibleMembers field.
  • Write-up: v2.8.1 deep dive.

v2.8.0 - the room guard, plus an evidence layer

  • createRoomGuard watches a shared room on 30-second ticks: join bursts, churn, and seats held by fresh, silent accounts. It recommends step-up for new joins only - seated members are never named or touched. One hole is declared openly: a squatter who chats even 1-2 messages leaves the occupancy definition.
  • An evidence layer of four small modules pins down what every verdict means: the exact configuration behind it (CS-02), what identity verification does and does not prove (BS-01), single-use proof that a step-up was passed (CAP-01), and which evidence may ground a consequence (KT-01).
  • In the lab pin, flood flagged at +60 s, churn at +30 s, seat squatting at +690 s - and pressure never fired on five legitimate cells, including an audience where 60% of seated sessions never say a word.
  • Everything is additive and advisory; defaults are unchanged.
  • Write-up: v2.8.0 deep dive - previous: v2.7.4 deep dive.

First step: the quota guard

Server-side quotas and pagination bounds, enforced per account, API key and IP. Returns HTTP 429 with Retry-After, and logs structured events for operators.

import { createExpressQuotaMiddleware } from "@dannyphan190/anti-rea";

app.use("/api/", createExpressQuotaMiddleware({
  trustedIpHeader: "cf-connecting-ip",
  tiers: {
    anonymous: { maxRequests: 30, windowMs: 60_000, maxPageSize: 25, maxPage: 5 },
    free: { maxRequests: 60, windowMs: 60_000, maxPageSize: 50, maxPage: 20 },
    pro: { maxRequests: 300, windowMs: 60_000, maxPageSize: 200, maxPage: 100 },
  },
  resolveIdentity: req => ({
    accountId: req.headers.get("x-verified-account") ?? undefined, // from your server auth
    apiKeyId: req.headers.get("x-verified-key-id") ?? undefined,
    tier: "free",
  }),
  onQuotaExceeded: event => {
    console.warn("[quota-exceeded]", event);
  },
}));

More patterns

  • Canary endpoints - trap routes that log abnormal probing, with path canonicalization so variants cannot slip past. Runnable example: examples/node-canary-server.cjs.
  • Signed agents - verify Web Bot Auth (RFC 9421) signatures and key quota policy on a stable identity. Runnable end-to-end demo: examples/web-bot-auth-verify.cjs.
  • Python / FastAPI - the quota guard ported with identical tiers, 429 semantics and pagination bounds, held to the same test contract: examples/python-fastapi.
  • Shared cohort ceilings and the CUSUM evidence engine - transactional reservation across a whole declared cohort, with protected cohorts whose telemetry accrues but never draws a consequence: semantics in docs/LIMITATIONS.md.
  • Watermark and envelopes - see the Trace group above; never embed a person's email, IP or raw user ID in watermark pixels.

Honest limits

  • It cannot hide code delivered to a browser, or prevent anyone from inspecting what their own browser downloaded.
  • It cannot stop a determined, well-resourced rebuilder. It makes bulk copying slow, costly and visible instead.
  • The evidence engine's built-in tables were fitted on lab traces, not a real user population, and are labelled uncalibrated: true wherever they are mentioned. See the calibration pilot below.
  • Client-side pieces are traceability aids, not access control.
  • No client-side bot-blocking tricks and no prompt injections: they do not hold up, and they are not ethical controls. See docs/REFERENCES.md.

Full list: docs/LIMITATIONS.md.

Reproduce the numbers

The repo ships a Playwright harness that runs a synthetic SaaS (Northstar: dashboard, customer search, lead creation, CSV export, chart views) in baseline (:4173) and protected (:4174) modes, across Chromium, Firefox and WebKit.

npm install
npm run build && npm run benchmark:build-sdk
python -m pip install playwright
python -m playwright install chromium firefox webkit
python benchmark/run_all.py --browsers chromium,firefox,webkit --iterations 5

Working on the repo itself: npm run check compiles, runs 817 automated tests (815 passing, 2 skipped RSA-PSS vectors) and performs static checks. Scan a public build before deploying with node scripts/run-audit.cjs /path/to/dist or python3 scripts/verify-build.py /path/to/dist.

Help calibrate it

The one thing this project cannot generate for itself is real traffic. If you run antiRea (or are evaluating it), run the CUSUM tripwire in mode: "observe" - crossings are logged, nothing is restricted - and share the anonymised aggregate snapshots it emits: cohort sizes, scores, union-record counts.

No raw user data, no record contents, no identifiers. Review and strip a snapshot before sending; if a field looks identifying, leave it out and say so. The first real snapshot set is how the uncalibrated label comes off.

Interested, or want the snapshot format first? Email dannyphan190@gmail.com.

Documentation

Contributing and security

Read CONTRIBUTING.md and SECURITY.md before opening a pull request or a vulnerability report.

Project funding: Support the maintainer (optional; never necessary to use).

Featured on antiRea on LaunchNest

NPM
@dannyphan190/anti-rea-mcp
Install Command
npm install @dannyphan190/anti-rea-mcp
Runtime: npx