diff mbox series

[bug#47849,1/1] services: Add a service for the Jami daemon.

Message ID 20210417200617.18182-1-maxim.cournoyer@gmail.com
State Accepted
Headers show
Series Add a jami-daemon service. | expand

Checks

Context Check Description
cbaines/comparison success View comparision
cbaines/git branch success View Git branch
cbaines/applying patch success View Laminar job
cbaines/issue success View issue

Commit Message

Maxim Cournoyer April 17, 2021, 8:06 p.m. UTC
* gnu/services/telephony.scm (serialize-boolean)
(serialize-string-list, string-list?): New variables.
(maybe-string-list, jami-daemon-configuration): New syntax.
(%jami-daemon-accounts): New variable.
(jami-daemon-configuration->command-line-arguments): New procedure.
(jami-dbus-session-activation): Likewise.
(define-with-retries, define-send-dbus)
(jami-daemon-shepherd-services, jami-daemon-service-type): New variables.
* gnu/tests/data/jami-dummy-account.dat: New file.
* gnu/tests/telephony.scm: New file.
* gnu/local.mk (GNU_SYSTEM_MODULES): Register the new test file.
(dist_patch_DATA): Register the new data file.
---
 gnu/local.mk                          |   6 +-
 gnu/services/telephony.scm            | 283 ++++++++++++++++++-
 gnu/tests/data/jami-dummy-account.dat | 391 ++++++++++++++++++++++++++
 gnu/tests/telephony.scm               | 202 +++++++++++++
 4 files changed, 878 insertions(+), 4 deletions(-)
 create mode 100644 gnu/tests/data/jami-dummy-account.dat
 create mode 100644 gnu/tests/telephony.scm

Comments

M April 18, 2021, 11:41 a.m. UTC | #1
Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
+                      (delete-file-recursively "/var/lib/jami/.cache/jami")
+                      (delete-file-recursively "/var/lib/jami/.config/jami")
+                      (delete-file-recursively "/var/lib/jami/.local/share/jami")
+                      (delete-file-recursively "/var/lib/jami/accounts"))

You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}
aren't symbolic links.  That way, if the Jami daemon is compromised (due to buffer
overflow --> arbitrary code execution or something), the attacker can't trick the
shepherd service into deleting arbitrary directories.

This attack is _not_ blocked by fs.protected_symlinks.  From the sysctl documentation:
  When set to "1" symlinks are permitted to be followed only when outside
  a sticky world-writable directory, or [...]

/var/lib/jami is not world-writable (I'd hope).

Example scenario:
  * the jami daemon has a security bug that allows arbitrary code execution
    within the daemon
  * the attacker exploits this
  * now the attacker can modify everything under /var/lib/jami
  * the attacker deletes /var/lib/jami/.config and replaces it with a symlink
    to /home/ANY-USER/.config
  * eventually, the system reboots
  * (delete-file-recursively "/var/lib/jami/.config/jami") is run.
    As "/var/lib/jami/.config" points to "/home/ANY-USER/.config",
    this means "/home/ANY-USER/.config/jami" is deleted.
  * thus, ANY-USER loses their jami configuration

Does that makes sense to you?

Greetings,
Maxime.
M April 18, 2021, 11:47 a.m. UTC | #2
Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
> +                ;; Start the daemon.
> +                (define daemon-pid
> +                  (fork+exec-command
> +                   '#$(jami-daemon-configuration->command-line-arguments config)
> +                   #:user "jami"
> +                   #:group "jami"
> +                   #:environment-variables
> +                   (list (string-append "DBUS_SESSION_BUS_ADDRESS="
> +                                        "unix:path=/var/run/jami/bus"))))

It would be nice if this could be run in a container
that only has access to the relevant parts of the file system
(and not, say, /run/setuid-programs).  See, e.g., gnu/build/linux-container.scm.

Greetings,
Maxime.
Maxim Cournoyer April 19, 2021, 12:07 p.m. UTC | #3
Hi Maxime!

Maxime Devos <maximedevos@telenet.be> writes:

> Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
> +                      (delete-file-recursively "/var/lib/jami/.cache/jami")
> +                      (delete-file-recursively "/var/lib/jami/.config/jami")
> +                      (delete-file-recursively "/var/lib/jami/.local/share/jami")
> +                      (delete-file-recursively "/var/lib/jami/accounts"))
>
> You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}
> aren't symbolic links.  That way, if the Jami daemon is compromised (due to buffer
> overflow --> arbitrary code execution or something), the attacker can't trick the
> shepherd service into deleting arbitrary directories.

It would only be able to delete directories that are world writable
though, right?  Seems the opportunity to cause damage is limited, but
it's a simple check to add, so I'll do it.  What about if the daemon was
run in a container (your suggestion in a following email, to which I
agree would be a good thing)?  It would prevent this kind of attack,
right?

> This attack is _not_ blocked by fs.protected_symlinks.  From the sysctl documentation:
>   When set to "1" symlinks are permitted to be followed only when outside
>   a sticky world-writable directory, or [...]
>
> /var/lib/jami is not world-writable (I'd hope).

No, it's only readable/writable by the 'jami' user of the service:

$ sudo  ls -ald /var/lib/jami
drwx------ 1 jami jami 80 Apr 19 00:38 /var/lib/jami

> Example scenario:
>   * the jami daemon has a security bug that allows arbitrary code execution
>     within the daemon
>   * the attacker exploits this
>   * now the attacker can modify everything under /var/lib/jami
>   * the attacker deletes /var/lib/jami/.config and replaces it with a symlink
>     to /home/ANY-USER/.config
>   * eventually, the system reboots
>   * (delete-file-recursively "/var/lib/jami/.config/jami") is run.
>     As "/var/lib/jami/.config" points to "/home/ANY-USER/.config",
>     this means "/home/ANY-USER/.config/jami" is deleted.
>   * thus, ANY-USER loses their jami configuration

The cleanup code is run as the 'jami' user, so I don't think it'd be
able to touch anything under /home/ANY-OTHER-USER/, unless they manually
loosened permissions on their home directory (shooting themselves in the
foot).

> Does that makes sense to you?

It does!  Thanks for explaining.

Maxim
Maxim Cournoyer April 19, 2021, 12:08 p.m. UTC | #4
Hi again,

Maxime Devos <maximedevos@telenet.be> writes:

> Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
>> +                ;; Start the daemon.
>> +                (define daemon-pid
>> +                  (fork+exec-command
>> +                   '#$(jami-daemon-configuration->command-line-arguments config)
>> +                   #:user "jami"
>> +                   #:group "jami"
>> +                   #:environment-variables
>> +                   (list (string-append "DBUS_SESSION_BUS_ADDRESS="
>> +                                        "unix:path=/var/run/jami/bus"))))
>
> It would be nice if this could be run in a container
> that only has access to the relevant parts of the file system
> (and not, say, /run/setuid-programs).  See, e.g., gnu/build/linux-container.scm.

I agree!  The opendht service is already doing so, so it should not be
too difficult I believe.  I'll try looking into it, after the 1.3.0
release is done.

Thank you!

Maxim
M April 19, 2021, 2:41 p.m. UTC | #5
Maxim Cournoyer schreef op ma 19-04-2021 om 08:07 [-0400]:
> Hi Maxime!
> 
> Maxime Devos <maximedevos@telenet.be> writes:
> 
> > Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
> > +                      [...]
> > +                       (delete-file-recursively "/var/lib/jami/accounts"))
> > 
> > You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}
> > aren't symbolic links.  That way, if the Jami daemon is compromised (due to buffer
> > overflow --> arbitrary code execution or something), the attacker can't trick the
> > shepherd service into deleting arbitrary directories.
> 
> It would only be able to delete directories that are world writable
> though, right?  Seems the opportunity to cause damage is limited, but
> it's a simple check to add, so I'll do it.

Let's step through the relevant code of the shepherd service.

(shepherd-service
  (documentation "Run the Jami daemon.")
  [blabla]
  (start
    #~(lambda args
        [other stuff]
        (when [blabla, and a 'catch' form]
          (delete-file-recursively "/var/lib/jami/.cache/jami")
          [etcetera])
         (let* (([blabla])
                (user (passwd:uid [blabla jami user]))
                (group [likewise]))
           [blabla] (chown accounts-dir user group)))
        ;; Start the daemon
        (define daemon-pid
          (fork+exec-command [blabla Jami cmdline arguments]
            #:user "jami" #:group "jami" [blabla]))
        [blabla]))
   (stop [blabla]))         

Remember that the shepherd daemon is run as root (and therefore
has read-write-execute access to everything).  The 'start' procedure
(and 'stop' procedure) are run _within_ the shepherd daemon.  Thus, the
'start' gexp is run as root.

As the start procedure didn't change the uid/gid from root to something
else, (delete-file-recursively "/var/lib/jami/.cache/jami") is run as
root.  IIUC, root user can read/write anything, ignoring things like "user"
and "group".  World-writability is not required.

> What about if the daemon was
> run in a container (your suggestion in a following email, to which I
> agree would be a good thing)?  It would prevent this kind of attack,
> right?

I don't see how that would help.  It is the _shepherd daemon_ (that runs
as root) that runs (delete-file-recursively ...), not the attacker (from
within the compromised jami-daemon process).  Perhaps this is cleared up
by my previous response?  If not, please walk me through the attack scenario
you had in mind that would be thwarted by running the jami-daemon in a container.

> > Example scenario:
> >   * the jami daemon has a security bug that allows arbitrary code execution
> >     within the daemon
> >   * the attacker exploits this
> >   * now the attacker can modify everything under /var/lib/jami
> >   * the attacker deletes /var/lib/jami/.config and replaces it with a symlink
> >     to /home/ANY-USER/.config
> >   * eventually, the system reboots
> >   * (delete-file-recursively "/var/lib/jami/.config/jami") is run.
> >     As "/var/lib/jami/.config" points to "/home/ANY-USER/.config",
> >     this means "/home/ANY-USER/.config/jami" is deleted.
> >   * thus, ANY-USER loses their jami configuration
> 
> The cleanup code is run as the 'jami' user,

Err, the cleanup code is run from within the shepherd service, outside the
fork+exec-command.  The shepherd service is run _within_ the shepherd daemon,
which runs as *root*.  Only in the fork+exec-command, code is run as the 'jami'
user; the cleanup code is run as 'root'.

> so I don't think it'd be able to touch anything under /home/ANY-OTHER-USER/,
> unless they manually loosened permissions on their home directory (shooting
> themselves in the foot).
See my comment above.

Greetings,
Maxime.
Maxim Cournoyer April 19, 2021, 3:42 p.m. UTC | #6
Hi Maxime,

Maxime Devos <maximedevos@telenet.be> writes:

> Maxim Cournoyer schreef op ma 19-04-2021 om 08:07 [-0400]:
>> Hi Maxime!
>> 
>> Maxime Devos <maximedevos@telenet.be> writes:
>> 
>> > Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
>> > +                      [...]
>> > +                       (delete-file-recursively "/var/lib/jami/accounts"))
>> > 
>> > You might want to verify whether /var/lib/jami/{.cache,.config,.local/share,.local}
>> > aren't symbolic links.  That way, if the Jami daemon is compromised (due to buffer
>> > overflow --> arbitrary code execution or something), the attacker can't trick the
>> > shepherd service into deleting arbitrary directories.
>> 
>> It would only be able to delete directories that are world writable
>> though, right?  Seems the opportunity to cause damage is limited, but
>> it's a simple check to add, so I'll do it.
>
> Let's step through the relevant code of the shepherd service.
>
> (shepherd-service
>   (documentation "Run the Jami daemon.")
>   [blabla]
>   (start
>     #~(lambda args
>         [other stuff]
>         (when [blabla, and a 'catch' form]
>           (delete-file-recursively "/var/lib/jami/.cache/jami")
>           [etcetera])
>          (let* (([blabla])
>                 (user (passwd:uid [blabla jami user]))
>                 (group [likewise]))
>            [blabla] (chown accounts-dir user group)))
>         ;; Start the daemon
>         (define daemon-pid
>           (fork+exec-command [blabla Jami cmdline arguments]
>             #:user "jami" #:group "jami" [blabla]))
>         [blabla]))
>    (stop [blabla]))         
>
> Remember that the shepherd daemon is run as root (and therefore
> has read-write-execute access to everything).  The 'start' procedure
> (and 'stop' procedure) are run _within_ the shepherd daemon.  Thus, the
> 'start' gexp is run as root.

Ah yes, looking the service definition it's obvious.  Sorry for missing
that earlier :-).

> As the start procedure didn't change the uid/gid from root to something
> else, (delete-file-recursively "/var/lib/jami/.cache/jami") is run as
> root.  IIUC, root user can read/write anything, ignoring things like "user"
> and "group".  World-writability is not required.
>
>> What about if the daemon was
>> run in a container (your suggestion in a following email, to which I
>> agree would be a good thing)?  It would prevent this kind of attack,
>> right?
>
> I don't see how that would help.  It is the _shepherd daemon_ (that runs
> as root) that runs (delete-file-recursively ...), not the attacker (from
> within the compromised jami-daemon process).  Perhaps this is cleared up
> by my previous response?

Indeed!  Thanks for taking the time to make it clear for me!

I'll address this in a v2 patch series, hopefully resolving the issues
with the daemon starting in double the first time the system is
reconfigured (something to do with d-bus autospawning I think -- perhaps
the D-Bus ping method is enough to spawn the process if it was not yet
up and running).

It'll take me some time to get to it; probably after the v1.3.0 is
released.

Thank you!

Maxim
Maxim Cournoyer May 20, 2021, 12:37 p.m. UTC | #7
Hello Maxime.

Maxime Devos <maximedevos@telenet.be> writes:

> Maxim Cournoyer schreef op za 17-04-2021 om 16:06 [-0400]:
>> +                ;; Start the daemon.
>> +                (define daemon-pid
>> +                  (fork+exec-command
>> +                   '#$(jami-daemon-configuration->command-line-arguments config)
>> +                   #:user "jami"
>> +                   #:group "jami"
>> +                   #:environment-variables
>> +                   (list (string-append "DBUS_SESSION_BUS_ADDRESS="
>> +                                        "unix:path=/var/run/jami/bus"))))
>
> It would be nice if this could be run in a container
> that only has access to the relevant parts of the file system
> (and not, say, /run/setuid-programs).  See, e.g., gnu/build/linux-container.scm.

That's now the case in the just-sent v2, both for the D-Bus session
service as well as the Jami process itself :-).  I figured out I could
simply call make-forkexec+constructor/container and execute apply the
resulting procedure.

I've also manage to (with much difficulty!) have the service properly
start, stop or restart without races.  The new tests proved really
useful for the lengthy trial and error process that I had to go through.

Thanks for your patience!

Maxim
diff mbox series

Patch

diff --git a/gnu/local.mk b/gnu/local.mk
index 50b11a8ca2..4a412f5a69 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -19,7 +19,7 @@ 
 # Copyright © 2018 Amirouche Boubekki <amirouche@hypermove.net>
 # Copyright © 2018, 2019, 2020, 2021 Oleg Pykhalov <go.wigust@gmail.com>
 # Copyright © 2018 Stefan Stefanović <stefanx2ovic@gmail.com>
-# Copyright © 2018, 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
+# Copyright © 2018, 2020, 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
 # Copyright © 2019, 2020 Guillaume Le Vaillant <glv@posteo.net>
 # Copyright © 2019, 2020 John Soo <jsoo1@asu.edu>
 # Copyright © 2019 Jonathan Brielmaier <jonathan.brielmaier@web.de>
@@ -710,6 +710,7 @@  GNU_SYSTEM_MODULES =				\
   %D%/tests/security-token.scm			\
   %D%/tests/singularity.scm			\
   %D%/tests/ssh.scm				\
+  %D%/tests/telephony.scm		        \
   %D%/tests/version-control.scm			\
   %D%/tests/virtualization.scm			\
   %D%/tests/web.scm
@@ -1829,7 +1830,8 @@  dist_patch_DATA =						\
   %D%/packages/patches/ytnef-CVE-2021-3403.patch	\
   %D%/packages/patches/ytnef-CVE-2021-3404.patch	\
   %D%/packages/patches/zstd-CVE-2021-24031_CVE-2021-24032.patch	\
-  %D%/packages/patches/zziplib-CVE-2018-16548.patch
+  %D%/packages/patches/zziplib-CVE-2018-16548.patch		\
+  %D%/tests/data/jami-dummy-account.dat
 
 MISC_DISTRO_FILES =				\
   %D%/packages/ld-wrapper.in
diff --git a/gnu/services/telephony.scm b/gnu/services/telephony.scm
index e1259cc2df..9e821f7286 100644
--- a/gnu/services/telephony.scm
+++ b/gnu/services/telephony.scm
@@ -1,5 +1,6 @@ 
 ;;; GNU Guix --- Functional package management for GNU
 ;;; Copyright © 2017 nee  <nee-git@hidamari.blue>
+;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -17,16 +18,31 @@ 
 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (gnu services telephony)
-  #:use-module (gnu services)
+  #:use-module ((gnu services) #:hide (delete))
+  #:use-module (gnu services configuration)
   #:use-module (gnu services shepherd)
   #:use-module (gnu system shadow)
   #:use-module (gnu packages admin)
+  #:use-module (gnu packages glib)
+  #:use-module (gnu packages jami)
   #:use-module (gnu packages telephony)
   #:use-module (guix records)
+  #:use-module (guix modules)
+  #:use-module (guix packages)
   #:use-module (guix gexp)
   #:use-module (srfi srfi-1)
   #:use-module (ice-9 match)
-  #:export (murmur-configuration
+  #:export (jami-daemon-configuration
+            jami-daemon-configuration-jami-daemon
+            jami-daemon-configuration-dbus
+            jami-daemon-configuration-enable-logging?
+            jami-daemon-configuration-debug?
+            jami-daemon-configuration-auto-answer?
+            jami-daemon-configuration-account-archives
+
+            jami-daemon-service-type
+
+            murmur-configuration
             make-murmur-configuration
             murmur-configuration?
             murmur-configuration-package
@@ -74,6 +90,269 @@ 
 
             murmur-service-type))
 
+
+
+;;;
+;;; Jami daemon.
+;;;
+
+;;; XXX: These dummy definitions is because there's no way to disable the
+;;; serialization code from define-configuration.
+(define (serialize-boolean option value) "")
+(define (serialize-string-list field-name val) "")
+
+;;; Copied from (gnu services messaging).
+(define (string-list? val)
+  (and (list? val)
+       (and-map (lambda (x)
+                  (or (computed-file? x) ;XXX: for tests
+                      (and (string? x) (not (string-index x #\,)))))
+                val)))
+(define-maybe string-list)
+
+(define-configuration jami-daemon-configuration
+  (jami-daemon
+   (package libring)
+   "The Jami daemon package to use.")
+  (dbus
+   (package dbus)
+   "The D-Bus package to use to start the required D-Bus session.")
+  (enable-logging?
+   (boolean #true)
+   "Whether to enable logging to syslog.")
+  (debug?
+   (boolean #false)
+   "Whether to enable debug level messages.")
+  (auto-answer?
+   (boolean #false)
+   "Whether to force automatic answer to incoming calls.")
+  (account-archives
+   (maybe-string-list 'disabled)
+   "A list of Jami account archive (backup) file names to be (re-)provisioned
+every time the Jami daemon service starts.  These Jami account backups should
+@emp{not} be encrypted and their file should be made readable only to the
+@samp{jami} user (i.e., not in the store), to guard against leaking the secret
+key material of the Jami accounts they contain.  When providing this field,
+the account directories under @file{/var/lib/jami/} are recreated every time
+the service starts, ensuring a consistent state."))
+
+(define %jami-daemon-accounts
+  (list (user-group (name "jami") (system? #t))
+        (user-account
+         (name "jami")
+         (group "jami")
+         (system? #t)
+         (comment "Jami daemon user")
+         (home-directory "/var/lib/jami"))))
+
+(define (jami-daemon-configuration->command-line-arguments config)
+  "Derive the command line arguments to used to launch the Jami daemon from
+CONFIG, a <jami-daemon-configuration> object."
+  (match-record config <jami-daemon-configuration>
+    (jami-daemon dbus enable-logging? debug? auto-answer?)
+    `(,(file-append jami-daemon "/lib/ring/dring")
+      "--persistent"                    ;stay alive after client quits
+      ,@(if enable-logging?
+            '()                         ;logs go to syslog by default
+            (list "--console"))         ;else stdout/stderr
+      ,@(if debug?
+            (list "--debug")
+            '())
+      ,@(if auto-answer?
+            (list "--auto-answer")
+            '()))))
+
+(define (jami-dbus-session-activation config)
+  "Create a directory to hold the Jami D-Bus session socket."
+  (with-imported-modules (source-module-closure '((gnu build activation)))
+    #~(begin
+        (use-modules (gnu build activation))
+        (let ((user (getpwnam "jami")))
+          (mkdir-p/perms "/var/run/jami" user #o700)))))
+
+;; Local definitions to expand in source form in G-exps.
+(define define-with-retries
+  '(define-syntax-rule (with-retries n delay body ...)
+     "Retry the code in BODY up to N times until it returns #t,
+else #f.  A delay of DELAY seconds is inserted before each retry."
+     (let loop ((attempts 0))
+       (if (< attempts n)
+           (or (begin
+                 body ...)              ;return #t on success
+               (begin
+                 (sleep delay)          ;else wait and retry
+                 (loop (+ 1 attempts))))
+           #f))))                   ;maximum number of attempts reached
+
+(define define-send-dbus
+  '(define (send-dbus dbus-send interface method . arguments)
+     "Print the response and return #t on success, else #f."
+     (let* ((command `(,dbus-send
+                       "--bus=unix:path=/var/run/jami/bus"
+                       "--print-reply"
+                       "--dest=cx.ring.Ring"
+                       "/cx/ring/Ring/ConfigurationManager" ;object path
+                       ,(string-append interface "." method)
+                       ,@arguments))
+            (pid (fork+exec-command command
+                                    #:user "jami"
+                                    #:group "jami")))
+       (zero? (cdr (waitpid pid))))))
+
+(define (jami-daemon-shepherd-services config)
+  "Return a <shepherd-service> running the Jami daemon."
+  (let* ((jami-daemon (jami-daemon-configuration-jami-daemon config))
+         (dbus (jami-daemon-configuration-dbus config))
+         (dbus-daemon (file-append dbus "/bin/dbus-daemon"))
+         (dbus-send (file-append dbus "/bin/dbus-send"))
+         (accounts (jami-daemon-configuration-account-archives config))
+         (declarative-mode? (not (eq? 'disabled accounts))))
+
+    (list (shepherd-service
+           (documentation "Run a D-Bus session for the Jami daemon.")
+           (provision '(jami-daemon-dbus-session))
+           ;; The requirement on dbus-system is to ensure other required
+           ;; activation for D-Bus, such as a /etc/machine-id file.
+           (requirement '(dbus-system syslogd))
+           (start
+            #~(lambda args
+                #$define-with-retries
+
+                (define pid (fork+exec-command
+                             (list #$dbus-daemon "--session"
+                                   "--address=unix:path=/var/run/jami/bus"
+                                   "--nofork" "--syslog-only" "--nopidfile")
+                             #:user "jami"
+                             #:group "jami"
+                             #:environment-variables
+                             ;; This is so that the cx.ring.Ring service D-Bus
+                             ;; definition is found by dbus-send.
+                             (list (string-append "XDG_DATA_DIRS="
+                                                  #$jami-daemon "/share"))))
+
+                ;; XXX: This manual synchronization probably wouldn't be
+                ;; needed if we were using a PID file, but providing it via a
+                ;; customized config file with the <pidfile> would not
+                ;; override the one inherited from the base config of D-Bus.
+                (let ((sock (socket PF_UNIX SOCK_STREAM 0)))
+                  (with-retries 20 1 (catch 'system-error
+                                       (lambda ()
+                                         (connect sock AF_UNIX
+                                                  "/var/run/jami/bus")
+                                         (close-port sock)
+                                         #t)
+                                       (lambda args
+                                         #f))))
+
+                pid))
+           (stop #~(make-kill-destructor)))
+
+          (shepherd-service
+           (documentation "Run the Jami daemon.")
+           (provision '(jami-daemon dring))
+           (requirement '(jami-daemon-dbus-session))
+           (modules `((ice-9 ftw)
+                      (srfi srfi-1)
+                      (srfi srfi-26)
+                      ,@%default-modules))
+           (start
+            #~(lambda args
+                #$define-with-retries
+                #$define-send-dbus
+
+                (when #$declarative-mode?
+                  ;; Clear the Jami configuration and accounts, to enforce the
+                  ;; declared state.
+                  (catch #t
+                    (lambda ()
+                      (delete-file-recursively "/var/lib/jami/.cache/jami")
+                      (delete-file-recursively "/var/lib/jami/.config/jami")
+                      (delete-file-recursively "/var/lib/jami/.local/share/jami")
+                      (delete-file-recursively "/var/lib/jami/accounts"))
+                    (lambda args
+                      #t))
+                  ;; Copy the Jami accounts from somewhere readable by root to
+                  ;; a place only the jami user can read.
+                  (let* ((accounts-dir "/var/lib/jami/accounts/")
+                         (pwd (getpwnam "jami"))
+                         (user (passwd:uid pwd))
+                         (group (passwd:gid pwd)))
+                    (mkdir-p accounts-dir)
+                    (chown accounts-dir user group)
+                    (for-each (lambda (f)
+                                (let ((dest (string-append accounts-dir
+                                                           (basename f))))
+                                  (copy-file f dest)
+                                  (chown dest user group)))
+                              '#$accounts)))
+
+                ;; Start the daemon.
+                (define daemon-pid
+                  (fork+exec-command
+                   '#$(jami-daemon-configuration->command-line-arguments config)
+                   #:user "jami"
+                   #:group "jami"
+                   #:environment-variables
+                   (list (string-append "DBUS_SESSION_BUS_ADDRESS="
+                                        "unix:path=/var/run/jami/bus"))))
+
+                ;; Wait until it's reachable via D-Bus.
+                (with-retries 20 1 (send-dbus #$dbus-send
+                                              "org.freedesktop.DBus.Peer"
+                                              "Ping"))
+
+                ;; Provision the accounts.
+                (when #$declarative-mode?
+                  (or (every identity
+                             (map (lambda (archive)
+                                    (send-dbus
+                                     #$dbus-send
+                                     "cx.ring.Ring.ConfigurationManager"
+                                     "addAccount"
+                                     (string-append
+                                      "dict:string:string:Account.archivePath,"
+                                      archive
+                                      ",Account.type,RING")))
+                                  (map (cut string-append
+                                            "/var/lib/jami/accounts/" <>)
+                                       (scandir "/var/lib/jami/accounts/"
+                                                (lambda (f)
+                                                  (not (member f '("." ".."))))))))
+                      (format (current-error-port)
+                              "error: failed provisioning the jami accounts")))
+
+                ;; Finally, return the PID of the dring process.
+                daemon-pid))
+           (stop
+            #~(lambda (pid . args)
+                (kill pid SIGTERM)
+                ;; Wait for the process to exit; this prevents overlapping
+                ;; processes when issuing 'herd restart'.
+                (waitpid pid)
+                #f))))))
+
+(define jami-daemon-service-type
+  (service-type
+   (name 'jami-daemon)
+   (default-value (jami-daemon-configuration))
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             jami-daemon-shepherd-services)
+          (service-extension account-service-type
+                             (const %jami-daemon-accounts))
+          (service-extension activation-service-type
+                             jami-dbus-session-activation)))
+   (description "Run the Jami daemon (@command{dring}).  This service is
+geared toward the use case of hosting Jami rendezvous points over a headless
+server.  If you use Jami on your local machine, you may prefer to setup a user
+Shepherd service for it instead; this way, the daemon will be shared via your
+normal user D-Bus session bus.")))
+
+
+;;;
+;;; Murmur.
+;;;
+
 ;; https://github.com/mumble-voip/mumble/blob/master/scripts/murmur.ini
 
 (define-record-type* <murmur-configuration> murmur-configuration
diff --git a/gnu/tests/data/jami-dummy-account.dat b/gnu/tests/data/jami-dummy-account.dat
new file mode 100644
index 0000000000..026890052d
--- /dev/null
+++ b/gnu/tests/data/jami-dummy-account.dat
@@ -0,0 +1,391 @@ 
+;;; JSON extracted from an actual Jami account and processed with
+;;; Emacs/guile-json.
+(define %jami-account-content-sexp
+  '(("RINGCAKEY" . "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRd0lCQURBTkJna3F\
+oa2lHOXcwQkFRRUZBQVNDQ1Mwd2dna3BBZ0VBQW9JQ0FRQzBxWUozSkYvTzhQRGEKRnUwRnpRcHBCaD\
+gybGJMdURrNTlVU0I0MUJSaS9kdDZGV1BRN29YOVpsY25vNGZzM2dmUHQ0dU1hRVBkVFBGKwowbGN2Q\
+jc2cytQTEFlcjlOZGpVQzQ2ZXp0UnNiNE9aQXc4ZUk1M3EwSU04QWJFd0o0ZjllLzBmQUFueHgrK3Qw\
+CmZDeGV1YTBUaVBqVHBpZVJMNmpwQkd5UGI2Qk5pU2ViTkZCNzJOMTBnbzI4REVQYlhkNE9CNkN1blZ\
+5RGZKSU0KOC9PRy8rMndNamI4WkRwT3JrYy93U2ZHbnQyZXA3U0xKSkgwOFIzV1FKWklsSndrcGdLTH\
+FRakVwWFNpclN4dAozSkdtdXdBdE9LaXFFTXh1R043elV3ZlNINEtHdkRaUFNkZklZVXJ3eEp4aDJZZ\
+3lobG5RRC9SSnhRN3d4YlJBCjFhMUZVV0FzbDhLODk5cEtESk1GL09VOWZMRUx6QlViblpaRDRmSlg1\
+NmcyTlluUnJobS94NG9FbFk0MFNYMUUKcHYzb01hNnZrVGN5RjJnUFhKL2FkUVJoS0dFaGRjaHBpeDl\
+5UVphaDFCUFBGYW5jNzBMcjhOaDZJeHFNQ1hiMQozMG9vWHpWZmZNMVFOd28rL3hzRnBlRkRqUTAxQ0\
+9pdWZocitKREcyc0txb0o0V0JwYVhubWI1YXVrVWUvV1RKCjAxVmRyaEkvSVExd3V4QzNMMnpac3dVU\
+1NTaDk0aXg1M0hpU3pWbkI5UkxmaVhZUUVCcFEyNHVoRTdiYlo0bm0KZTczZC9zenpPTXMzYUt3OWtW\
+a2VLMTVtYWhSVWZjdEdhSVQxTkhGWUNYYXByaWExakdNdVpmSk1pSUtZUzNidQpMbUhZckF6dEptNDZ\
+0aHpjdnN3NHlhMnFoa2xUUlFJREFRQUJBb0lDQVFDaHZaUm85KzZ5aFhFTHZ6U3FXZHcxCkZGOERibG\
+hIMmhVWkNuV0kxMDM5SmdyRkxMczFSU1krSzg1aFZYMk9hV1VTNk44TmNCYzUyL1hrdFltS09HUFQKM\
+WZqMnE2M3pPcDNSSFdGNWVPMXhNeExRN3JZSDhqMGZZTFFTUytKemdwb3ZRVnJLSXkrb21JSSt3aUN6\
+R1laRApGQUM0ODJzL0J5MHdtRjVjdC9JTEdIeVY3ZXNVUlo1Vi9iL0ltQzUwQ1lDUWpQR2xBb3JkeUx\
+1MHp2NjZZUXc2CkQycTg0VHAyVUg3SExEVmhFNytUbDg4Q04xWll0VGtpSkthbkNpMFVmbStPKzJFM0\
+5HM01hajk1aDl2NktqYkoKUlkxeTNDRTVmQmkyUFNLbVVzRjN3SzdhbzJDRks5MTgybmlxL2FaNm5WO\
+Xc3NmVrRjhEOWUvS1pqUE5ZT0xkaApFczBSL2laV3RpbUx2RHdXQWNWNFNnSFFjNXJvNU9yOEFUS1ZK\
+VmlzZGFuWXkvdUhmVXZWN3U5cDVLK2E4SHU2CllabW13ZTh4bnF5M3V2M0VabE9LY20zTnZvWllVMnJ\
+HUUFQQW1sWWQ4WlRsZGxPa1JCSGxxYzllMmJuSnNTQW8KNUhhS0N3aDJsWmZpalVGNXFrMXNQcm1kN3\
+BlMld2VVV3QmVuSjJnS1ZoTE5VVGtHWmtTWGhzNlV3WWRRMjVtRQppQzl6WjhXNkQ2OXBvb0lsTTVXT\
+01ySEs0Rng1ck9vT05kUHQ4NEk1bTI3cnpnbXM4QnJXVUlGLytZZjJ0bGdmClBIR0V4c3ZCK3JRQk52\
+WHU3dXoxcVdFTlJTL2YwR2E3dVF4ZW5sZ2dubHc5M1pNOW1GWXpXb1RpdWFmdnphTnAKWEsrTEVrV2F\
+RYUs1Q0VaNEhmUlhBUUtDQVFFQTUyK240OUxQODlyQkR2bFdsTkxNanJqTDdSb0xyQ3FVZGpQWQpyT1\
+hZS3ZkTkxyS2NTc1hNdkY4YW5HQng1UG5oVDZGY05ic2dzQ3BUUXowMThZYmcrbUUxQno5ZTdFNTJGZ\
+i9NCk9BbWZqSllXUnZueUtiNnB3SGlvOHlXUXlVVk1zZU1CcmpvWk1kNkpPZEZ0K2JITHBWOS9iSkdR\
+a3NTRE04WTkKbWxGQUlUL0gyNTh1K1ZKTWsrT0prU28zZmJQSk5Ja1Q2WVBKVmNaSnZTRGI5QU83WDF\
+lendCOXVRL0FEblZ6YwpSQkJOUVZaTStZS2ZNWFJBdmFuWnlmWFFwaUxCQW8rRVRPSHJCR1dDRUhtSF\
+RCaEZIMTkyamtxNlcrTStvS0R1Cm1xMitMc2hZWTVFc2NpL2hPOVZjK2FCM0hhaGliME03M092MHFNc\
+WZoTncyU1BncHdRS0NBUUVBeDlaR1gxQnQKL3MwdGtNcGV1QWhlWjFqTklnZmFEY0RRTWlmU0k4QjRx\
+WUhiL1hOREQ5NjFQME9zMDdCN2wzNE1iZ3U2QlNwcwpXdSt0Y1hjSFlqQVJUc0Qzd0pSaVRIb0RSQzR\
+YYkxEa2pHRUVCVzRKbFVqZTA1QWZrU0QrdkZSMkJwZStxQlBLCm5yb3Mwd1BWL3RXa3MzY2VFOUlBTV\
+pWWDhQeFA3RVNXbitVZDJEWkZhcVFLb0JybHZXRXhxelpYUEJSVjhoS3QKcFBqWnFkZXFQLzhUZTBtS\
+zh5MEVreXVXOWhFdGZ2Sm5HWXhMNStrSG9xd2hQVk1tODZ5YlZNVHRQYWJTdCtPUwp0WHhJTE9RMWRN\
+QkFabzRxSnNkUUZJcTJnSHA5WFYwa2ZNUms1ajdJT1Q4c2Z6TlpKVkRNK1k5VHVlSGJXSnduCnZsWld\
+VZ2NVZTlBaWhRS0NBUUVBbFJaK2h1ckUvNGdLR2dWUld5bTRrTEJHM2dTTFJHdGhuQXVtSnlzaFoveE\
+wKZ2l1Wk55bll5L2hRQWpDMjdoUnlxb04rRFRid3hjdGVPOUJ3c1poNzBZOVJROHYwOERGVExMVE43O\
+E56UG5OcApBbXY5TGhzZTYxaFBMZU1qTkNVcVZPV3hyWFRMeWk1YkpCM2Z4SnhlWGJmNU5BMUpudUpz\
+eXF1SC82TWJ0cytKCmhkY3p3WFRjMCtBZVBKOS9nOENQZXdKYkMzRFVBQ2R1VlNHWHo4ZWZxcm1xbDd\
+jbnB5ZzBpK2pJRkNpVU8rVEcKVFcxeDg3KzUvUFF2MGtSQ0Z1UUloZ2ZCNkcwWW9vcHBrUWRZdXhKZl\
+pPaHdUUldpbTVMMlF5K294WWZySGVQOQozSlltbGFCMmJiN3kxL1FoQjcvek9VMk1nTEtYdHl4Z09ve\
+EpoQlFwZ1FLQ0FRQkIwSUE4dy9CMkNuMEhRcDhQClhUSTZOelRZRUYzd1NhQkg1SFdBOE5MTWdNaERJ\
+TUxsWnlPcVFrK1pLSGFMM2llWjFxTGRNS3VmQjNESC9idWcKeXRQb2JBVXNsN0lJSGVjVmZWaVpvMml\
+pRXhHUCtEMlB2UUFtRFVGWU90V3FrT2FPSlV2VmJ5ODhOM1NyeW9lZgo5aHpZUGxMWmxFQWNGR055S3\
+FibjJXOENHaU5LSWhXYW1Zd21UclY3T1pkeUcrTi9GZk40Vms1NkZyc1pCTDQ5CmRYU2xGZ045TTBaZ\
+WNleTEvZEpPRE9lSHNuME5VK0gvNFZEUk1hR1NmelpwSkxJOXE4T2FiSWpVM0ttb24wQTcKdzFWeWNU\
+L1FwYlBxRUFVckt5dytvMzV3MlAyaUZ1czZiMlBvUUxFTGFTRVl6K3R6UEw5UTM1ejNRdGdMQytuagp\
+IUmxCQW9JQkFES0Q4NGhrYkphczlIQ00zanNNSU9kb213ZEMvcktxVUxKNHFWZU5QR0xDY2VpNEdocn\
+dlQnNICnNoN0hibFlZSDN2U2Y3RW1iZEVCb2xlWVJsaUx5ZzJDQU1ZOGpNK0lpUEwydk1yM2Y3SzZPT\
+mU0UkVPUFJSZkcKWlJDcTh4a0ZPQlg1SUJUbkVCV3QzdVdyb1NGY2x4RTdUa2I4VkUyVzExTG9ZNlkw\
+TUNPaHdxN21xYXRUVnNrawpTRDNySmkrTFR6a2Y4OEx1bjZZNjdiaFNOTWpKZkFaUXNQc0FTRkJBUTJ\
+rQnE5alRLZGVuaU4yYTJIbm0xNCtrCnJDeU9ZVE14Q2hQbWNpS25pVy9MWnFUL0U1dlNRUGdBVzc0dT\
+VLazJoSjRBajNjRW9NVEwxSytZbStWYWh2U0cKTi8xOFdYQ1JRQkg1d0p2eXJYczBtT29GQlRnTWg4d\
+z0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=")
+    ("ringAccountKey" . "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRUUlCQURBTk\
+Jna3Foa2lHOXcwQkFRRUZBQVNDQ1Nzd2dna25BZ0VBQW9JQ0FRRDNCdDRnOUVUdk9EVnYKM3hWV0ZlS\
+1Nnbk5uVEF3S2dYa3IrQ1FhdU8vTGZWM01RenNSNHliL3hoaWhhb1Z2c2FtZ3ZRU1haL0M1R3I5QQpE\
+YlAxbHNHclRCK1pXMC9uMXVEb3hmVWdZRWY3SGtVanJtdVFjUGlFWGlUVkNiY002U0NzdVNrMnRxdE1\
+BNjBNClRacVo2LzAvbkEvblR5TnFNaUJNSmFRN0NUb2xOTTh2Z2tvd0tBRW14OGpJUG9YZEttMnd6MD\
+Z6SnhwU1d4WjUKc0FBTkdSSHU0b2ZXNWJiMERvamtnRzFBYUJ6Nm9uSmdKK1JFSWV1UkpNQWFHYmtzW\
+TQ4Z1Z1b21BVTU0UFNvUApFb3psVGdHd1k0cnhJTGE0V1Y0RVZMVExrR240ZTYrRUYrSjIvRURBUFdN\
+bXREdFpUeURCc2t5bStLaDRUT2xPCjdnN3JlUUhLdDd1R21YU0RnbzVKZ2hOOHNVRFUxL1Z2YmFFcUZ\
+tTDJrZWNGOVlVZmNsUWRGY1ZncmIrMkh5R3AKRVc0b3RkZjlYYzhOMWxrbGk1dFBqRGZuQ1U3OHB6QT\
+BxQmV1SWhZTnF1VjhGSm1NemhXeVFDbE1MUEFYbXFVdwpWYlY0MWduM0NkektuUlVhZXFONXlzOG5KQ\
+mRJNDBleWlYRUlvU0VKcFpyT1Z5ck1icnNCQzltaVFpZUFhSnlBClFvcjFaRGlwMkpZNUFza2phUGQ0\
+NGk0MGNkeFpob1RhNnpzako5UjZScllFYjhWTmZQemFLaElwSXJSd2NpbCsKWjFrbUUwSE1kY21ITXR\
+hbWZhK0l6WUR4dDV6OGVOZys1RGpVVG1MRkdyQUVWRW5hNDcxdllxYnk5UCs1T1cvNQpSdkxtUER2Tj\
+dlUURpTlZENlBYRFk5OTY4bTZaaHdJREFRQUJBb0lDQURjOEkrTCtlNE41OEFqcHV0MmEyeVNqCllxY\
+VFUSWowMW1GTWhOWXMwQUdTTUswQncyMkdleXZwNFl3R1EzdnNIOSsvSkEydXdoYkJzazNpUW9FQmlx\
+Q0EKenZmOWdPcDRFNlk0elV6RitwSmQvRnUwSG4wWHBab0Rhdnp2eFN4djNFeUN3b0puYWZuL1FHeGw\
+xZEhoQUtsKwpmZGZjekRCc3NPZ1Y2cGtBd1MyY2wwOHFOT2g3cVhaQWFkYk1sQ1lWM0owU1hhaVZiNz\
+lHZXNvTzNwUVBMUUZiClNjQjFjT2sxYnNxWkpOU244d0xmMis5QVBEdzMwWEtNNHg5eTdRTE42Q3oxQ\
+WpvcFJLQ0NIS3R1SEc4UmVETTIKcnRTbjJmTnltQ0VqeDZGVTB6MHFldDV3Y01UbU5weEZuYXdEMU5s\
+dFpnZXBsSllwTjVKZXNEUmo2cFlnWXBPKwo5UDU2cEdtZVNTVzVxcHRoWFFLQmFsdy8wWXp2YUlYdHp\
+hTnZyNUJzRFNrWkU3cldzODIvQmFzUE1RckFmOGpLClZFMU9pSzcrVllUVTRFVWVoZ0FZOXdzNjFqYk\
+lSSEpQbW9VQXpkWHcyL0d0Vk9JUTFwUFJnOXNYN0JaMmUyV1YKdmd5aThPUEJxRWtwblBiMkU5Z0d1U\
+25rY01OeWFVbUl0c3pFandadW14dnZrUW5HanlTb3pjY2R3dnNQNnBJagpoN0g5VUNQTHdOM0N5N1lp\
+UmliSlZBWlNjZkF6QzhubXNLODQrTzJUZHBzTXk0SEZDMmM4dlZiclpteTVkWC9qCk1ESnBzS25JWlZ\
+JMmpXSzRpRS82aUdIWVdoY3JvWnYvVEJ0YW1SQUxTWDVOYkhhWTI0bXVRSG5yMldtaytld3EKbHRGbC\
+90bXgyVkpWUitMZ0JCUGhBb0lCQVFENEI0MjQyVTgvbkJ4d2RzelhCdWxBOVFTa0I5Zktud0RlRkV3S\
+wp3Nks0eU14YVdRU04ycjRxRDhLcW93OVZVMzRYdkRWbFh0RUlDaVh3Q0hZdW5IL3g3cXNSdEVzbHJM\
+aWg4UHRPClpDSU8zUml4RmlIQXFlQUh2YXF0NVhXdndaREx6WnV2THRJOTdINkZ1QjYxck5qMnhxdlR\
+IN05pUmp2S0R0WXgKR1VtNURoQ094cm9tR0NkWHRnWHJGaS9WRU1TSmpQMkM3OTNrN1pTNmNZL0Nkc1\
+RqWEsrZ1UzeWM3OC9kN1pYbwpKMGg2WFlSdmhlQW9Bd2dkVTl4MWtYL2ZmY2tZK1hUcFRwV2xXWmtlU\
+ytsejBpQkRnUlJzMm45OFZDeTZDRmRZCldsZXZaZy9SWXZ6dzlKdWFVcXArOHpHbHNXR2xuOEhtZW5X\
+Q1luSHJnNFBxQnRkL0FvSUJBUUQrOXhDL1N5ZGIKVWZxMUJHYy95YktZc3RLb3A1azB0d3M0SlUwTzN\
+aT1U3MlZ5YTdLT2lTemdPSzVnL2QzckhMMXR2dHViTDBWNAo1dEF4a1AzSkVYbmxZZU5IVkpROTc2RH\
+NGS3Ztc0FGL2JJdVBsdFBRT1dyM3g1eW1RU3lCOTBUczV0dFdWMTVQCktYYVNnMTZidDhwNS9MeERkZ\
+ng2c1YzN1o5RDFRd21EQllreVFIcWQ4clljTm9ad1M5ZnI3UTZhN1ZNSDVtT3IKbEF5dzBCYVdZQk9k\
+bjFGd0pVV3NlRlpmTy9vNUVqZk9Hd0xMR1hiOEVmQ1hqdlRYcUNHLzNrT1JvN1NkOWY1eQowVjIxMmt\
+YVVNINHNDbFB0SmwzeHpaMWJxQ2RMVDNITWNLUTk5UGFFVFppQnNXQ1lOcXg1c0Q0RGhoMzdZQ2hKCm\
+hlN3VUM1E0MElINUFvSUJBREN1VXR1b0UweloyQjhld2grbUpKdnlPMEh5cENFSnlrTE1Xd3gxejNkV\
+E9nQzEKbmhZMWk4TjNxbTZSYUk0SHdDVHFkTlI3b3ExZ1NJZnZNVHIrem9IdXBUYnBXeUorM3hJeDJU\
+Rk9wL3lnMnByUApURHFqWE94SUJycnc0WU5vaTRIa3poeTVKTnl3a1RpdnBaOWsySVMvQTdTQmNWVGx\
+raENianVDK0pPRWthSTJOClpiWGFZY1p1WElVQ3FzcTM2c3RRbCtWZUxRQWt2VjlHc0wrclRnT09Dbz\
+UrTkdRZEVZQnVoRkMzZlJzL1JhSVoKOWFBRTBFL3BTTWp1a05tTnQ2Mm1NSk1tTUdydXhnWFRRblBRR\
+npNSW43aXB2Z0hxQjRsUDM4emdsbnMvbmZVcgo1NWRuZXk3ejhMRFFETHVIc0RHd3hINzNKQjgrTVR2\
+WGFVbkNwQU1DZ2dFQVNBSGxBL0dvdXR6TFRvWmcxcDRUClI1YnhjZHBycFh5d3VYbW5hclJmY3VldG9\
+nUVNtTGpiS0xRNVk0RXZSTENJTzA5MDNENGNnOG5FTU10L01XTXoKSnZwZll3emJGU2J4THR1anRQSX\
+VhaHR3eXV2UkJIVEM1aG5FL3h0WEE1bWZLTDBHWXpzbmtubm1WL2lzSnBSZwpwZFVnSW5sWEJodkRyR\
+FlreUsvWEp0N1FZWlhlUzI5NXlUd0krZndoamlzVVBlTWEyUmRUUE9rQ01JbUVaNUhZCjJHSmZjS25H\
+SkxDVHpDKzNPcGtQazdFRE4vTUlMS2F3YVUxaGp1cVlKWVVUVmpXQzFEM2VUL1ViWHptM0VQNHMKVEN\
+uYWpCYVMzN0N2YVd4ek5JektXZS9TSXdGbEFmYWNSTHlneUR4Z3Q3bHp1akVObEtvU2xya3h3ckpEND\
+Z2WAptUUtDQVFBcTVQWWxSQjgvNnFiWWt1OTA0NUZRdXk2QWtlYXBaMW0raS9SQzZtbFRvUXB6NDlPU\
+Gs4ZGx5YjVtCndvSVhpaEo2V05jN1RsWlRYMnpQTTRBS0M5VFNBUWJrWDg5bjEyU2VDSUlHbXVINnk0\
+TjZiY2lxZjVVcSsvc0IKcHJKeFRNYlRSUFpqS0VVd1N0SFg1MUQ1bi9sQnZERGY3Y2VEZytsYlE0RjR\
+KMTlPd09oZ1lGcjFheGQvNXd2VgpURjNoVlQwbFZGN2RyRC9iMHZOcmxnbUNjbEk4UDg1a2dkRUhZbG\
+ZtTFoxeXJIMkNXVy9SS0lsWk9ZdFVuNFNpCkp5a2VlNDROWElXU3ovalRBdFRta3VQTzRvUjF5d3dRc\
+jdhUTF5a3hRVm9rVm5vY2xqU0tyQlk4R294a0I0eDIKUDNrb3F1UnkvcUd3QzBnN1o4ZjBTQjNQZVZt\
+eQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==")
+    ("ringAccountCert" . "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZXakNDQTBLZ\
+0F3SUJBZ0lJRm1tNmZuaWRndEl3RFFZSktvWklodmNOQVFFTUJRQXdUREVRTUE0R0ExVUUKQXhNSFNt\
+RnRhU0JEUVRFNE1EWUdDZ21TSm9tVDhpeGtBUUVUS0RjNVpqSTJaVFZpWTJNeU9EWXlPREppT0dFMwp\
+PRFF6TUdOak1EWXpNakV4T1RFNFkyWm1PVGd3SGhjTk1qRXdOREUyTVRjek1qRXdXaGNOTXpFd05ERT\
+BNVGN6Ck1qRXdXakJKTVEwd0N3WURWUVFERXdSS1lXMXBNVGd3TmdZS0NaSW1pWlB5TEdRQkFSTW9aa\
+k16TkRWbU1qYzMKTldSa1ptVXdOMkUwWWpCa09UVmtZV1ZoTVRFeFpERTFabUpqTVRFNU9UQ0NBaUl3\
+RFFZSktvWklodmNOQVFFQgpCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFQY0czaUQwUk84NE5XL2ZGVllWNHB\
+LQ2MyZE1EQXFCZVN2NEpCcTQ3OHQ5ClhjeERPeEhqSnYvR0dLRnFoVyt4cWFDOUJKZG44TGthdjBBTn\
+MvV1d3YXRNSDVsYlQrZlc0T2pGOVNCZ1Ivc2UKUlNPdWE1QncrSVJlSk5VSnR3enBJS3k1S1RhMnEwd\
+0RyUXhObXBuci9UK2NEK2RQSTJveUlFd2xwRHNKT2lVMAp6eStDU2pBb0FTYkh5TWcraGQwcWJiRFBU\
+ck1uR2xKYkZubXdBQTBaRWU3aWg5Ymx0dlFPaU9TQWJVQm9IUHFpCmNtQW41RVFoNjVFa3dCb1p1U3h\
+qanlCVzZpWUJUbmc5S2c4U2pPVk9BYkJqaXZFZ3RyaFpYZ1JVdE11UWFmaDcKcjRRWDRuYjhRTUE5WX\
+lhME8xbFBJTUd5VEtiNHFIaE02VTd1RHV0NUFjcTN1NGFaZElPQ2prbUNFM3l4UU5UWAo5Vzl0b1NvV\
+1l2YVI1d1gxaFI5eVZCMFZ4V0N0djdZZklha1JiaWkxMS8xZHp3M1dXU1dMbTArTU4rY0pUdnluCk1E\
+U29GNjRpRmcycTVYd1VtWXpPRmJKQUtVd3M4QmVhcFRCVnRYaldDZmNKM01xZEZScDZvM25Lenlja0Y\
+wamoKUjdLSmNRaWhJUW1sbXM1WEtzeHV1d0VMMmFKQ0o0Qm9uSUJDaXZWa09LbllsamtDeVNObzkzam\
+lMalJ4M0ZtRwpoTnJyT3lNbjFIcEd0Z1J2eFUxOC9Ob3FFaWtpdEhCeUtYNW5XU1lUUWN4MXlZY3kxc\
+Vo5cjRqTmdQRzNuUHg0CjJEN2tPTlJPWXNVYXNBUlVTZHJqdlc5aXB2TDAvN2s1Yi9sRzh1WThPODN0\
+NUFPSTFVUG85Y05qMzNyeWJwbUgKQWdNQkFBR2pRekJCTUIwR0ExVWREZ1FXQkJUek5GOG5kZDMrQjZ\
+TdzJWMnVvUkhSWDd3Um1UQVBCZ05WSFJNQgpBZjhFQlRBREFRSC9NQThHQTFVZER3RUIvd1FGQXdNSE\
+JnQXdEUVlKS29aSWh2Y05BUUVNQlFBRGdnSUJBRDMrCjlscFluMjZyeG5pekY2UkNvZFFFVmgvOVF2R\
+Wp1V3dHUWZqa3gxb3VlVjdDMzUyWnpIT2hWU3VGcG43TUxkVFcKamI2dWhMRkpoMWtlTDlYZ0pHalMy\
+V0UwdDlJUGp2UWx5UHIwRWJoWGRJNDJMYUR0NDk0dWQ2MEE0bWg0bW1zbwovcjY2NERKOWMwUjZBOUd\
+QZUJjWi9zekhMQjJ4VmM5M3hYNjVjcmNoVTFLMDBVK2ZZUWtHS0xidFFXeUZzdnlKCmRHdFRxamVlYV\
+VNeGt5dFFWNWFxVHJ1SG4vV3U2RWRZejYrZ0ViQXJHUURRK1J3QmMxMDNGcWRIU0xReWdEWHoKd0pNV\
+GhDREdBVmR6V2NCemJYL1JIZms5bTZzK01HblNJUFBrNG9FOUdFbGdQc2JUZ2FrTzc3aElUdzRxZXJi\
+TAp2M0tiaTVOeVZZVFNVa3hDb0VRSkIrWlZJT3BVNFhRdVAvNkZkWldBRk1LU0Jkd1JLcnRmT1hZT1E\
+xcVBvYU1uCjZPR1VGMU0rYWZ0dTNCMkNhZ2ZMaE5hbVBoSjdxWTNSMzJhK2VRVllvYWlESXZKMElIUH\
+FnQ0NuWXlIaVBHTjQKS3VvaE9IZFpXTkxOUXdIQTg3SDV5NEwrSGZISmdFZFppdWYrbTNMa3JJcnN5M\
+WFoUjgyaEpZVkhreHVyZVRDcQpIR2NJaUIvTHpkSG91WFVrWGNrNjRvWXVRQnM3ZE9KVEx6bGlibU8x\
+elRzVWRoc0d4dE9zc2lYWkFjUURmZHBnCjNHdFJ3UkRwd0ZtL2k5TC9UWjMzMjBwY0VZd21aN1dBSzJ\
+Ra3Z6eWZlelRGeXdLWmh3c0RQRkwycllzLzhXdWsKRk1sek5BVmRMTytTS1RxeGlkM0l1elFaQ0FwU0\
+5ybTJMY2ZVN2grWAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBV\
+EUtLS0tLQpNSUlGWFRDQ0EwV2dBd0lCQWdJSXV0T3MxUXBsSVRvd0RRWUpLb1pJaHZjTkFRRU1CUUF3\
+VERFUU1BNEdBMVVFCkF4TUhTbUZ0YVNCRFFURTRNRFlHQ2dtU0pvbVQ4aXhrQVFFVEtEYzVaakkyWlR\
+WaVkyTXlPRFl5T0RKaU9HRTMKT0RRek1HTmpNRFl6TWpFeE9URTRZMlptT1Rnd0hoY05NakV3TkRFMk\
+1UY3pNakExV2hjTk16RXdOREUwTVRjegpNakExV2pCTU1SQXdEZ1lEVlFRREV3ZEtZVzFwSUVOQk1UZ\
+3dOZ1lLQ1pJbWlaUHlMR1FCQVJNb056bG1NalpsCk5XSmpZekk0TmpJNE1tSTRZVGM0TkRNd1kyTXdO\
+ak15TVRFNU1UaGpabVk1T0RDQ0FpSXdEUVlKS29aSWh2Y04KQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0l\
+CQUxTcGduY2tYODd3OE5vVzdRWE5DbWtHSHphVnN1NE9UbjFSSUhqVQpGR0w5MjNvVlk5RHVoZjFtVn\
+llamgremVCOCszaTR4b1E5MU04WDdTVnk4SHZxejQ4c0I2djAxMk5RTGpwN08xCkd4dmc1a0REeDRqb\
+mVyUWd6d0JzVEFuaC8xNy9SOEFDZkhINzYzUjhMRjY1clJPSStOT21KNUV2cU9rRWJJOXYKb0UySko1\
+czBVSHZZM1hTQ2pid01ROXRkM2c0SG9LNmRYSU44a2d6ejg0Yi83YkF5TnZ4a09rNnVSei9CSjhhZQo\
+zWjZudElza2tmVHhIZFpBbGtpVW5DU21Bb3VwQ01TbGRLS3RMRzNja2FhN0FDMDRxS29Rekc0WTN2Tl\
+RCOUlmCmdvYThOazlKMThoaFN2REVuR0haaURLR1dkQVA5RW5GRHZERnRFRFZyVVZSWUN5WHdyejMya\
+29Na3dYODVUMTgKc1F2TUZSdWRsa1BoOGxmbnFEWTFpZEd1R2IvSGlnU1ZqalJKZlVTbS9lZ3hycStS\
+TnpJWGFBOWNuOXAxQkdFbwpZU0YxeUdtTEgzSkJscUhVRTg4VnFkenZRdXZ3Mkhvakdvd0pkdlhmU2l\
+oZk5WOTh6VkEzQ2o3L0d3V2w0VU9OCkRUVUk2SzUrR3Y0a01iYXdxcWduaFlHbHBlZVp2bHE2UlI3OV\
+pNblRWVjJ1RWo4aERYQzdFTGN2Yk5tekJSSkoKS0gzaUxIbmNlSkxOV2NIMUV0K0pkaEFRR2xEYmk2R\
+VR0dHRuaWVaN3ZkMyt6UE00eXpkb3JEMlJXUjRyWG1acQpGRlI5eTBab2hQVTBjVmdKZHFtdUpyV01Z\
+eTVsOGt5SWdwaExkdTR1WWRpc0RPMG1ianEySE55K3pEakpyYXFHClNWTkZBZ01CQUFHalF6QkJNQjB\
+HQTFVZERnUVdCQlI1OG01YnpDaGlncmluaERETUJqSVJrWXovbURBUEJnTlYKSFJNQkFmOEVCVEFEQV\
+FIL01BOEdBMVVkRHdFQi93UUZBd01IQmdBd0RRWUpLb1pJaHZjTkFRRU1CUUFEZ2dJQgpBRWFDblluY\
+m9yWmRhWWZRUmxSb0dtSE94T1g5VFdNMXY0dWEweCtFcG11RDVWRDVlWEY1QVZkMlZadEdaeHYvCkd2\
+VnFBU2l0UTk3ampKV2p2bURWTUZtb3hQSmRYWDFkYTd5cmJYeFRmYm1mM1pac1RpdmZVdWQxYThxdUN\
+3dTUKTnBrdHFjV0JyMHRnNzFhOXlidHJOdm1GczZGRE1WMXkrY2JxYlp1UWlDWnc0WmZhekFaeTRQRG\
+NocE9SNjRCSwpBWFZIT05HcWFoV2hwcDkwd2E0TFEwUTE1U3FDR25kYVI4SHg5MHJOeEdkRjM1T1BLU\
+jd1TDdrWDU4ZGxaWStDClJJK1pKMndYMzJUZzgrc1RtTmNaUTliWDdvS0t3R3E5Uk94SjZJSUhOSnN2\
+bXhaSnlPcmE2N21hNTd3OWxiQnUKSzJlQ3cwZjRDeHdLNU1LNStkbWx6R3dhZmJlMG00TTBvVjlhWUx\
+ZTzAwR3Iza05heW9PdjRRVGtEM2pCMzVSMQpDMGJnQmk2eU1sTVJ2akZ5eEZkOFJpL003VG1jcXNObT\
+B5aklqbkZaenVtMFZTR1NLMXlRU3Flak40S0Y2R0JMCllpZ2JpM3c4WG5HYm9pZGdBUE9ncVVJeTJ1d\
+EU1MllzVXFsVHVncXhtM2xDOUhzaDM2UFJLNURDUG93eHVUNlgKcXo1M1ZiN2h6TkxLelpiRlJzbUdF\
+OFY2cWM2bXZTbUFXa25nL3QwaStXVmdGVkZuZFQrQ0oyNTJsa0ZacGljdAp6ekdETW44VUNDRUp4TDR\
+KTklTM2lLOUhlRys2MlZuay9QOEM3YVpLSXpVdjFud25rcVdUUUFYWDBKckJGdDdICjI5ZDk1RElmRT\
+RuT0FyS0JFNHc2Z1R4SU1uZzVzWi9ZbDFjcG5wUHlsR3VICi0tLS0tRU5EIENFUlRJRklDQVRFLS0tL\
+S0K")
+    ("ethKey" . "fN8cOT1lYNziaW0+pjBIgZ8r6+zMMhHsukkWBNPDsFo=")
+    ("TURN.username" . "ring")
+    ("TURN.server" . "turn.jami.net")
+    ("TURN.realm" . "ring")
+    ("TURN.password" . "ring")
+    ("TURN.enable" . "true")
+    ("TLS.verifyServer" . "true")
+    ("TLS.verifyClient" . "true")
+    ("TLS.serverName" . "")
+    ("TLS.requireClientCertificate" . "true")
+    ("TLS.privateKeyFile" . "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRd0lCQU\
+RBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1Mwd2dna3BBZ0VBQW9JQ0FRQzM5b1Z0cXNtUGdaSUgKcHpTV\
+GtlT3BlWC9CSEx2KzFTYnJPSFpVRHEwNFZCUU5BNmJmSFNSWTJpbHE1WEVheXNVSmwzQmsvM0txZEhS\
+cQpEV01wQ1dpcE1Vc2FwSGxJR0tSWHEwbXhQZ29WODZSUVBub1dCRTdhWVVEZTlJZXlxMmllZXpDK1l\
+YSnBtWTljCk5tblpaMFlHOHJGMEVpWFA0SHpVWGphZklTKzdKTTJ5ZTZyUlpINXBvdHBQNmV4NXhqVU\
+VuNEFFdWhuWGJ6U0EKNnZMaC9wYnhIUVBHMVlmNHFhcU9TY1lXV0ZWaE5ySVBCVFJNZ2RaTWRGRTh0W\
+VFyaUNkNTV4dUhrYncrQmY1VQpmVENqN25tVEcybDJNbGcrSXBHVkFXUFRWNkl0NFNiS3VadW5MZmRD\
+S2FrSU03SnA3V2dJWjZNRHdkaFFQWjJNClJPQzN0b0c1ajh4aFZOU0Q5QlZiOGZBcnRmeHdldlliQk1\
+yOEdRV2JQYlRKYk9tZHZnUGRGcXNTb1F4QnRUcWQKSG1TMG53RjJ4UGZrdm1Cd3BSWWhHNHpuZlVtMl\
+BQcDBIK1g5Sk96d0R1QWI5K0c0cWZtK3Z6bDd1N0o3anJ0TQpHQlJlSlREdGFNd2RHa0lmcUNRZGREZ\
+HFpM1hzVHVVOVNubVN4NkdxT2V6ZU04bk55dmtZWjR2Uk9vODFzU1JjCjQ4L3pZZjJ1OFBJUm4vVy9x\
+Zk9hbjVIbkcxeFNBUDZkcHAzQ1Y0YTgwUFduZyszcXQ4NW5Ha2k2Nk00NUlnRjgKNHZwRGUyM2YyZnp\
+rTk5OOTk1VTBvUStKVGg5eG1HaWJmenlCaXk5d3RUVHVYWDB3Y3gwTm9wNjdYaFFDVVBGSApybVd6Wj\
+V5bXhEdkRhcUN1U3NwcG0vTkRVRWQyc1FJREFRQUJBb0lDQUYyWXo4bzhXdERvMjZPSkx2Ym1BeTcyC\
+jRra2VsWWZTYXpyQ1AzSUZCWnpqS2xCMHl6STVZWVRUZXI4b2ZhTmtCMXdaOE5WeUlxVVhHeVBhSzls\
+MU1BcmwKU1pFRW5iQlEyVXpCbVh6VVU4MVhhUUpxeHpMc2ZqSWR5U09teG1QaFVobFFGRHpJMTYxaXM\
+4MzI0V1A3WjJXaApsU2U1RkFQdjg1TVpYREVhY1c2R0N5SUVTYVMvdkpHQ2loQ2VzL0pCSmpoejdtNT\
+VRU3liSjl0dnJxUE5KSDhJCmhDTm1BUWhEU3NPYVJXNlpBdGV6UEdUb0FQUHNHMXhLMGdwUlVDM1YyR\
+C90bGRRa0VlSjhxaW5TaUN6ZjZIc3cKTnpncjVUbTMzTm96R3Rjc2Z4ZFl0cVB1UzRPRG40bktLSFpE\
+MTBLTng2Qi9HakdQTHIra21jUUVSMUV4U2s2QwpSemtzSTRzRml6VnVLYTFNK2ZqaVNVc3RTdHV1cTJ\
+QQTJJNzFNTTZlMHJmU3ZKM3VESEJYOWY5T1RFaGxZUFBkCnB1VWM2ek1pdEJKRTg2SFo1M08wR0MzdG\
+p1M1BnSnhmWnJyMHBCU20rSzQ3ajVTUFNkaytiVGh3UDZDZDhzWGUKNmljS3YvMXI0SjVqd1pFTmRna\
+2hKeHVuMkE1WVA0c0NBU21JSFFXWmU1cGdFQ0ljUXBHZjNBVllVdVpGeXZ6cApLL3VBRTc0L3NMK0VU\
+UnhDUlhkN0ZJM2cwZFVySTA2WUFIV1FkY1JpdWpIUU12ZXB3V2RrdUdkaE9wV3VRTy9oCjc0MGgvRlZ\
+0dFdOMjc3ajhhc3ptSDA3T2lRWFIwTU1mN2FEaldMaElMYkd2YkV4TzN0TnRBYXZ6cGxmMUNSNk8KUm\
+1nNDdhVTZpR3lON1MwTE85RUJBb0lCQVFEWWMwelh2UmJHTWxkVVZnOFFTT1ZHKzV4NXVocTVyQ2xtQ\
+lp2ZApvYitWS1hkMXBONThraWkxTFBiYUJmZG9JcWVUamdOK0E0YnVpT3RGNjdCWUdISkp0WTk4ZWR5\
+RkpZREtCSzIyCmthRml2eWVuV01UWG5jaHMvUmNXMnVGS1M3cFAyNDRXYkF2clQxZGxmTTF3L1JzbTZ\
+QVjF1dndPSmNJcEdLSzgKWWgyN2hiaThUbEF6bnJCSS9TbGZwNlpxV3Y0MFRLanNNdmVGVFNWRU5yd1\
+F2MVl4TjcxbjA1UWRVUkIrT0lJSQpPenpNMWpNcm43cjNmS3RKTEx0RllEdUhJZzJDL2E0cUZORmpjM\
+296V1laQkhlcDkrWlEyOTgxdy96VTJIZ0oxCkxiajZjMy9qQU5EakI3MnRkcWMxVUkvMWhwVUh2WDJz\
+emY2czM0L3VSY3RlaFgxWEFvSUJBUURaazVaT01RdVMKNzlVQkhCeTRFbFR2cVkvUUxjMUd1aG9LVXE\
+0dzhONEtJZlpMSDk5TTcwek9JWjNoaEdaYWJDREZHN3RqSDl4Vwo3UnhXVWt5cFRDK1h5TTc1YjF0YW\
+9jRVVyZ1hnUnE4R1JFRXg3OUtwWUE3ei95TVc1OHJCTWhHTWlGZ1JTM00zCmNQSzg2dHBvaTFDN2crd\
+lUxcjJwcDEzN0habzZiUHpKTFRPNXA1OG1tTFRPQVpKd1VSZ3pzZWJnc1dsWXZlR0wKZWNBZ3lYbUtt\
+WmVSeGRNUFlCK0NmMno1TWZ5Ujk4MHRVSDJPaitGQjlJNExzRTExREphZ2wwS1lHMWxKRTBFagpodEV\
+1cTFOTG9DcVkzYXVKYjhtRUpqNlA2Ylc2SytCZTBlUi9CS1MraGxxb3VOYWEySnlhSDc4Qis2U1VZWE\
+RuCnd6MityWUhVZEI4M0FvSUJBRFlXc2ZnaloyS0Z4KzdxUm45aVIvRXlCUXNpSjNXSWdSdmVnUEdrY\
+nRTZWRSeXYKNDIwcnRRSjVSd0o2aFRXL216S3pSVW9qSlgvTU5VYld1ODEzNW05bThJRkJqb3F6TVhq\
+S0xJSzM1NlZlY1ZGUApUSGs1RTVHd3VTbGI3dnA2N0FieXJaSUswL3VzYXdHUWEySTF6YWd1aE5BenR\
+yTHVXcE9jZFdZditwQVd2WEJJCi9aKzRvd0xLU0tGL3FvVmZVYkRPQzFSaTlCbWFpcHArTndiVVdYeV\
+pHanFzMDVGejVYUTFPTUZIMUV5M3BqZmIKaFlRODRpeTZBZDQzU3dqY3lKV1lRUUtCQzBZWDRFeWVyW\
+Dd1TTkvaEUxbWRHUGlJdmNwVk8zWCt3Ly9LQndZNQorUGtTd1NKc3lTSDRqTkRsSGE2K2VuNUpSNy81\
+YWVVNENiY0lFcWNDZ2dFQkFLelMvNnhDWnZnalN5V2poK2hxCm4wN3plQW1icUJmTEVZNHJtTFBGVUF\
+ucWFqSElNbDV4SXFnRnFkd05pQ1BCQ2RLbnNaUU9KYjVpZjRUTndKa2wKckJRNzdMUFRVVlJQY2dnVU\
+p4Uzc4S0Rnckl5Vys5V1FPTEIxZEJEb3MzUDhhbFlmb3h5eHV1Wko4SFpCY3BWaQpQQkdHdTFnSDd3V\
+0lyUzBmbVhkWlJQNGp5cGRvM3hFUWNXWEZkK1dCZE9EektmcEcwZkFzZTdDSFdDWnpBdmttCkFYQklH\
+OXQxdGZHNWQvMEZTS05GbTVPb0FPT3h3L0xZNTgrL0RmZXd0U0VBcFdRZkxTL1BmSWxVdUdvQ3FwcEM\
+Kc2pOVXVNSGxxc011Z2JsY29mNHNoZityWjMzQldYOEJSNWdIb21mRE1ibDNDQWp5TXd1dHpybzVxcD\
+BBUTBWWApxOGNDZ2dFQkFLakFXVVRYY1F2TE8yYkxOZmJBTUlSVXk1T2lTZmJCbDNYRThSZnNzaUt2V\
+VBnTFcwSlV1V3FLCjdGdUFxTlJPRHhrS0pMSTdyQlo2YVNqNitFWHpUMnJwY2dFWktnSjFOUEFRdFNs\
+UjNJUkcyU3JJdjBFK3UzbkUKK1laa3pOa2Q0MUJqTkRjRm1HV21lZk5ROUJmaVIrZlZFSkZmcE5oSkl\
+mNUloSWU0RUtZUE5VUXNua0tSVTlxUApzWi9idXBXc2w4bWVFcko3bllJQ05ucHpnSHRpNXdSMlliVF\
+VXT01odmRFUldxMnhTV3BBYmtNMElhZDBUc05kCmUrYVRQVmJOMXFibFZLMm1qUTl2YS9JSkVuSE51V\
+E9TREtJeUpvcVArQkxiRTVjQU5acXQ2OFFadWdOc2RxNHkKV2FoeStydU5LS1F3Mk5MYzQzZUtsNmxv\
+bXdtRlFZOD0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=")
+    ("TLS.password" . "")
+    ("TLS.negotiationTimeoutSec" . "-1")
+    ("TLS.method" . "Automatic")
+    ("TLS.ciphers" . "")
+    ("TLS.certificateFile" . "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZHVENDQ\
+XdHZ0F3SUJBZ0lJU1pUdlZPQnh3akF3RFFZSktvWklodmNOQVFFTUJRQXdTVEVOTUFzR0ExVUUKQXhN\
+RVNtRnRhVEU0TURZR0NnbVNKb21UOGl4a0FRRVRLR1l6TXpRMVpqSTNOelZrWkdabE1EZGhOR0l3WkR\
+rMQpaR0ZsWVRFeE1XUXhOV1ppWXpFeE9Ua3dIaGNOTWpFd05ERTJNVGN6TWpFd1doY05NekV3TkRFME\
+1UY3pNakV3CldqQlFNUlF3RWdZRFZRUURFd3RLWVcxcElHUmxkbWxqWlRFNE1EWUdDZ21TSm9tVDhpe\
+GtBUUVUS0dFM09XTmwKTURVNE16VmhPRFV5WlRsbU5qWmxOelF3TURjeU5EUXdPVFZpTVdaa1kyVXdO\
+bVV3Z2dJaU1BMEdDU3FHU0liMwpEUUVCQVFVQUE0SUNEd0F3Z2dJS0FvSUNBUUMzOW9WdHFzbVBnWkl\
+IcHpTVGtlT3BlWC9CSEx2KzFTYnJPSFpVCkRxMDRWQlFOQTZiZkhTUlkyaWxxNVhFYXlzVUpsM0JrLz\
+NLcWRIUnFEV01wQ1dpcE1Vc2FwSGxJR0tSWHEwbXgKUGdvVjg2UlFQbm9XQkU3YVlVRGU5SWV5cTJpZ\
+WV6QytZWEpwbVk5Y05tblpaMFlHOHJGMEVpWFA0SHpVWGphZgpJUys3Sk0yeWU2clJaSDVwb3RwUDZl\
+eDV4alVFbjRBRXVoblhielNBNnZMaC9wYnhIUVBHMVlmNHFhcU9TY1lXCldGVmhOcklQQlRSTWdkWk1\
+kRkU4dFlRcmlDZDU1eHVIa2J3K0JmNVVmVENqN25tVEcybDJNbGcrSXBHVkFXUFQKVjZJdDRTYkt1Wn\
+VuTGZkQ0tha0lNN0pwN1dnSVo2TUR3ZGhRUFoyTVJPQzN0b0c1ajh4aFZOU0Q5QlZiOGZBcgp0Znh3Z\
+XZZYkJNcjhHUVdiUGJUSmJPbWR2Z1BkRnFzU29ReEJ0VHFkSG1TMG53RjJ4UGZrdm1Cd3BSWWhHNHpu\
+CmZVbTJQUHAwSCtYOUpPendEdUFiOStHNHFmbSt2emw3dTdKN2pydE1HQlJlSlREdGFNd2RHa0lmcUN\
+RZGREZHEKaTNYc1R1VTlTbm1TeDZHcU9lemVNOG5OeXZrWVo0dlJPbzgxc1NSYzQ4L3pZZjJ1OFBJUm\
+4vVy9xZk9hbjVIbgpHMXhTQVA2ZHBwM0NWNGE4MFBXbmcrM3F0ODVuR2tpNjZNNDVJZ0Y4NHZwRGUyM\
+2YyZnprTk5OOTk1VTBvUStKClRoOXhtR2liZnp5Qml5OXd0VFR1WFgwd2N4ME5vcDY3WGhRQ1VQRkhy\
+bVd6WjV5bXhEdkRhcUN1U3NwcG0vTkQKVUVkMnNRSURBUUFCTUEwR0NTcUdTSWIzRFFFQkRBVUFBNEl\
+DQVFCZmRCc0p4RVVWeFRBeG5vNll1bEFoR1ZvUAplWG8xMHIwOE5DcDZRZlJxeGJlTkZ5aXEvKzEwSE\
+NpL1RoZk41OVdLNlpudFF4NlFNZUxEUVZTb0NjNzlaaWQ4ClE2RUdsWkp1c2RTTmg0VjVteXRCQVZHZ\
+2J2aXJFWU1Wcm5jWWg3bHR2LzVuSGRsbyt2WXV3Vzh0aEhHTk1TUkIKQmhJN2xydmpqY3NkbVl6L1Bp\
+NFNZdkg3c3RaVWpRYmUzNzh3UE90b3lBMTNybEtQUTF4QjJFbUxISDYya21WTQowa25wL1hQQ2o2alR\
+zakpFWko1NzZNMmloYy9DTzkvREVlWWZ4RFlJb004NEF5dTc0UU9UUVN1cnVHRjF5T1RKCmlxRkltTH\
+hMcWRxNk1Zdmx1QjFmTzlKaU9QaS84UEJFTTVpeGYzajlGOFVMQTZUTU1NSklFR1ROeUNzOWpzQloKR\
+UNiWVgweTY4WWtGYnYzQmNWaGk2K1VVWVhBVUd0akhwQ3Z2VThYaHowL0RVZFVaTkpqejRJeWl1ZE5N\
+dnFjQgppSEJDdkxFS1B4VFd1ZlFZaGM5ZkVXTm1valc1WSsvalBVeDcrQWxPM1RXZm1OclBpWDJuSEd\
+5UG5tYjZIR2hWCmp3UU0wT1h4cGxnZ2dQMWpVZ0VYWWRucStjYzdRMy94RFIvdi9Fd244T1d4OVB3eV\
+Y0WFZwZXY5QnpmeWwyQTMKdFhWVkFKMzJtcTlPQmVadXczdWlWU2E2TWdCVG80eXcrYnBjU2VIU2xXO\
+XNRc1NYY3dUNjhvOUdhR2hMNjdXMApwQWx4R2VxSWRtbTZ0MkxIUCtQQnBtL3BweTMvTmh1NFIwMTNv\
+Znp5V2Y0bEcrVnpWVGE0eXVqU1J3UlluYmZyCjlSWER0L0I5VEg5TnNsamdBQT09Ci0tLS0tRU5EIEN\
+FUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZXakNDQTBLZ0F3SU\
+JBZ0lJRm1tNmZuaWRndEl3RFFZSktvWklodmNOQVFFTUJRQXdUREVRTUE0R0ExVUUKQXhNSFNtRnRhU\
+0JEUVRFNE1EWUdDZ21TSm9tVDhpeGtBUUVUS0RjNVpqSTJaVFZpWTJNeU9EWXlPREppT0dFMwpPRFF6\
+TUdOak1EWXpNakV4T1RFNFkyWm1PVGd3SGhjTk1qRXdOREUyTVRjek1qRXdXaGNOTXpFd05ERTBNVGN\
+6Ck1qRXdXakJKTVEwd0N3WURWUVFERXdSS1lXMXBNVGd3TmdZS0NaSW1pWlB5TEdRQkFSTW9aak16Tk\
+RWbU1qYzMKTldSa1ptVXdOMkUwWWpCa09UVmtZV1ZoTVRFeFpERTFabUpqTVRFNU9UQ0NBaUl3RFFZS\
+ktvWklodmNOQVFFQgpCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFQY0czaUQwUk84NE5XL2ZGVllWNHBLQ2My\
+ZE1EQXFCZVN2NEpCcTQ3OHQ5ClhjeERPeEhqSnYvR0dLRnFoVyt4cWFDOUJKZG44TGthdjBBTnMvV1d\
+3YXRNSDVsYlQrZlc0T2pGOVNCZ1Ivc2UKUlNPdWE1QncrSVJlSk5VSnR3enBJS3k1S1RhMnEwd0RyUX\
+hObXBuci9UK2NEK2RQSTJveUlFd2xwRHNKT2lVMAp6eStDU2pBb0FTYkh5TWcraGQwcWJiRFBUck1uR\
+2xKYkZubXdBQTBaRWU3aWg5Ymx0dlFPaU9TQWJVQm9IUHFpCmNtQW41RVFoNjVFa3dCb1p1U3hqanlC\
+VzZpWUJUbmc5S2c4U2pPVk9BYkJqaXZFZ3RyaFpYZ1JVdE11UWFmaDcKcjRRWDRuYjhRTUE5WXlhME8\
+xbFBJTUd5VEtiNHFIaE02VTd1RHV0NUFjcTN1NGFaZElPQ2prbUNFM3l4UU5UWAo5Vzl0b1NvV1l2YV\
+I1d1gxaFI5eVZCMFZ4V0N0djdZZklha1JiaWkxMS8xZHp3M1dXU1dMbTArTU4rY0pUdnluCk1EU29GN\
+jRpRmcycTVYd1VtWXpPRmJKQUtVd3M4QmVhcFRCVnRYaldDZmNKM01xZEZScDZvM25Lenlja0YwamoK\
+UjdLSmNRaWhJUW1sbXM1WEtzeHV1d0VMMmFKQ0o0Qm9uSUJDaXZWa09LbllsamtDeVNObzkzamlMalJ\
+4M0ZtRwpoTnJyT3lNbjFIcEd0Z1J2eFUxOC9Ob3FFaWtpdEhCeUtYNW5XU1lUUWN4MXlZY3kxcVo5cj\
+RqTmdQRzNuUHg0CjJEN2tPTlJPWXNVYXNBUlVTZHJqdlc5aXB2TDAvN2s1Yi9sRzh1WThPODN0NUFPS\
+TFVUG85Y05qMzNyeWJwbUgKQWdNQkFBR2pRekJCTUIwR0ExVWREZ1FXQkJUek5GOG5kZDMrQjZTdzJW\
+MnVvUkhSWDd3Um1UQVBCZ05WSFJNQgpBZjhFQlRBREFRSC9NQThHQTFVZER3RUIvd1FGQXdNSEJnQXd\
+EUVlKS29aSWh2Y05BUUVNQlFBRGdnSUJBRDMrCjlscFluMjZyeG5pekY2UkNvZFFFVmgvOVF2RWp1V3\
+dHUWZqa3gxb3VlVjdDMzUyWnpIT2hWU3VGcG43TUxkVFcKamI2dWhMRkpoMWtlTDlYZ0pHalMyV0Uwd\
+DlJUGp2UWx5UHIwRWJoWGRJNDJMYUR0NDk0dWQ2MEE0bWg0bW1zbwovcjY2NERKOWMwUjZBOUdQZUJj\
+Wi9zekhMQjJ4VmM5M3hYNjVjcmNoVTFLMDBVK2ZZUWtHS0xidFFXeUZzdnlKCmRHdFRxamVlYVVNeGt\
+5dFFWNWFxVHJ1SG4vV3U2RWRZejYrZ0ViQXJHUURRK1J3QmMxMDNGcWRIU0xReWdEWHoKd0pNVGhDRE\
+dBVmR6V2NCemJYL1JIZms5bTZzK01HblNJUFBrNG9FOUdFbGdQc2JUZ2FrTzc3aElUdzRxZXJiTAp2M\
+0tiaTVOeVZZVFNVa3hDb0VRSkIrWlZJT3BVNFhRdVAvNkZkWldBRk1LU0Jkd1JLcnRmT1hZT1ExcVBv\
+YU1uCjZPR1VGMU0rYWZ0dTNCMkNhZ2ZMaE5hbVBoSjdxWTNSMzJhK2VRVllvYWlESXZKMElIUHFnQ0N\
+uWXlIaVBHTjQKS3VvaE9IZFpXTkxOUXdIQTg3SDV5NEwrSGZISmdFZFppdWYrbTNMa3JJcnN5MWFoUj\
+gyaEpZVkhreHVyZVRDcQpIR2NJaUIvTHpkSG91WFVrWGNrNjRvWXVRQnM3ZE9KVEx6bGlibU8xelRzV\
+WRoc0d4dE9zc2lYWkFjUURmZHBnCjNHdFJ3UkRwd0ZtL2k5TC9UWjMzMjBwY0VZd21aN1dBSzJRa3Z6\
+eWZlelRGeXdLWmh3c0RQRkwycllzLzhXdWsKRk1sek5BVmRMTytTS1RxeGlkM0l1elFaQ0FwU05ybTJ\
+MY2ZVN2grWAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gQ0VSVElGSUNBVEUtLS\
+0tLQpNSUlGWFRDQ0EwV2dBd0lCQWdJSXV0T3MxUXBsSVRvd0RRWUpLb1pJaHZjTkFRRU1CUUF3VERFU\
+U1BNEdBMVVFCkF4TUhTbUZ0YVNCRFFURTRNRFlHQ2dtU0pvbVQ4aXhrQVFFVEtEYzVaakkyWlRWaVky\
+TXlPRFl5T0RKaU9HRTMKT0RRek1HTmpNRFl6TWpFeE9URTRZMlptT1Rnd0hoY05NakV3TkRFMk1UY3p\
+NakExV2hjTk16RXdOREUwTVRjegpNakExV2pCTU1SQXdEZ1lEVlFRREV3ZEtZVzFwSUVOQk1UZ3dOZ1\
+lLQ1pJbWlaUHlMR1FCQVJNb056bG1NalpsCk5XSmpZekk0TmpJNE1tSTRZVGM0TkRNd1kyTXdOak15T\
+VRFNU1UaGpabVk1T0RDQ0FpSXdEUVlKS29aSWh2Y04KQVFFQkJRQURnZ0lQQURDQ0Fnb0NnZ0lCQUxT\
+cGduY2tYODd3OE5vVzdRWE5DbWtHSHphVnN1NE9UbjFSSUhqVQpGR0w5MjNvVlk5RHVoZjFtVnllamg\
+remVCOCszaTR4b1E5MU04WDdTVnk4SHZxejQ4c0I2djAxMk5RTGpwN08xCkd4dmc1a0REeDRqbmVyUW\
+d6d0JzVEFuaC8xNy9SOEFDZkhINzYzUjhMRjY1clJPSStOT21KNUV2cU9rRWJJOXYKb0UySko1czBVS\
+HZZM1hTQ2pid01ROXRkM2c0SG9LNmRYSU44a2d6ejg0Yi83YkF5TnZ4a09rNnVSei9CSjhhZQozWjZu\
+dElza2tmVHhIZFpBbGtpVW5DU21Bb3VwQ01TbGRLS3RMRzNja2FhN0FDMDRxS29Rekc0WTN2TlRCOUl\
+mCmdvYThOazlKMThoaFN2REVuR0haaURLR1dkQVA5RW5GRHZERnRFRFZyVVZSWUN5WHdyejMya29Na3\
+dYODVUMTgKc1F2TUZSdWRsa1BoOGxmbnFEWTFpZEd1R2IvSGlnU1ZqalJKZlVTbS9lZ3hycStSTnpJW\
+GFBOWNuOXAxQkdFbwpZU0YxeUdtTEgzSkJscUhVRTg4VnFkenZRdXZ3Mkhvakdvd0pkdlhmU2loZk5W\
+OTh6VkEzQ2o3L0d3V2w0VU9OCkRUVUk2SzUrR3Y0a01iYXdxcWduaFlHbHBlZVp2bHE2UlI3OVpNblR\
+WVjJ1RWo4aERYQzdFTGN2Yk5tekJSSkoKS0gzaUxIbmNlSkxOV2NIMUV0K0pkaEFRR2xEYmk2RVR0dH\
+RuaWVaN3ZkMyt6UE00eXpkb3JEMlJXUjRyWG1acQpGRlI5eTBab2hQVTBjVmdKZHFtdUpyV01ZeTVsO\
+Gt5SWdwaExkdTR1WWRpc0RPMG1ianEySE55K3pEakpyYXFHClNWTkZBZ01CQUFHalF6QkJNQjBHQTFV\
+ZERnUVdCQlI1OG01YnpDaGlncmluaERETUJqSVJrWXovbURBUEJnTlYKSFJNQkFmOEVCVEFEQVFIL01\
+BOEdBMVVkRHdFQi93UUZBd01IQmdBd0RRWUpLb1pJaHZjTkFRRU1CUUFEZ2dJQgpBRWFDblluYm9yWm\
+RhWWZRUmxSb0dtSE94T1g5VFdNMXY0dWEweCtFcG11RDVWRDVlWEY1QVZkMlZadEdaeHYvCkd2VnFBU\
+2l0UTk3ampKV2p2bURWTUZtb3hQSmRYWDFkYTd5cmJYeFRmYm1mM1pac1RpdmZVdWQxYThxdUN3dTUK\
+TnBrdHFjV0JyMHRnNzFhOXlidHJOdm1GczZGRE1WMXkrY2JxYlp1UWlDWnc0WmZhekFaeTRQRGNocE9\
+SNjRCSwpBWFZIT05HcWFoV2hwcDkwd2E0TFEwUTE1U3FDR25kYVI4SHg5MHJOeEdkRjM1T1BLUjd1TD\
+drWDU4ZGxaWStDClJJK1pKMndYMzJUZzgrc1RtTmNaUTliWDdvS0t3R3E5Uk94SjZJSUhOSnN2bXhaS\
+nlPcmE2N21hNTd3OWxiQnUKSzJlQ3cwZjRDeHdLNU1LNStkbWx6R3dhZmJlMG00TTBvVjlhWUxZTzAw\
+R3Iza05heW9PdjRRVGtEM2pCMzVSMQpDMGJnQmk2eU1sTVJ2akZ5eEZkOFJpL003VG1jcXNObTB5akl\
+qbkZaenVtMFZTR1NLMXlRU3Flak40S0Y2R0JMCllpZ2JpM3c4WG5HYm9pZGdBUE9ncVVJeTJ1dEU1Ml\
+lzVXFsVHVncXhtM2xDOUhzaDM2UFJLNURDUG93eHVUNlgKcXo1M1ZiN2h6TkxLelpiRlJzbUdFOFY2c\
+WM2bXZTbUFXa25nL3QwaStXVmdGVkZuZFQrQ0oyNTJsa0ZacGljdAp6ekdETW44VUNDRUp4TDRKTklT\
+M2lLOUhlRys2MlZuay9QOEM3YVpLSXpVdjFud25rcVdUUUFYWDBKckJGdDdICjI5ZDk1RElmRTRuT0F\
+yS0JFNHc2Z1R4SU1uZzVzWi9ZbDFjcG5wUHlsR3VICi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K")
+    ("STUN.server" . "")
+    ("STUN.enable" . "false")
+    ("SRTP.rtpFallback" . "false")
+    ("SRTP.keyExchange" . "sdes")
+    ("SRTP.enable" . "true")
+    ("RingNS.uri" . "")
+    ("RingNS.account" . "0790738ce15fa05933b49dd77034312787da86c3")
+    ("DHT.PublicInCalls" . "true")
+    ("Account.videoPortMin" . "49152")
+    ("Account.videoPortMax" . "65534")
+    ("Account.videoEnabled" . "true")
+    ("Account.username" . "f3345f2775ddfe07a4b0d95daea111d15fbc1199")
+    ("Account.useragent" . "")
+    ("Account.upnpEnabled" . "true")
+    ("Account.type" . "RING")
+    ("Account.ringtoneEnabled" . "true")
+    ("Account.rendezVous" . "true")
+    ("Account.publishedSameAsLocal" . "true")
+    ("Account.publishedPort" . "5060")
+    ("Account.publishedAddress" . "")
+    ("Account.presenceSubscribeSupported" . "true")
+    ("Account.peerDiscovery" . "false")
+    ("Account.managerUsername" . "")
+    ("Account.managerUri" . "")
+    ("Account.mailbox" . "")
+    ("Account.localModeratorsEnabled" . "true")
+    ("Account.localInterface" . "default")
+    ("Account.hostname" . "bootstrap.jami.net")
+    ("Account.hasCustomUserAgent" . "false")
+    ("Account.enable" . "true")
+    ("Account.dtmfType" . "overrtp")
+    ("Account.displayName" . "dummy")
+    ("Account.defaultModerators" . "")
+    ("Account.audioPortMin" . "16384")
+    ("Account.audioPortMax" . "32766")
+    ("Account.archiveHasPassword" . "false")
+    ("Account.allowCertFromTrusted" . "true")
+    ("Account.allowCertFromHistory" . "true")
+    ("Account.allowCertFromContact" . "true")
+    ("Account.allModeratorEnabled" . "true")
+    ("Account.alias" . "dummy")
+    ("Account.activeCallLimit" . "-1")
+    ("Account.accountPublish" . "false")
+    ("Account.accountDiscovery" . "false")))
diff --git a/gnu/tests/telephony.scm b/gnu/tests/telephony.scm
new file mode 100644
index 0000000000..cdcd00d057
--- /dev/null
+++ b/gnu/tests/telephony.scm
@@ -0,0 +1,202 @@ 
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gnu.org>.
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu tests telephony)
+  #:use-module (gnu)
+  #:use-module (gnu packages guile)
+  #:use-module (gnu tests)
+  #:use-module (gnu system vm)
+  #:use-module (gnu services)
+  #:use-module (gnu services dbus)
+  #:use-module (gnu services ssh)
+  #:use-module (gnu services telephony)
+  #:use-module (guix gexp)
+  #:use-module (guix modules)
+  #:export (%test-jami-daemon
+            %test-jami-daemon-provisioning))
+
+;;;
+;;; Jami daemon.
+;;;
+
+(define define-with-retries (@@ (gnu services telephony) define-with-retries))
+
+(include "data/jami-dummy-account.dat") ;defines %jami-account-content-sexp
+
+(define %dummy-jami-account
+  ;; A Jami account archive is a gzipped JSON file.
+  (computed-file
+   "dummy-jami-account.gz"
+   (with-extensions (list guile-json-4 guile-zlib)
+     #~(begin
+         (use-modules (json) (zlib))
+         (let ((port (open-output-file #$output)))
+           (call-with-gzip-output-port port
+             (lambda (port)
+               (scm->json '#$%jami-account-content-sexp port))))))))
+
+(define* (make-jami-daemon-os #:key provisioning?)
+  (simple-operating-system
+   (service jami-daemon-service-type
+            (if provisioning?
+                (jami-daemon-configuration
+                 (account-archives (list %dummy-jami-account)))
+                (jami-daemon-configuration)))
+   (service dbus-root-service-type)
+   (service openssh-service-type        ;for debugging
+            (openssh-configuration
+             (permit-root-login 'without-password)))))
+
+(define %jami-daemon-os
+  (make-jami-daemon-os))
+
+(define %jami-daemon-os-provisioning
+  (make-jami-daemon-os #:provisioning? #t))
+
+(define* (run-jami-daemon-test #:key provisioning?)
+  "Run tests in %JAMI-DAEMON-OS.  When PROVISIONING? is true, test the
+accounts provisioning feature of the service."
+  (define os (marionette-operating-system
+              (if provisioning?
+                  %jami-daemon-os-provisioning
+                  %jami-daemon-os)
+              #:imported-modules '((gnu services herd)
+                                   (guix combinators))))
+  (define vm (virtual-machine
+              (operating-system os)
+              (memory-size 512)))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (rnrs base)
+                       (srfi srfi-11)
+                       (srfi srfi-64)
+                       (gnu build marionette))
+
+          (define marionette
+            (make-marionette (list #$vm)))
+
+          (mkdir #$output)
+          (chdir #$output)
+
+          (test-begin "jami-daemon")
+
+          (test-assert "service running"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd))
+                (match (start-service 'jami-daemon)
+                  (#f #f)
+                  (('service response-parts ...)
+                   (match (assq-ref response-parts 'running)
+                     ((pid) (number? pid))))))
+             marionette))
+
+          (test-assert "service can be stopped"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd)
+                             (rnrs base))
+                #$define-with-retries
+                (setenv "PATH" "/run/current-system/profile/bin")
+                (let ((pid (match (start-service 'jami-daemon)
+                             (#f #f)
+                             (('service response-parts ...)
+                              (match (assq-ref response-parts 'running)
+                                ((pid) pid))))))
+
+                  (assert (number? pid))
+
+                  (match (stop-service 'jami-daemon)
+                    (services           ;a list of service symbols
+                     (member 'jami-daemon services)))
+                  ;; Sometimes, the process still appear in pgrep, even
+                  ;; though we are using waitpid after sending it SIGTERM
+                  ;; in the service; use retries.
+                  (with-retries
+                   19 1
+                   (not (zero? (status:exit-val
+                                (system* "pgrep" "dring")))))))
+             marionette))
+
+          (test-assert "service can be restarted"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd)
+                             (rnrs base))
+                ;; Start and retrieve the current PID.
+                (define pid (match (start-service 'jami-daemon)
+                              (#f #f)
+                              (('service response-parts ...)
+                               (match (assq-ref response-parts 'running)
+                                 ((pid) pid)))))
+                (assert (number? pid))
+
+                ;; Restart the service.
+                (restart-service 'jami-daemon)
+
+                (define new-pid (match (start-service 'jami-daemon)
+                                  (#f #f)
+                                  (('service response-parts ...)
+                                   (match (assq-ref response-parts 'running)
+                                     ((pid) pid)))))
+                (assert (number? new-pid))
+
+                (not (eq? pid new-pid)))
+             marionette))
+
+          (unless #$provisioning? (test-skip 1))
+          (test-assert "jami accounts provisioning"
+            (marionette-eval
+             '(begin
+                (use-modules (gnu services herd))
+
+                #$define-with-retries
+
+                (setenv "PATH" "/run/current-system/profile/bin")
+                ;; The imported account takes some time to appear in the
+                ;; account files; retry for up to about 10 s.
+                (with-retries
+                 20 1
+                 (zero? (status:exit-val
+                         (system* "grep" "-r"
+                                  #$(assoc-ref %jami-account-content-sexp
+                                               "Account.username")
+                                  "/var/lib/jami/.local/share/jami/")))))
+             marionette))
+
+          (test-end)
+          (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
+
+  (gexp->derivation (if provisioning?
+                        "jami-daemon-provisioning-test"
+                        "jami-daemon-test")
+                    test))
+
+(define %test-jami-daemon
+  (system-test
+   (name "jami-daemon")
+   (description "Basic tests for the jami-daemon service.")
+   (value (run-jami-daemon-test))))
+
+(define %test-jami-daemon-provisioning
+  (system-test
+   (name "jami-daemon-provisioning")
+   (description "Provisioning test for the jami-daemon service.")
+   (value (run-jami-daemon-test #:provisioning? #t))))