diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d5a5a5ecafa65be7c50f7f67f1426bca3826a3e8..19327fe5d69027acf77d329f7f31b605b8cd6865 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,35 +2,65 @@ stages:
     - build_and_tests
     - fmt
     - clippy
+    - package
+    - prerelease
     - publish
     
 before_script:
     - export PATH="$HOME/.cargo/bin:$PATH"
+
+variables:
+  CARGO_HOME: $CI_PROJECT_DIR/cargo
+
+
+.rust_stable_env: &rust_stable_env
+  tags:
+    - redshift-rs-stable
+  before_script:
+    - export PATH="$HOME/.cargo/bin:$PATH"
+    - rustc --version && cargo --version
+
+.rust_beta_env: &rust_beta_env
+  tags:
+    - redshift-rs-beta
+  before_script:
+    - export PATH="$HOME/.cargo/bin:$PATH"
+    - rustup update
+    - rustc --version && cargo --version
+
+.rust_nightly_env: &rust_nightly_env
+  image: rustlang/rust:nightly
+  tags:
+    - redshift-rs-nightly
+  before_script:
+    - export PATH="$HOME/.cargo/bin:$PATH"
+    - rustc --version && cargo --version
     
 build_and_tests:stable:
+  <<: *rust_stable_env
   stage: build_and_tests
   tags:
     - redshift-rs-stable
   script: 
     - cargo build --features strict
     - cargo test --all
+  cache:
+    paths:
+      - cargo/
+      - target/
     
 build_and_tests:beta:
+  <<: *rust_beta_env
   stage: build_and_tests
-  tags:
-    - redshift-rs-beta
   script:
-    - rustup update
     - cargo build --features strict
     - cargo test --all
   when: manual
   allow_failure: true
     
 build_and_tests:nightly:
+  <<: *rust_nightly_env
   stage: build_and_tests
-  image: rustlang/rust:nightly
-  tags:
-    - redshift-rs-nightly
   script:
     - cargo build --features strict
     - cargo test --all
@@ -38,49 +68,100 @@ build_and_tests:nightly:
   allow_failure: true
   
 fmt:
+  <<: *rust_nightly_env
   stage: fmt
-  image: rustlang/rust:nightly
-  tags:
-    - redshift-rs-nightly
   before_script:
-    - export PATH="$HOME/.cargo/bin:$PATH"
     - cargo install --force rustfmt-nightly
   script:
     - cargo fmt -- --check
   allow_failure: true
 
 clippy:
+  <<: *rust_nightly_env
   stage: clippy
-  image: rustlang/rust:nightly
-  tags:
-    - redshift-rs-nightly
   before_script:
-    - export PATH="$HOME/.cargo/bin:$PATH"
     - cargo install --force clippy --verbose
   script:
     - cargo clippy --all -- -D warnings --verbose
   allow_failure: true  
 
+package:test:linux-x64:
+  <<: *rust_stable_env
+  stage: package
+  script:
+    - bash "release/arch/linux-x64/build-lin-x64.sh" "$(date +%Y%m%d).$(date +%H%M).$(date +%S)"
+  cache:
+    paths:
+      - cargo/
+      - target/
+  artifacts:
+    paths:
+      - work/bin/
+    expire_in: 1 weeks
+  except:
+      - tags
+  when: manual
+
+package:prod:linux-x64:
+  <<: *rust_stable_env
+  stage: package
+  script:
+    - bash "release/arch/linux-x64/build-lin-x64.sh" "${CI_COMMIT_TAG#v}"
+  cache:
+    paths:
+      - cargo/
+      - target/
+  artifacts:
+    paths:
+      - work/bin/
+    expire_in: 2 weeks
+  only:
+      - dev
+      - tags
+
+.release_jobs: &release_jobs
+  image: tensorflow/tensorflow:latest-py3
+  tags:
+    - redshift-rs
+  script:
+    - python3 .gitlab/releaser
+  only:
+    - dev
+    - tags
+
+prerelease:
+  <<: *release_jobs
+  stage: prerelease
+  variables:
+    RELEASE_BIN_DIR: work/bin/
+    SOURCE_EXT: '["tar.gz", "zip"]'
+
+publish:release:
+  <<: *release_jobs
+  stage: publish
+  variables:
+    RELEASE_BIN_DIR: work/bin/
+    WIKI_RELEASE: Releases
+  allow_failure: false
+  when: manual
+
 publish:crate:
+  <<: *rust_stable_env
   stage: publish
-  tags:
-    - redshift-rs-stable
   script:
     - IFS='/' read -r first a <<< "$CI_COMMIT_TAG"
     - cd $first
     - cargo login $DUNITER_CRATES_TOKEN
     - cargo publish
   only:
+    - publish-crate
     - tags
   allow_failure: false
   when: manual
 
 pages:
+  <<: *rust_stable_env
   stage: publish
-  tags:
-    - redshift-rs-stable
-  before_script:
-    - export PATH="$HOME/.cargo/bin:$PATH"
   script:
     - cargo doc
     - mv target/doc public
@@ -90,4 +171,6 @@ pages:
     paths:
       - public
   allow_failure: true
-  when: manual
\ No newline at end of file
+  when: manual
+  only:
+    - dev
\ No newline at end of file
diff --git a/.gitlab/release_template.md b/.gitlab/release_template.md
new file mode 100644
index 0000000000000000000000000000000000000000..adc0470e572edb7f8b37ba7bf1730c710da15ef4
--- /dev/null
+++ b/.gitlab/release_template.md
@@ -0,0 +1,34 @@
+{% block prerelease %}
+# :gift: Pre-release
+
+[Go to Pipeline page :arrow_forward:](https://git.duniter.org/nodes/rust/duniter-rs/pipelines/{{pipeline}})
+
+{% endblock %}
+
+{% block release %}
+# :white_check_mark: Release
+
+{% endblock %}
+
+{% block notebody %}
+<placeholder content="end-title" />
+<placeholder content="note">
+{{current_message}}
+</placeholder>
+
+## Downloads
+
+| Category | Arch | Type | Size | File |
+|----------|------|------|------|------|
+{% for artifact in artifacts %}
+| {{artifact.category}} | {{artifact.arch}} | {{artifact.type}} | {{artifact.size}} | [{{artifact.icon}} {{artifact.name}}]({{artifact.url}}) |
+{% endfor %}
+{% endblock %}
+
+{% block previouswiki %}
+
+
+## {{tag}}
+
+{{body}}
+{% endblock %}
diff --git a/.gitlab/releaser/__init__.py b/.gitlab/releaser/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f0a11e46162aa197c5f61768335d949955ae02b
--- /dev/null
+++ b/.gitlab/releaser/__init__.py
@@ -0,0 +1,12 @@
+'''
+This module is meant add release notes in gitlab for the current project.
+Expects to find in environment following variables:
+  - CI_PROJECT_URL - Automatically set by gitlab-ci
+  - CI_PROJECT_ID - Automatically set by gitlab-ci
+  - CI_COMMIT_TAG - Automatically set by gitlab-ci
+  - CI_PIPELINE_ID - Automatically set by gitlab-ci
+  - RELEASE_BIN_DIR - Directory where releases are to be found
+  - SOURCE_EXT - Source extensions (pre-release only)
+  - WIKI_RELEASE - Wiki page where releases are stored (release only)
+  - RELEASER_TOKEN - Token used by technical user
+'''
diff --git a/.gitlab/releaser/__main__.py b/.gitlab/releaser/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..83a061b83f7f74905faeee23b9fa4702ffe3ab11
--- /dev/null
+++ b/.gitlab/releaser/__main__.py
@@ -0,0 +1,3 @@
+from releaser import Releaser
+
+Releaser().release()
diff --git a/.gitlab/releaser/artifact.py b/.gitlab/releaser/artifact.py
new file mode 100644
index 0000000000000000000000000000000000000000..e0d37f33a7d6896b471a708bf20a5c9e43ce8082
--- /dev/null
+++ b/.gitlab/releaser/artifact.py
@@ -0,0 +1,84 @@
+class Artifact:
+    '''
+    An artifact to be uploaded.
+    '''
+
+    def __init__(self, file_name, category, arch, dtype, icon):
+        '''
+        :param file_name: The name of the artifact file (may have directory).
+        :param category: The category (OS, distrib) for the artifact.
+        :param arch: The architecture name.
+        :param dtype: The delivery type (either server or desktop).
+        :param icon: The name of the icon to be used for artifact representation.
+        :type file_name: str
+        :type category: str
+        :type arch: str
+        :type dtype: str
+        :type icon: str
+        '''
+        self.file_name = file_name
+        self.category = category
+        self.arch = arch
+        self.dtype = dtype
+        self.icon = icon
+
+    def __lt__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category < other.category or \
+            (self.category == other.category and self.arch < other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype < other.dtype)
+
+    def __le__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category <= other.category or \
+            (self.category == other.category and self.arch <= other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype <= other.dtype)
+
+    def __eq__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category == other.category and self.arch == other.arch and self.dtype == other.dtype
+
+    def __ne__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category != other.category or self.arch != other.arch or self.dtype != other.dtype
+
+    def __gt__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category > other.category or \
+            (self.category == other.category and self.arch > other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype > other.dtype)
+
+    def __ge__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category >= other.category or \
+            (self.category == other.category and self.arch >= other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype >= other.dtype)
+
+    def to_dict(self):
+        '''
+        :return: A dictionnary containing artifact data.
+        :rtype: dict
+        '''
+        return {
+            'name': self.file_name.split('/')[-1],
+            'category': self.category,
+            'arch': self.arch,
+            'type': self.dtype,
+            'url': self._build_url(),
+            'size': self._get_size(),
+            'icon': ':{}:'.format(self.icon)
+        }
+
+    def _get_size(self):
+        '''
+        :return: The size of the artifact.
+        :rtype: FSItemSize
+        '''
+        raise NotImplementedError()
+
+    def _build_url(self):
+        '''
+        :return: The URL which can be used to get this artifact.
+        :rtype: str
+        '''
+        raise NotImplementedError()
diff --git a/.gitlab/releaser/binartifact.py b/.gitlab/releaser/binartifact.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc3ab04e05fa1386e93d745ff610dd5ea9def5a4
--- /dev/null
+++ b/.gitlab/releaser/binartifact.py
@@ -0,0 +1,37 @@
+import json
+import os
+
+from artifact import Artifact
+from fsitemsize import FSItemSize
+
+class BinArtifact(Artifact):
+    '''
+    A binary artifact.
+    '''
+
+    def __init__(self, folder, desc_file, desc_ext):
+        '''
+        :param folder: The folder where files can be found.
+        :param desc_file: The name of the description file.
+        :param desc_ext: The extention of the description file.
+        :type folder: str
+        :type desc_file: str
+        :type desc_ext: str
+        '''
+        try:
+            description = json.load(open(desc_file))
+        except json.decoder.JSONDecodeError:
+            print('CRITICAL Description file {} could not be read'.format(desc_file))
+            exit(1)
+
+        self.tag = description['version']
+        self.job = description['job']
+        file_name = desc_file[:-len(desc_ext)]
+        Artifact.__init__(self, file_name, description['category'], description['arch'], description['type'], 'package')
+
+    def _get_size(self):
+        return FSItemSize(int(os.path.getsize(self.file_name)))
+
+    def _build_url(self):
+        return '{}/-/jobs/{}/artifacts/raw/{}'.format(
+            os.environ['CI_PROJECT_URL'], self.job, self.file_name)
diff --git a/.gitlab/releaser/fsitemsize.py b/.gitlab/releaser/fsitemsize.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6c8be232cdaf2ae00cebef732c225c27b427a3c
--- /dev/null
+++ b/.gitlab/releaser/fsitemsize.py
@@ -0,0 +1,28 @@
+import math
+
+class FSItemSize:
+    '''
+    The size of a file system item.
+    '''
+
+    def __init__(self, bsize = None):
+        '''
+        :param bsize: Size of item in bytes.
+        :type bsize: int
+        '''
+        self.bsize = bsize
+
+    def __str__(self):
+        '''
+        :return: Human readable size.
+        :rtype: str
+        '''
+        if self.bsize is None:
+            return '(unknown)'
+        elif self.bsize == 0:
+            return '0 B'
+        size_name = ('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
+        i = int(math.floor(math.log(self.bsize, 1024)))
+        power = math.pow(1024, i)
+        size = round(self.bsize / power, 2)
+        return '{} {}'.format(size, size_name[i])
diff --git a/.gitlab/releaser/job.py b/.gitlab/releaser/job.py
new file mode 100644
index 0000000000000000000000000000000000000000..2dc55c08fcb14a76074b597b81f892a8aa339646
--- /dev/null
+++ b/.gitlab/releaser/job.py
@@ -0,0 +1,22 @@
+import urllib.request
+
+from projectapi import ProjectApi
+
+class Job(ProjectApi):
+    '''
+    Job data API.
+    '''
+
+    def __init__(self, job_id):
+        '''
+        :param job_id: The job id.
+        :type job_id: int
+        '''
+        ProjectApi.__init__(self, '/jobs/{}'.format(job_id))
+
+    def keep_artifacts(self):
+        '''
+        Force artifacts to be kept forever.
+        '''
+        request = self.build_request('/artifacts/keep', method='POST')
+        urllib.request.urlopen(request)
diff --git a/.gitlab/releaser/placeholder.py b/.gitlab/releaser/placeholder.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3624f0b3b94ac494f3451820fbbb2ec127aba9a
--- /dev/null
+++ b/.gitlab/releaser/placeholder.py
@@ -0,0 +1,120 @@
+class PlaceHolder:
+    '''
+    Placeholder tags in Markdown texts.
+    '''
+    __PLACEHOLDER_PART = '<placeholder'
+    __PLACEHOLDER_START = '<placeholder content="{}">'
+    __PLACEHOLDER_STOP = '</placeholder>'
+    __PLACEHOLDER_FULL = '<placeholder content="{}" />'
+
+    def __init__(self, content_id):
+        '''
+        :param content_id: The identifier to be used for placeholder content.
+        :type content_id: str
+        '''
+        self.ph_start = PlaceHolder.__PLACEHOLDER_START.format(content_id)
+        self.ph_stop = PlaceHolder.__PLACEHOLDER_STOP
+        self.ph_full = PlaceHolder.__PLACEHOLDER_FULL.format(content_id)
+
+    def get_content(self, text):
+        '''
+        :param text: The text in which to extract content.
+        :type text: str
+        :return: The content between placeholder markers.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_start)
+        if pos >= 0:
+            text = text[pos + len(self.ph_start):]
+            pos = text.find(self.ph_stop)
+            if pos >= 0: text = text[:pos]
+        return text
+
+    def get_before(self, text, keep_mark=False):
+        '''
+        :param text: The text in which to extract content.
+        :param keep_mark: If true, the mark is kept in final text.
+        :type text: str
+        :type keep_mark: bool
+        :return: The content before (full) placeholder marker.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_full)
+        if pos >= 0:
+            if keep_mark: pos += len(self.ph_full)
+            text = text[:pos]
+        return text
+
+    def get_after(self, text, keep_mark=False):
+        '''
+        :param text: The text in which to extract content.
+        :param keep_mark: If true, the mark is kept in final text.
+        :type text: str
+        :type keep_mark: bool
+        :return: The content after (full) placeholder marker.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_full)
+        if pos >= 0:
+            if not keep_mark: pos += len(self.ph_full)
+            text = text[pos:]
+        return text
+
+    def replace_content(self, text, content):
+        '''
+        :param text: The text in which to extract content.
+        :param content: The new content to insert.
+        :type text: str
+        :type content: str
+        :return: The text where content has been replaced.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_start)
+        if pos >= 0:
+            pos += len(self.ph_start)
+            text_before = text[:pos]
+        else:
+            pos = 0
+            text_before = ''
+        pos = text.find(self.ph_stop, pos)
+        if pos >= 0:
+            text_after = text[pos:]
+        else:
+            text_after = ''
+        return text_before + content + text_after
+
+    def insert_after(self, text, content):
+        '''
+        :param text: The text in which to extract content.
+        :param content: The new content to insert.
+        :type text: str
+        :type content: str
+        :return: The text where content has been inserted.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_full)
+        if pos >= 0: pos += len(self.ph_full)
+        else: pos = 0
+        text_before = text[:pos]
+        text_after = text[pos:]
+        return text_before + content + text_after
+
+    def clear_all(text):
+        '''
+        Clear all placeholders from given text.
+        :param text: The text to clear.
+        :type text: str
+        :return: The clean text.
+        :rtype: str
+        '''
+        while True:
+            pos = text.find(PlaceHolder.__PLACEHOLDER_PART)
+            if pos < 0: break
+            end = text.find('>')
+            if end < 0: end = len(text)
+            text = text[:pos] + text[end + 1:]
+        while True:
+            pos = text.find(PlaceHolder.__PLACEHOLDER_STOP)
+            if pos < 0: break
+            text = text[:pos] + text[pos + len(PlaceHolder.__PLACEHOLDER_STOP):]
+        return text
diff --git a/.gitlab/releaser/projectapi.py b/.gitlab/releaser/projectapi.py
new file mode 100644
index 0000000000000000000000000000000000000000..eea07e769b95af92960e6538d7f2af60f3c699e8
--- /dev/null
+++ b/.gitlab/releaser/projectapi.py
@@ -0,0 +1,31 @@
+import os
+import urllib.request
+
+class ProjectApi:
+    '''
+    Gitlab API project access.
+    '''
+    __PROJECT_URL = 'https://git.duniter.org/api/v4/projects/{}'
+
+    def __init__(self, url=''):
+        '''
+        :param url: The URL portion to add to base project URL (if needed).
+        :type url: str
+        '''
+        self.base_url = ProjectApi.__PROJECT_URL.format(os.environ['CI_PROJECT_ID'])
+        self.base_url += url
+        self.token = ('Private-Token', os.environ['RELEASER_TOKEN'])
+
+    def build_request(self, url='', **params):
+        '''
+        Create the request to send to project API.
+        :param url: The portion of URL to add to base URL (if needed).
+        :param params: The optional parameters.
+        :type url: str
+        :type params: dict
+        :return: The request, ready to be used.
+        :rtype: urllib.request.Request
+        '''
+        request = urllib.request.Request(self.base_url + url, **params)
+        request.add_header(*self.token)
+        return request
diff --git a/.gitlab/releaser/releasenote.py b/.gitlab/releaser/releasenote.py
new file mode 100644
index 0000000000000000000000000000000000000000..c4fff9a756273d38fed03907fb585e3db7fa90b1
--- /dev/null
+++ b/.gitlab/releaser/releasenote.py
@@ -0,0 +1,74 @@
+import json
+import os
+import urllib.request
+
+from placeholder import PlaceHolder
+from projectapi import ProjectApi
+
+class ReleaseNote(ProjectApi):
+    '''
+    Release note API.
+    '''
+    __PH_TITLE = PlaceHolder('end-title')
+    __PH_NOTE = PlaceHolder('note')
+
+    def __init__(self):
+        ProjectApi.__init__(self, '/repository/tags/{}'.format(os.environ['CI_COMMIT_TAG']))
+        self.message_read = False
+
+    def get_note(self):
+        '''
+        Get full release note.
+        :return: The note if it exists, None otherwise.
+        :rtype: str or None
+        '''
+        request = self.build_request()
+        response = urllib.request.urlopen(request)
+        response_data = response.read().decode()
+        data = json.loads(response_data)
+        if data['release'] is None:
+            return None
+        else:
+            self.message_read = True
+            return data['release']['description']
+
+    def get_message(self):
+        '''
+        Get release message. Message is extracted from full note.
+        :return: The message if it exists, empty string otherwise.
+        :rtype: str
+        '''
+        data = self.get_note()
+        if data is None:
+            return ''
+        else:
+            return ReleaseNote.__PH_NOTE.get_content(data)
+
+    def get_note_body(self):
+        '''
+        Get release note body (without title). Body is extracted from full note.
+        :return: The body.
+        :rtype: str
+        '''
+        data = self.get_note()
+        if data is None:
+            print('CRITICAL No release information to publish')
+            exit(1)
+        return ReleaseNote.__PH_TITLE.get_after(data, True)
+
+    def send_note(self, note):
+        '''
+        Send the full release note. The current message should have been read
+        unless you are sure there are none.
+        :param note: The full note to send.
+        :type note: str
+        '''
+        method = 'PUT' if self.message_read else 'POST'
+        send_data = {
+            'tag_name': os.environ['CI_COMMIT_TAG'],
+            'description': note
+        }
+        send_data_serialized = json.dumps(send_data).encode('utf-8')
+        request = self.build_request('/release', data=send_data_serialized, method=method)
+        request.add_header('Content-Type', 'application/json')
+        urllib.request.urlopen(request)
diff --git a/.gitlab/releaser/releaser.py b/.gitlab/releaser/releaser.py
new file mode 100644
index 0000000000000000000000000000000000000000..dca04456186c0c187cdb899c67ee6f8dd58c5429
--- /dev/null
+++ b/.gitlab/releaser/releaser.py
@@ -0,0 +1,107 @@
+import glob
+import jinja2
+import json
+import os
+
+from binartifact import BinArtifact
+from job import Job
+from placeholder import PlaceHolder
+from releasenote import ReleaseNote
+from releasewikipage import ReleaseWikiPage
+from sourceartifact import SourceArtifact
+from template import Template
+
+class Releaser:
+    '''
+    The main releaser class
+    '''
+
+    def __init__(self):
+        self.template = Template('release_template.md')
+        if 'RELEASE_BIN_DIR' in os.environ:
+            self.release_bin_dir = os.environ['RELEASE_BIN_DIR']
+            if not self.release_bin_dir.endswith('/'): self.release_bin_dir += '/'
+        else:
+            print('CRITICAL RELEASE_BIN_DIR environment variable not set')
+            exit(1)
+        if 'SOURCE_EXT' in os.environ:
+            self.source_ext = os.environ['SOURCE_EXT']
+            try:
+                self.source_ext = json.loads(self.source_ext)
+            except json.decoder.JSONDecodeError:
+                print('CRITICAL SOURCE_EXT environment variable JSON probably malformed')
+                print('CRITICAL Correct : \'["zip","tar.gz"]\' ')
+                print('CRITICAL Not Correct: "[\'zip\',\'tar.gz\']" ')
+                exit(1)
+        else: self.source_ext = None
+
+    def release(self):
+        if self.source_ext is None:
+            self.publish_release()
+        else:
+            self.publish_prerelease()
+
+    def publish_prerelease(self):
+        '''
+        Main job to publish a pre-release.
+        '''
+        releaseNote = ReleaseNote()
+        current_message = releaseNote.get_message()
+        artifacts_list = []
+
+        # Get releases
+        artifacts_list += self._get_bin_artifacts()
+        artifacts_list.sort()
+        artifacts_list += list(map(lambda e: SourceArtifact(e), self.source_ext))
+
+        # Send result
+        note = self.template.render('notebody', {
+            'current_message': current_message,
+            'artifacts': list(map(lambda a: a.to_dict(), artifacts_list))
+        })
+        title_line = self.template.render('prerelease', {
+            'tag': os.environ['CI_COMMIT_TAG'],
+            'pipeline': os.environ['CI_PIPELINE_ID']
+        })
+        releaseNote.send_note(title_line + note)
+
+        print('Pre-release published')
+
+    def publish_release(self):
+        '''
+        Main job to publish the final release.
+        '''
+        # Change release note
+        releaseNote = ReleaseNote()
+        note = releaseNote.get_note_body()
+        title_line = self.template.render('release', {
+            'tag': os.environ['CI_COMMIT_TAG'],
+            'pipeline': os.environ['CI_PIPELINE_ID']
+        })
+        releaseNote.send_note(title_line + note)
+
+        # Update Wiki release page
+        wiki_page = ReleaseWikiPage(self.template)
+        wiki_page.add_release(os.environ['CI_COMMIT_TAG'], PlaceHolder.clear_all(note))
+        wiki_page.save()
+
+        # Keep artifacts
+        jobs = []
+        for artifact in self._get_bin_artifacts():
+            if not artifact.job in jobs:
+                jobs.append(artifact.job)
+        for job_id in jobs: Job(job_id).keep_artifacts()
+
+        print('Release published')
+
+    def _get_bin_artifacts(self):
+        '''
+        Get the binary artifacts for the current tag.
+        :return: The list of binary artifacts, based on found descriptions.
+        :rtype: list of BinArtifact
+        '''
+        DESC_EXT = '.desc'
+        artifacts = glob.glob('{}*{}'.format(self.release_bin_dir, DESC_EXT))
+        artifacts = map(lambda d: BinArtifact(self.release_bin_dir, d, DESC_EXT), artifacts)
+        artifacts = filter(lambda a: a.tag == os.environ['CI_COMMIT_TAG'], artifacts)
+        return list(artifacts)
diff --git a/.gitlab/releaser/releasewikipage.py b/.gitlab/releaser/releasewikipage.py
new file mode 100644
index 0000000000000000000000000000000000000000..c76479610d4eba3ee0244df475f632c9fc9818fd
--- /dev/null
+++ b/.gitlab/releaser/releasewikipage.py
@@ -0,0 +1,59 @@
+import json
+import os
+import urllib.request
+
+from placeholder import PlaceHolder
+from projectapi import ProjectApi
+
+class ReleaseWikiPage(ProjectApi):
+    '''
+    Release Wiki page API.
+    '''
+    __PH_TAG = PlaceHolder('tag')
+    __PH_NOTE = PlaceHolder('note')
+    __PH_PREVIOUS = PlaceHolder('previous-beg')
+
+    def __init__(self, template):
+        '''
+        :param template: The template to use.
+        :type template: Template
+        '''
+        if not 'WIKI_RELEASE' in os.environ:
+            print('CRITICAL WIKI_RELEASE variable is not defined')
+            exit(1)
+        ProjectApi.__init__(self, '/wikis/{}'.format(os.environ['WIKI_RELEASE']))
+        self.template = template
+
+        # Query existing page
+        request = self.build_request()
+        response = urllib.request.urlopen(request)
+        response_data = response.read().decode()
+        data = json.loads(response_data)
+        self.page_content = data['content']
+
+    def add_release(self, tag, note):
+        '''
+        Add the release to the Wiki page.
+        '''
+        prev_tag = ReleaseWikiPage.__PH_TAG.get_content(self.page_content)
+        prev_note = ReleaseWikiPage.__PH_NOTE.get_content(self.page_content)
+        self.page_content = ReleaseWikiPage.__PH_TAG.replace_content(self.page_content, tag)
+        self.page_content = ReleaseWikiPage.__PH_NOTE.replace_content(self.page_content, note)
+        previous = self.template.render('previouswiki', {
+            'tag': prev_tag,
+            'body': prev_note
+        })
+        self.page_content = ReleaseWikiPage.__PH_PREVIOUS.insert_after(
+            self.page_content, previous)
+
+    def save(self):
+        send_data = {
+            'content': self.page_content,
+            'format': 'markdown',
+            'slug': os.environ['WIKI_RELEASE'],
+            'title': os.environ['WIKI_RELEASE']
+        }
+        send_data_serialized = json.dumps(send_data).encode('utf-8')
+        request = self.build_request(data=send_data_serialized, method='PUT')
+        request.add_header('Content-Type', 'application/json')
+        urllib.request.urlopen(request)
diff --git a/.gitlab/releaser/sourceartifact.py b/.gitlab/releaser/sourceartifact.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5eda1c81d26cbed6ced8339aff1001bdc562c70
--- /dev/null
+++ b/.gitlab/releaser/sourceartifact.py
@@ -0,0 +1,23 @@
+import os
+
+from artifact import Artifact
+from fsitemsize import FSItemSize
+
+class SourceArtifact(Artifact):
+    '''
+    A source artifact.
+    '''
+
+    def __init__(self, extention):
+        '''
+        :param extention: The extention of the source archive.
+        :type extention: str
+        '''
+        Artifact.__init__(self, 'archive.{}'.format(extention), 'Source code ({})'.format(extention), '', '', 'compression')
+
+    def _get_size(self):
+        return FSItemSize()
+
+    def _build_url(self):
+        return '{}/repository/{}/{}'.format(
+            os.environ['CI_PROJECT_URL'], os.environ['CI_COMMIT_TAG'], self.file_name)
diff --git a/.gitlab/releaser/template.py b/.gitlab/releaser/template.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c607a059dade516d96db0a0a89b8d43b82921e6
--- /dev/null
+++ b/.gitlab/releaser/template.py
@@ -0,0 +1,31 @@
+import jinja2
+import os
+
+class Template:
+    '''
+    Manages the template file. The template file is split into blocks.
+    '''
+    def __init__(self, fname):
+        '''
+        :param fname: The name of the template file.
+        :type fname: str
+        '''
+        path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+        environment = jinja2.Environment(
+            loader=jinja2.FileSystemLoader(path),
+            trim_blocks=True
+        )
+        self.template = environment.get_template(fname)
+
+    def render(self, block, params):
+        '''
+        Render a block from the template file.
+        :param block: The name of the block to render.
+        :param params: The parameters to be used in the block.
+        :type block: str
+        :type params: dict
+        :return: The rendered block.
+        :rtype: str
+        '''
+        context = self.template.new_context(params)
+        return jinja2.utils.concat(self.template.blocks[block](context))
diff --git a/Cargo.lock b/Cargo.lock
index a1bb5e00f6673d11ae1ad07dc71a151e605919a9..5360a187b3f8bcab195d163ed2192766f3b47c9a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -173,16 +173,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "duniter-blockchain"
-version = "0.1.0"
-dependencies = [
- "duniter-conf 0.1.0",
- "duniter-crypto 0.1.2",
- "duniter-dal 0.1.0",
- "duniter-documents 0.7.1",
- "duniter-message 0.1.0",
- "duniter-module 0.1.0",
- "duniter-network 0.1.0",
- "duniter-wotb 0.8.0-a0.6",
+version = "0.1.0-a0.1"
+dependencies = [
+ "duniter-conf 0.1.0-a0.1",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-dal 0.1.0-a0.1",
+ "duniter-documents 0.8.0-a0.1",
+ "duniter-message 0.1.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
+ "duniter-network 0.1.0-a0.1",
+ "duniter-wotb 0.8.0-a0.7",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -197,10 +197,10 @@ dependencies = [
 
 [[package]]
 name = "duniter-conf"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 dependencies = [
- "duniter-crypto 0.1.2",
- "duniter-module 0.1.0",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -209,15 +209,15 @@ dependencies = [
 
 [[package]]
 name = "duniter-core"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 dependencies = [
  "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "duniter-blockchain 0.1.0",
- "duniter-conf 0.1.0",
- "duniter-crypto 0.1.2",
- "duniter-message 0.1.0",
- "duniter-module 0.1.0",
- "duniter-network 0.1.0",
+ "duniter-blockchain 0.1.0-a0.1",
+ "duniter-conf 0.1.0-a0.1",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-message 0.1.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
+ "duniter-network 0.1.0-a0.1",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -233,7 +233,7 @@ dependencies = [
 
 [[package]]
 name = "duniter-crypto"
-version = "0.1.2"
+version = "0.2.0-a0.1"
 dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -244,13 +244,13 @@ dependencies = [
 
 [[package]]
 name = "duniter-dal"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 dependencies = [
- "duniter-crypto 0.1.2",
- "duniter-documents 0.7.1",
- "duniter-module 0.1.0",
- "duniter-network 0.1.0",
- "duniter-wotb 0.8.0-a0.6",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-documents 0.8.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
+ "duniter-network 0.1.0-a0.1",
+ "duniter-wotb 0.8.0-a0.7",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -264,11 +264,11 @@ dependencies = [
 
 [[package]]
 name = "duniter-documents"
-version = "0.7.1"
+version = "0.8.0-a0.1"
 dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "duniter-crypto 0.1.2",
+ "duniter-crypto 0.2.0-a0.1",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -279,23 +279,23 @@ dependencies = [
 
 [[package]]
 name = "duniter-message"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 dependencies = [
- "duniter-crypto 0.1.2",
- "duniter-dal 0.1.0",
- "duniter-documents 0.7.1",
- "duniter-module 0.1.0",
- "duniter-network 0.1.0",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-dal 0.1.0-a0.1",
+ "duniter-documents 0.8.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
+ "duniter-network 0.1.0-a0.1",
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "duniter-module"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 dependencies = [
- "duniter-crypto 0.1.2",
- "duniter-documents 0.7.1",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-documents 0.8.0-a0.1",
  "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -303,11 +303,11 @@ dependencies = [
 
 [[package]]
 name = "duniter-network"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 dependencies = [
- "duniter-crypto 0.1.2",
- "duniter-documents 0.7.1",
- "duniter-module 0.1.0",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-documents 0.8.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -318,15 +318,15 @@ dependencies = [
 
 [[package]]
 name = "duniter-tui"
-version = "0.1.0"
-dependencies = [
- "duniter-conf 0.1.0",
- "duniter-crypto 0.1.2",
- "duniter-dal 0.1.0",
- "duniter-documents 0.7.1",
- "duniter-message 0.1.0",
- "duniter-module 0.1.0",
- "duniter-network 0.1.0",
+version = "0.1.0-a0.1"
+dependencies = [
+ "duniter-conf 0.1.0-a0.1",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-dal 0.1.0-a0.1",
+ "duniter-documents 0.8.0-a0.1",
+ "duniter-message 0.1.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
+ "duniter-network 0.1.0-a0.1",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -334,7 +334,7 @@ dependencies = [
 
 [[package]]
 name = "duniter-wotb"
-version = "0.8.0-a0.6"
+version = "0.8.0-a0.7"
 dependencies = [
  "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -345,16 +345,16 @@ dependencies = [
 
 [[package]]
 name = "duniter-ws2p"
-version = "0.1.0"
-dependencies = [
- "duniter-conf 0.1.0",
- "duniter-crypto 0.1.2",
- "duniter-dal 0.1.0",
- "duniter-documents 0.7.1",
- "duniter-message 0.1.0",
- "duniter-module 0.1.0",
- "duniter-network 0.1.0",
- "duniter-wotb 0.8.0-a0.6",
+version = "0.1.0-a0.1"
+dependencies = [
+ "duniter-conf 0.1.0-a0.1",
+ "duniter-crypto 0.2.0-a0.1",
+ "duniter-dal 0.1.0-a0.1",
+ "duniter-documents 0.8.0-a0.1",
+ "duniter-message 0.1.0-a0.1",
+ "duniter-module 0.1.0-a0.1",
+ "duniter-network 0.1.0-a0.1",
+ "duniter-wotb 0.8.0-a0.7",
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -369,11 +369,11 @@ dependencies = [
 
 [[package]]
 name = "durs"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 dependencies = [
- "duniter-core 0.1.0",
- "duniter-tui 0.1.0",
- "duniter-ws2p 0.1.0",
+ "duniter-core 0.1.0-a0.1",
+ "duniter-tui 0.1.0-a0.1",
+ "duniter-ws2p 0.1.0-a0.1",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 3eecdcaed45e67aa65b3fbca4941d536e73be393..905e909efa42949396f84eee7c127690aefa7dba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "durs"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@duniter.org>","nanocryk <nanocryk@duniter.org>"]
 description = "DUniter-RS (durs) is a new implementation of Duniter protocol and software in Rust, a safe, concurrent, practical language"
 license = "AGPL-3.0"
diff --git a/blockchain/Cargo.toml b/blockchain/Cargo.toml
index 300c2b50e32c01e861215c90fea3a6df9704c88a..59f9cfe500323bf2ca160a2c91404bb8fbdb50ce 100644
--- a/blockchain/Cargo.toml
+++ b/blockchain/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-blockchain"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@ifee.fr>"]
 description = "Blockchain module for the Duniter project."
 license = "AGPL-3.0"
diff --git a/conf/Cargo.toml b/conf/Cargo.toml
index 82d52df89a94b14cd743c98beadc6a07b6993558..9a39060460e515d069c698e5704394c7fdfd8eb8 100644
--- a/conf/Cargo.toml
+++ b/conf/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-conf"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@ifee.fr>"]
 description = "Configuration module for the Duniter project."
 license = "AGPL-3.0"
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 1cfd1489f5cfb3774b4b2b4f5358c79bf592f58a..7a8e598b9545e3ef86ede1f87ec3adf971a3d3ed 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-core"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@ifee.fr>"]
 description = "Duniter-rs core."
 license = "AGPL-3.0"
diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml
index c313880c7a23fabbb929516a49ddb08014aba16d..9723b75e99c1233528c30d0f0b6cb7fcb68fdc9c 100644
--- a/crypto/Cargo.toml
+++ b/crypto/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-crypto"
-version = "0.1.2"
+version = "0.2.0-a0.1"
 authors = ["nanocryk <nanocryk@duniter.org>"]
 description = "Manage cryptographic building blocks for the Duniter project."
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
diff --git a/dal/Cargo.toml b/dal/Cargo.toml
index 8a80ab5067c7ec193a8f1f441c2efe0515274ae1..a4def26bec0e754802a359f96e4e4b4ffe559630 100644
--- a/dal/Cargo.toml
+++ b/dal/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-dal"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@ifee.fr>"]
 description = "Data Access Layer for the Duniter project."
 license = "AGPL-3.0"
diff --git a/documents/Cargo.toml b/documents/Cargo.toml
index 539c2476d0091e09bd8fe2770ba95c209f4d49c5..74c7b8d5c0fb6bf919db58a836de0af67a3f5d4d 100644
--- a/documents/Cargo.toml
+++ b/documents/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-documents"
-version = "0.7.1"
+version = "0.8.0-a0.1"
 authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@ifee.fr>"]
 description = "Handles Duniter documents"
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
diff --git a/images/duniter-rs.png b/images/duniter-rs.png
new file mode 100644
index 0000000000000000000000000000000000000000..757311a09386b08764b9dda5deba4e0567f0d9ad
Binary files /dev/null and b/images/duniter-rs.png differ
diff --git a/message/Cargo.toml b/message/Cargo.toml
index 86b913c81609e6c2669212a9598aeca390d8025e..59254a6a78df74561cdf930469ea98f03b0e8663 100644
--- a/message/Cargo.toml
+++ b/message/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-message"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@duniter.org>"]
 description = "message model for the Duniter project."
 license = "AGPL-3.0"
diff --git a/module/Cargo.toml b/module/Cargo.toml
index cdcc7302e05e6a9eb9e7429aee4aa80a4c6df2f5..9c21549e332254d6aebd5e2b4bb79cbe88b9cb3a 100644
--- a/module/Cargo.toml
+++ b/module/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-module"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@duniter.org>"]
 description = "Modules model for the Duniter project."
 license = "AGPL-3.0"
diff --git a/network/Cargo.toml b/network/Cargo.toml
index 91b638382602f067e4efdd106158fe6341727cb1..6ad4c44ab3d34d317938b80fcb7463afeba84fa4 100644
--- a/network/Cargo.toml
+++ b/network/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-network"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@duniter.org>"]
 description = "network model for the Duniter project."
 license = "AGPL-3.0"
diff --git a/network/network_head.rs b/network/network_head.rs
index efd2dad5ade55ede0b08e1312223e62741856af9..91bf2843e11cbcffa64cd1232f6b00c40576271a 100644
--- a/network/network_head.rs
+++ b/network/network_head.rs
@@ -68,9 +68,9 @@ impl NetworkHeadMessageV2 {
     pub fn to_human_string(&self, max_len: usize, uid: Option<String>) -> String {
         let short_api = &self.api[4..];
 
-        if max_len > 80 && uid.is_some() {
+        if max_len > 85 && uid.is_some() {
             format!(
-                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:7} {pre:3} [{api:5}]  {mer:02}:{mir:02} {uid}",
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:14} {pre:3} [{api:5}]  {mer:02}:{mir:02} {uid}",
                 node_id = self.node_uuid.to_string(),
                 pubkey = self.pubkey.to_string(),
                 blockstamp = self.blockstamp.to_string(),
@@ -82,9 +82,9 @@ impl NetworkHeadMessageV2 {
                 mir = self.free_mirror_room.unwrap_or(0),
                 uid = uid.unwrap(),
             )
-        } else if max_len > 67 {
+        } else if max_len > 75 {
             format!(
-                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:7} {pre:3} [{api:5}]  {mer:02}:{mir:02}",
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:14} {pre:3} [{api:5}]  {mer:02}:{mir:02}",
                 node_id = self.node_uuid.to_string(),
                 pubkey = self.pubkey.to_string(),
                 blockstamp = self.blockstamp.to_string(),
@@ -95,9 +95,9 @@ impl NetworkHeadMessageV2 {
                 mer = self.free_member_room.unwrap_or(0),
                 mir = self.free_mirror_room.unwrap_or(0),
             )
-        } else if max_len > 63 {
+        } else if max_len > 70 {
             format!(
-                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:7} [{api:5}]  {mer:02}:{mir:02}",
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:14} [{api:5}]  {mer:02}:{mir:02}",
                 node_id = self.node_uuid.to_string(),
                 pubkey = self.pubkey.to_string(),
                 blockstamp = self.blockstamp.to_string(),
diff --git a/release/arch/linux-x64/build-lin-x64.sh b/release/arch/linux-x64/build-lin-x64.sh
new file mode 100644
index 0000000000000000000000000000000000000000..d1293e7abee6c158b86230a139def219b668ba0c
--- /dev/null
+++ b/release/arch/linux-x64/build-lin-x64.sh
@@ -0,0 +1,131 @@
+#!/bin/bash
+
+if [[ -z "${1}" ]]; then
+	echo "Fatal: no version given to build script"
+	exit 1
+fi
+
+# ---------
+# Functions
+# ---------
+
+# Create description.
+# -
+# Parameters:
+# 1. Initial file name.
+# 2. Building type (either “desktop” or “server”).
+# 3. Category (OS, distribution).
+create_desc() {
+	cat >"${1}".desc <<-EOF
+	{
+	  "version": "${DURS_TAG}",
+	  "job": "${CI_JOB_ID}",
+	  "type": "${2^}",
+	  "category": "${3}",
+	  "arch": "x64"
+	}
+	EOF
+}
+
+# Server specific building phase.
+# -
+# Parameters:
+# 1. Building directory.
+build_extra_server() {
+	mkdir -p "${1}/lib/systemd/system" || exit 1
+	cp "${ROOT}/release/extra/systemd/durs.service" "${1}/lib/systemd/system" || exit 1
+}
+
+# Debian package building.
+# -
+# Parameters:
+# 1. Building type (either “desktop” or “server”).
+# 2. Debian package name.
+build_deb_pack() {
+	rm -rf "${RELEASES}/durs-x64"
+	mkdir "${RELEASES}/durs-x64" || exit 1
+	cp -r "${ROOT}/release/extra/debian/package/"* "${RELEASES}/durs-x64" || exit 1
+	build_extra_${1} "${RELEASES}/durs-x64"
+	mkdir -p "${RELEASES}/durs-x64/opt/durs/" || exit 1
+	chmod 755 "${RELEASES}/durs-x64/DEBIAN/"post* || exit 1
+	chmod 755 "${RELEASES}/durs-x64/DEBIAN/"pre* || exit 1
+	sed -i "s/Version:.*/Version:${DURS_DEB_VER}/g" "${RELEASES}/durs-x64/DEBIAN/control" || exit 1
+
+	cd "${RELEASES}/${1}_/"
+	zip -qr "${RELEASES}/durs-x64/opt/durs/durs.zip" * || exit 1
+
+	sed -i "s/Package: .*/Package: ${2}/g" "${RELEASES}/durs-x64/DEBIAN/control" || exit 1
+
+	cd "${RELEASES}"
+	fakeroot dpkg-deb --build durs-x64 || exit 1
+	mv durs-x64.deb "${BIN}/duniter-rust-${1}-${DURS_TAG}-linux-x64.deb" || exit 1
+	create_desc "${BIN}/duniter-rust-${1}-${DURS_TAG}-linux-x64.deb" "${1}" "Linux (Ubuntu/Debian)"
+}
+
+# -----------
+# Prepare
+# -----------
+
+DURS_TAG="v${1}"
+DURS_DEB_VER=" ${1}"
+
+#rustup add target ${TARGET} || exit 1
+
+# -----------
+# Folders
+# -----------
+
+ROOT="${PWD}"
+WORK_NAME=work
+WORK="${ROOT}/${WORK_NAME}"
+DOWNLOADS="${WORK}/downloads"
+RELEASES="${WORK}/releases"
+BIN="${WORK}/bin"
+
+mkdir -p "${DOWNLOADS}" "${RELEASES}" "${BIN}" || exit 1
+rm -rf "${BIN}/"*.{deb,tar.gz}{,.desc} # Clean up
+
+# -----------
+# Downloads
+# -----------
+
+cd "${DOWNLOADS}"
+
+# -----------
+# Releases
+# -----------
+
+# Prepare sources
+mkdir -p "${RELEASES}/durs" || exit 1
+cp -r $(find "${ROOT}" -mindepth 1 -maxdepth 1 ! -name "${WORK_NAME}") "${RELEASES}/durs" || exit 1
+cd "${RELEASES}/durs"
+rm -Rf .gitignore .git || exit 1 # Remove git files
+
+# Build binary
+echo ">> Building binary..."
+cd "${ROOT}"
+cargo build --release || exit 1
+
+mkdir -p "${RELEASES}/server_" || exit 1
+cp "${ROOT}/target/release/durs" "${RELEASES}/server_/" || exit 1
+#cp "${ROOT}/target/release/durs" "${RELEASES}/desktop_" || exit 1
+
+# Copy logo
+cp "${ROOT}/images/duniter-rs.png" "${RELEASES}/server_/" || exit 1
+#cp "${ROOT}/images/duniter-rs.png" "${RELEASES}/desktop_" || exit 1
+
+
+# ---------------
+# Build .tar.gz
+# ---------------
+
+cd "${RELEASES}/server_"
+tar czf "${BIN}/duniter-rust-server-${DURS_TAG}-linux-x64.tar.gz" * || exit 1
+create_desc "${BIN}/duniter-rust-server-${DURS_TAG}-linux-x64.tar.gz" "Server" "Linux (generic)"
+
+# -----------------------
+# Build Debian packages
+# -----------------------
+
+build_deb_pack server durs
+#build_deb_pack desktop durs
diff --git a/release/extra/debian/package/DEBIAN/control b/release/extra/debian/package/DEBIAN/control
new file mode 100644
index 0000000000000000000000000000000000000000..50190018b1689b3ef5155b0c324c93842381fe3b
--- /dev/null
+++ b/release/extra/debian/package/DEBIAN/control
@@ -0,0 +1,9 @@
+Package: duniter-rs
+Version: 0.0.1
+Depends: unzip
+Section: misc
+Priority: optional
+Architecture: all
+Installed-Size: 20000
+Maintainer: Éloïs <elois@duniter.org>
+Description: Crypto-currency software to powering Ğ1 libre currency
diff --git a/release/extra/debian/package/DEBIAN/postinst b/release/extra/debian/package/DEBIAN/postinst
new file mode 100755
index 0000000000000000000000000000000000000000..13b5d086602e1ef18c4c5768c325c73ea840f7fb
--- /dev/null
+++ b/release/extra/debian/package/DEBIAN/postinst
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+DURS_ROOT=/opt/durs
+DUN_SOURCES=$DURS_ROOT
+mkdir -p $DUN_SOURCES
+
+# Duniter-Rust binary extraction
+if [[ -f $DURS_ROOT/durs.zip ]]; then
+  unzip -q -d $DUN_SOURCES/ $DURS_ROOT/durs.zip
+  rm -rf $DURS_ROOT/durs.zip
+fi
+
+# Create binary symbolic link
+chmod 755 $DUN_SOURCES/bin/durs
+ln -s $DUN_SOURCES/durs /usr/bin/durs
+
+# Add durs user for service
+mkdir -p /var/lib/durs
+adduser --system --quiet --home /var/lib/durs --no-create-home --disabled-password --group durs
+chown durs:durs /var/lib/durs
+
+# Add reading rights
+chmod +r -R $DURS_ROOT
diff --git a/release/extra/debian/package/DEBIAN/prerm b/release/extra/debian/package/DEBIAN/prerm
new file mode 100755
index 0000000000000000000000000000000000000000..565cc57fc8e8f31cec5a0dd6d173132b37e1414f
--- /dev/null
+++ b/release/extra/debian/package/DEBIAN/prerm
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+[[ -f /usr/bin/durs ]] && rm /usr/bin/durs
+[[ -f /usr/bin/durs-desktop ]] && rm -f /usr/bin/durs-desktop
+[[ -d /opt/durs ]] && rm -Rf /opt/durs
diff --git a/release/extra/systemd/durs.service b/release/extra/systemd/durs.service
new file mode 100644
index 0000000000000000000000000000000000000000..3e8131caafecc9bee6643179c7bfa37aa03b8b03
--- /dev/null
+++ b/release/extra/systemd/durs.service
@@ -0,0 +1,20 @@
+[Unit]
+Description=Duniter-Rust node
+After=network.target
+
+[Service]
+Environment="DURS_HOME=/var/lib/durs/.config/durs-dev"
+Environment="PROFILE=default"
+# If using a key file, DURS_OPTS can be defined like so:
+#Environment="DURS_OPTS=--keyfile /etc/durs/keys.yml"
+Environment="DURS_OPTS="
+Group=durs
+User=durs
+Type=forking
+ExecStart=/usr/bin/duniter start --home ${DURS_HOME} -p ${PROFILE} $DURS_OPTS
+ExecReload=/usr/bin/duniter restart --home ${DURS_HOME} -p ${PROFILE} $DURS_OPTS
+ExecStop=/usr/bin/duniter stop --home ${DURS_HOME} -p ${PROFILE}
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/tui/Cargo.toml b/tui/Cargo.toml
index 5b3bc0a26cafa512d153ffeb4924df0a87811b42..6db7e07cd0a4c383f029526b996e39f791ab7448 100644
--- a/tui/Cargo.toml
+++ b/tui/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-tui"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@ifee.fr>"]
 description = "Terminal user interface for Duniter-Rs."
 license = "AGPL-3.0"
diff --git a/tui/lib.rs b/tui/lib.rs
index 3e877a8af94ebd3f586bab9df07b64db81b3466f..022a796f1dd0b13e7ebc1a9c13131b6a086024b0 100644
--- a/tui/lib.rs
+++ b/tui/lib.rs
@@ -259,7 +259,7 @@ impl TuiModuleDatas {
         line += 1;
         write!(
             stdout,
-            "{}{}Step NodeId-Pubkey BlockId-BlockHash    Soft:Ver     Pre [ Api ] MeR:MiR uid",
+            "{}{}Step NodeId-Pubkey BlockId-BlockHash    Soft:Ver            Pre [ Api ] MeR:MiR uid",
             cursor::Goto(1, line),
             color::Fg(color::White)
         ).unwrap();
diff --git a/wotb/Cargo.toml b/wotb/Cargo.toml
index cabd64653d52a0cdb2d67b4640e0bb228bdeeee7..fe022f517985202212d77a70f8bc9b998c32ac43 100644
--- a/wotb/Cargo.toml
+++ b/wotb/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-wotb"
-version = "0.8.0-a0.6"
+version = "0.8.0-a0.7"
 authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@duniter.org>"]
 description = "Makes Web of Trust computations for the Duniter project."
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
diff --git a/ws2p/Cargo.toml b/ws2p/Cargo.toml
index 89d3ace4289a8c9dc78deb4d03639a25bb60f26f..e31b8194566eee57aa4bb8b8090e6b83ccd0f661 100644
--- a/ws2p/Cargo.toml
+++ b/ws2p/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-ws2p"
-version = "0.1.0"
+version = "0.1.0-a0.1"
 authors = ["librelois <elois@ifee.fr>"]
 description = "WebSocketToPeer API for the Duniter project."
 license = "AGPL-3.0"