diff --git a/.gitignore b/.gitignore
index 0e09cdbccde7987d66a0bf8c3d6ba63aed2b3865..77f105a03c53c3e9a99764a7d3f46681bb38a1fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 .idea
 *.apk
-*.jar
+build/
 briar/
+briar-mailbox/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 71895e22d568c6d2bceb9ae5490dcb5225df9889..972e139021ca11347ad4b6256387ae119ef956ee 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,13 +25,17 @@ build:
   except:
     - triggers
 
-test_success:
+test_briar_success:
   stage: test
   script:
-  # Consider adding the cap and the device directly to the CI config
-  # https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-docker-section
-    # TODO replace alpha-1.4.3 with release version
-    - docker run --cap-add SYS_ADMIN --device /dev/fuse ${TEST_IMAGE} ./reproduce.py alpha-1.4.3
+    - docker run --cap-add SYS_ADMIN --device /dev/fuse ${TEST_IMAGE} ./reproduce.py briar release-1.4.20
+  except:
+    - triggers
+
+test_mailbox_success:
+  stage: test
+  script:
+    - docker run --cap-add SYS_ADMIN --device /dev/fuse ${TEST_IMAGE} ./reproduce.py briar-mailbox alpha-1.0.2
   except:
     - triggers
 
@@ -57,6 +61,8 @@ release:
 check:
   stage: check
   script:
+    # TODO update RELEASE_TAG in briar and mailbox repo to include app name prefix: "briar $tag"
+    # https://code.briarproject.org/briar/briar/-/blob/4253bbaaf5b63e90d74a54df3c15cf6c53e555d4/.gitlab-ci.yml#L86
     - docker run --cap-add SYS_ADMIN --device /dev/fuse ${RELEASE_IMAGE} ./reproduce.py ${RELEASE_TAG}
     - docker tag ${RELEASE_IMAGE} ${TAG_IMAGE}
     - docker push ${TAG_IMAGE}
diff --git a/Dockerfile b/Dockerfile
index dc55722edb97869e014ff73bb0a6c19051f2c9a9..53ca65fbb8abd5806574fb73fef9bc14dece255b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,13 +3,13 @@ FROM debian:bullseye-slim
 ENV LANG=C.UTF-8
 ENV DEBIAN_FRONTEND=noninteractive
 ENV ANDROID_HOME=/opt/android-sdk
-ENV REPO_URL=https://code.briarproject.org/briar/briar.git
 
 WORKDIR /opt/briar-reproducer
 
 # add required files to WORKDIR
 ADD install*.sh ./
 ADD *.py ./
+ADD apps.json ./
 
 RUN ./install.sh
 
diff --git a/README.md b/README.md
index 528dfb1a3b24bdd69f3acf31c1ee92c10feebb96..2913dad0f68d1dae0b6265cb2125ade6374f4653 100644
--- a/README.md
+++ b/README.md
@@ -103,3 +103,5 @@ If the build fails with `fuse: mount failed: Permission denied`, you may need to
   which broke reproducibility.
   An [old version of briar-reproducer](https://code.briarproject.org/briar/briar-reproducer/tags/pre-1.1.7)
   can be used to verify these releases.
+* Before version `1.1.0`, the path to the APK (`gradle_apk_path` in `apps.json`) was
+  `briar-android/build/outputs/apk/release/briar-android-release-unsigned.apk`
diff --git a/apps.json b/apps.json
new file mode 100644
index 0000000000000000000000000000000000000000..24cc27872b67b1c99e5b4065fbf87d5f2d0145cc
--- /dev/null
+++ b/apps.json
@@ -0,0 +1,18 @@
+{
+  "briar": {
+    "repo_url": "https://code.briarproject.org/briar/briar.git",
+    "repo_default_branch": "master",
+    "reference_url": "https://briarproject.org/apk/briar-%s.apk",
+    "reference_apk": "briar-%s.apk",
+    "gradle_task": "briar-android:assembleRelease",
+    "gradle_apk_path": "briar-android/build/outputs/apk/official/release/briar-android-official-release-unsigned.apk"
+  },
+  "briar-mailbox": {
+    "repo_url": "https://code.briarproject.org/briar/briar-mailbox.git",
+    "repo_default_branch": "main",
+    "reference_url": "https://briarproject.org/apk/mailbox-%s.apk",
+    "reference_apk": "mailbox-%s.apk",
+    "gradle_task": "mailbox-android:assembleRelease",
+    "gradle_apk_path": "mailbox-android/build/outputs/apk/release/mailbox-android-release-unsigned.apk"
+  }
+}
diff --git a/install.sh b/install.sh
index 5097b15d68643adbad8da6f6ead3f6755139d98e..bde1779f9d2c594f2da37978ff951da52d666d85 100755
--- a/install.sh
+++ b/install.sh
@@ -3,7 +3,7 @@ set -e
 set -x
 
 # use snapshot repos for deterministic package versions
-DATE="20211214T000000Z"
+DATE="20220208T000000Z"
 cat << EOF > /etc/apt/sources.list
 deb http://snapshot.debian.org/archive/debian/${DATE}/ bullseye main
 deb http://snapshot.debian.org/archive/debian-security/${DATE}/ bullseye-security main
diff --git a/reproduce.py b/reproduce.py
index 7b1cb753bb0a4d508b8cdfd4cad5b078b996ea67..43da5145a9a4fa67a92335c94344478c16f2852f 100755
--- a/reproduce.py
+++ b/reproduce.py
@@ -5,135 +5,74 @@ import os
 import sys
 from subprocess import call, check_call, check_output
 from verify_apk import verify_apk
-from verify_jar import verify_jar
-from utils import fail
-
-REPO_DIR = "briar"
-REFERENCE_APK_URL = 'https://briarproject.org/apk/briar-%s.apk'
-REFERENCE_JAR_URL = 'https://briarproject.org/jar/briar-headless-%s.jar'
-GRADLE_APK_TASK = "briar-android:assembleRelease"
-GRADLE_JAR_TASK = "briar-headless:%s"
-APK_PATH = "briar-android/build/outputs/apk/official/release/" + \
-           "briar-android-official-release-unsigned.apk"
-OLD_APK_PATH = "briar-android/build/outputs/apk/release/briar-android-release-unsigned.apk"
-JAR_PATH= "briar-headless/build/libs/briar-headless-%s.jar"
-BUILD_DIR = "briar-build"
-
-JAR_FLAVOURS = [
-                   {
-                       "task": "x86LinuxJar",
-                       "file": "linux-x86_64"
-                   },
-                   {
-                       "task": "aarch64LinuxJar",
-                       "file": "linux-aarch64"
-                   },
-                   {
-                       "task": "armhfLinuxJar",
-                       "file": "linux-armhf"
-                   }
-               ]
+from utils import get_app_info, fail
+
+BUILD_DIR = "build"
 
 
 def main():
-    if len(sys.argv) > 2:
-        fail("Usage: %s [tag]" % sys.argv[0])
-    tag = sys.argv[1] if len(sys.argv) > 1 else None
+    if len(sys.argv) > 3:
+        fail("Usage: %s briar [tag]" % sys.argv[0])
+    app_name = sys.argv[1]
+    tag = sys.argv[2] if len(sys.argv) > 2 else None
+
+    # get app info
+    app = get_app_info(app_name)
 
     # clone/reset repo and checkout tag
-    tag = prepare_repo(tag)
+    tag = prepare_repo(app["repo_url"], app["repo_default_branch"], app_name, tag)
+    version = tag.split('-')[1]
 
     # download reference binaries (before building to detect missing file early on)
-    version = tag.split('-')[1]
-    apk_url = REFERENCE_APK_URL % version
-    reference_apk = "briar-%s.apk" % version
+    apk_url = app["reference_url"] % version
+    reference_apk = app["reference_apk"] % version
     check_call(['wget', '--no-verbose', apk_url, '-O', reference_apk])
 
-    reference_jars = list()
-    if version >= "1.2.17":
-        for flavour in JAR_FLAVOURS:
-            suffix = flavour['file'] + "-" + version
-            jar_url = REFERENCE_JAR_URL % suffix
-            reference_jar = "briar-headless-%s.jar" % suffix
-            reference_jars.append(reference_jar)
-            if version >= "1.2.10":
-                check_call(['wget', '--no-verbose', jar_url, '-O', reference_jar])
-    else:
-        jar_url = REFERENCE_JAR_URL % version
-        reference_jar = "briar-headless-%s.jar" % version
-        reference_jars.append(reference_jar)
-        if version >= "1.2.10":
-            check_call(['wget', '--no-verbose', jar_url, '-O', reference_jar])
-
     # use non-deterministic file system for building the app to detect issues
     if not os.path.exists(BUILD_DIR):
         os.makedirs(BUILD_DIR)
-    check_call(['disorderfs', '--shuffle-dirents=yes', REPO_DIR, BUILD_DIR])
+    check_call(['disorderfs', '--shuffle-dirents=yes', app_name, BUILD_DIR])
     atexit.register(unmount)
 
     # build the app
-    if version >= "1.2.17":
-        tasks = list()
-        for flavour in JAR_FLAVOURS:
-            tasks.append(GRADLE_JAR_TASK % flavour["task"])
-        check_call(["./gradlew", "--no-daemon", GRADLE_APK_TASK, *tasks], cwd=BUILD_DIR)
-    else:
-        gradle_jar_task = "briar-headless:assemble"
-        check_call(["./gradlew", "--no-daemon", GRADLE_APK_TASK, gradle_jar_task], cwd=BUILD_DIR)
+    check_call(["./gradlew", "--no-daemon", app["gradle_task"]], cwd=BUILD_DIR)
 
     # check if both APKs match
-    apk = os.path.join(BUILD_DIR, APK_PATH if version >= "1.1.0" else OLD_APK_PATH)
+    apk = os.path.join(BUILD_DIR, app["gradle_apk_path"])
     if not verify_apk(reference_apk, apk):
         sys.exit(1)
 
-    # check if jars match pairwise
-    if version >= "1.2.10":
-        if version >= "1.2.17":
-            for i in range(len(JAR_FLAVOURS)):
-                flavour = JAR_FLAVOURS[i]
-                jar = os.path.join(BUILD_DIR, JAR_PATH % flavour["file"])
-                if not verify_jar(reference_jars[i], jar):
-                    sys.exit(1)
-        else:
-            jar = os.path.join(BUILD_DIR, "briar-headless/build/libs/briar-headless.jar")
-            if not verify_jar(reference_jars[0], jar):
-                sys.exit(1)
-
     print("Version '%s' was built reproducible! :)" % tag)
 
 
-def prepare_repo(tag):
-    if os.path.isdir(REPO_DIR):
+def prepare_repo(repo_url, repo_branch, repo_dir, tag):
+    if os.path.isdir(repo_dir):
         # get latest commits and tags from remote
-        repo_call(['git', 'fetch', 'origin'])
-        # checkout to latest master HEAD
-        repo_call(['git', 'checkout', '-f', 'master'])
+        check_call(['git', 'fetch', 'origin'], cwd=repo_dir)
+        # checkout to latest main HEAD
+        check_call(['git', 'checkout', '-f', repo_branch], cwd=repo_dir)
     else:
         # clone repo
-        check_call(['git', 'clone', os.environ.get("REPO_URL"), REPO_DIR])
+        check_call(['git', 'clone', repo_url, repo_dir])
 
     # undo all changes
-    repo_call(['git', 'reset', '--hard'])
+    check_call(['git', 'reset', '--hard'], cwd=repo_dir)
 
     # clean all untracked files and directories (-d) from repo
-    repo_call(['git', 'clean', '-dffx'])
+    check_call(['git', 'clean', '-dffx'], cwd=repo_dir)
 
     # use latest tag if none given
     if tag is None:
-        result = check_output(['git', 'describe', '--abbrev=0', '--tags'], cwd=REPO_DIR)
+        result = check_output(['git', 'describe', '--abbrev=0', '--tags'], cwd=repo_dir)
         tag = result.decode().rstrip()  # strip away line-break
 
     # checkout tag
-    repo_call(['git', 'checkout', tag])
+    check_call(['git', 'checkout', tag], cwd=repo_dir)
 
     # return the tag that was used for the checkout
     return tag
 
 
-def repo_call(command):
-    check_call(command, cwd=REPO_DIR)
-
-
 def unmount():
     call(['fusermount', '-u', BUILD_DIR])
 
diff --git a/utils.py b/utils.py
index 1aa609d53cf4e3df6d3912c573f435fd7beb6e91..bd5014027a9ab77c414620c91505b631f3d621b8 100755
--- a/utils.py
+++ b/utils.py
@@ -1,9 +1,15 @@
 #!/usr/bin/env python3
 
 import hashlib
+import json
 import sys
 
 
+def get_app_info(app_name):
+    with open('apps.json', 'r') as f:
+        return json.load(f)[app_name]
+
+
 def calc_hash(filename, block_size=65536):
     sha512 = hashlib.sha512()
     with open(filename, 'rb') as f:
@@ -15,4 +21,3 @@ def calc_hash(filename, block_size=65536):
 def fail(msg=""):
     sys.stderr.write(msg + "\n")
     sys.exit(1)
-
diff --git a/verify_jar.py b/verify_jar.py
deleted file mode 100755
index a2f7c0875133fee61a6342f3a851290355df83f4..0000000000000000000000000000000000000000
--- a/verify_jar.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import subprocess
-import sys
-from utils import calc_hash, fail
-
-
-def main():
-    if len(sys.argv) != 3:
-        fail("Usage: %s reference_jar built_jar" % sys.argv[0])
-    if not verify_jar(sys.argv[1], sys.argv[2]): sys.exit(1)
-
-
-def verify_jar(reference_jar, built_jar):
-    if not os.path.isfile(reference_jar):
-        sys.stderr.write("Cannot verify: Reference jar '%s' does not exist" % reference_jar)
-        return False
-
-    if not os.path.isfile(built_jar):
-        sys.stderr.write("Cannot verify: Built jar '%s' does not exist" % built_jar)
-        return False
-
-    if calc_hash(reference_jar) != calc_hash(built_jar):
-        sys.stderr.write("Mismatch detected. Trying to find reason...\n")
-        subprocess.call(['diffoscope', reference_jar, built_jar])
-        sys.stderr.write("Files do NOT match! :(")
-        return False
-    else:
-        print("Files match! :)")
-        return True
-
-
-if __name__ == "__main__":
-    main()