Guide

Reveal

The parts of a page its reader never sees. Comments, hidden form fields, invisible characters and white text on white, each with its line.

What isn't the page showing you?

A read gives you the text. That's its job. It's also exactly how the clue gets lost. Read https://gsmg.io/theseedisplanted and you get one line, # GSMG Puzzle. That's it.

revealUrl fetches the page's own HTML and lists what a browser keeps out of sight.

ts
import { revealUrl } from "@agntn/web";

const { findings } = await revealUrl("https://gsmg.io/theseedisplanted");

for (const finding of findings) {
  console.log(finding.kind, finding.line, finding.value ?? finding.reason);
}

Same page from the terminal:

shell
web reveal https://gsmg.io/theseedisplanted
text
[reveal] https://gsmg.io/theseedisplanted
text/html; charset=utf-8 · 832 bytes
Found 1 comment, 1 hidden element, 2 form fields, 4 meta tags, 1 link

Comments
  line 12 in html.no-js: Nice to see you around! Good luck little bunny hunter ;)

Hidden elements
  line 23 div (display: none): (no text)

Form fields
  line 23 input password (type=password, inside hidden div at line 23)
  line 23 input (no name) (type=submit, inside hidden div at line 23)

Meta tags
  line 5 charset: utf-8
  line 7 robots: noindex, nofollow
  line 8 description: GSMG Puzzle
  line 9 viewport: width=device-width, initial-scale=1

Links
  line 6 icon https://gsmg.io/img/favicon.png

There it is. The first step of the GSMG puzzle, a comment and a password form behind display: none. Good luck, little bunny hunter.

What counts as hidden

KindWhat it lists
commentHTML comments, plus <?...?> and <![CDATA[...]]>, which browsers keep as comments too
hiddendisplay: none, visibility: hidden, zero opacity or font size, the hidden attribute, a closed <dialog>, <template>, text pushed off screen or clipped to nothing
fieldfields of type="hidden", and every field inside a hidden element
camouflagetext in its background's color
invisiblezero width and other invisible characters, by code point
attributedata-* and attributes HTML doesn't define
meta, linkmeta and link tags
script, styleinline code as an excerpt, linked files as URLs

Every finding carries its line in the source and the element around it. The table order is the answer's order, so the things nobody sees come first. Hidden text spelled in tag characters or variation selectors gets decoded. Sneaky. Not anymore.

Wayback captures come raw

Pass a web.archive.org/web/<timestamp>/<url> address and it reads the id_ form instead. That's the capture as archived. No toolbar, no Wayback scripts posing as the page's own. Relative links resolve against the archived page, not the archive.

The Wayback Machine is slow, and sometimes it answers 503 for a minute. A 503 gets two more tries. A timeout doesn't, it already took its minute.

Public pages only

localhost, 10.0.0.8, 169.254.169.254 and the rest of the private ranges get refused before a single request. Every redirect is checked too. So a model holding this tool can't aim it at your router or your cloud metadata. Your own code can, with allowPrivateNetwork: true.

Stylesheets count too

A .secret { display: none } rarely sits in the page. It lives in site.css. So the reveal reads the linked sheets too. Plus whatever they @import, cascaded the way a browser does it. Ten at most. Each one passes the same address check and byte limit as the page, and all of them get 30 seconds together. A sheet that 404s or comes back as HTML doesn't count, same as in a browser. A Wayback capture gets its sheets from the same capture, not from today's site.

Cascade layers count as well. Tailwind v4 tucks nearly all its CSS into @layer, .hidden and .sr-only included. Skip those blocks and the reveal goes as blind as the reader. So layer blocks, @layer a, b; statements and layer() imports rank the way a browser ranks them. Nested layers too, and !important still turns the order around.

And @media? A block every screen matches counts like the rules around it: screen, all, not print, or no query at all. print stays out, and so does anything about width. So Bootstrap's .d-md-none stays a mystery. It lives in (min-width: 768px), and the reveal has no window to measure.

Which ones made it? stylesheets in the result lists them, and web_reveal says how many it applied.

What it can't see

Nothing runs. No JavaScript. Selectors stay simple: a tag, an id, some classes. nav a:hover gets skipped, and so does any @media waiting for a window size, a printer or a reader's setting. Colors count only when the page or its sheets declare them. A page that builds itself in the browser needs a browser, and that's @agntn/browsers.

For agents

web_reveal takes a url, plus maxChars (20 000 by default) and timeoutSeconds. When the findings don't fit, the answer says what it left out by kind. Then the model knows to ask for more, right?