a1d2.org is a five-week-old personal site with exactly one project on it: a forked link compressor
that packs a URL into the fragment after the #. The compression is not the interesting part.
The interesting part is the code comment that says the confirmation screen was removed — and the stats
page that still claims it exists.
The site is not the story — the redirect is. a1d2.org#PAYLOAD navigates instantly,
with no interstitial, to whatever URL the payload decodes to. Anyone can craft a payload. The only guard is
an http(s) allowlist. So a link like this is a blind jump, and the site's own copy still
describes a confirmation step that was deleted from the code.
Three HTML pages, a stylesheet, and five JavaScript files. The landing page is bilingual, Chinese
first (lang="zh-Hans" on the document, with an English block below), and it names exactly
one project plus one way to make contact:
That is the entire site. The one project is a fork of ha.mr (p2r3, MIT) — the files
keep the upstream structure (compress.js, alphabets.js, main.js,
lean-qr), and its own title calls it “link compressor (private)”. QR generation is
lean-qr (David Evans, MIT). There is no framework, no build step, no server-side rendering: 129 KB of
compression tables, 340 lines of glue, and a 7 KB QR encoder.
| File | Size | Role |
|---|---|---|
/index.html | 4.6 KB | Landing page: title, one project, contact |
/style.css | 8.8 KB | Shared stylesheet |
/q/index.html | 6.4 KB | The compressor UI |
/q/main.js | 13.4 KB · 340 lines | Glue: hash parsing, redirect, UI updates |
/q/compress.js | 129 KB | Upstream compression algorithm (ha.mr) |
/q/alphabets.js | 57 KB | Three alphabets: 84 printable ASCII, 43 uppercase QR-safe, 3,953 emoji |
/q/lean-qr/lean-qr.js | 7 KB | QR encoder |
/q/stats + stats.js | 2.8 KB | A stats page that talks to an endpoint that returns 404 |
I ran the mirror's copy of the code in a real browser and fed it four URLs. The payload travels in the fragment, so the domain is a fixed cost — compression only pays off on long links.
| Input | Chars | Payload | Chars | Saving |
|---|---|---|---|---|
https://en.wikipedia.org/wiki/Photography#History | 49 | .mGaQe0L5lmOp6Z3iW;4&,SRP! | 15 | 69% |
https://ps.ps/?id=33 | 20 | bisZU849/W# | 11 | 45% |
https://example.com/a/very/long/path?with=query&and=more | 56 | jOID0UNb]T5=!;R5'yCyaID,cuN??JMyJxKe | 25 | 55% |
https://www.instagram.com/p/Cx7Qz1PtYbK/?igsh=MTZ2bXJq… | 66 | ~j)2FvjUXC=IOJ4@FDzYaT_pgJwbGG3-F).-rvv#DC | 42 | 36% |
Three alphabets are in play. The payloads above use 84 printable ASCII
characters. A second, 43-character uppercase set is reserved for QR codes, which carry the payload in the
path (/q/PAYLOAD) instead of the fragment, which
is why /q/anything renders the compressor rather than a 404.
The third set is the party trick: 3,953 emoji, one per entry, switched on with a UI toggle rather
than a URL flag — so the mode never appears in a shared link. Decoding stays unambiguous because
textAlphabets() picks the emoji table only when the payload contains a character outside the
ASCII alphabet, and a byte-for-byte comparison of the two tables finds no overlap. It compresses hardest of
the three and needs a phone keyboard to type.
Round-trip is lossy in exactly one way: an empty path segment before a query is dropped —
https://ps.ps/?id=33 decodes to https://ps.ps?id=33. Semantically the same request
for virtually every server, but worth knowing if a signature ever depends on the slash.
The decoder earns its keep, though. Because the format carries no checksum, it re-compresses what it decoded and compares against the input before trusting it:
/q/main.js, lines 216–219My first read of this site said it showed a “Proceed to <url>?” screen and that nothing was fetched
until you clicked. That was wrong. It is true of upstream ha.mr, which I had read about
before reading this fork's main.js. The fork deletes the interstitial, and says so in a comment:
/q/main.js, lines 317–326I confirmed it behaviourally rather than trusting the comment: navigating to
/q/#<payload for example.com> landed on example.com, with the fragment gone and no
intermediate page. location.replace also means no history entry — the back button skips
the compressor entirely, so a redirected visitor cannot easily walk back to see what they clicked.
a1d2.org#… link is a blind jump. The fragment is never
sent to the server, so no filter, preview bot or link scanner can see where it goes — that is the privacy
feature and the attack surface at the same time. The http(s) allowlist stops
javascript: and data: payloads from running on the domain; it does not stop
the payload from pointing at a phishing page.
The /q/stats page is where the fork's paperwork fell behind its code. It still describes the
deleted step:
/q/stats, live copyThere is no confirmation screen, and main.js contains no fetch call at all —
the counter was removed from the client. The endpoints agree: GET /stats.json returns the
site's 404 page, and POST /a answers 405 to every method and content-type I tried. So the
stats page is an orphan: a UI for a feature whose client half and server half are both gone.
#, so the destination never reaches the server — not in access logs, not in the CDN, not in a referrernoindex, nofollow on all three pages, so the site is deliberately invisible410474ed…21e3), so there is a third-party request per page load/cdn-cgi/rum; it is cookieless, but it still records the visithttps://a1d2.org/Correcting myself again: I earlier called this site free of
third-party requests. That was wrong — the beacon is there, which is why the mirror throws a 404 for
/cdn-cgi/rum whenever one of its pages loads.
There is no origin IP to geolocate. Every address belongs to Cloudflare's anycast range, and the
real server could be in a Shanghai flat, a Frankfurt VPS, or a static build with no origin at all — all three
produce identical headers. The registrant fields are redacted to the registrar itself, and certificate
transparency shows no mail., dev. or bare-IP certificate that would leak a
direct-to-origin host.
Two consequences worth spelling out:
MX record, mail to
[email protected] falls back to the A record — Cloudflare's proxy, which does not accept SMTP.
And with no SPF/DMARC, the domain can also be spoofed in mail.lang="zh-Hans" and puts its Chinese block first, and the custom 404 page opens with
页面未找到 before “Page not found”. That is solid evidence of audience and language —
the one thing this site does disclose — but it is not evidence of where the author is sitting. A site can
be written in one language from anywhere on earth.Twelve files, 234 KB, no server required. This is the unusual kind of mirror that is fully functional offline: the compressor is client-side, so it compresses, decodes and renders QR codes from the local folder with the network unplugged.
| Origin URL | Mirror path | Changed? |
|---|---|---|
/ | index.html | byte-identical |
/style.css | style.css | byte-identical |
/q/ | q/index.html | 3 refs: /q/x → ./x |
/q/main.js, compress.js, alphabets.js | q/… | byte-identical |
/q/lean-qr/lean-qr.js, /q/stats.js | q/… | byte-identical |
/q/stats | q/stats | 2 refs, same rule |
/stats.json (404) | 404.html | the origin's own 404 page |
How to check a link without trusting it: open the mirror, paste the payload into the Reverse box — not the address bar — and it prints the destination without any navigation. That is the whole safety workflow this fork removed.
/stats.json → 404)/cdn-cgi/rum)Fidelity: MIRROR.json records URL → path → sha256 for all 12 files,
plus the exact three-line rewrite applied to q/index.html.
Served vs archived: the files on disk are byte-identical to the origin, but the
host does not serve them that way — Cloudflare injects one Web Analytics <script>
into every HTML page and rewrites mailto: links to /cdn-cgi/l/email-protection.
.html URLs also take a 308 to the extensionless path. The archive is faithful; the wire
copy carries the host's additions.
eval, no exfiltration, no credential form. The code is
careful in ways hobby projects rarely are: a protocol allowlist before navigating, a re-compression
integrity check because the format has no checksum, and everything computed client-side. Both dependencies
are MIT-licensed and correctly attributed.
noindex, dead contact address), the domain is five weeks old, and the one interaction
the site offers is an instant redirect to an arbitrary URL with no preview. Treat every
a1d2.org#… link as “somebody wants me to land somewhere”, decode it first, and only then decide.