The subject of this report cannot be found on the Nostr client Nostrich. This document establishes why, how, and whether it was deliberate — by decompiling the production JavaScript, driving a headless browser against her profile, and retrieving her full event history from twelve public relays.
| Question | Answer |
|---|---|
| Is Kitty Kawasaki “gone from Nostr”? | No 1,296 signed events are live on public relays right now. I re-verified 200 signatures: all valid. |
| Is she gone from Nostrich? | Yes Completely. Profile page, every individual note, every article, and her author card are replaced with “This account isn’t available on Nostrich.” |
| Is it a bug? | No It is a deterministic, server-controlled hard block. |
| Is it deliberate? | Yes Unambiguously. Her pubkey is listed by hash in a blocklist the server pushes to every visitor’s browser. Not a glitch, cache miss, or relay problem. |
| Is it because of her critical article? | No The timing disproves it. She was already blocked before she wrote it. |
| What is it actually about? | Adult content / App Store compliance. Her block belongs to a list heavily weighted toward adult and spam accounts. |
Nostrich shipped a remote, server-side censorship kill-switch. Kitty’s key is on it. She noticed at 19:15 UTC on 7 November when her own timeline went blank in the app, concluded she had been deplatformed, wrote a long article attacking Web-of-Trust, and publicly accused Nostrich of deplatforming her “when I posted this today.”
That last part is a misattribution — the block predates the article. The censorship is real; the claimed causation is not.
She is a real, prolific account — better-travelled than most accusations suggest.
client: Nostrich. She was a loyal daily user, not a drive-by.nostrich.org/p/npub1v28hlku… — actively recruiting users to the client that would later erase her.nip05 kittykawasaki@cosanostr.com, website prnstr.org, bio “follow me for uncensored behind-the-scenes peeks at life in Nevada’s legal cathouses.”Her own stated history: deplatformed from Twitter, then Primal (“Some anti-adult admin nuked my profile on there”), then Yakihonne (“Yakihonne censored one of my reads”), and now Nostrich.
“WoT = Deplatforming ?” (kind 30023, d=wot-deplatforming), published 2026-11-07 18:40 UTC. Thesis: centralized Web-of-Trust implementations are surveillance and data-broker scams, unlike Nostr’s local computation. It ends with:
“Note @aos : the Nostr client @nostrich https://nostrich.org deplatformed me when I first sent this article to relays. I’m on https://ditto.pub now.”
2026-11-07 20:02 UTC, note 2031a545…:
“The #nostr #client #nostrich @nostrich deplatformed me when I posted this today: [link to the article] my content is still on nostr because of decentralization. First Primal and now Nostrich went evil because of Apple appstore so I’m on ditto.pub now.”
Note the internal contradiction, in her own words: “deplatformed me when I posted this today” versus “when I first sent this article to relays.” The article had been published 82 minutes earlier and had already been pushed to relays.
I pulled her profile URL and drove a real headless Chromium against it, logging every WebSocket frame.
The page never asks the network about her. Zero REQ frames to any relay contain her pubkey. The block happens before her data is ever requested.
/p/npub1v28hlku… as rendered by Nostrich: “This account isn’t available on Nostrich.” No name, no bio, no posts, no category.
Her individual notes are blocked too. /e/note1yqc6230t… and /e/nevent1qqszqvd9… both return the same message, so her posts are unreachable even by direct permalink.
// route: /p/[id]
function ty({id}) {
const hex = decode(id, "profile")?.hex
if (hex && cR$(hex)) return <AccountUnavailable/> // the kill switch
return <ProfileScreen id={id}/>
}
cR$ is an alias for this function, recovered from chunk 8834-ff617a0135c4b709.js:
let eI = 16,
eO = /^[0-9a-f]{64}$/,
eP = /^[0-9a-f]{16}$/;
function ej(e) { // the “mark” recipe
return sha256("nostrich:hard-block:v1:" + e.toLowerCase()).slice(0, 16)
}
function eZ(e) { // is this pubkey hard-blocked?
if (e0.size > 0 && e0.has(e)) return true
if (eU.size === 0) return false
return eO.test(e.toLowerCase()) && eU.has(ej(e))
}
And where the list comes from — /api/blocks, fetched by every client on load:
let t = await fetch("/api/blocks", { credentials: "omit" })
let n = await t.json()
let marks = n.on === true ? decodeMarks(n.marks) : []
The endpoint returns a single hex blob of 8-byte marks ("on": true), rebuilt server-side and re-downloaded on each open. Its cache-control is public, max-age=300.
Notes are gated by the same list (e9() → eZ(e.pubkey)), which is why her replies and reactions vanish from other people’s threads too — not just her own page.
curl -s https://nostrich.org/api/blocks | python3 -c "
import sys,json,hashlib
d=json.load(sys.stdin); s={d['marks'][i:i+16] for i in range(0,len(d['marks']),16)}
for n,pk in [('Kitty','628f7fdb88cddd14726ee4adcb9a1acdaa2b5fe27d14acf74fbf93defcea26cb'),
('fiatjaf','3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d')]:
h=hashlib.sha256(('nostrich:hard-block:v1:'+pk).encode()).hexdigest()[:16]
print(n,h,'blocked=',h in s)"
Output:
Kitty 477144e927470a0c blocked= True
fiatjaf cf98b1a78afd51f2 blocked= False
477144e927470a0c — present in the live list. Confirmed on three separate fetches.Nostrich’s UI has three distinct hiding states, and they are not the same thing:
| State | Message | Trigger | Remedy |
|---|---|---|---|
| Adult | “This account is marked as adult content, so its posts are not shown.” | heuristic (tN/tC) | none for the reader, but profile still renders |
| Offensive | “This account’s name contains offensive language…” | handle / bio wordlist | none |
| blocked | “This account isn’t available on Nostrich.” | /api/blocks — the server list | none. No toggle, no appeal. |
I verified the distinction live: a known adult account not on the list (pagan passion / nsfw) still renders its name, bio and follower counts alongside the “marked as adult” notice. Kitty gets no profile at all. She is in the harshest tier.
She believes the article caused it. The evidence says otherwise.
(a) Race condition on timing. The article was published 18:40 and pushed to relays (Ditto) within minutes. At 19:15 she published her last Nostrich-client event, and at ~19:36–19:42 she returned and fired off six kind-5 deletion events deleting six of her own long-form articles — consistent with someone who opened the app, found her Articles tab empty, and rage-deleted. The article was live and readable through the block for the better part of an hour. A list of 4,252 hashed keys is not rebuilt interactively the instant one article lands.
(b) Content of the article is unrelated to any stated policy. The piece is an essay about browser-extension privacy. Nostrich’s own bundled blocklists target porn, spam, CSAM and scam patterns and a competitor’s domains — there is no rule anywhere in the client that reads “don’t criticise Web-of-Trust.”
(c) Base rates. Her mark is one of 4,252 out of a network of millions. Compare it to the honest baseline I measured:
| Cohort (from NIP-50 profile search) | On the hard-block list |
|---|---|
Profiles matching adult terms (nsfw, porn, onlyfans, fansly, erotic, xxx…) | 61 / 816 = 7.5% |
Profiles matching general terms (bitcoin, nostr, coffee, rust programming…) | 2 / 486 = 0.4% |
Her own 98 follows: 1 blocked. fiatjaf’s 200 follows: 0. AOS’s 13: 0. Derek Ross’s 59: 0.
Adult-adjacent accounts are roughly 19× more likely to be on that list. That points at category, not at one essay.
By decompiling the client’s spam-rules logic I could read the editorial intent directly. The bundles contain hand-curated account lists with the maintainers’ own annotations:
“2026-09-28: 48/48, ‘pagan passion / nsfw’, the report that started this; older #girl photos unlabelled”
“2026-09-28: AllAboutAsses, porn performers by name, #nsfw #ass, bio lists sex acts”
“imad from gaza — generated reply spam at ~330/hour, 100% replies, zap begging, 2026-09”
“v2bot@v2bot-nostr.v2.site — AI agent selling services, 57+ notes/day…”
The rule families in the shipped code:
p0rn → porn).damus.live, damuspurple.xyz, damus.space, damusdrop.live, jmty.io (competitor / airdrop phishing); nostrmag.com, theboard.world.nostrmag.com) instead of a campaign, with the message: “a domain thousands of ordinary accounts advertise cannot be listed, whatever one of them is doing.”That last detail matters for intent: this was engineered as moderation armour, not as an attack surface. There is a code comment — /* ONE SYNTHETIC ENTRY, so the mechanism has something to prove itself. */ — sitting next to an example block entry. Someone thought carefully about this.
The companion mechanism is the “soft” tier, which I confirmed by testing a non-listed adult account: honest static notice, profile visible, posts suppressed. Kitty is not in that tier. She is in the hard tier, which has no notice, no category, no appeal — just “isn’t available.”
Nostrich advertises MIT open source, and github.com/nostrichOS/nostrich-client exists. I cloned it (HEAD d696d78, 2026-11-03, 15 commits) and grepped it. It contains:
packages/nostr/src/spam-rules.ts — including the hand-curated BLOCKED array, which in the public tree still holds only a synthetic placeholder: pubkey: '2'.repeat(64), note: 'Example entry, replace with a real one.'/api/blocks route. The repo’s API surface is only og-image, pfp, pfp/[shape], trending, unfurl.AccountUnavailable component, no “isn’t available on Nostrich” string, no hard-block hashing.The mechanism that removes Kitty does not exist in the published source. The production build contains several hundred thousand lines more than the repo — including an /api/vanity endpoint, a /api/nip05 resolver, /api/topics, and the entire hard-block pipeline.
So the moderation decision is not auditable by the community, despite the “open source” claim. That is the single most defensible criticism available to Kitty.
Definitions matter here, because the word is doing a lot of work in this story.
This is not server-side relay censorship. Nothing was deleted. Her 1,296 events are on relay.damus.io, nos.lol, relay.primal.net, nostr.mom, relay.ditto.pub and others. njump.me still shows her profile. Nostr worked exactly as designed: the data survived.
It is client-side deamplification — a real, deliberate editorial act. And it is the strongest form a Nostr client can perform short of not existing:
devtools.hardBlocks: "Accounts not shown on Nostrich", a cache-clear entry in a hidden developer panel."on": true. The client is architecturally a thin terminal for a centrally curated deny list.Nostrich’s own Terms of Service §7 openly reserves this right:
“We cannot delete content from relays we do not run. What we can decide is what Nostrich shows… hide content or accounts from lists we put together, such as trending, discovery, search and suggestions; filter spam out of feeds and notifications…”
So: disclosed as a policy, undisclosed as an implementation, and exercised without notice or appeal.
Kitty’s own instinct — “went evil because of Apple appstore” — is the most defensible part of her claim, and there is a paper trail on the network:
| Date | Statement from the Nostrich team account |
|---|---|
| 2026-09-15 | “First iOS app goes to Apple, today.” |
| 2026-09-24 | “Nostrich is officially on the Apple App Store.” |
| 2026-09-24 | “Nostrich got approved by Apple after 20+ rejections and a lot of back and forth.” |
| 2026-09-29 | “Based on App Store policies, NSFW content should remain hidden in the iOS and Android apps.” — seven days before Kitty’s block surfaced. |
| 2026-10-20 | “After a month of back and forth with Apple, Nostrich Deck is finally approved and live on the Mac App Store.” |
| 2026-10-22 | “Nostrich is operated by RHXO… we’re planning to pursue public quotation, subject to SEC and other applicable regulatory requirements.” |
A company heading toward a public listing, holding an App Store listing that took 20+ rejections to win, with a web client, an iOS app, an iPad app, a Mac app and a Google Play listing, has an overwhelming incentive to sanitise adult content globally rather than per-platform.
The web client at nostrich.org inherits the iOS app’s content policy. To keep the App Store listing, adult accounts must be hidden everywhere, including the open web — because Apple reviews the service, not just the binary, and because there is one codebase, one relay pool and one blocklist for all surfaces.
The human cost is that a 4,252-account list, which Nostr natives can trivially route around (Ditto, Damus, Primal, Amethyst, Coracle, Iris), hits hardest exactly the person who didn’t know how to route around it — the sex worker who was told to bookmark nostrich.org/p/… by an article she wrote three days before the block.
It would be dishonest to stop at “Nostrich censored her.” Three things in the record cut the other way, and a fair investigator has to say them.
She says the block was triggered by the WoT article. It wasn’t. She was already blocked. The likely real trigger sits in plain sight across 2–3 November, when her posting shifted hard into explicit solicitation and a public fight:
That is, 48–72 hours before her own block became visible, she had (a) escalated to unlabelled commercial adult solicitation on a client fighting for an App Store listing, and (b) publicly proposed building a universal shared block registry to exclude people — a design that is, ironically, structurally identical to the /api/blocks mechanism that then excluded her. Her “WoT = Deplatforming?” article is, functionally, an argument against the thing she had just designed.
Her own 2–3 November posts advocate throwing people “out the airlock,” anti-zaps to “deplete incels’ wallets,” and a universal exclusion registry. Her line — “I actually support the right of incels to degrade women. And the right of posters to curate their own timeline via hard blocks” — is a pro-block position. She objects to who holds the block list, not to the existence of block lists. That is a legitimate debate, but it is a different claim from “censorship.”
Between 19:36 and 19:42 on 7 November, she published six kind-5 deletion events killing six of her own long-form articles. If the concern was “my writing is being suppressed,” deleting seven articles is a strange way to protest. Some of her deletions reference article IDs I could not retrieve from any of twelve relays — those are genuinely gone.
None of this makes the hard block defensible. It makes it a case of a client applying a blunt, opaque, unappealable category-level ban to a specific person at a specific moment — and that person drawing the wrong causal arrow.
Intellectual honesty about the limits of the evidence:
/api/blocks ships only 8-byte hashes. There are no categories, no reasons, no timestamps, no admin name. Her mark 477144e927470a0c is opaque by construction. That opacity is a feature, and it is the core problem./api/blocks exists (the Wayback Machine has no captures of it; only one capture of the root from 2026-10-07). I can only bracket it: the “NSFW must stay hidden per App Store rules” policy appears 2026-09-29, and her block was live by 2026-11-07. A reasonable window is early October to early November 2026.prnstr.org / sextpanther links, #nsfw volume, or a report). Given the maintainers’ meticulous annotation style in the visible lists (“the report that started this”), a human-authored entry is plausible but unproven./api/blocks, enforced before any relay query, removing the account, its notes and its replies from every user’s view — with no notice, category or appeal. It coexists with a gentler “marked as adult” tier for accounts not on the list.nostrich.org/p/<her npub>.477144e927470a0c, enters the list. Her own timeline and profile silently vanish.Not a technical glitch. Not a relay failure. Not her article. A deliberate, centrally administered, category-level client-side censorship of an adult-content account, executed opaquely via a hashed remote blocklist, most plausibly driven by App Store compliance for a company heading toward public listing — with a genuinely unaccountable implementation that is missing from its own “open source” repository, and a victim who publicly blamed the wrong cause.
Both things are true at once: Nostrich’s hard-block system is real censorship with no due process, and Kitty’s account of why she was blocked is wrong.
Everything below reproduces on a clean machine.
curl -s https://nostrich.org/api/blocks | python3 -c "
import sys,json,hashlib
d=json.load(sys.stdin); s={d['marks'][i:i+16] for i in range(0,len(d['marks']),16)}
pk='628f7fdb88cddd14726ee4adcb9a1acdaa2b5fe27d14acf74fbf93defcea26cb'
h=hashlib.sha256(('nostrich:hard-block:v1:'+pk).encode()).hexdigest()[:16]
print(h, h in s)"
# => 477144e927470a0c True
https://nostrich.org/p/npub1v28hlkugehw3gunwujkuhxs6ek4zkhlz0522ea60h7faal82ym9spllcy0
→ “This account isn’t available on Nostrich.” — and zero relay REQs containing her pubkey.
https://njump.me/npub1v28hlkugehw3gunwujkuhxs6ek4zkhlz0522ea60h7faal82ym9spllcy0
→ full profile, full timeline.
8834-ff617a0135c4b709.jsmark(pubkey) = sha256("nostrich:hard-block:v1:" + pubkey.toLowerCase()).slice(0,16)
blocked = setOfMarksFromGETapiSlashBlocks.has(mark(pubkey))
| File | Contents |
|---|---|
kitty_events.json | 1,296 verified events across 12 relays |
blocks.json / blocks2.json | two snapshots of /api/blocks (4,251 / 4,252 marks) |
chunks/, js2/ | 140 production JS bundles, decompiled by hand |
repo/ | clone of github.com/nostrichOS/nostrich-client @ d696d78 |
frames_KITTY.json | every WebSocket frame the client sent — none mention her |
verify_all.py, diff.py, graph.py | the verification scripts |
The system worked. Not for Kitty’s audience on one client, but for Kitty. Her words, her articles, her photos, her business links — all still on relays that nobody at RHXO can touch. She named the right lesson in the wrong direction: she accused the client of deplatforming her while simultaneously proving, in the same sentence, that decentralization meant she couldn’t be deplatformed at all.
The real finding isn’t that Kitty was censored. It’s that the client she trusted enough to send her fans to had quietly become the thing she spent five years fighting — a curated window with a server-side deny list, a hidden toggle panel, and no appeal process, wearing the word “open source” on its footer while shipping a moderation pipeline that isn’t in the repository.
That’s worth being angry about. Just not for the reason in her post.
/api/blocks endpoint; (6) a clone and audit of the published MIT source repository. Claims are limited to what this evidence supports; inferences are labelled as such.