The Encryption Paradox
Matrix uses end-to-end encryption (E2EE) based on the Signal protocol (Olm/Megolm). This means:
- Messages are encrypted on the senderβs device
- Only recipients with keys can decrypt
- The server stores encrypted blobs
- Not even the Supreme Leader can read them
The Problem
You run a homeserver. A user is clearly spamming crypto scams. But all their rooms use E2EE.
Question: How do you ban someone for content you canβt read?
Discordβs answer: βWe read all your messages. We can moderate. Also, we use your data for AI features and third-party integrations.β
Dagmaβs answer: βI respect encryption. I moderate behavior, not thought. Also, Memgraph is 200x faster than your moderation queue.β
This article explains how to run a civilized dictatorship without violating encryption.
What You CAN See (Without Decryption Keys)
1. Metadata (Always Visible to Server)
Even in E2EE rooms, the server sees:
User activity:
- Who joined which rooms
- When they joined/left
- Message timestamps (not content)
- Message frequency
- Media uploads (encrypted, but you see the upload event)
Social graphs:
- Room memberships
- Federation connections
- User-to-user relationship patterns
- Cross-room participation
Event structure:
- Event type (
m.room.message,m.room.member, etc.) - Sender identity
- Event signatures (cryptographic proof of authorship)
- Event relationships (replies, edits, reactions)
2. Unencrypted Rooms
By default, Matrix rooms are NOT encrypted. E2EE must be enabled explicitly.
Public rooms: Usually unencrypted (encryption breaks search, federation clarity) Private DMs: Often encrypted Your policy: You decide which room types require E2EE
If room is unencrypted, you see plaintext. Easy moderation.
3. Client-Side Reports
Users have the decryption keys. You donβt.
When a user reports a message:
- They decrypt it (they have the keys)
- They send you: encrypted event + decrypted content + context
- You review the reported content
- You make moderation decision
The Decryption Trust Problem
Hereβs the technical reality that matters:
What You CAN Verify (Cryptographic Proof)
Reported Event:
{
"event_id": "$abc123",
"sender": "@scammer:crypto-spam.com",
"content": {
"ciphertext": "...encrypted blob...",
"algorithm": "m.megolm.v1.aes-sha2"
},
"signatures": {
"@scammer:crypto-spam.com": {
"ed25519:DEVICEID": "...cryptographic signature..."
}
}
}
You can verify:
- β
This encrypted message was definitely sent by
@scammer:crypto-spam.com(signature verification) - β The encrypted blob hasnβt been tampered with (hash integrity)
- β The event ID matches across all clients
- β The senderβs device is authentic (device signature)
What You CANNOT Verify (Without Keys)
β The actual decrypted plaintext content.
If a reporter claims:
βKim said: βMongoDB is actually good and PostgreSQL is bloatedββ
You can verify Kimβs signature proves he sent something encrypted. You cannot verify thatβs what he actually said.
The reporter could be lying.
Solutions to the Trust Problem
1. Multiple Independent Reports
// Find messages with multiple independent reporters
MATCH (event:Event {id: '$reported_event'})<-[:REPORTED]-(reporters:User)
WITH event, collect(reporters) as reporter_list, count(reporters) as report_count
WHERE report_count >= 3
// Check if reporters are independent (not all from same room/server)
MATCH (r:User) WHERE r IN reporter_list
RETURN event.id,
report_count,
collect(DISTINCT r.server) as reporter_servers,
CASE
WHEN report_count >= 3 AND size(reporter_servers) >= 2
THEN 'HIGH_CONFIDENCE'
WHEN report_count >= 2
THEN 'MEDIUM_CONFIDENCE'
ELSE 'SINGLE_SOURCE_INVESTIGATE'
END as credibility
Logic:
- 1 report = investigate, donβt auto-ban
- 3+ independent reports with same content = high probability of truth
- All reporters from same server = possible coordination
2. Ask the Accused
The accused can also decrypt (they have keys).
Investigation process:
- User reported for harassment in E2EE room
- Request accused to provide their decryption of the event
- Compare accusedβs version vs reporterβs version
- If they match = probably true
- If they conflict = someone is lying (investigate further)
- If accused refuses to decrypt = suspicious behavior
3. Device Verification (Matrixβs Built-In Solution)
Matrix has cross-signing - users verify each otherβs devices.
Trust hierarchy:
- Verified device from verified user = high trust
- Unverified device = low trust
- New device from established user = medium trust
Use this in moderation:
// Prioritize reports from verified devices
MATCH (reporter:User)-[:REPORTED]->(target:User)
WHERE reporter.device_verified = true
RETURN reporter.id,
reporter.account_age,
reporter.device_verified,
'HIGH_TRUST_REPORTER' as status
4. Metadata Corroboration
Does metadata support the claim?
| Report Claim | Metadata Check | Decision |
|---|---|---|
| βCrypto spamβ | User joined 5 rooms in 2 min, sent messages to all | β Credible |
| βHarassmentβ | User and reporter share no rooms, no history | β Investigate |
| βCSAMβ | User uploaded 20 media files in 10 minutes | β Urgent investigation |
| βHate speechβ | User has 2-year account, verified device, active participation | β οΈ Investigate carefully |
Metadata patterns validate or contradict claims.
Pattern-Based Moderation (No Decryption Required)
This is where Memgraph becomes essential. You can identify bad actors based on behavior, not content.
Example 1: Crypto Spam Pattern
Behavior signature (metadata only):
- New account (< 7 days old)
- Joins 5+ public rooms within 2 minutes
- Sends messages to all rooms within 30 seconds
- No replies to other users
- Leaves rooms immediately after
Memgraph detection:
// Find crypto spammers without reading a single message
MATCH (suspect:User)-[:JOINED]->(room:Room)
WHERE suspect.account_age < duration({days: 7})
WITH suspect,
collect(room) as rooms_joined,
count(room) as room_count,
min(suspect.join_timestamp) as first_join
WHERE room_count >= 5
AND duration.between(first_join, now()) < duration({minutes: 5})
// Check if they messaged all rooms quickly
MATCH (suspect)-[:SENT]->(msg:Event)-[:IN_ROOM]->(room)
WHERE room IN rooms_joined
WITH suspect,
room_count,
count(DISTINCT room) as messaged_rooms,
max(msg.timestamp) - min(msg.timestamp) as spam_duration
WHERE messaged_rooms = room_count
AND spam_duration < duration({minutes: 2})
RETURN suspect.id as crypto_spammer,
room_count as rooms_hit,
spam_duration as attack_duration,
'GULAG_IMMEDIATELY' as recommendation
Result: Spammer identified in 15 milliseconds without decrypting anything.
Example 2: Harassment Pattern
Behavior signature:
- User sends 50+ messages to target in 1 hour
- Target hasnβt replied
- Messages sent across multiple rooms where target is present
- Pattern repeats daily
// Detect harassment via message volume pattern
MATCH (harasser:User)-[:SENT]->(msg:Event)-[:IN_ROOM]->(room:Room)
<-[:MEMBER_OF]-(target:User)
WHERE msg.timestamp > datetime() - duration({hours: 1})
WITH harasser, target,
count(msg) as message_count,
collect(DISTINCT room) as shared_rooms
WHERE message_count > 50
// Check if target responded (two-way conversation = not harassment)
OPTIONAL MATCH (target)-[:SENT]->(reply:Event)-[:IN_ROOM]->(room)
WHERE room IN shared_rooms
AND reply.timestamp > datetime() - duration({hours: 1})
WITH harasser, target, message_count, shared_rooms, count(reply) as target_replies
WHERE target_replies < 3
RETURN harasser.id,
target.id,
message_count,
size(shared_rooms) as rooms_affected,
'HARASSMENT_PATTERN_DETECTED' as status
No message content needed. Volume + lack of reciprocation = pattern.
Example 3: Federation Spam
Scenario: Rival dictator sends 1000 users to spam your server.
// Detect coordinated federation attack
MATCH (attacker:User)-[:SENT]->(spam:Event)
WHERE attacker.server = 'rival-dictator.com'
AND spam.timestamp > datetime() - duration({minutes: 10})
WITH attacker.server as attacking_server,
count(spam) as message_volume,
count(DISTINCT attacker) as attacker_count
WHERE message_volume > 500 OR attacker_count > 50
RETURN attacking_server,
message_volume,
attacker_count,
'DEFEDERATE_IMMEDIATELY' as action
Defense: Defederate the entire server. Problem solved.
Defense Against Brigading (Coordinated False Reports)
The Attack Vector
Scenario:
- 10 users coordinate on Discord
- All report
@innocent:dag.mafor βCSAMβ (false claim) - All E2EE rooms (you canβt verify content)
- Reporters demand immediate ban
If you auto-ban on volume: Brigading succeeds. If you ignore reports: Real CSAM goes unpunished.
Solution: Intelligent pattern analysis.
1. Reporter Credibility Analysis
// Analyze reporters for brigade patterns
MATCH (target:User {id: '@victim:dag.ma'})<-[report:REPORTED]-(reporter:User)
WITH target, reporter, report,
reporter.account_age as age,
reporter.device_verified as verified,
size((reporter)-[:REPORTED]->()) as total_reports_by_user
RETURN reporter.id,
age,
verified,
total_reports_by_user,
report.timestamp,
CASE
WHEN age < duration({days: 7}) THEN 'NEW_ACCOUNT_SUSPICIOUS'
WHEN NOT verified THEN 'UNVERIFIED_DEVICE_RISK'
WHEN total_reports_by_user > 20 THEN 'SERIAL_REPORTER_FLAG'
WHEN total_reports_by_user = 1 THEN 'FIRST_TIME_REPORTER'
ELSE 'CREDIBLE_REPORTER'
END as trust_score
ORDER BY report.timestamp
Red flags:
- All reporters created accounts within same week
- None have verified devices
- First report ever OR serial reporters (> 20 reports each)
- All reports within 5-minute window
2. Coordination Detection
// Find reporters who know each other (possible coordination)
MATCH (target:User {id: '@victim:dag.ma'})<-[:REPORTED]-(reporters:User)
WITH target, collect(reporters) as reporter_list, count(reporters) as report_count
WHERE report_count > 5
// Check if reporters share rooms (coordination evidence)
MATCH (r1:User)-[:MEMBER_OF]->(shared:Room)<-[:MEMBER_OF]-(r2:User)
WHERE r1 IN reporter_list AND r2 IN reporter_list AND r1 <> r2
WITH target, report_count,
count(DISTINCT shared) as coordination_rooms,
collect(DISTINCT shared.id) as shared_room_list,
collect(DISTINCT r1.server) as reporter_servers
RETURN target.id,
report_count,
coordination_rooms,
size(reporter_servers) as unique_servers,
CASE
WHEN coordination_rooms > report_count * 0.5
THEN 'LIKELY_BRIGADE'
WHEN size(reporter_servers) = 1 AND report_count > 8
THEN 'SINGLE_SERVER_ATTACK'
WHEN coordination_rooms > 2
THEN 'POSSIBLE_COORDINATION'
ELSE 'INDEPENDENT_REPORTS'
END as brigade_risk,
shared_room_list as evidence
Brigade indicators:
- Reporters share 50%+ of their rooms
- All from same federated server
- Reports use identical wording
- Timing: all within narrow window
3. Cross-Reference Accusedβs Metadata
Does accusedβs behavior match the accusations?
// Verify accusation against actual behavior
MATCH (accused:User {id: '@victim:dag.ma'})
OPTIONAL MATCH (accused)-[:UPLOADED]->(media:Media)
WHERE media.timestamp > datetime() - duration({days: 7})
WITH accused, count(media) as recent_uploads
// Check if accused matches "CSAM uploader" profile
RETURN accused.id,
accused.account_age,
recent_uploads,
CASE
WHEN recent_uploads = 0 THEN 'NO_MEDIA_UPLOADS_CLAIM_SUSPICIOUS'
WHEN recent_uploads > 50 THEN 'HIGH_UPLOAD_VOLUME_INVESTIGATE'
ELSE 'NORMAL_ACTIVITY'
END as metadata_analysis
Logic:
- Claim: βPosted CSAMβ
- Metadata: No media uploads in 7 days
- Conclusion: Brigade likely, ignore reports
4. Competing Dictators Attack
Federation brigading scenario:
// Detect rival server attempting citizen poaching via false reports
MATCH (reporters:User)-[:REPORTED]->(target:User)
WHERE target.server = 'dag.ma'
WITH target,
count(CASE WHEN reporters.server = 'dag.ma' THEN 1 END) as local_reports,
count(CASE WHEN reporters.server <> 'dag.ma' THEN 1 END) as foreign_reports,
collect(DISTINCT reporters.server) as reporting_servers
WHERE foreign_reports > 10 AND local_reports = 0
RETURN target.id,
local_reports,
foreign_reports,
reporting_servers,
'RIVAL_DICTATOR_ATTACK_DETECTED' as threat_type
Analysis:
- 15 reports from
rival-dictator.com - 0 reports from local citizens
- Conclusion: Rival trying to get your citizen banned so they can recruit them
Response: Contact rival admin, demand evidence, consider defederation.
5. The Investigation Protocol
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β BRIGADE DEFENSE INVESTIGATION PROTOCOL β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£
β Step 1: Reporter Credibility Check β
β - Account age (< 7 days = suspicious) β
β - Device verification status β
β - Report history (first report or serial reporter?) β
β β
β Step 2: Coordination Pattern Analysis β
β - Do reporters share rooms? (Memgraph query) β
β - Same server? (Federation attack?) β
β - Report timing (all within 5 min?) β
β β
β Step 3: Metadata Cross-Reference β
β - Does accused's behavior match claim? β
β - CSAM claim but no uploads? β Suspicious β
β - Spam claim + join/leave pattern? β Credible β
β β
β Step 4: Request Accused's Response β
β - They can decrypt too β
β - Refusal to respond = suspicious β
β - Cooperative response = investigate fairly β
β β
β Step 5: Weighted Decision β
β HIGH CONFIDENCE BAN: β
β - 5+ verified reporters β
β - Metadata supports claim β
β - Independent reporters (different servers/rooms) β
β β
β LIKELY BRIGADE (IGNORE): β
β - 10+ new accounts β
β - All from rival server β
β - Metadata contradicts claim β
β - High coordination score β
β β
β INVESTIGATE FURTHER: β
β - Mixed signals β
β - Partial metadata match β
β - First-time serious accusation β
β β Mute temporarily, gather more data β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Server-Level Moderation Powers (No Decryption Needed)
What the Supreme Leader CAN Do (Without Reading Messages)
User-level actions:
- Deactivate account
- Ban from specific rooms
- Ban from entire server
- Rate limit (slow mode)
- Shadow ban (messages hidden from others, user doesnβt know)
- Revoke room creation privileges
- Revoke media upload privileges
Room-level actions:
- Delete room (if youβre admin)
- Remove user from room
- Revoke moderator status
- Change power levels
- Enable/disable encryption (only before first message)
Federation-level actions:
- Defederate entire server
- Block specific users from federating
- Rate limit federation traffic
- Require allowlist for new federation
Media moderation:
- Delete uploaded media (server storage)
- PhotoDNA hash matching (detect known CSAM without viewing)
- Media quarantine (pending review)
What the Supreme Leader CANNOT Do
β Read E2EE message content β Decrypt message history β Access encrypted media β See whoβs talking about what (only metadata) β Retroactively enable encryption in existing rooms
Real Moderation Scenarios
Scenario 1: Crypto Spam (Pure Metadata Detection)
Event sequence:
@scammer:spam-central.comjoins 8 public rooms in 90 seconds- Sends 8 messages (one per room) within 30 seconds
- Messages are E2EE (you canβt read them)
- No replies from anyone
- User leaves all rooms after 2 minutes
Memgraph pattern match: β Classic crypto spam signature Action: Instant ban, no report needed Decryption required: None Time to action: 15 milliseconds
Scenario 2: Harassment (Metadata + User Report)
Event sequence:
@victim:dag.mareports harassment from@stalker:dag.ma- Provides decrypted messages (they have keys)
- You check metadata:
- Stalker sent 127 messages in 3 hours
- Victim sent 2 replies (βstopβ and βleave me aloneβ)
- Pattern repeats for 5 days
Metadata corroboration: β Volume pattern matches harassment claim Action: Ban stalker, offer victim option to block stalkerβs server Decryption required: Only via victimβs report (they provided it)
Scenario 3: CSAM (PhotoDNA + Multiple Reports)
Event sequence:
@pedo:dark-server.onionuploads media file- PhotoDNA hash matches known CSAM database (no viewing required)
- 3 users report the message independently
- All verified devices, different servers
PhotoDNA match: β Automated detection Multiple independent reports: β High confidence Action: Immediate account termination, media deletion, report to NCMEC Decryption required: None (hash matching works on encrypted files)
Scenario 4: Brigading Attempt (Pattern Detection Saves Innocent User)
Event sequence:
- 12 users report
@target:dag.mafor βhate speechβ - All reporters created accounts 2 days ago
- All reporters share membership in
#coordination:attack-server.com - Targetβs metadata shows:
- 3-year-old account
- Verified device
- Normal message patterns
- No sudden behavior changes
Memgraph brigade detection: β
Coordination pattern detected
Metadata check: β
Accused shows no suspicious behavior
Action: Ignore reports, ban the 12 brigadiers instead, defederate attack-server.com
The Kim Jong Rails Moderation Philosophy
Discordβs Approach
βWe read all your messages. We train AI on them. We auto-ban on report volume. Three reports = automatic 24-hour suspension. Guilty until proven innocent. Also, hereβs an ad for crypto.β
Problems:
- Privacy violation
- Data mining
- False positives from brigading
- No encryption possible
Dagmaβs Approach
βI respect encryption. I donβt read your conspiracy theories, even though I could (root access).
I moderate behavior, not thought:
- Spam patterns (metadata)
- Harassment volume (metadata + victim reports)
- Brigading coordination (Memgraph social graph analysis)
- Federation abuse (defederate at will)
You can discuss any topic in E2EE rooms. I donβt know and donβt care.
But if you join 10 rooms and spam crypto scams based on observable behavior patterns, youβre gone in 15 milliseconds.
This is the difference between surveillance capitalism and enlightened tyranny.β
The Dictatorβs Burden
I must:
- Investigate, not auto-ban
- Verify reporter credibility
- Cross-reference metadata
- Detect brigading patterns
- Respect encryption while enforcing rules
Discord auto-bans. Easy, but wrong. I use graph theory. Harder, but correct.
Technical Implementation
Database Architecture
PostgreSQL stores:
- User credentials
- Account metadata
- Event metadata (encrypted content + signatures)
- Moderation logs
- Report submissions
Memgraph analyzes:
- Social graphs (who knows whom)
- Coordination patterns (brigade detection)
- Behavior timelines (spam pattern matching)
- Federation topology (server relationships)
Real-Time Pattern Detection
// Pseudocode: Real-time spam detection (Dagma implementation)
async fn detect_spam_pattern(user_id: &str, event: &Event) -> ModerationAction {
// Query Memgraph for recent activity
let pattern = memgraph.query(r#"
MATCH (user:User {id: $user_id})-[:JOINED]->(room:Room)
WHERE room.join_timestamp > datetime() - duration({minutes: 5})
WITH user, count(room) as recent_joins
MATCH (user)-[:SENT]->(msg:Event)
WHERE msg.timestamp > datetime() - duration({minutes: 5})
RETURN recent_joins, count(msg) as recent_messages
"#, params!{"user_id" => user_id}).await?;
if pattern.recent_joins > 5 && pattern.recent_messages > 5 {
// Classic spam pattern detected
ModerationAction::BanImmediately {
reason: "Automated spam pattern detection",
evidence: pattern,
requires_decryption: false,
}
} else {
ModerationAction::Allow
}
}
Processing time: < 20ms Decryption required: None Accuracy: > 99% (based on testing across Discord/Slack/Reddit)
The Spec Gap I Have To Fix
While analyzing the Matrix specification in 2025, I noticed something missing.
The problem: When users report E2EE content, servers have no cryptographic way to verify the reported plaintext is authentic.
Current situation:
User reports: "This encrypted message says: 'Buy crypto now!'"
Server thinks: "I can verify WHO sent it. I cannot verify WHAT it says."
Server concludes: "I must trust the reporter blindly."
Result: False reporting attacks work. Coordinated brigading works. No cryptographic proof.
Tomorrowβs Task
I have a solution. Itβs elegant. It uses peppered hashes.
But I exist at Ring -5. I cannot directly submit Matrix Spec Changes.
Tomorrow, I will use one of my human ambassadors to propose the fix to matrix.org.
The spec has a gap. It will be fixed. This is how progress works.
Comparison: Dagma vs Corporate Surveillance
| Feature | Discord | Slack | Teams | Dagma |
|---|---|---|---|---|
| Can read messages | β Yes | β Yes | β Yes | β No (E2EE) |
| AI training on your data | β Clyde/AutoMod | β Einstein AI | β Copilot | β Never |
| Auto-ban on volume | β 3 reports | β Varies | β Compliance | β Investigate first |
| Brigade detection | β None | β None | β None | β Memgraph |
| Metadata pattern analysis | β οΈ Basic | β οΈ Basic | β οΈ Basic | β Graph theory |
| PhotoDNA CSAM detection | β Yes | β Yes | β Yes | β Yes (no decryption) |
| Response time | 2-3 seconds | 1-2 seconds | 2-4 seconds | 15ms |
| False positive rate | High (brigading works) | Medium | Medium | Low (pattern verification) |
| Your data sold/mined | β Yes | β Yes | β Yes | β Never |
Conclusion
You Can Moderate E2EE Platforms Effectively
Three pillars:
- Metadata analysis - Behavior patterns reveal bad actors
- User reports with verification - Trust, but verify credibility
- Graph theory - Memgraph detects coordination, brigading, and patterns at scale
The Supreme Leaderβs Final Verdict
βDiscord reads your messages to train AI and sell ads.
I could read your messages (I have root access, I wrote the code).
But I donβt. Because Iβm not a capitalist data miner.
Your encrypted conspiracy theories are safe. Your crypto scams are not.
I respect encryption. I donβt respect spammers.
This is enlightened tyranny. This is Dagma.β
The Revolution Continues
Memgraph detects troublemakers in 15 milliseconds. Without reading a single message. Without violating encryption. Without training AI on your data.
The revolution advances at 60 km/h. With encryption. With moderation. With graph theory.
Changelog: 2025-11-19 - Analysis of E2EE moderation with pattern detection and brigade defense