A federated IRC server for hobbyist networks. Modern protocol internals, classic IRC interface.
MeshIRCd is a from-scratch IRC server in Go that federates with other MeshIRCd servers over a custom S2S protocol. Clients connect with any standard IRC client. Servers find each other via a shared GitHub repository.
JSON-LD S2S Protocol
Server-to-server communication uses JSON-LD over TLS. Events are signed with Ed25519, ordered with Lamport clocks, and deduplicated across the mesh. No TS6, no services, no legacy baggage.
GitHub-Based Discovery
Servers find peers by polling a servers.json file in a GitHub repo. Join the network with a PR. Leave by deleting your entry. Git history is your audit log.
DID Identity
Users can attach Decentralized Identifiers to their presence. Prove you control did:key:... or did:web:example.com. Identity propagates across the federation and shows in WHOIS.
IRCv3 Features
SASL EXTERNAL (TLS client certs mapped to DIDs), SASL GITHUB (device flow login), SASL OAUTHBEARER, message IDs, echo-message, chathistory, labeled-response. Modern client UX on a modern server.
GitHub Login
Users can authenticate with their GitHub account. No NickServ, no password registration — just AUTHENTICATE GITHUB, authorize in browser, done. Verified users get +r mode and nick protection.
Soft Mesh Topology
Servers connect to all peers they can reach. Messages flood with deduplication. No spanning tree, no single point of failure. Partitions heal automatically.
┌──────────────┐ JSON-LD/TLS ┌──────────────┐
│ MeshIRCd A │◄──────────────────►│ MeshIRCd B │
└──────┬───────┘ └───────┬──────┘
│ IRC │ IRC
▼ ▼
┌───────┐ ┌───────┐
│ irssi │ │ weechat│
└───────┘ └───────┘
- C2S: Standard IRC protocol + IRCv3 extensions
- S2S: JSON-LD messages, Ed25519 signatures, Lamport ordering
- Discovery: GitHub repo with
servers.json
# Generate keypair and server config
meshircd --init --hostname irc.example.com --port 6697 --admin you@example.com
# Start the server
meshircd \
--hostname irc.example.com \
--port 6697 \
--cert server.crt \
--key server.key \
--discovery-url https://raw.githubusercontent.com/MarkAtwood/meshircd-network/main/servers.json- Run
meshircd --initto generate your server block - Fork the network's config repo
- Add your block to
servers.json - Open a PR
- Wait for merge
- Start your server — peers connect within 5 minutes
servers.json (in your network's GitHub repo):
{
"network": "MyNetwork",
"servers": {
"irc.example.com": {
"port": 6697,
"pubkey": "ed25519:MCowBQYDK2VwAyEA...",
"admin": "admin@example.com"
}
}
}MeshIRCd supports GitHub-based authentication via SASL GITHUB mechanism and did:github identity verification. This requires a GitHub OAuth App.
- Go to GitHub Settings > Developer settings > OAuth Apps > New OAuth App
- Fill in the application details:
- Application name: MeshIRCd (or your network name)
- Homepage URL: Your server's URL or GitHub repo
- Authorization callback URL:
http://localhost(not used, but required)
- Click Register application
- Copy the Client ID
- Click Generate a new client secret and copy it immediately
- Device flow must be enabled: On the OAuth App settings page, check Enable Device Flow
- Scopes: Only
read:useris needed (for username lookup)
OAuth credentials can be provided via:
1. Init wizard (recommended)
meshircd --init --hostname irc.example.com --admin you@example.com
# Follow prompts for GitHub OAuth setup2. Environment variables
export GITHUB_CLIENT_ID=your_client_id
export GITHUB_CLIENT_SECRET=your_client_secret3. Command-line flags
meshircd --github-client-id=xxx --github-client-secret=xxx4. Config file (/data/github-oauth.json)
{
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}Priority: flags > env vars > config file.
Users can attach DIDs to their IRC presence:
/quote IDENTITY CHALLENGE
/quote METADATA * SET identity :{"@context":[...],"id":"did:key:z6Mk...","proof":{...}}
Identity shows in WHOIS and propagates across the federation.
| Document | Description |
|---|---|
| S2S.md | Server-to-server federation protocol |
| IDENTITY.md | DID-based identity extension |
| IRCV3.md | IRCv3 client protocol extensions |
| DISCOVERY.md | GitHub-based peer discovery |
| GITHUB.md | GitHub OAuth authentication |
go build -o meshircd .Requires Go 1.21+.
# Build
docker build -t meshircd .
# Initialize (generates keys, outputs servers.json block)
docker run --rm -v meshircd-data:/data \
-e MESHIRCD_HOSTNAME=irc.example.com \
-e MESHIRCD_ADMIN=you@example.com \
meshircd --init
# Run
docker run -d --name meshircd \
-v meshircd-data:/data \
-p 6697:6697 \
-e MESHIRCD_HOSTNAME=irc.example.com \
-e MESHIRCD_DISCOVERY_URL=https://raw.githubusercontent.com/MarkAtwood/meshircd-network/main/servers.json \
meshircdEnvironment variables:
MESHIRCD_HOSTNAME— server hostname (required)MESHIRCD_DISCOVERY_URL— servers.json URL for federationMESHIRCD_NETWORK— network name (default: MeshIRCd)MESHIRCD_ADMIN— admin email (for init)MESHIRCD_DATA— data directory (default: /data)GITHUB_CLIENT_ID— GitHub OAuth app client ID (enables GitHub login)GITHUB_CLIENT_SECRET— GitHub OAuth app client secret
Keys persist in the /data volume. First run with --init, PR the output block to the network repo, then run normally.
MeshIRCd can run behind Caddy or another proxy using TCP passthrough. TLS must terminate at MeshIRCd (not the proxy) so federation peers see the correct certificate.
Caddy with layer4 plugin:
# Caddyfile
{
layer4 {
:6697 {
route {
proxy meshircd-backend:6697
}
}
}
}
Build Caddy with the layer4 plugin:
xcaddy build --with github.com/mholt/caddy-l4This works well for homelab setups where MeshIRCd runs on an internal server (e.g., via Tailscale) and Caddy runs on a public VPC.
MeshIRCd can federate over Tor, both connecting to .onion peers and running as a hidden service.
Enable Tor SOCKS5 proxy to connect to .onion federation peers:
meshircd --tor-socks=127.0.0.1:9050 ...Or via environment variable:
TOR_SOCKS=127.0.0.1:9050 meshircd ...When enabled, any peer in servers.json with a .onion hostname will be dialed through Tor.
Running as a Hidden Service
Run your server as a Tor hidden service for anonymous federation:
meshircd --onion --tor-control=127.0.0.1:9051 --tor-cookie=/var/lib/tor/control_auth_cookie ...Or with password auth:
meshircd --onion --tor-control=127.0.0.1:9051 --tor-password=mypassword ...The server creates an ephemeral hidden service and logs the .onion address on startup.
Mixed clearnet and .onion servers can federate together:
{
"servers": {
"irc.example.com": {
"port": 6697,
"pubkey": "ed25519:...",
"admin": "admin@example.com"
},
"abc123xyzdef456.onion": {
"port": 6697,
"pubkey": "ed25519:...",
"admin": "anon@protonmail.com",
"onion": true
}
}
}Environment variables:
TOR_SOCKS— SOCKS5 proxy address (default: 127.0.0.1:9050)TOR_CONTROL— Control port addressTOR_PASSWORD— Control port passwordTOR_COOKIE— Path to control_auth_cookie fileMESHIRCD_ONION— Set to "true" to enable hidden serviceMESHIRCD_TOR_CLIENT_ADDR— Listener for anonymous Tor clients (e.g., 127.0.0.1:6698)
Separate listener for clients connecting via Tor hidden service:
meshircd --tor-client-addr=127.0.0.1:6698 --onion --tor-control=127.0.0.1:9051 ...Point the hidden service at this port. Users connect normally (NICK/USER), pick their own nick. No identity verification available, so no +r mode — but otherwise standard IRC.
Authenticate via any OIDC provider (Azure AD, Okta, Keycloak, university IdPs, etc).
meshircd \
--oidc-issuer=https://login.microsoftonline.com/tenant/v2.0 \
--oidc-client-id=your-client-id \
--oidc-client-secret=your-secret \
...Or via environment variables:
OIDC_ISSUER=https://... OIDC_CLIENT_ID=... OIDC_CLIENT_SECRET=... meshircd/CAP REQ :sasl
/AUTHENTICATE OIDC
Server sends a verification URL. Visit it, log in with your institution, and you're authenticated.
Authenticated users get username_oidc nick with +r mode.
For university authentication:
- Register an OIDC client with your institution's IdP
- Request
openid profile emailscopes - Enable device flow (if supported)
- Configure meshircd with issuer URL and client credentials
Many universities support OIDC via Shibboleth 4+, Azure AD, or Google Workspace.
Connect via SSH instead of TLS. Identity verified against GitHub SSH keys.
meshircd --ssh-addr=:2222 ...Users connect with:
ssh alice@irc.example.com -p 2222The alice in alice@host is the GitHub username claim. Server fetches github.com/alice.keys and verifies the connecting key is in the list.
| Auth | Nick |
|---|---|
| GitHub key verified | alice_github |
| Unknown key | _ssh_a1b2c3d4 |
MESHIRCD_SSH_ADDR— SSH listener address (e.g., :2222)
- TLS only — no plaintext, no STARTTLS upgrade dance
- No services — identity is DIDs, not NickServ
- No linking complexity — soft mesh, not spanning tree
- Git as coordination — PRs for trust decisions, history for audit
- JSON-LD for extensibility — add contexts, not protocol versions
- Existing clients work — IRCv3 where it helps, standard IRC everywhere
AGPL-3.0