diff --git a/conf/mentat-hawat-dev.py.conf b/conf/mentat-hawat-dev.py.conf
index 6586d32c09840512f58f54b92caf3501665ae16b..aa3915decef8fe7d3ff7ead2dd66e95cfc11d71a 100644
--- a/conf/mentat-hawat-dev.py.conf
+++ b/conf/mentat-hawat-dev.py.conf
@@ -52,6 +52,7 @@ ENABLED_BLUEPRINTS = [
     'hawat.blueprints.home',
     'hawat.blueprints.reports',
     'hawat.blueprints.events',
+    'hawat.blueprints.detectors',
     'hawat.blueprints.hosts',
     'hawat.blueprints.timeline',
     'hawat.blueprints.dnsr',
diff --git a/conf/mentat-hawat.py.conf b/conf/mentat-hawat.py.conf
index 48e422757c522a3b78b7cf819b73a33dde735640..eef420e439c1b7bccfc177c226589d933237210b 100644
--- a/conf/mentat-hawat.py.conf
+++ b/conf/mentat-hawat.py.conf
@@ -45,6 +45,7 @@ ENABLED_BLUEPRINTS = [
     'hawat.blueprints.home',
     'hawat.blueprints.reports',
     'hawat.blueprints.events',
+    'hawat.blueprints.detectors',
     'hawat.blueprints.hosts',
     'hawat.blueprints.timeline',
     'hawat.blueprints.dnsr',
diff --git a/lib/hawat/blueprints/detectors/__init__.py b/lib/hawat/blueprints/detectors/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e521ed78d97b20f018c1b3bbdd87f7c4e077ebee
--- /dev/null
+++ b/lib/hawat/blueprints/detectors/__init__.py
@@ -0,0 +1,398 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# -------------------------------------------------------------------------------
+# This file is part of Mentat system (https://mentat.cesnet.cz/).
+#
+# Copyright (C) since 2011 CESNET, z.s.p.o (http://www.ces.net/)
+# Use of this source is governed by the MIT license, see LICENSE file.
+# -------------------------------------------------------------------------------
+
+
+"""
+This pluggable module provides access to detectors management features. These
+features include:
+
+* general detectors listing
+* detailed detector record view
+* creating new detector records
+* updating existing detector records
+* deleting existing detector records
+"""
+
+__author__ = "Rajmund Hruška <rajmund.hruska@cesnet.cz>"
+__credits__ = "Jan Mach <jan.mach@cesnet.cz>, Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"
+
+import markupsafe
+from flask_babel import gettext, lazy_gettext
+
+from sqlalchemy import or_
+
+from mentat.datatype.sqldb import DetectorModel, ItemChangeLogModel
+
+import hawat.acl
+import hawat.menu
+from hawat.base import HawatBlueprint
+from hawat.view import ItemListView, ItemShowView, ItemCreateView, ItemUpdateView, ItemDeleteView
+from hawat.view.mixin import HTMLMixin, SQLAlchemyMixin
+from hawat.blueprints.detectors.forms import AdminCreateDetectorForm, AdminUpdateDetectorForm, DetectorSearchForm
+
+BLUEPRINT_NAME = 'detectors'
+"""Name of the blueprint as module global constant."""
+
+
+class ListView(HTMLMixin, SQLAlchemyMixin, ItemListView):
+    """
+    General detector record listing.
+    """
+    methods = ['GET']
+
+    authentication = True
+
+    authorization = [hawat.acl.PERMISSION_POWER]
+
+    @classmethod
+    def get_view_title(cls, **kwargs):
+        return lazy_gettext('Detector management')
+
+    @property
+    def dbmodel(self):
+        return DetectorModel
+
+    @classmethod
+    def get_action_menu(cls):
+        action_menu = hawat.menu.Menu()
+        action_menu.add_entry(
+            'endpoint',
+            'create',
+            endpoint='detectors.create',
+            resptitle=True
+        )
+        return action_menu
+
+    @classmethod
+    def get_context_action_menu(cls):
+        action_menu = hawat.menu.Menu()
+        action_menu.add_entry(
+            'endpoint',
+            'show',
+            endpoint='detectors.show',
+            hidetitle=True
+        )
+        action_menu.add_entry(
+            'endpoint',
+            'update',
+            endpoint='detectors.update',
+            hidetitle=True
+        )
+        action_menu.add_entry(
+            'endpoint',
+            'delete',
+            endpoint='detectors.delete',
+            hidetitle=True
+        )
+        return action_menu
+
+    @staticmethod
+    def get_search_form(request_args):
+        """
+        Must return instance of :py:mod:`flask_wtf.FlaskForm` appropriate for
+        searching given type of items.
+        """
+        return DetectorSearchForm(
+            request_args,
+            meta={'csrf': False}
+        )
+
+    @staticmethod
+    def build_query(query, model, form_args):
+        # Adjust query based on text search string.
+        if 'search' in form_args and form_args['search']:
+            query = query \
+                .filter(
+                    or_(
+                        model.name.like('%{}%'.format(form_args['search'])),
+                        model.description.like('%{}%'.format(form_args['search'])),
+                    )
+                )
+        # Adjust query based on lower time boudary selection.
+        if 'dt_from' in form_args and form_args['dt_from']:
+            query = query.filter(model.createtime >= form_args['dt_from'])
+        # Adjust query based on upper time boudary selection.
+        if 'dt_to' in form_args and form_args['dt_to']:
+            query = query.filter(model.createtime <= form_args['dt_to'])
+        # Adjust query based on record source selection.
+        if 'source' in form_args and form_args['source']:
+            query = query \
+                .filter(model.source == form_args['source'])
+        if 'sortby' in form_args and form_args['sortby']:
+            sortmap = {
+                'createtime.desc': lambda x, y: x.order_by(y.createtime.desc()),
+                'createtime.asc': lambda x, y: x.order_by(y.createtime.asc()),
+                'name.desc': lambda x, y: x.order_by(y.name.desc()),
+                'name.asc': lambda x, y: x.order_by(y.name.asc()),
+                'credibility.desc': lambda x, y: x.order_by(y.credibility.desc()),
+                'credibility.asc': lambda x, y: x.order_by(y.credibility.asc())
+            }
+            query = sortmap[form_args['sortby']](query, model)
+        return query
+
+
+class ShowView(HTMLMixin, SQLAlchemyMixin, ItemShowView):
+    """
+    Detailed detector record view.
+    """
+    methods = ['GET']
+
+    authentication = True
+
+    @classmethod
+    def get_menu_legend(cls, **kwargs):
+        return lazy_gettext(
+            'View details of detector record &quot;%(item)s&quot;',
+            item=markupsafe.escape(kwargs['item'].name)
+        )
+
+    @classmethod
+    def get_view_title(cls, **kwargs):
+        return lazy_gettext('Show detector record details')
+
+    @property
+    def dbmodel(self):
+        return DetectorModel
+
+    @classmethod
+    def authorize_item_action(cls, **kwargs):
+        return hawat.acl.PERMISSION_POWER.can()
+
+    @classmethod
+    def get_action_menu(cls):
+        action_menu = hawat.menu.Menu()
+
+        action_menu.add_entry(
+            'endpoint',
+            'update',
+            endpoint='detectors.update'
+        )
+        action_menu.add_entry(
+            'endpoint',
+            'delete',
+            endpoint='detectors.delete'
+        )
+
+        return action_menu
+
+    def do_before_response(self, **kwargs):
+        item = self.response_context['item']
+        if self.can_access_endpoint('detectors.update', item=item) and self.has_endpoint('changelogs.search'):
+            self.response_context.update(
+                context_action_menu_changelogs=self.get_endpoint_class(
+                    'changelogs.search'
+                ).get_context_action_menu()
+            )
+
+            item_changelog = self.dbsession.query(ItemChangeLogModel). \
+                filter(ItemChangeLogModel.model == item.__class__.__name__). \
+                filter(ItemChangeLogModel.model_id == item.id). \
+                order_by(ItemChangeLogModel.createtime.desc()). \
+                limit(100). \
+                all()
+            self.response_context.update(item_changelog=item_changelog)
+
+
+class CreateView(HTMLMixin, SQLAlchemyMixin, ItemCreateView):  # pylint: disable=locally-disabled,too-many-ancestors
+    """
+    View for creating new detector records.
+    """
+    methods = ['GET', 'POST']
+
+    authentication = True
+
+    @classmethod
+    def get_menu_title(cls, **kwargs):
+        return lazy_gettext('Create detector record')
+
+    @classmethod
+    def get_view_title(cls, **kwargs):
+        return lazy_gettext('Create new detector record')
+
+    @property
+    def dbmodel(self):
+        return DetectorModel
+
+    @property
+    def dbchlogmodel(self):
+        return ItemChangeLogModel
+
+    @classmethod
+    def authorize_item_action(cls, **kwargs):
+        return hawat.acl.PERMISSION_POWER.can()
+
+    @staticmethod
+    def get_message_success(**kwargs):
+        return gettext(
+            'Detector record <strong>%(item_id)s</strong> was successfully created.',
+            item_id=markupsafe.escape(str(kwargs['item']))
+        )
+
+    @staticmethod
+    def get_message_failure(**kwargs):
+        return gettext('Unable to create new detector record.')
+
+    @staticmethod
+    def get_message_cancel(**kwargs):
+        return gettext('Canceled creating new detector record.')
+
+    @staticmethod
+    def get_item_form(item):
+        return AdminCreateDetectorForm()
+
+
+class UpdateView(HTMLMixin, SQLAlchemyMixin, ItemUpdateView):  # pylint: disable=locally-disabled,too-many-ancestors
+    """
+    View for updating existing detector records.
+    """
+    methods = ['GET', 'POST']
+
+    authentication = True
+
+    @classmethod
+    def get_menu_legend(cls, **kwargs):
+        return lazy_gettext(
+            'Update details of detector record &quot;%(item)s&quot;',
+            item=markupsafe.escape(kwargs['item'].name)
+        )
+
+    @classmethod
+    def get_view_title(cls, **kwargs):
+        return lazy_gettext('Update detector record details')
+
+    @property
+    def dbmodel(self):
+        return DetectorModel
+
+    @property
+    def dbchlogmodel(self):
+        return ItemChangeLogModel
+
+    @classmethod
+    def authorize_item_action(cls, **kwargs):
+        return hawat.acl.PERMISSION_POWER.can()
+
+    @staticmethod
+    def get_message_success(**kwargs):
+        return gettext(
+            'Detector record <strong>%(item_id)s</strong> was successfully updated.',
+            item_id=markupsafe.escape(str(kwargs['item']))
+        )
+
+    @staticmethod
+    def get_message_failure(**kwargs):
+        return gettext(
+            'Unable to update detector record <strong>%(item_id)s</strong>.',
+            item_id=markupsafe.escape(str(kwargs['item']))
+        )
+
+    @staticmethod
+    def get_message_cancel(**kwargs):
+        return gettext(
+            'Canceled updating detector record <strong>%(item_id)s</strong>.',
+            item_id=markupsafe.escape(str(kwargs['item']))
+        )
+
+    @staticmethod
+    def get_item_form(item):
+        return AdminUpdateDetectorForm(db_item_id=item.id, obj=item)
+
+
+class DeleteView(HTMLMixin, SQLAlchemyMixin, ItemDeleteView):  # pylint: disable=locally-disabled,too-many-ancestors
+    """
+    View for deleting existing detector records.
+    """
+    methods = ['GET', 'POST']
+
+    authentication = True
+
+    @classmethod
+    def get_menu_legend(cls, **kwargs):
+        return lazy_gettext(
+            'Delete detector record &quot;%(item)s&quot;',
+            item=markupsafe.escape(kwargs['item'].name)
+        )
+
+    @property
+    def dbmodel(self):
+        return DetectorModel
+
+    @property
+    def dbchlogmodel(self):
+        return ItemChangeLogModel
+
+    @classmethod
+    def authorize_item_action(cls, **kwargs):
+        return hawat.acl.PERMISSION_POWER.can()
+
+    @staticmethod
+    def get_message_success(**kwargs):
+        return gettext(
+            'Detector record <strong>%(item_id)s</strong> was successfully deleted.',
+            item_id=markupsafe.escape(str(kwargs['item']))
+        )
+
+    @staticmethod
+    def get_message_failure(**kwargs):
+        return gettext(
+            'Unable to delete detector record <strong>%(item_id)s</strong>.',
+            item_id=markupsafe.escape(str(kwargs['item']))
+        )
+
+    @staticmethod
+    def get_message_cancel(**kwargs):
+        return gettext(
+            'Canceled deleting detector record <strong>%(item_id)s</strong>.',
+            item_id=markupsafe.escape(str(kwargs['item']))
+        )
+
+
+# -------------------------------------------------------------------------------
+
+
+class DetectorsBlueprint(HawatBlueprint):
+    """Pluggable module - detector management (*detectors*)."""
+
+    @classmethod
+    def get_module_title(cls):
+        return lazy_gettext('Detector management')
+
+    def register_app(self, app):
+        app.menu_main.add_entry(
+            'view',
+            'admin.{}'.format(BLUEPRINT_NAME),
+            position=71,
+            view=ListView
+        )
+
+
+# -------------------------------------------------------------------------------
+
+
+def get_blueprint():
+    """
+    Mandatory interface for :py:mod:`hawat.Hawat` and factory function. This function
+    must return a valid instance of :py:class:`hawat.app.HawatBlueprint` or
+    :py:class:`flask.Blueprint`.
+    """
+
+    hbp = DetectorsBlueprint(
+        BLUEPRINT_NAME,
+        __name__,
+        template_folder='templates',
+        url_prefix='/{}'.format(BLUEPRINT_NAME)
+    )
+
+    hbp.register_view_class(ListView, '/list')
+    hbp.register_view_class(CreateView, '/create')
+    hbp.register_view_class(ShowView, '/<int:item_id>/show')
+    hbp.register_view_class(UpdateView, '/<int:item_id>/update')
+    hbp.register_view_class(DeleteView, '/<int:item_id>/delete')
+
+    return hbp
diff --git a/lib/hawat/blueprints/detectors/forms.py b/lib/hawat/blueprints/detectors/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..a25833599d4b17fe6767b890148a35e523a400a0
--- /dev/null
+++ b/lib/hawat/blueprints/detectors/forms.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# -------------------------------------------------------------------------------
+# This file is part of Mentat system (https://mentat.cesnet.cz/).
+#
+# Copyright (C) since 2011 CESNET, z.s.p.o (http://www.ces.net/)
+# Use of this source is governed by the MIT license, see LICENSE file.
+# -------------------------------------------------------------------------------
+
+
+"""
+This module contains custom detector management forms for Hawat.
+"""
+
+__author__ = "Rajmund Hruška <rajmund.hruska@cesnet.cz>"
+__credits__ = "Jan Mach <jan.mach@cesnet.cz>, Pavel Kácha <pavel.kacha@cesnet.cz>, Andrea Kropáčová <andrea.kropacova@cesnet.cz>"
+
+import wtforms
+
+#
+# Flask related modules.
+#
+from flask_babel import lazy_gettext, gettext
+
+#
+# Custom modules.
+#
+import hawat.const
+import hawat.forms
+import hawat.db
+
+from mentat.datatype.sqldb import DetectorModel
+
+
+def get_available_sources():
+    """
+    Query the database for list of network record sources.
+    """
+    result = hawat.db.db_query(DetectorModel) \
+        .distinct(DetectorModel.source) \
+        .order_by(DetectorModel.source) \
+        .all()
+    return [x.source for x in result]
+
+
+def check_name_uniqueness(form, field):
+    """
+    Callback for validating names during detector update action.
+    """
+    item = hawat.db.db_get().session.query(DetectorModel). \
+        filter(DetectorModel.name == field.data). \
+        filter(DetectorModel.id != form.db_item_id). \
+        all()
+    if not item:
+        return
+    raise wtforms.validators.ValidationError(gettext('Detector with this name already exists.'))
+
+
+class BaseDetectorForm(hawat.forms.BaseItemForm):
+    """
+    Class representing base detector record form.
+    """
+    source = wtforms.HiddenField(
+        default='manual',
+        validators=[
+            wtforms.validators.DataRequired(),
+            wtforms.validators.Length(min=3, max=50)
+        ]
+    )
+    credibility = wtforms.FloatField(
+        lazy_gettext('Credibility:'),
+        validators=[
+            wtforms.validators.Optional(),
+            wtforms.validators.NumberRange(min=0, max=1)
+        ]
+    )
+    description = wtforms.TextAreaField(
+        lazy_gettext('Description:')
+    )
+    submit = wtforms.SubmitField(
+        lazy_gettext('Submit')
+    )
+    cancel = wtforms.SubmitField(
+        lazy_gettext('Cancel')
+    )
+
+
+class AdminCreateDetectorForm(BaseDetectorForm):
+    """
+    Class representing detector record create form.
+    """
+    name = wtforms.StringField(
+        lazy_gettext('Name:'),
+        validators=[
+            wtforms.validators.DataRequired(),
+            wtforms.validators.Length(min=3, max=250),
+            hawat.forms.check_null_character,
+            check_name_uniqueness
+        ]
+    )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.db_item_id = None
+
+class AdminUpdateDetectorForm(BaseDetectorForm):
+    """
+    Class representing detector record create form.
+    """
+    name = wtforms.StringField(
+        lazy_gettext('Name:'),
+        validators=[
+            wtforms.validators.DataRequired(),
+            wtforms.validators.Length(min=3, max=250),
+            hawat.forms.check_null_character,
+            check_name_uniqueness
+        ]
+    )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Store the ID of original item in database to enable the ID uniqueness
+        # check with check_name_uniqueness() validator.
+        self.db_item_id = kwargs['db_item_id']
+
+
+class DetectorSearchForm(hawat.forms.BaseSearchForm):
+    """
+    Class representing simple user search form.
+    """
+    search = wtforms.StringField(
+        lazy_gettext('Name, description:'),
+        validators=[
+            wtforms.validators.Optional(),
+            wtforms.validators.Length(min=3, max=100),
+            hawat.forms.check_null_character
+        ],
+        description=lazy_gettext(
+            'Detector`s name or description. Search is performed even in the middle of the strings.')
+    )
+    dt_from = hawat.forms.SmartDateTimeField(
+        lazy_gettext('Creation time from:'),
+        validators=[
+            wtforms.validators.Optional()
+        ],
+        description=lazy_gettext(
+            'Lower time boundary for item creation time. Timestamp is expected to be in the format <code>YYYY-MM-DD hh:mm:ss</code> and in the timezone according to the user`s preferences.')
+    )
+    dt_to = hawat.forms.SmartDateTimeField(
+        lazy_gettext('Creation time to:'),
+        validators=[
+            wtforms.validators.Optional()
+        ],
+        description=lazy_gettext(
+            'Upper time boundary for item creation time. Timestamp is expected to be in the format <code>YYYY-MM-DD hh:mm:ss</code> and in the timezone according to the user`s preferences.')
+    )
+
+    source = wtforms.SelectField(
+        lazy_gettext('Record source:'),
+        validators=[
+            wtforms.validators.Optional()
+        ],
+        default=''
+    )
+
+    sortby = wtforms.SelectField(
+        lazy_gettext('Sort by:'),
+        validators=[
+            wtforms.validators.Optional()
+        ],
+        choices=[
+            ('createtime.desc', lazy_gettext('by creation time descending')),
+            ('createtime.asc', lazy_gettext('by creation time ascending')),
+            ('name.desc', lazy_gettext('by name descending')),
+            ('name.asc', lazy_gettext('by name ascending')),
+            ('credibility.asc', lazy_gettext('by credibility ascending')),
+            ('credibility.asc', lazy_gettext('by credibility descending'))
+        ],
+        default='name.asc'
+    )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        #
+        # Handle additional custom keywords.
+        #
+
+        # The list of choices for 'roles' attribute comes from outside of the
+        # form to provide as loose tie as possible to the outer application.
+        # Another approach would be to load available choices here with:
+        #
+        #   roles = flask.current_app.config['ROLES']
+        #
+        # That would mean direct dependency on flask.Flask application.
+        source_list = get_available_sources()
+        self.source.choices = [('', lazy_gettext('Nothing selected'))] + list(zip(source_list, source_list))
+
+    @staticmethod
+    def is_multivalue(field_name):
+        """
+        Check, if given form field is a multivalue field.
+
+        :param str field_name: Name of the form field.
+        :return: ``True``, if the field can contain multiple values, ``False`` otherwise.
+        :rtype: bool
+        """
+        return False
diff --git a/lib/hawat/blueprints/detectors/templates/detectors/creatupdate.html b/lib/hawat/blueprints/detectors/templates/detectors/creatupdate.html
new file mode 100644
index 0000000000000000000000000000000000000000..f8e3a59d3377577dc94cc95da330099a927263e8
--- /dev/null
+++ b/lib/hawat/blueprints/detectors/templates/detectors/creatupdate.html
@@ -0,0 +1,11 @@
+{%- extends "_layout_creatupdate.html" %}
+
+{%- block itemform_fields %}
+
+                                {{ macros_form.render_form_item_default(form.name) }}
+
+                                {{ macros_form.render_form_item_default(form.description) }}
+
+                                {{ macros_form.render_form_item_default(form.credibility) }}
+
+{%- endblock itemform_fields %}
diff --git a/lib/hawat/blueprints/detectors/templates/detectors/list.html b/lib/hawat/blueprints/detectors/templates/detectors/list.html
new file mode 100644
index 0000000000000000000000000000000000000000..cc75aa51504ce1987c2bf52c29bb2d9d4cc99f65
--- /dev/null
+++ b/lib/hawat/blueprints/detectors/templates/detectors/list.html
@@ -0,0 +1,69 @@
+{%- extends "_layout_list.html" %}
+
+{%- block searchformfields %}
+
+                            <div class="row">
+                                <div class="col-sm-4">
+                                    {{ macros_form.render_form_item_default(g.search_form.search) }}
+                                </div>
+                                <div class="col-sm-4">
+                                    {{ macros_form.render_form_item_datetime(g.search_form.dt_from, 'datetimepicker-hm-from') }}
+                                </div>
+                                <div class="col-sm-4">
+                                    {{ macros_form.render_form_item_datetime(g.search_form.dt_to, 'datetimepicker-hm-to') }}
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-sm-6">
+                                    {{ macros_form.render_form_item_select(g.search_form.source) }}
+                                </div>
+                            </div>
+
+{%- endblock searchformfields %}
+
+{%- block contentinner %}
+
+                    <table class="table table-bordered table-hover table-striped">
+                        <thead>
+                            <tr>
+                                <th>
+                                    {{ _('Name') }} {{ macros_site.render_sorter(request.endpoint, query_params, 'name') }}
+                                </th>
+                                <th>
+                                    {{ _('Source') }}
+                                </th>
+                                <th>
+                                    {{ _('Credibility') }} {{ macros_site.render_sorter(request.endpoint, query_params, 'credibility') }}
+                                </th>
+                                <th>
+                                    {{ _('Description') }}
+                                </th>
+                                <th data-toggle="tooltip" title="{{ _('Contextual item actions') }}">
+                                    {{ get_icon('actions') }} {{ _('Actions') }}
+                                </th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            {%- for item in items %}
+                            <tr>
+                                <td>
+                                    {{ item.name | default(_('<< unknown >>'), True) }}
+                                </td>
+                                <td>
+                                    {{ item.source | default(_('<< unknown >>'), True) }}
+                                </td>
+                                <td>
+                                    {{ item.credibility | default(_('<< unknown >>'), True) }}
+                                </td>
+                                <td>
+                                    {{ item.description | default(_('<< unknown >>'), True) | truncate(50) }}
+                                </td>
+                                <td class="column-actions">
+                                    {{ macros_page.render_menu_context_actions(item) }}
+                                </td>
+                            </tr>
+                            {%- endfor %}
+                        </tbody>
+                    </table>
+
+{%- endblock contentinner %}
diff --git a/lib/hawat/blueprints/detectors/templates/detectors/show.html b/lib/hawat/blueprints/detectors/templates/detectors/show.html
new file mode 100644
index 0000000000000000000000000000000000000000..f54dab447a69cbff0e576829761763186c833dcd
--- /dev/null
+++ b/lib/hawat/blueprints/detectors/templates/detectors/show.html
@@ -0,0 +1,113 @@
+{%- extends "_layout.html" %}
+
+{%- block content %}
+
+            <div class="row">
+                <div class="col-lg-12">
+                    {{ macros_page.render_breadcrumbs(item) }}
+
+                    <h2>{{ hawat_current_view.get_view_title() }}</h2>
+                    <hr>
+                    <h3>{{ item.name }}</h3>
+                    <div class="pull-right">
+                        {{ macros_page.render_menu_actions(item) }}
+
+                    </div>
+                    <p>
+                        <small>
+                            <strong>{{ _('Detector created') }}:</strong> {{ babel_format_datetime(item.createtime) }} ({{ _('%(delta)s ago', delta = babel_format_timedelta(current_datetime_utc - item.createtime)) }})
+                        </small>
+                    </p>
+                    <br>
+
+                    <!-- Nav tabs -->
+                    <ul class="nav nav-tabs" role="tablist">
+                        <li role="presentation" class="active">
+                            <a href="#tab-general" aria-controls="tab-general" role="tab" data-toggle="tab">
+                                <strong>{{ get_icon('alert-info') }} {{ _('General information') }}</strong>
+                            </a>
+                        </li>
+                        {%- if can_access_endpoint('detectors.update', item) %}
+                        <li role="presentation">
+                            <a href="#tab-changelog" aria-controls="tab-changelog" role="tab" data-toggle="tab">
+                                <strong>{{ get_icon('module-changelogs') }} {{ _('Changelogs') }}</strong> <span class="badge">{{ item_changelog | length }}</span>
+                            </a>
+                        </li>
+                        {%- endif %}
+                    </ul>
+
+                    <!-- Tab panes -->
+                    <div class="tab-content">
+
+                        <div role="tabpanel" class="tab-pane fade in active" id="tab-general">
+                            <br>
+                            <table class="table table-striped">
+                                <tbody>
+                                    <tr>
+                                        <th>
+                                            {{ _('Name') }}:
+                                        </th>
+                                        <td>
+                                            {{ item.name | default(_('<< unknown >>'), True) }}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th>
+                                            {{ _('Source') }}:
+                                        </th>
+                                        <td>
+                                            {{ item.source | default(_('<< unknown >>'), True) }}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th>
+                                            {{ _('Description') }}:
+                                        </th>
+                                        <td>
+                                            {{ item.description | default(_('<< unknown >>'), True) }}
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th>
+                                            {{ _('Credibility') }}:
+                                        </th>
+                                        <td>
+                                            {{ item.credibility }}
+                                        </td>
+                                    </tr>
+                                </tbody>
+                            </table>
+                        </div>
+
+                        {%- if can_access_endpoint('detectors.update', item) %}
+                        <div role="tabpanel" class="tab-pane fade" id="tab-changelog">
+                            <br>
+                            {%- if item_changelog %}
+                            {{ macros_page.render_changelog_records(item_changelog, context_action_menu_changelogs) }}
+                            <p>
+                                <small>
+                                    {{ _('Displaying only latest %(count)s changelogs', count = 100) }}
+                                </small>
+                            </p>
+                            {%- else %}
+                            {%- call macros_site.render_alert('info', False) %}
+                            {{ _('This object does not have any changelog records at the moment.') }}
+                            {%- endcall %}
+                            {%- endif %}
+                        </div>
+                        {%- endif %}
+
+                    </div>
+
+                </div><!-- /.col-lg-12 -->
+            </div><!-- /.row -->
+
+    {%- if permission_can('developer') %}
+
+            <hr>
+
+            {{ macros_site.render_raw_item_view(item) }}
+
+    {%- endif %}
+
+{%- endblock content %}
diff --git a/lib/hawat/config.py b/lib/hawat/config.py
index 8f09ca9cb91958fbab1782ade0e889d24544103d..c35b323f092fe3c54264e04f17cc82d7abe9fb2d 100644
--- a/lib/hawat/config.py
+++ b/lib/hawat/config.py
@@ -162,6 +162,7 @@ class Config:  # pylint: disable=locally-disabled,too-few-public-methods
         'hawat.blueprints.home',
         'hawat.blueprints.reports',
         'hawat.blueprints.events',
+        'hawat.blueprints.detectors',
         'hawat.blueprints.hosts',
         'hawat.blueprints.timeline',
         'hawat.blueprints.dnsr',
@@ -313,6 +314,7 @@ class DevelopmentConfig(Config):  # pylint: disable=locally-disabled,too-few-pub
         'hawat.blueprints.home',
         'hawat.blueprints.reports',
         'hawat.blueprints.events',
+        'hawat.blueprints.detectors',
         'hawat.blueprints.hosts',
         'hawat.blueprints.timeline',
         'hawat.blueprints.dnsr',
@@ -363,6 +365,7 @@ class TestingConfig(Config):  # pylint: disable=locally-disabled,too-few-public-
         'hawat.blueprints.home',
         'hawat.blueprints.reports',
         'hawat.blueprints.events',
+        'hawat.blueprints.detectors',
         'hawat.blueprints.hosts',
         'hawat.blueprints.timeline',
         'hawat.blueprints.dnsr',
diff --git a/lib/hawat/const.py b/lib/hawat/const.py
index 88823315b846185d57be41a24cb3d6b4ef17ccd7..3e340f7aaa5510e6aa63b49cff8c983b84c90b7f 100644
--- a/lib/hawat/const.py
+++ b/lib/hawat/const.py
@@ -236,6 +236,7 @@ ICONS = {
     'module-dashboards': '<i class="fas fa-fw fa-tachometer-alt"></i>',
     'module-dbstatus': '<i class="fas fa-fw fa-database"></i>',
     'module-design': '<i class="fas fa-fw fa-palette"></i>',
+    'module-detectors': '<i class="fas fa-fw fa-user-secret"></i>',
     'module-devtools': '<i class="fas fa-fw fa-bug"></i>',
     'module-dnsr': '<i class="fas fa-fw fa-directions"></i>',
     'module-events': '<i class="fas fa-fw fa-bell"></i>',