freeq
CLAUDE.md90 starsChanged 46 days agoMITView on GitHub
⚠️ Pending developer-account work — do not lose track. A batch of signing/distribution tasks for macOS, iOS, and Android is blocked on Apple Developer Program + Google Play Console approval. When those accounts are approved, work through docs/DEVELOPER-ACCOUNT-TODO.md (Developer ID + notarization + Sparkle for Mac; Live Activity/Watch provisioning + APNs/CallKit + TestFlight for iOS; release keystore + Play App Signing + FCM for Android; plus the coordinated S2 session-scoping flip and store-compliance items). The code is already ready —…
- Reads credentials
In this repository7 other files it gives its agents.
- agent-docs/agents.mdAGENTS.md
- docs/agents.mdAGENTS.md
- Freeq.WinUI/CLAUDE.mdCLAUDE.md
- llms.txtllms.txt
- freeq-apiSkill
- freeq-botsSkill
- freeqSkill
The file
We split this CLAUDE.md into the rules it gives an agent
10 rules. About 0 are common, 9 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈9
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 26
Verify the signature over the exact challenge bytes
- Distinctive: line 33
Do not accept delegation keys
- Distinctive: line 42
No hashing unless explicitly required by key type
- Distinctive: line 51
Treat the IRC nick as a display alias
- Distinctive: line 67
No existing IRC behavior may break
- Distinctive: line 109
Ask the user for:
- Distinctive: line 131
No crashes on malformed server responses
- Distinctive: line 199
If something feels “too clever,” it’s probably wrong.
- Very specific: line 222
Run ./scripts/hotspots.sh at session start to identify high-risk files.
- Distinctive: line 232
When modifying a high-gamma file, write tests FIRST.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
⚠️ Pending developer-account work — do not lose track. A batch of signing/distribution tasks for macOS, iOS, and Android is blocked on Apple Developer Program + Google Play Console approval. When those accounts are approved, work through
docs/DEVELOPER-ACCOUNT-TODO.md(Developer ID + notarization + Sparkle for Mac; Live Activity/Watch provisioning + APNs/CallKit + TestFlight for iOS; release keystore + Play App Signing + FCM for Android; plus the coordinated S2 session-scoping flip and store-compliance items). The code is already ready — these are the identity/provisioning steps ad-hoc signing can't do.
Requirements:
session_idmust be unique per TCP connectionnoncemust be cryptographically random- Timestamp validity window: ≤ 60 seconds
- Challenge must be invalidated after use
3.6 Signature Verification
The server must:
- Resolve the DID document
- Extract acceptable verification keys
- Verify the signature over the exact challenge bytes
Key Rules
- Accept keys listed under:
authentication- (optional fallback)
assertionMethod
- Do not accept delegation keys
- Supported curves:
secp256k1(MUST)ed25519(SHOULD)
Signature Encoding
- Signature is
base64url(unpadded) - Signature is over raw challenge bytes
- No hashing unless explicitly required by key type
3.7 Post-Authentication Behavior
On success:
- Bind the connection to the DID
- Treat the IRC nick as a display alias
- Internal account identity = DID
- Emit standard IRC numeric
903
On failure:
- Emit numeric
904 - Terminate SASL flow cleanly
- Allow fallback to guest auth
3.8 Backward Compatibility
- Clients that do not request SASL must still connect
- Clients that do not support
ATPROTO-CHALLENGEmust still connect - No existing IRC behavior may break
4. Deliverable B: Minimal TUI Client
4.1 Purpose
The client exists to:
- Prove the SASL mechanism works
- Demonstrate a realistic user flow
- Serve as a reference implementation
This is not a full IRC client.
4.2 Base Requirements
- Language: Go or Rust
- Runs in a terminal
- Uses a simple text UI (no mouse, no GUI toolkit required)
- Connects to the custom IRC server
4.3 Client Capabilities
The client must:
- Perform IRC registration
- Negotiate IRCv3 capabilities
- Perform SASL authentication using
ATPROTO-CHALLENGE - Join a channel
- Send and receive plain text messages
4.4 AT Authentication Flow (Client-Side)
The client must:
- Ask the user for:
- AT identifier (DID or handle)
- Resolve handle → DID (if needed)
- Authenticate to the user’s AT identity provider
- OAuth or app-password is acceptable
- Receive server challenge
- Sign challenge with the user’s private key
- Send signature via SASL
- Complete IRC registration
Private keys must never be sent to the IRC server.
4.5 UX Expectations
Minimal but clear:
- Status line showing:
- connection state
- authenticated DID
- Clear error messages on auth failure
- No crashes on malformed server responses
5. Testing & Validation
5.1 Required Tests
- Successful auth with valid DID
- Failure on:
- expired challenge
- replayed nonce
- invalid signature
- unsupported key type
- Connection without SASL still works
- Standard IRC client can connect in guest mode
5.2 Manual Demo Scenario
Contractor must be able to demonstrate:
- Start server locally
- Connect with:
- a standard IRC client (guest)
- the custom TUI client (authenticated)
- Join the same channel
- Exchange messages
6. Documentation Deliverables
The contractor must provide:
- README
- How to build server
- How to run server
- How to run client
- Protocol Notes
- Any deviations or assumptions
- Known Limitations
- Explicit list
7. Acceptance Criteria
This project is complete when:
- Server successfully authenticates users via AT-backed SASL
- Client completes full auth flow without hacks
- System behaves as a normal IRC server for non-AT clients
- Code is readable, commented, and auditable
- The implementation could plausibly be referenced in an IRCv3 WG proposal
8. Philosophy (Context for the Implementer)
This project treats IRC as infrastructure, not a product.
The goal is to modernize identity without:
- centralization
- UX regressions
- protocol breakage
If something feels “too clever,” it’s probably wrong.
Production deploy (irc.freeq.at)
Prod host reth (160.202.129.155, TLS cert CN tech.blueyard.com), SSH user
chad (passwordless sudo). systemd unit freeq-server, repo at
/home/chad/src/freeq, secrets in /home/chad/src/freeq/.env.secrets
(not in git — that's where GITHUB_CLIENT_ID etc. belong).
Deploy ONLY via the script — never hand-run cargo/npm on the host:
ssh chad@160.202.129.155 "cd ~/src/freeq && ./deploy/deploy.sh"The script builds with --features av-native; a plain cargo build --release
produces a no-AV binary, and AV in prod silently went down for ~2h on
2026-08-18 from exactly that mistake. The script hard-fails if
/api/v1/health doesn't report "av":true after restart — if it fails,
the deploy failed, fix the build before doing anything else.
Hotspot Analysis
Run ./scripts/hotspots.sh at session start to identify high-risk files. Focus adversarial testing, careful review, and iterative refactoring on files with high gamma scores. Files with low gamma can be changed quickly.
Current hotspots (updated 2026-03-31):
server.rs(gamma 334) — heavily tested, 116 unit/integration testsweb.rs(gamma 275) — 41 tests (broker + upload + REST)irc/client.ts(gamma 133) — UNDERTESTED — needs dedicated unit testsMessageList.tsx(gamma 103) — UNDERTESTED — only Playwright coveragesdk/client.rs(gamma 104) — ZERO unit tests on connection state machinestore.ts(gamma 43) — well tested (397 vitest)
When modifying a high-gamma file, write tests FIRST.
TODO
P0 — Critical (do next)
msgidon all messages — ✅ DONE. ULID on every PRIVMSG/NOTICE, carried in IRCv3msgidtag, stored in DB + history, included in CHATHISTORY replay and JOIN history. S2S preserves msgid across federation.- Message signing by default — ✅ DONE (Phase 1 + 1.5). Client-side ed25519 signing with session keys for true non-repudiation. SDK/web/iOS generate per-session ed25519 keypair, register via
MSGSIG, sign every PRIVMSG with+freeq.at/sig. Server verifies client sigs and relays unchanged. Fallback: server signs if client doesn't support signing. Public keys at/api/v1/signing-key(server) and/api/v1/signing-keys/{did}(per-DID). Web client shows signed badge (🔒).
P1 — High priority
- JOIN races PROVENANCE on connect — ✅ DONE (instant-rooms branch). bot-kit no longer hands
channels to the SDK autojoin:
FreeqBotsends PROVENANCE first, waits (bounded, 3s) for the server's provenance NOTICE, then JOINs. Covers pi, freeqcc and@freeq/mcp(which now also actually sends PROVENANCE viabot.start()). - AV: web call grid auto-layout — web CallPanel must auto-adjust tile layout for participant counts from 1 to ~30 (macOS already does this via
CallGridLayout.tileSize; port the same policy to web). - AV: click-to-focus a call tile (ALL clients) — clicking a participant chip/card focuses it (large tile, others shrink to strip). Web + macOS + iOS.
- Message editing — ✅ DONE.
+draft/edit=<msgid>on PRIVMSG. Server verifies authorship, stores withreplaces_msgid, updates in-memory history, broadcasts to channel. - Message deletion — ✅ DONE.
+draft/delete=<msgid>on TAGMSG. Soft delete (deleted_at). Author or ops can delete. Excluded from CHATHISTORY/history. away-notifycap — ✅ DONE. Broadcast AWAY changes to shared channel members. Server, SDK, TUI, and web client all support it.- S2S authorization on Kick/Mode — ✅ DONE. Receiving server verifies the kicker/mode-setter is an op (via remote_members is_op, founder_did, or did_ops) before executing. Unauthorized mode/kick events are rejected with warning log.
- S2S authorization on Topic — ✅ DONE. +t channels reject topic changes from non-ops. Removed "trust unknown users" fallback.
- SyncResponse channel creation limit — ✅ DONE.
MAX_SYNC_CHANNELS = 500cap inserver.rs:3116; overflow truncated withtracing::warn!. - ChannelCreated should propagate default modes — ✅ DONE. New channels from S2S get +nt defaults.
- Invites should sync via S2S — ✅ DONE. S2sMessage::Invite variant relays invite tokens (DID or nick:XXX) to peers. SyncResponse carries invites (additive merge). S2S Join enforcement checks invite list before rejecting +i. Invites consumed on join.
- S2S rate limiting — ✅ DONE. 100 events/sec/peer enforced in
process_s2s_message(server.rs:2135) viaS2S_RATE_LIMITSmap +S2S_MAX_EVENTS_PER_SEC. Over-limit events dropped with a single warn-per-second per peer. - DPoP nonce retry for SASL verification — ✅ DONE. Server detects PDS
use_dpop_nonceerrors, sends fresh nonce to client via NOTICE, re-issues SASL challenge. Client (SDK) updates DPoP nonce and retries automatically. Capped at 3 retries per SASL attempt to prevent infinite loops. Counter resets on new SASL attempt.
P2 — Important
- Topic merge consistency — ✅ DONE. Sync-adopted topics now seed the CRDT (only when CRDT has none), making the CRDT the single topic authority. No more dual merge strategies.
- Channel key removal propagation — ✅ DONE. With no local members, SyncResponse adopts the full mode snapshot including
key: None(-k propagates). With locals present, snapshots still never weaken local protections (live S2S Mode events handle -k there). - SyncResponse invite authority — ✅ DONE. Synced invites are only merged when the peer's snapshot names the same founder we have (or we have none); mismatches are rejected + logged. Closes the +i bypass.
- S2S authentication (allowlist enforcement) —
--s2s-allowed-peersonly checks incoming. Formalize mutual auth. - Ban sync + enforcement — ✅ DONE. S2sMessage::Ban variant, authorized set/remove, SyncResponse carries bans, additive merge.
- S2S Join enforcement — ✅ DONE. Incoming S2S Joins check bans (nick + DID) and +i (invite only). Blocked joins logged.
- Hostname cloaking — ✅ DONE.
freeq/plc/xxxxxxxxfor DID users,freeq/guestfor guests. - IRCv3: account-notify / extended-join — ✅ DONE. DID broadcast on SASL success and extended JOIN.
- IRCv3: CHATHISTORY — ✅ DONE. On-demand history retrieval with batch support.
- Connection limits — ✅ DONE. 20 per-IP at TCP + WebSocket level.
- OPER command — ✅ DONE. OPER
<name><password>+ auto-OPER via --oper-dids. Server opers bypass channel op checks. - TUI auto-reconnection — Reconnect with backoff, rejoin channels.
- Normalize nick_to_session to lowercase keys — ✅ DONE. NickMap wrapper with O(1) bidirectional lookups. All 39 call sites updated.
P2.5 — Web App Prerequisites (see docs/WEB-APP-PLAN.md)
- Web app (Phase 1) — ✅ DONE. React+TS+Vite+Tailwind at freeq-app/.
- Search (FTS5) — ✅ DONE. SQLite FTS5 index (plaintext DBs; encrypted DBs use bounded decrypt-and-scan, and opening encrypted drops any stale plaintext index). IRC
SEARCH <target> :<query>with CHATHISTORY-equivalent authorization, results infreeq.at/searchbatch. RESTGET /api/v1/search?channel=&q=(channels only, +i/+k → 403). 9 db unit tests + 6 acceptance tests. - Pinned messages — ✅ DONE. PIN/UNPIN/PINS commands, REST API, web client PinnedBar + context menu.
P3 — Future
- Wire CRDT to live S2S (replace ad-hoc JSON for durable state)
- DID-based key exchange for E2EE (replace passphrase-based)
- Full-text search (SQLite FTS5)
- Bot framework (formalize SDK pattern)
- AT Protocol record-backed channels
- Reputation/trust via social graph
- Serverless P2P mode
- IRCv3 WG proposal for ATPROTO-CHALLENGE
- Web client — ✅ DONE (freeq-app/, deployed at irc.freeq.at)
- Moderation event log (CRDT-backed, ULID-keyed)
- AT Protocol label integration for moderation
Done (this session)
- Case-insensitive remote_members helpers (
remote_member(),has_remote_member(),remove_remote_member()) - All S2S handlers use case-insensitive nick lookups (Privmsg +n/+m, Part, Quit, NickChange, Mode +o/+v, Kick, Topic)
- SyncResponse mode protection (never weakens local +n/+i/+t/+m)
- Topic flow fix (S2S Topic +t trusts peer authorization for unknown users)
- KICK sending-side case-insensitive remove
- 15 new edge case acceptance tests (96 total, all passing)
- Full S2S sync audit (
docs/SYNC-AUDIT.md) - Lint updated to catch raw remote_members access
End of document
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

