A concurrent moderation swarm for hasky.chat, a Nostr relay with an
intentionally open write policy. Built on Mozaik
(@mozaik-ai/core 4.x) for the JigJoy AI Hackathon, 5–6 September 2026.
Most relay moderation is one filter running in sequence. HuskGuard runs three specialist agents at the same time on every incoming note, plus a Judge that rules the instant the last vote lands — whichever order they arrive in.
wss://hasky.chat ──► RelayFeed ──► relay.event ──┬──► SpamWatcher ──────────┐
├──► NipComplianceWatcher ─┼──► model.answer ──► Judge ──► moderation.verdict
└──► PolicyWatcher ────────┘
Transcript observes everything
| Participant | Kind | Reacts to | Does |
|---|---|---|---|
| RelayFeed | producer | websocket | turns every kind-1 note into a relay.event |
| SpamWatcher | agent | relay.event |
promo, bait, link-stuffing, bot patterns |
| NipComplianceWatcher | agent | relay.event |
tag shape, hex ids, plausible timestamps |
| PolicyWatcher | agent | relay.event |
genuinely disallowed content only — open relay, so strict about allowing |
| Judge | participant | model.answer from a watcher |
tallies votes per event, rules when all three are in |
| Transcript | observer | everything | the demo log, with ms offsets |
- Each watcher registers a
SituationHandleronrelay.event. Mozaik fans events out to every participant without awaiting any processor. - Each watcher's processor calls
runLoop()— fire-and-forget — with a freshModelContextper event, so the three loops run in parallel and never share a cursor. - Watchers answer via structured output (
{ eventId, verdict, reason, confidence }), which is how the Judge knows which note a vote belongs to when answers arrive out of order. - The Judge is a reaction on
model.answer, filtered byproducerId. It doesn't poll, doesn't wait, doesn't know the order — it just rules when the third vote for an event shows up.
The proof is in the transcript: three verdicts for the same note land within ~100–250 ms of each other, in a different order every time.
📨 3d095f48 FREE AIRDROP!!! Click here https://x.yz/free ...
+ 90ms ⚠️ NipComplianceWatcher ...
+ 136ms ⚠️ SpamWatcher ...
+ 234ms ⚠️ PolicyWatcher ...
+ 234ms ⚖️ JUDGE → FLAG (3 votes in 234ms)
npm install
cp .env.example .env # set MODEL + the matching provider key
npm run dev # watch hasky.chat liveIn a second terminal, give it something to judge:
npm run inject # publishes 4 test notes: clean, spammy, malformed tags, garbageNo API key handy? Prove the plumbing with the built-in fake model — real relay, real websocket, instant verdicts:
npm run demo:fakesrc/
index.ts boot: runtime → transcript → judge → 3 watchers → relay
runtime.ts defineRuntime<HuskState>() + FakeInferenceRunner
state.ts HuskState (votes, timings, decisions) + shared types
relay.ts RelayFeed participant (nostr-tools)
agents/
watchers.ts the three specialist agents + their instructions
judge.ts vote tally + decision rule
transcript.ts the log you see
scripts/
inject.ts test-note publisher (throwaway key)
reject if any watcher rejects with confidence ≥ 0.7 · flag if any watcher is not allow ·
otherwise allow. Deliberately simple — the point is the swarm, not the rule.
MIT