diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..dac1314
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,2 @@
+knotteye <knotteye@airmail.cc>
+crushv <nik@telekem.net>
\ No newline at end of file
diff --git a/config/default.toml b/config/default.toml
index cda1845..c7ff14d 100644
--- a/config/default.toml
+++ b/config/default.toml
@@ -12,9 +12,11 @@ rootredirect = '/users/live'
 
 [ircd]
 enable = false
-port = 7000
-user = ''
+port = 6667
+sid = ''
+server = ''
 pass = ''
+vhost = 'web.satyr.net'
 
 [database]
 host = 'localhost'
diff --git a/src/controller.ts b/src/controller.ts
index 593679e..8ed3e1d 100644
--- a/src/controller.ts
+++ b/src/controller.ts
@@ -56,7 +56,7 @@ async function run() {
 	db.init(dbcfg, bcryptcfg);
 	await cleanup.init(config.server.http.directory);
 	api.init(satyr);
-	http.init(satyr, config.server.http.port);
+	http.init(satyr, config.server.http.port, config.ircd);
 	mediaserver.init(nms, satyr);
 }
 run();
diff --git a/src/http.ts b/src/http.ts
index 02e7e17..4593f4d 100644
--- a/src/http.ts
+++ b/src/http.ts
@@ -7,6 +7,7 @@ import * as http from "http";
 import * as dirty from "dirty";
 import * as api from "./api";
 import * as db from "./database";
+import * as irc from "./irc";
 
 const app = express();
 const server = http.createServer(app);
@@ -14,7 +15,7 @@ const io = socketio(server);
 const store = dirty();
 var njkconf;
 
-function init(satyr: any, port: number){
+async function init(satyr: any, port: number, ircconf: any){
 	njk.configure('templates', {
 		autoescape: true,
 		express   : app,
@@ -119,26 +120,44 @@ function init(satyr: any, port: number){
 	app.use(function (req, res, next) {
 		res.status(404).render('404.njk', njkconf);
 	});
+	//irc peering
+	if(ircconf.enable){
+		await irc.connect({
+			port: ircconf.port,
+			sid: ircconf.sid,
+			pass: ircconf.pass,
+			server: ircconf.server,
+			vhost: ircconf.vhost
+		});
+		irc.events.on('message', (nick, channel, msg) => {
+			io.to(channel).emit('MSG', {nick: nick, msg: msg});
+		});
+	}
 	//socket.io chat logic
 	io.on('connection', async (socket) => {
 		socket.nick = await newNick(socket);
+		if(ircconf.enable) irc.registerUser(socket.nick);
 		socket.on('JOINROOM', async (data) => {
 			let t: any = await db.query('select username from users where username='+db.raw.escape(data));
 			if(t[0]){
 				socket.join(data);
 				io.to(data).emit('JOINED', {nick: socket.nick});
+				if(ircconf.enable) irc.join(socket.nick, data);
 			}
 			else socket.emit('ALERT', 'Room does not exist');
 		});
 		socket.on('LEAVEROOM', (data) => {
 			socket.leave(data);
+			if(ircconf.enable) irc.part(socket.nick, data);
 			io.to(data).emit('LEFT', {nick: socket.nick});
 		});
 		socket.on('disconnecting', (reason) => {
 			let rooms = Object.keys(socket.rooms);
 			for(let i=1;i<rooms.length;i++){
+				if(ircconf.enable) irc.part(socket.nick, rooms[i]);
 				io.to(rooms[i]).emit('ALERT', socket.nick+' disconnected');
 			}
+			irc.unregisterUser(socket.nick);
 			store.rm(socket.nick);
 		});
 		socket.on('NICK', async (data) => {
@@ -164,6 +183,7 @@ function init(satyr: any, port: number){
 		});
 		socket.on('MSG', (data) => {
 			io.to(data.room).emit('MSG', {nick: socket.nick, msg: data.msg});
+			if(ircconf.enable) irc.send(socket.nick, data.room, data.msg);
 		});
 		socket.on('KICK', (data) => {
 			if(socket.nick === data.room){
diff --git a/src/irc.js b/src/irc.js
new file mode 100644
index 0000000..4427303
--- /dev/null
+++ b/src/irc.js
@@ -0,0 +1,213 @@
+// written by crushv <nik@telekem.net>
+// thanks nikki
+
+const net = require('net')
+const EventEmitter = require('events')
+
+const socket = new net.Socket()
+const emitter = new EventEmitter()
+
+socket.setEncoding('utf8')
+
+socket.on('error', console.error)
+
+function m (text) {
+  console.log('> ' + text)
+  socket.write(text + '\r\n')
+}
+
+var config
+
+socket.once('connect', async () => {
+  console.log('Connected')
+  m(`PASS ${config.pass} TS 6 :${config.sid}`)
+  m('CAPAB QS ENCAP EX IE SAVE EUID')
+  m(`SERVER ${config.server} 1 satyr`)
+})
+
+function parseLine (l) {
+  const colIndex = l.lastIndexOf(':')
+  if (colIndex > -1) {
+    return {
+      params: l.substring(0, colIndex - 1).split(' '),
+      query: l.substring(colIndex + 1)
+    }
+  } else return { params: l.split(' ') }
+}
+
+const servers = []
+const users = {}
+const channels = {}
+
+const globalCommands = {
+  // PING :42X
+  // params: SID
+  PING: l => {
+    const { query } = parseLine(l)
+    m(`PONG :${query}`)
+    emitter.emit('ping')
+  },
+  // PASS hunter2 TS 6 :42X
+  // params: password, 'TS', TS version, SID
+  PASS: l => {
+    const { query } = parseLine(l)
+    // adds a server
+    servers.push(query)
+  }
+}
+
+const serverCommands = {
+  // EUID nik 1 1569146316 +i ~nik localhost6.attlocal.net 0::1 42XAAAAAB * * :nik
+  // params: nickname, hopcount, nickTS, umodes, username, visible hostname, IP address, UID, real hostname, account name, gecos
+  EUID: l => {
+    const { params } = parseLine(l)
+    const user = {
+      nick: params[0],
+      nickTS: params[2],
+      modes: params[3],
+      username: params[4],
+      vhost: params[5],
+      ip: params[6],
+      uid: params[7]
+    }
+    users[user.uid] = user
+  },
+  // SJOIN 1569142987 #test +nt :42XAAAAAB
+  // params: channelTS, channel, simple modes, opt. mode parameters..., nicklist
+  SJOIN: l => {
+    const { params, query } = parseLine(l)
+    const channel = {
+      timestamp: params[0],
+      name: params[1],
+      modes: params.slice(2).join(' '),
+      nicklist: query.split(' ').map(uid => {
+        if (/[^0-9a-zA-Z]/.test(uid[0])) return { uid: uid.slice(1), mode: uid[0] }
+        else return { uid: uid, mode: '' }
+      })
+    }
+    channels[channel.name] = channel
+  }
+}
+
+const userCommands = {
+  // :42XAAAAAC PRIVMSG #test :asd
+  // params: target, msg
+  PRIVMSG: (l, source) => {
+    const { params, query } = parseLine(l)
+    emitter.emit('message', users[source].nick, params[0], query)
+  },
+  // :42XAAAAAC JOIN 1569149395 #test +
+  JOIN: (l, source) => {
+    const { params } = parseLine(l)
+    channels[params[1]].nicklist.push({
+      uid: source
+    })
+  },
+  // :42XAAAAAC PART #test :WeeChat 2.6
+  PART: (l, source) => {
+    const { params } = parseLine(l)
+    for (let i = 0; i < channels[params[0]].nicklist.length; i++) {
+      if (channels[params[0]].nicklist[i].uid === source) {
+        channels[params[0]].nicklist.splice(i, 1)
+        return
+      }
+    }
+  },
+  QUIT: (_l, source) => {
+    delete users[source]
+  }
+}
+
+function parser (l) {
+  const split = l.split(' ')
+  const cmd = split[0]
+  const args = split.slice(1).join(' ')
+  if (globalCommands[cmd]) return globalCommands[cmd](args)
+  if (cmd[0] === ':') {
+    const source = cmd.slice(1)
+    const subcmd = split[1]
+    const subargs = split.slice(2).join(' ')
+    if (servers.indexOf(source) > -1 && serverCommands[subcmd]) serverCommands[subcmd](subargs)
+    if (users[source] && userCommands[subcmd]) userCommands[subcmd](subargs, source)
+  }
+}
+
+socket.on('data', data => {
+  data.split('\r\n')
+    .filter(l => l !== '')
+    .forEach(l => {
+      console.log('< ' + l)
+      parser(l)
+    })
+})
+
+process.on('SIGINT', () => {
+  socket.write('QUIT\r\n')
+  process.exit()
+})
+
+module.exports.connect = conf => new Promise((resolve, reject) => {
+  emitter.once('ping', resolve)
+  config = conf
+  socket.connect(config.port)
+})
+module.exports.events = emitter
+
+const genTS = () => Math.trunc((new Date()).getTime() / 1000)
+const genUID = () => {
+  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+  var uid = ''
+  for (let i = 0; i < 6; i++) uid += chars.charAt(Math.floor(Math.random() * chars.length))
+  if (users[uid]) return genUID()
+  return config.sid + uid
+}
+const getUID = nick => {
+  for (const key in users) if (users[key].nick === nick) return key
+}
+
+module.exports.registerUser = nick => {
+  const user = {
+    nick: nick,
+    nickTS: genTS(),
+    modes: '+i',
+    username: '~' + nick,
+    vhost: config.vhost,
+    ip: '0::1',
+    uid: genUID()
+  }
+  users[user.uid] = user
+  m(`EUID ${user.nick} 1 ${user.nickTS} ${user.modes} ~${user.nick} ${user.vhost} 0::1 ${user.uid} * * :${user.nick}`)
+}
+module.exports.unregisterUser = nick => {
+  const uid = getUID(nick)
+  m(`:${uid} QUIT :Quit: satyr`)
+  delete users[uid]
+}
+module.exports.join = (nick, channelName) => {
+  const uid = getUID(nick)
+  if (!channels[channelName]) {
+    const channel = {
+      timestamp: genTS(),
+      name: channelName,
+      modes: '+nt',
+      nicklist: [{ uid: uid, mode: '' }]
+    }
+    channels[channel.name] = channel
+  }
+  m(`:${uid} JOIN ${channels[channelName].timestamp} ${channelName} +`)
+}
+module.exports.part = (nick, channelName) => {
+  const uid = getUID(nick)
+  m(`:${uid} PART ${channelName} :satyr`)
+  for (let i = 0; i < channels[channelName].nicklist.length; i++) {
+    if (channels[channelName].nicklist[i].uid === uid) {
+      channels[channelName].nicklist.splice(i, 1)
+      return
+    }
+  }
+}
+module.exports.send = (nick, channelName, message) => {
+  const uid = getUID(nick)
+  m(`:${uid} PRIVMSG ${channelName} :${message}`)
+  emitter.emit('message', nick, channelName, message)
+}