diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -29,4 +29,5 @@
 
 # local configuration files
 /conf/taskotron.yaml
+/conf/namespaces.yaml
 /conf/yumrepoinfo.conf
diff --git a/conf/namespaces.yaml.example b/conf/namespaces.yaml.example
new file mode 100644
--- /dev/null
+++ b/conf/namespaces.yaml.example
@@ -0,0 +1,12 @@
+# A list of namespaces that any task is allowed to post a result into
+namespaces_safe:
+    - scratch
+
+# A list of git repos that are allowed to post a result into a particular namespace
+namespaces_whitelist:
+    qa:
+        - git@bitbucket.org:fedoraqa/task-rpmlint.git
+        - git@bitbucket.org:fedoraqa/task-depcheck.git
+        - git@bitbucket.org:fedoraqa/task-upgradepath.git
+    pkg:
+        - git://pkgs.fedoraproject.org/tasks/
diff --git a/libtaskotron.spec b/libtaskotron.spec
--- a/libtaskotron.spec
+++ b/libtaskotron.spec
@@ -4,7 +4,7 @@
 
 Name:           libtaskotron
 Version:        0.4.10
-Release:        2%{?dist}
+Release:        3%{?dist}
 Summary:        Taskotron Support Library
 
 License:        GPLv3
@@ -26,13 +26,15 @@
 Summary:        The minimal, core parts of libtaskotron
 
 Requires:       libtaskotron-config = %{version}-%{release}
+Requires:       python-configparser >= 3.5.0b2
 Requires:       python-jinja2 >= 2.8
 Requires:       python-progressbar >= 2.3
 Requires:       python-requests >= 2.7.0
 Requires:       python-setuptools
 Requires:       PyYAML >= 3.11
 Requires:       resultsdb_api >= 1.2.1
 BuildRequires:  pytest >= 2.7.3
+BuildRequires:  python-configparser >= 3.5.0b2
 BuildRequires:  python-devel
 BuildRequires:  python-dingus >= 0.3.4
 BuildRequires:  python-mock >= 1.0.1
@@ -102,6 +104,7 @@
 # configuration files
 mkdir -p %{buildroot}%{_sysconfdir}/taskotron/
 install conf/taskotron.yaml.example %{buildroot}%{_sysconfdir}/taskotron/taskotron.yaml
+install conf/namespaces.yaml.example %{buildroot}%{_sysconfdir}/taskotron/namespaces.yaml
 install conf/yumrepoinfo.conf.example %{buildroot}%{_sysconfdir}/taskotron/yumrepoinfo.conf
 
 # log dir
@@ -145,6 +148,7 @@
 %files -n libtaskotron-config
 %dir %{_sysconfdir}/taskotron
 %config(noreplace) %{_sysconfdir}/taskotron/taskotron.yaml
+%config %{_sysconfdir}/taskotron/namespaces.yaml
 %config %{_sysconfdir}/taskotron/yumrepoinfo.conf
 
 %files -n libtaskotron-fedora
@@ -156,6 +160,10 @@
 %{python_sitelib}/libtaskotron/ext/disposable/*
 
 %changelog
+* Tue Mar 15 2016 Martin Krizek <mkrizek@fedoraproject.org> - 0.4.10-3
+- add python-configparser as dep
+- install namespaces.yaml
+
 * Mon Mar 14 2016 Martin Krizek <mkrizek@fedoraproject.org> - 0.4.10-2
 - add report_templates directory
 
diff --git a/libtaskotron/config.py b/libtaskotron/config.py
--- a/libtaskotron/config.py
+++ b/libtaskotron/config.py
@@ -30,6 +30,9 @@
 CONF_FILE = 'taskotron.yaml'
 '''the name of our configuration file'''
 
+NS_CONF_FILE = 'namespaces.yaml'
+'''the name of result namespaces configuration file'''
+
 PROFILE_VAR = 'TASKOTRON_PROFILE'
 '''environment variable name for setting config profile'''
 
@@ -272,3 +275,42 @@
             raise exc.TaskotronError("Failed to create required directory '%s', "
                                      "please create it manually. Caused by: %s"
                                      % (dir_, e))
+
+
+def parse_yaml_from_file(filename):
+    '''
+    Parse given file in YAML format into a dictionary.
+
+    :param str filename: a filename that yaml data are loaded from
+    :return: dictionary constructed from the yaml document
+    '''
+    with open(filename, 'r') as datafile:
+        return yaml.safe_load(datafile.read())
+
+
+def load_namespaces_config():
+    '''
+    Load and parse namespaces config file into a dictionary.
+
+    :return: dictionary constructed from the yaml document
+    '''
+
+    config = {
+        'namespaces_safe': ['scratch'],
+        'namespaces_whitelist': {}
+    }
+
+    if get_config().profile == ProfileName.TESTING:
+        log.debug('Testing profile, not loading config files from disk')
+        return config
+
+    filename = _search_dirs(CONF_DIRS, NS_CONF_FILE)
+    if filename is None:
+        log.warning('Could not find namespaces config in %s. Using defaults.' %
+                    ', '.join(CONF_DIRS))
+        return config
+
+    data = parse_yaml_from_file(filename)
+    config.update(data)
+
+    return config
diff --git a/libtaskotron/directives/resultsdb_directive.py b/libtaskotron/directives/resultsdb_directive.py
--- a/libtaskotron/directives/resultsdb_directive.py
+++ b/libtaskotron/directives/resultsdb_directive.py
@@ -75,6 +75,7 @@
 """
 
 import os
+import configparser
 
 from libtaskotron.directives import BaseDirective
 
@@ -181,9 +182,6 @@
         return os.path.relpath(artifact_path, start=artifactsdir)
 
     def process(self, params, arg_data):
-        #TODO: Replace with proper location and adjust exc. text
-        # we're creating the jobid in the directive for now, so adjust the check
-
         # checking if reporting is enabled is done after importing yaml which
         # serves as validation of input results
         try:
@@ -204,18 +202,46 @@
                      "following would get reported:\n%s" % params['results'])
             return check.export_YAML(check_details)
 
+        # find out if the task is allowed to post results into the namespace
+        conf_ns = config.load_namespaces_config()
+        if (conf.profile == config.ProfileName.PRODUCTION and
+                arg_data['namespace'] not in conf_ns['namespaces_safe']):
+
+            try:
+                taskdir = os.path.dirname(os.path.abspath(arg_data['task']))
+                gitconfig_filename = os.path.join(taskdir, '.git/config')
+
+                gitconfig = configparser.ConfigParser()
+                gitconfig.read(gitconfig_filename)
+                repo_url = gitconfig['remote "origin"']['url']
+            except TypeError, e:
+                log.exception(e)
+                raise TaskotronDirectiveError("Could not find task's git remote 'origin' url in %s"
+                                              % gitconfig_filename)
+
+            try:
+                ns_whitelist = conf_ns['namespaces_whitelist'][arg_data['namespace']]
+                if not [ns for ns in ns_whitelist if ns.startswith(repo_url.strip())]:
+                    raise TaskotronDirectiveError
+            except (KeyError, TaskotronDirectiveError):
+                raise TaskotronDirectiveError("%s is not allowed to post results into %s "
+                                              "namespace. Not posting results." %
+                                              (arg_data['checkname'], arg_data['namespace']))
+
         # for now, we're creating the resultsdb job at reporting time
         # the job needs to be 'RUNNING' in order to append any results
         job_url, log_url = buildbot_utils.get_urls(arg_data['jobid'],
                                                    self.masterurl,
                                                    self.task_stepname)
-        job_data = self.create_resultsdb_job(arg_data['checkname'],
-                                             uuid=arg_data['uuid'],
-                                             )
+
+        checkname = '%s.%s' % (arg_data['namespace'], arg_data['checkname'])
+        job_data = self.create_resultsdb_job(checkname, uuid=arg_data['uuid'])
 
         log.info('Posting %s results to ResultsDB...' % len(check_details))
         for detail in check_details:
-            checkname = detail.checkname or arg_data['checkname']
+            checkname = '%s.%s' % (arg_data['namespace'],
+                                   detail.checkname or arg_data['checkname'])
+
             self.ensure_testcase_exists(checkname)
             result_log_url = log_url
             if detail.artifact:
diff --git a/libtaskotron/executor.py b/libtaskotron/executor.py
--- a/libtaskotron/executor.py
+++ b/libtaskotron/executor.py
@@ -81,6 +81,7 @@
         log.debug("Current workdir: %s", self.workdir)
         self.arg_data['workdir'] = self.workdir
         self.arg_data['checkname'] = self.formula['name']
+        self.arg_data['namespace'] = self.formula.get('namespace', 'scratch')
 
         # override variable values
         for var, val in self.arg_data['override'].items():
diff --git a/libtaskotron/overlord.py b/libtaskotron/overlord.py
--- a/libtaskotron/overlord.py
+++ b/libtaskotron/overlord.py
@@ -9,7 +9,6 @@
 from libtaskotron.logger import log
 from libtaskotron import executor
 from libtaskotron import file_utils
-from libtaskotron import taskformula
 from libtaskotron import exceptions as exc
 from libtaskotron.directives import exitcode_directive
 
@@ -29,7 +28,7 @@
         self.exitcode = None
 
         # parse task formula
-        self.formula = taskformula.parse_yaml_from_file(arg_data['task'])
+        self.formula = config.parse_yaml_from_file(arg_data['task'])
         if not self.formula:
             raise exc.TaskotronYamlError('Task formula file should not be empty: %s' %
                                          arg_data['task'])
diff --git a/libtaskotron/taskformula.py b/libtaskotron/taskformula.py
--- a/libtaskotron/taskformula.py
+++ b/libtaskotron/taskformula.py
@@ -9,20 +9,10 @@
 import collections
 import string
 
-import yaml
 from libtaskotron import exceptions as exc
 from libtaskotron.logger import log
 
 
-def parse_yaml_from_file(filename):
-    with open(filename, 'r') as datafile:
-        return parse_yaml(datafile.read())
-
-
-def parse_yaml(contents):
-    return yaml.safe_load(contents)
-
-
 def _replace_vars(text, variables):
     '''Go through ``text`` and replace all variables (in the form of what is
     supported by :class:`string.Template`) with their values, as provided in
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,6 +10,7 @@
 # lifecycle). If the Fedora libraries span multiple versions across releases,
 # you can define a dependency range here instead of an exact version.
 
+configparser == 3.5.0b2
 dingus == 0.3.4
 Jinja2 == 2.8
 munch == 2.0.2
diff --git a/testing/test_exitcode_directive.py b/testing/test_exitcode_directive.py
--- a/testing/test_exitcode_directive.py
+++ b/testing/test_exitcode_directive.py
@@ -6,7 +6,7 @@
 from libtaskotron.directives import exitcode_directive
 from libtaskotron.exceptions import TaskotronDirectiveError, TaskotronValueError
 from libtaskotron import overlord
-from libtaskotron import taskformula
+from libtaskotron import config
 from libtaskotron import file_utils
 from dingus import Dingus
 import pytest
@@ -84,7 +84,7 @@
                     'no_destroy': None}
         arg_data['artifactsdir'] = '/dir'
 
-        monkeypatch.setattr(taskformula, 'parse_yaml_from_file', Dingus())
+        monkeypatch.setattr(config, 'parse_yaml_from_file', Dingus())
         monkeypatch.setattr(file_utils, 'makedirs', Dingus())
         self.test_runner = overlord.Overlord(arg_data)
 
diff --git a/testing/test_overlord.py b/testing/test_overlord.py
--- a/testing/test_overlord.py
+++ b/testing/test_overlord.py
@@ -8,7 +8,6 @@
 
 from libtaskotron import executor
 from libtaskotron import config
-from libtaskotron import taskformula
 from libtaskotron import file_utils
 from libtaskotron import minion
 from libtaskotron import overlord
@@ -22,7 +21,7 @@
         self.stub_local = Dingus()
         self.stub_remote = Dingus()
         self.stub_remote_persistent = Dingus()
-        monkeypatch.setattr(taskformula, 'parse_yaml_from_file', Dingus())
+        monkeypatch.setattr(config, 'parse_yaml_from_file', Dingus())
         monkeypatch.setattr(file_utils, 'makedirs', Dingus())
 
         monkeypatch.setattr(executor, 'Executor', self.stub_local)
@@ -107,7 +106,7 @@
         assert len(self.stub_remote_persistent.calls()) == 0
 
     def test_executor_used(self, monkeypatch):
-        monkeypatch.setattr(taskformula, 'parse_yaml_from_file', Dingus())
+        monkeypatch.setattr(config, 'parse_yaml_from_file', Dingus())
         monkeypatch.setattr(file_utils, 'makedirs', Dingus())
 
         ref_params = {'local': True, 'ssh': None, 'ssh_privkey': None, 'uuid': '',
diff --git a/testing/test_resultsdb_directive.py b/testing/test_resultsdb_directive.py
--- a/testing/test_resultsdb_directive.py
+++ b/testing/test_resultsdb_directive.py
@@ -8,16 +8,26 @@
 import os
 import pytest
 from dingus import Dingus, exception_raiser
+import configparser
 
 from libtaskotron.directives import resultsdb_directive
 from libtaskotron.exceptions import TaskotronDirectiveError, TaskotronValueError
 
 from libtaskotron import check
 from libtaskotron import config
+from libtaskotron import config_defaults
 
 from resultsdb_api import ResultsDBapiException
 
 
+class StubConfigParser(object):
+    def read(self, *args):
+        pass
+
+    def __getitem__(self, key):
+        return {'url': 'giturl'}
+
+
 @pytest.mark.usefixtures('setup')
 class TestResultsdbReport():
 
@@ -40,9 +50,11 @@
         self.ref_arg_data = {
                 'resultsdb_job_id': 1,
                 'checkname': 'test_resultsdb_report',
+                'namespace': 'qa',
                 'jobid': 'all/123',
                 'uuid': 'c25237a4-b6b3-11e4-b98a-3c970e018701',
                 'artifactsdir': '/some/directory/',
+                'task': '/taskdir'
                 }
 
         self.ref_resultdata = {u'id': 1234}
@@ -70,6 +82,7 @@
         self.conf = config.get_config()
         self.conf.report_to_resultsdb = True
 
+        monkeypatch.setattr(configparser, 'ConfigParser', StubConfigParser)
 
     def test_config_reporting_disabled(self):
         """Checks config option that disables reporting."""
@@ -89,7 +102,6 @@
 
         config._config = None
 
-
     def test_failed_yaml_import(self, monkeypatch):
         """Checks if failed YAML import raises exception"""
         monkeypatch.setattr(check, 'import_YAML',
@@ -149,9 +161,11 @@
         ref_log_url = '%s/builders/%s/builds/%s/steps/%s/logs/stdio' %\
                       (self.conf.taskotron_master, ref_builder, ref_jobid,
                        self.conf.buildbot_task_step)
+        ref_testcase_name = '%s.%s' % (self.ref_arg_data['namespace'],
+                                       self.ref_arg_data['checkname'])
 
         assert call_data['job_id'] == self.ref_jobid
-        assert call_data['testcase_name'] == self.ref_arg_data['checkname']
+        assert call_data['testcase_name'] == ref_testcase_name
         assert call_data['outcome'] == self.cd.outcome
         assert call_data['summary'] == self.cd.note
         assert call_data['log_url'] == ref_log_url
@@ -293,3 +307,27 @@
         data = check.import_YAML(output)
 
         assert data[0]._internal['resultsdb_result_id'] == 1234
+
+    def test_not_allowed_namespace(self, monkeypatch):
+        ref_ns = {'namespaces_safe': [], 'namespaces_whitelist': {'qa': []}}
+        monkeypatch.setattr(config, 'load_namespaces_config', lambda: ref_ns)
+        self.conf.profile = config_defaults.ProfileName.PRODUCTION
+
+        with pytest.raises(TaskotronDirectiveError):
+            self.test_rdb.process(self.ref_input, self.ref_arg_data)
+
+    def test_allowed_namespace(self, monkeypatch):
+        ref_ns = {'namespaces_safe': [], 'namespaces_whitelist': {'qa': ['giturl']}}
+        monkeypatch.setattr(config, 'load_namespaces_config', lambda: ref_ns)
+        self.conf.profile = config_defaults.ProfileName.PRODUCTION
+
+        self.test_rdb.process(self.ref_input, self.ref_arg_data)
+        # did not raise, pass
+
+    def test_safe_namespace(self, monkeypatch):
+        ref_ns = {'namespaces_safe': ['qa']}
+        monkeypatch.setattr(config, 'load_namespaces_config', lambda: ref_ns)
+        self.conf.profile = config_defaults.ProfileName.PRODUCTION
+
+        self.test_rdb.process(self.ref_input, self.ref_arg_data)
+        # did not raise, pass
diff --git a/testing/test_taskformula.py b/testing/test_taskformula.py
--- a/testing/test_taskformula.py
+++ b/testing/test_taskformula.py
@@ -7,69 +7,11 @@
 
 import copy
 
-import yaml
 import pytest
 from libtaskotron import taskformula
 import libtaskotron.exceptions as exc
 
 
-ref_taskname = 'testtask'
-ref_taskdesc = 'this is a test task'
-ref_maintainer = 'testuser'
-ref_deps = ['libtaskotron', 'libpanacea']
-ref_input = {'args': 'arch, koji_build'}
-ref_actions = []
-
-
-def create_yamldata(name=ref_taskname, desc=ref_taskdesc, maint=ref_maintainer,
-                    deps=ref_deps, input_=ref_input, actions=ref_actions):
-    return {'name': name,
-            'desc': desc,
-            'maintainer': maint,
-            'deps': deps,
-            'input': input_,
-            'actions': actions}
-
-
-class TestTaskYamlMetadata(object):
-    def setup_method(self, method):
-        self.formula = create_yamldata()
-        self.test_task = taskformula.parse_yaml(yaml.safe_dump(self.formula))
-
-    def test_taskyaml_taskname(self):
-        assert self.test_task['name'] == self.formula['name']
-
-    def test_taskyaml_taskdesc(self):
-        assert self.test_task['desc'] == self.formula['desc']
-
-    def test_taskyaml_taskmaintainer(self):
-        assert self.test_task['maintainer'] == self.formula['maintainer']
-
-    def test_taskyaml_taskdeps(self):
-        assert self.test_task['deps'] == self.formula['deps']
-
-    def test_taskyaml_taskinput(self):
-        assert self.test_task['input'] == self.formula['input']
-
-
-class TestTaskYamlActionsNoVariables(object):
-    def test_taskyaml_simple_action(self):
-        ref_action = {'name': 'testaction', 'action': {'key': 'value'}}
-        formula = create_yamldata(actions=[ref_action])
-
-        test_task = taskformula.parse_yaml(yaml.safe_dump(formula))
-
-        assert test_task['actions'][0] == ref_action
-
-    def test_taskyaml_multiple_actions(self):
-        ref_action = {'name': 'testaction', 'action': {'key': 'value'}}
-        formula = create_yamldata(actions=[ref_action, ref_action, ref_action])
-
-        test_task = taskformula.parse_yaml(yaml.safe_dump(formula))
-
-        assert len(test_task['actions']) == 3
-
-
 class TestReplaceVarsInAction(object):
 
     def setup_method(self, method):

