diff --git a/src/bot.ts b/src/bot.ts
index 74b050beab4da15999b84e6f1da92e2264e7beb3..176d81c87f3e651836420f94ca524fafc4fcf300 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -64,7 +64,6 @@ interface IThirdPartyLookup {
 
 export class DiscordBot {
     private clientFactory: DiscordClientFactory;
-    private store: DiscordStore;
     private bot: Discord.Client;
     private presenceInterval: number;
     private sentMessages: string[];
@@ -72,7 +71,7 @@ export class DiscordBot {
     private discordMsgProcessor: DiscordMessageProcessor;
     private mxEventProcessor: MatrixEventProcessor;
     private presenceHandler: PresenceHandler;
-    private userSync: UserSyncroniser;
+    private userSync!: UserSyncroniser;
     private channelSync: ChannelSyncroniser;
     private roomHandler: MatrixRoomHandler;
     private provisioner: Provisioner;
@@ -82,22 +81,25 @@ export class DiscordBot {
     /* Handles messages queued up to be sent to discord. */
     private discordMessageQueue: { [channelId: string]: Promise<void> };
 
-    constructor(private botUserId: string, private config: DiscordBridgeConfig, private bridge: Bridge) {
+    constructor(
+        private botUserId: string,
+        private config: DiscordBridgeConfig,
+        private bridge: Bridge,
+        private store: DiscordStore,
+    ) {
 
-        // create classes
-        this.store = new DiscordStore(config.database);
-        this.provisioner = new Provisioner(this.bridge);
-        this.clientFactory = new DiscordClientFactory(this.store, config.auth);
+        // create handlers
+        this.provisioner = new Provisioner(store.roomStore);
+        this.clientFactory = new DiscordClientFactory(store, config.auth);
         this.discordMsgProcessor = new DiscordMessageProcessor(
-            new DiscordMessageProcessorOpts(this.config.bridge.domain, this),
+            new DiscordMessageProcessorOpts(config.bridge.domain, this),
         );
         this.presenceHandler = new PresenceHandler(this);
-        this.roomHandler = new MatrixRoomHandler(this, this.config, this.provisioner, this.bridge);
+        this.roomHandler = new MatrixRoomHandler(this, config, this.provisioner, bridge, store.roomStore);
         this.mxEventProcessor = new MatrixEventProcessor(
-            new MatrixEventProcessorOpts(this.config, this.bridge, this),
+            new MatrixEventProcessorOpts(config, bridge, this),
         );
-        this.channelSync = new ChannelSyncroniser(this.bridge, this.config, this);
-
+        this.channelSync = new ChannelSyncroniser(bridge, config, this, store.roomStore);
         // init vars
         this.sentMessages = [];
         this.discordMessageQueue = {};
@@ -135,10 +137,9 @@ export class DiscordBot {
     }
 
     public async init(): Promise<void> {
-        await this.store.init();
-        // This uses userStore which needs to be accessed after the bridge has started.
-        this.userSync = new UserSyncroniser(this.bridge, this.config, this);
         await this.clientFactory.init();
+        // This immediately pokes UserStore, so it must be created after the bridge has started.
+        this.userSync = new UserSyncroniser(this.bridge, this.config, this);
     }
 
     public async run(): Promise<void> {
@@ -496,7 +497,7 @@ export class DiscordBot {
     }
 
     public async GetChannelFromRoomId(roomId: string, client?: Discord.Client): Promise<Discord.Channel> {
-        const entries = await this.bridge.getRoomStore().getEntriesByMatrixId(
+        const entries = await this.store.roomStore.getEntriesByMatrixId(
             roomId,
         );
 
@@ -509,9 +510,12 @@ export class DiscordBot {
             throw Error("Room(s) not found.");
         }
         const entry = entries[0];
-        const guild = client.guilds.get(entry.remote.get("discord_guild"));
+        if (!entry.remote) {
+            throw Error("Room had no remote component");
+        }
+        const guild = client.guilds.get(entry.remote!.get("discord_guild") as string);
         if (guild) {
-            const channel = client.channels.get(entry.remote.get("discord_channel"));
+            const channel = client.channels.get(entry.remote!.get("discord_channel") as string);
             if (channel) {
                 return channel;
             }
@@ -567,14 +571,14 @@ export class DiscordBot {
             this.roomIdsForGuildCache.set(`${guild.id}:${guild.member}`, {roomIds: rooms, ts: Date.now()});
             return rooms;
         } else {
-            const rooms = await this.bridge.getRoomStore().getEntriesByRemoteRoomData({
+            const rooms = await this.store.roomStore.getEntriesByRemoteRoomData({
                 discord_guild: guild.id,
             });
             if (rooms.length === 0) {
                 log.verbose(`Couldn't find room(s) for guild id:${guild.id}.`);
                 throw new Error("Room(s) not found.");
             }
-            const roomIds = rooms.map((room) => room.matrix.getId());
+            const roomIds = rooms.map((room) => room.matrix!.getId());
             this.roomIdsForGuildCache.set(`${guild.id}:`, {roomIds, ts: Date.now()});
             return roomIds;
         }
@@ -837,7 +841,7 @@ export class DiscordBot {
                     await afterSend(res);
                 } catch (e) {
                     if (e.errcode !== "M_FORBIDDEN" && e.errcode !==  "M_GUEST_ACCESS_FORBIDDEN") {
-                        log.error("DiscordBot", "Failed to send message into room.", e);
+                        log.error("Failed to send message into room.", e);
                         return;
                     }
                     if (msg.member) {
diff --git a/src/channelsyncroniser.ts b/src/channelsyncroniser.ts
index 74932cf6215145a1292787055f61d4fa5422bde1..9759dfa091e831ffaa11062686bb50d227e02442 100644
--- a/src/channelsyncroniser.ts
+++ b/src/channelsyncroniser.ts
@@ -18,8 +18,9 @@ import * as Discord from "discord.js";
 import { DiscordBot } from "./bot";
 import { Util } from "./util";
 import { DiscordBridgeConfig } from "./config";
-import { Bridge, RoomBridgeStore, Entry } from "matrix-appservice-bridge";
+import { Bridge } from "matrix-appservice-bridge";
 import { Log } from "./log";
+import { DbRoomStore, IRoomStoreEntry } from "./db/roomstore";
 
 const log = new Log("ChannelSync");
 
@@ -56,13 +57,13 @@ export interface IChannelState {
 }
 
 export class ChannelSyncroniser {
-
-    private roomStore: RoomBridgeStore;
     constructor(
         private bridge: Bridge,
         private config: DiscordBridgeConfig,
-        private bot: DiscordBot) {
-        this.roomStore = this.bridge.getRoomStore();
+        private bot: DiscordBot,
+        private roomStore: DbRoomStore,
+    ) {
+
     }
 
     public async OnUpdate(channel: Discord.Channel) {
@@ -111,7 +112,7 @@ export class ChannelSyncroniser {
         }
         log.info(`Channel ${channel.id} has been deleted.`);
         let roomids;
-        let entries;
+        let entries: IRoomStoreEntry[];
         try {
             roomids = await this.GetRoomIdsFromChannel(channel);
             entries = await this.roomStore.getEntriesByMatrixIds(roomids);
@@ -139,9 +140,6 @@ export class ChannelSyncroniser {
     }
 
     public async GetRoomIdsFromChannel(channel: Discord.Channel): Promise<string[]> {
-        if (!this.roomStore) {
-            this.roomStore = this.bridge.getRoomStore();
-        }
         const rooms = await this.roomStore.getEntriesByRemoteRoomData({
             discord_channel: channel.id,
         });
@@ -149,7 +147,7 @@ export class ChannelSyncroniser {
             log.verbose(`Couldn't find room(s) for channel ${channel.id}.`);
             return Promise.reject("Room(s) not found.");
         }
-        return rooms.map((room) => room.matrix.getId() as string);
+        return rooms.map((room) => room.matrix!.getId() as string);
     }
 
     public async GetChannelUpdateState(channel: Discord.TextChannel, forceUpdate = false): Promise<IChannelState> {
@@ -159,9 +157,6 @@ export class ChannelSyncroniser {
             mxChannels: [],
         });
 
-        if (!this.roomStore) {
-            this.roomStore = this.bridge.getRoomStore();
-        }
         const remoteRooms = await this.roomStore.getEntriesByRemoteRoomData({discord_channel: channel.id});
         if (remoteRooms.length === 0) {
             log.verbose(`Could not find any channels in room store.`);
@@ -183,26 +178,26 @@ export class ChannelSyncroniser {
             iconUrl = `https://cdn.discordapp.com/icons/${channel.guild.id}/${icon}.png`;
         }
         remoteRooms.forEach((remoteRoom) => {
-            const mxid = remoteRoom.matrix.getId();
+            const mxid = remoteRoom.matrix!.getId();
             const singleChannelState: ISingleChannelState = Object.assign({}, DEFAULT_SINGLECHANNEL_STATE, {
                 mxid,
             });
 
-            const oldName = remoteRoom.remote.get("discord_name");
-            if (remoteRoom.remote.get("update_name") && (forceUpdate || oldName !== name)) {
+            const oldName = remoteRoom.remote!.get("discord_name");
+            if (remoteRoom.remote!.get("update_name") && (forceUpdate || oldName !== name)) {
                 log.verbose(`Channel ${mxid} name should be updated`);
                 singleChannelState.name = name;
             }
 
-            const oldTopic = remoteRoom.remote.get("discord_topic");
-            if (remoteRoom.remote.get("update_topic") && (forceUpdate || oldTopic !== topic)) {
+            const oldTopic = remoteRoom.remote!.get("discord_topic");
+            if (remoteRoom.remote!.get("update_topic") && (forceUpdate || oldTopic !== topic)) {
                 log.verbose(`Channel ${mxid} topic should be updated`);
                 singleChannelState.topic = topic;
             }
 
-            const oldIconUrl = remoteRoom.remote.get("discord_iconurl");
+            const oldIconUrl = remoteRoom.remote!.get("discord_iconurl");
             // no force on icon update as we don't want to duplicate ALL the icons
-            if (remoteRoom.remote.get("update_icon") && oldIconUrl !== iconUrl) {
+            if (remoteRoom.remote!.get("update_icon") && oldIconUrl !== iconUrl) {
                 log.verbose(`Channel ${mxid} icon should be updated`);
                 if (iconUrl !== null) {
                     singleChannelState.iconUrl = iconUrl;
@@ -227,6 +222,10 @@ export class ChannelSyncroniser {
         for (const channelState of channelsState.mxChannels) {
             let roomUpdated = false;
             const remoteRoom = (await this.roomStore.getEntriesByMatrixId(channelState.mxid))[0];
+            if (!remoteRoom.remote) {
+                log.warn("Remote room not set for this room");
+                return;
+            }
             if (channelState.name !== null) {
                 log.verbose(`Updating channelname for ${channelState.mxid} to "${channelState.name}"`);
                 await intent.setRoomName(channelState.mxid, channelState.name);
@@ -274,13 +273,13 @@ export class ChannelSyncroniser {
     private async handleChannelDeletionForRoom(
         channel: Discord.TextChannel,
         roomId: string,
-        entry: Entry): Promise<void> {
+        entry: IRoomStoreEntry): Promise<void> {
         log.info(`Deleting ${channel.id} from ${roomId}.`);
         const intent = await this.bridge.getIntent();
         const options = this.config.channel.deleteOptions;
-        const plumbed = entry.remote.get("plumbed");
+        const plumbed = entry.remote!.get("plumbed");
 
-        this.roomStore.upsertEntry(entry);
+        await this.roomStore.upsertEntry(entry);
         if (options.ghostsLeave) {
             for (const member of channel.members.array()) {
                 try {
@@ -314,7 +313,7 @@ export class ChannelSyncroniser {
         if (plumbed !== true) {
             if (options.unsetRoomAlias) {
                 try {
-                    const alias = `#_${entry.remote.roomId}:${this.config.bridge.domain}`;
+                    const alias = `#_${entry.remote!.roomId}:${this.config.bridge.domain}`;
                     const canonicalAlias = await intent.getClient().getStateEvent(roomId, "m.room.canonical_alias");
                     if (canonicalAlias.alias === alias) {
                         await intent.getClient().sendStateEvent(roomId, "m.room.canonical_alias", {});
diff --git a/src/db/connector.ts b/src/db/connector.ts
index fc6b74fddf1ef2c721d80b07405ba51fabc26630..d247db60dcc914e95f3efb65ba3ac9b8ac84f200 100644
--- a/src/db/connector.ts
+++ b/src/db/connector.ts
@@ -14,12 +14,14 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+type SQLTYPES = number | boolean | string | null;
+
 export interface ISqlCommandParameters {
-    [paramKey: string]: number | boolean | string | Promise<number | boolean | string>;
+    [paramKey: string]: SQLTYPES | Promise<SQLTYPES>;
 }
 
 export interface ISqlRow {
-    [key: string]: number | boolean | string;
+    [key: string]: SQLTYPES;
 }
 
 export interface IDatabaseConnector {
diff --git a/src/db/postgres.ts b/src/db/postgres.ts
index 834f841b00cd19042b7e1223c180f408e340e387..aa9116d9dc94fe0585c701b79901e334796a10bd 100644
--- a/src/db/postgres.ts
+++ b/src/db/postgres.ts
@@ -17,7 +17,7 @@ limitations under the License.
 import * as pgPromise from "pg-promise";
 import { Log } from "../log";
 import { IDatabaseConnector, ISqlCommandParameters, ISqlRow } from "./connector";
-const log = new Log("SQLite3");
+const log = new Log("Postgres");
 
 const pgp: pgPromise.IMain = pgPromise({
     // Initialization Options
diff --git a/src/db/roomstore.ts b/src/db/roomstore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..054a468cf871da86fc481ba28cd893d98a8fa5ab
--- /dev/null
+++ b/src/db/roomstore.ts
@@ -0,0 +1,368 @@
+/*
+Copyright 2019 matrix-appservice-discord
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+import { Log } from "../log";
+import { IDatabaseConnector } from "./connector";
+
+import * as uuid from "uuid/v4";
+
+const log = new Log("DbRoomStore");
+
+/**
+ * A RoomStore compatible with
+ * https://github.com/matrix-org/matrix-appservice-bridge/blob/master/lib/components/room-bridge-store.js
+ * that accesses the database instead.
+ */
+
+interface IRemoteRoomData extends IRemoteRoomDataLazy {
+    discord_guild: string;
+    discord_channel: string;
+}
+
+interface IRemoteRoomDataLazy  {
+    discord_guild?: string;
+    discord_channel?: string;
+    discord_name?: string|null;
+    discord_topic?: string|null;
+    discord_type?: string|null;
+    discord_iconurl?: string|null;
+    discord_iconurl_mxc?: string|null;
+    update_name?: number|boolean|null;
+    update_topic?: number|boolean|null;
+    update_icon?: number|boolean|null;
+    plumbed?: number|boolean|null;
+}
+
+export class RemoteStoreRoom {
+    public data: IRemoteRoomData;
+    constructor(public readonly roomId: string, data: IRemoteRoomData) {
+        for (const k of ["discord_guild", "discord_channel", "discord_name",
+        "discord_topic", "discord_iconurl", "discord_iconurl_mxc", "discord_type"]) {
+            data[k] = typeof(data[k]) === "number" ? String(data[k]) : data[k] || null;
+        }
+        for (const k of ["update_name", "update_topic", "update_icon", "plumbed"]) {
+            data[k] = Number(data[k]) || 0;
+        }
+        this.data = data;
+    }
+
+    public getId() {
+        return this.roomId;
+    }
+
+    public get(key: string): string|boolean|null {
+        return this.data[key];
+    }
+
+    public set(key: string, value: string|boolean|null) {
+        this.data[key] = typeof(value) === "boolean" ? Number(value) : value;
+    }
+}
+
+export class MatrixStoreRoom {
+    constructor(public readonly roomId: string) { }
+
+    public getId() {
+        return this.roomId;
+    }
+}
+
+export interface IRoomStoreEntry {
+    id: string;
+    matrix: MatrixStoreRoom|null;
+    remote: RemoteStoreRoom|null;
+}
+
+const ENTRY_CACHE_LIMETIME = 30000;
+
+// XXX: This only implements functions used in the bridge at the moment.
+export class DbRoomStore {
+
+    private entriesMatrixIdCache: Map<string, {e: IRoomStoreEntry[], ts: number}>;
+
+    constructor(private db: IDatabaseConnector) {
+        this.entriesMatrixIdCache = new Map();
+    }
+
+    public async upsertEntry(entry: IRoomStoreEntry) {
+        const promises: Promise<void>[] = [];
+
+        const row = (await this.db.Get("SELECT * FROM room_entries WHERE id = $id", {id: entry.id})) || {};
+
+        if (!row.id) {
+            // Doesn't exist at all, create the room_entries row.
+            const values = {
+                id: entry.id,
+                matrix: entry.matrix ? entry.matrix.roomId : null,
+                remote: entry.remote ? entry.remote.roomId : null,
+            };
+            try {
+                await this.db.Run(`INSERT INTO room_entries VALUES ($id, $matrix, $remote)`, values);
+                log.verbose("Created new entry " + entry.id);
+            } catch (ex) {
+                log.error("Failed to insert room entry", ex);
+                throw Error("Failed to insert room entry");
+            }
+        }
+
+        const matrixId = entry.matrix ? entry.matrix.roomId : null;
+        const remoteId = entry.remote ? entry.remote.roomId : null;
+        const mxIdDifferent = matrixId !== row.matrix_id;
+        const rmIdDifferent = remoteId !== row.remote_id;
+        // Did the room ids change?
+        if (mxIdDifferent || rmIdDifferent) {
+            if (matrixId) {
+                this.entriesMatrixIdCache.delete(matrixId);
+            }
+            const items: string[] = [];
+
+            if (mxIdDifferent) {
+                items.push("matrix_id = $matrixId");
+            }
+
+            if (rmIdDifferent) {
+                items.push("remote_id = $remoteId");
+            }
+
+            await this.db.Run(`UPDATE room_entries SET ${items.join(", ")} WHERE id = $id`,
+                {
+                    id: entry.id,
+                    matrixId: matrixId as string|null,
+                    remoteId: remoteId as string|null,
+                },
+            );
+        }
+
+        // Matrix room doesn't store any data.
+        if (entry.remote) {
+            await this.upsertRoom(entry.remote);
+        }
+    }
+
+    public async getEntriesByMatrixId(matrixId: string): Promise<IRoomStoreEntry[]> {
+        const cached = this.entriesMatrixIdCache.get(matrixId);
+        if (cached && cached.ts + ENTRY_CACHE_LIMETIME > Date.now()) {
+            return cached.e;
+        }
+        const entries = await this.db.All(
+            "SELECT * FROM room_entries WHERE matrix_id = $id", {id: matrixId},
+        );
+        const res: IRoomStoreEntry[] = [];
+        for (const entry of entries) {
+            let remote: RemoteStoreRoom|null = null;
+            if (entry.remote_id) {
+                const remoteId = entry.remote_id as string;
+                const row = await this.db.Get(
+                    "SELECT * FROM remote_room_data WHERE room_id = $remoteId",
+                    {remoteId},
+                );
+                if (row) {
+                    // tslint:disable-next-line no-any
+                    remote = new RemoteStoreRoom(remoteId, row as any);
+                }
+            }
+            if (remote) {
+                // Only push rooms with a remote
+                res.push({
+                    id: (entry.id as string),
+                    matrix: new MatrixStoreRoom(matrixId),
+                    remote,
+                });
+            }
+        }
+        if (res.length > 0) {
+            this.entriesMatrixIdCache.set(matrixId, {e: res, ts: Date.now()});
+        }
+        return res;
+    }
+
+    public async getEntriesByMatrixIds(matrixIds: string[]): Promise<IRoomStoreEntry[]> {
+        const mxIdMap = { };
+        matrixIds.forEach((mxId, i) => mxIdMap[i] = mxId);
+        const sql = `SELECT * FROM room_entries WHERE matrix_id IN (${matrixIds.map((_, id) => `\$${id}`).join(", ")})`;
+        const entries = await this.db.All(sql, mxIdMap);
+        const res: IRoomStoreEntry[] = [];
+        for (const entry of entries) {
+            let remote: RemoteStoreRoom|null = null;
+            const matrixId = entry.matrix_id as string || "";
+            const remoteId = entry.remote_id as string;
+            if (remoteId) {
+                const row = await this.db.Get(
+                    "SELECT * FROM remote_room_data WHERE room_id = $rid",
+                    {rid: remoteId},
+                );
+                if (row) {
+                    // tslint:disable-next-line no-any
+                    remote = new RemoteStoreRoom(remoteId, row as any);
+                }
+            }
+            if (remote) {
+                // Only push rooms with a remote
+                res.push({
+                    id: (entry.id as string),
+                    matrix: matrixId ? new MatrixStoreRoom(matrixId) : null,
+                    remote,
+                });
+            }
+        }
+        return res;
+    }
+
+    public async linkRooms(matrixRoom: MatrixStoreRoom, remoteRoom: RemoteStoreRoom) {
+        await this.upsertRoom(remoteRoom);
+
+        const values = {
+            id: uuid(),
+            matrix: matrixRoom.roomId,
+            remote: remoteRoom.roomId,
+        };
+
+        try {
+            await this.db.Run(`INSERT INTO room_entries VALUES ($id, $matrix, $remote)`, values);
+            log.verbose("Created new entry " + values.id);
+        } catch (ex) {
+            log.error("Failed to insert room entry", ex);
+            throw Error("Failed to insert room entry");
+        }
+    }
+
+    public async setMatrixRoom(matrixRoom: MatrixStoreRoom) {
+        // This no-ops, because we don't store anything interesting.
+    }
+
+    public async getEntriesByRemoteRoomData(data: IRemoteRoomDataLazy): Promise<IRoomStoreEntry[]> {
+        const whereClaues = Object.keys(data).map((key) => {
+            return `${key} = $${key}`;
+        }).join(" AND ");
+        const sql = `
+        SELECT * FROM remote_room_data
+        INNER JOIN room_entries ON remote_room_data.room_id = room_entries.remote_id
+        WHERE ${whereClaues}`;
+        // tslint:disable-next-line no-any
+        return (await this.db.All(sql, data as any)).map((row) => {
+            const id = row.id as string;
+            const matrixId = row.matrix_id;
+            const remoteId = row.room_id;
+            return {
+                id,
+                matrix: matrixId ? new MatrixStoreRoom(matrixId as string) : null,
+                // tslint:disable-next-line no-any
+                remote: matrixId ? new RemoteStoreRoom(remoteId as string, row as any) : null,
+            };
+        });
+    }
+
+    public async removeEntriesByRemoteRoomId(remoteId: string) {
+        await this.db.Run(`DELETE FROM room_entries WHERE remote_id = $remoteId`, {remoteId});
+        await this.db.Run(`DELETE FROM remote_room_data WHERE room_id = $remoteId`, {remoteId});
+    }
+
+    public async removeEntriesByMatrixRoomId(matrixId: string) {
+        const entries = (await this.db.All(`SELECT * room_entries WHERE matrix_id = $matrixId`, {matrixId})) || [];
+        entries.map((entry) => {
+            if (entry.remote_id) {
+                return this.removeEntriesByRemoteRoomId(entry.remote_id as string);
+            } else if (entry.matrix_id) {
+                return this.db.Run(`DELETE FROM room_entries WHERE matrix_id = $matrixId`, {matrixId: entry.matrix_id});
+            }
+        });
+    }
+
+    private async upsertRoom(room: RemoteStoreRoom) {
+        if (!room.data) {
+            throw new Error("Tried to upsert a room with undefined data");
+        }
+
+        const existingRow = await this.db.Get(
+            "SELECT * FROM remote_room_data WHERE room_id = $id",
+            {id: room.roomId},
+        );
+
+        const data = {
+            discord_channel:     room.data.discord_channel,
+            discord_guild:       room.data.discord_guild,
+            discord_iconurl:     room.data.discord_iconurl,
+            discord_iconurl_mxc: room.data.discord_iconurl_mxc,
+            discord_name:        room.data.discord_name,
+            discord_topic:       room.data.discord_topic,
+            discord_type:        room.data.discord_type,
+            plumbed:             room.data.plumbed || 0,
+            update_icon:         room.data.update_icon || 0,
+            update_name:         room.data.update_name || 0,
+            update_topic:        room.data.update_topic || 0,
+        } as IRemoteRoomData;
+
+        if (!existingRow) {
+            // Insert new data.
+            await this.db.Run(
+            `INSERT INTO remote_room_data VALUES (
+                $id,
+                $discord_guild,
+                $discord_channel,
+                $discord_name,
+                $discord_topic,
+                $discord_type,
+                $discord_iconurl,
+                $discord_iconurl_mxc,
+                $update_name,
+                $update_topic,
+                $update_icon,
+                $plumbed
+            )
+            `,
+            {
+                id: room.roomId,
+                // tslint:disable-next-line no-any
+                ...data as any,
+            });
+            return;
+        }
+
+        const keysToUpdate = { } as IRemoteRoomDataLazy;
+
+        // New keys
+        Object.keys(room.data).filter(
+            (k: string) => existingRow[k] === null).forEach((key) => {
+                keysToUpdate[key] = room.data[key];
+        });
+
+        // Updated keys
+        Object.keys(room.data).filter(
+            (k: string) => existingRow[k] !== room.data[k]).forEach((key) => {
+            keysToUpdate[key] = room.data[key];
+        });
+
+        if (Object.keys(keysToUpdate).length === 0) {
+            return;
+        }
+
+        const setStatement = Object.keys(keysToUpdate).map((k) => {
+            return `${k} = $${k}`;
+        }).join(", ");
+
+        try {
+            await this.db.Run(`UPDATE remote_room_data SET ${setStatement} WHERE room_id = $id`,
+            {
+                id: room.roomId,
+                // tslint:disable-next-line no-any
+                ...keysToUpdate as any,
+            });
+            log.verbose("Upserted room " + room.roomId);
+        } catch (ex) {
+            log.error("Failed to upsert room", ex);
+            throw Error("Failed to upsert room");
+        }
+    }
+}
diff --git a/src/db/schema/dbschema.ts b/src/db/schema/dbschema.ts
index 9f215f1b6d6aa32783030406660cb3dbf36dfed9..132e7c003155f0175ddb8b8891e78d863273baea 100644
--- a/src/db/schema/dbschema.ts
+++ b/src/db/schema/dbschema.ts
@@ -15,6 +15,7 @@ limitations under the License.
 */
 
 import { DiscordStore } from "../../store";
+import { DiscordBridgeConfigDatabase } from "../../config";
 export interface IDbSchema {
     description: string;
     run(store: DiscordStore): Promise<null|void|Error|Error[]>;
diff --git a/src/db/schema/v8.ts b/src/db/schema/v8.ts
new file mode 100644
index 0000000000000000000000000000000000000000..96890f97322fb1b7b407a93d9c09d6ddc977e525
--- /dev/null
+++ b/src/db/schema/v8.ts
@@ -0,0 +1,96 @@
+/*
+Copyright 2019 matrix-appservice-discord
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import {IDbSchema} from "./dbschema";
+import {DiscordStore} from "../../store";
+import { Log } from "../../log";
+import {
+    RoomStore,
+} from "matrix-appservice-bridge";
+import { RemoteStoreRoom, MatrixStoreRoom } from "../roomstore";
+const log = new Log("SchemaV8");
+
+export class Schema implements IDbSchema {
+    public description = "create room store tables";
+
+    constructor(private roomStore: RoomStore|null) {
+
+    }
+
+    public async run(store: DiscordStore): Promise<void> {
+        await store.create_table(`
+            CREATE TABLE remote_room_data (
+                room_id TEXT NOT NULL,
+                discord_guild TEXT NOT NULL,
+                discord_channel TEXT NOT NULL,
+                discord_name TEXT DEFAULT NULL,
+                discord_topic TEXT DEFAULT NULL,
+                discord_type TEXT DEFAULT NULL,
+                discord_iconurl TEXT DEFAULT NULL,
+                discord_iconurl_mxc TEXT DEFAULT NULL,
+                update_name NUMERIC DEFAULT 0,
+                update_topic NUMERIC DEFAULT 0,
+                update_icon NUMERIC DEFAULT 0,
+                plumbed NUMERIC DEFAULT 0,
+                PRIMARY KEY(room_id)
+        );`, "remote_room_data");
+
+        await store.create_table(`
+            CREATE TABLE room_entries (
+                id TEXT NOT NULL,
+                matrix_id TEXT,
+                remote_id TEXT,
+                PRIMARY KEY(id)
+        );`, "room_entries");
+
+        if (this.roomStore === null) {
+            log.warn("Not migrating rooms from room store, room store is null");
+            return;
+        }
+        log.warn("Migrating rooms from roomstore, this may take a while...");
+        const rooms = await this.roomStore.select({});
+        log.info(`Found ${rooms.length} rooms in the DB`);
+        // Matrix room only entrys are useless.
+        const entrys = rooms.filter((r) => r.remote);
+        log.info(`Filtered out rooms without remotes. Have ${entrys.length} entries`);
+        let migrated = 0;
+        for (const e of entrys) {
+            const matrix = new MatrixStoreRoom(e.matrix_id);
+            try {
+                const remote = new RemoteStoreRoom(e.remote_id, e.remote);
+                await store.roomStore.linkRooms(matrix, remote);
+                log.info(`Migrated ${matrix.roomId}`);
+                migrated++;
+            } catch (ex) {
+                log.error(`Failed to link ${matrix.roomId}: `, ex);
+            }
+        }
+        if (migrated !== entrys.length) {
+            log.error(`Didn't migrate all rooms, ${entrys.length - migrated} failed to be migrated.`);
+        } else {
+            log.info("Migrated all rooms successfully");
+        }
+    }
+
+    public async rollBack(store: DiscordStore): Promise<void> {
+        await store.db.Run(
+            `DROP TABLE IF EXISTS remote_room_data;`,
+        );
+        await store.db.Run(
+            `DROP TABLE IF EXISTS room_entries;`,
+        );
+    }
+}
diff --git a/src/discordas.ts b/src/discordas.ts
index bf15c03f0912775f2c876a42e9e80b80fdb251ad..56a8f7d24bbdf280cb368ea753bef177faad115b 100644
--- a/src/discordas.ts
+++ b/src/discordas.ts
@@ -20,9 +20,7 @@ import * as yaml from "js-yaml";
 import * as fs from "fs";
 import { DiscordBridgeConfig } from "./config";
 import { DiscordBot } from "./bot";
-import { MatrixRoomHandler } from "./matrixroomhandler";
 import { DiscordStore } from "./store";
-import { Provisioner } from "./provisioner";
 import { Log } from "./log";
 import "source-map-support/register";
 
@@ -77,6 +75,7 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
         token: registration.as_token,
         url: config.bridge.homeserverUrl,
     });
+    const store = new DiscordStore(config.database);
 
     const callbacks: { [id: string]: callbackFn; } = {};
 
@@ -94,11 +93,25 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
                     return await callbacks.onAliasQuery(alias, aliasLocalpart);
                 } catch (err) { log.error("Exception thrown while handling \"onAliasQuery\" event", err); }
             },
-            onEvent: async (request, context) => {
+            onEvent: async (request) => {
                 try {
+                    // Build our own context.
+                    if (!store.roomStore) {
+                        log.warn("Discord store not ready yet, dropping message");
+                        return;
+                    }
+                    const roomId = request.getData().room_id;
+                    let context = {};
+                    if (roomId) {
+                        const entries  = await store.roomStore.getEntriesByMatrixId(request.getData().room_id);
+                        context = {
+                            rooms: entries[0],
+                        };
+                    }
                     await request.outcomeFrom(Bluebird.resolve(callbacks.onEvent(request, context)));
                 } catch (err) {
                     log.error("Exception thrown while handling \"onEvent\" event", err);
+                    await request.outcomeFrom(Bluebird.reject("Failed to handle"));
                 }
             },
             onLog: (line, isError) => {
@@ -112,6 +125,7 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
                 }
             },
         },
+        disableContext: true,
         domain: config.bridge.domain,
         homeserverUrl: config.bridge.homeserverUrl,
         intentOptions: {
@@ -119,17 +133,33 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
                 dontJoin: true, // handled manually
             },
         },
+        // To avoid out of order message sending.
         queue: {
             perRequest: true,
             type: "per_room",
         },
         registration,
+        // These must be kept for a while yet since we use them for migrations.
         roomStore: config.database.roomStorePath,
         userStore: config.database.userStorePath,
-        // To avoid out of order message sending.
     });
-    // Warn and deprecate old config options.
-    const discordbot = new DiscordBot(botUserId, config, bridge);
+
+    if (config.database.roomStorePath) {
+        log.warn("[DEPRECATED] The room store is now part of the SQL database."
+               + "The config option roomStorePath no longer has any use.");
+    }
+
+    await bridge.run(port, config);
+    log.info(`Started listening on port ${port}`);
+
+    try {
+        await store.init(undefined, bridge.getRoomStore());
+    } catch (ex) {
+        log.error("Failed to init database. Exiting.", ex);
+        process.exit(1);
+    }
+
+    const discordbot = new DiscordBot(botUserId, config, bridge, store);
     const roomhandler = discordbot.RoomHandler;
 
     try {
@@ -144,19 +174,18 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
         process.exit(1);
     }
 
-    log.info("Initing bridge.");
+    log.info("Initing bridge");
 
     try {
         log.info("Initing store.");
-        await bridge.run(port, config);
         await discordbot.init();
         log.info(`Started listening on port ${port}.`);
         log.info("Initing bot.");
         await discordbot.run();
-        log.info("Discordbot started successfully.");
+        log.info("Discordbot started successfully");
     } catch (err) {
         log.error(err);
-        log.error("Failure during startup. Exiting.");
+        log.error("Failure during startup. Exiting");
         process.exit(1);
     }
 }
diff --git a/src/matrixroomhandler.ts b/src/matrixroomhandler.ts
index ef1e84c5c3746b1646fab56a3625d71f88d126f7..032a77fb8ee1b556b363953ad15dc6aaf5e299ce 100644
--- a/src/matrixroomhandler.ts
+++ b/src/matrixroomhandler.ts
@@ -35,6 +35,7 @@ import { Provisioner } from "./provisioner";
 import { Log } from "./log";
 const log = new Log("MatrixRoomHandler");
 import { IMatrixEvent } from "./matrixtypes";
+import { DbRoomStore, MatrixStoreRoom, RemoteStoreRoom } from "./db/roomstore";
 
 const ICON_URL = "https://matrix.org/_matrix/media/r0/download/matrix.org/mlxoESwIsTbJrfXyAAogrNxA";
 /* tslint:disable:no-magic-numbers */
@@ -65,7 +66,8 @@ export class MatrixRoomHandler {
         private discord: DiscordBot,
         private config: DiscordBridgeConfig,
         private provisioner: Provisioner,
-        private bridge: Bridge) {
+        private bridge: Bridge,
+        private roomStore: DbRoomStore) {
         this.botUserId = this.discord.BotUserId;
         this.botJoinedRooms = new Set();
     }
@@ -85,6 +87,19 @@ export class MatrixRoomHandler {
         log.verbose(`Got OnAliasQueried for ${alias} ${roomId}`);
         let channel: Discord.GuildChannel;
         try {
+            // We previously stored the room as an alias.
+            const entry = (await this.roomStore.getEntriesByMatrixId(alias))[0];
+            if (!entry) {
+                throw new Error("Entry was not found");
+            }
+            // Remove the old entry
+            await this.roomStore.removeEntriesByMatrixRoomId(
+                entry.matrix!.roomId,
+            );
+            await this.roomStore.linkRooms(
+                new MatrixStoreRoom(roomId),
+                entry.remote!,
+            );
             channel = await this.discord.GetChannelFromRoomId(roomId) as Discord.GuildChannel;
         } catch (err) {
             log.error(`Cannot find discord channel for ${alias} ${roomId}`, err);
@@ -124,7 +139,7 @@ export class MatrixRoomHandler {
         await Promise.all(promiseList);
     }
 
-    public async OnEvent(request, context): Promise<void> {
+    public async OnEvent(request, context: BridgeContext): Promise<void> {
         const event = request.getData() as IMatrixEvent;
         if (event.unsigned.age > AGE_LIMIT) {
             log.warn(`Skipping event due to age ${event.unsigned.age} > ${AGE_LIMIT}`);
@@ -204,7 +219,7 @@ export class MatrixRoomHandler {
         );
         await sendPromise;
         await intent.leave(roomId);
-        await this.bridge.getRoomStore().removeEntriesByMatrixRoomId(roomId);
+        await this.roomStore.removeEntriesByMatrixRoomId(roomId);
     }
 
     public async HandleInvite(event: IMatrixEvent) {
@@ -322,7 +337,7 @@ export class MatrixRoomHandler {
                 });
 
                 await this.provisioner.AskBridgePermission(channel, event.sender);
-                this.provisioner.BridgeMatrixRoom(channel, event.room_id);
+                await this.provisioner.BridgeMatrixRoom(channel, event.room_id);
                 return this.bridge.getIntent().sendMessage(event.room_id, {
                     body: "I have bridged this room to your channel",
                     msgtype: "m.notice",
@@ -399,7 +414,7 @@ export class MatrixRoomHandler {
         try {
             const result = await this.discord.LookupRoom(srvChanPair[0], srvChanPair[1]);
             log.info("Creating #", aliasLocalpart);
-            return this.createMatrixRoom(result.channel, aliasLocalpart);
+            return this.createMatrixRoom(result.channel, alias, aliasLocalpart);
         } catch (err) {
             log.error(`Couldn't find discord room '${aliasLocalpart}'.`, err);
         }
@@ -616,14 +631,16 @@ export class MatrixRoomHandler {
         }
     }
 
-    private createMatrixRoom(channel: Discord.TextChannel, alias: string): ProvisionedRoom {
-        const remote = new RemoteRoom(`discord_${channel.guild.id}_${channel.id}`);
-        remote.set("discord_type", "text");
-        remote.set("discord_guild", channel.guild.id);
-        remote.set("discord_channel", channel.id);
-        remote.set("update_name", true);
-        remote.set("update_topic", true);
-        remote.set("update_icon", true);
+    private async createMatrixRoom(channel: Discord.TextChannel,
+                                   alias: string, aliasLocalpart: string): ProvisionedRoom {
+        const remote = new RemoteStoreRoom(`discord_${channel.guild.id}_${channel.id}`, {
+            discord_channel: channel.id,
+            discord_guild: channel.guild.id,
+            discord_type: "text",
+            update_icon: 1,
+            update_name: 1,
+            update_topic: 1,
+        });
         const creationOpts = {
             initial_state: [
                 {
@@ -634,12 +651,16 @@ export class MatrixRoomHandler {
                     type: "m.room.join_rules",
                 },
             ],
-            room_alias_name: alias,
+            room_alias_name: aliasLocalpart,
             visibility: this.config.room.defaultVisibility,
         };
+        // We need to tempoarily store this until we know the room_id.
+        await this.roomStore.linkRooms(
+            new MatrixStoreRoom(alias),
+            remote,
+        );
         return {
             creationOpts,
-            remote,
         } as ProvisionedRoom;
     }
 
diff --git a/src/provisioner.ts b/src/provisioner.ts
index 0ac84e407ce0e93dec3a4e3407d364ab021867e8..9a871a1750fae09eb2f3592b24d1ba1488ccc55d 100644
--- a/src/provisioner.ts
+++ b/src/provisioner.ts
@@ -20,6 +20,7 @@ import {
     MatrixRoom,
 } from "matrix-appservice-bridge";
 import * as Discord from "discord.js";
+import { DbRoomStore } from "./db/roomstore";
 
 const PERMISSION_REQUEST_TIMEOUT = 300000; // 5 minutes
 
@@ -27,9 +28,9 @@ export class Provisioner {
 
     private pendingRequests: Map<string, (approved: boolean) => void> = new Map(); // [channelId]: resolver fn
 
-    constructor(private bridge: Bridge) { }
+    constructor(private roomStore: DbRoomStore) { }
 
-    public BridgeMatrixRoom(channel: Discord.TextChannel, roomId: string) {
+    public async BridgeMatrixRoom(channel: Discord.TextChannel, roomId: string) {
         const remote = new RemoteRoom(`discord_${channel.guild.id}_${channel.id}_bridged`);
         remote.set("discord_type", "text");
         remote.set("discord_guild", channel.guild.id);
@@ -37,12 +38,11 @@ export class Provisioner {
         remote.set("plumbed", true);
 
         const local = new MatrixRoom(roomId);
-        this.bridge.getRoomStore().linkRooms(local, remote);
-        this.bridge.getRoomStore().setMatrixRoom(local); // Needs to be done after linking
+        return this.roomStore.linkRooms(local, remote);
     }
 
-    public UnbridgeRoom(remoteRoom: RemoteRoom) {
-        return this.bridge.getRoomStore().removeEntriesByRemoteRoomId(remoteRoom.getId());
+    public async UnbridgeRoom(remoteRoom: RemoteRoom) {
+        return this.roomStore.removeEntriesByRemoteRoomId(remoteRoom.getId());
     }
 
     public async AskBridgePermission(
diff --git a/src/store.ts b/src/store.ts
index 62787dddc60ce7bb179dbad96148cf76e9be07e0..7397b3b6c765066f90a2518c5fc368ccc8af53ea 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -18,24 +18,26 @@ import * as fs from "fs";
 import { IDbSchema } from "./db/schema/dbschema";
 import { IDbData} from "./db/dbdatainterface";
 import { SQLite3 } from "./db/sqlite3";
-export const CURRENT_SCHEMA = 7;
+export const CURRENT_SCHEMA = 8;
 
 import { Log } from "./log";
 import { DiscordBridgeConfigDatabase } from "./config";
 import { Postgres } from "./db/postgres";
 import { IDatabaseConnector } from "./db/connector";
+import { DbRoomStore } from "./db/roomstore";
+import {
+    RoomStore,
+} from "matrix-appservice-bridge";
 const log = new Log("DiscordStore");
 /**
  * Stores data for specific users and data not specific to rooms.
  */
 export class DiscordStore {
-    /**
-     * @param  {string} filepath Location of the SQLite database file.
-     */
     public db: IDatabaseConnector;
     private version: number;
     private config: DiscordBridgeConfigDatabase;
-    constructor(private configOrFile: DiscordBridgeConfigDatabase|string) {
+    private pRoomStore: DbRoomStore;
+    constructor(configOrFile: DiscordBridgeConfigDatabase|string) {
         if (typeof(configOrFile) === "string") {
             this.config = new DiscordBridgeConfigDatabase();
             this.config.filename = configOrFile;
@@ -45,6 +47,10 @@ export class DiscordStore {
         this.version = 0;
     }
 
+    get roomStore() {
+        return this.pRoomStore;
+    }
+
     public async backup_database(): Promise<void|{}> {
         if (this.config.filename == null) {
             log.warn("Backups not supported on non-sqlite connector");
@@ -80,15 +86,22 @@ export class DiscordStore {
     /**
      * Checks the database has all the tables needed.
      */
-    public async init(overrideSchema: number = 0): Promise<void> {
+    public async init(overrideSchema: number = 0, roomStore: RoomStore = null): Promise<void> {
+        const SCHEMA_ROOM_STORE_REQUIRED = 8;
         log.info("Starting DB Init");
         await this.open_database();
         let version = await this.getSchemaVersion();
         const targetSchema = overrideSchema || CURRENT_SCHEMA;
+        log.info(`Database schema version is ${version}, latest version is ${targetSchema}`);
         while (version < targetSchema) {
             version++;
             const schemaClass = require(`./db/schema/v${version}.js`).Schema;
-            const schema = (new schemaClass() as IDbSchema);
+            let schema: IDbSchema;
+            if (version === SCHEMA_ROOM_STORE_REQUIRED) { // 8 requires access to the roomstore.
+                schema = (new schemaClass(roomStore) as IDbSchema);
+            } else {
+                schema = (new schemaClass() as IDbSchema);
+            }
             log.info(`Updating database to v${version}, "${schema.description}"`);
             try {
                 await schema.run(this);
@@ -327,6 +340,7 @@ export class DiscordStore {
         }
         try {
             this.db.Open();
+            this.pRoomStore = new DbRoomStore(this.db);
         } catch (ex) {
             log.error("Error opening database:", ex);
             throw new Error("Couldn't open database. The appservice won't be able to continue.");
diff --git a/test/db/test_roomstore.ts b/test/db/test_roomstore.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fea079ebd855053468ffcb166036c80cc3b04b50
--- /dev/null
+++ b/test/db/test_roomstore.ts
@@ -0,0 +1,267 @@
+/*
+Copyright 2019 matrix-appservice-discord
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import * as Chai from "chai";
+// import * as Proxyquire from "proxyquire";
+import { DiscordStore, CURRENT_SCHEMA } from "../../src/store";
+import { RemoteStoreRoom, MatrixStoreRoom } from "../../src/db/roomstore";
+
+// we are a test file and thus need those
+/* tslint:disable: no-any no-unused-expression */
+
+const expect = Chai.expect;
+
+// const assert = Chai.assert;
+let store: DiscordStore;
+describe("RoomStore", () => {
+    before(async () => {
+        store = new DiscordStore(":memory:");
+        await store.init();
+    });
+    describe("upsertEntry|getEntriesByMatrixId", () => {
+        it("will create a new entry", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test1",
+                matrix: new MatrixStoreRoom("!abc:def.com"),
+                remote: new RemoteStoreRoom("123456_789", {discord_guild: "123", discord_channel: "456"}),
+            });
+            const entry = (await store.roomStore.getEntriesByMatrixId("!abc:def.com"))[0];
+            expect(entry.id).to.equal("test1");
+            expect(entry.matrix!.roomId).to.equal("!abc:def.com");
+            expect(entry.remote!.roomId).to.equal("123456_789");
+            expect(entry.remote!.get("discord_guild")).to.equal("123");
+            expect(entry.remote!.get("discord_channel")).to.equal("456");
+        });
+        it("will update an existing entry's rooms", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test2",
+                matrix: new MatrixStoreRoom("test2_m"),
+                remote: new RemoteStoreRoom("test2_r", {discord_guild: "123", discord_channel: "456"}),
+            });
+            await store.roomStore.upsertEntry({
+                id: "test2",
+                matrix: new MatrixStoreRoom("test2_2m"),
+                remote: new RemoteStoreRoom("test2_2r", {discord_guild: "555", discord_channel: "999"}),
+            });
+            const entry = (await store.roomStore.getEntriesByMatrixId("test2_2m"))[0];
+            expect(entry.id).to.equal("test2");
+            expect(entry.matrix!.roomId).to.equal("test2_2m");
+            expect(entry.remote!.roomId).to.equal("test2_2r");
+            expect(entry.remote!.get("discord_guild")).to.equal("555");
+            expect(entry.remote!.get("discord_channel")).to.equal("999");
+        });
+        it("will add new data to an existing entry", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test3",
+                matrix: new MatrixStoreRoom("test3_m"),
+                remote: new RemoteStoreRoom("test3_r", {discord_guild: "123", discord_channel: "456"}),
+            });
+            await store.roomStore.upsertEntry({
+                id: "test3",
+                matrix: new MatrixStoreRoom("test3_m"),
+                remote: new RemoteStoreRoom("test3_r", {discord_guild: "123", discord_channel: "456", update_topic: 1}),
+            });
+            const entry = (await store.roomStore.getEntriesByMatrixId("test3_m"))[0];
+            expect(entry.id).to.equal("test3");
+            expect(entry.matrix!.roomId).to.equal("test3_m");
+            expect(entry.remote!.roomId).to.equal("test3_r");
+            expect(entry.remote!.get("update_topic")).to.equal(1);
+        });
+        it("will replace data on an existing entry", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test3.1",
+                matrix: new MatrixStoreRoom("test3.1_m"),
+                remote: new RemoteStoreRoom("test3.1_r", {discord_guild: "123", discord_channel: "456"}),
+            });
+            await store.roomStore.upsertEntry({
+                id: "test3.1",
+                matrix: new MatrixStoreRoom("test3.1_m"),
+                remote: new RemoteStoreRoom("test3.1_r", {discord_guild: "-100", discord_channel: "seventythousand"}),
+            });
+            const entry = (await store.roomStore.getEntriesByMatrixId("test3.1_m"))[0];
+            expect(entry.id).to.equal("test3.1");
+            expect(entry.matrix!.roomId).to.equal("test3.1_m");
+            expect(entry.remote!.roomId).to.equal("test3.1_r");
+            expect(entry.remote!.get("discord_guild")).to.equal("-100");
+            expect(entry.remote!.get("discord_channel")).to.equal("seventythousand");
+        });
+        it("will delete data on an existing entry", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test3.2",
+                matrix: new MatrixStoreRoom("test3.2_m"),
+                remote: new RemoteStoreRoom("test3.2_r", {
+                    discord_channel: "456", discord_guild: "123",  update_icon: true,
+                }),
+            });
+            await store.roomStore.upsertEntry({
+                id: "test3.2",
+                matrix: new MatrixStoreRoom("test3.2_m"),
+                remote: new RemoteStoreRoom("test3.2_r", {discord_guild: "123", discord_channel: "456"}),
+            });
+            const entry = (await store.roomStore.getEntriesByMatrixId("test3.2_m"))[0];
+            expect(entry.id).to.equal("test3.2");
+            expect(entry.matrix!.roomId).to.equal("test3.2_m");
+            expect(entry.remote!.roomId).to.equal("test3.2_r");
+            expect(entry.remote!.get("update_icon")).to.be.eq(0);
+        });
+    });
+    describe("getEntriesByMatrixIds", () => {
+        it("will get multiple entries", async () => {
+            const EXPECTED_ROOMS = 2;
+            await store.roomStore.upsertEntry({
+                id: "test4_1",
+                matrix: new MatrixStoreRoom("!test_mOne:eggs.com"),
+                remote: new RemoteStoreRoom("test4_r", {discord_guild: "five", discord_channel: "five"}),
+            });
+            await store.roomStore.upsertEntry({
+                id: "test4_2",
+                matrix: new MatrixStoreRoom("!test_mTwo:eggs.com"),
+                remote: new RemoteStoreRoom("test4_r", {discord_guild: "nine", discord_channel: "nine"}),
+            });
+            const entries = await store.roomStore.getEntriesByMatrixIds(["!test_mOne:eggs.com", "!test_mTwo:eggs.com"]);
+            expect(entries).to.have.lengthOf(EXPECTED_ROOMS);
+            expect(entries[0].id).to.equal("test4_1");
+            expect(entries[0].matrix!.roomId).to.equal("!test_mOne:eggs.com");
+            expect(entries[1].id).to.equal("test4_2");
+            expect(entries[1].matrix!.roomId).to.equal("!test_mTwo:eggs.com");
+        });
+    });
+    describe("linkRooms", () => {
+        it("will link a room", async () => {
+            const matrix = new MatrixStoreRoom("test5_m");
+            const remote = new RemoteStoreRoom("test5_r", {discord_guild: "five", discord_channel: "five"});
+            await store.roomStore.linkRooms(matrix, remote);
+            const entries = await store.roomStore.getEntriesByMatrixId("test5_m");
+            expect(entries[0].matrix!.roomId).to.equal("test5_m");
+            expect(entries[0].remote!.roomId).to.equal("test5_r");
+            expect(entries[0].remote!.get("discord_guild")).to.equal("five");
+            expect(entries[0].remote!.get("discord_channel")).to.equal("five");
+        });
+    });
+    describe("getEntriesByRemoteRoomData", () => {
+        it("will get an entry", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test6",
+                matrix: new MatrixStoreRoom("test6_m"),
+                remote: new RemoteStoreRoom("test6_r", {discord_guild: "find", discord_channel: "this"}),
+            });
+            const entries = await store.roomStore.getEntriesByRemoteRoomData({
+                discord_channel: "this",
+                discord_guild: "find",
+            });
+            expect(entries[0].matrix!.roomId).to.equal("test6_m");
+            expect(entries[0].remote!.roomId).to.equal("test6_r");
+            expect(entries[0].remote!.get("discord_guild")).to.equal("find");
+            expect(entries[0].remote!.get("discord_channel")).to.equal("this");
+        });
+    });
+    describe("removeEntriesByRemoteRoomId", () => {
+        it("will remove a room", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test7",
+                matrix: new MatrixStoreRoom("test7_m"),
+                remote: new RemoteStoreRoom("test7_r", {discord_guild: "find", discord_channel: "this"}),
+            });
+            await store.roomStore.removeEntriesByRemoteRoomId("test7_r");
+            const entries = await store.roomStore.getEntriesByMatrixId("test7_m");
+            expect(entries).to.be.empty;
+        });
+    });
+    describe("removeEntriesByMatrixRoomId", () => {
+        it("will remove a room", async () => {
+            await store.roomStore.upsertEntry({
+                id: "test8",
+                matrix: new MatrixStoreRoom("test8_m"),
+                remote: new RemoteStoreRoom("test8_r", {discord_guild: "find", discord_channel: "this"}),
+            });
+            await store.roomStore.removeEntriesByRemoteRoomId("test8_m");
+            const entries = await store.roomStore.getEntriesByMatrixId("test8_r");
+            expect(entries).to.be.empty;
+        });
+    });
+});
+describe("RoomStore.schema.v8", () => {
+    it("will successfully migrate rooms", async () => {
+        const SCHEMA_VERSION = 8;
+        store = new DiscordStore(":memory:");
+        const roomStore = {
+            select: () => {
+                return [
+                    {
+                        _id: "DGFUYs4hlXNDmmw0",
+                        id: "123",
+                        matrix: {extras: {}},
+                        matrix_id: "!badroom:localhost",
+                    },
+                    {
+                        _id: "Dd37MWDw57dAQz5p",
+                        data: {},
+                        id: "!xdnLTCNErGnwsGnmnm:localhost   discord_282616294245662720_514843269599985674_bridged",
+                        matrix: {
+                            extras: {},
+                        },
+                        matrix_id: "!bridged1:localhost",
+                        remote: {
+                            discord_channel: "514843269599985674",
+                            discord_guild: "282616294245662720",
+                            discord_type: "text",
+                            plumbed: false,
+                        },
+                        remote_id: "discord_282616294245662720_514843269599985674_bridged",
+                    },
+                    {
+                        _id: "H3XEftQWj8BZYuCe",
+                        data: {},
+                        id: "!oGkfjmeNEkJdFasVRF:localhost   discord_282616294245662720_520332167952334849",
+                        matrix: {
+                            extras: {},
+                        },
+                        matrix_id: "!bridged2:localhost",
+                        remote: {
+                            discord_channel: "514843269599985674",
+                            discord_guild: "282616294245662720",
+                            discord_type: "text",
+                            plumbed: true,
+                            update_icon: true,
+                            update_name: false,
+                            update_topic: true,
+                        },
+                        remote_id: "discord_282616294245662720_520332167952334849",
+                    },
+                ];
+            },
+        };
+        await store.init(SCHEMA_VERSION, roomStore);
+        expect(await store.roomStore.getEntriesByMatrixId("!badroom:localhost")).to.be.empty;
+        const bridge1 = (await store.roomStore.getEntriesByMatrixId("!bridged1:localhost"))[0];
+        expect(bridge1).to.exist;
+        expect(bridge1.remote).to.not.be.null;
+        expect(bridge1.remote!.data.discord_channel).to.be.equal("514843269599985674");
+        expect(bridge1.remote!.data.discord_guild).to.be.equal("282616294245662720");
+        expect(bridge1.remote!.data.discord_type).to.be.equal("text");
+        expect(!!bridge1.remote!.data.plumbed).to.be.false;
+        const bridge2 = (await store.roomStore.getEntriesByMatrixId("!bridged2:localhost"))[0];
+        expect(bridge2).to.exist;
+        expect(bridge2.remote).to.not.be.null;
+        expect(bridge2.remote!.data.discord_channel).to.be.equal("514843269599985674");
+        expect(bridge2.remote!.data.discord_guild).to.be.equal("282616294245662720");
+        expect(bridge2.remote!.data.discord_type).to.be.equal("text");
+        expect(!!bridge2.remote!.data.plumbed).to.be.true;
+        expect(!!bridge2.remote!.data.update_icon).to.be.true;
+        expect(!!bridge2.remote!.data.update_name).to.be.false;
+        expect(!!bridge2.remote!.data.update_topic).to.be.true;
+    });
+});
diff --git a/test/test_channelsyncroniser.ts b/test/test_channelsyncroniser.ts
index e4dc61866a91474669a313fbbe8dc17b18797227..c54aa2f1cd7e4ce0e728aa33428452351f95f691 100644
--- a/test/test_channelsyncroniser.ts
+++ b/test/test_channelsyncroniser.ts
@@ -26,7 +26,6 @@ import { MatrixEventProcessor, MatrixEventProcessorOpts } from "../src/matrixeve
 import { DiscordBridgeConfig } from "../src/config";
 import { MockChannel } from "./mocks/channel";
 import { Bridge, MatrixRoom, RemoteRoom } from "matrix-appservice-bridge";
-
 // we are a test file and thus need those
 /* tslint:disable:no-unused-expression max-file-line-count no-any */
 
@@ -109,50 +108,48 @@ function CreateChannelSync(remoteChannels: any[] = []): ChannelSyncroniser {
                 },
             };
         },
-        getRoomStore: () => {
-            REMOTECHANNEL_SET = false;
-            REMOTECHANNEL_REMOVED = false;
-            return {
-                getEntriesByMatrixId: (roomid) => {
-                    const entries: any[] = [];
-                    remoteChannels.forEach((c) => {
-                        const mxid = c.matrix.getId();
-                        if (roomid === mxid) {
-                            entries.push(c);
-                        }
-                    });
-                    return entries;
-                },
-                getEntriesByMatrixIds: (roomids) => {
-                    const entries = {};
-                    remoteChannels.forEach((c) => {
-                        const mxid = c.matrix.getId();
-                        if (roomids.includes(mxid)) {
-                            if (!entries[mxid]) {
-                                entries[mxid] = [];
-                            }
-                            entries[mxid].push(c);
-                        }
-                    });
-                    return entries;
-                },
-                getEntriesByRemoteRoomData: (data) => {
-                    return remoteChannels.filter((c) => {
-                        for (const d of Object.keys(data)) {
-                            if (c.remote.get(d) !== data[d]) {
-                                return false;
-                            }
-                        }
-                        return true;
-                    });
-                },
-                removeEntriesByMatrixRoomId: (room) => {
-                    REMOTECHANNEL_REMOVED = true;
-                },
-                upsertEntry: (room) => {
-                    REMOTECHANNEL_SET = true;
-                },
-            };
+    };
+    REMOTECHANNEL_REMOVED = false;
+    REMOTECHANNEL_SET = false;
+    const roomStore = {
+        getEntriesByMatrixId: (roomid) => {
+            const entries: any[] = [];
+            remoteChannels.forEach((c) => {
+                const mxid = c.matrix.getId();
+                if (roomid === mxid) {
+                    entries.push(c);
+                }
+            });
+            return entries;
+        },
+        getEntriesByMatrixIds: (roomids) => {
+            const entries = {};
+            remoteChannels.forEach((c) => {
+                const mxid = c.matrix.getId();
+                if (roomids.includes(mxid)) {
+                    if (!entries[mxid]) {
+                        entries[mxid] = [];
+                    }
+                    entries[mxid].push(c);
+                }
+            });
+            return entries;
+        },
+        getEntriesByRemoteRoomData: (data) => {
+            return remoteChannels.filter((c) => {
+                for (const d of Object.keys(data)) {
+                    if (c.remote.get(d) !== data[d]) {
+                        return false;
+                    }
+                }
+                return true;
+            });
+        },
+        removeEntriesByMatrixRoomId: (room) => {
+            REMOTECHANNEL_REMOVED = true;
+        },
+        upsertEntry: (room) => {
+            REMOTECHANNEL_SET = true;
         },
     };
     const discordbot: any = {
@@ -161,7 +158,8 @@ function CreateChannelSync(remoteChannels: any[] = []): ChannelSyncroniser {
     const config = new DiscordBridgeConfig();
     config.bridge.domain = "localhost";
     config.channel.namePattern = "[Discord] :guild :name";
-    return new ChannelSync(bridge as Bridge, config, discordbot);
+    const cs = new ChannelSync(bridge as Bridge, config, discordbot, roomStore) as ChannelSyncroniser;
+    return cs;
 }
 
 describe("ChannelSyncroniser", () => {
diff --git a/test/test_discordbot.ts b/test/test_discordbot.ts
index 88feb7eb4a8ad5775a338e73c5109c5029eb4b22..d6dcd1b3a2c1d9484bf60e5daced3b600cf04e14 100644
--- a/test/test_discordbot.ts
+++ b/test/test_discordbot.ts
@@ -92,6 +92,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
             await discordBot.run();
         });
@@ -103,6 +104,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
             await discordBot.run();
         });
@@ -144,6 +146,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
             discord.bot = { user: { id: "654" } };
             discord.provisioner = {
@@ -298,6 +301,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
 
             const guild: any = new MockGuild("123", []);
@@ -324,6 +328,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
             discordBot.store.Get = (a, b) => null;
 
@@ -351,6 +356,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
             discordBot.store.Get = (a, b) => { return {
                 MatrixId: "$event:localhost;!room:localhost",
@@ -387,6 +393,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
             let expected = 0;
             discordBot.OnMessage = async (msg: any) => {
@@ -408,6 +415,7 @@ describe("DiscordBot", () => {
                 "",
                 config,
                 mockBridge,
+                {},
             );
             let expected = 0;
             const THROW_EVERY = 5;
diff --git a/test/test_matrixroomhandler.ts b/test/test_matrixroomhandler.ts
index 4639cac61ad53183a797033b48a7b056217af374..d653c7c1ecff472d527bdeb1e6430aa5741729f1 100644
--- a/test/test_matrixroomhandler.ts
+++ b/test/test_matrixroomhandler.ts
@@ -17,15 +17,9 @@ limitations under the License.
 import * as Chai from "chai";
 import * as Proxyquire from "proxyquire";
 import {DiscordBridgeConfig} from "../src/config";
-import {MockDiscordClient} from "./mocks/discordclient";
-import {PresenceHandler} from "../src/presencehandler";
-import {DiscordBot} from "../src/bot";
-import {MatrixRoomHandler} from "../src/matrixroomhandler";
 import {MockChannel} from "./mocks/channel";
 import {MockMember} from "./mocks/member";
-import * as Bluebird from "bluebird";
 import {MockGuild} from "./mocks/guild";
-import {Guild} from "discord.js";
 import { Util } from "../src/util";
 
 // we are a test file and thus need those
@@ -33,10 +27,6 @@ import { Util } from "../src/util";
 
 const expect = Chai.expect;
 
-// const DiscordClientFactory = Proxyquire("../src/clientfactory", {
-//     "discord.js": { Client: require("./mocks/discordclient").MockDiscordClient },
-// }).DiscordClientFactory;
-
 const RoomHandler = (Proxyquire("../src/matrixroomhandler", {
     "./util": {
         Util: {
@@ -97,13 +87,6 @@ function createRH(opts: any = {}) {
                 unban: async () => { USERSUNBANNED++; },
             };
         },
-        getRoomStore: () => {
-            return {
-                removeEntriesByMatrixRoomId: () => {
-
-                },
-            };
-        },
     };
     const us = {
         JoinRoom: async () => { USERSJOINED++; },
@@ -202,7 +185,21 @@ function createRH(opts: any = {}) {
             }
         },
     };
-    const handler = new RoomHandler(bot as any, config, provisioner as any, bridge as any);
+    const store = {
+        getEntriesByMatrixId: (matrixId) => {
+            return [{
+                matrix: {},
+                remote: {},
+            }];
+        },
+        linkRooms: () => {
+
+        },
+        removeEntriesByMatrixRoomId: () => {
+
+        },
+    };
+    const handler = new RoomHandler(bot as any, config, provisioner as any, bridge as any, store);
     return handler;
 }
 
@@ -740,12 +737,11 @@ describe("MatrixRoomHandler", () => {
         });
     });
     describe("createMatrixRoom", () => {
-        it("will return an object", () => {
+        it("will return an object", async () => {
             const handler: any = createRH({});
             const channel = new MockChannel("123", new MockGuild("456"));
-            const roomOpts = handler.createMatrixRoom(channel, "#test:localhost");
+            const roomOpts = await handler.createMatrixRoom(channel, "#test:localhost");
             expect(roomOpts.creationOpts).to.exist;
-            expect(roomOpts.remote).to.exist;
         });
     });
     describe("HandleDiscordCommand", () => {
diff --git a/test/test_provisioner.ts b/test/test_provisioner.ts
index bafbeffaccb55cc35dfe39a5c3ff128d7f13170c..79f8f71734c2f3da382bd6a8c2093c4f7c90f235 100644
--- a/test/test_provisioner.ts
+++ b/test/test_provisioner.ts
@@ -29,7 +29,7 @@ const TIMEOUT_MS = 1000;
 describe("Provisioner", () => {
     describe("AskBridgePermission", () => {
         it("should fail to bridge a room that timed out", async () => {
-            const p = new Provisioner(null);
+            const p = new Provisioner({} as any);
             const startAt = Date.now();
             try {
                 await p.AskBridgePermission(
@@ -47,7 +47,7 @@ describe("Provisioner", () => {
             }
         });
         it("should fail to bridge a room that was declined", async () => {
-            const p = new Provisioner(null);
+            const p = new Provisioner({} as any);
             const promise = p.AskBridgePermission(
                 new MockChannel("foo", "bar") as any,
                 "Mark",
@@ -63,7 +63,7 @@ describe("Provisioner", () => {
 
         });
         it("should bridge a room that was approved", async () => {
-            const p = new Provisioner(null);
+            const p = new Provisioner({} as any);
             const promise = p.AskBridgePermission(
                 new MockChannel("foo", "bar") as any,
                 "Mark",
diff --git a/tools/addRoomsToDirectory.ts b/tools/addRoomsToDirectory.ts
index f4f4b9fbc39e4fcf2c79ea836e649d5d5ea916e3..200bebb500f7e85ece31b7cd5575334106aa2518 100644
--- a/tools/addRoomsToDirectory.ts
+++ b/tools/addRoomsToDirectory.ts
@@ -27,6 +27,7 @@ import * as usage from "command-line-usage";
 import { DiscordBridgeConfig } from "../src/config";
 import { Log } from "../src/log";
 import { Util } from "../src/util";
+import { DiscordStore } from "../src/store";
 const log = new Log("AddRoomsToDirectory");
 const optionDefinitions = [
     {
@@ -89,20 +90,20 @@ const bridge = new Bridge({
     domain: "rubbish",
     homeserverUrl: true,
     registration: true,
-    roomStore: options.store,
 });
 
+const discordstore = new DiscordStore(config.database ? config.database.filename : "discord.db");
+
 async function run() {
     try {
-        await bridge.loadDatabases();
+        await discordstore.init();
     } catch (e) {
         log.error(`Failed to load database`, e);
     }
-
-    let rooms = await bridge.getRoomStore().getEntriesByRemoteRoomData({
+    let rooms = await discordstore.roomStore.getEntriesByRemoteRoomData({
         discord_type: "text",
     });
-    rooms = rooms.filter((r) => r.remote.get("plumbed") !== true );
+    rooms = rooms.filter((r) => r.remote && r.remote.get("plumbed") !== true );
     const client = clientFactory.getClientAs();
     log.info(`Got ${rooms.length} rooms to set`);
     try {
diff --git a/tools/chanfix.ts b/tools/chanfix.ts
index dc0c85b50664ad6c664536ca3b964759a9f2a7c3..b37b0fe9a35fdc892ed37118e19108497c359612 100644
--- a/tools/chanfix.ts
+++ b/tools/chanfix.ts
@@ -80,6 +80,8 @@ const clientFactory = new ClientFactory({
     token: registration.as_token,
     url: config.bridge.homeserverUrl,
 });
+const discordstore = new DiscordStore(config.database ? config.database.filename : "discord.db");
+const discordbot = new DiscordBot("", config, null, discordstore);
 
 const bridge = new Bridge({
     clientFactory,
@@ -98,16 +100,13 @@ const bridge = new Bridge({
     userStore: config.database.userStorePath,
 });
 
-const discordbot = new DiscordBot(botUserId, config, bridge);
-
 async function run() {
-    try {
-        await bridge.loadDatabases();
-    } catch (e) { }
+    await bridge.loadDatabases();
+    await discordstore.init();
     bridge._clientFactory = clientFactory;
     bridge._botClient = bridge._clientFactory.getClientAs();
     bridge._botIntent = new Intent(bridge._botClient, bridge._botClient, { registered: true });
-    await discordbot.init();
+    await discordbot.ClientFactory.init();
     const client = await discordbot.ClientFactory.getClient();
 
     // first set update_icon to true if needed
diff --git a/tools/ghostfix.ts b/tools/ghostfix.ts
index 9d5c6bc3344e096433e457af5d634c8cb3b430e9..3e9a4ced371aba06b27820ba0b2c945b7ae85499 100644
--- a/tools/ghostfix.ts
+++ b/tools/ghostfix.ts
@@ -20,15 +20,11 @@ import * as fs from "fs";
 import * as args from "command-line-args";
 import * as usage from "command-line-usage";
 import * as Bluebird from "bluebird";
-import { ChannelSyncroniser } from "../src/channelsyncroniser";
 import { DiscordBridgeConfig } from "../src/config";
-import { DiscordBot } from "../src/bot";
-import { DiscordStore } from "../src/store";
-import { Provisioner } from "../src/provisioner";
-import { UserSyncroniser } from "../src/usersyncroniser";
 import { Log } from "../src/log";
 import { Util } from "../src/util";
-import { TextChannel } from "discord.js";
+import { DiscordBot } from "../src/bot";
+import { DiscordStore } from "../src/store";
 
 const log = new Log("GhostFix");
 
@@ -112,10 +108,9 @@ const bridge = new Bridge({
 });
 
 async function run() {
-    try {
-        await bridge.loadDatabases();
-    } catch (e) { }
-    const discordbot = new DiscordBot(botUserId, config, bridge);
+    await bridge.loadDatabases();
+    const store = new DiscordStore(config.database);
+    const discordbot = new DiscordBot(botUserId, config, bridge, store);
     await discordbot.init();
     bridge._clientFactory = clientFactory;
     const client = await discordbot.ClientFactory.getClient();