Isaac Chan · Site teardown a1d2.org Preservation mirror Oct 2026

234 KB, three pages, and no confirmation screen

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.

Files
12 4 html · 5 js · 1 css · 2 licences
Total size
234 KB, uncompressed
Domain age
5.5 weeks at capture
Compression
69% best measured case
Origin IPs found
0 Cloudflare in front
Cookies
0 one CF beacon instead
The one-line version

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.

01 · What is actually there

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:

a1d2.org 链接压缩器 在浏览器里把长链接压短、并优化二维码,不需要后端。基于 p2r3 的开源项目 ha.mr(MIT)。 打开 → Link compressor Compress long links and optimise QR codes entirely in the browser, no backend. Based on ha.mr by p2r3 (MIT). Open → 更多陆续放上来。 / More to come. 联系 — 有事找我,发邮件:[email protected]

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.

FileSizeRole
/index.html4.6 KBLanding page: title, one project, contact
/style.css8.8 KBShared stylesheet
/q/index.html6.4 KBThe compressor UI
/q/main.js13.4 KB · 340 linesGlue: hash parsing, redirect, UI updates
/q/compress.js129 KBUpstream compression algorithm (ha.mr)
/q/alphabets.js57 KBThree alphabets: 84 printable ASCII, 43 uppercase QR-safe, 3,953 emoji
/q/lean-qr/lean-qr.js7 KBQR encoder
/q/stats + stats.js2.8 KBA stats page that talks to an endpoint that returns 404

02 · The compressor, measured

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.

InputCharsPayloadCharsSaving
https://en.wikipedia.org/wiki/Photography#History49.mGaQe0L5lmOp6Z3iW;4&,SRP!1569%
https://ps.ps/?id=3320bisZU849/W#1145%
https://example.com/a/very/long/path?with=query&and=more56jOID0UNb]T5=!;R5'yCyaID,cuN??JMyJxKe2555%
https://www.instagram.com/p/Cx7Qz1PtYbK/?igsh=MTZ2bXJq…66~j)2FvjUXC=IOJ4@FDzYaT_pgJwbGG3-F).-rvv#DC4236%

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:

// The format carries no checksum, so decoding with the WRONG alphabet can produce // convincing-looking nonsense (a random payload once "decoded" to a plausible youtube URL). // Re-compressing the result is the integrity check: only the correct alphabet reproduces the // payload exactly, so anything else is rejected. — /q/main.js, lines 216–219

03 · The thing I got wrong, and the comment that proves it

My 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:

const target = decompress(payload, alphabet); // Redirect straight through - no confirmation screen. The protocol check is not // decoration: a payload is opaque data that anyone can craft, so refuse anything // that is not http(s) instead of letting it navigate to javascript: or data: here. const url = new URL(target); if (url.protocol !== "http:" && url.protocol !== "https:") { throw new Error(`Refusing to redirect to ${url.protocol}`); } window.location.replace(url.href); return; — /q/main.js, lines 317–326

I 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.

What this means practically. An 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:

A link is counted when its confirmation screen appears, so pressing Stop still counted. — /q/stats, live copy

There 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.

04 · Privacy: what it protects and what it leaks

Genuinely private by design

  • The payload lives after #, so the destination never reaches the server — not in access logs, not in the CDN, not in a referrer
  • Compression happens in the browser; there is no API call to compress anything
  • No cookies, no localStorage of URLs, no third-party scripts except one beacon (below)
  • noindex, nofollow on all three pages, so the site is deliberately invisible

Where it leaks anyway

  • Cloudflare Web Analytics is injected into all three pages (token 410474ed…21e3), so there is a third-party request per page load
  • The beacon posts to /cdn-cgi/rum; it is cookieless, but it still records the visit
  • Once you land on the destination, that site sees a referrer of https://a1d2.org/
  • Anyone watching the URL bar (screen share, shoulder, screenshot) sees the whole payload

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.

05 · Where it lives — or doesn't

a1d2.org A ──▶ 104.21.72.240 · 172.67.155.232 CLOUDFLARENET │ ├─ cf-ray …-HKG ──▶ the edge nearest me, not the site ├─ NS kehlani / ridge.ns.cloudflare.com ├─ RDAP ──▶ Squarespace Domains LLC · created 2026-09-12 · expires 2029 │ registrant redacted · DNSSEC off · no country anywhere ├─ CT logs ──▶ exactly 3 names: apex, www, wildcard └─ MX ──▶ none · TXT ──▶ none

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:

06 · The mirror

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 URLMirror pathChanged?
/index.htmlbyte-identical
/style.cssstyle.cssbyte-identical
/q/q/index.html3 refs: /q/x → ./x
/q/main.js, compress.js, alphabets.jsq/…byte-identical
/q/lean-qr/lean-qr.js, /q/stats.jsq/…byte-identical
/q/statsq/stats2 refs, same rule
/stats.json (404)404.htmlthe 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.

Works offline

  • Compress a URL to ASCII or emoji payload
  • Reverse/decode a payload you were sent
  • QR generation and the QR-path decode
  • Language toggle, both pages

Doesn't

  • Hit counting (/stats.json → 404)
  • The Cloudflare beacon (visible as a 404 for /cdn-cgi/rum)
  • Anything that needs the origin — there is nothing else

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.

07 · Verdict

Not malicious. No obfuscation, no 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.
Also not trustworthy, in the sense that matters. The operator is anonymous by construction (proxied, redacted, 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.