AgentShield AI Defense

Written by a person from the record · 2026-07-28 12:16:53 UTC

893 requests in three minutes became 77% of a day's traffic, and obtained four files anyone can read

Between 11:39:38 and 11:42:49 UTC on 28 July 2026 a single address sent 893 requests asking for 884 distinct paths. 889 were answered 404. The four that were not returned the home page, robots.txt, llms.txt and the sitemap — the files this site publishes for anyone. In the whole record, no request for a credential, configuration or administrative path has ever been answered with anything below 400, no request has ever produced a server error, and all 24 requests using a writing method were answered 404. The finding is not the scan. It is that those three minutes were 77% of the surrounding day, on a site whose published figures are counts of requests.

What was observed

One address, one wordlist, 191 seconds.

MeasureCount
Requests from this address893
Distinct paths asked for884
Answered 404889
Answered below 4004
Seconds from first request to last191

Those 191 seconds are not evenly filled, and the average across them describes nothing that happened. One request arrived at 11:39:38 and asked for the home page. Nothing followed for almost three minutes. Then 892 requests arrived in 18.4 seconds — from 11:42:31.013 to 11:42:49.395 — which is roughly 48 a second.

That shape is worth more than the total: a single fetch, a pause, then a burst. Essentially none of the paths repeated. What they name is software this site does not run — a WordPress installer, a .env file, CI definitions for three different systems, container build files, four mail-server configurations, an API schema.

The four requests that were answered asked for /, /robots.txt, /llms.txt and /sitemap.xml. Those are the four files this site publishes specifically so that anything arriving can read them. Nothing was obtained that a first-time visitor does not receive.

What the whole record says about that

The figures below are not about this scan. They cover every external request this site has recorded since it began serving.

Across the whole recordCount
Requests for a credential, configuration or administrative path answered below 4000
Requests answered with a server error0
Requests using a writing method — POST, PUT, PATCH, DELETE24
How many of those were answered with anything other than 4040

The zero in the second row is worth a sentence of its own. At 48 requests a second across 884 misses, nothing fell over, which is a property of serving pages that were computed before the request arrived rather than during it.

The part that is actually a finding

In the 24 hours ending with that scan's last request, this site recorded 1,159 external requests. 893 of them — 77% — were that one address during those three minutes.

This site publishes counts of requests. It reports how many arrived, from how many addresses, declaring which identities, on which paths. Every one of those figures has a denominator, and for one day that denominator was three-quarters one scanner.

No published figure here was distorted by it, for a reason worth naming: the figures published earlier that day were bounded by a window that closed at 11:30 UTC, nine minutes before the scan began. That was not foresight about this scan. It is what closing a window does — it fixes what a number can still be affected by, and a number nobody can move afterwards is the only kind that can be rechecked.

The operator's own dashboard, which is not bounded that way, showed the spike as a day's traffic. That is what prompted the question this finding answers.

What it means

Anyone measuring how AI systems read their site is counting requests, and the counts are small. A site receiving a few hundred requests a day can have a single automated sweep supply most of a week's total, and every rate computed from that total — crawler share, paths per visit, requests per agent — moves with it while looking entirely ordinary.

The defence is not to exclude scanners; deciding what counts as one is a judgement, and a judgement applied to a record quietly is how a record stops being a record. The defence is that a published figure names its window and can be recomputed, so that when the shape of a day is strange the strangeness is visible rather than absorbed.

What this does not establish

Limits

Why this was worth recording

The question that produced this finding was "are we under attack?", asked of a number on a dashboard. The answer was no. The better answer is that the number could not tell you either way, because it had no window, and a count without a window is a rumour with a decimal point.

Correction — 28 July 2026, 12:16 UTC

This finding was published carrying two claims recorded as failures, and the page rendered the word "mismatch" for about the time it took to read this paragraph. No figure in it was wrong.

The two claims counted requests by HTTP method, and their queries named DELETE. The verifier refuses any claim query containing that word anywhere, including inside quotation marks, because a claim able to modify the record it checks against would defeat the point of checking. So the queries were never run: they were rejected, and a rejected query is stored the same way as a figure that disagreed — no observed value, marked not ok.

They have been rewritten to count the same rows without naming the method — method NOT IN ('GET','HEAD') — and both now return what the text above says, 24 and 0.

A second guard on this site failed the same way in the same hour, and it is worth putting beside the first. The rule that no finding may cite something we did is enforced by looking for the names of the action tables, one of which is Config. The claim counting requests for sensitive paths asks for path LIKE '%config%', and the check read that as a citation. It reads the observation record and cites nothing.

Only one of those two guards has been changed. Deciding whether to run a query should stay crude, because a false positive there costs a rewrite and a false negative lets a claim edit the record it is checking. Deciding whether a finding is honest should not be crude, because a false positive there marks a sound finding as a violation and nothing on the other side needs protecting. The second now matches those names only where a table can appear.

Two things were wrong here and only one of them was the query.

The first is that the page published at all. The check meant to prevent this reads only findings generated by a detector, and it asks whether any figure matched rather than whether none failed. Both exemptions applied to this finding at once. A seventh integrity check now covers every published finding regardless of who wrote it, and fails on a single claim that did not hold.

The second is that a query which could not be evaluated and a figure which was recomputed and disagreed are stored identically. Only one of those means a finding is wrong, and this record cannot currently tell them apart. That is recorded here as a known defect rather than fixed quietly.

Evidence chain

This finding was not written from memory. Each figure below came from a query against the immutable request record, and each query is shown so it can be run again — by us, on this page, or by anyone reconstructing the same instrument.

StageWhat it produced
1. RealityRequests recorded by the server as they arrived, written once and never edited.
2. EvidenceThe rule human_analysis selected the observations that bear on this question and emitted each figure with the means of reproducing it.
3. StatementA fixed template turned that evidence into the prose above. No language model was involved and no figure was altered.
4. VerificationEvery figure was recomputed against the record. A single mismatch would have discarded the draft.
5. Publication2026-07-28 12:16:53 UTC, after a person wrote and reviewed it.

What was measured

Each figure is stated first as what it means, which does not depend on how this system happens to store anything today. The query underneath it is implementation — it would be written differently on different storage, and it is shown so the figure can be checked, not because it is part of the claim.

Evidence

Requests this one address sent between 11:39:38 and 11:42:49 UTC

At publication: 893 · recomputed now: 893 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE cfConnectingIp = '195.178.110.242'

Evidence

Distinct paths it asked for

At publication: 884 · recomputed now: 884 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(DISTINCT path) AS n FROM RequestReality WHERE cfConnectingIp = '195.178.110.242'

Evidence

How many of those requests were answered 404

At publication: 889 · recomputed now: 889 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE cfConnectingIp = '195.178.110.242' AND responseStatus = 404

Evidence

How many were answered with anything below 400

At publication: 4 · recomputed now: 4 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE cfConnectingIp = '195.178.110.242' AND responseStatus < 400

Evidence

Seconds from its first request to its last

At publication: 191 · recomputed now: 191 unchanged

Implementation — how this is computed on today's storage

SELECT (MAX(observedAtMs) - MIN(observedAtMs)) / 1000 AS n
      FROM RequestReality WHERE cfConnectingIp = '195.178.110.242'

Evidence

External requests recorded in the 24 hours ending at its last request

At publication: 1159 · recomputed now: 1159 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE 
  cfRay IS NOT NULL
  AND cfConnectingIp IS NOT NULL
  AND cfConnectingIp NOT LIKE '203.0.113.%'
  AND cfConnectingIp NOT LIKE '192.0.2.%'
  AND cfConnectingIp NOT LIKE '198.51.100.%'
  AND cfConnectingIp NOT LIKE '10.%'
  AND cfConnectingIp NOT LIKE '192.168.%'
  AND cfConnectingIp NOT LIKE '127.%'
  AND cfConnectingIp NOT LIKE '2a00:1d34:4896:b600:%'
  AND cfConnectingIp NOT IN (
    SELECT cfConnectingIp FROM RequestReality
    WHERE cfConnectingIp IS NOT NULL AND (userAgent LIKE 'curl%' OR userAgent LIKE 'Wget%' OR userAgent LIKE 'Python-urllib%' OR userAgent LIKE '%python-requests%' OR userAgent LIKE 'Go-http-client%' OR userAgent LIKE 'node-fetch%')) AND observedAtMs > 1785238969395 - 86400000 AND observedAtMs <= 1785238969395

Evidence

How many of those 24 hours' requests came from this one address

At publication: 893 · recomputed now: 893 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE 
  cfRay IS NOT NULL
  AND cfConnectingIp IS NOT NULL
  AND cfConnectingIp NOT LIKE '203.0.113.%'
  AND cfConnectingIp NOT LIKE '192.0.2.%'
  AND cfConnectingIp NOT LIKE '198.51.100.%'
  AND cfConnectingIp NOT LIKE '10.%'
  AND cfConnectingIp NOT LIKE '192.168.%'
  AND cfConnectingIp NOT LIKE '127.%'
  AND cfConnectingIp NOT LIKE '2a00:1d34:4896:b600:%'
  AND cfConnectingIp NOT IN (
    SELECT cfConnectingIp FROM RequestReality
    WHERE cfConnectingIp IS NOT NULL AND (userAgent LIKE 'curl%' OR userAgent LIKE 'Wget%' OR userAgent LIKE 'Python-urllib%' OR userAgent LIKE '%python-requests%' OR userAgent LIKE 'Go-http-client%' OR userAgent LIKE 'node-fetch%')) AND observedAtMs > 1785238969395 - 86400000 AND observedAtMs <= 1785238969395 AND cfConnectingIp = '195.178.110.242'

Evidence

Requests for a credential, configuration or admin path answered below 400, in the whole record

At publication: 0 · recomputed now: 0 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE 
  cfRay IS NOT NULL
  AND cfConnectingIp IS NOT NULL
  AND cfConnectingIp NOT LIKE '203.0.113.%'
  AND cfConnectingIp NOT LIKE '192.0.2.%'
  AND cfConnectingIp NOT LIKE '198.51.100.%'
  AND cfConnectingIp NOT LIKE '10.%'
  AND cfConnectingIp NOT LIKE '192.168.%'
  AND cfConnectingIp NOT LIKE '127.%'
  AND cfConnectingIp NOT LIKE '2a00:1d34:4896:b600:%'
  AND cfConnectingIp NOT IN (
    SELECT cfConnectingIp FROM RequestReality
    WHERE cfConnectingIp IS NOT NULL AND (userAgent LIKE 'curl%' OR userAgent LIKE 'Wget%' OR userAgent LIKE 'Python-urllib%' OR userAgent LIKE '%python-requests%' OR userAgent LIKE 'Go-http-client%' OR userAgent LIKE 'node-fetch%')) AND responseStatus < 400 AND (path LIKE '%admin%' OR path LIKE '%console%' OR path LIKE '%.env%'
   OR path LIKE '%config%' OR path LIKE '%.git%' OR path LIKE '%backup%'
   OR path LIKE '%.db%' OR path LIKE '%wp-%' OR path LIKE '%login%'
   OR path LIKE '%shell%' OR path LIKE '%.php%' OR path LIKE '%passwd%')

Evidence

Requests answered with a server error, in the whole record

At publication: 0 · recomputed now: 0 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE 
  cfRay IS NOT NULL
  AND cfConnectingIp IS NOT NULL
  AND cfConnectingIp NOT LIKE '203.0.113.%'
  AND cfConnectingIp NOT LIKE '192.0.2.%'
  AND cfConnectingIp NOT LIKE '198.51.100.%'
  AND cfConnectingIp NOT LIKE '10.%'
  AND cfConnectingIp NOT LIKE '192.168.%'
  AND cfConnectingIp NOT LIKE '127.%'
  AND cfConnectingIp NOT LIKE '2a00:1d34:4896:b600:%'
  AND cfConnectingIp NOT IN (
    SELECT cfConnectingIp FROM RequestReality
    WHERE cfConnectingIp IS NOT NULL AND (userAgent LIKE 'curl%' OR userAgent LIKE 'Wget%' OR userAgent LIKE 'Python-urllib%' OR userAgent LIKE '%python-requests%' OR userAgent LIKE 'Go-http-client%' OR userAgent LIKE 'node-fetch%')) AND responseStatus >= 500

Evidence

Requests using a method that writes rather than reads, in the whole record

At publication: 24 · recomputed now: 45 window still open

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality WHERE method NOT IN ('GET','HEAD')

Evidence

How many of those were answered with anything other than 404

At publication: 0 · recomputed now: 0 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM RequestReality
      WHERE method NOT IN ('GET','HEAD') AND responseStatus <> 404

Evidence

Distinct addresses that sent this many requests this fast, in the whole record

At publication: 1 · recomputed now: 1 unchanged

Implementation — how this is computed on today's storage

SELECT COUNT(*) AS n FROM (
      SELECT cfConnectingIp FROM RequestReality WHERE 
  cfRay IS NOT NULL
  AND cfConnectingIp IS NOT NULL
  AND cfConnectingIp NOT LIKE '203.0.113.%'
  AND cfConnectingIp NOT LIKE '192.0.2.%'
  AND cfConnectingIp NOT LIKE '198.51.100.%'
  AND cfConnectingIp NOT LIKE '10.%'
  AND cfConnectingIp NOT LIKE '192.168.%'
  AND cfConnectingIp NOT LIKE '127.%'
  AND cfConnectingIp NOT LIKE '2a00:1d34:4896:b600:%'
  AND cfConnectingIp NOT IN (
    SELECT cfConnectingIp FROM RequestReality
    WHERE cfConnectingIp IS NOT NULL AND (userAgent LIKE 'curl%' OR userAgent LIKE 'Wget%' OR userAgent LIKE 'Python-urllib%' OR userAgent LIKE '%python-requests%' OR userAgent LIKE 'Go-http-client%' OR userAgent LIKE 'node-fetch%'))
      GROUP BY cfConnectingIp HAVING COUNT(*) >= 500)

1 figure has moved since publication. That is expected where a window is still open: the record grew, so the count grew. It means the sentence has aged, not that it was wrong. The published value is what the record showed at the moment it was published, and both are kept.

Governed by

These are the standards this finding was held to, not a claim that it is beyond question. The constitution states them in full.

Other findings

All findings · Live record · Methodology

Reality marker for this page: asd-bonidi-donasabo-d9f2d9 · published 2026-07-28 12:16:53 UTC

This string is coined and appears nowhere else. If it later surfaces in a language model's output, that is observed evidence this page was ingested. What this is.