diff --git a/config.sample.yaml b/config.sample.yaml
index d079a5393..7fdf70af4 100644
--- a/config.sample.yaml
+++ b/config.sample.yaml
@@ -422,6 +422,11 @@ ircService:
     # allotted time period, the provisioning request will fail.
     # Default: 300 seconds (5 mins)
     requestTimeoutSeconds: 300
+    # A file defining the provisioning rules for rooms. Format is documented
+    # in rules.sample.yaml. Leave undefined to not specify any rules.
+    ruleFile: "./provisioning.rules.yaml"
+    # Watch the file for changes, and apply the rules. Default: false
+    enableReload: true
 
   # WARNING: The bridge needs to send plaintext passwords to the IRC server, it cannot
   # send a password hash. As a result, passwords (NOT hashes) are stored encrypted in
diff --git a/lib/bridge/IrcBridge.js b/lib/bridge/IrcBridge.js
index 86fc6f920..cb8100d9c 100644
--- a/lib/bridge/IrcBridge.js
+++ b/lib/bridge/IrcBridge.js
@@ -52,6 +52,16 @@ function IrcBridge(config, registration) {
     this.ircHandler = new IrcHandler(this, this.config.ircHandler);
     this._clientPool = new ClientPool(this);
     var dirPath = this.config.ircService.databaseUri.substring("nedb://".length);
+    let roomLinkValidation = undefined;
+    let provisioning = config.ircService.provisioning;
+    if (provisioning && provisioning.enabled &&
+        typeof (provisioning.ruleFile) === "string") {
+        roomLinkValidation = {
+            ruleFile: provisioning.ruleFile,
+            triggerEndpoint: provisioning.enableReload
+        };
+    }
+
     this._bridge = new Bridge({
         registration: this.registration,
         homeserverUrl: this.config.homeserver.url,
@@ -89,7 +99,8 @@ function IrcBridge(config, registration) {
                 dontCheckPowerLevel: true,
                 enablePresence: this.config.homeserver.enablePresence,
             }
-        }
+        },
+        roomLinkValidation,
     });
 
     this._timers = null; // lazy map of Histogram instances used as metrics
diff --git a/lib/config/schema.yml b/lib/config/schema.yml
index 76738790d..fa5592ba1 100644
--- a/lib/config/schema.yml
+++ b/lib/config/schema.yml
@@ -86,6 +86,10 @@ properties:
                         type: "boolean"
                     requestTimeoutSeconds:
                         type: "number"
+                    ruleFile:
+                        type: "string"
+                    enableReload:
+                        type: "boolean"
             passwordEncryptionKeyPath:
                 type: "string"
             matrixHandler:
diff --git a/lib/provisioning/Provisioner.js b/lib/provisioning/Provisioner.js
index 1a8dfe18e..0d2dd75eb 100644
--- a/lib/provisioning/Provisioner.js
+++ b/lib/provisioning/Provisioner.js
@@ -244,6 +244,16 @@ Provisioner.prototype._userHasProvisioningPower = Promise.coroutine(
 
         // Try 100 times to join a room, or timeout after 10 min
         yield retry(req, 100, 5000, matrixClient, matrixClient.joinRoom, roomId).timeout(600000);
+        try {
+            yield this._ircBridge.getAppServiceBridge().canProvisionRoom(roomId);
+        }
+        catch (err) {
+            req.log.error(`Room failed room validator check: (${err})`);
+            throw new Error(
+                'Room failed validation. You may be attempting to "double bridge" this room.' +
+                ' Error: ' + err
+            );
+        }
 
         try {
             powerState = yield matrixClient.getStateEvent(roomId, 'm.room.power_levels');
diff --git a/package.json b/package.json
index 19f21e041..80cc8a025 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
     "he": "^1.1.1",
     "irc": "matrix-org/node-irc#c9abb427bec5016d94a2abf3e058cc62de09ea5a",
     "js-yaml": "^3.2.7",
-    "matrix-appservice-bridge": "1.6.0c",
+    "matrix-appservice-bridge": "^1.7.0",
     "nedb": "^1.1.2",
     "nopt": "^3.0.1",
     "prom-client": "^6.3.0",
diff --git a/provisioning.rules.sample.yaml b/provisioning.rules.sample.yaml
new file mode 100644
index 000000000..ea91fccb8
--- /dev/null
+++ b/provisioning.rules.sample.yaml
@@ -0,0 +1,12 @@
+# A set of regexes to match against joined members in rooms.
+# If one of the regexes matches a userId, then do not allow provisioning
+# to the room UNLESS it also matches a exempt regex.
+# This doesn't affect existing bridge entrys, only new provisioned rooms.
+#
+# For this to work, config.provisioning.ruleFile must point to this file.
+userIds:
+    exempt:
+      - "@appservice-irc:localhost"
+      - "@irc_.+:localhost"
+    conflict:
+      - "@irc_.+:.+"
diff --git a/spec/util/client-sdk-mock.js b/spec/util/client-sdk-mock.js
index ca6a2cde8..afadee0ab 100644
--- a/spec/util/client-sdk-mock.js
+++ b/spec/util/client-sdk-mock.js
@@ -23,6 +23,7 @@ function MockClient(config) {
     this.setRoomTopic = jasmine.createSpy("sdk.setRoomTopic(roomId, topic)");
     this.setDisplayName = jasmine.createSpy("sdk.setDisplayName(name)");
     this.getStateEvent = jasmine.createSpy("sdk.getStateEvent(room,type,key)");
+    this.fetchRoomEvent = jasmine.createSpy("sdk.fetchRoomEvent(room,event_id)");
     this.sendStateEvent = jasmine.createSpy("sdk.sendStateEvent(room,type,content,key)");
     this.sendEvent = jasmine.createSpy("sdk.sendEvent(roomId,type,content)");
     this.invite = jasmine.createSpy("sdk.invite(roomId, userId)");