Forensic Investigation  ·  Nostr Client Analysis

The Case of the
Vanishing Courtesan

Why a Nevada brothel courtesan disappeared from the Nostrich web client — and whether it was deliberate censorship
VERDICT: Not a glitch. Her pubkey is listed by hash in a 4,252-account blocklist that Nostrich's server pushes to every visitor's browser. Deliberate, centrally administered, unappealable.

BUT: She was blocked before the article she blames. The real driver is App Store adult-content compliance — not retaliation for criticism.
SUBJECT
Kitty Kawasaki · npub1v28hlku…spllcy0
628f7fdb88cddd14726ee4adcb9a1acdaa2b5fe27d14acf74fbf93defcea26cb
CLIENT
nostrich.org — “Nostrich”
operated by RHXO
REPORT DATE
7 November 2026
1,296 events examined

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.

1The verdict, up front

QuestionAnswer
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.
Short version

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.

2What her own record shows

She is a real, prolific account — better-travelled than most accusations suggest.

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.

The article in question

“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.”

The accusation note

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.

3The mechanism — how Nostrich actually hides her

I pulled her profile URL and drove a real headless Chromium against it, logging every WebSocket frame.

The decisive finding

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.

Nostrich rendering the block message for Kitty Kawasaki
Evidence A. /p/npub1v28hlku… as rendered by Nostrich: “This account isn’t available on Nostrich.” No name, no bio, no posts, no category.
Nostrich rendering the softer adult-content tier
Evidence B. Control: an adult account not on the list still renders name, bio and follower counts, with the softer “marked as adult content” notice.

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.

The code path (from the production JS bundles)

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

The reproducible proof

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
Kitty’s mark: 477144e927470a0c — present in the live list. Confirmed on three separate fetches.
The list grew from 4,251 → 4,252 accounts during the investigation (one added, none removed). She is a stable entry.

What kind of block is it?

Nostrich’s UI has three distinct hiding states, and they are not the same thing:

StateMessageTriggerRemedy
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 wordlistnone
blocked“This account isn’t available on Nostrich.”/api/blocks — the server listnone. 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.

4Why it isn’t her article — three independent proofs

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.

Signal

Adult-adjacent accounts are roughly 19× more likely to be on that list. That points at category, not at one essay.

5What the blocklist actually is

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:

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

Does the public “open source” repo match?

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:

Accountability gap

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.

6Is it “censorship”? Yes — deliberately, and by design

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:

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.

The motive: Apple

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:

DateStatement 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 real answer to “why”

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.

7The uncomfortable part: Kitty’s own blind spot

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.

1. Her accusation is misattributed

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.

2. She is not a neutral free-speech martyr

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

3. She deleted the evidence herself

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.

Balance

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.

8What I could not establish

Intellectual honesty about the limits of the evidence:

  1. The exact reason code for her entry. /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.
  2. The exact date she was added. No archive of /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.
  3. Whether a human reviewed her specifically or she was swept in by an automated rule (solicitation keywords, 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.
  4. Whether the block was ever communicated to her. I found no notice, no report, no DM in her public record — only a UI message that names no category and no cause. If there was a private notification, it isn’t on the network.
  5. Whether other clients share the list. I saw no evidence of that; Primal’s own adult purge was a separate, earlier event she also complained about.

9So what actually happened — the 90-second version

  1. Kitty Kawasaki, a Nevada brothel courtesan and self-taught developer, joins Nostr in September 2026 and adopts Nostrich as her main client — 1,199 posts from it.
  2. Nostrich wins App Store approval in late September after 20+ rejections, and on 2026-09-29 states the policy that will define the case: “Based on App Store policies, NSFW content should remain hidden in the iOS and Android apps.”
  3. Nostrich builds a remote hard-block system: a centrally maintained list of ~4,250 hashed pubkeys, pushed to every browser via /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.
  4. On 1 November she writes an article telling fans to visit nostrich.org/p/<her npub>.
  5. On 2–3 November she pushes hard into explicit commercial solicitation and a public fight, and publishes a spec for a universal shared block registry — a structural twin of the mechanism about to be used on her.
  6. At some point (bracketed October–November), her pubkey’s hash, 477144e927470a0c, enters the list. Her own timeline and profile silently vanish.
  7. On 7 November at 19:15 she posts her last Nostrich-client event. Around 19:36 she starts deleting her own articles. At 18:40 (before that) she had published “WoT = Deplatforming ?”; at 20:02 she publicly accuses Nostrich of deplatforming her for that article.
  8. Her content is entirely intact on 12+ relays. Every other major client still shows her. Only Nostrich’s curated window is closed.
Bottom line

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.

10Appendix — reproduction kit

Everything below reproduces on a clean machine.

A. Confirm the block

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

B. See it render

https://nostrich.org/p/npub1v28hlkugehw3gunwujkuhxs6ek4zkhlz0522ea60h7faal82ym9spllcy0

→ “This account isn’t available on Nostrich.” — and zero relay REQs containing her pubkey.

C. See it not render elsewhere

https://njump.me/npub1v28hlkugehw3gunwujkuhxs6ek4zkhlz0522ea60h7faal82ym9spllcy0

→ full profile, full timeline.

D. The mark recipe, from chunk 8834-ff617a0135c4b709.js

mark(pubkey) = sha256("nostrich:hard-block:v1:" + pubkey.toLowerCase()).slice(0,16)
blocked      = setOfMarksFromGETapiSlashBlocks.has(mark(pubkey))

E. Files produced during this investigation

FileContents
kitty_events.json1,296 verified events across 12 relays
blocks.json / blocks2.jsontwo 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.jsonevery WebSocket frame the client sent — none mention her
verify_all.py, diff.py, graph.pythe verification scripts

A closing note on Nostr

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.

Method note. Findings derive from: (1) decoding the subject’s npub and retrieving 1,296 signed events from twelve public relays, with 200 signatures re-verified against secp256k1; (2) static analysis of 140 production JavaScript bundles served by nostrich.org; (3) dynamic analysis via headless Chromium with full WebSocket frame capture on both the subject’s profile and a control account; (4) differential testing against known adult and general-population cohorts via NIP-50 search; (5) three independent snapshots of the /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.