diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..348f1bdd38
--- /dev/null
+++ b/src/META-INF/MANIFEST.MF
@@ -0,0 +1 @@
+Manifest-Version: 1.0
\ No newline at end of file
diff --git a/src/META-INF/services/net.opentsdb.uid.UniqueIdFilterPlugin b/src/META-INF/services/net.opentsdb.uid.UniqueIdFilterPlugin
new file mode 100644
index 0000000000..010e95484c
--- /dev/null
+++ b/src/META-INF/services/net.opentsdb.uid.UniqueIdFilterPlugin
@@ -0,0 +1 @@
+net.opentsdb.uid.UniqueIdWhitelistFilter
\ No newline at end of file
diff --git a/src/uid/UniqueIdWhitelistFilter.java b/src/uid/UniqueIdWhitelistFilter.java
new file mode 100644
index 0000000000..371ea7bd50
--- /dev/null
+++ b/src/uid/UniqueIdWhitelistFilter.java
@@ -0,0 +1,200 @@
+// This file is part of OpenTSDB.
+// Copyright (C) 2016 The OpenTSDB Authors.
+//
+// This program is free software: you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 2.1 of the License, or (at your
+// option) any later version. This program is distributed in the hope that it
+// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+// General Public License for more details. You should have received a copy
+// of the GNU Lesser General Public License along with this program. If not,
+// see .
+package net.opentsdb.uid;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.stumbleupon.async.Deferred;
+
+import net.opentsdb.core.TSDB;
+import net.opentsdb.stats.StatsCollector;
+import net.opentsdb.uid.UniqueId.UniqueIdType;
+import net.opentsdb.utils.Config;
+
+/**
+ * A UID filter implementation using regular expression based whitelists.
+ * Multiple regular expressions can be provided in the configuration file with
+ * a configurable delimiter. Each expression is compiled into a list per UID
+ * type and when a new UID passes through the filter, each expression in the
+ * list is compared to make sure the name satisfies all expressions.
+ */
+public class UniqueIdWhitelistFilter extends UniqueIdFilterPlugin {
+
+ /** Default delimiter */
+ public static final String DEFAULT_REGEX_DELIMITER = ",";
+
+ /** Lists of patterns for each type. */
+ private List metric_patterns;
+ private List tagk_patterns;
+ private List tagv_patterns;
+
+ /** Counters for tracking stats */
+ private final AtomicLong metrics_rejected = new AtomicLong();
+ private final AtomicLong metrics_allowed = new AtomicLong();
+ private final AtomicLong tagks_rejected = new AtomicLong();
+ private final AtomicLong tagks_allowed = new AtomicLong();
+ private final AtomicLong tagvs_rejected = new AtomicLong();
+ private final AtomicLong tagvs_allowed = new AtomicLong();
+
+ @Override
+ public void initialize(final TSDB tsdb) {
+ final Config config = tsdb.getConfig();
+ String delimiter = config.getString("tsd.uidfilter.whitelist.delimiter");
+ if (delimiter == null) {
+ delimiter = DEFAULT_REGEX_DELIMITER;
+ }
+
+ String raw = config.getString("tsd.uidfilter.whitelist.metric_patterns");
+ if (raw != null) {
+ final String[] splits = raw.split(delimiter);
+ metric_patterns = new ArrayList(splits.length);
+ for (final String pattern : splits) {
+ try {
+ metric_patterns.add(Pattern.compile(pattern));
+ } catch (PatternSyntaxException e) {
+ throw new IllegalArgumentException("The metric whitelist pattern [" +
+ pattern + "] does not compile.", e);
+ }
+ }
+ }
+
+ raw = config.getString("tsd.uidfilter.whitelist.tagk_patterns");
+ if (raw != null) {
+ final String[] splits = raw.split(delimiter);
+ tagk_patterns = new ArrayList(splits.length);
+ for (final String pattern : splits) {
+ try {
+ tagk_patterns.add(Pattern.compile(pattern));
+ } catch (PatternSyntaxException e) {
+ throw new IllegalArgumentException("The tagk whitelist pattern [" +
+ pattern + "] does not compile.", e);
+ }
+ }
+ }
+
+ raw = config.getString("tsd.uidfilter.whitelist.tagv_patterns");
+ if (raw != null) {
+ final String[] splits = raw.split(delimiter);
+ tagv_patterns = new ArrayList(splits.length);
+ for (final String pattern : splits) {
+ try {
+ tagv_patterns.add(Pattern.compile(pattern));
+ } catch (PatternSyntaxException e) {
+ throw new IllegalArgumentException("The tagv whitelist pattern [" +
+ pattern + "] does not compile.", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ public Deferred