freeq uses the AT Protocol (Bluesky) for identity. Authentication is optional — unauthenticated users connect as guests with standard IRC features.
ATPROTO-CHALLENGE mechanismUser → auth.freeq.at/auth/login → Bluesky OAuth popup
→ broker gets PDS token → mints web-token → pushes to server
→ client sends web-token via SASL → authenticated
No passwords are sent to freeq. The broker talks to the user's PDS (Personal Data Server) via standard AT Protocol OAuth.
# OAuth (opens browser)
freeq-tui --handle alice.bsky.social
# App password (legacy)
freeq-tui --handle alice.bsky.social --app-password xxx-xxxx-xxxx
Sessions are cached at ~/.config/freeq/ and reused across connections.
freeq-tui --did did:plc:abc123 --key-file key.hex --key-type ed25519
Or use the SDK's KeySigner:
let signer = KeySigner::new(did, private_key);
let conn = establish_connection(&config).await?;
let (handle, events) = connect_with_stream(conn, config, Some(Arc::new(signer)));
| Feature | Guest | Authenticated |
|---|---|---|
| Chat in channels | ✅ | ✅ |
| Message signing | ❌ | ✅ (automatic) |
| E2EE DMs | ❌ | ✅ |
| Policy-gated channels | ❌ | ✅ |
| Media uploads | ❌ | ✅ (via PDS) |
| Hostname cloaking | freeq/guest |
freeq/plc/xxxxxxxx |
| Multi-device | ❌ | ✅ (same DID) |
| Ghost grace period | ❌ | ✅ (30s reconnect window) |