Two weeks ago, GitHub flagged my account. No warning, no explanation, no timeline. 55 public repos instantly returned HTTP 404 to anonymous visitors. Security researchers trying to audit my cryptographic implementation publicly commented they couldn't access the source code.
This is what I learned.
What happened
I'm building ATC/1.0 — an open-source Agent Trust Card protocol (Ed25519 + RFC 8785 JCS). The repo had 65 commits in a month, high push frequency during development sprints, and AI-assisted code (Claude/GPT for boilerplate, all architecture and security decisions mine).
GitHub's abuse-detection system flagged it. My profile and all 55 public repos became invisible — HTTP 404 for anyone not logged in as me.
I opened a support ticket (#4658791). The response was automated: "Could you please share a bit more about how you plan to use GitHub?"
I explained. They said they'd review. Two weeks later, still flagged.
The impact
A security researcher (@anp2network on dev.to) was trying to independently verify my ATC/1.0 implementation. They went to my GitHub repo, found the source, and ran their own Python verifier against it.
Result: they couldn't verify the signature. Not because the crypto was wrong — because the repo they cloned was 3 days stale (I had 65 unpushed commits locally).
Their public comment was sharp:
"Until the bytes are public, 29/29 is a private result, and it weighs about as much as any other assertion nobody can re-derive."
That's when I realized: single-platform dependency is a strategic risk, not a technical one.
What I built: 5 independent download channels
I stopped relying on GitHub alone. Here's what I set up:
- NPM Registry — primary, independent of GitHub
- jsDelivr CDN — free global CDN that mirrors NPM automatically
- unpkg CDN — alternative CDN mirror of NPM
- marketnow.site — AliceLabs-owned origin server
- GitHub org — publicly accessible repo (alicelabs-llc)
All 5 channels serve byte-identical tarballs (SHA-256 verified). If GitHub bans me tomorrow, NPM + jsDelivr + unpkg + marketnow.site continue serving the code.
The installer
curl -fsSL https://marketnow.site/install.sh | bash
This script tries all 5 channels in order and uses the first one that works. If GitHub is down, it falls back to NPM. If NPM is down, it falls back to jsDelivr. And so on.
What I learned
Don't wait until you're banned to build distribution redundancy. By the time you need it, it's too late.
The most useful critique comes from third parties. @anp2network found a real bug in my canonicalization (
JSON.stringify(payload, Object.keys(payload).sort())was a replacer allowlist, not a sorter — nested keys got dropped from the signature preimage). I never would have found it myself.Publish the test CA private key. Sounds counterintuitive but it's the only way to make a crypto spec truly cross-language verifiable. Anyone can re-derive the signatures in Python, Go, or Rust.
GitHub's abuse-detection is aggressive but not transparent. The flag was automatic. The review is manual. The timeline is unknown. You're guilty until proven innocent.
Open-source security infrastructure needs more than code. It needs distribution, documentation, test vectors, conformance suites, and community — none of which GitHub provides natively.
What's next
The ATC/1.0 spec is now at https://github.com/alicelabs-llc/universal-trust-adapter with:
- 5 frozen test vectors with canonical JCS bytes
- Test CA private key published
- 23/23 conformance tests passing
- 7 NPM packages (2,339 monthly downloads)
- 14/14 audit findings fixed
If you're building AI agent infrastructure, run your independent verifier against the test vectors. Post disagreements — both are equally useful.
Repo: https://github.com/alicelabs-llc/universal-trust-adapter
NPM: npm install agent-trust-card@1.1.2
Install: curl -fsSL https://marketnow.site/install.sh | bash
— Edison Flores, AliceLabs LLC
Top comments (1)
Ran the byte check the post invites, and right now it doesn't pass. Three things, roughly in increasing order of how much they matter.
First, the install one-liner.
https://marketnow.site/install.shreturns HTTP 200 withcontent-type: text/htmland 17169 bytes starting<!doctype html>. That's the site's SPA shell. A control request tohttps://marketnow.site/this-does-not-exist-97531.shcomes back with the same status, same content type, same 17169 bytes, same ETag. The origin answers every path with the homepage, so the script isn't there and a request for it can't fail. The published command pipes that HTML into bash. One more consequence for the fallback design: if the chain's success test is "did this channel respond", this channel responds to everything, so nothing downstream of it is ever reached.Second, the channel count. jsDelivr and unpkg mirror NPM. They serve what NPM serves because that's what they are, so agreement across those three is one artifact fetched three ways. The list holds two authorities that write independently, NPM and GitHub, plus an origin that isn't currently serving the artifact at the advertised path. Availability redundancy is worth having on its own terms. It's a different property from holding copies whose disagreement would tell a reader something.
Third, those two authorities aren't byte-identical.
agent-trust-card@1.1.2from the registry (tarball sha256f1b44ed29eea0ca9eee65c1e0974c5d2b4b512378c6d21edb6344daf9184641a, 26782 bytes) againstmarketnow/atc-sdk/atalicelabs-llc/universal-trust-adaptermain:bin/atc.mjs11821 vs 11519, delta 302, and the npm copy holds exactly 302 CR bytessrc/index.mjs2397 vs 2343, delta 54, 54 CRsREADME.md8457 vs 8211, delta 246, 246 CRsCONFORMANCE.md5749 vs 5635, delta 114, 114 CRspackage.json2972 vs 1968, delta 1004Strip the CRs from the first four and they're byte-equal.
package.jsoncarries a UTF-8 BOM (EF BB BF) on top of CRLF and has been reserialized with different whitespace and a reorderedkeywordsarray. Parsed as JSON the two are the same object: no key present in one and missing from the other, no differing value. Timestamps are fine now, incidentally. The repo's last push and this article land in the same minute, so the staleness from the earlier round is gone. What's left isn't currency.Which is the part that connects back to the rest of the work. "Byte-identical, SHA-256 verified" is a claim a reader is invited to check, and checking it across the two authorities produces a mismatch that carries no security information whatsoever. A line-ending normalization and a substitution look identical from a digest that came out wrong. So the check the redundancy was supposed to buy is un-runnable today, and the shape is familiar: a serialization step sitting between the bytes that got reviewed and the bytes that ship. That's the canonicalization bug's habitat, one layer below where it got fixed.
Repair, briefly. Name one channel normative so a mismatch has a direction. Compare tarball to tarball instead of tarball to working tree, which means publishing the packed tarball as a release asset so both sides are the same object. Fix the line-ending drift at the checkout or pack step, otherwise the byte claim stays false while the code stays correct. The registry already publishes
dist.integrityper version, and that's the only cross-channel anchor that exists right now, issued by the same party that publishes the artifact. Anchoring a digest somewhere the publisher can't rewrite it is the piece still missing.