diff --git a/.gitignore b/.gitignore
index 431e4395b6e6a5ccdbbed6d7c978e86af6fc4f5d..026d091724e79fbb527d8330235ed394c2c5a3a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,7 @@ dist
 build
 eggs
 parts
-bin
+bin/[a-b,d-z]
 var
 sdist
 develop-eggs
@@ -35,6 +35,7 @@ nosetests.xml
 .project
 .pydevproject
 .settings
+.idea
 
 # Generated files
 src/cutecoin/gen_resources/*
diff --git a/bin/cutecoin b/bin/cutecoin
new file mode 100755
index 0000000000000000000000000000000000000000..e8fa6c9da337427a4c9d4efa5c03bf256cfd6990
--- /dev/null
+++ b/bin/cutecoin
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+"""
+Created on 1 févr. 2014
+
+@author: inso
+"""
+import signal
+import sys
+import os
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../src')))
+from PyQt5.QtWidgets import QApplication, QDialog
+from cutecoin.gui.mainWindow import MainWindow
+from cutecoin.core.app import Application
+
+
+if __name__ == '__main__':
+    # activate ctrl-c interrupt
+    signal.signal(signal.SIGINT, signal.SIG_DFL)
+    cutecoin = QApplication(sys.argv)
+    app = Application(sys.argv)
+    window = MainWindow(app)
+    window.show()
+    sys.exit(cutecoin.exec_())
+    pass
diff --git a/res/ui/communityTabWidget.ui b/res/ui/communityTabWidget.ui
index 8a16e9120d7baf5717067aa9e09c5ce8615b78e8..8cec0bb4205f3f08363bd05b952b82bce0502b20 100644
--- a/res/ui/communityTabWidget.ui
+++ b/res/ui/communityTabWidget.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>400</width>
-    <height>300</height>
+    <width>421</width>
+    <height>369</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -15,45 +15,55 @@
   </property>
   <layout class="QHBoxLayout" name="horizontalLayout">
    <item>
-    <layout class="QVBoxLayout" name="verticalLayout_6">
-     <item>
-      <widget class="QLabel" name="label">
-       <property name="text">
-        <string>Members</string>
-       </property>
-      </widget>
-     </item>
-     <item>
-      <widget class="QListView" name="list_community_members"/>
-     </item>
-     <item>
-      <layout class="QHBoxLayout" name="horizontalLayout_2">
-       <property name="leftMargin">
-        <number>0</number>
-       </property>
-       <property name="topMargin">
-        <number>5</number>
-       </property>
+    <widget class="QTabWidget" name="tabs_community">
+     <property name="currentIndex">
+      <number>1</number>
+     </property>
+     <widget class="QWidget" name="tab_members">
+      <attribute name="title">
+       <string>Members</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout">
        <item>
-        <widget class="QLabel" name="label_quality">
-         <property name="text">
-          <string>Quality : </string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QPushButton" name="button_membership">
-         <property name="text">
-          <string>Send membership demand</string>
-         </property>
-        </widget>
+        <layout class="QVBoxLayout" name="verticalLayout_6">
+         <item>
+          <widget class="QListView" name="list_community_members"/>
+         </item>
+         <item>
+          <layout class="QHBoxLayout" name="horizontalLayout_2">
+           <property name="leftMargin">
+            <number>0</number>
+           </property>
+           <property name="topMargin">
+            <number>5</number>
+           </property>
+           <item>
+            <widget class="QLabel" name="label_quality">
+             <property name="text">
+              <string>Quality : </string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QPushButton" name="button_membership">
+             <property name="text">
+              <string>Send membership demand</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+        </layout>
        </item>
       </layout>
-     </item>
-    </layout>
-   </item>
-   <item>
-    <layout class="QVBoxLayout" name="verticalLayout"/>
+     </widget>
+     <widget class="QWidget" name="tab_wot">
+      <attribute name="title">
+       <string>Wot</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2"/>
+     </widget>
+    </widget>
    </item>
   </layout>
  </widget>
diff --git a/res/ui/wot_form.ui b/res/ui/wot_form.ui
new file mode 100644
index 0000000000000000000000000000000000000000..ff58cb34a110af213ad4e0e14a7498670a75e0fe
--- /dev/null
+++ b/res/ui/wot_form.ui
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>522</width>
+    <height>442</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QComboBox" name="comboBoxSearch">
+     <property name="editable">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QPushButton" name="pushButtonReset">
+     <property name="maximumSize">
+      <size>
+       <width>85</width>
+       <height>27</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>Me</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" colspan="2">
+    <widget class="View" name="graphicsView">
+     <property name="viewportUpdateMode">
+      <enum>QGraphicsView::BoundingRectViewportUpdate</enum>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>View</class>
+   <extends>QGraphicsView</extends>
+   <header>cutecoin.wot.qt.view.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>pushButtonReset</sender>
+   <signal>clicked()</signal>
+   <receiver>Form</receiver>
+   <slot>reset()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>516</x>
+     <y>23</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>284</x>
+     <y>198</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>comboBoxSearch</sender>
+   <signal>currentIndexChanged(int)</signal>
+   <receiver>Form</receiver>
+   <slot>select_node()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>215</x>
+     <y>22</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>260</x>
+     <y>220</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <slots>
+  <slot>reset()</slot>
+  <slot>search()</slot>
+  <slot>select_node()</slot>
+ </slots>
+</ui>
diff --git a/setup.py b/setup.py
index bcf1812ff6eb922e57ec5440dcb5a203e340c71b..c19b45b44dd84c33dff6837abf7eb3ed20da75f6 100644
--- a/setup.py
+++ b/setup.py
@@ -15,6 +15,7 @@ print(sys.path)
 includes = ["sip", "re", "json", "logging", "hashlib", "os", "urllib", "ucoinpy", "requests", "cutecoin.core"]
 excludes = []
 packages = ["libnacl", "pylibscrypt"]
+
 includefiles = []
 
 options = {"path": sys.path,
diff --git a/src/cutecoin/core/community.py b/src/cutecoin/core/community.py
index 3561dc677c3dde4ecd9109f25130399feb54d1e7..5ccf5d0e06cc62a0dc7fb66be0745fba7cc15060 100644
--- a/src/cutecoin/core/community.py
+++ b/src/cutecoin/core/community.py
@@ -126,3 +126,6 @@ class Community(object):
         data = {'currency': self.currency,
                 'peers': self.jsonify_peers_list()}
         return data
+
+    def get_parameters(self):
+        return self.request(bma.blockchain.Parameters)
diff --git a/src/cutecoin/gui/communityTabWidget.py b/src/cutecoin/gui/communityTabWidget.py
index 40ed31f1903b99bd751258e161a51c033ce744b2..1424d8408cbb2359fa9322d3b7b4d6fb89832d9e 100644
--- a/src/cutecoin/gui/communityTabWidget.py
+++ b/src/cutecoin/gui/communityTabWidget.py
@@ -9,6 +9,7 @@ from PyQt5.QtWidgets import QWidget, QErrorMessage
 from cutecoin.models.members import MembersListModel
 from cutecoin.gen_resources.communityTabWidget_uic import Ui_CommunityTabWidget
 from cutecoin.gui.addContactDialog import AddContactDialog
+from cutecoin.wot.qt.form import Form
 
 
 class CommunityTabWidget(QWidget, Ui_CommunityTabWidget):
@@ -34,6 +35,9 @@ class CommunityTabWidget(QWidget, Ui_CommunityTabWidget):
             self.button_membership.setText("Send membership demand")
             self.button_membership.clicked.connect(self.send_membership_demand)
 
+        # create wot widget
+        self.verticalLayout_2.addWidget(Form(account, community))
+
     def add_member_as_contact(self, index):
         members_model = self.list_community_members.model()
         members = members_model.members
diff --git a/src/cutecoin/wot/__init__.py b/src/cutecoin/wot/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/cutecoin/wot/qt/__init__.py b/src/cutecoin/wot/qt/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/cutecoin/wot/qt/form.py b/src/cutecoin/wot/qt/form.py
new file mode 100644
index 0000000000000000000000000000000000000000..67c14a4127cc79cd8015ba14caa8a7336a0dad9d
--- /dev/null
+++ b/src/cutecoin/wot/qt/form.py
@@ -0,0 +1,154 @@
+# -*- coding: utf-8 -*-
+
+import time
+import datetime
+from PyQt5.QtWidgets import QWidget
+
+from cutecoin.gen_resources.wot_form_uic import Ui_Form
+from cutecoin.wot.qt.view import NODE_STATUS_HIGHLIGHTED, NODE_STATUS_SELECTED, ARC_STATUS_STRONG, ARC_STATUS_WEAK
+from ucoinpy.api import bma
+
+
+class Form(QWidget, Ui_Form):
+    def __init__(self, account, community, parent=None):
+        """
+
+        :param cutecoin.core.account.Account account:
+        :param cutecoin.core.community.Community community:
+        :param parent:
+        :return:
+        """
+        super(Form, self).__init__(parent)
+
+        # construct from qtDesigner
+        self.setupUi(self)
+
+        # add combobox events
+        self.comboBoxSearch.lineEdit().textEdited.connect(self.search)
+        self.comboBoxSearch.lineEdit().returnPressed.connect(self.combobox_return_pressed)
+
+        # add scene events
+        self.graphicsView.scene().node_signed.connect(self.sign_node)
+        self.graphicsView.scene().node_clicked.connect(self.draw_graph)
+
+        self.account = account
+        self.community = community
+
+        # nodes list for menu from search
+        self.nodes = list()
+        self.signature_validity = self.community.get_parameters()['sigValidity']
+        # arc considered strong during 75% of signature validity time
+        self.ARC_STATUS_STRONG_time = int(self.signature_validity * 0.75)
+        self.draw_graph(self.account.pubkey)
+
+    def draw_graph(self, public_key):
+        """
+        Draw community graph centered on public_key identity
+
+        :param public_key: Public key of the identity
+        """
+        graph = dict()
+        # add wallet node
+        node_status = (NODE_STATUS_HIGHLIGHTED and (public_key == self.account.pubkey)) or 0
+        node_status += NODE_STATUS_SELECTED
+        certifiers = self.community.request(bma.wot.CertifiersOf, {'search': public_key})
+
+        graph[public_key] = {'arcs': [], 'text': certifiers['uid'], 'tooltip': public_key, 'status': node_status}
+
+        # add certifiers of uid
+        for certifier in certifiers['certifications']:
+            if (time.time() - certifier['cert_time']['medianTime']) > self.ARC_STATUS_STRONG_time:
+                arc_status = ARC_STATUS_WEAK
+            else:
+                arc_status = ARC_STATUS_STRONG
+            arc = {
+                'id': public_key,
+                'status': arc_status,
+                'tooltip': datetime.datetime.fromtimestamp(
+                    certifier['cert_time']['medianTime'] + self.signature_validity
+                ).strftime("%Y/%m/%d")
+            }
+            if certifier['pubkey'] not in graph.keys():
+                node_status = (NODE_STATUS_HIGHLIGHTED and (certifier['pubkey'] == self.account.pubkey)) or 0
+                graph[certifier['pubkey']] = {
+                    'arcs': [arc],
+                    'text': certifier['uid'],
+                    'tooltip': certifier['pubkey'],
+                    'status': node_status
+                }
+
+        # add certified by uid
+        for certified in self.community.request(bma.wot.CertifiedBy, {'search': public_key})['certifications']:
+            if (time.time() - certified['cert_time']['medianTime']) > self.ARC_STATUS_STRONG_time:
+                arc_status = ARC_STATUS_WEAK
+            else:
+                arc_status = ARC_STATUS_STRONG
+            arc = {
+                'id': certified['pubkey'],
+                'status': arc_status,
+                'tooltip': datetime.datetime.fromtimestamp(
+                    certified['cert_time']['medianTime'] + self.signature_validity
+                ).strftime("%Y/%m/%d")
+            }
+            graph[public_key]['arcs'].append(arc)
+            if certified['pubkey'] not in graph.keys():
+                node_status = (NODE_STATUS_HIGHLIGHTED and (certified['pubkey'] == self.account.pubkey)) or 0
+                graph[certified['pubkey']] = {
+                    'arcs': list(),
+                    'text': certified['uid'],
+                    'tooltip': certified['pubkey'],
+                    'status': node_status
+                }
+
+        # draw graph in qt scene
+        self.graphicsView.scene().update_wot(graph)
+
+    def reset(self):
+        """
+        Reset graph scene to wallet identity
+        """
+        self.draw_graph(
+            self.account.pubkey
+        )
+
+    def combobox_return_pressed(self):
+        """
+        Search nodes when return is pressed in combobox lineEdit
+        """
+        self.search(self.comboBoxSearch.lineEdit().text())
+
+    def search(self, text):
+        """
+        Search nodes when text is edited in combobox lineEdit
+        """
+        if len(text) < 2:
+            return False
+
+        response = self.community.request(bma.wot.Lookup, {'search': text})
+        nodes = {}
+        for identity in response['results']:
+            if identity['uids'][0]['others']:
+                nodes[identity['pubkey']] = identity['uids'][0]['uid']
+
+        if nodes:
+            self.nodes = list()
+            self.comboBoxSearch.clear()
+            self.comboBoxSearch.lineEdit().setText(text)
+            for pubkey, uid in nodes.items():
+                self.nodes.append({'pubkey': pubkey, 'uid': uid})
+                self.comboBoxSearch.addItem(uid)
+            self.comboBoxSearch.showPopup()
+
+    def select_node(self, index):
+        """
+        Select node in graph when item is selected in combobox
+        """
+        if index < 0 or index >= len(self.nodes):
+            return False
+        node = self.nodes[index]
+        self.draw_graph(
+                node['pubkey']
+        )
+
+    def sign_node(self, public_key):
+        print('sign node {} not implemented'.format(public_key))
diff --git a/src/cutecoin/wot/qt/view.py b/src/cutecoin/wot/qt/view.py
new file mode 100644
index 0000000000000000000000000000000000000000..9aa5a4e57edf866102648f6fec752bf4083e97e3
--- /dev/null
+++ b/src/cutecoin/wot/qt/view.py
@@ -0,0 +1,417 @@
+# -*- coding: utf-8 -*-
+
+import math
+from PyQt5.QtGui import QPainter, QBrush, QPen, QPolygonF, QColor, QRadialGradient,\
+    QPainterPath, QMouseEvent, QWheelEvent, QTransform
+from PyQt5.QtCore import Qt, QRectF, QLineF, QPoint, QPointF, QSizeF, qFuzzyCompare, pyqtSignal
+from PyQt5.QtWidgets import QGraphicsView, QGraphicsScene, QGraphicsEllipseItem,\
+    QGraphicsSimpleTextItem, QGraphicsLineItem, QMenu, QAction, QGraphicsSceneHoverEvent,\
+    QGraphicsSceneContextMenuEvent
+
+NODE_STATUS_HIGHLIGHTED = 1
+NODE_STATUS_SELECTED = 2
+ARC_STATUS_STRONG = 1
+ARC_STATUS_WEAK = 2
+
+
+class View(QGraphicsView):
+    def __init__(self, parent=None):
+        """
+        Create View to display scene
+
+        :param parent:  [Optional, default=None] Parent widget
+        """
+        super(View, self).__init__(parent)
+
+        self.setScene(Scene(self))
+
+        self.setCacheMode(QGraphicsView.CacheBackground)
+        self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
+        self.setRenderHint(QPainter.Antialiasing)
+        self.setRenderHint(QPainter.SmoothPixmapTransform)
+
+    def wheelEvent(self, event: QWheelEvent):
+        """
+        Zoom in/out on the mouse cursor
+        """
+        # zoom only when CTRL key pressed
+        if (event.modifiers() & Qt.ControlModifier) == Qt.ControlModifier:
+            steps = event.angleDelta().y() / 15 / 8
+
+            if steps == 0:
+                event.ignore()
+                return
+
+            # scale factor 1.25
+            sc = pow(1.25, steps)
+            self.scale(sc, sc)
+            self.centerOn(self.mapToScene(event.pos()))
+            event.accept()
+        # act normally on scrollbar
+        else:
+            # transmit event to parent class wheelevent
+            super(QGraphicsView, self).wheelEvent(event)
+
+
+class Scene(QGraphicsScene):
+
+    # This defines a signal called 'nodeSigned' that takes on string argument
+    node_signed = pyqtSignal(str, name='nodeSigned')
+    node_clicked = pyqtSignal(str, name='nodeClicked')
+
+    def __init__(self, parent=None):
+        """
+        Create scene of the graph
+
+        :param parent: [Optional, default=None] Parent view
+        """
+        super(Scene, self).__init__(parent)
+
+        self.lastDragPos = QPoint()
+        self.setItemIndexMethod(QGraphicsScene.NoIndex)
+
+        # axis of the scene for debug purpose
+        # self.addLine(-100, 0, 100, 0)
+        # self.addLine(0, -100, 0, 100)
+
+    def add_node(self, _id, node, pos):
+        """
+        Add a node item in the graph
+
+        :param str _id: Node id
+        :param dict node: Node data
+        :param tuple pos: Position (x,y) of the node
+
+        :return: Node
+        """
+        node = Node(_id, node, pos)
+        self.addItem(node)
+        return node
+
+    def add_arc(self, source_node, destination_node, arc):
+        """
+        Add an arc between two nodes
+
+        :param Node source_node: Source node of the arc
+        :param Node destination_node: Destination node of the arc
+        :param dict arc: Arc data
+
+        :return: Arc
+        """
+        arc = Arc(source_node, destination_node, arc)
+        self.addItem(arc)
+        return arc
+
+    def update_wot(self, graph):
+        """
+        draw community graph
+
+        :param dict graph: graph to draw
+        """
+        # clear scene
+        self.clear()
+
+        # capture selected node (to draw it in the center)
+        for _id, node in graph.items():
+            if node['status'] & NODE_STATUS_SELECTED:
+                selected_id = _id
+                selected_node = node
+
+        root_node = self.add_node(selected_id, selected_node, (0, 0))
+
+        # add certified by selected node
+        y = 0
+        x = 200
+        # capture nodes for sorting by text
+        nodes = dict()
+        for arc in selected_node['arcs']:
+            nodes[arc['id']] = {'node': graph[arc['id']], 'arc': arc}
+        # sort by text
+        nodes = ((k, v) for (k, v) in sorted(nodes.items(), key=lambda kv: kv[1]['node']['text'].lower()))
+        # add nodes and arcs
+        for _id, items in nodes:
+            node = self.add_node(_id, items['node'], (x, y))
+            self.add_arc(root_node, node, items['arc'])
+            y += 50
+
+        # add certifiers of selected node
+        y = 0
+        x = -200
+        # sort by text
+        nodes = ((k, v) for (k, v) in sorted(graph.items(), key=lambda kv: kv[1]['text'].lower()) if selected_id in (arc['id'] for arc in v['arcs']))
+        # add nodes and arcs
+        for _id, certifier_node in nodes:
+            node = self.add_node(_id, certifier_node, (x, y))
+            for arc in certifier_node['arcs']:
+                if arc['id'] == selected_id:
+                    self.add_arc(node, root_node, arc)
+            y += 50
+
+        self.update()
+
+
+class Node(QGraphicsEllipseItem):
+    def __init__(self, _id, data, x_y):
+        """
+        Create node in the graph scene
+
+        :param dict data: Node data
+        :param x_y: Position of the node
+        """
+        # unpack tuple
+        x, y = x_y
+
+        super(Node, self).__init__()
+        self.id = _id
+        self.status_wallet = data['status'] & NODE_STATUS_HIGHLIGHTED
+        self.text = data['text']
+        self.setToolTip(data['tooltip'])
+        self.arcs = []
+        self.menu = None
+        self.action_sign = None
+
+        # color around ellipse
+        outline_color = QColor('grey')
+        if self.status_wallet:
+            outline_color = QColor('black')
+        self.setPen(QPen(outline_color))
+
+        # text inside ellipse
+        self.text_item = QGraphicsSimpleTextItem(self)
+        self.text_item.setText(self.text)
+        text_color = QColor('grey')
+        if self.status_wallet == NODE_STATUS_HIGHLIGHTED:
+            text_color = QColor('black')
+        self.text_item.setBrush(QBrush(text_color))
+        # center ellipse around text
+        self.setRect(
+            0,
+            0,
+            self.text_item.boundingRect().width() * 2,
+            self.text_item.boundingRect().height() * 2
+        )
+
+        # set anchor to the center
+        self.setTransform(QTransform().translate(-self.boundingRect().width()/2.0, -self.boundingRect().height()/2.0))
+        self.setPos(x, y)
+        #print(x, y)
+        # center text in ellipse
+        self.text_item.setPos(self.boundingRect().width() / 4.0, self.boundingRect().height() / 4.0)
+
+        # create gradient inside the ellipse
+        gradient = QRadialGradient(QPointF(0, self.boundingRect().height() / 4), self.boundingRect().width())
+        gradient.setColorAt(0, QColor('white'))
+        gradient.setColorAt(1, QColor('darkgrey'))
+        self.setBrush(QBrush(gradient))
+
+        # cursor change on hover
+        self.setAcceptHoverEvents(True)
+        self.setZValue(1)
+
+    def mousePressEvent(self, event: QMouseEvent):
+        """
+        Click on mouse button
+
+        :param event: mouse event
+        """
+        if event.button() == Qt.LeftButton:
+            # trigger scene signal
+            self.scene().node_clicked.emit(self.id)
+
+    def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
+        """
+        Mouse enter on node zone
+
+        :param event: scene hover event
+        """
+        self.setCursor(Qt.ArrowCursor)
+
+    def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent):
+        """
+        Right click on node to show node menu
+
+        :param event: scene context menu event
+        """
+        # create node context menus
+        self.menu = QMenu()
+        # action sign identity
+        self.action_sign = QAction('Sign identity', self.scene())
+        self.menu.addAction(self.action_sign)
+        self.action_sign.triggered.connect(self.sign_action)
+        self.menu.exec(event.screenPos())
+
+    def add_arc(self, arc):
+        """
+        Add arc to the arc list
+
+        :param arc: Arc
+        """
+        self.arcs.append(arc)
+
+    def sign_action(self):
+        """
+        Sign identity node
+        """
+        # trigger scene signal
+        self.scene().node_signed.emit(self.id)
+
+
+class Arc(QGraphicsLineItem):
+    def __init__(self, source_node, destination_node, data):
+        """
+        Create an arc between two nodes
+
+        :param Node source_node: Source node of the arc
+        :param Node destination_node: Destination node of the arc
+        :param dict data: Arc data
+        """
+        super(Arc, self).__init__()
+
+        self.source = source_node
+        self.destination = destination_node
+        self.setToolTip(data['tooltip'])
+        self.source.add_arc(self)
+
+        self.status = data['status']
+
+        self.source_point = None
+        self.destination_point = None
+        self.arrow_size = 5.0
+
+        self.setAcceptedMouseButtons(Qt.NoButton)
+
+        # cursor change on hover
+        self.setAcceptHoverEvents(True)
+        self.adjust()
+        self.setZValue(0)
+
+    def adjust(self):
+        """
+        Draw the arc line
+        """
+        if not self.source or not self.destination:
+            return
+        line = QLineF(
+            self.mapFromItem(
+                self.source,
+                self.source.boundingRect().width() - (self.source.boundingRect().width() / 2.0),
+                self.source.boundingRect().height() / 2.0
+            ),
+            self.mapFromItem(
+                self.destination,
+                self.destination.boundingRect().width() / 2.0,
+                self.destination.boundingRect().height() / 2.0
+            )
+        )
+        self.prepareGeometryChange()
+        self.source_point = line.p1()
+        self.destination_point = line.p2()
+
+        # mouse over on line only
+        self.setLine(line)
+
+    # virtual function require subclassing
+    def boundingRect(self):
+        """
+        Return the bounding rectangle size
+
+        :return: QRectF
+        """
+        if not self.source or not self.destination:
+            return QRectF()
+        pen_width = 1.0
+        extra = (pen_width + self.arrow_size) / 2.0
+
+        return QRectF(
+            self.source_point, QSizeF(
+                self.destination_point.x() - self.source_point.x(),
+                self.destination_point.y() - self.source_point.y()
+            )
+        ).normalized().adjusted(
+            -extra,
+            -extra,
+            extra,
+            extra
+        )
+
+    def paint(self, painter, option, widget):
+        """
+        Customize line adding an arrow head
+
+        :param QPainter painter: Painter instance of the item
+        :param option:  Painter option of the item
+        :param widget:  Widget instance
+        """
+        if not self.source or not self.destination:
+            return
+        line = QLineF(self.source_point, self.destination_point)
+        if qFuzzyCompare(line.length(), 0):
+            return
+
+        # Draw the line itself
+        color = QColor()
+        style = Qt.SolidLine
+        if self.status == ARC_STATUS_STRONG:
+            color.setNamedColor('blue')
+        if self.status == ARC_STATUS_WEAK:
+            color.setNamedColor('salmon')
+            style = Qt.DashLine
+
+        painter.setPen(QPen(color, 1, style, Qt.RoundCap, Qt.RoundJoin))
+        painter.drawLine(line)
+        painter.setPen(QPen(color, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
+
+        # Draw the arrows
+        angle = math.acos(line.dx() / line.length())
+        if line.dy() >= 0:
+            angle = (2.0 * math.pi) - angle
+
+        # arrow in the middle of the arc
+        hpx = (line.p2().x() + line.p1().x()) / 2.0
+        hpy = (line.p2().y() - line.p1().y()) / 2.0
+        if line.dy() < 0:
+            hpy = -hpy
+        head_point = QPointF(hpx, hpy)
+
+        painter.setPen(QPen(color, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
+        destination_arrow_p1 = head_point + QPointF(
+            math.sin(angle - math.pi / 3) * self.arrow_size,
+            math.cos(angle - math.pi / 3) * self.arrow_size)
+        destination_arrow_p2 = head_point + QPointF(
+            math.sin(angle - math.pi + math.pi / 3) * self.arrow_size,
+            math.cos(angle - math.pi + math.pi / 3) * self.arrow_size)
+
+        painter.setBrush(color)
+        painter.drawPolygon(QPolygonF([head_point, destination_arrow_p1, destination_arrow_p2]))
+
+    def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
+        """
+        Mouse enter on arc zone
+
+        :param event: scene hover event
+        """
+        self.setCursor(Qt.ArrowCursor)
+
+    def shape(self):
+        """
+        Return real shape of the item to detect collision or hover accurately
+
+        :return: QPainterPath
+        """
+        # detection mouse hover on arc path
+        path = QPainterPath()
+        path.addPolygon(QPolygonF([self.line().p1(), self.line().p2()]))
+        # add handles at the start and end of arc
+        path.addRect(QRectF(
+            self.line().p1().x()-5,
+            self.line().p1().y()-5,
+            self.line().p1().x()+5,
+            self.line().p1().y()+5
+        ))
+        path.addRect(QRectF(
+            self.line().p2().x()-5,
+            self.line().p2().y()-5,
+            self.line().p2().x()+5,
+            self.line().p2().y()+5
+        ))
+        return path