aboutsummaryrefslogtreecommitdiff
path: root/cluster
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-08-24 15:42:47 +0200
committerAlex Auvolat <alex@adnab.me>2022-08-24 15:42:47 +0200
commit2e8923b383eb06c53261eee8e5c442b857fb67e4 (patch)
tree0ad148f75f7b54dfed2dbac8f43f6df9badc502a /cluster
parent9848f3090f77363a2fda0f9fa673ebcf1fb8228c (diff)
downloadnixcfg-2e8923b383eb06c53261eee8e5c442b857fb67e4.tar.gz
nixcfg-2e8923b383eb06c53261eee8e5c442b857fb67e4.zip
Move app files into cluster subdirectories; add prod garage
Diffstat (limited to 'cluster')
-rw-r--r--cluster/prod/app/cryptpad/build/README.md29
-rw-r--r--cluster/prod/app/cryptpad/build/common.nix22
-rw-r--r--cluster/prod/app/cryptpad/build/default.nix77
-rw-r--r--cluster/prod/app/cryptpad/build/docker.nix11
-rw-r--r--cluster/prod/app/cryptpad/build/nix.lock/bower.json57
-rw-r--r--cluster/prod/app/cryptpad/build/nix.lock/bower.nix37
-rw-r--r--cluster/prod/app/cryptpad/build/nix.lock/node-env.nix588
-rw-r--r--cluster/prod/app/cryptpad/build/nix.lock/node-packages.nix756
-rw-r--r--cluster/prod/app/cryptpad/build/nix.lock/npm.nix17
-rw-r--r--cluster/prod/app/cryptpad/build/nix.lock/package-lock.json6618
-rw-r--r--cluster/prod/app/cryptpad/build/nix.lock/package.json55
-rw-r--r--cluster/prod/app/cryptpad/build/shell.nix31
-rw-r--r--cluster/prod/app/cryptpad/config/application_config.js40
-rw-r--r--cluster/prod/app/cryptpad/config/config.js281
-rw-r--r--cluster/prod/app/cryptpad/deploy/backup.hcl57
-rw-r--r--cluster/prod/app/cryptpad/deploy/cryptpad.hcl75
-rw-r--r--cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_access_key_id1
-rw-r--r--cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_secret_access_key1
-rw-r--r--cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_password1
-rw-r--r--cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_repository1
-rw-r--r--cluster/prod/app/drone-ci/config/litestream.yml10
-rw-r--r--cluster/prod/app/drone-ci/deploy/server.hcl139
-rw-r--r--cluster/prod/app/drone-ci/integration/README.md74
-rw-r--r--cluster/prod/app/drone-ci/integration/docker-compose.yml32
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/cookie_secret1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/db_enc_secret1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_id1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_secret1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/rpc_secret1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/s3_ak1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/s3_db_bucket1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/s3_sk1
-rw-r--r--cluster/prod/app/drone-ci/secrets/drone-ci/s3_storage_bucket1
-rw-r--r--cluster/prod/app/frontend/deploy/frontend-tricot-prod.hcl90
-rw-r--r--cluster/prod/app/jitsi/build/jitsi-conference-focus/0001-Remove-broken-command-line-args-parameters-setting.patch91
-rw-r--r--cluster/prod/app/jitsi/build/jitsi-conference-focus/Dockerfile26
-rwxr-xr-xcluster/prod/app/jitsi/build/jitsi-conference-focus/jicofo11
-rw-r--r--cluster/prod/app/jitsi/build/jitsi-meet/Dockerfile23
-rw-r--r--cluster/prod/app/jitsi/build/jitsi-videobridge/0001-Remove-deprecated-argument.patch40
-rw-r--r--cluster/prod/app/jitsi/build/jitsi-videobridge/Dockerfile24
-rwxr-xr-xcluster/prod/app/jitsi/build/jitsi-videobridge/jvb_run22
-rw-r--r--cluster/prod/app/jitsi/build/jitsi-xmpp/Dockerfile35
-rwxr-xr-xcluster/prod/app/jitsi/build/jitsi-xmpp/xmpp_prosody9
-rw-r--r--cluster/prod/app/jitsi/config/config.js773
-rw-r--r--cluster/prod/app/jitsi/config/jicofo.conf273
-rw-r--r--cluster/prod/app/jitsi/config/nginx.conf133
-rw-r--r--cluster/prod/app/jitsi/config/prosody.cfg.lua135
-rw-r--r--cluster/prod/app/jitsi/config/videobridge.conf290
-rw-r--r--cluster/prod/app/jitsi/deploy/jitsi.hcl257
-rw-r--r--cluster/prod/app/jitsi/integration/README.md91
-rw-r--r--cluster/prod/app/jitsi/integration/docker-compose.yml42
-rw-r--r--cluster/prod/app/jitsi/integration/jicofo/jicofo.conf273
-rw-r--r--cluster/prod/app/jitsi/integration/jvb/logging.properties47
-rw-r--r--cluster/prod/app/jitsi/integration/jvb/videobridge.conf290
-rw-r--r--cluster/prod/app/jitsi/integration/meet/config.js773
-rw-r--r--cluster/prod/app/jitsi/integration/meet/nginx.conf72
-rw-r--r--cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua137
-rw-r--r--cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua.back64
-rw-r--r--cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.crt1
-rw-r--r--cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.key1
-rw-r--r--cluster/prod/app/jitsi/secrets/jitsi/jicofo_pass1
-rw-r--r--cluster/prod/app/jitsi/secrets/jitsi/jitsi.crt1
-rw-r--r--cluster/prod/app/jitsi/secrets/jitsi/jitsi.key1
-rw-r--r--cluster/prod/app/jitsi/secrets/jitsi/jvb_pass1
-rw-r--r--cluster/prod/garage/config/garage.toml24
-rw-r--r--cluster/prod/garage/deploy/garage.hcl131
-rw-r--r--cluster/prod/garage/secrets/garage/rpc_secret1
-rw-r--r--cluster/staging/app/directory/config/bottin/config.json.tpl26
-rw-r--r--cluster/staging/app/directory/config/guichet/config.json.tpl34
-rw-r--r--cluster/staging/app/directory/deploy/directory.hcl141
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/mail_domain1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/mail_from1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/s3_access_key1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/s3_bucket1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/s3_endpoint1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/s3_region1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/s3_secret_key1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/smtp_pass1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/smtp_server1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/smtp_user1
-rw-r--r--cluster/staging/app/directory/secrets/directory/guichet/web_hostname1
-rw-r--r--cluster/staging/app/directory/secrets/directory/ldap_base_dn1
-rw-r--r--cluster/staging/app/docker-compose.yml16
-rw-r--r--cluster/staging/app/drone-ci/build/.gitignore2
-rw-r--r--cluster/staging/app/drone-ci/build/Makefile8
-rw-r--r--cluster/staging/app/drone-ci/build/build-qcow2.nix24
-rw-r--r--cluster/staging/app/drone-ci/build/machine-config.nix89
-rw-r--r--cluster/staging/app/drone-ci/deploy/bad-runner-vm.hcl48
-rw-r--r--cluster/staging/app/drone-ci/deploy/runner-docker.hcl91
-rw-r--r--cluster/staging/app/frontend/deploy/frontend-tricot.hcl89
-rw-r--r--cluster/staging/app/garage/config/garage.toml31
-rw-r--r--cluster/staging/app/garage/deploy/garage.hcl195
-rw-r--r--cluster/staging/app/garage/secrets/garage-staging/rpc_secret1
-rw-r--r--cluster/staging/app/im/build/matrix-synapse/Dockerfile54
-rwxr-xr-xcluster/staging/app/im/build/matrix-synapse/entrypoint.sh3
-rwxr-xr-xcluster/staging/app/im/build/matrix-synapse/matrix-s3-async16
-rwxr-xr-xcluster/staging/app/im/build/matrix-synapse/matrix-s3-async-sqlite13
-rw-r--r--cluster/staging/app/im/config/homeserver.yaml2651
-rw-r--r--cluster/staging/app/im/config/litestream.yml10
-rw-r--r--cluster/staging/app/im/config/synapse.log.config.yaml23
-rw-r--r--cluster/staging/app/im/deploy/im.hcl165
-rw-r--r--cluster/staging/app/im/secrets/synapse/form_secret1
-rw-r--r--cluster/staging/app/im/secrets/synapse/macaroon_secret_key1
-rw-r--r--cluster/staging/app/im/secrets/synapse/registration_shared_secret1
-rw-r--r--cluster/staging/app/im/secrets/synapse/s3_access_key1
-rw-r--r--cluster/staging/app/im/secrets/synapse/s3_secret_key1
-rw-r--r--cluster/staging/app/im/secrets/synapse/signing_key1
-rw-r--r--cluster/staging/app/telemetry/config/apm-config.yaml20
-rw-r--r--cluster/staging/app/telemetry/config/filebeat.yml46
-rw-r--r--cluster/staging/app/telemetry/config/grafana-litestream.yml10
-rw-r--r--cluster/staging/app/telemetry/config/grafana/provisioning/datasources/elastic.yaml21
-rw-r--r--cluster/staging/app/telemetry/config/otel-config.yaml56
-rw-r--r--cluster/staging/app/telemetry/deploy/telemetry-system.hcl182
-rw-r--r--cluster/staging/app/telemetry/deploy/telemetry.hcl181
114 files changed, 17466 insertions, 0 deletions
diff --git a/cluster/prod/app/cryptpad/build/README.md b/cluster/prod/app/cryptpad/build/README.md
new file mode 100644
index 0000000..079d836
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/README.md
@@ -0,0 +1,29 @@
+## Build
+
+Cryptpad being not NixOS native, an upgrade must be done in 4 steps:
+ 1. Bump the cryptpad version in `common.nix`
+ 2. Rebuild the Nix lock files for the dependencies
+ 3. Build the package for Nix
+ 4. Create a container from the Nix package
+
+To bump the nix version, set the desired tag in `common.nix` in the `cryptpadVersion` entry.
+Set the corresponding commit in the `cryptadCommit` field, its goal would be to detect unwanted update of the tag.
+
+To rebuild the lock files (they are stored in the `nix.lock` folder):
+
+```
+nix-shell --run "update_lock"
+```
+
+To build cryptpad:
+
+```
+nix-build
+```
+
+Create the container:
+
+```
+docker load < $(nix-build docker.nix)
+docker push superboum/cryptpad:???
+```
diff --git a/cluster/prod/app/cryptpad/build/common.nix b/cluster/prod/app/cryptpad/build/common.nix
new file mode 100644
index 0000000..957d381
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/common.nix
@@ -0,0 +1,22 @@
+rec {
+ cryptpadVersion = "4.14.1+2";
+ cryptpadCommit = "18c371bb5bda068a5d962dd7c4f0726320eea5e9";
+
+ pkgsSrc = fetchTarball {
+ # Latest commit on https://github.com/NixOS/nixpkgs/tree/nixos-21.11
+ # As of 2022-04-15
+ url ="https://github.com/NixOS/nixpkgs/archive/2f06b87f64bc06229e05045853e0876666e1b023.tar.gz";
+ sha256 = "sha256:1d7zg96xw4qsqh7c89pgha9wkq3rbi9as3k3d88jlxy2z0ns0cy2";
+ };
+ cryptpadSrc = builtins.fetchGit {
+ url = "https://github.com/superboum/cryptpad";
+ ref = "refs/tags/${cryptpadVersion}";
+ rev = cryptpadCommit;
+ };
+ bower2nixSrc = builtins.fetchGit {
+ url = "https://github.com/superboum/bower2nix";
+ ref = "new";
+ rev = "618ab3e206325c63fe4526ae842a1f6c792b0e27";
+ };
+ nodejs = "nodejs-slim-16_x";
+}
diff --git a/cluster/prod/app/cryptpad/build/default.nix b/cluster/prod/app/cryptpad/build/default.nix
new file mode 100644
index 0000000..f0a5c00
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/default.nix
@@ -0,0 +1,77 @@
+let
+ common = import ./common.nix;
+ pkgs = import common.pkgsSrc {};
+ nodejs = pkgs.${common.nodejs};
+
+ bower = (pkgs.buildBowerComponents {
+ name = "cryptpad-${common.cryptpadVersion}-bower";
+ generated = ./nix.lock/bower.nix;
+ src = common.cryptpadSrc;
+ }).overrideAttrs (old: {
+ bowerPackages = old.bowerPackages.override (old_: {
+ # add missing dependencies:
+ # Those dependencies are EOL and they are not installed by buildBowerComponents,
+ # but they are required, otherwise the resolver crashes.
+ # * add the second jquery ~2.1.0 entry
+ # * add the second bootstrap ~3.1.1 entry
+ paths = old_.paths ++ [
+ (pkgs.fetchbower "jquery" "2.1.0" "~2.1.0" "02kwvz93vzpv10qnp7s0dz3al0jh77awwrizb6wadsvgifxssnlr")
+ (pkgs.fetchbower "bootstrap" "3.1.1" "~3.1.1" "06bhjwa8p7mzbpr3jkgydd804z1nwrkdql66h7jkfml99psv9811")
+ ];
+ });
+ });
+
+ npm = import ./nix.lock/npm.nix {
+ inherit pkgs;
+ };
+
+in
+ pkgs.stdenv.mkDerivation {
+ name = "cryptpad-${common.cryptpadVersion}";
+ src = common.cryptpadSrc;
+
+ buildPhase = ''
+ cp -r ${npm.nodeDependencies}/lib/node_modules node_modules
+ chmod +w -R node_modules
+
+ # clear executable files inside the node_modules folder to reduce dependencies
+ # and attack surface
+ find node_modules -type f ! -path 'node_modules/gar/*' -executable -print | tee >(xargs -n 20 rm)
+
+ # Remove only office that IS BIG
+ # COMMENTED as it is not as easy as planned.
+ # rm -rf www/common/onlyoffice
+ '';
+
+ installPhase = ''
+ mkdir -p $out/{bin,opt}
+
+ out_cryptpad=$out/opt/
+
+ # copy the source code
+ cp -r .bowerrc bower.json package.json package-lock.json customize.dist lib server.js www $out_cryptpad
+
+ # mount node_modules
+ cp -r node_modules $out_cryptpad/node_modules
+
+ # patch
+ substituteInPlace $out_cryptpad/lib/workers/index.js --replace "lib/workers/db-worker" "$out_cryptpad/lib/workers/db-worker"
+
+ # mount bower, based on the .bowerrc file at the git repo root
+ cp -r ${bower}/bower_components $out_cryptpad/www/
+
+ # cryptpad is bugged with absolute path, this is a workaround to use absolute path as relative path
+ ln -s / $out_cryptpad/root
+
+ # start script, cryptpad is lost if its working directory is not its source directory
+ cat > $out/bin/cryptpad <<EOF
+ #!${pkgs.stdenv.shell}
+ cd $out_cryptpad
+ exec ${nodejs}/bin/node server.js
+ EOF
+
+ chmod +x $out/bin/cryptpad
+ '';
+
+ dontFixup = true;
+ }
diff --git a/cluster/prod/app/cryptpad/build/docker.nix b/cluster/prod/app/cryptpad/build/docker.nix
new file mode 100644
index 0000000..168963d
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/docker.nix
@@ -0,0 +1,11 @@
+let
+ common = import ./common.nix;
+ pkgs = import common.pkgsSrc {};
+ app = import ./default.nix;
+in
+ pkgs.dockerTools.buildLayeredImage {
+ name = "superboum/cryptpad";
+ config = {
+ Cmd = [ "${app}/bin/cryptpad" ];
+ };
+ }
diff --git a/cluster/prod/app/cryptpad/build/nix.lock/bower.json b/cluster/prod/app/cryptpad/build/nix.lock/bower.json
new file mode 100644
index 0000000..faae9d9
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/nix.lock/bower.json
@@ -0,0 +1,57 @@
+{
+ "name": "cryptpad",
+ "version": "0.1.0",
+ "authors": [
+ "Caleb James DeLisle <cjd@cjdns.fr>"
+ ],
+ "description": "realtime collaborative visual editor with zero knowlege server",
+ "main": "www/index.html",
+ "moduleType": [
+ "node"
+ ],
+ "license": "AGPLv3",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "dependencies": {
+ "jquery": "3.6.0",
+ "tweetnacl": "0.12.2",
+ "components-font-awesome": "^4.6.3",
+ "ckeditor": "4.14.0",
+ "codemirror": "^5.19.0",
+ "requirejs": "2.3.5",
+ "marked": "1.1.0",
+ "rangy": "rangy-release#~1.3.0",
+ "json.sortify": "~2.1.0",
+ "hyperjson": "~1.4.0",
+ "chainpad-crypto": "^0.2.0",
+ "chainpad-listmap": "^1.0.0",
+ "chainpad": "^5.2.0",
+ "file-saver": "1.3.1",
+ "alertifyjs": "1.0.11",
+ "scrypt-async": "1.2.0",
+ "require-css": "0.1.10",
+ "bootstrap": "^v4.0.0",
+ "diff-dom": "2.1.1",
+ "nthen": "0.1.7",
+ "open-sans-fontface": "^1.4.2",
+ "bootstrap-tokenfield": "0.12.1",
+ "localforage": "^1.5.2",
+ "html2canvas": "^0.4.1",
+ "croppie": "^2.5.0",
+ "sortablejs": "^1.6.0",
+ "saferphore": "^0.0.1",
+ "jszip": "3.7.1",
+ "requirejs-plugins": "^1.0.3",
+ "dragula.js": "3.7.2",
+ "MathJax": "3.0.5"
+ },
+ "resolutions": {
+ "bootstrap": "^v4.0.0",
+ "jquery": "3.6.0"
+ }
+}
diff --git a/cluster/prod/app/cryptpad/build/nix.lock/bower.nix b/cluster/prod/app/cryptpad/build/nix.lock/bower.nix
new file mode 100644
index 0000000..cf06e51
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/nix.lock/bower.nix
@@ -0,0 +1,37 @@
+# Generated by bower2nix v3.3.0 (https://github.com/rvl/bower2nix)
+{ fetchbower, buildEnv }:
+buildEnv { name = "bower-env"; ignoreCollisions = true; paths = [
+ (fetchbower "jquery" "3.6.0" "3.6.0" "1wx5n605x6ga483hba43gxjncgzk8yvxc3h0jlwgpjd0h54y9v6l")
+ (fetchbower "tweetnacl" "0.12.2" "0.12.2" "1lfzbfrdaly3zyzbcp1p53yhxlrx56k8x04q924kg7l52gblm65g")
+ (fetchbower "components-font-awesome" "4.7.0" "^4.6.3" "1w27im6ayjrbgjqa0i49ml5d3wy4ld40h9b29hz9myv77bpx4lg1")
+ (fetchbower "ckeditor" "4.14.0" "4.14.0" "0lw9q0k8c0jlxvf35vrccab9c3c8rgpc6x66czj9si8yy2lyliyp")
+ (fetchbower "codemirror" "5.65.3" "^5.19.0" "0z6pd0q0cy0k0dkplx4f3cmmjqbiixv6wqlzbz5j8dnsxr5hhgzh")
+ (fetchbower "requirejs" "2.3.5" "2.3.5" "05lyvgz914h2w08r24rk0vkk3yxmqrvlg7j3i5av9ffkg9lpzsli")
+ (fetchbower "marked" "1.1.0" "1.1.0" "1sdgqw9iki9c1pfm4c5h6c956mchbip2jywjrcmrlb75k53flsjz")
+ (fetchbower "rangy" "rangy-release#1.3.0" "rangy-release#~1.3.0" "13x3wci003p8jyv2ncir0k23bxckx99b3555r0zvgmlwycg7w0zv")
+ (fetchbower "json.sortify" "2.1.0" "~2.1.0" "1rz9xz0gnm4ak31n10vhslqsw8fw493gjylwj8xsy3bxqq1ygpnh")
+ (fetchbower "hyperjson" "1.4.0" "~1.4.0" "1n68ls3x4lyhg1yy8i4q3xkgh5xqpyakf45sny4x91mkr68x4bd9")
+ (fetchbower "chainpad-crypto" "0.2.7" "^0.2.0" "16j0gjj1v8dckqpsg38229qs4dammz7vx8ywsik6f0brzf4py65a")
+ (fetchbower "chainpad-listmap" "1.0.1" "^1.0.0" "0s2v27hhraifb1yjw5fka4a922zmgsdngsaq1nfd48gbs8gd2rrd")
+ (fetchbower "chainpad" "5.2.4" "^5.2.0" "1f4nap0r8w50qpmjdfhhjhpz5xcl0n4zaxxnav1qaxi5j6dyg8h6")
+ (fetchbower "file-saver" "1.3.1" "1.3.1" "065nzkvdiicxnw06z1sjz1sbp9nyis8z839hv6ng1fk25dc5kvkg")
+ (fetchbower "alertifyjs" "1.0.11" "1.0.11" "0v7323bzq90k35shm3h6azj4wd9la3kbi1va1pw4qyvndkwma69l")
+ (fetchbower "scrypt-async" "1.2.0" "1.2.0" "0d076ax708p9b8hcmk4f82j925nlnm0hmp0ni45ql37g7iirfpyv")
+ (fetchbower "require-css" "0.1.10" "0.1.10" "106gz9i76v71q9zx2pnqkkj342m630lvssnw54023a0ljc0gqcwq")
+ (fetchbower "bootstrap" "4.6.1" "^v4.0.0" "0g8zy1fl396lawgjvfhlpcl38zxsgybhnzi8b6b4m9nccvmpxv83")
+ (fetchbower "diff-dom" "2.1.1" "2.1.1" "0bp8c80g11hhlkvl3lhrqc39jvqiiyqvrgk1nsn35ps01ava07z9")
+ (fetchbower "nthen" "0.1.7" "0.1.7" "03yap5ildigaw4rwxmxs37pcwhq415iham8w39zd56ka98gpfxa5")
+ (fetchbower "open-sans-fontface" "1.4.2" "^1.4.2" "0ksav1fcq640fmdz49ra4prwsrrfj35y2p4shx1jh1j7zxd044nf")
+ (fetchbower "bootstrap-tokenfield" "0.12.1" "0.12.1" "1dh791s6ih8bf9ihck9n39h68c273jb3lg4mqk94bvqraz45fvwx")
+ (fetchbower "localforage" "1.10.0" "^1.5.2" "019rh006v2w5x63mgk78qhw59kf8czbkwdvfngmac8fs6gz88lc8")
+ (fetchbower "html2canvas" "0.4.1" "^0.4.1" "0yg7y90nav068q0i5afc2c221zkddpf28hi0hwc46cawx4180c69")
+ (fetchbower "croppie" "2.6.5" "^2.5.0" "1j1v5620zi13ad42r358i4ay891abwn6nz357484kgq2bgjj6ccx")
+ (fetchbower "sortablejs" "1.15.0" "^1.6.0" "1wk1097jrxbp2c4ghcppqd3h2gnq5b01qkf9426mc08zgszlvjr7")
+ (fetchbower "saferphore" "0.0.1" "^0.0.1" "1wfr9wpbm3lswmvy2p0247ydb108h4qh5s286py89k871qh6jwdi")
+ (fetchbower "jszip" "3.7.1" "3.7.1" "0f14bak7vylxizi6pvj3znjc2cx922avbv7lslklvic85x0318lf")
+ (fetchbower "requirejs-plugins" "1.0.3" "^1.0.3" "00s3sdz1ykygx5shldwhhhybwgw7c99vkqd94i5i5x0gl97ifxf5")
+ (fetchbower "dragula.js" "3.7.2" "3.7.2" "0dbkmrl8bcxiplprmmp9fj96ri5nahb2ql8cc7zwawncv0drvlh0")
+ (fetchbower "MathJax" "3.0.5" "3.0.5" "087a9av15qj43m8pr3b9g59ncmydhmg40m6dfzsac62ykianh2a0")
+ (fetchbower "chainpad-netflux" "1.0.0" "^1.0.0" "08rpc73x1vyvd6zkb7w0m1smzjhq3b7cwb30nlmg93x873zjlsl6")
+ (fetchbower "netflux-websocket" "1.0.0" "^1.0.0" "10hgc5ra3ll7qc2r8aal6p03gx6dgz06l2b54lh995pvf901wzi6")
+]; }
diff --git a/cluster/prod/app/cryptpad/build/nix.lock/node-env.nix b/cluster/prod/app/cryptpad/build/nix.lock/node-env.nix
new file mode 100644
index 0000000..5f05578
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/nix.lock/node-env.nix
@@ -0,0 +1,588 @@
+# This file originates from node2nix
+
+{lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile, writeShellScript}:
+
+let
+ # Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master
+ utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux;
+
+ python = if nodejs ? python then nodejs.python else python2;
+
+ # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise
+ tarWrapper = runCommand "tarWrapper" {} ''
+ mkdir -p $out/bin
+
+ cat > $out/bin/tar <<EOF
+ #! ${stdenv.shell} -e
+ $(type -p tar) "\$@" --warning=no-unknown-keyword --delay-directory-restore
+ EOF
+
+ chmod +x $out/bin/tar
+ '';
+
+ # Function that generates a TGZ file from a NPM project
+ buildNodeSourceDist =
+ { name, version, src, ... }:
+
+ stdenv.mkDerivation {
+ name = "node-tarball-${name}-${version}";
+ inherit src;
+ buildInputs = [ nodejs ];
+ buildPhase = ''
+ export HOME=$TMPDIR
+ tgzFile=$(npm pack | tail -n 1) # Hooks to the pack command will add output (https://docs.npmjs.com/misc/scripts)
+ '';
+ installPhase = ''
+ mkdir -p $out/tarballs
+ mv $tgzFile $out/tarballs
+ mkdir -p $out/nix-support
+ echo "file source-dist $out/tarballs/$tgzFile" >> $out/nix-support/hydra-build-products
+ '';
+ };
+
+ # Common shell logic
+ installPackage = writeShellScript "install-package" ''
+ installPackage() {
+ local packageName=$1 src=$2
+
+ local strippedName
+
+ local DIR=$PWD
+ cd $TMPDIR
+
+ unpackFile $src
+
+ # Make the base dir in which the target dependency resides first
+ mkdir -p "$(dirname "$DIR/$packageName")"
+
+ if [ -f "$src" ]
+ then
+ # Figure out what directory has been unpacked
+ packageDir="$(find . -maxdepth 1 -type d | tail -1)"
+
+ # Restore write permissions to make building work
+ find "$packageDir" -type d -exec chmod u+x {} \;
+ chmod -R u+w "$packageDir"
+
+ # Move the extracted tarball into the output folder
+ mv "$packageDir" "$DIR/$packageName"
+ elif [ -d "$src" ]
+ then
+ # Get a stripped name (without hash) of the source directory.
+ # On old nixpkgs it's already set internally.
+ if [ -z "$strippedName" ]
+ then
+ strippedName="$(stripHash $src)"
+ fi
+
+ # Restore write permissions to make building work
+ chmod -R u+w "$strippedName"
+
+ # Move the extracted directory into the output folder
+ mv "$strippedName" "$DIR/$packageName"
+ fi
+
+ # Change to the package directory to install dependencies
+ cd "$DIR/$packageName"
+ }
+ '';
+
+ # Bundle the dependencies of the package
+ #
+ # Only include dependencies if they don't exist. They may also be bundled in the package.
+ includeDependencies = {dependencies}:
+ lib.optionalString (dependencies != []) (
+ ''
+ mkdir -p node_modules
+ cd node_modules
+ ''
+ + (lib.concatMapStrings (dependency:
+ ''
+ if [ ! -e "${dependency.name}" ]; then
+ ${composePackage dependency}
+ fi
+ ''
+ ) dependencies)
+ + ''
+ cd ..
+ ''
+ );
+
+ # Recursively composes the dependencies of a package
+ composePackage = { name, packageName, src, dependencies ? [], ... }@args:
+ builtins.addErrorContext "while evaluating node package '${packageName}'" ''
+ installPackage "${packageName}" "${src}"
+ ${includeDependencies { inherit dependencies; }}
+ cd ..
+ ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+ '';
+
+ pinpointDependencies = {dependencies, production}:
+ let
+ pinpointDependenciesFromPackageJSON = writeTextFile {
+ name = "pinpointDependencies.js";
+ text = ''
+ var fs = require('fs');
+ var path = require('path');
+
+ function resolveDependencyVersion(location, name) {
+ if(location == process.env['NIX_STORE']) {
+ return null;
+ } else {
+ var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json");
+
+ if(fs.existsSync(dependencyPackageJSON)) {
+ var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON));
+
+ if(dependencyPackageObj.name == name) {
+ return dependencyPackageObj.version;
+ }
+ } else {
+ return resolveDependencyVersion(path.resolve(location, ".."), name);
+ }
+ }
+ }
+
+ function replaceDependencies(dependencies) {
+ if(typeof dependencies == "object" && dependencies !== null) {
+ for(var dependency in dependencies) {
+ var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency);
+
+ if(resolvedVersion === null) {
+ process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n");
+ } else {
+ dependencies[dependency] = resolvedVersion;
+ }
+ }
+ }
+ }
+
+ /* Read the package.json configuration */
+ var packageObj = JSON.parse(fs.readFileSync('./package.json'));
+
+ /* Pinpoint all dependencies */
+ replaceDependencies(packageObj.dependencies);
+ if(process.argv[2] == "development") {
+ replaceDependencies(packageObj.devDependencies);
+ }
+ replaceDependencies(packageObj.optionalDependencies);
+
+ /* Write the fixed package.json file */
+ fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2));
+ '';
+ };
+ in
+ ''
+ node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"}
+
+ ${lib.optionalString (dependencies != [])
+ ''
+ if [ -d node_modules ]
+ then
+ cd node_modules
+ ${lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies}
+ cd ..
+ fi
+ ''}
+ '';
+
+ # Recursively traverses all dependencies of a package and pinpoints all
+ # dependencies in the package.json file to the versions that are actually
+ # being used.
+
+ pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args:
+ ''
+ if [ -d "${packageName}" ]
+ then
+ cd "${packageName}"
+ ${pinpointDependencies { inherit dependencies production; }}
+ cd ..
+ ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+ fi
+ '';
+
+ # Extract the Node.js source code which is used to compile packages with
+ # native bindings
+ nodeSources = runCommand "node-sources" {} ''
+ tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
+ mv node-* $out
+ '';
+
+ # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty)
+ addIntegrityFieldsScript = writeTextFile {
+ name = "addintegrityfields.js";
+ text = ''
+ var fs = require('fs');
+ var path = require('path');
+
+ function augmentDependencies(baseDir, dependencies) {
+ for(var dependencyName in dependencies) {
+ var dependency = dependencies[dependencyName];
+
+ // Open package.json and augment metadata fields
+ var packageJSONDir = path.join(baseDir, "node_modules", dependencyName);
+ var packageJSONPath = path.join(packageJSONDir, "package.json");
+
+ if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored
+ console.log("Adding metadata fields to: "+packageJSONPath);
+ var packageObj = JSON.parse(fs.readFileSync(packageJSONPath));
+
+ if(dependency.integrity) {
+ packageObj["_integrity"] = dependency.integrity;
+ } else {
+ packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads.
+ }
+
+ if(dependency.resolved) {
+ packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided
+ } else {
+ packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories.
+ }
+
+ if(dependency.from !== undefined) { // Adopt from property if one has been provided
+ packageObj["_from"] = dependency.from;
+ }
+
+ fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2));
+ }
+
+ // Augment transitive dependencies
+ if(dependency.dependencies !== undefined) {
+ augmentDependencies(packageJSONDir, dependency.dependencies);
+ }
+ }
+ }
+
+ if(fs.existsSync("./package-lock.json")) {
+ var packageLock = JSON.parse(fs.readFileSync("./package-lock.json"));
+
+ if(![1, 2].includes(packageLock.lockfileVersion)) {
+ process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n");
+ process.exit(1);
+ }
+
+ if(packageLock.dependencies !== undefined) {
+ augmentDependencies(".", packageLock.dependencies);
+ }
+ }
+ '';
+ };
+
+ # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes
+ reconstructPackageLock = writeTextFile {
+ name = "addintegrityfields.js";
+ text = ''
+ var fs = require('fs');
+ var path = require('path');
+
+ var packageObj = JSON.parse(fs.readFileSync("package.json"));
+
+ var lockObj = {
+ name: packageObj.name,
+ version: packageObj.version,
+ lockfileVersion: 1,
+ requires: true,
+ dependencies: {}
+ };
+
+ function augmentPackageJSON(filePath, dependencies) {
+ var packageJSON = path.join(filePath, "package.json");
+ if(fs.existsSync(packageJSON)) {
+ var packageObj = JSON.parse(fs.readFileSync(packageJSON));
+ dependencies[packageObj.name] = {
+ version: packageObj.version,
+ integrity: "sha1-000000000000000000000000000=",
+ dependencies: {}
+ };
+ processDependencies(path.join(filePath, "node_modules"), dependencies[packageObj.name].dependencies);
+ }
+ }
+
+ function processDependencies(dir, dependencies) {
+ if(fs.existsSync(dir)) {
+ var files = fs.readdirSync(dir);
+
+ files.forEach(function(entry) {
+ var filePath = path.join(dir, entry);
+ var stats = fs.statSync(filePath);
+
+ if(stats.isDirectory()) {
+ if(entry.substr(0, 1) == "@") {
+ // When we encounter a namespace folder, augment all packages belonging to the scope
+ var pkgFiles = fs.readdirSync(filePath);
+
+ pkgFiles.forEach(function(entry) {
+ if(stats.isDirectory()) {
+ var pkgFilePath = path.join(filePath, entry);
+ augmentPackageJSON(pkgFilePath, dependencies);
+ }
+ });
+ } else {
+ augmentPackageJSON(filePath, dependencies);
+ }
+ }
+ });
+ }
+ }
+
+ processDependencies("node_modules", lockObj.dependencies);
+
+ fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2));
+ '';
+ };
+
+ prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}:
+ let
+ forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com";
+ in
+ ''
+ # Pinpoint the versions of all dependencies to the ones that are actually being used
+ echo "pinpointing versions of dependencies..."
+ source $pinpointDependenciesScriptPath
+
+ # Patch the shebangs of the bundled modules to prevent them from
+ # calling executables outside the Nix store as much as possible
+ patchShebangs .
+
+ # Deploy the Node.js package by running npm install. Since the
+ # dependencies have been provided already by ourselves, it should not
+ # attempt to install them again, which is good, because we want to make
+ # it Nix's responsibility. If it needs to install any dependencies
+ # anyway (e.g. because the dependency parameters are
+ # incomplete/incorrect), it fails.
+ #
+ # The other responsibilities of NPM are kept -- version checks, build
+ # steps, postprocessing etc.
+
+ export HOME=$TMPDIR
+ cd "${packageName}"
+ runHook preRebuild
+
+ ${lib.optionalString bypassCache ''
+ ${lib.optionalString reconstructLock ''
+ if [ -f package-lock.json ]
+ then
+ echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!"
+ echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!"
+ rm package-lock.json
+ else
+ echo "No package-lock.json file found, reconstructing..."
+ fi
+
+ node ${reconstructPackageLock}
+ ''}
+
+ node ${addIntegrityFieldsScript}
+ ''}
+
+ npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild
+
+ if [ "''${dontNpmInstall-}" != "1" ]
+ then
+ # NPM tries to download packages even when they already exist if npm-shrinkwrap is used.
+ rm -f npm-shrinkwrap.json
+
+ npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} install
+ fi
+ '';
+
+ # Builds and composes an NPM package including all its dependencies
+ buildNodePackage =
+ { name
+ , packageName
+ , version
+ , dependencies ? []
+ , buildInputs ? []
+ , production ? true
+ , npmFlags ? ""
+ , dontNpmInstall ? false
+ , bypassCache ? false
+ , reconstructLock ? false
+ , preRebuild ? ""
+ , dontStrip ? true
+ , unpackPhase ? "true"
+ , buildPhase ? "true"
+ , meta ? {}
+ , ... }@args:
+
+ let
+ extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" "meta" ];
+ in
+ stdenv.mkDerivation ({
+ name = "${name}-${version}";
+ buildInputs = [ tarWrapper python nodejs ]
+ ++ lib.optional (stdenv.isLinux) utillinux
+ ++ lib.optional (stdenv.isDarwin) libtool
+ ++ buildInputs;
+
+ inherit nodejs;
+
+ inherit dontStrip; # Stripping may fail a build for some package deployments
+ inherit dontNpmInstall preRebuild unpackPhase buildPhase;
+
+ compositionScript = composePackage args;
+ pinpointDependenciesScript = pinpointDependenciesOfPackage args;
+
+ passAsFile = [ "compositionScript" "pinpointDependenciesScript" ];
+
+ installPhase = ''
+ source ${installPackage}
+
+ # Create and enter a root node_modules/ folder
+ mkdir -p $out/lib/node_modules
+ cd $out/lib/node_modules
+
+ # Compose the package and all its dependencies
+ source $compositionScriptPath
+
+ ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
+
+ # Create symlink to the deployed executable folder, if applicable
+ if [ -d "$out/lib/node_modules/.bin" ]
+ then
+ ln -s $out/lib/node_modules/.bin $out/bin
+ fi
+
+ # Create symlinks to the deployed manual page folders, if applicable
+ if [ -d "$out/lib/node_modules/${packageName}/man" ]
+ then
+ mkdir -p $out/share
+ for dir in "$out/lib/node_modules/${packageName}/man/"*
+ do
+ mkdir -p $out/share/man/$(basename "$dir")
+ for page in "$dir"/*
+ do
+ ln -s $page $out/share/man/$(basename "$dir")
+ done
+ done
+ fi
+
+ # Run post install hook, if provided
+ runHook postInstall
+ '';
+
+ meta = {
+ # default to Node.js' platforms
+ platforms = nodejs.meta.platforms;
+ } // meta;
+ } // extraArgs);
+
+ # Builds a node environment (a node_modules folder and a set of binaries)
+ buildNodeDependencies =
+ { name
+ , packageName
+ , version
+ , src
+ , dependencies ? []
+ , buildInputs ? []
+ , production ? true
+ , npmFlags ? ""
+ , dontNpmInstall ? false
+ , bypassCache ? false
+ , reconstructLock ? false
+ , dontStrip ? true
+ , unpackPhase ? "true"
+ , buildPhase ? "true"
+ , ... }@args:
+
+ let
+ extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ];
+ in
+ stdenv.mkDerivation ({
+ name = "node-dependencies-${name}-${version}";
+
+ buildInputs = [ tarWrapper python nodejs ]
+ ++ lib.optional (stdenv.isLinux) utillinux
+ ++ lib.optional (stdenv.isDarwin) libtool
+ ++ buildInputs;
+
+ inherit dontStrip; # Stripping may fail a build for some package deployments
+ inherit dontNpmInstall unpackPhase buildPhase;
+
+ includeScript = includeDependencies { inherit dependencies; };
+ pinpointDependenciesScript = pinpointDependenciesOfPackage args;
+
+ passAsFile = [ "includeScript" "pinpointDependenciesScript" ];
+
+ installPhase = ''
+ source ${installPackage}
+
+ mkdir -p $out/${packageName}
+ cd $out/${packageName}
+
+ source $includeScriptPath
+
+ # Create fake package.json to make the npm commands work properly
+ cp ${src}/package.json .
+ chmod 644 package.json
+ ${lib.optionalString bypassCache ''
+ if [ -f ${src}/package-lock.json ]
+ then
+ cp ${src}/package-lock.json .
+ fi
+ ''}
+
+ # Go to the parent folder to make sure that all packages are pinpointed
+ cd ..
+ ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+
+ ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
+
+ # Expose the executables that were installed
+ cd ..
+ ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
+
+ mv ${packageName} lib
+ ln -s $out/lib/node_modules/.bin $out/bin
+ '';
+ } // extraArgs);
+
+ # Builds a development shell
+ buildNodeShell =
+ { name
+ , packageName
+ , version
+ , src
+ , dependencies ? []
+ , buildInputs ? []
+ , production ? true
+ , npmFlags ? ""
+ , dontNpmInstall ? false
+ , bypassCache ? false
+ , reconstructLock ? false
+ , dontStrip ? true
+ , unpackPhase ? "true"
+ , buildPhase ? "true"
+ , ... }@args:
+
+ let
+ nodeDependencies = buildNodeDependencies args;
+ in
+ stdenv.mkDerivation {
+ name = "node-shell-${name}-${version}";
+
+ buildInputs = [ python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux ++ buildInputs;
+ buildCommand = ''
+ mkdir -p $out/bin
+ cat > $out/bin/shell <<EOF
+ #! ${stdenv.shell} -e
+ $shellHook
+ exec ${stdenv.shell}
+ EOF
+ chmod +x $out/bin/shell
+ '';
+
+ # Provide the dependencies in a development shell through the NODE_PATH environment variable
+ inherit nodeDependencies;
+ shellHook = lib.optionalString (dependencies != []) ''
+ export NODE_PATH=${nodeDependencies}/lib/node_modules
+ export PATH="${nodeDependencies}/bin:$PATH"
+ '';
+ };
+in
+{
+ buildNodeSourceDist = lib.makeOverridable buildNodeSourceDist;
+ buildNodePackage = lib.makeOverridable buildNodePackage;
+ buildNodeDependencies = lib.makeOverridable buildNodeDependencies;
+ buildNodeShell = lib.makeOverridable buildNodeShell;
+}
diff --git a/cluster/prod/app/cryptpad/build/nix.lock/node-packages.nix b/cluster/prod/app/cryptpad/build/nix.lock/node-packages.nix
new file mode 100644
index 0000000..e60d27a
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/nix.lock/node-packages.nix
@@ -0,0 +1,756 @@
+# This file has been generated by node2nix 1.9.0. Do not edit!
+
+{nodeEnv, fetchurl, fetchgit, nix-gitignore, stdenv, lib, globalBuildInputs ? []}:
+
+let
+ sources = {
+ "@mcrowe/minibloom-0.2.0" = {
+ name = "_at_mcrowe_slash_minibloom";
+ packageName = "@mcrowe/minibloom";
+ version = "0.2.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/@mcrowe/minibloom/-/minibloom-0.2.0.tgz";
+ sha1 = "1bed96aec18388198da37443899b2c3ff5948053";
+ };
+ };
+ "accepts-1.3.8" = {
+ name = "accepts";
+ packageName = "accepts";
+ version = "1.3.8";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz";
+ sha512 = "PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==";
+ };
+ };
+ "array-flatten-1.1.1" = {
+ name = "array-flatten";
+ packageName = "array-flatten";
+ version = "1.1.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz";
+ sha1 = "9a5f699051b1e7073328f2a008968b64ea2955d2";
+ };
+ };
+ "async-limiter-1.0.1" = {
+ name = "async-limiter";
+ packageName = "async-limiter";
+ version = "1.0.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz";
+ sha512 = "csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==";
+ };
+ };
+ "body-parser-1.18.3" = {
+ name = "body-parser";
+ packageName = "body-parser";
+ version = "1.18.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz";
+ sha1 = "5b292198ffdd553b3a0f20ded0592b956955c8b4";
+ };
+ };
+ "bytes-3.0.0" = {
+ name = "bytes";
+ packageName = "bytes";
+ version = "3.0.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz";
+ sha1 = "d32815404d689699f85a4ea4fa8755dd13a96048";
+ };
+ };
+ "chainpad-crypto-0.2.7" = {
+ name = "chainpad-crypto";
+ packageName = "chainpad-crypto";
+ version = "0.2.7";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.7.tgz";
+ sha512 = "H2FfFmMwWw4i8XeGVjKUNEmgOnJohlAvc5IpnVnHqCDm6axntpZ15rv9hV70uhzDrmFhlAPW8MoY4roe5PhUyA==";
+ };
+ };
+ "chainpad-server-5.1.0" = {
+ name = "chainpad-server";
+ packageName = "chainpad-server";
+ version = "5.1.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.1.0.tgz";
+ sha512 = "BdjgOOLTXXo1EjQ7lURDe7oqsqfQISNvwhILfp3K3diY2K1hxpPLbjYzOSgxNOTADeOAff0xnInR5eUCESVWaQ==";
+ };
+ };
+ "content-disposition-0.5.2" = {
+ name = "content-disposition";
+ packageName = "content-disposition";
+ version = "0.5.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz";
+ sha1 = "0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4";
+ };
+ };
+ "content-type-1.0.4" = {
+ name = "content-type";
+ packageName = "content-type";
+ version = "1.0.4";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz";
+ sha512 = "hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==";
+ };
+ };
+ "cookie-0.3.1" = {
+ name = "cookie";
+ packageName = "cookie";
+ version = "0.3.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz";
+ sha1 = "e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb";
+ };
+ };
+ "cookie-signature-1.0.6" = {
+ name = "cookie-signature";
+ packageName = "cookie-signature";
+ version = "1.0.6";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz";
+ sha1 = "e303a882b342cc3ee8ca513a79999734dab3ae2c";
+ };
+ };
+ "debug-2.6.9" = {
+ name = "debug";
+ packageName = "debug";
+ version = "2.6.9";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz";
+ sha512 = "bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==";
+ };
+ };
+ "depd-1.1.2" = {
+ name = "depd";
+ packageName = "depd";
+ version = "1.1.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz";
+ sha1 = "9bcd52e14c097763e749b274c4346ed2e560b5a9";
+ };
+ };
+ "destroy-1.0.4" = {
+ name = "destroy";
+ packageName = "destroy";
+ version = "1.0.4";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz";
+ sha1 = "978857442c44749e4206613e37946205826abd80";
+ };
+ };
+ "ee-first-1.1.1" = {
+ name = "ee-first";
+ packageName = "ee-first";
+ version = "1.1.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz";
+ sha1 = "590c61156b0ae2f4f0255732a158b266bc56b21d";
+ };
+ };
+ "encodeurl-1.0.2" = {
+ name = "encodeurl";
+ packageName = "encodeurl";
+ version = "1.0.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz";
+ sha1 = "ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59";
+ };
+ };
+ "escape-html-1.0.3" = {
+ name = "escape-html";
+ packageName = "escape-html";
+ version = "1.0.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz";
+ sha1 = "0258eae4d3d0c0974de1c169188ef0051d1d1988";
+ };
+ };
+ "etag-1.8.1" = {
+ name = "etag";
+ packageName = "etag";
+ version = "1.8.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz";
+ sha1 = "41ae2eeb65efa62268aebfea83ac7d79299b0887";
+ };
+ };
+ "express-4.16.4" = {
+ name = "express";
+ packageName = "express";
+ version = "4.16.4";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/express/-/express-4.16.4.tgz";
+ sha512 = "j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==";
+ };
+ };
+ "finalhandler-1.1.1" = {
+ name = "finalhandler";
+ packageName = "finalhandler";
+ version = "1.1.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz";
+ sha512 = "Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==";
+ };
+ };
+ "forwarded-0.2.0" = {
+ name = "forwarded";
+ packageName = "forwarded";
+ version = "0.2.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz";
+ sha512 = "buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==";
+ };
+ };
+ "fresh-0.5.2" = {
+ name = "fresh";
+ packageName = "fresh";
+ version = "0.5.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz";
+ sha1 = "3d8cadd90d976569fa835ab1f8e4b23a105605a7";
+ };
+ };
+ "fs-extra-7.0.1" = {
+ name = "fs-extra";
+ packageName = "fs-extra";
+ version = "7.0.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz";
+ sha512 = "YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==";
+ };
+ };
+ "gar-1.0.4" = {
+ name = "gar";
+ packageName = "gar";
+ version = "1.0.4";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz";
+ sha512 = "w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w==";
+ };
+ };
+ "get-folder-size-2.0.1" = {
+ name = "get-folder-size";
+ packageName = "get-folder-size";
+ version = "2.0.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz";
+ sha512 = "+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==";
+ };
+ };
+ "graceful-fs-4.2.10" = {
+ name = "graceful-fs";
+ packageName = "graceful-fs";
+ version = "4.2.10";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz";
+ sha512 = "9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==";
+ };
+ };
+ "http-errors-1.6.3" = {
+ name = "http-errors";
+ packageName = "http-errors";
+ version = "1.6.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz";
+ sha1 = "8b55680bb4be283a0b5bf4ea2e38580be1d9320d";
+ };
+ };
+ "iconv-lite-0.4.23" = {
+ name = "iconv-lite";
+ packageName = "iconv-lite";
+ version = "0.4.23";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz";
+ sha512 = "neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==";
+ };
+ };
+ "inherits-2.0.3" = {
+ name = "inherits";
+ packageName = "inherits";
+ version = "2.0.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz";
+ sha1 = "633c2c83e3da42a502f52466022480f4208261de";
+ };
+ };
+ "ipaddr.js-1.9.1" = {
+ name = "ipaddr.js";
+ packageName = "ipaddr.js";
+ version = "1.9.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz";
+ sha512 = "0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==";
+ };
+ };
+ "jsonfile-4.0.0" = {
+ name = "jsonfile";
+ packageName = "jsonfile";
+ version = "4.0.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz";
+ sha1 = "8771aae0799b64076b76640fca058f9c10e33ecb";
+ };
+ };
+ "lex-1.7.9" = {
+ name = "lex";
+ packageName = "lex";
+ version = "1.7.9";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/lex/-/lex-1.7.9.tgz";
+ sha1 = "5d5636ccef574348362938b79a47f0eed8ed0d43";
+ };
+ };
+ "looper-3.0.0" = {
+ name = "looper";
+ packageName = "looper";
+ version = "3.0.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/looper/-/looper-3.0.0.tgz";
+ sha1 = "2efa54c3b1cbaba9b94aee2e5914b0be57fbb749";
+ };
+ };
+ "media-typer-0.3.0" = {
+ name = "media-typer";
+ packageName = "media-typer";
+ version = "0.3.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz";
+ sha1 = "8710d7af0aa626f8fffa1ce00168545263255748";
+ };
+ };
+ "merge-descriptors-1.0.1" = {
+ name = "merge-descriptors";
+ packageName = "merge-descriptors";
+ version = "1.0.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz";
+ sha1 = "b00aaa556dd8b44568150ec9d1b953f3f90cbb61";
+ };
+ };
+ "methods-1.1.2" = {
+ name = "methods";
+ packageName = "methods";
+ version = "1.1.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz";
+ sha1 = "5529a4d67654134edcc5266656835b0f851afcee";
+ };
+ };
+ "mime-1.4.1" = {
+ name = "mime";
+ packageName = "mime";
+ version = "1.4.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz";
+ sha512 = "KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==";
+ };
+ };
+ "mime-db-1.52.0" = {
+ name = "mime-db";
+ packageName = "mime-db";
+ version = "1.52.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz";
+ sha512 = "sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==";
+ };
+ };
+ "mime-types-2.1.35" = {
+ name = "mime-types";
+ packageName = "mime-types";
+ version = "2.1.35";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz";
+ sha512 = "ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==";
+ };
+ };
+ "ms-2.0.0" = {
+ name = "ms";
+ packageName = "ms";
+ version = "2.0.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz";
+ sha1 = "5608aeadfc00be6c2901df5f9861788de0d597c8";
+ };
+ };
+ "negotiator-0.6.3" = {
+ name = "negotiator";
+ packageName = "negotiator";
+ version = "0.6.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz";
+ sha512 = "+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==";
+ };
+ };
+ "netflux-websocket-0.1.21" = {
+ name = "netflux-websocket";
+ packageName = "netflux-websocket";
+ version = "0.1.21";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-0.1.21.tgz";
+ sha512 = "Zjl5lefg8urC0a0T7YCPGiUgRsISZBsTZl1STylmQz8Bq4ohcZ8cP3r6VoCpeVcvJ1Y/e3ZCXPxndWlNP9Jfug==";
+ };
+ };
+ "nthen-0.1.8" = {
+ name = "nthen";
+ packageName = "nthen";
+ version = "0.1.8";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/nthen/-/nthen-0.1.8.tgz";
+ sha512 = "Oh2CwIbhj+wUT94lQV7LKmmgw3UYAGGd8oLIqp6btQN3Bz3PuWp4BuvtUo35H3rqDknjPfKx5P6mt7v+aJNjcw==";
+ };
+ };
+ "on-finished-2.3.0" = {
+ name = "on-finished";
+ packageName = "on-finished";
+ version = "2.3.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz";
+ sha1 = "20f1336481b083cd75337992a16971aa2d906947";
+ };
+ };
+ "parseurl-1.3.3" = {
+ name = "parseurl";
+ packageName = "parseurl";
+ version = "1.3.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz";
+ sha512 = "CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==";
+ };
+ };
+ "path-to-regexp-0.1.7" = {
+ name = "path-to-regexp";
+ packageName = "path-to-regexp";
+ version = "0.1.7";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz";
+ sha1 = "df604178005f522f15eb4490e7247a1bfaa67f8c";
+ };
+ };
+ "proxy-addr-2.0.7" = {
+ name = "proxy-addr";
+ packageName = "proxy-addr";
+ version = "2.0.7";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz";
+ sha512 = "llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==";
+ };
+ };
+ "pull-stream-3.6.14" = {
+ name = "pull-stream";
+ packageName = "pull-stream";
+ version = "3.6.14";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/pull-stream/-/pull-stream-3.6.14.tgz";
+ sha512 = "KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew==";
+ };
+ };
+ "qs-6.5.2" = {
+ name = "qs";
+ packageName = "qs";
+ version = "6.5.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz";
+ sha512 = "N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==";
+ };
+ };
+ "range-parser-1.2.1" = {
+ name = "range-parser";
+ packageName = "range-parser";
+ version = "1.2.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz";
+ sha512 = "Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==";
+ };
+ };
+ "raw-body-2.3.3" = {
+ name = "raw-body";
+ packageName = "raw-body";
+ version = "2.3.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz";
+ sha512 = "9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==";
+ };
+ };
+ "safe-buffer-5.1.2" = {
+ name = "safe-buffer";
+ packageName = "safe-buffer";
+ version = "5.1.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz";
+ sha512 = "Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==";
+ };
+ };
+ "safer-buffer-2.1.2" = {
+ name = "safer-buffer";
+ packageName = "safer-buffer";
+ version = "2.1.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz";
+ sha512 = "YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==";
+ };
+ };
+ "saferphore-0.0.1" = {
+ name = "saferphore";
+ packageName = "saferphore";
+ version = "0.0.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/saferphore/-/saferphore-0.0.1.tgz";
+ sha1 = "cc962eda4e2b2452e6437fd32dcfb6f69ef2ea63";
+ };
+ };
+ "send-0.16.2" = {
+ name = "send";
+ packageName = "send";
+ version = "0.16.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/send/-/send-0.16.2.tgz";
+ sha512 = "E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==";
+ };
+ };
+ "serve-static-1.13.2" = {
+ name = "serve-static";
+ packageName = "serve-static";
+ version = "1.13.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz";
+ sha512 = "p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==";
+ };
+ };
+ "setprototypeof-1.1.0" = {
+ name = "setprototypeof";
+ packageName = "setprototypeof";
+ version = "1.1.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz";
+ sha512 = "BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==";
+ };
+ };
+ "sortify-1.0.4" = {
+ name = "sortify";
+ packageName = "sortify";
+ version = "1.0.4";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/sortify/-/sortify-1.0.4.tgz";
+ sha1 = "f0178687c83231be8a34fc0ec5462ea957b60284";
+ };
+ };
+ "statuses-1.4.0" = {
+ name = "statuses";
+ packageName = "statuses";
+ version = "1.4.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz";
+ sha512 = "zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==";
+ };
+ };
+ "stream-to-pull-stream-1.7.3" = {
+ name = "stream-to-pull-stream";
+ packageName = "stream-to-pull-stream";
+ version = "1.7.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz";
+ sha512 = "6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg==";
+ };
+ };
+ "tiny-each-async-2.0.3" = {
+ name = "tiny-each-async";
+ packageName = "tiny-each-async";
+ version = "2.0.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz";
+ sha1 = "8ebbbfd6d6295f1370003fbb37162afe5a0a51d1";
+ };
+ };
+ "tweetnacl-0.12.2" = {
+ name = "tweetnacl";
+ packageName = "tweetnacl";
+ version = "0.12.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.12.2.tgz";
+ sha1 = "bd59f890507856fb0a1136acc3a8b44547e29ddb";
+ };
+ };
+ "type-is-1.6.18" = {
+ name = "type-is";
+ packageName = "type-is";
+ version = "1.6.18";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz";
+ sha512 = "TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==";
+ };
+ };
+ "ulimit-0.0.2" = {
+ name = "ulimit";
+ packageName = "ulimit";
+ version = "0.0.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/ulimit/-/ulimit-0.0.2.tgz";
+ sha1 = "2b51f9dc8381ae4102636cec5eb338c2630588a0";
+ };
+ };
+ "ultron-1.1.1" = {
+ name = "ultron";
+ packageName = "ultron";
+ version = "1.1.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz";
+ sha512 = "UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==";
+ };
+ };
+ "universalify-0.1.2" = {
+ name = "universalify";
+ packageName = "universalify";
+ version = "0.1.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz";
+ sha512 = "rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==";
+ };
+ };
+ "unpipe-1.0.0" = {
+ name = "unpipe";
+ packageName = "unpipe";
+ version = "1.0.0";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz";
+ sha1 = "b2bf4ee8514aae6165b4817829d21b2ef49904ec";
+ };
+ };
+ "utils-merge-1.0.1" = {
+ name = "utils-merge";
+ packageName = "utils-merge";
+ version = "1.0.1";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz";
+ sha1 = "9f95710f50a267947b2ccc124741c1028427e713";
+ };
+ };
+ "vary-1.1.2" = {
+ name = "vary";
+ packageName = "vary";
+ version = "1.1.2";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz";
+ sha1 = "2299f02c6ded30d4a5961b0b9f74524a18f634fc";
+ };
+ };
+ "ws-3.3.3" = {
+ name = "ws";
+ packageName = "ws";
+ version = "3.3.3";
+ src = fetchurl {
+ url = "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz";
+ sha512 = "nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==";
+ };
+ };
+ };
+ args = {
+ name = "cryptpad";
+ packageName = "cryptpad";
+ version = "4.14.1";
+ src = ./.;
+ dependencies = [
+ sources."@mcrowe/minibloom-0.2.0"
+ sources."accepts-1.3.8"
+ sources."array-flatten-1.1.1"
+ sources."async-limiter-1.0.1"
+ sources."body-parser-1.18.3"
+ sources."bytes-3.0.0"
+ sources."chainpad-crypto-0.2.7"
+ sources."chainpad-server-5.1.0"
+ sources."content-disposition-0.5.2"
+ sources."content-type-1.0.4"
+ sources."cookie-0.3.1"
+ sources."cookie-signature-1.0.6"
+ sources."debug-2.6.9"
+ sources."depd-1.1.2"
+ sources."destroy-1.0.4"
+ sources."ee-first-1.1.1"
+ sources."encodeurl-1.0.2"
+ sources."escape-html-1.0.3"
+ sources."etag-1.8.1"
+ sources."express-4.16.4"
+ sources."finalhandler-1.1.1"
+ sources."forwarded-0.2.0"
+ sources."fresh-0.5.2"
+ sources."fs-extra-7.0.1"
+ sources."gar-1.0.4"
+ sources."get-folder-size-2.0.1"
+ sources."graceful-fs-4.2.10"
+ sources."http-errors-1.6.3"
+ sources."iconv-lite-0.4.23"
+ sources."inherits-2.0.3"
+ sources."ipaddr.js-1.9.1"
+ sources."jsonfile-4.0.0"
+ sources."lex-1.7.9"
+ sources."looper-3.0.0"
+ sources."media-typer-0.3.0"
+ sources."merge-descriptors-1.0.1"
+ sources."methods-1.1.2"
+ sources."mime-db-1.52.0"
+ sources."mime-types-2.1.35"
+ sources."ms-2.0.0"
+ sources."negotiator-0.6.3"
+ sources."netflux-websocket-0.1.21"
+ sources."nthen-0.1.8"
+ sources."on-finished-2.3.0"
+ sources."parseurl-1.3.3"
+ sources."path-to-regexp-0.1.7"
+ sources."proxy-addr-2.0.7"
+ sources."pull-stream-3.6.14"
+ sources."qs-6.5.2"
+ sources."range-parser-1.2.1"
+ sources."raw-body-2.3.3"
+ sources."safe-buffer-5.1.2"
+ sources."safer-buffer-2.1.2"
+ sources."saferphore-0.0.1"
+ (sources."send-0.16.2" // {
+ dependencies = [
+ sources."mime-1.4.1"
+ ];
+ })
+ sources."serve-static-1.13.2"
+ sources."setprototypeof-1.1.0"
+ sources."sortify-1.0.4"
+ sources."statuses-1.4.0"
+ sources."stream-to-pull-stream-1.7.3"
+ sources."tiny-each-async-2.0.3"
+ sources."tweetnacl-0.12.2"
+ sources."type-is-1.6.18"
+ sources."ulimit-0.0.2"
+ sources."ultron-1.1.1"
+ sources."universalify-0.1.2"
+ sources."unpipe-1.0.0"
+ sources."utils-merge-1.0.1"
+ sources."vary-1.1.2"
+ sources."ws-3.3.3"
+ ];
+ buildInputs = globalBuildInputs;
+ meta = {
+ description = "realtime collaborative visual editor with zero knowlege server";
+ license = "AGPL-3.0+";
+ };
+ production = true;
+ bypassCache = true;
+ reconstructLock = false;
+ };
+in
+{
+ args = args;
+ sources = sources;
+ tarball = nodeEnv.buildNodeSourceDist args;
+ package = nodeEnv.buildNodePackage args;
+ shell = nodeEnv.buildNodeShell args;
+ nodeDependencies = nodeEnv.buildNodeDependencies (lib.overrideExisting args {
+ src = stdenv.mkDerivation {
+ name = args.name + "-package-json";
+ src = nix-gitignore.gitignoreSourcePure [
+ "*"
+ "!package.json"
+ "!package-lock.json"
+ ] args.src;
+ dontBuild = true;
+ installPhase = "mkdir -p $out; cp -r ./* $out;";
+ };
+ });
+}
diff --git a/cluster/prod/app/cryptpad/build/nix.lock/npm.nix b/cluster/prod/app/cryptpad/build/nix.lock/npm.nix
new file mode 100644
index 0000000..53bdef1
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/nix.lock/npm.nix
@@ -0,0 +1,17 @@
+# This file has been generated by node2nix 1.9.0. Do not edit!
+
+{pkgs ? import <nixpkgs> {
+ inherit system;
+ }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs-12_x"}:
+
+let
+ nodeEnv = import ./node-env.nix {
+ inherit (pkgs) stdenv lib python2 runCommand writeTextFile writeShellScript;
+ inherit pkgs nodejs;
+ libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null;
+ };
+in
+import ./node-packages.nix {
+ inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit;
+ inherit nodeEnv;
+}
diff --git a/cluster/prod/app/cryptpad/build/nix.lock/package-lock.json b/cluster/prod/app/cryptpad/build/nix.lock/package-lock.json
new file mode 100644
index 0000000..3385f7e
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/nix.lock/package-lock.json
@@ -0,0 +1,6618 @@
+{
+ "name": "cryptpad",
+ "version": "4.14.1",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "cryptpad",
+ "version": "4.14.1",
+ "license": "AGPL-3.0+",
+ "dependencies": {
+ "@mcrowe/minibloom": "^0.2.0",
+ "chainpad-crypto": "^0.2.5",
+ "chainpad-server": "^5.1.0",
+ "express": "~4.16.0",
+ "fs-extra": "^7.0.0",
+ "get-folder-size": "^2.0.1",
+ "netflux-websocket": "^0.1.20",
+ "nthen": "0.1.8",
+ "pull-stream": "^3.6.1",
+ "saferphore": "0.0.1",
+ "sortify": "^1.0.4",
+ "stream-to-pull-stream": "^1.7.2",
+ "tweetnacl": "~0.12.2",
+ "ulimit": "0.0.2",
+ "ws": "^3.3.1"
+ },
+ "devDependencies": {
+ "jshint": "^2.13.4",
+ "less": "3.7.1",
+ "lesshint": "6.3.7",
+ "selenium-webdriver": "^3.6.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/cryptpad"
+ }
+ },
+ "node_modules/@mcrowe/minibloom": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@mcrowe/minibloom/-/minibloom-0.2.0.tgz",
+ "integrity": "sha1-G+2WrsGDiBmNo3RDiZssP/WUgFM="
+ },
+ "node_modules/@mrmlnc/readdir-enhanced": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
+ "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==",
+ "dev": true,
+ "dependencies": {
+ "call-me-maybe": "^1.0.1",
+ "glob-to-regexp": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
+ "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "dev": true,
+ "dependencies": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "node_modules/@types/node": {
+ "version": "17.0.23",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
+ "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "node_modules/array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "dependencies": {
+ "array-uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true,
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "dependencies": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/base/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/body-parser": {
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
+ "dependencies": {
+ "bytes": "3.0.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "~1.6.3",
+ "iconv-lite": "0.4.23",
+ "on-finished": "~2.3.0",
+ "qs": "6.5.2",
+ "raw-body": "2.3.3",
+ "type-is": "~1.6.16"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "dependencies": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/braces/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/braces/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "dependencies": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/call-me-maybe": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
+ "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
+ "dev": true
+ },
+ "node_modules/caller-callsite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/caller-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "dev": true,
+ "dependencies": {
+ "caller-callsite": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/chainpad-crypto": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.7.tgz",
+ "integrity": "sha512-H2FfFmMwWw4i8XeGVjKUNEmgOnJohlAvc5IpnVnHqCDm6axntpZ15rv9hV70uhzDrmFhlAPW8MoY4roe5PhUyA==",
+ "dependencies": {
+ "tweetnacl": "~0.12.2"
+ }
+ },
+ "node_modules/chainpad-server": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.1.0.tgz",
+ "integrity": "sha512-BdjgOOLTXXo1EjQ7lURDe7oqsqfQISNvwhILfp3K3diY2K1hxpPLbjYzOSgxNOTADeOAff0xnInR5eUCESVWaQ==",
+ "dependencies": {
+ "nthen": "0.1.8",
+ "pull-stream": "^3.6.9",
+ "stream-to-pull-stream": "^1.7.3",
+ "ws": "^3.3.1"
+ }
+ },
+ "node_modules/class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+ "dev": true,
+ "dependencies": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=0.2.5"
+ }
+ },
+ "node_modules/collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "dependencies": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "node_modules/console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "dependencies": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "node_modules/copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "node_modules/cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "dev": true,
+ "dependencies": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
+ "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
+ "dev": true,
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "node_modules/dir-glob": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+ "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+ "dev": true,
+ "dependencies": {
+ "path-type": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+ "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "entities": "^2.0.0"
+ }
+ },
+ "node_modules/dom-serializer/node_modules/domelementtype": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+ "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ]
+ },
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "node_modules/domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "dev": true,
+ "dependencies": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
+ "dev": true
+ },
+ "node_modules/errno": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "prr": "~1.0.1"
+ },
+ "bin": {
+ "errno": "cli.js"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "dependencies": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.16.4",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
+ "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
+ "dependencies": {
+ "accepts": "~1.3.5",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.18.3",
+ "content-disposition": "0.5.2",
+ "content-type": "~1.0.4",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.1.1",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.4",
+ "qs": "6.5.2",
+ "range-parser": "~1.2.0",
+ "safe-buffer": "5.1.2",
+ "send": "0.16.2",
+ "serve-static": "1.13.2",
+ "setprototypeof": "1.1.0",
+ "statuses": "~1.4.0",
+ "type-is": "~1.6.16",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "dependencies": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "optional": true
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/fast-glob": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
+ "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==",
+ "dev": true,
+ "dependencies": {
+ "@mrmlnc/readdir-enhanced": "^2.2.1",
+ "@nodelib/fs.stat": "^1.1.2",
+ "glob-parent": "^3.1.0",
+ "is-glob": "^4.0.0",
+ "merge2": "^1.2.3",
+ "micromatch": "^3.1.10"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fill-range/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fill-range/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.4.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/flatten": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
+ "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==",
+ "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.",
+ "dev": true
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "dependencies": {
+ "map-cache": "^0.2.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "node_modules/gar": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz",
+ "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w=="
+ },
+ "node_modules/get-folder-size": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz",
+ "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==",
+ "dependencies": {
+ "gar": "^1.0.4",
+ "tiny-each-async": "2.0.3"
+ },
+ "bin": {
+ "get-folder-size": "bin/get-folder-size"
+ }
+ },
+ "node_modules/get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ }
+ },
+ "node_modules/glob-parent/node_modules/is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
+ "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
+ "dev": true
+ },
+ "node_modules/globby": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz",
+ "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==",
+ "dev": true,
+ "dependencies": {
+ "@types/glob": "^7.1.1",
+ "array-union": "^1.0.2",
+ "dir-glob": "^2.2.2",
+ "fast-glob": "^2.2.6",
+ "glob": "^7.1.3",
+ "ignore": "^4.0.3",
+ "pify": "^4.0.1",
+ "slash": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ },
+ "node_modules/har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "deprecated": "this library is no longer supported",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+ "dev": true,
+ "dependencies": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.3.7"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
+ "dev": true
+ },
+ "node_modules/import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "dev": true,
+ "dependencies": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+ "dev": true
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "node_modules/is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/jshint": {
+ "version": "2.13.4",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz",
+ "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==",
+ "dev": true,
+ "dependencies": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "~4.17.21",
+ "minimatch": "~3.0.2",
+ "strip-json-comments": "1.0.x"
+ },
+ "bin": {
+ "jshint": "bin/jshint"
+ }
+ },
+ "node_modules/json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/jszip": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz",
+ "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==",
+ "dev": true,
+ "dependencies": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "set-immediate-shim": "~1.0.1"
+ }
+ },
+ "node_modules/jszip/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "node_modules/jszip/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/jszip/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/less": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/less/-/less-3.7.1.tgz",
+ "integrity": "sha512-Cmf5XJlzNklkBC8eAa+Ef16AHUBAkApHNAw3x9Vmn84h2BvGrri5Id7kf6H1n6SN74Fc0WdHIRPlFMxsl0eJkA==",
+ "dev": true,
+ "bin": {
+ "lessc": "bin/lessc"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "optionalDependencies": {
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "mime": "^1.4.1",
+ "mkdirp": "^0.5.0",
+ "promise": "^7.1.1",
+ "request": "^2.83.0",
+ "source-map": "~0.6.0"
+ }
+ },
+ "node_modules/lesshint": {
+ "version": "6.3.7",
+ "resolved": "https://registry.npmjs.org/lesshint/-/lesshint-6.3.7.tgz",
+ "integrity": "sha512-ontd3g1seYMgeTusLTrm5NMYllLrQs4QFPkqh6GCut2CSG2csMpFbLfd/GJTQIlJXX8ixPQ4i27NeElMuftGaQ==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.8.0",
+ "cosmiconfig": "^5.0.1",
+ "globby": "^9.1.0",
+ "lodash.merge": "^4.0.1",
+ "lodash.orderby": "^4.6.0",
+ "postcss": "^7.0.14",
+ "postcss-less": "^3.1.1",
+ "postcss-selector-parser": "^5.0.0",
+ "postcss-values-parser": "^2.0.0",
+ "strip-json-comments": "^3.0.0"
+ },
+ "bin": {
+ "lesshint": "bin/lesshint"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lesshint/node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lex": {
+ "version": "1.7.9",
+ "resolved": "https://registry.npmjs.org/lex/-/lex-1.7.9.tgz",
+ "integrity": "sha1-XVY2zO9XQ0g2KTi3mkfw7tjtDUM="
+ },
+ "node_modules/lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "dependencies": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lodash.orderby": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.orderby/-/lodash.orderby-4.6.0.tgz",
+ "integrity": "sha1-5pfwTOXXhSL1TZM4syuBozk+TrM=",
+ "dev": true
+ },
+ "node_modules/looper": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/looper/-/looper-3.0.0.tgz",
+ "integrity": "sha1-LvpUw7HLq6m5Su4uWRSwvlf7t0k="
+ },
+ "node_modules/map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "dependencies": {
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+ "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "dependencies": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "node_modules/nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/netflux-websocket": {
+ "version": "0.1.21",
+ "resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-0.1.21.tgz",
+ "integrity": "sha512-Zjl5lefg8urC0a0T7YCPGiUgRsISZBsTZl1STylmQz8Bq4ohcZ8cP3r6VoCpeVcvJ1Y/e3ZCXPxndWlNP9Jfug=="
+ },
+ "node_modules/nthen": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/nthen/-/nthen-0.1.8.tgz",
+ "integrity": "sha512-Oh2CwIbhj+wUT94lQV7LKmmgw3UYAGGd8oLIqp6btQN3Bz3PuWp4BuvtUo35H3rqDknjPfKx5P6mt7v+aJNjcw=="
+ },
+ "node_modules/oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "dependencies": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "node_modules/parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "dependencies": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "node_modules/path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/path-type/node_modules/pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/picocolors": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+ "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+ "dev": true
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "7.0.39",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+ "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+ "dev": true,
+ "dependencies": {
+ "picocolors": "^0.2.1",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ }
+ },
+ "node_modules/postcss-less": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz",
+ "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==",
+ "dev": true,
+ "dependencies": {
+ "postcss": "^7.0.14"
+ },
+ "engines": {
+ "node": ">=6.14.4"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
+ "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^2.0.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-values-parser": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz",
+ "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==",
+ "dev": true,
+ "dependencies": {
+ "flatten": "^1.0.2",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=6.14.4"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "node_modules/promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "asap": "~2.0.3"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/pull-stream": {
+ "version": "3.6.14",
+ "resolved": "https://registry.npmjs.org/pull-stream/-/pull-stream-3.6.14.tgz",
+ "integrity": "sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew=="
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+ "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+ "dependencies": {
+ "bytes": "3.0.0",
+ "http-errors": "1.6.3",
+ "iconv-lite": "0.4.23",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/repeat-element": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "deprecated": "https://github.com/lydell/resolve-url#deprecated",
+ "dev": true
+ },
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "node_modules/safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "dependencies": {
+ "ret": "~0.1.10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/saferphore": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/saferphore/-/saferphore-0.0.1.tgz",
+ "integrity": "sha1-zJYu2k4rJFLmQ3/TLc+29p7y6mM="
+ },
+ "node_modules/sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
+ "node_modules/selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "dependencies": {
+ "jszip": "^3.1.3",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
+ },
+ "engines": {
+ "node": ">= 6.9.0"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/mime": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
+ "bin": {
+ "mime": "cli.js"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+ "dependencies": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.2",
+ "send": "0.16.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-immediate-shim": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ },
+ "node_modules/slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "dependencies": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sortify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/sortify/-/sortify-1.0.4.tgz",
+ "integrity": "sha1-8BeGh8gyMb6KNPwOxUYuqVe2AoQ=",
+ "dependencies": {
+ "lex": "^1.7.9"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
+ "dev": true,
+ "dependencies": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "node_modules/source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "deprecated": "See https://github.com/lydell/source-map-url#deprecated",
+ "dev": true
+ },
+ "node_modules/split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "node_modules/sshpk": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+ "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sshpk/node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/stream-to-pull-stream": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz",
+ "integrity": "sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg==",
+ "dependencies": {
+ "looper": "^3.0.0",
+ "pull-stream": "^3.2.3"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "node_modules/strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+ "dev": true,
+ "bin": {
+ "strip-json-comments": "cli.js"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/tiny-each-async": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz",
+ "integrity": "sha1-jru/1tYpXxNwAD+7NxYq/loKUdE="
+ },
+ "node_modules/tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
+ "dev": true,
+ "dependencies": {
+ "os-tmpdir": "~1.0.1"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-object-path/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.12.2.tgz",
+ "integrity": "sha1-vVn4kFB4VvsKETasw6i0RUfinds="
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ulimit": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/ulimit/-/ulimit-0.0.2.tgz",
+ "integrity": "sha1-K1H53IOBrkECY2zsXrM4wmMFiKA=",
+ "engines": {
+ "node": ">= v0.4"
+ }
+ },
+ "node_modules/ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
+ },
+ "node_modules/union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/union-value/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+ "dev": true
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "dependencies": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "dependencies": {
+ "isarray": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "deprecated": "Please see https://github.com/lydell/urix#deprecated",
+ "dev": true
+ },
+ "node_modules/use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "optional": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "optional": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/verror/node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "node_modules/ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "dependencies": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ }
+ },
+ "node_modules/xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "dev": true,
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ }
+ },
+ "dependencies": {
+ "@mcrowe/minibloom": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@mcrowe/minibloom/-/minibloom-0.2.0.tgz",
+ "integrity": "sha1-G+2WrsGDiBmNo3RDiZssP/WUgFM="
+ },
+ "@mrmlnc/readdir-enhanced": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
+ "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==",
+ "dev": true,
+ "requires": {
+ "call-me-maybe": "^1.0.1",
+ "glob-to-regexp": "^0.3.0"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
+ "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==",
+ "dev": true
+ },
+ "@types/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "dev": true,
+ "requires": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/minimatch": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+ "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "17.0.23",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz",
+ "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "dev": true,
+ "optional": true
+ },
+ "asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true,
+ "optional": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true,
+ "optional": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true,
+ "optional": true
+ },
+ "aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true,
+ "optional": true
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ },
+ "dependencies": {
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "body-parser": {
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
+ "requires": {
+ "bytes": "3.0.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "~1.6.3",
+ "iconv-lite": "0.4.23",
+ "on-finished": "~2.3.0",
+ "qs": "6.5.2",
+ "raw-body": "2.3.3",
+ "type-is": "~1.6.16"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ }
+ }
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "call-me-maybe": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
+ "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
+ "dev": true
+ },
+ "caller-callsite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "dev": true,
+ "requires": {
+ "callsites": "^2.0.0"
+ }
+ },
+ "caller-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "dev": true,
+ "requires": {
+ "caller-callsite": "^2.0.0"
+ }
+ },
+ "callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true,
+ "optional": true
+ },
+ "chainpad-crypto": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/chainpad-crypto/-/chainpad-crypto-0.2.7.tgz",
+ "integrity": "sha512-H2FfFmMwWw4i8XeGVjKUNEmgOnJohlAvc5IpnVnHqCDm6axntpZ15rv9hV70uhzDrmFhlAPW8MoY4roe5PhUyA==",
+ "requires": {
+ "tweetnacl": "~0.12.2"
+ }
+ },
+ "chainpad-server": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/chainpad-server/-/chainpad-server-5.1.0.tgz",
+ "integrity": "sha512-BdjgOOLTXXo1EjQ7lURDe7oqsqfQISNvwhILfp3K3diY2K1hxpPLbjYzOSgxNOTADeOAff0xnInR5eUCESVWaQ==",
+ "requires": {
+ "nthen": "0.1.8",
+ "pull-stream": "^3.6.9",
+ "stream-to-pull-stream": "^1.7.3",
+ "ws": "^3.3.1"
+ }
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
+ "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
+ "dev": true,
+ "requires": {
+ "exit": "0.1.2",
+ "glob": "^7.1.1"
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "dev": true,
+ "requires": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ }
+ },
+ "cssesc": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz",
+ "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==",
+ "dev": true
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true,
+ "optional": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "dir-glob": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+ "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+ "dev": true,
+ "requires": {
+ "path-type": "^3.0.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+ "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "entities": "^2.0.0"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+ "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+ "dev": true
+ },
+ "entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true
+ }
+ }
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
+ "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "entities": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
+ "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=",
+ "dev": true
+ },
+ "errno": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "prr": "~1.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "express": {
+ "version": "4.16.4",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
+ "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
+ "requires": {
+ "accepts": "~1.3.5",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.18.3",
+ "content-disposition": "0.5.2",
+ "content-type": "~1.0.4",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.1.1",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.4",
+ "qs": "6.5.2",
+ "range-parser": "~1.2.0",
+ "safe-buffer": "5.1.2",
+ "send": "0.16.2",
+ "serve-static": "1.13.2",
+ "setprototypeof": "1.1.0",
+ "statuses": "~1.4.0",
+ "type-is": "~1.6.16",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true,
+ "optional": true
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true,
+ "optional": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "optional": true
+ },
+ "fast-glob": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
+ "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==",
+ "dev": true,
+ "requires": {
+ "@mrmlnc/readdir-enhanced": "^2.2.1",
+ "@nodelib/fs.stat": "^1.1.2",
+ "glob-parent": "^3.1.0",
+ "is-glob": "^4.0.0",
+ "merge2": "^1.2.3",
+ "micromatch": "^3.1.10"
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "optional": true
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ }
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.4.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "flatten": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz",
+ "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==",
+ "dev": true
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true,
+ "optional": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "gar": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/gar/-/gar-1.0.4.tgz",
+ "integrity": "sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w=="
+ },
+ "get-folder-size": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/get-folder-size/-/get-folder-size-2.0.1.tgz",
+ "integrity": "sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA==",
+ "requires": {
+ "gar": "^1.0.4",
+ "tiny-each-async": "2.0.3"
+ }
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "glob-to-regexp": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
+ "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
+ "dev": true
+ },
+ "globby": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz",
+ "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==",
+ "dev": true,
+ "requires": {
+ "@types/glob": "^7.1.1",
+ "array-union": "^1.0.2",
+ "dir-glob": "^2.2.2",
+ "fast-glob": "^2.2.6",
+ "glob": "^7.1.3",
+ "ignore": "^4.0.3",
+ "pify": "^4.0.1",
+ "slash": "^2.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true,
+ "optional": true
+ },
+ "har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "htmlparser2": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
+ "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1",
+ "domhandler": "2.3",
+ "domutils": "1.5",
+ "entities": "1.0",
+ "readable-stream": "1.1"
+ }
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+ "dev": true,
+ "optional": true
+ },
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "dev": true,
+ "requires": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+ "dev": true
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true,
+ "optional": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true,
+ "optional": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true,
+ "optional": true
+ },
+ "jshint": {
+ "version": "2.13.4",
+ "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.4.tgz",
+ "integrity": "sha512-HO3bosL84b2qWqI0q+kpT/OpRJwo0R4ivgmxaO848+bo10rc50SkPnrtwSFXttW0ym4np8jbJvLwk5NziB7jIw==",
+ "dev": true,
+ "requires": {
+ "cli": "~1.0.0",
+ "console-browserify": "1.1.x",
+ "exit": "0.1.x",
+ "htmlparser2": "3.8.x",
+ "lodash": "~4.17.21",
+ "minimatch": "~3.0.2",
+ "strip-json-comments": "1.0.x"
+ }
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true,
+ "optional": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "optional": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true,
+ "optional": true
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "jszip": {
+ "version": "3.9.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.9.1.tgz",
+ "integrity": "sha512-H9A60xPqJ1CuC4Ka6qxzXZeU8aNmgOeP5IFqwJbQQwtu2EUYxota3LdsiZWplF7Wgd9tkAd0mdu36nceSaPuYw==",
+ "dev": true,
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "set-immediate-shim": "~1.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "less": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/less/-/less-3.7.1.tgz",
+ "integrity": "sha512-Cmf5XJlzNklkBC8eAa+Ef16AHUBAkApHNAw3x9Vmn84h2BvGrri5Id7kf6H1n6SN74Fc0WdHIRPlFMxsl0eJkA==",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "mime": "^1.4.1",
+ "mkdirp": "^0.5.0",
+ "promise": "^7.1.1",
+ "request": "^2.83.0",
+ "source-map": "~0.6.0"
+ }
+ },
+ "lesshint": {
+ "version": "6.3.7",
+ "resolved": "https://registry.npmjs.org/lesshint/-/lesshint-6.3.7.tgz",
+ "integrity": "sha512-ontd3g1seYMgeTusLTrm5NMYllLrQs4QFPkqh6GCut2CSG2csMpFbLfd/GJTQIlJXX8ixPQ4i27NeElMuftGaQ==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.8.0",
+ "cosmiconfig": "^5.0.1",
+ "globby": "^9.1.0",
+ "lodash.merge": "^4.0.1",
+ "lodash.orderby": "^4.6.0",
+ "postcss": "^7.0.14",
+ "postcss-less": "^3.1.1",
+ "postcss-selector-parser": "^5.0.0",
+ "postcss-values-parser": "^2.0.0",
+ "strip-json-comments": "^3.0.0"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ }
+ }
+ },
+ "lex": {
+ "version": "1.7.9",
+ "resolved": "https://registry.npmjs.org/lex/-/lex-1.7.9.tgz",
+ "integrity": "sha1-XVY2zO9XQ0g2KTi3mkfw7tjtDUM="
+ },
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lodash.orderby": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.orderby/-/lodash.orderby-4.6.0.tgz",
+ "integrity": "sha1-5pfwTOXXhSL1TZM4syuBozk+TrM=",
+ "dev": true
+ },
+ "looper": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/looper/-/looper-3.0.0.tgz",
+ "integrity": "sha1-LvpUw7HLq6m5Su4uWRSwvlf7t0k="
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "optional": true
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+ "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
+ "dev": true,
+ "optional": true
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimist": "^1.2.6"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
+ },
+ "netflux-websocket": {
+ "version": "0.1.21",
+ "resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-0.1.21.tgz",
+ "integrity": "sha512-Zjl5lefg8urC0a0T7YCPGiUgRsISZBsTZl1STylmQz8Bq4ohcZ8cP3r6VoCpeVcvJ1Y/e3ZCXPxndWlNP9Jfug=="
+ },
+ "nthen": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/nthen/-/nthen-0.1.8.tgz",
+ "integrity": "sha512-Oh2CwIbhj+wUT94lQV7LKmmgw3UYAGGd8oLIqp6btQN3Bz3PuWp4BuvtUo35H3rqDknjPfKx5P6mt7v+aJNjcw=="
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "optional": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ }
+ }
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true,
+ "optional": true
+ },
+ "picocolors": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+ "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "postcss": {
+ "version": "7.0.39",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+ "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+ "dev": true,
+ "requires": {
+ "picocolors": "^0.2.1",
+ "source-map": "^0.6.1"
+ }
+ },
+ "postcss-less": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz",
+ "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.14"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz",
+ "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^2.0.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ },
+ "postcss-values-parser": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz",
+ "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==",
+ "dev": true,
+ "requires": {
+ "flatten": "^1.0.2",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "requires": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+ "dev": true,
+ "optional": true
+ },
+ "psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true,
+ "optional": true
+ },
+ "pull-stream": {
+ "version": "3.6.14",
+ "resolved": "https://registry.npmjs.org/pull-stream/-/pull-stream-3.6.14.tgz",
+ "integrity": "sha512-KIqdvpqHHaTUA2mCYcLG1ibEbu/LCKoJZsBWyv9lSYtPkJPBq8m3Hxa103xHi6D2thj5YXa0TqK3L3GUkwgnew=="
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "optional": true
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "raw-body": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+ "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+ "requires": {
+ "bytes": "3.0.0",
+ "http-errors": "1.6.3",
+ "iconv-lite": "0.4.23",
+ "unpipe": "1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "repeat-element": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "dev": true
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "saferphore": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/saferphore/-/saferphore-0.0.1.tgz",
+ "integrity": "sha1-zJYu2k4rJFLmQ3/TLc+29p7y6mM="
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
+ "selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "requires": {
+ "jszip": "^3.1.3",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
+ }
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.2",
+ "send": "0.16.2"
+ }
+ },
+ "set-immediate-shim": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ }
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "sortify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/sortify/-/sortify-1.0.4.tgz",
+ "integrity": "sha1-8BeGh8gyMb6KNPwOxUYuqVe2AoQ=",
+ "requires": {
+ "lex": "^1.7.9"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "dev": true
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+ "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "dependencies": {
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ }
+ },
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
+ },
+ "stream-to-pull-stream": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/stream-to-pull-stream/-/stream-to-pull-stream-1.7.3.tgz",
+ "integrity": "sha512-6sNyqJpr5dIOQdgNy/xcDWwDuzAsAwVzhzrWlAPAQ7Lkjx/rv0wgvxEyKwTq6FmNd5rjTrELt/CLmaSw7crMGg==",
+ "requires": {
+ "looper": "^3.0.0",
+ "pull-stream": "^3.2.3"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
+ "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=",
+ "dev": true
+ },
+ "tiny-each-async": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-each-async/-/tiny-each-async-2.0.3.tgz",
+ "integrity": "sha1-jru/1tYpXxNwAD+7NxYq/loKUdE="
+ },
+ "tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.1"
+ }
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.12.2.tgz",
+ "integrity": "sha1-vVn4kFB4VvsKETasw6i0RUfinds="
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "ulimit": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/ulimit/-/ulimit-0.0.2.tgz",
+ "integrity": "sha1-K1H53IOBrkECY2zsXrM4wmMFiKA="
+ },
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ }
+ }
+ },
+ "uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+ "dev": true
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ }
+ }
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true,
+ "optional": true
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ },
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ }
+ },
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "dev": true,
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true
+ }
+ }
+}
diff --git a/cluster/prod/app/cryptpad/build/nix.lock/package.json b/cluster/prod/app/cryptpad/build/nix.lock/package.json
new file mode 100644
index 0000000..2bca4de
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/nix.lock/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "cryptpad",
+ "description": "realtime collaborative visual editor with zero knowlege server",
+ "version": "4.14.1",
+ "license": "AGPL-3.0+",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/xwiki-labs/cryptpad.git"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/cryptpad"
+ },
+ "dependencies": {
+ "@mcrowe/minibloom": "^0.2.0",
+ "chainpad-crypto": "^0.2.5",
+ "chainpad-server": "^5.1.0",
+ "express": "~4.16.0",
+ "fs-extra": "^7.0.0",
+ "get-folder-size": "^2.0.1",
+ "netflux-websocket": "^0.1.20",
+ "nthen": "0.1.8",
+ "pull-stream": "^3.6.1",
+ "saferphore": "0.0.1",
+ "sortify": "^1.0.4",
+ "stream-to-pull-stream": "^1.7.2",
+ "tweetnacl": "~0.12.2",
+ "ulimit": "0.0.2",
+ "ws": "^3.3.1"
+ },
+ "devDependencies": {
+ "jshint": "^2.13.4",
+ "less": "3.7.1",
+ "lesshint": "6.3.7",
+ "selenium-webdriver": "^3.6.0"
+ },
+ "scripts": {
+ "start": "node server.js",
+ "dev": "DEV=1 node server.js",
+ "fresh": "FRESH=1 node server.js",
+ "offline": "FRESH=1 OFFLINE=1 node server.js",
+ "offlinedev": "DEV=1 OFFLINE=1 node server.js",
+ "package": "PACKAGE=1 node server.js",
+ "lint": "jshint --config .jshintrc --exclude-path .jshintignore . && ./node_modules/lesshint/bin/lesshint -c ./.lesshintrc ./customize.dist/src/less2/",
+ "lint:js": "jshint --config .jshintrc --exclude-path .jshintignore .",
+ "lint:server": "jshint --config .jshintrc lib",
+ "lint:less": "./node_modules/lesshint/bin/lesshint -c ./.lesshintrc ./customize.dist/src/less2/",
+ "lint:translations": "node ./scripts/translations/lint-translations.js",
+ "unused-translations": "node ./scripts/translations/unused-translations.js",
+ "test": "node scripts/TestSelenium.js",
+ "test-rpc": "cd scripts/tests && node test-rpc",
+ "template": "cd customize.dist/src && for page in ../index.html ../privacy.html ../terms.html ../contact.html ../what-is-cryptpad.html ../features.html ../../www/login/index.html ../../www/register/index.html ../../www/user/index.html;do echo $page; cp template.html $page; done;",
+ "evict-inactive": "node scripts/evict-inactive.js"
+ }
+}
diff --git a/cluster/prod/app/cryptpad/build/shell.nix b/cluster/prod/app/cryptpad/build/shell.nix
new file mode 100644
index 0000000..bf701fe
--- /dev/null
+++ b/cluster/prod/app/cryptpad/build/shell.nix
@@ -0,0 +1,31 @@
+let
+ common = import ./common.nix;
+ pkgs = import common.pkgsSrc {};
+
+ bower2nixRepo = (import common.bower2nixSrc {
+ inherit pkgs;
+ });
+ bower2nix = bower2nixRepo // {
+ package = bower2nixRepo.package.override {
+ postInstall = "tsc";
+ };
+ };
+in
+ pkgs.mkShell {
+ nativeBuildInputs = [
+ bower2nix.package
+ pkgs.nodePackages.node2nix
+ ];
+
+ shellHook = ''
+function update_lock {
+ set -exuo pipefail
+ mkdir -p nix.lock
+ ${pkgs.wget}/bin/wget https://raw.githubusercontent.com/xwiki-labs/cryptpad/${common.cryptpadCommit}/package.json -O nix.lock/package.json
+ ${pkgs.wget}/bin/wget https://raw.githubusercontent.com/xwiki-labs/cryptpad/${common.cryptpadCommit}/package-lock.json -O nix.lock/package-lock.json
+ ${pkgs.wget}/bin/wget https://raw.githubusercontent.com/xwiki-labs/cryptpad/${common.cryptpadCommit}/bower.json -O nix.lock/bower.json
+ ${bower2nix.package}/bin/bower2nix nix.lock/bower.json nix.lock/bower.nix
+ ${pkgs.nodePackages.node2nix}/bin/node2nix --input nix.lock/package.json --lock nix.lock/package-lock.json --composition nix.lock/npm.nix --node-env nix.lock/node-env.nix --output nix.lock/node-packages.nix
+}
+ '';
+ }
diff --git a/cluster/prod/app/cryptpad/config/application_config.js b/cluster/prod/app/cryptpad/config/application_config.js
new file mode 100644
index 0000000..94a613d
--- /dev/null
+++ b/cluster/prod/app/cryptpad/config/application_config.js
@@ -0,0 +1,40 @@
+/*
+ * You can override the configurable values from this file.
+ * The recommended method is to make a copy of this file (/customize.dist/application_config.js)
+ in a 'customize' directory (/customize/application_config.js).
+ * If you want to check all the configurable values, you can open the internal configuration file
+ but you should not change it directly (/common/application_config_internal.js)
+*/
+define(['/common/application_config_internal.js'], function (AppConfig) {
+ // To inform users of the support ticket panel which languages your admins speak:
+ AppConfig.supportLanguages = [ 'en', 'fr' ];
+
+ /* Select the buttons displayed on the main page to create new collaborative sessions.
+ * Removing apps from the list will prevent users from accessing them. They will instead be
+ * redirected to the drive.
+ * You should never remove the drive from this list.
+ */
+ AppConfig.availablePadTypes = ['drive', 'teams', 'doc', 'presentation', 'pad', 'kanban', 'code', 'form', 'poll', 'whiteboard',
+ 'file', 'contacts', 'slide', 'convert'];
+ // disabled: sheet
+
+ /* You can display a link to your own privacy policy in the static pages footer.
+ * Since this is different for each individual or organization there is no default value.
+ * See the comments above for a description of possible configurations.
+ */
+ AppConfig.privacy = {
+ "default": "https://deuxfleurs.fr/CGU.html",
+ };
+
+ /* You can display a link to your instances's terms of service in the static pages footer.
+ * A default is included for backwards compatibility, but we recommend replacing this
+ * with your own terms.
+ *
+ * See the comments above for a description of possible configurations.
+ */
+ AppConfig.terms = {
+ "default": "https://deuxfleurs.fr/CGU.html",
+ };
+
+ return AppConfig;
+});
diff --git a/cluster/prod/app/cryptpad/config/config.js b/cluster/prod/app/cryptpad/config/config.js
new file mode 100644
index 0000000..3ed7074
--- /dev/null
+++ b/cluster/prod/app/cryptpad/config/config.js
@@ -0,0 +1,281 @@
+/* globals module */
+
+/* DISCLAIMER:
+
+ There are two recommended methods of running a CryptPad instance:
+
+ 1. Using a standalone nodejs server without HTTPS (suitable for local development)
+ 2. Using NGINX to serve static assets and to handle HTTPS for API server's websocket traffic
+
+ We do not officially recommend or support Apache, Docker, Kubernetes, Traefik, or any other configuration.
+ Support requests for such setups should be directed to their authors.
+
+ If you're having difficulty difficulty configuring your instance
+ we suggest that you join the project's IRC/Matrix channel.
+
+ If you don't have any difficulty configuring your instance and you'd like to
+ support us for the work that went into making it pain-free we are quite happy
+ to accept donations via our opencollective page: https://opencollective.com/cryptpad
+
+*/
+module.exports = {
+/* CryptPad is designed to serve its content over two domains.
+ * Account passwords and cryptographic content is handled on the 'main' domain,
+ * while the user interface is loaded on a 'sandbox' domain
+ * which can only access information which the main domain willingly shares.
+ *
+ * In the event of an XSS vulnerability in the UI (that's bad)
+ * this system prevents attackers from gaining access to your account (that's good).
+ *
+ * Most problems with new instances are related to this system blocking access
+ * because of incorrectly configured sandboxes. If you only see a white screen
+ * when you try to load CryptPad, this is probably the cause.
+ *
+ * PLEASE READ THE FOLLOWING COMMENTS CAREFULLY.
+ *
+ */
+
+/* httpUnsafeOrigin is the URL that clients will enter to load your instance.
+ * Any other URL that somehow points to your instance is supposed to be blocked.
+ * The default provided below assumes you are loading CryptPad from a server
+ * which is running on the same machine, using port 3000.
+ *
+ * In a production instance this should be available ONLY over HTTPS
+ * using the default port for HTTPS (443) ie. https://cryptpad.fr
+ * In such a case this should be also handled by NGINX, as documented in
+ * cryptpad/docs/example.nginx.conf (see the $main_domain variable)
+ *
+ */
+ httpUnsafeOrigin: 'https://pad.deuxfleurs.fr',
+
+/* httpSafeOrigin is the URL that is used for the 'sandbox' described above.
+ * If you're testing or developing with CryptPad on your local machine then
+ * it is appropriate to leave this blank. The default behaviour is to serve
+ * the main domain over port 3000 and to serve the sandbox content over port 3001.
+ *
+ * This is not appropriate in a production environment where invasive networks
+ * may filter traffic going over abnormal ports.
+ * To correctly configure your production instance you must provide a URL
+ * with a different domain (a subdomain is sufficient).
+ * It will be used to load the UI in our 'sandbox' system.
+ *
+ * This value corresponds to the $sandbox_domain variable
+ * in the example nginx file.
+ *
+ * Note that in order for the sandboxing system to be effective
+ * httpSafeOrigin must be different from httpUnsafeOrigin.
+ *
+ * CUSTOMIZE AND UNCOMMENT THIS FOR PRODUCTION INSTALLATIONS.
+ */
+ httpSafeOrigin: "https://pad-sandbox.deuxfleurs.fr",
+
+/* httpAddress specifies the address on which the nodejs server
+ * should be accessible. By default it will listen on 127.0.0.1
+ * (IPv4 localhost on most systems). If you want it to listen on
+ * all addresses, including IPv6, set this to '::'.
+ *
+ */
+ httpAddress: '::',
+
+/* httpPort specifies on which port the nodejs server should listen.
+ * By default it will serve content over port 3000, which is suitable
+ * for both local development and for use with the provided nginx example,
+ * which will proxy websocket traffic to your node server.
+ *
+ */
+ httpPort: 3000,
+
+/* httpSafePort allows you to specify an alternative port from which
+ * the node process should serve sandboxed assets. The default value is
+ * that of your httpPort + 1. You probably don't need to change this.
+ *
+ */
+ // httpSafePort: 3001,
+
+/* CryptPad will launch a child process for every core available
+ * in order to perform CPU-intensive tasks in parallel.
+ * Some host environments may have a very large number of cores available
+ * or you may want to limit how much computing power CryptPad can take.
+ * If so, set 'maxWorkers' to a positive integer.
+ */
+ // maxWorkers: 4,
+
+ /* =====================
+ * Admin
+ * ===================== */
+
+ /*
+ * CryptPad contains an administration panel. Its access is restricted to specific
+ * users using the following list.
+ * To give access to the admin panel to a user account, just add their public signing
+ * key, which can be found on the settings page for registered users.
+ * Entries should be strings separated by a comma.
+ */
+ adminKeys: [
+ "[quentin@pad.deuxfleurs.fr/EWtzm-CiqJnM9RZL9mj-YyTgAtX-Zh76sru1K5bFpN8=]",
+ ],
+
+ /* =====================
+ * STORAGE
+ * ===================== */
+
+ /* Pads that are not 'pinned' by any registered user can be set to expire
+ * after a configurable number of days of inactivity (default 90 days).
+ * The value can be changed or set to false to remove expiration.
+ * Expired pads can then be removed using a cron job calling the
+ * `evict-inactive.js` script with node
+ *
+ * defaults to 90 days if nothing is provided
+ */
+ //inactiveTime: 90, // days
+
+ /* CryptPad archives some data instead of deleting it outright.
+ * This archived data still takes up space and so you'll probably still want to
+ * remove these files after a brief period.
+ *
+ * cryptpad/scripts/evict-inactive.js is intended to be run daily
+ * from a crontab or similar scheduling service.
+ *
+ * The intent with this feature is to provide a safety net in case of accidental
+ * deletion. Set this value to the number of days you'd like to retain
+ * archived data before it's removed permanently.
+ *
+ * defaults to 15 days if nothing is provided
+ */
+ //archiveRetentionTime: 15,
+
+ /* It's possible to configure your instance to remove data
+ * stored on behalf of inactive accounts. Set 'accountRetentionTime'
+ * to the number of days an account can remain idle before its
+ * documents and other account data is removed.
+ *
+ * Leave this value commented out to preserve all data stored
+ * by user accounts regardless of inactivity.
+ */
+ //accountRetentionTime: 365,
+
+ /* Starting with CryptPad 3.23.0, the server automatically runs
+ * the script responsible for removing inactive data according to
+ * your configured definition of inactivity. Set this value to `true`
+ * if you prefer not to remove inactive data, or if you prefer to
+ * do so manually using `scripts/evict-inactive.js`.
+ */
+ //disableIntegratedEviction: true,
+
+
+ /* Max Upload Size (bytes)
+ * this sets the maximum size of any one file uploaded to the server.
+ * anything larger than this size will be rejected
+ * defaults to 20MB if no value is provided
+ */
+ //maxUploadSize: 20 * 1024 * 1024,
+
+ /* Users with premium accounts (those with a plan included in their customLimit)
+ * can benefit from an increased upload size limit. By default they are restricted to the same
+ * upload size as any other registered user.
+ *
+ */
+ //premiumUploadSize: 100 * 1024 * 1024,
+
+ /* =====================
+ * DATABASE VOLUMES
+ * ===================== */
+
+ /*
+ * CryptPad stores each document in an individual file on your hard drive.
+ * Specify a directory where files should be stored.
+ * It will be created automatically if it does not already exist.
+ */
+ filePath: './root/mnt/datastore/',
+
+ /* CryptPad offers the ability to archive data for a configurable period
+ * before deleting it, allowing a means of recovering data in the event
+ * that it was deleted accidentally.
+ *
+ * To set the location of this archive directory to a custom value, change
+ * the path below:
+ */
+ archivePath: './root/mnt/data/archive',
+
+ /* CryptPad allows logged in users to request that particular documents be
+ * stored by the server indefinitely. This is called 'pinning'.
+ * Pin requests are stored in a pin-store. The location of this store is
+ * defined here.
+ */
+ pinPath: './root/mnt/data/pins',
+
+ /* if you would like the list of scheduled tasks to be stored in
+ a custom location, change the path below:
+ */
+ taskPath: './root/mnt/data/tasks',
+
+ /* if you would like users' authenticated blocks to be stored in
+ a custom location, change the path below:
+ */
+ blockPath: './root/mnt/block',
+
+ /* CryptPad allows logged in users to upload encrypted files. Files/blobs
+ * are stored in a 'blob-store'. Set its location here.
+ */
+ blobPath: './root/mnt/blob',
+
+ /* CryptPad stores incomplete blobs in a 'staging' area until they are
+ * fully uploaded. Set its location here.
+ */
+ blobStagingPath: './root/mnt/data/blobstage',
+
+ decreePath: './root/mnt/data/decrees',
+
+ /* CryptPad supports logging events directly to the disk in a 'logs' directory
+ * Set its location here, or set it to false (or nothing) if you'd rather not log
+ */
+ logPath: false,
+
+ /* =====================
+ * Debugging
+ * ===================== */
+
+ /* CryptPad can log activity to stdout
+ * This may be useful for debugging
+ */
+ logToStdout: true,
+
+ /* CryptPad can be configured to log more or less
+ * the various settings are listed below by order of importance
+ *
+ * silly, verbose, debug, feedback, info, warn, error
+ *
+ * Choose the least important level of logging you wish to see.
+ * For example, a 'silly' logLevel will display everything,
+ * while 'info' will display 'info', 'warn', and 'error' logs
+ *
+ * This will affect both logging to the console and the disk.
+ */
+ logLevel: 'silly',
+
+ /* clients can use the /settings/ app to opt out of usage feedback
+ * which informs the server of things like how much each app is being
+ * used, and whether certain clientside features are supported by
+ * the client's browser. The intent is to provide feedback to the admin
+ * such that the service can be improved. Enable this with `true`
+ * and ignore feedback with `false` or by commenting the attribute
+ *
+ * You will need to set your logLevel to include 'feedback'. Set this
+ * to false if you'd like to exclude feedback from your logs.
+ */
+ logFeedback: false,
+
+ /* CryptPad supports verbose logging
+ * (false by default)
+ */
+ verbose: true,
+
+ /* Surplus information:
+ *
+ * 'installMethod' is included in server telemetry to voluntarily
+ * indicate how many instances are using unofficial installation methods
+ * such as Docker.
+ *
+ */
+ installMethod: 'deuxfleurs.fr',
+};
diff --git a/cluster/prod/app/cryptpad/deploy/backup.hcl b/cluster/prod/app/cryptpad/deploy/backup.hcl
new file mode 100644
index 0000000..99dee2f
--- /dev/null
+++ b/cluster/prod/app/cryptpad/deploy/backup.hcl
@@ -0,0 +1,57 @@
+job "cryptpad_backup" {
+ datacenters = ["neptune"]
+ type = "batch"
+
+ priority = "60"
+
+ periodic {
+ cron = "@daily"
+ // Do not allow overlapping runs.
+ prohibit_overlap = true
+ }
+
+ group "backup-cryptpad" {
+ constraint {
+ attribute = "${attr.unique.hostname}"
+ operator = "="
+ value = "courgette"
+ }
+
+ task "main" {
+ driver = "docker"
+
+ config {
+ image = "restic/restic:0.12.1"
+ entrypoint = [ "/bin/sh", "-c" ]
+ args = [ "restic backup /cryptpad && restic forget --keep-within 1m1d --keep-within-weekly 3m --keep-within-monthly 1y && restic prune --max-unused 50% --max-repack-size 2G && restic check" ]
+ volumes = [
+ "/mnt/storage/cryptpad:/cryptpad"
+ ]
+ }
+
+ template {
+ data = <<EOH
+AWS_ACCESS_KEY_ID={{ key "secrets/cryptpad_backup/backup_aws_access_key_id" }}
+AWS_SECRET_ACCESS_KEY={{ key "secrets/cryptpad_backup/backup_aws_secret_access_key" }}
+RESTIC_REPOSITORY={{ key "secrets/cryptpad_backup/backup_restic_repository" }}
+RESTIC_PASSWORD={{ key "secrets/cryptpad_backup/backup_restic_password" }}
+EOH
+
+ destination = "secrets/env_vars"
+ env = true
+ }
+
+ resources {
+ cpu = 500
+ memory = 200
+ }
+
+ restart {
+ attempts = 2
+ interval = "30m"
+ delay = "15s"
+ mode = "fail"
+ }
+ }
+ }
+}
diff --git a/cluster/prod/app/cryptpad/deploy/cryptpad.hcl b/cluster/prod/app/cryptpad/deploy/cryptpad.hcl
new file mode 100644
index 0000000..726fe5a
--- /dev/null
+++ b/cluster/prod/app/cryptpad/deploy/cryptpad.hcl
@@ -0,0 +1,75 @@
+job "cryptpad" {
+ datacenters = ["neptune"]
+ type = "service"
+
+ group "cryptpad" {
+ count = 1
+
+ network {
+ port "http" {
+ to = 3000
+ }
+ }
+
+ restart {
+ attempts = 10
+ delay = "30s"
+ }
+
+ task "main" {
+ driver = "docker"
+
+ constraint {
+ attribute = "${attr.unique.hostname}"
+ operator = "="
+ value = "courgette"
+ }
+
+ config {
+ image = "superboum/cryptpad:0p3s44hjh4s1x55kbwkmywmwmx4wfyb8"
+ ports = [ "http" ]
+
+ volumes = [
+ "/mnt/storage/cryptpad:/mnt",
+ "secrets/config.js:/etc/cryptpad/config.js",
+ ]
+ }
+ env {
+ CRYPTPAD_CONFIG = "/etc/cryptpad/config.js"
+ }
+
+ template {
+ data = file("../config/config.js")
+ destination = "secrets/config.js"
+ }
+
+ /* Disabled because it requires modifications to the docker image and I do not want to invest the time yet
+ template {
+ data = file("../config/application_config.js")
+ destination = "secrets/config.js"
+ }
+ */
+
+ resources {
+ memory = 1000
+ cpu = 500
+ }
+
+ service {
+ port = "http"
+ tags = [
+ "tricot pad.deuxfleurs.fr",
+ "tricot pad-sandbox.deuxfleurs.fr",
+ "tricot-add-header Cross-Origin-Resource-Policy cross-origin",
+ "tricot-add-header Cross-Origin-Embedder-Policy require-corp",
+ ]
+ check {
+ type = "http"
+ path = "/"
+ interval = "10s"
+ timeout = "2s"
+ }
+ }
+ }
+ }
+}
diff --git a/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_access_key_id b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_access_key_id
new file mode 100644
index 0000000..9235e53
--- /dev/null
+++ b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_access_key_id
@@ -0,0 +1 @@
+USER Backup AWS access key ID
diff --git a/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_secret_access_key b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_secret_access_key
new file mode 100644
index 0000000..f34677e
--- /dev/null
+++ b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_aws_secret_access_key
@@ -0,0 +1 @@
+USER Backup AWS secret access key
diff --git a/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_password b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_password
new file mode 100644
index 0000000..fbaa5fa
--- /dev/null
+++ b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_password
@@ -0,0 +1 @@
+USER Restic password to encrypt backups
diff --git a/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_repository b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_repository
new file mode 100644
index 0000000..3f6cb93
--- /dev/null
+++ b/cluster/prod/app/cryptpad/secrets/cryptpad_backup/backup_restic_repository
@@ -0,0 +1 @@
+USER Restic repository, eg. s3:https://s3.garage.tld
diff --git a/cluster/prod/app/drone-ci/config/litestream.yml b/cluster/prod/app/drone-ci/config/litestream.yml
new file mode 100644
index 0000000..813c824
--- /dev/null
+++ b/cluster/prod/app/drone-ci/config/litestream.yml
@@ -0,0 +1,10 @@
+dbs:
+ - path: /ephemeral/drone.db
+ replicas:
+ - url: s3://{{ key "secrets/drone-ci/s3_db_bucket" | trimSpace }}/drone.db
+ region: garage
+ endpoint: https://garage.deuxfleurs.fr
+ access-key-id: {{ key "secrets/drone-ci/s3_ak" | trimSpace }}
+ secret-access-key: {{ key "secrets/drone-ci/s3_sk" | trimSpace }}
+ force-path-style: true
+ sync-interval: 60s
diff --git a/cluster/prod/app/drone-ci/deploy/server.hcl b/cluster/prod/app/drone-ci/deploy/server.hcl
new file mode 100644
index 0000000..85eb776
--- /dev/null
+++ b/cluster/prod/app/drone-ci/deploy/server.hcl
@@ -0,0 +1,139 @@
+job "drone-ci" {
+ datacenters = ["neptune"]
+ type = "service"
+
+ group "server" {
+ count = 1
+
+ network {
+ port "web_port" {
+ to = 80
+ }
+ }
+
+ task "restore-db" {
+ lifecycle {
+ hook = "prestart"
+ sidecar = false
+ }
+
+ driver = "docker"
+ config {
+ image = "litestream/litestream:0.3.9"
+ args = [
+ "restore", "-config", "/etc/litestream.yml", "/ephemeral/drone.db"
+ ]
+ volumes = [
+ "../alloc/data:/ephemeral",
+ "secrets/litestream.yml:/etc/litestream.yml"
+ ]
+ }
+
+ template {
+ data = file("../config/litestream.yml")
+ destination = "secrets/litestream.yml"
+ }
+
+ resources {
+ memory = 200
+ cpu = 1000
+ }
+ }
+
+ task "drone_server" {
+ driver = "docker"
+ config {
+ image = "drone/drone:2.12.0"
+ ports = [ "web_port" ]
+
+ volumes = [
+ "../alloc/data:/ephemeral",
+ ]
+ }
+
+ template {
+ data = <<EOH
+DRONE_GITEA_SERVER=https://git.deuxfleurs.fr
+DRONE_GITEA_CLIENT_ID={{ key "secrets/drone-ci/oauth_client_id" }}
+DRONE_GITEA_CLIENT_SECRET={{ key "secrets/drone-ci/oauth_client_secret" }}
+DRONE_RPC_SECRET={{ key "secrets/drone-ci/rpc_secret" }}
+DRONE_SERVER_HOST=drone.deuxfleurs.fr
+DRONE_SERVER_PROTO=https
+DRONE_DATABASE_SECRET={{ key "secrets/drone-ci/db_enc_secret" }}
+DRONE_COOKIE_SECRET={{ key "secrets/drone-ci/cookie_secret" }}
+AWS_ACCESS_KEY_ID={{ key "secrets/drone-ci/s3_ak" }}
+AWS_SECRET_ACCESS_KEY={{ key "secrets/drone-ci/s3_sk" }}
+AWS_DEFAULT_REGION=garage
+AWS_REGION=garage
+DRONE_S3_BUCKET={{ key "secrets/drone-ci/s3_storage_bucket" }}
+DRONE_S3_ENDPOINT=https://garage.deuxfleurs.fr
+DRONE_S3_PATH_STYLE=true
+DRONE_DATABASE_DRIVER=sqlite3
+DRONE_DATABASE_DATASOURCE=/ephemeral/drone.db
+DRONE_USER_CREATE=username:lx-admin,admin:true
+__DRONE_REGISTRATION_CLOSED=true
+DRONE_LOGS_TEXT=true
+DRONE_LOGS_PRETTY=true
+DRONE_LOGS_DEBUG=true
+DOCKER_API_VERSION=1.39
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ resources {
+ cpu = 100
+ memory = 100
+ }
+
+ service {
+ name = "drone"
+ tags = [
+ "drone",
+ "tricot drone.deuxfleurs.fr",
+ ]
+ port = "web_port"
+ address_mode = "host"
+ check {
+ type = "http"
+ protocol = "http"
+ port = "web_port"
+ path = "/"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "600s"
+ ignore_warnings = false
+ }
+ }
+ }
+ }
+
+ task "replicate-db" {
+ driver = "docker"
+ config {
+ image = "litestream/litestream:0.3.9"
+ entrypoint = [ "/bin/sh" ]
+ args = [
+ "-c",
+ "echo sleeping; sleep 60; echo launching; litestream replicate -config /etc/litestream.yml"
+ ]
+ volumes = [
+ "../alloc/data:/ephemeral",
+ "secrets/litestream.yml:/etc/litestream.yml"
+ ]
+ }
+
+ template {
+ data = file("../config/litestream.yml")
+ destination = "secrets/litestream.yml"
+ }
+
+ resources {
+ memory = 250
+ cpu = 100
+ }
+ }
+ }
+}
diff --git a/cluster/prod/app/drone-ci/integration/README.md b/cluster/prod/app/drone-ci/integration/README.md
new file mode 100644
index 0000000..b3c1cc6
--- /dev/null
+++ b/cluster/prod/app/drone-ci/integration/README.md
@@ -0,0 +1,74 @@
+## Install Debian
+
+We recommend Debian Bullseye
+
+## Install Docker CE from docker.io
+
+Do not use the docker engine shipped by Debian
+
+Doc:
+
+ - https://docs.docker.com/engine/install/debian/
+ - https://docs.docker.com/compose/install/
+
+On a fresh install, as root:
+
+```bash
+apt-get remove -y docker docker-engine docker.io containerd runc
+apt-get update
+apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
+curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
+ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
+apt-get update
+apt-get install -y docker-ce docker-ce-cli containerd.io
+
+curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
+chmod +x /usr/local/bin/docker-compose
+```
+
+## Prepare the runner
+
+Nix folder must be populated before launching any build.
+
+```bash
+docker run --rm -it -v /var/lib/drone/nix:/mnt nixpkgs/nix:nixos-21.05 cp -r /nix/{store,var} /mnt/
+```
+
+This folder will grow over time and might need to be garbage collected.
+As a rule of thumb, after running a full release of Garage, this folder will require 10GB.
+Consider provisioning it with at least 20GB.
+
+## Launch the runner
+
+Because we use a shared nix folder, we set the number of concurrent builds to 1.
+For more details and customizations, see `docker-compose.yml`.
+
+```bash
+DRONE_NAME=lheureduthe DRONE_OWNER=quentin DRONE_SECRET=xxx docker-compose up -d
+```
+
+That's all folks.
+
+## Check if a given job is built by your runner
+
+```bash
+export URL=https://drone.deuxfleurs.fr
+export REPO=Deuxfleurs/garage
+export BUILD=1312
+curl ${URL}/api/repos/${REPO}/builds/${BUILD} \
+ | jq -c '[.stages[] | { name: .name, machine: .machine }]'
+```
+
+It will give you the following result:
+
+```json
+[{"name":"default","machine":"1686a"},{"name":"release-linux-x86_64","machine":"vimaire"},{"name":"release-linux-i686","machine":"carcajou"},{"name":"release-linux-aarch64","machine":"caribou"},{"name":"release-linux-armv6l","machine":"cariacou"},{"name":"refresh-release-page","machine":null}]
+```
+
+## Random note
+
+This setup is done mainly to allow nix builds with some cache.
+To use the cache in Drone, you must set your repository as trusted.
+The command line tool does not work (it says it successfully set your repository as trusted but it did nothing):
+the only way to set your repository as trusted is to connect on the DB and set the `repo_trusted` field of your repo to true.
+
diff --git a/cluster/prod/app/drone-ci/integration/docker-compose.yml b/cluster/prod/app/drone-ci/integration/docker-compose.yml
new file mode 100644
index 0000000..1e37255
--- /dev/null
+++ b/cluster/prod/app/drone-ci/integration/docker-compose.yml
@@ -0,0 +1,32 @@
+version: '3.4'
+services:
+ drone-runner:
+ image: drone/drone-runner-docker:latest
+ restart: always
+ environment:
+ - DRONE_RPC_PROTO=https
+ - DRONE_RPC_HOST=drone.deuxfleurs.fr
+ - DRONE_RPC_SECRET=${DRONE_SECRET}
+ - DRONE_RUNNER_CAPACITY=1
+ - DRONE_DEBUG=true
+ - DRONE_LOGS_TRACE=true
+ - DRONE_RPC_DUMP_HTTP=true
+ - DRONE_RPC_DUMP_HTTP_BODY=true
+ - DRONE_RUNNER_NAME=${DRONE_NAME}
+ - DRONE_RUNNER_LABELS=nix:1
+ #- DRONE_RUNNER_VOLUMES=/var/lib/drone/nix:/nix
+ ports:
+ - "3000:3000/tcp"
+ volumes:
+ - "/var/run/docker.sock:/var/run/docker.sock"
+ - "/var/lib/drone/nix:/var/lib/drone/nix"
+
+ drone-gc:
+ image: drone/gc:latest
+ restart: always
+ environment:
+ - GC_DEBUG=true
+ - GC_CACHE=10gb
+ - GC_INTERVAL=10m
+ volumes:
+ - "/var/run/docker.sock:/var/run/docker.sock"
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/cookie_secret b/cluster/prod/app/drone-ci/secrets/drone-ci/cookie_secret
new file mode 100644
index 0000000..04c819e
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/cookie_secret
@@ -0,0 +1 @@
+CMD openssl rand -hex 16
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/db_enc_secret b/cluster/prod/app/drone-ci/secrets/drone-ci/db_enc_secret
new file mode 100644
index 0000000..3f9e696
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/db_enc_secret
@@ -0,0 +1 @@
+CMD_ONCE openssl rand -hex 16
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_id b/cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_id
new file mode 100644
index 0000000..c801b28
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_id
@@ -0,0 +1 @@
+USER OAuth client ID (on Gitea)
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_secret b/cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_secret
new file mode 100644
index 0000000..b79b688
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/oauth_client_secret
@@ -0,0 +1 @@
+USER OAuth client secret (for gitea)
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/rpc_secret b/cluster/prod/app/drone-ci/secrets/drone-ci/rpc_secret
new file mode 100644
index 0000000..04c819e
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/rpc_secret
@@ -0,0 +1 @@
+CMD openssl rand -hex 16
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/s3_ak b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_ak
new file mode 100644
index 0000000..3a8e4a2
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_ak
@@ -0,0 +1 @@
+USER S3 (garage) access key for Drone
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/s3_db_bucket b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_db_bucket
new file mode 100644
index 0000000..c36f17d
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_db_bucket
@@ -0,0 +1 @@
+CONST drone-db
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/s3_sk b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_sk
new file mode 100644
index 0000000..46fd9fa
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_sk
@@ -0,0 +1 @@
+USER S3 (garage) secret key for Drone
diff --git a/cluster/prod/app/drone-ci/secrets/drone-ci/s3_storage_bucket b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_storage_bucket
new file mode 100644
index 0000000..ca2702c
--- /dev/null
+++ b/cluster/prod/app/drone-ci/secrets/drone-ci/s3_storage_bucket
@@ -0,0 +1 @@
+CONST drone-storage
diff --git a/cluster/prod/app/frontend/deploy/frontend-tricot-prod.hcl b/cluster/prod/app/frontend/deploy/frontend-tricot-prod.hcl
new file mode 100644
index 0000000..804345b
--- /dev/null
+++ b/cluster/prod/app/frontend/deploy/frontend-tricot-prod.hcl
@@ -0,0 +1,90 @@
+job "frontend" {
+ datacenters = ["neptune"]
+ type = "service"
+ priority = 90
+
+ group "tricot" {
+ # Temporarily pin to single machine, remove this later
+ constraint {
+ attribute = "${attr.unique.hostname}"
+ value = "courgette"
+ }
+
+ network {
+ port "http_port" { static = 80 }
+ port "https_port" { static = 443 }
+ }
+
+ task "server" {
+ driver = "docker"
+
+ config {
+ image = "lxpz/amd64_tricot:41"
+ network_mode = "host"
+ readonly_rootfs = true
+ ports = [ "http_port", "https_port" ]
+ volumes = [
+ "secrets:/etc/tricot",
+ ]
+ }
+
+ resources {
+ cpu = 2000
+ memory = 200
+ }
+
+ restart {
+ interval = "30m"
+ attempts = 2
+ delay = "15s"
+ mode = "delay"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-ca.crt\" }}"
+ destination = "secrets/consul-ca.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-client.crt\" }}"
+ destination = "secrets/consul-client.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-client.key\" }}"
+ destination = "secrets/consul-client.key"
+ }
+
+ template {
+ data = <<EOH
+TRICOT_NODE_NAME={{ env "attr.unique.consul.name" }}
+TRICOT_LETSENCRYPT_EMAIL=alex@adnab.me
+TRICOT_ENABLE_COMPRESSION=true
+TRICOT_CONSUL_HOST=https://localhost:8501
+TRICOT_CONSUL_CA_CERT=/etc/tricot/consul-ca.crt
+TRICOT_CONSUL_CLIENT_CERT=/etc/tricot/consul-client.crt
+TRICOT_CONSUL_CLIENT_KEY=/etc/tricot/consul-client.key
+TRICOT_HTTP_BIND_ADDR=[::]:80
+TRICOT_HTTPS_BIND_ADDR=[::]:443
+RUST_LOG=tricot=debug
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ service {
+ name = "tricot-http"
+ port = "http_port"
+ tags = [ "(diplonat (tcp_port 80))" ]
+ address_mode = "host"
+ }
+
+ service {
+ name = "tricot-https"
+ port = "https_port"
+ tags = [ "(diplonat (tcp_port 443))" ]
+ address_mode = "host"
+ }
+ }
+ }
+}
diff --git a/cluster/prod/app/jitsi/build/jitsi-conference-focus/0001-Remove-broken-command-line-args-parameters-setting.patch b/cluster/prod/app/jitsi/build/jitsi-conference-focus/0001-Remove-broken-command-line-args-parameters-setting.patch
new file mode 100644
index 0000000..14d48c5
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-conference-focus/0001-Remove-broken-command-line-args-parameters-setting.patch
@@ -0,0 +1,91 @@
+From 3da458fc04560e8ddd597f7910c4f53b714d58ab Mon Sep 17 00:00:00 2001
+From: Quentin Dufour <quentin@dufour.io>
+Date: Mon, 1 Feb 2021 06:53:21 +0100
+Subject: [PATCH] Remove broken command line args parameters setting
+
+---
+ src/main/java/org/jitsi/jicofo/Main.java | 61 ------------------------
+ 1 file changed, 61 deletions(-)
+
+diff --git a/src/main/java/org/jitsi/jicofo/Main.java b/src/main/java/org/jitsi/jicofo/Main.java
+index 558d1b3..59e04bb 100644
+--- a/src/main/java/org/jitsi/jicofo/Main.java
++++ b/src/main/java/org/jitsi/jicofo/Main.java
+@@ -50,7 +50,6 @@ public static void main(String[] args)
+ logger.error("An uncaught exception occurred in thread=" + t, e));
+
+ setupMetaconfigLogger();
+- setSystemProperties(args);
+ JitsiConfig.Companion.reloadNewConfig();
+
+ // Make sure that passwords are not printed by ConfigurationService
+@@ -80,66 +79,6 @@ public static void main(String[] args)
+ JicofoServices.jicofoServicesSingleton = null;
+ }
+
+- /**
+- * Read the command line arguments and env variables, and set the corresponding system properties used for
+- * configuration of the XMPP component and client connections.
+- */
+- private static void setSystemProperties(String[] args)
+- throws ParseException
+- {
+- CmdLine cmdLine = new CmdLine();
+-
+- // We may end execution here if one of required arguments is missing
+- cmdLine.parse(args);
+-
+- // XMPP host/domain
+- String host;
+- String componentDomain;
+- // Try to get domain, can be null after this call(we'll fix that later)
+- componentDomain = cmdLine.getOptionValue("domain");
+- // Host name
+- host = cmdLine.getOptionValue("--host", componentDomain == null ? "localhost" : componentDomain);
+- // Try to fix component domain
+- if (isBlank(componentDomain))
+- {
+- componentDomain = host;
+- }
+- if (componentDomain != null)
+- {
+- // For backward compat, the "--domain" command line argument controls the domain for the XMPP component
+- // as well as XMPP client connection.
+- System.setProperty(XmppClientConnectionConfig.legacyXmppDomainPropertyName, componentDomain);
+- }
+- if (host != null)
+- {
+- // For backward compat, the "--host" command line argument controls the hostname for the XMPP component
+- // as well as XMPP client connection.
+- System.setProperty(XmppClientConnectionConfig.legacyHostnamePropertyName, host);
+- }
+-
+- // XMPP client connection
+- String focusDomain = cmdLine.getOptionValue("--user_domain");
+- String focusUserName = cmdLine.getOptionValue("--user_name");
+- String focusPassword = cmdLine.getOptionValue("--user_password");
+- if (isBlank(focusPassword))
+- {
+- focusPassword = System.getenv("JICOFO_AUTH_PASSWORD");
+- }
+-
+- if (focusDomain != null)
+- {
+- System.setProperty(XmppClientConnectionConfig.legacyDomainPropertyName, focusDomain);
+- }
+- if (focusUserName != null)
+- {
+- System.setProperty(XmppClientConnectionConfig.legacyUsernamePropertyName, focusUserName);
+- }
+- if (isNotBlank(focusPassword))
+- {
+- System.setProperty(XmppClientConnectionConfig.legacyPasswordPropertyName, focusPassword);
+- }
+- }
+-
+ private static void setupMetaconfigLogger()
+ {
+ org.jitsi.utils.logging2.Logger configLogger = new org.jitsi.utils.logging2.LoggerImpl("org.jitsi.config");
+--
+2.25.1
+
diff --git a/cluster/prod/app/jitsi/build/jitsi-conference-focus/Dockerfile b/cluster/prod/app/jitsi/build/jitsi-conference-focus/Dockerfile
new file mode 100644
index 0000000..241c61b
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-conference-focus/Dockerfile
@@ -0,0 +1,26 @@
+FROM debian:bookworm AS builder
+
+# unzip is required when executing the mvn package command
+RUN apt-get update && \
+ apt-get install -y openjdk-11-jdk-headless maven git unzip
+
+ARG JICOFO_TAG
+RUN git clone --depth 1 --branch $JICOFO_TAG https://github.com/jitsi/jicofo
+
+WORKDIR jicofo
+COPY *.patch .
+RUN git apply 0001-Remove-broken-command-line-args-parameters-setting.patch
+RUN mvn package -DskipTests -Dassembly.skipAssembly=false
+
+RUN unzip target/jicofo-1.1-SNAPSHOT-archive.zip && \
+ mv jicofo-1.1-SNAPSHOT /srv/build
+
+FROM debian:bookworm
+
+RUN apt-get update && \
+ apt-get install -y openjdk-11-jre-headless ca-certificates
+
+COPY --from=builder /srv/build /usr/share/jicofo
+COPY jicofo /usr/local/bin
+
+CMD ["/usr/local/bin/jicofo"]
diff --git a/cluster/prod/app/jitsi/build/jitsi-conference-focus/jicofo b/cluster/prod/app/jitsi/build/jitsi-conference-focus/jicofo
new file mode 100755
index 0000000..8fc8fce
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-conference-focus/jicofo
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+update-ca-certificates -f
+
+exec java \
+ -Dlog4j2.formatMsgNoLookups=true \
+ -Djdk.tls.ephemeralDHKeySize=2048 \
+ -Djava.util.logging.config.file=/usr/share/jicofo/lib/logging.properties \
+ -Dconfig.file=/etc/jitsi/jicofo.conf \
+ -cp "/usr/share/jicofo/*:/usr/share/jicofo/lib/*" \
+ org.jitsi.jicofo.Main
diff --git a/cluster/prod/app/jitsi/build/jitsi-meet/Dockerfile b/cluster/prod/app/jitsi/build/jitsi-meet/Dockerfile
new file mode 100644
index 0000000..d8c7cf8
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-meet/Dockerfile
@@ -0,0 +1,23 @@
+FROM debian:bookworm AS builder
+
+RUN apt-get update && \
+ apt-get install -y curl && \
+ curl -sL https://deb.nodesource.com/setup_16.x | bash - && \
+ apt-get install -y git nodejs make git unzip
+
+ARG MEET_TAG
+RUN git clone --depth 1 --branch ${MEET_TAG} https://github.com/jitsi/jitsi-meet
+
+WORKDIR jitsi-meet
+RUN npm install && \
+ make
+
+FROM debian:bookworm
+
+COPY --from=builder /jitsi-meet /srv/jitsi-meet
+RUN apt-get update && \
+ apt-get install -y nginx && \
+ rm /etc/nginx/sites-enabled/* && \
+ rm /etc/nginx/nginx.conf
+
+CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
diff --git a/cluster/prod/app/jitsi/build/jitsi-videobridge/0001-Remove-deprecated-argument.patch b/cluster/prod/app/jitsi/build/jitsi-videobridge/0001-Remove-deprecated-argument.patch
new file mode 100644
index 0000000..575d93f
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-videobridge/0001-Remove-deprecated-argument.patch
@@ -0,0 +1,40 @@
+From 01507442620e5a57624c921b508eac7d572440d0 Mon Sep 17 00:00:00 2001
+From: Quentin Dufour <quentin@deuxfleurs.fr>
+Date: Tue, 25 Jan 2022 14:46:22 +0100
+Subject: [PATCH] Remove deprecated argument
+
+---
+ .../main/kotlin/org/jitsi/videobridge/Main.kt | 17 -----------------
+ 1 file changed, 17 deletions(-)
+
+diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
+index 4f6cb78..3db00f2 100644
+--- a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
++++ b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
+@@ -52,23 +52,6 @@ import org.jitsi.videobridge.websocket.singleton as webSocketServiceSingleton
+ fun main(args: Array<String>) {
+ val logger = LoggerImpl("org.jitsi.videobridge.Main")
+
+- // We only support command line arguments for backward compatibility. The --apis options is the last one supported,
+- // and it is only used to enable/disable the REST API (XMPP is only controlled through the config files).
+- // TODO: fully remove support for --apis
+- CmdLine().apply {
+- parse(args)
+- getOptionValue("--apis")?.let {
+- logger.warn(
+- "A deprecated command line argument (--apis) is present. Please use the config file to control the " +
+- "REST API instead (see rest.md). Support for --apis will be removed in a future version."
+- )
+- System.setProperty(
+- Videobridge.REST_API_PNAME,
+- it.contains(Videobridge.REST_API).toString()
+- )
+- }
+- }
+-
+ setupMetaconfigLogger()
+
+ setSystemPropertyDefaults()
+--
+2.33.1
+
diff --git a/cluster/prod/app/jitsi/build/jitsi-videobridge/Dockerfile b/cluster/prod/app/jitsi/build/jitsi-videobridge/Dockerfile
new file mode 100644
index 0000000..1f2509b
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-videobridge/Dockerfile
@@ -0,0 +1,24 @@
+FROM debian:bookworm AS builder
+
+RUN apt-get update && \
+ apt-get install -y git unzip maven openjdk-11-jdk-headless
+
+ARG JVB_TAG
+RUN git clone --depth 1 --branch ${JVB_TAG} https://github.com/jitsi/jitsi-videobridge
+
+WORKDIR jitsi-videobridge
+COPY *.patch .
+RUN git apply 0001-Remove-deprecated-argument.patch
+RUN mvn package -DskipTests
+RUN unzip jvb/target/jitsi-videobridge*.zip && \
+ mv jitsi-videobridge-*-SNAPSHOT build
+
+FROM debian:bookworm
+
+RUN apt-get update && \
+ apt-get install -y openjdk-11-jre-headless curl iproute2
+
+COPY --from=builder /jitsi-videobridge/build /usr/share/jvb
+COPY jvb_run /usr/local/bin/jvb_run
+
+CMD ["/usr/local/bin/jvb_run"]
diff --git a/cluster/prod/app/jitsi/build/jitsi-videobridge/jvb_run b/cluster/prod/app/jitsi/build/jitsi-videobridge/jvb_run
new file mode 100755
index 0000000..8d595e6
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-videobridge/jvb_run
@@ -0,0 +1,22 @@
+#!/bin/bash
+update-ca-certificates -f
+
+if [ -z "${JITSI_NAT_LOCAL_IP}" ]; then
+ JITSI_NAT_LOCAL_IP=$(ip route get $(ip route show 0.0.0.0/0 | grep -oP 'via \K\S+') | grep -oP 'src \K\S+')
+fi
+
+if [ -z "${JITSI_NAT_PUBLIC_IP}" ]; then
+ JITSI_NAT_PUBLIC_IP=$(curl https://ifconfig.me)
+fi
+
+echo "NAT config: ${JITSI_NAT_LOCAL_IP} -> ${JITSI_NAT_PUBLIC_IP}"
+
+exec java \
+ -Dlog4j2.formatMsgNoLookups=true \
+ -Djdk.tls.ephemeralDHKeySize=2048 \
+ -Djava.util.logging.config.file=/usr/share/jvb/lib/logging.properties \
+ -Dconfig.file=/etc/jitsi/videobridge.conf \
+ -Dorg.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=${JITSI_NAT_LOCAL_IP} \
+ -Dorg.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=${JITSI_NAT_PUBLIC_IP} \
+ -cp '/usr/share/jvb/jitsi-videobridge.jar:/usr/share/jvb/lib/*' \
+ org.jitsi.videobridge.MainKt
diff --git a/cluster/prod/app/jitsi/build/jitsi-xmpp/Dockerfile b/cluster/prod/app/jitsi/build/jitsi-xmpp/Dockerfile
new file mode 100644
index 0000000..a060fda
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-xmpp/Dockerfile
@@ -0,0 +1,35 @@
+FROM debian:bookworm as builder
+
+RUN apt-get update && \
+ apt-get install -y git unzip
+
+ARG MEET_TAG
+RUN git clone --depth 1 --branch ${MEET_TAG} https://github.com/jitsi/jitsi-meet/
+
+FROM debian:bookworm
+
+ARG PROSODY_VERSION
+RUN apt-get update && \
+ apt-get install -y wget gnupg2 && \
+ echo deb http://packages.prosody.im/debian buster main \
+ | tee -a /etc/apt/sources.list && \
+ wget https://prosody.im/files/prosody-debian-packages.key -O - \
+ | apt-key add - && \
+ apt-get update && \
+ apt-get install -y prosody=${PROSODY_VERSION} lua-event
+
+RUN mkdir -p /usr/local/share/ca-certificates/ && \
+ ln -sf \
+ /var/lib/prosody/certs/auth.jitsi.crt \
+ /usr/local/share/ca-certificates/auth.jitsi.crt && \
+ mkdir /run/prosody && \
+ touch /run/prosody/prosody.pid && \
+ mkdir -p /var/lib/prosody && \
+ chown -R prosody:prosody /var/lib/prosody /run/prosody
+
+COPY --from=builder /jitsi-meet/resources/prosody-plugins /usr/share/jitsi-meet/prosody-plugins/
+COPY xmpp_prosody /usr/local/bin/xmpp_prosody
+
+WORKDIR /var/lib/prosody
+USER prosody
+CMD ["/usr/local/bin/xmpp_prosody"]
diff --git a/cluster/prod/app/jitsi/build/jitsi-xmpp/xmpp_prosody b/cluster/prod/app/jitsi/build/jitsi-xmpp/xmpp_prosody
new file mode 100755
index 0000000..af179e5
--- /dev/null
+++ b/cluster/prod/app/jitsi/build/jitsi-xmpp/xmpp_prosody
@@ -0,0 +1,9 @@
+#!/bin/bash
+prosodyctl register focus auth.jitsi ${JICOFO_AUTH_PASSWORD}
+prosodyctl register jvb auth.jitsi ${JVB_AUTH_PASSWORD}
+
+# copied from jitsi-meet.postinst
+# Make sure the focus@auth user's roster includes the proxy component (this is idempotent)
+prosodyctl mod_roster_command subscribe focus.jitsi focus@auth.jitsi
+
+exec prosody
diff --git a/cluster/prod/app/jitsi/config/config.js b/cluster/prod/app/jitsi/config/config.js
new file mode 100644
index 0000000..9464f37
--- /dev/null
+++ b/cluster/prod/app/jitsi/config/config.js
@@ -0,0 +1,773 @@
+/* eslint-disable no-unused-vars, no-var */
+
+var config = {
+ // Connection
+ //
+
+ hosts: {
+ // XMPP domain.
+ domain: 'jitsi',
+
+ // When using authentication, domain for guest users.
+ // anonymousdomain: 'guest.example.com',
+
+ // Domain for authenticated users. Defaults to <domain>.
+ // authdomain: 'jitsi-meet.example.com',
+
+ // Focus component domain. Defaults to focus.<domain>.
+ // focus: 'focus.jitsi-meet.example.com',
+
+ // XMPP MUC domain. FIXME: use XEP-0030 to discover it.
+ muc: 'conference.jitsi'
+ },
+
+ // BOSH URL. FIXME: use XEP-0156 to discover it.
+ bosh: '//jitsi.deuxfleurs.fr/http-bind',
+
+ // Websocket URL
+ // websocket: 'wss://jitsi-meet.example.com/xmpp-websocket',
+
+ // The name of client node advertised in XEP-0115 'c' stanza
+ clientNode: 'http://jitsi.org/jitsimeet',
+
+ // The real JID of focus participant - can be overridden here
+ // Do not change username - FIXME: Make focus username configurable
+ // https://github.com/jitsi/jitsi-meet/issues/7376
+ // focusUserJid: 'focus@auth.jitsi-meet.example.com',
+
+
+ // Testing / experimental features.
+ //
+
+ testing: {
+ // Disables the End to End Encryption feature. Useful for debugging
+ // issues related to insertable streams.
+ // disableE2EE: false,
+
+ // P2P test mode disables automatic switching to P2P when there are 2
+ // participants in the conference.
+ p2pTestMode: false
+
+ // Enables the test specific features consumed by jitsi-meet-torture
+ // testMode: false
+
+ // Disables the auto-play behavior of *all* newly created video element.
+ // This is useful when the client runs on a host with limited resources.
+ // noAutoPlayVideo: false
+
+ // Enable / disable 500 Kbps bitrate cap on desktop tracks. When enabled,
+ // simulcast is turned off for the desktop share. If presenter is turned
+ // on while screensharing is in progress, the max bitrate is automatically
+ // adjusted to 2.5 Mbps. This takes a value between 0 and 1 which determines
+ // the probability for this to be enabled.
+ // capScreenshareBitrate: 1 // 0 to disable
+
+ // Enable callstats only for a percentage of users.
+ // This takes a value between 0 and 100 which determines the probability for
+ // the callstats to be enabled.
+ // callStatsThreshold: 5 // enable callstats for 5% of the users.
+ },
+
+ // Disables ICE/UDP by filtering out local and remote UDP candidates in
+ // signalling.
+ // webrtcIceUdpDisable: false,
+
+ // Disables ICE/TCP by filtering out local and remote TCP candidates in
+ // signalling.
+ // webrtcIceTcpDisable: false,
+
+
+ // Media
+ //
+
+ // Audio
+
+ // Disable measuring of audio levels.
+ // disableAudioLevels: false,
+ // audioLevelsInterval: 200,
+
+ // Enabling this will run the lib-jitsi-meet no audio detection module which
+ // will notify the user if the current selected microphone has no audio
+ // input and will suggest another valid device if one is present.
+ enableNoAudioDetection: true,
+
+ // Enabling this will show a "Save Logs" link in the GSM popover that can be
+ // used to collect debug information (XMPP IQs, SDP offer/answer cycles)
+ // about the call.
+ // enableSaveLogs: false,
+
+ // Enabling this will run the lib-jitsi-meet noise detection module which will
+ // notify the user if there is noise, other than voice, coming from the current
+ // selected microphone. The purpose it to let the user know that the input could
+ // be potentially unpleasant for other meeting participants.
+ enableNoisyMicDetection: false,
+
+ // Start the conference in audio only mode (no video is being received nor
+ // sent).
+ startAudioOnly: false,
+
+ // Every participant after the Nth will start audio muted.
+ startAudioMuted: 5,
+
+ // Start calls with audio muted. Unlike the option above, this one is only
+ // applied locally. FIXME: having these 2 options is confusing.
+ // startWithAudioMuted: false,
+
+ // Enabling it (with #params) will disable local audio output of remote
+ // participants and to enable it back a reload is needed.
+ // startSilent: false
+
+ // Sets the preferred target bitrate for the Opus audio codec by setting its
+ // 'maxaveragebitrate' parameter. Currently not available in p2p mode.
+ // Valid values are in the range 6000 to 510000
+ // opusMaxAverageBitrate: 20000,
+
+ // Enables support for opus-red (redundancy for Opus).
+ // enableOpusRed: false
+
+ // Video
+
+ // Sets the preferred resolution (height) for local video. Defaults to 720.
+ // resolution: 720,
+
+ // How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
+ // Use -1 to disable.
+ // maxFullResolutionParticipants: 2,
+
+ // w3c spec-compliant video constraints to use for video capture. Currently
+ // used by browsers that return true from lib-jitsi-meet's
+ // util#browser#usesNewGumFlow. The constraints are independent from
+ // this config's resolution value. Defaults to requesting an ideal
+ // resolution of 720p.
+ // constraints: {
+ // video: {
+ // height: {
+ // ideal: 720,
+ // max: 720,
+ // min: 240
+ // }
+ // }
+ // },
+
+ // Enable / disable simulcast support.
+ // disableSimulcast: false,
+
+ // Enable / disable layer suspension. If enabled, endpoints whose HD
+ // layers are not in use will be suspended (no longer sent) until they
+ // are requested again.
+ // enableLayerSuspension: false,
+
+ // Every participant after the Nth will start video muted.
+ startVideoMuted: 5,
+
+ // Start calls with video muted. Unlike the option above, this one is only
+ // applied locally. FIXME: having these 2 options is confusing.
+ // startWithVideoMuted: false,
+
+ // If set to true, prefer to use the H.264 video codec (if supported).
+ // Note that it's not recommended to do this because simulcast is not
+ // supported when using H.264. For 1-to-1 calls this setting is enabled by
+ // default and can be toggled in the p2p section.
+ // This option has been deprecated, use preferredCodec under videoQuality section instead.
+ // preferH264: true,
+
+ // If set to true, disable H.264 video codec by stripping it out of the
+ // SDP.
+ // disableH264: false,
+
+ // Desktop sharing
+
+ // Optional desktop sharing frame rate options. Default value: min:5, max:5.
+ // desktopSharingFrameRate: {
+ // min: 5,
+ // max: 5
+ // },
+
+ // Try to start calls with screen-sharing instead of camera video.
+ // startScreenSharing: false,
+
+ // Recording
+
+ // Whether to enable file recording or not.
+ // fileRecordingsEnabled: false,
+ // Enable the dropbox integration.
+ // dropbox: {
+ // appKey: '<APP_KEY>' // Specify your app key here.
+ // // A URL to redirect the user to, after authenticating
+ // // by default uses:
+ // // 'https://jitsi-meet.example.com/static/oauth.html'
+ // redirectURI:
+ // 'https://jitsi-meet.example.com/subfolder/static/oauth.html'
+ // },
+ // When integrations like dropbox are enabled only that will be shown,
+ // by enabling fileRecordingsServiceEnabled, we show both the integrations
+ // and the generic recording service (its configuration and storage type
+ // depends on jibri configuration)
+ // fileRecordingsServiceEnabled: false,
+ // Whether to show the possibility to share file recording with other people
+ // (e.g. meeting participants), based on the actual implementation
+ // on the backend.
+ // fileRecordingsServiceSharingEnabled: false,
+
+ // Whether to enable live streaming or not.
+ // liveStreamingEnabled: false,
+
+ // Transcription (in interface_config,
+ // subtitles and buttons can be configured)
+ // transcribingEnabled: false,
+
+ // Enables automatic turning on captions when recording is started
+ // autoCaptionOnRecord: false,
+
+ // Misc
+
+ // Default value for the channel "last N" attribute. -1 for unlimited.
+ channelLastN: -1,
+
+ // Provides a way to use different "last N" values based on the number of participants in the conference.
+ // The keys in an Object represent number of participants and the values are "last N" to be used when number of
+ // participants gets to or above the number.
+ //
+ // For the given example mapping, "last N" will be set to 20 as long as there are at least 5, but less than
+ // 29 participants in the call and it will be lowered to 15 when the 30th participant joins. The 'channelLastN'
+ // will be used as default until the first threshold is reached.
+ //
+ // lastNLimits: {
+ // 5: 20,
+ // 30: 15,
+ // 50: 10,
+ // 70: 5,
+ // 90: 2
+ // },
+
+ // Specify the settings for video quality optimizations on the client.
+ // videoQuality: {
+ // // Provides a way to prevent a video codec from being negotiated on the JVB connection. The codec specified
+ // // here will be removed from the list of codecs present in the SDP answer generated by the client. If the
+ // // same codec is specified for both the disabled and preferred option, the disable settings will prevail.
+ // // Note that 'VP8' cannot be disabled since it's a mandatory codec, the setting will be ignored in this case.
+ // disabledCodec: 'H264',
+ //
+ // // Provides a way to set a preferred video codec for the JVB connection. If 'H264' is specified here,
+ // // simulcast will be automatically disabled since JVB doesn't support H264 simulcast yet. This will only
+ // // rearrange the the preference order of the codecs in the SDP answer generated by the browser only if the
+ // // preferred codec specified here is present. Please ensure that the JVB offers the specified codec for this
+ // // to take effect.
+ // preferredCodec: 'VP8',
+ //
+ // // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
+ // // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
+ // // are the max.bitrates to be set on that particular type of stream. The actual send may vary based on
+ // // the available bandwidth calculated by the browser, but it will be capped by the values specified here.
+ // // This is currently not implemented on app based clients on mobile.
+ // maxBitratesVideo: {
+ // low: 200000,
+ // standard: 500000,
+ // high: 1500000
+ // },
+ //
+ // // The options can be used to override default thresholds of video thumbnail heights corresponding to
+ // // the video quality levels used in the application. At the time of this writing the allowed levels are:
+ // // 'low' - for the low quality level (180p at the time of this writing)
+ // // 'standard' - for the medium quality level (360p)
+ // // 'high' - for the high quality level (720p)
+ // // The keys should be positive numbers which represent the minimal thumbnail height for the quality level.
+ // //
+ // // With the default config value below the application will use 'low' quality until the thumbnails are
+ // // at least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to
+ // // the high quality.
+ // minHeightForQualityLvl: {
+ // 360: 'standard',
+ // 720: 'high'
+ // },
+ //
+ // // Provides a way to resize the desktop track to 720p (if it is greater than 720p) before creating a canvas
+ // // for the presenter mode (camera picture-in-picture mode with screenshare).
+ // resizeDesktopForPresenter: false
+ // },
+
+ // // Options for the recording limit notification.
+ // recordingLimit: {
+ //
+ // // The recording limit in minutes. Note: This number appears in the notification text
+ // // but doesn't enforce the actual recording time limit. This should be configured in
+ // // jibri!
+ // limit: 60,
+ //
+ // // The name of the app with unlimited recordings.
+ // appName: 'Unlimited recordings APP',
+ //
+ // // The URL of the app with unlimited recordings.
+ // appURL: 'https://unlimited.recordings.app.com/'
+ // },
+
+ // Disables or enables RTX (RFC 4588) (defaults to false).
+ // disableRtx: false,
+
+ // Disables or enables TCC support in this client (default: enabled).
+ // enableTcc: true,
+
+ // Disables or enables REMB support in this client (default: enabled).
+ // enableRemb: true,
+
+ // Enables ICE restart logic in LJM and displays the page reload overlay on
+ // ICE failure. Current disabled by default because it's causing issues with
+ // signaling when Octo is enabled. Also when we do an "ICE restart"(which is
+ // not a real ICE restart), the client maintains the TCC sequence number
+ // counter, but the bridge resets it. The bridge sends media packets with
+ // TCC sequence numbers starting from 0.
+ // enableIceRestart: false,
+
+ // Use TURN/UDP servers for the jitsi-videobridge connection (by default
+ // we filter out TURN/UDP because it is usually not needed since the
+ // bridge itself is reachable via UDP)
+ // useTurnUdp: false
+
+ // UI
+ //
+
+ // Disables responsive tiles.
+ // disableResponsiveTiles: false,
+
+ // Hides lobby button
+ // hideLobbyButton: false,
+
+ // Require users to always specify a display name.
+ // requireDisplayName: true,
+
+ // Whether to use a welcome page or not. In case it's false a random room
+ // will be joined when no room is specified.
+ enableWelcomePage: true,
+
+ // Disable app shortcuts that are registered upon joining a conference
+ // disableShortcuts: false,
+
+ // Disable initial browser getUserMedia requests.
+ // This is useful for scenarios where users might want to start a conference for screensharing only
+ // disableInitialGUM: false,
+
+ // Enabling the close page will ignore the welcome page redirection when
+ // a call is hangup.
+ // enableClosePage: false,
+
+ // Disable hiding of remote thumbnails when in a 1-on-1 conference call.
+ // disable1On1Mode: false,
+
+ // Default language for the user interface.
+ defaultLanguage: 'fr',
+
+ // Disables profile and the edit of all fields from the profile settings (display name and email)
+ // disableProfile: false,
+
+ // Whether or not some features are checked based on token.
+ // enableFeaturesBasedOnToken: false,
+
+ // When enabled the password used for locking a room is restricted to up to the number of digits specified
+ // roomPasswordNumberOfDigits: 10,
+ // default: roomPasswordNumberOfDigits: false,
+
+ // Message to show the users. Example: 'The service will be down for
+ // maintenance at 01:00 AM GMT,
+ // noticeMessage: '',
+
+ // Enables calendar integration, depends on googleApiApplicationClientID
+ // and microsoftApiApplicationClientID
+ // enableCalendarIntegration: false,
+
+ // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
+ prejoinPageEnabled: true,
+
+ // If etherpad integration is enabled, setting this to true will
+ // automatically open the etherpad when a participant joins. This
+ // does not affect the mobile app since opening an etherpad
+ // obscures the conference controls -- it's better to let users
+ // choose to open the pad on their own in that case.
+ // openSharedDocumentOnJoin: false,
+
+ // If true, shows the unsafe room name warning label when a room name is
+ // deemed unsafe (due to the simplicity in the name) and a password is not
+ // set or the lobby is not enabled.
+ // enableInsecureRoomNameWarning: false,
+
+ // Whether to automatically copy invitation URL after creating a room.
+ // Document should be focused for this option to work
+ // enableAutomaticUrlCopy: false,
+
+ // Base URL for a Gravatar-compatible service. Defaults to libravatar.
+ // gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/';
+
+ // Stats
+ //
+
+ // Whether to enable stats collection or not in the TraceablePeerConnection.
+ // This can be useful for debugging purposes (post-processing/analysis of
+ // the webrtc stats) as it is done in the jitsi-meet-torture bandwidth
+ // estimation tests.
+ // gatherStats: false,
+
+ // The interval at which PeerConnection.getStats() is called. Defaults to 10000
+ // pcStatsInterval: 10000,
+
+ // To enable sending statistics to callstats.io you must provide the
+ // Application ID and Secret.
+ // callStatsID: '',
+ // callStatsSecret: '',
+
+ // Enables sending participants' display names to callstats
+ // enableDisplayNameInStats: false,
+
+ // Enables sending participants' emails (if available) to callstats and other analytics
+ // enableEmailInStats: false,
+
+ // Privacy
+ //
+
+ // If third party requests are disabled, no other server will be contacted.
+ // This means avatars will be locally generated and callstats integration
+ // will not function.
+ // disableThirdPartyRequests: false,
+
+
+ // Peer-To-Peer mode: used (if enabled) when there are just 2 participants.
+ //
+
+ p2p: {
+ // Enables peer to peer mode. When enabled the system will try to
+ // establish a direct connection when there are exactly 2 participants
+ // in the room. If that succeeds the conference will stop sending data
+ // through the JVB and use the peer to peer connection instead. When a
+ // 3rd participant joins the conference will be moved back to the JVB
+ // connection.
+ enabled: true,
+
+ // The STUN servers that will be used in the peer to peer connections
+ stunServers: [
+
+ // { urls: 'stun:jitsi-meet.example.com:3478' },
+ { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
+ ]
+
+ // Sets the ICE transport policy for the p2p connection. At the time
+ // of this writing the list of possible values are 'all' and 'relay',
+ // but that is subject to change in the future. The enum is defined in
+ // the WebRTC standard:
+ // https://www.w3.org/TR/webrtc/#rtcicetransportpolicy-enum.
+ // If not set, the effective value is 'all'.
+ // iceTransportPolicy: 'all',
+
+ // If set to true, it will prefer to use H.264 for P2P calls (if H.264
+ // is supported). This setting is deprecated, use preferredCodec instead.
+ // preferH264: true
+
+ // Provides a way to set the video codec preference on the p2p connection. Acceptable
+ // codec values are 'VP8', 'VP9' and 'H264'.
+ // preferredCodec: 'H264',
+
+ // If set to true, disable H.264 video codec by stripping it out of the
+ // SDP. This setting is deprecated, use disabledCodec instead.
+ // disableH264: false,
+
+ // Provides a way to prevent a video codec from being negotiated on the p2p connection.
+ // disabledCodec: '',
+
+ // How long we're going to wait, before going back to P2P after the 3rd
+ // participant has left the conference (to filter out page reload).
+ // backToP2PDelay: 5
+ },
+
+ analytics: {
+ // The Google Analytics Tracking ID:
+ // googleAnalyticsTrackingId: 'your-tracking-id-UA-123456-1'
+
+ // Matomo configuration:
+ // matomoEndpoint: 'https://your-matomo-endpoint/',
+ // matomoSiteID: '42',
+
+ // The Amplitude APP Key:
+ // amplitudeAPPKey: '<APP_KEY>'
+
+ // Configuration for the rtcstats server:
+ // By enabling rtcstats server every time a conference is joined the rtcstats
+ // module connects to the provided rtcstatsEndpoint and sends statistics regarding
+ // PeerConnection states along with getStats metrics polled at the specified
+ // interval.
+ // rtcstatsEnabled: true,
+
+ // In order to enable rtcstats one needs to provide a endpoint url.
+ // rtcstatsEndpoint: wss://rtcstats-server-pilot.jitsi.net/,
+
+ // The interval at which rtcstats will poll getStats, defaults to 1000ms.
+ // If the value is set to 0 getStats won't be polled and the rtcstats client
+ // will only send data related to RTCPeerConnection events.
+ // rtcstatsPolIInterval: 1000
+
+ // Array of script URLs to load as lib-jitsi-meet "analytics handlers".
+ // scriptURLs: [
+ // "libs/analytics-ga.min.js", // google-analytics
+ // "https://example.com/my-custom-analytics.js"
+ // ],
+ },
+
+ // Logs that should go be passed through the 'log' event if a handler is defined for it
+ // apiLogLevels: ['warn', 'log', 'error', 'info', 'debug'],
+
+ // Information about the jitsi-meet instance we are connecting to, including
+ // the user region as seen by the server.
+ deploymentInfo: {
+ // shard: "shard1",
+ // region: "europe",
+ // userRegion: "asia"
+ },
+
+ // Decides whether the start/stop recording audio notifications should play on record.
+ // disableRecordAudioNotification: false,
+
+ // Information for the chrome extension banner
+ // chromeExtensionBanner: {
+ // // The chrome extension to be installed address
+ // url: 'https://chrome.google.com/webstore/detail/jitsi-meetings/kglhbbefdnlheedjiejgomgmfplipfeb',
+
+ // // Extensions info which allows checking if they are installed or not
+ // chromeExtensionsInfo: [
+ // {
+ // id: 'kglhbbefdnlheedjiejgomgmfplipfeb',
+ // path: 'jitsi-logo-48x48.png'
+ // }
+ // ]
+ // },
+
+ // Local Recording
+ //
+
+ // localRecording: {
+ // Enables local recording.
+ // Additionally, 'localrecording' (all lowercase) needs to be added to
+ // TOOLBAR_BUTTONS in interface_config.js for the Local Recording
+ // button to show up on the toolbar.
+ //
+ // enabled: true,
+ //
+
+ // The recording format, can be one of 'ogg', 'flac' or 'wav'.
+ // format: 'flac'
+ //
+
+ // },
+
+ // Options related to end-to-end (participant to participant) ping.
+ // e2eping: {
+ // // The interval in milliseconds at which pings will be sent.
+ // // Defaults to 10000, set to <= 0 to disable.
+ // pingInterval: 10000,
+ //
+ // // The interval in milliseconds at which analytics events
+ // // with the measured RTT will be sent. Defaults to 60000, set
+ // // to <= 0 to disable.
+ // analyticsInterval: 60000,
+ // },
+
+ // If set, will attempt to use the provided video input device label when
+ // triggering a screenshare, instead of proceeding through the normal flow
+ // for obtaining a desktop stream.
+ // NOTE: This option is experimental and is currently intended for internal
+ // use only.
+ // _desktopSharingSourceDevice: 'sample-id-or-label',
+
+ // If true, any checks to handoff to another application will be prevented
+ // and instead the app will continue to display in the current browser.
+ // disableDeepLinking: false,
+
+ // A property to disable the right click context menu for localVideo
+ // the menu has option to flip the locally seen video for local presentations
+ // disableLocalVideoFlip: false,
+
+ // Mainly privacy related settings
+
+ // Disables all invite functions from the app (share, invite, dial out...etc)
+ // disableInviteFunctions: true,
+
+ // Disables storing the room name to the recents list
+ // doNotStoreRoom: true,
+
+ // Deployment specific URLs.
+ // deploymentUrls: {
+ // // If specified a 'Help' button will be displayed in the overflow menu with a link to the specified URL for
+ // // user documentation.
+ // userDocumentationURL: 'https://docs.example.com/video-meetings.html',
+ // // If specified a 'Download our apps' button will be displayed in the overflow menu with a link
+ // // to the specified URL for an app download page.
+ // downloadAppsUrl: 'https://docs.example.com/our-apps.html'
+ // },
+
+ // Options related to the remote participant menu.
+ // remoteVideoMenu: {
+ // // If set to true the 'Kick out' button will be disabled.
+ // disableKick: true
+ // },
+
+ // If set to true all muting operations of remote participants will be disabled.
+ // disableRemoteMute: true,
+
+ // Enables support for lip-sync for this client (if the browser supports it).
+ // enableLipSync: false
+
+ /**
+ External API url used to receive branding specific information.
+ If there is no url set or there are missing fields, the defaults are applied.
+ None of the fields are mandatory and the response must have the shape:
+ {
+ // The hex value for the colour used as background
+ backgroundColor: '#fff',
+ // The url for the image used as background
+ backgroundImageUrl: 'https://example.com/background-img.png',
+ // The anchor url used when clicking the logo image
+ logoClickUrl: 'https://example-company.org',
+ // The url used for the image used as logo
+ logoImageUrl: 'https://example.com/logo-img.png'
+ }
+ */
+ // dynamicBrandingUrl: '',
+
+ // The URL of the moderated rooms microservice, if available. If it
+ // is present, a link to the service will be rendered on the welcome page,
+ // otherwise the app doesn't render it.
+ // moderatedRoomServiceUrl: 'https://moderated.jitsi-meet.example.com',
+
+ // If true, tile view will not be enabled automatically when the participants count threshold is reached.
+ // disableTileView: true,
+
+ // Hides the conference subject
+ // hideConferenceSubject: true
+
+ // Hides the conference timer.
+ // hideConferenceTimer: true,
+
+ // Hides the participants stats
+ // hideParticipantsStats: true
+
+ // Sets the conference subject
+ // subject: 'Conference Subject',
+
+ // List of undocumented settings used in jitsi-meet
+ /**
+ _immediateReloadThreshold
+ debug
+ debugAudioLevels
+ deploymentInfo
+ dialInConfCodeUrl
+ dialInNumbersUrl
+ dialOutAuthUrl
+ dialOutCodesUrl
+ disableRemoteControl
+ displayJids
+ etherpad_base
+ externalConnectUrl
+ firefox_fake_device
+ googleApiApplicationClientID
+ iAmRecorder
+ iAmSipGateway
+ microsoftApiApplicationClientID
+ peopleSearchQueryTypes
+ peopleSearchUrl
+ requireDisplayName
+ tokenAuthUrl
+ */
+
+ /**
+ * This property can be used to alter the generated meeting invite links (in combination with a branding domain
+ * which is retrieved internally by jitsi meet) (e.g. https://meet.jit.si/someMeeting
+ * can become https://brandedDomain/roomAlias)
+ */
+ // brandingRoomAlias: null,
+
+ // List of undocumented settings used in lib-jitsi-meet
+ /**
+ _peerConnStatusOutOfLastNTimeout
+ _peerConnStatusRtcMuteTimeout
+ abTesting
+ avgRtpStatsN
+ callStatsConfIDNamespace
+ callStatsCustomScriptUrl
+ desktopSharingSources
+ disableAEC
+ disableAGC
+ disableAP
+ disableHPF
+ disableNS
+ enableTalkWhileMuted
+ forceJVB121Ratio
+ forceTurnRelay
+ hiddenDomain
+ ignoreStartMuted
+ websocketKeepAlive
+ websocketKeepAliveUrl
+ */
+
+ /**
+ Use this array to configure which notifications will be shown to the user
+ The items correspond to the title or description key of that notification
+ Some of these notifications also depend on some other internal logic to be displayed or not,
+ so adding them here will not ensure they will always be displayed
+
+ A falsy value for this prop will result in having all notifications enabled (e.g null, undefined, false)
+ */
+ // notifications: [
+ // 'connection.CONNFAIL', // shown when the connection fails,
+ // 'dialog.cameraNotSendingData', // shown when there's no feed from user's camera
+ // 'dialog.kickTitle', // shown when user has been kicked
+ // 'dialog.liveStreaming', // livestreaming notifications (pending, on, off, limits)
+ // 'dialog.lockTitle', // shown when setting conference password fails
+ // 'dialog.maxUsersLimitReached', // shown when maximmum users limit has been reached
+ // 'dialog.micNotSendingData', // shown when user's mic is not sending any audio
+ // 'dialog.passwordNotSupportedTitle', // shown when setting conference password fails due to password format
+ // 'dialog.recording', // recording notifications (pending, on, off, limits)
+ // 'dialog.remoteControlTitle', // remote control notifications (allowed, denied, start, stop, error)
+ // 'dialog.reservationError',
+ // 'dialog.serviceUnavailable', // shown when server is not reachable
+ // 'dialog.sessTerminated', // shown when there is a failed conference session
+ // 'dialog.tokenAuthFailed', // show when an invalid jwt is used
+ // 'dialog.transcribing', // transcribing notifications (pending, off)
+ // 'dialOut.statusMessage', // shown when dial out status is updated.
+ // 'liveStreaming.busy', // shown when livestreaming service is busy
+ // 'liveStreaming.failedToStart', // shown when livestreaming fails to start
+ // 'liveStreaming.unavailableTitle', // shown when livestreaming service is not reachable
+ // 'lobby.joinRejectedMessage', // shown when while in a lobby, user's request to join is rejected
+ // 'lobby.notificationTitle', // shown when lobby is toggled and when join requests are allowed / denied
+ // 'localRecording.localRecording', // shown when a local recording is started
+ // 'notify.disconnected', // shown when a participant has left
+ // 'notify.grantedTo', // shown when moderator rights were granted to a participant
+ // 'notify.invitedOneMember', // shown when 1 participant has been invited
+ // 'notify.invitedThreePlusMembers', // shown when 3+ participants have been invited
+ // 'notify.invitedTwoMembers', // shown when 2 participants have been invited
+ // 'notify.kickParticipant', // shown when a participant is kicked
+ // 'notify.mutedRemotelyTitle', // shown when user is muted by a remote party
+ // 'notify.mutedTitle', // shown when user has been muted upon joining,
+ // 'notify.newDeviceAudioTitle', // prompts the user to use a newly detected audio device
+ // 'notify.newDeviceCameraTitle', // prompts the user to use a newly detected camera
+ // 'notify.passwordRemovedRemotely', // shown when a password has been removed remotely
+ // 'notify.passwordSetRemotely', // shown when a password has been set remotely
+ // 'notify.raisedHand', // shown when a partcipant used raise hand,
+ // 'notify.startSilentTitle', // shown when user joined with no audio
+ // 'prejoin.errorDialOut',
+ // 'prejoin.errorDialOutDisconnected',
+ // 'prejoin.errorDialOutFailed',
+ // 'prejoin.errorDialOutStatus',
+ // 'prejoin.errorStatusCode',
+ // 'prejoin.errorValidation',
+ // 'recording.busy', // shown when recording service is busy
+ // 'recording.failedToStart', // shown when recording fails to start
+ // 'recording.unavailableTitle', // shown when recording service is not reachable
+ // 'toolbar.noAudioSignalTitle', // shown when a broken mic is detected
+ // 'toolbar.noisyAudioInputTitle', // shown when noise is detected for the current microphone
+ // 'toolbar.talkWhileMutedPopup', // shown when user tries to speak while muted
+ // 'transcribing.failedToStart' // shown when transcribing fails to start
+ // ]
+
+ // Allow all above example options to include a trailing comma and
+ // prevent fear when commenting out the last value.
+ makeJsonParserHappy: 'even if last key had a trailing comma'
+
+ // no configuration value should follow this line.
+};
+
+/* eslint-enable no-unused-vars, no-var */
diff --git a/cluster/prod/app/jitsi/config/jicofo.conf b/cluster/prod/app/jitsi/config/jicofo.conf
new file mode 100644
index 0000000..5586348
--- /dev/null
+++ b/cluster/prod/app/jitsi/config/jicofo.conf
@@ -0,0 +1,273 @@
+jicofo {
+ // Authentication with external services
+ authentication {
+ enabled = false
+ // The type of authentication. Supported values are XMPP, JWT or SHIBBOLETH (default).
+ type = SHIBBOLETH
+
+ // The pattern of authentication URL. See ShibbolethAuthAuthority for more information.
+ # login-url =
+
+ # logout-url =
+
+ authentication-lifetime = 24 hours
+ enable-auto-login = true
+ }
+ // Configuration related to jitsi-videobridge
+ bridge {
+ // The maximum number of participants in a single conference to put on one bridge (use -1 for no maximum).
+ max-bridge-participants = -1
+ // The assumed maximum packet rate that a bridge can handle.
+ max-bridge-packet-rate = 50000
+ // The assumed average packet rate per participant.
+ average-participant-packet-rate-pps = 500
+ // The assumed average stress per participant.
+ average-participant-stress = 0.01
+ // The assumed time that an endpoint takes to start contributing fully to the load on a bridge. To avoid allocating
+ // a burst of endpoints to the same bridge, the bridge stress is adjusted by adding the number of new endpoints
+ // in the last [participant-rampup-time] multiplied by [average-participant-stress].
+ participant-rampup-interval = 20 seconds
+ // The stress level above which a bridge is considered overstressed.
+ stress-threshold = 0.8
+ // The amount of to wait before retrying using a failed bridge.
+ failure-reset-threshold = 1 minute
+ // The bridge selection strategy. The built-in strategies are:
+ // SingleBridgeSelectionStrategy: Use the least loaded bridge, do not split a conference between bridges (Octo).
+ // SplitBridgeSelectionStrategy: Use a separate bridge for each participant (for testing).
+ // RegionBasedBridgeSelectionStrategy: Attempt to put each participant in a bridge in their local region (i.e. use
+ // Octo for geo-location).
+ // IntraRegionBridgeSelectionStrategy: Use additional bridges when a bridge becomes overloaded (i.e. use Octo for
+ // load balancing).
+ //
+ // Additionally, you can use the fully qualified class name for custom BridgeSelectionStrategy implementations.
+ selection-strategy = SingleBridgeSelectionStrategy
+ health-checks {
+ // Whether jicofo should perform periodic health checks to the connected bridges.
+ enabled = true
+ // The interval at which to perform health checks.
+ interval = 10 seconds
+ // When a health checks times out, jicofo will retry and only consider it fail after the retry fails. This
+ // configures the delay between the original health check timing out and the second health check being sent.
+ // It is a duration and defaults to half the [interval].
+ # retry-delay = 5 seconds
+ }
+
+ // The JID of the MUC to be used as a brewery for bridge instances.
+ brewery-jid = "jvbbrewery@internal.auth.jitsi"
+ }
+ // Configure the codecs and RTP extensions to be used in the offer sent to clients.
+ codec {
+ video {
+ vp8 {
+ enabled = true
+ pt = 100
+ // Payload type for the associated RTX stream. Set to -1 to disable RTX.
+ rtx-pt = 96
+ }
+ vp9 {
+ enabled = true
+ pt = 101
+ // Payload type for the associated RTX stream. Set to -1 to disable RTX.
+ rtx-pt = 97
+ }
+ h264 {
+ enabled = true
+ pt = 107
+ // Payload type for the associated RTX stream. Set to -1 to disable RTX.
+ rtx-pt = 99
+ }
+ }
+
+ audio {
+ isac-16000 {
+ enabled = true
+ pt = 103
+ }
+ isac-32000 {
+ enabled = true
+ pt = 104
+ }
+ opus {
+ enabled = true
+ pt = 111
+ minptime = 10
+ use-inband-fec = true
+ red {
+ enabled = false
+ pt = 112
+ }
+ }
+ telephone-event {
+ enabled = true
+ pt = 126
+ }
+ }
+
+ // RTP header extensions
+ rtp-extensions {
+ audio-level {
+ enabled = true
+ id = 1
+ }
+ tof {
+ // TOF is currently disabled, because we don't support it in the bridge
+ // (and currently clients seem to not use it when abs-send-time is
+ // available).
+ enabled = false
+ id = 2
+ }
+ abs-send-time {
+ enabled = true
+ id = 3
+ }
+ rid {
+ enabled = false
+ id = 4
+ }
+ tcc {
+ enabled = true
+ id = 5
+ }
+ video-content-type {
+ enabled = false
+ id = 7
+ }
+ framemarking {
+ enabled = false
+ id = 9
+ }
+ }
+ }
+
+ conference {
+ // Whether to automatically grant the 'owner' role to the first participant in the conference (and subsequently to
+ // the next in line when the current owner leaves).
+ enable-auto-owner = true
+
+ // How long to wait for the initial participant in a conference.
+ initial-timeout = 15 seconds
+
+ // Whether jicofo should inject a random SSRC for endpoints which don't advertise any SSRCs. This is a temporary
+ // workaround for an issue with signaling endpoints for Octo.
+ inject-ssrc-for-recv-only-endpoints = false
+
+ max-ssrcs-per-user = 20
+
+ // How long a participant's media session will be kept alive once it remains the only participant in the room.
+ single-participant-timeout = 20 seconds
+
+ // The minimum number of participants required for the conference to be started.
+ min-participants = 2
+
+ // Experimental.
+ enable-lip-sync = false
+
+ shared-document {
+ // If `true` the shared document uses a random name. Otherwise, it uses the conference name.
+ use-random-name = false
+ }
+ }
+
+ // Configuration for the internal health checks performed by jicofo.
+ health {
+ // Whether to perform health checks.
+ enabled = false
+
+ // The interval between health checks. If set to 0, periodic health checks will not be performed.
+ interval = 10 seconds
+
+ # The timeout for a health check
+ timeout = 30 seconds
+
+ # If performing a health check takes longer than this, it is considered unsuccessful.
+ max-check-duration = 20 seconds
+
+ # The prefix to use when creating MUC rooms for the purpose of health checks.
+ room-name-prefix = "__jicofo-health-check"
+ }
+
+ jibri {
+ // The JID of the MUC to be used as a brewery for jibri instances for streaming.
+ # brewery-jid = "jibribrewery@example.com"
+
+ // How many times to retry a given Jibri request before giving up. Set to -1 to allow infinite retries.
+ num-retries = 5
+
+ // How long to wait for Jibri to start recording from the time it accepts a START request.
+ pending-timeout = 90 seconds
+ }
+
+ jibri-sip {
+ // The JID of the MUC to be used as a brewery for jibri instances for SIP.
+ # brewery-jid = "jibrisipbrewery@example.com"
+ }
+
+ jigasi {
+ // The JID of the MUC to be used as a brewery for jigasi instances.
+ # brewery-jid = "jigasibrewery@example.com"
+ }
+
+ // The region in which the machine is running.
+ #local-region="us-east-1"
+
+ octo {
+ // Whether or not to use Octo. Note that when enabled, its use will be determined by
+ // $jicofo.bridge.selection-strategy.
+ enabled = false
+
+ // An identifier of the Jicofo instance, used for the purpose of generating conference IDs unique across a set of
+ // Jicofo instances. Valid values are [1, 65535]. The value 0 is used when none is explicitly configured.
+ id = 1
+ }
+
+ rest {
+ port = 8888
+ tls-port = 8843
+ }
+
+ sctp {
+ // Whether to allocate SCTP channels on the bridge (only when the client advertises support, and SCTP is
+ // enabled in the per-conference configuration).
+ enabled = true
+ }
+
+ task-pools {
+ shared-pool-max-threads = 1500
+ }
+
+ xmpp {
+ // The separate XMPP connection used for communication with clients (endpoints).
+ client {
+ enabled = true
+ hostname = "{{ env "NOMAD_IP_xmpp_port" }}"
+ port = {{ env "NOMAD_PORT_xmpp_port" }}
+ domain = "auth.jitsi"
+ username = "focus"
+ password = "{{ key "secrets/jitsi/jicofo_pass" | trimSpace }}"
+
+ // How long to wait for a response to a stanza before giving up.
+ reply-timeout = 15 seconds
+
+ // The JID/domain of the MUC service used for conferencing.
+ conference-muc-jid = conference.jitsi
+
+ // A flag to suppress the TLS certificate verification.
+ disable-certificate-verification = false
+ }
+ // The separate XMPP connection used for internal services (currently only jitsi-videobridge).
+ service {
+ enabled = false
+ hostname = "jitsi-xmpp"
+ port = 5222
+ domain = "auth.jitsi"
+ username = "focus"
+ password = "jicofopass"
+
+ // How long to wait for a response to a stanza before giving up.
+ reply-timeout = 15 seconds
+
+ // A flag to suppress the TLS certificate verification.
+ disable-certificate-verification = false
+ }
+ }
+}
diff --git a/cluster/prod/app/jitsi/config/nginx.conf b/cluster/prod/app/jitsi/config/nginx.conf
new file mode 100644
index 0000000..32cc3c1
--- /dev/null
+++ b/cluster/prod/app/jitsi/config/nginx.conf
@@ -0,0 +1,133 @@
+# some doc: https://www.nginx.com/resources/wiki/start/topics/examples/full/
+error_log /dev/stderr info;
+
+events {}
+
+http {
+ ##
+ # Basic Settings
+ ##
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+
+
+ # mimetypes, required by jitsi!
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ types {
+ application/wasm wasm;
+ }
+
+ ##
+ # SSL Settings
+ ##
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
+ ssl_prefer_server_ciphers on;
+
+ ##
+ # Gzip Settings
+ ##
+ gzip on;
+
+ access_log /dev/stdout;
+ server_names_hash_bucket_size 64;
+
+ # inspired by https://raw.githubusercontent.com/jitsi/docker-jitsi-meet/master/web/rootfs/defaults/meet.conf
+ server {
+ #listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
+ #listen [::]:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
+ listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} default_server;
+ listen [::]:{{ env "NOMAD_PORT_https_port" }} default_server;
+ client_max_body_size 0;
+ server_name _;
+
+ # ssi on with javascript for multidomain variables in config.js
+ ssi on;
+ ssi_types application/x-javascript application/javascript;
+
+ #ssl_certificate /etc/nginx/jitsi.crt;
+ #ssl_certificate_key /etc/nginx/jitsi.key;
+ root /srv/jitsi-meet;
+ index index.html;
+ error_page 404 /static/404.html;
+
+ location = /config.js {
+ alias /srv/jitsi-meet/config.js;
+ }
+
+ location = /interface_config.js {
+ alias /srv/jitsi-meet/interface_config.js;
+ }
+
+ location = /external_api.js {
+ alias /srv/jitsi-meet/libs/external_api.min.js;
+ }
+
+ # ensure all static content can always be found first
+ location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$
+ {
+ add_header 'Access-Control-Allow-Origin' '*';
+ alias /srv/jitsi-meet/$1/$2;
+ }
+
+ # not used yet VVV
+ # colibri (JVB) websockets
+ #location ~ ^/colibri-ws/([a-zA-Z0-9-\.]+)/(.*) {
+ # proxy_pass http://$1:9090/colibri-ws/$1/$2$is_args$args;
+ # proxy_http_version 1.1;
+ # proxy_set_header Upgrade $http_upgrade;
+ # proxy_set_header Connection "upgrade";
+ # tcp_nodelay on;
+ #}
+
+ location = /http-bind {
+ # We add CORS to use a different frontend which is useful for load testing as we do not want to advertise too much our URL
+ add_header 'Access-Control-Allow-Headers' 'content-type';
+ add_header 'Access-Control-Allow-Methods' 'GET,POST,PUT,DELETE,OPTIONS';
+ add_header 'Access-Control-Allow-Origin' '*';
+ proxy_pass http://{{ env "NOMAD_ADDR_bosh_port" }}/http-bind;
+ proxy_set_header X-Forwarded-For \$remote_addr;
+ #proxy_set_header Host \$http_host;
+ }
+
+ # not used yet VVV
+ # xmpp websockets
+ #location = /xmpp-websocket {
+ # proxy_pass {{ .Env.XMPP_BOSH_URL_BASE }}/xmpp-websocket;
+ # proxy_http_version 1.1;
+ # proxy_set_header Connection "upgrade";
+ # proxy_set_header Upgrade $http_upgrade;
+ # proxy_set_header Host {{ .Env.XMPP_DOMAIN }};
+ # proxy_set_header X-Forwarded-For $remote_addr;
+ # tcp_nodelay on;
+ #}
+
+ location ~ ^/([^/?&:'"]+)$ {
+ try_files $uri @root_path;
+ }
+
+ location @root_path {
+ rewrite ^/(.*)$ / break;
+ }
+
+ # Not used yet VVVV
+ # Etherpad-lite
+ # location /etherpad/ {
+ # proxy_http_version 1.1;
+ # proxy_set_header Upgrade $http_upgrade;
+ # proxy_set_header Connection 'upgrade';
+ # proxy_set_header Host $host;
+ # proxy_cache_bypass $http_upgrade;
+ # proxy_pass {{ .Env.ETHERPAD_URL_BASE }}/;
+ # proxy_set_header X-Forwarded-For $remote_addr;
+ # proxy_buffering off;
+ # proxy_set_header Host {{ .Env.XMPP_DOMAIN }};
+ # }
+
+ }
+}
diff --git a/cluster/prod/app/jitsi/config/prosody.cfg.lua b/cluster/prod/app/jitsi/config/prosody.cfg.lua
new file mode 100644
index 0000000..7141f8b
--- /dev/null
+++ b/cluster/prod/app/jitsi/config/prosody.cfg.lua
@@ -0,0 +1,135 @@
+modules_enabled = {
+ "roster"; -- Allow users to have a roster. Recommended ;)
+ "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+ "tls"; -- Add support for secure TLS on c2s/s2s connections
+ "dialback"; -- s2s dialback support
+ "disco"; -- Service discovery
+ "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
+ "version"; -- Replies to server version requests
+ "uptime"; -- Report how long server has been running
+ "time"; -- Let others know the time here on this server
+ "ping"; -- Replies to XMPP pings with pongs
+ "pep"; -- Enables users to publish their mood, activity, playing music and more
+ -- jitsi
+ --"smacks"; -- not shipped with prosody
+ "carbons";
+ "mam";
+ "lastactivity";
+ "offline";
+ "pubsub";
+ "adhoc";
+ "websocket";
+ --"http_altconnect"; -- not shipped with prosody
+}
+modules_disabled = { "s2s" }
+
+plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
+
+log = {
+ --log less on console with warn="*console"; or err="*console" or more with debug="*console"
+ info="*console";
+}
+daemonize = false
+use_libevent = true
+
+-- domain mapper options, must at least have domain base set to use the mapper
+muc_mapper_domain_base = "jitsi.deuxfleurs.fr";
+
+--@FIXME would be great to configure it
+--turncredentials_secret = "__turnSecret__";
+
+--turncredentials = {
+-- { type = "stun", host = "jitmeet.example.com", port = "3478" },
+-- { type = "turn", host = "jitmeet.example.com", port = "3478", transport = "udp" },
+-- { type = "turns", host = "jitmeet.example.com", port = "5349", transport = "tcp" }
+--};
+
+cross_domain_bosh = false;
+consider_bosh_secure = true;
+component_ports = { } -- it seems we don't need external components for now...
+https_ports = { } -- we don't need https
+http_ports = { {{env "NOMAD_PORT_bosh_port" }} }
+c2s_ports = { {{env "NOMAD_PORT_xmpp_port" }} }
+
+
+-- https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
+ssl = {
+ protocol = "tlsv1_2+";
+ ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
+}
+
+VirtualHost "jitsi"
+ enabled = true -- Remove this line to enable this host
+ authentication = "anonymous"
+ -- Properties below are modified by jitsi-meet-tokens package config
+ -- and authentication above is switched to "token"
+ --app_id="example_app_id"
+ --app_secret="example_app_secret"
+ -- Assign this host a certificate for TLS, otherwise it would use the one
+ -- set in the global section (if any).
+ -- Note that old-style SSL on port 5223 only supports one certificate, and will always
+ -- use the global one.
+ ssl = {
+ key = "/var/lib/prosody/jitsi.key";
+ certificate = "/var/lib/prosody/jitsi.crt";
+ }
+ speakerstats_component = "speakerstats.jitsi"
+ conference_duration_component = "conferenceduration.jitsi"
+ -- we need bosh
+ modules_enabled = {
+ "bosh";
+ "pubsub";
+ "ping"; -- Enable mod_ping
+ "speakerstats";
+ --"turncredentials"; not supported yet
+ "conference_duration";
+ "muc_lobby_rooms";
+ }
+ c2s_require_encryption = false
+ lobby_muc = "lobby.jitsi"
+ main_muc = "conference.jitsi"
+ -- muc_lobby_whitelist = { "recorder.jitmeet.example.com" } -- Here we can whitelist jibri to enter lobby enabled rooms
+
+Component "conference.jitsi" "muc"
+ storage = "memory"
+ modules_enabled = {
+ "muc_meeting_id";
+ "muc_domain_mapper";
+ --"token_verification";
+ }
+ admins = { "focus@auth.jitsi" }
+ muc_room_locking = false
+ muc_room_default_public_jids = true
+
+-- internal muc component
+Component "internal.auth.jitsi" "muc"
+ storage = "memory"
+ modules_enabled = {
+ "ping";
+ }
+ admins = { "focus@auth.jitsi", "jvb@auth.jitsi" }
+ muc_room_locking = false
+ muc_room_default_public_jids = true
+
+VirtualHost "auth.jitsi"
+ ssl = {
+ key = "/var/lib/prosody/auth.jitsi.key";
+ certificate = "/var/lib/prosody/auth.jitsi.crt";
+ }
+ authentication = "internal_plain"
+
+Component "focus.jitsi" "client_proxy"
+ target_address = "focus@auth.jitsi"
+
+Component "speakerstats.jitsi" "speakerstats_component"
+ muc_component = "conference.jitsi"
+
+Component "conferenceduration.jitsi" "conference_duration_component"
+ muc_component = "conference.jitsi"
+
+Component "lobby.jitsi" "muc"
+ storage = "memory"
+ restrict_room_creation = true
+ muc_room_locking = false
+ muc_room_default_public_jids = true
+
diff --git a/cluster/prod/app/jitsi/config/videobridge.conf b/cluster/prod/app/jitsi/config/videobridge.conf
new file mode 100644
index 0000000..a7c166a
--- /dev/null
+++ b/cluster/prod/app/jitsi/config/videobridge.conf
@@ -0,0 +1,290 @@
+videobridge {
+ entity-expiration {
+ # If an entity has no activity after this timeout, it is expired
+ timeout=1 minute
+
+ # The interval at which the videobridge will check for expired entities
+ check-interval=${videobridge.entity-expiration.timeout}
+ }
+ health {
+ # The interval between health checks
+ interval=10 seconds
+
+ # The timeout for a health check
+ timeout=30 seconds
+
+ # If performing a health check takes longer than this, it is considered unsuccessful.
+ max-check-duration=3 seconds
+
+ # Whether or not health check failures should be 'sticky'
+ # (i.e. once the bridge becomes unhealthy, it will never
+ # go back to a healthy state)
+ sticky-failures=false
+ }
+ ep-connection-status {
+ # How long we'll wait for an endpoint to *start* sending
+ # data before we consider it 'inactive'
+ first-transfer-timeout=15 seconds
+
+ # How long an endpoint can be 'inactive' before it will
+ # be considered disconnected
+ max-inactivity-limit=3 seconds
+
+ # How often we check endpoint's connectivity status
+ check-interval=500 milliseconds
+ }
+ cc {
+ bwe-change-threshold=0.15
+ thumbnail-max-height-px=180
+ onstage-ideal-height-px=1080
+ onstage-preferred-height-px=360
+ onstage-preferred-framerate=30
+ enable-onstage-video-suspend=false
+ trust-bwe=true
+
+ # How often we check to send probing data
+ padding-period=15ms
+
+ # How often we'll force recalculations of forwarded
+ # streams
+ max-time-between-calculations = 15 seconds
+
+ # A JVB-wide last-n value, observed by all endpoints. Endpoints
+ # will take the minimum of their setting and this one (-1 implies
+ # no last-n limit)
+ jvb-last-n = -1
+ }
+ # The APIs by which the JVB can be controlled
+ apis {
+ xmpp-client {
+ # The interval at which presence is published in the configured MUCs.
+ presence-interval = ${videobridge.stats.interval}
+
+ configs {
+ unique-xmpp-server {
+ hostname="{{ env "NOMAD_IP_xmpp_port" }}"
+ port = {{ env "NOMAD_PORT_xmpp_port" }}
+ domain = "auth.jitsi"
+ username = "jvb"
+ password = "{{ key "secrets/jitsi/jvb_pass" | trimSpace }}"
+ muc_jids = "jvbbrewery@internal.auth.jitsi"
+ # The muc_nickname must be unique across all jitsi-videobridge instances
+ muc_nickname = "unique-jvb-server"
+ disable_certificate_verification = false
+ }
+ # example-connection-id {
+ # For the properties which should be
+ # filled out here, see MucClientConfiguration
+ # }
+ }
+ }
+ # The COLIBRI REST API
+ rest {
+ enabled = true
+ }
+ jvb-api {
+ enabled = true
+ }
+ }
+ # Configuration of the different REST APIs.
+ # Note that the COLIBRI REST API is configured under videobridge.apis.rest instead.
+ rest {
+ debug {
+ enabled = true
+ }
+ health {
+ enabled = true
+ }
+ shutdown {
+ # Note that the shutdown API requires the COLIBRI API to also be enabled.
+ enabled = false
+ }
+ version {
+ enabled = true
+ }
+ }
+ http-servers {
+ # The HTTP server which hosts services intended for 'public' use
+ # (e.g. websockets for the bridge channel connection)
+ public {
+ # See JettyBundleActivatorConfig in Jicoco for values
+ port = -1
+ tls-port = -1
+ }
+ # The HTTP server which hosts services intended for 'private' use
+ # (e.g. health or debug stats)
+ private {
+ # See JettyBundleActivatorConfig in Jicoco for values
+ host = 127.0.0.1
+ }
+ }
+ octo {
+ # Whether or not Octo is enabled
+ enabled=false
+
+ # A string denoting the 'region' of this JVB. This region
+ # will be used by Jicofo in the selection of a bridge for
+ # a client by comparing it to the client's region.
+ # Must be set when 'enabled' is true
+ #region="us-west-1"
+
+ # The address on which the Octo relay should bind
+ # Must be set when 'enabled' is true
+ #bind-address=198.51.100.1
+
+ # The port to which the Octo relay should bind
+ bind-port=4096
+
+ # The address which controls the public address which
+ # will be part of the Octo relayId
+ #public-address=198.51.100.1
+
+ # The size of the incoming octo queue. This queue is per-remote-endpoint,
+ # so it matches what we use for local endpoints
+ recv-queue-size=1024
+
+ # The size of the outgoing octo queue. This is a per-originating-endpoint
+ # queue, so assuming all packets are routed (as they currently are for Octo)
+ # it should be the same size as the transceiver recv queue in
+ # jitsi-media-transform. Repeating the description from there:
+ # Assuming 300pps for high-definition, 200pps for standard-definition,
+ # 100pps for low-definition and 50pps for audio, this queue is fed
+ # 650pps, so its size in terms of millis is 1024/650*1000 ~= 1575ms.
+ send-queue-size=1024
+ }
+ load-management {
+ # Whether or not the reducer will be enabled to take actions to mitigate load
+ reducer-enabled = false
+ load-measurements {
+ packet-rate {
+ # The packet rate at which we'll consider the bridge overloaded
+ load-threshold = 50000
+ # The packet rate at which we'll consider the bridge 'underloaded' enough
+ # to start recovery
+ recovery-threshold = 40000
+ }
+ }
+ load-reducers {
+ last-n {
+ # The factor by which we'll reduce the current last-n when trying to reduce load
+ reduction-scale = .75
+ # The factor by which we'll increase the current last-n when trying to recover
+ recover-scale = 1.25
+ # The minimum time in between runs of the last-n reducer to reduce or recover from
+ # load
+ impact-time = 1 minute
+ # The lowest value we'll set for last-n
+ minimum-last-n-value = 0
+ # The highest last-n value we'll enforce. Once the enforced last-n exceeds this value
+ # we'll remove the limit entirely
+ maximum-enforced-last-n-value = 40
+ }
+ }
+ }
+ sctp {
+ # Whether SCTP data channels are enabled.
+ enabled=true
+ }
+ stats {
+ # Whether periodic collection of statistics is enabled or not. When enabled they are accessible through the REST
+ # API (at `/colibri/stats`), and are available to other modules (e.g. to be pushed to callstats or in a MUC).
+ enabled = true
+
+ # The interval at which stats are gathered.
+ interval = 5 seconds
+
+ # Configuration related to pushing statistics to callstats.io.
+ callstats {
+ # An integer application ID (use 0 to disable pushing stats to callstats).
+ app-id = 0
+
+ # The shared secred to authentication with callstats.io.
+ //app-secret = "s3cret"
+
+ # ID of the key that was used to generate token.
+ //key-id = "abcd"
+
+ # The path to private key file.
+ //key-path = "/etc/jitsi/videobridge/ecpriv.jwk"
+
+ # The ID of the server instance to be used when reporting to callstats.
+ bridge-id = "jitsi"
+
+ # TODO: document
+ //conference-id-prefix = "abcd"
+
+ # The interval at which statististics will be published to callstats. This affects both per-conference and global
+ # statistics.
+ # Note that this value will be overriden if a "callstatsio" transport is defined in the parent "stats" section.
+ interval = ${videobridge.stats.interval}
+ }
+ }
+ websockets {
+ enabled=false
+ server-id="default-id"
+
+ # Optional, even when 'enabled' is set to true
+ # tls=true
+ # Must be set when enabled = true
+ #domain="some-domain"
+ }
+ ice {
+ tcp {
+ # Whether ICE/TCP is enabled.
+ enabled = true
+
+ # The port to bind to for ICE/TCP.
+ port = {{ env "NOMAD_PORT_video_port" }}
+
+ # An optional additional port to advertise.
+ # mapped-port = 8443
+ # Whether to use "ssltcp" or plain "tcp".
+ ssltcp = true
+ }
+
+ udp {
+ # The port for ICE/UDP.
+ port = {{ env "NOMAD_PORT_video_port" }}
+ }
+
+ # An optional prefix to include in STUN username fragments generated by the bridge.
+ #ufrag-prefix = "jvb-123:"
+
+ # Which candidate pairs to keep alive. The accepted values are defined in ice4j's KeepAliveStrategy:
+ # "selected_and_tcp", "selected_only", or "all_succeeded".
+ keep-alive-strategy = "selected_and_tcp"
+
+ # Whether to use the "component socket" feature of ice4j.
+ use-component-socket = true
+
+ # Whether to attempt DNS resolution for remote candidates that contain a non-literal address. When set to 'false'
+ # such candidates will be ignored.
+ resolve-remote-candidates = false
+
+ # The nomination strategy to use for ICE. THe accepted values are defined in ice4j's NominationStrategy:
+ # "NominateFirstValid", "NominateHighestPriority", "NominateFirstHostOrReflexiveValid", or "NominateBestRTT".
+ nomination-strategy = "NominateFirstValid"
+ }
+
+ transport {
+ send {
+ # The size of the dtls-transport outgoing queue. This is a per-participant
+ # queue. Packets from the egress end-up in this queue right before
+ # transmission by the outgoing srtp pipeline (which mainly consists of the
+ # packet sender).
+ #
+ # Its size needs to be of the same order of magnitude as the rtp sender
+ # queue. In a 100 participant call, assuming 300pps for the on-stage and
+ # 100pps for low-definition, last-n 20 and 2 participants talking, so
+ # 2*50pps for audio, this queue is fed 300+19*100+2*50 = 2300pps, so its
+ # size in terms of millis is 1024/2300*1000 ~= 445ms.
+ queue-size=1024
+ }
+ }
+
+ version {
+ // Wheather to announe the jitsi-videobridge version to clients in the ServerHello message.
+ announce = false
+ }
+}
+
diff --git a/cluster/prod/app/jitsi/deploy/jitsi.hcl b/cluster/prod/app/jitsi/deploy/jitsi.hcl
new file mode 100644
index 0000000..7e12ae3
--- /dev/null
+++ b/cluster/prod/app/jitsi/deploy/jitsi.hcl
@@ -0,0 +1,257 @@
+job "jitsi" {
+ datacenters = ["neptune"]
+ type = "service"
+
+ priority = "10"
+
+ constraint {
+ attribute = "${attr.cpu.arch}"
+ value = "amd64"
+ }
+
+ group "core" {
+
+ network {
+ port "bosh_port" { }
+ port "xmpp_port" { }
+ port "https_port" { }
+ port "video_port" { static = 8080 }
+ }
+
+ task "xmpp" {
+ driver = "docker"
+ config {
+ image = "superboum/amd64_jitsi_xmpp:v10"
+ ports = [ "bosh_port", "xmpp_port" ]
+ network_mode = "host"
+ volumes = [
+ "secrets/prosody.cfg.lua:/etc/prosody/prosody.cfg.lua",
+ "secrets/certs/auth.jitsi.crt:/var/lib/prosody/auth.jitsi.crt",
+ "secrets/certs/auth.jitsi.key:/var/lib/prosody/auth.jitsi.key",
+ "secrets/certs/jitsi.crt:/var/lib/prosody/jitsi.crt",
+ "secrets/certs/jitsi.key:/var/lib/prosody/jitsi.key"
+ ]
+ }
+
+ template {
+ data = <<EOF
+JICOFO_AUTH_PASSWORD={{ key "secrets/jitsi/jicofo_pass" | trimSpace }}
+JVB_AUTH_PASSWORD={{ key "secrets/jitsi/jvb_pass" | trimSpace }}
+EOF
+ destination = "secrets/global_env"
+ env = true
+ }
+
+ template {
+ data = file("../config/prosody.cfg.lua")
+ destination = "secrets/prosody.cfg.lua"
+ }
+
+ # --- secrets ---
+ template {
+ data = "{{ key \"secrets/jitsi/auth.jitsi.crt\" }}"
+ destination = "secrets/certs/auth.jitsi.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/jitsi/auth.jitsi.key\" }}"
+ destination = "secrets/certs/auth.jitsi.key"
+ }
+
+ template {
+ data = "{{ key \"secrets/jitsi/jitsi.crt\" }}"
+ destination = "secrets/certs/jitsi.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/jitsi/jitsi.key\" }}"
+ destination = "secrets/certs/jitsi.key"
+ }
+
+ resources {
+ cpu = 300
+ memory = 200
+ }
+
+ service {
+ tags = [ "jitsi", "bosh" ]
+ port = "bosh_port"
+ address_mode = "host"
+ name = "bosh-jitsi"
+ check {
+ type = "tcp"
+ port = "bosh_port"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ service {
+ tags = [ "jitsi", "xmpp" ]
+ port = "xmpp_port"
+ address_mode = "host"
+ name = "xmpp-jitsi"
+ }
+ }
+
+ task "front" {
+ driver = "docker"
+ config {
+ image = "superboum/amd64_jitsi_meet:v5"
+ network_mode = "host"
+ ports = [ "https_port" ]
+ volumes = [
+ "secrets/certs/jitsi.crt:/etc/nginx/jitsi.crt",
+ "secrets/certs/jitsi.key:/etc/nginx/jitsi.key",
+ "secrets/config.js:/srv/jitsi-meet/config.js",
+ "secrets/nginx.conf:/etc/nginx/nginx.conf"
+ ]
+ }
+
+ template {
+ data = file("../config/config.js")
+ destination = "secrets/config.js"
+ }
+
+ template {
+ data = file("../config/nginx.conf")
+ destination = "secrets/nginx.conf"
+ }
+
+ # --- secrets ---
+ template {
+ data = "{{ key \"secrets/jitsi/jitsi.crt\" }}"
+ destination = "secrets/certs/jitsi.crt"
+ }
+ template {
+ data = "{{ key \"secrets/jitsi/jitsi.key\" }}"
+ destination = "secrets/certs/jitsi.key"
+ }
+
+ resources {
+ cpu = 300
+ memory = 200
+ }
+
+ service {
+ tags = [
+ "jitsi",
+ "tricot jitsi.deuxfleurs.fr",
+ ]
+ port = "https_port"
+ address_mode = "host"
+ name = "https-jitsi"
+ check {
+ type = "tcp"
+ port = "https_port"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+ }
+
+ task "jicofo" {
+ driver = "docker"
+ config {
+ image = "superboum/amd64_jitsi_conference_focus:v9"
+ network_mode = "host"
+ volumes = [
+ "secrets/certs/jitsi.crt:/usr/local/share/ca-certificates/jitsi.crt",
+ "secrets/certs/auth.jitsi.crt:/usr/local/share/ca-certificates/auth.jitsi.crt",
+ "secrets/jicofo.conf:/etc/jitsi/jicofo.conf"
+ ]
+ }
+
+ template {
+ data = file("../config/jicofo.conf")
+ destination = "secrets/jicofo.conf"
+ }
+
+ #--- secrets ---
+ template {
+ data = "{{ key \"secrets/jitsi/jitsi.crt\" }}"
+ destination = "secrets/certs/jitsi.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/jitsi/auth.jitsi.crt\" }}"
+ destination = "secrets/certs/auth.jitsi.crt"
+ }
+
+ resources {
+ cpu = 300
+ memory = 400
+ }
+ }
+
+ task "videobridge" {
+ driver = "docker"
+ config {
+ image = "superboum/amd64_jitsi_videobridge:v20"
+ network_mode = "host"
+ ports = [ "video_port" ]
+ ulimit {
+ nofile = "1048576:1048576"
+ nproc = "65536:65536"
+ }
+ volumes = [
+ "secrets/certs/jitsi.crt:/usr/local/share/ca-certificates/jitsi.crt",
+ "secrets/certs/auth.jitsi.crt:/usr/local/share/ca-certificates/auth.jitsi.crt",
+ "secrets/videobridge.conf:/etc/jitsi/videobridge.conf"
+ ]
+ }
+
+ env {
+ # Our container can autodetect the public IP with the ifconfig.me service
+ # However we would like to avoid relying on a 3rd party service for production use
+ # That's why I am setting the public IP address statically here VVVV
+ JITSI_NAT_PUBLIC_IP = "77.207.15.215"
+ }
+
+ template {
+ data = file("../config/videobridge.conf")
+ destination = "secrets/videobridge.conf"
+ }
+
+ # --- secrets ---
+ template {
+ data = "{{ key \"secrets/jitsi/jitsi.crt\" }}"
+ destination = "secrets/certs/jitsi.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/jitsi/auth.jitsi.crt\" }}"
+ destination = "secrets/certs/auth.jitsi.crt"
+ }
+
+ resources {
+ cpu = 900
+ memory = 3000
+ }
+
+ service {
+ tags = [ "jitsi", "(diplonat (tcp_port 8080) (udp_port 8080))" ]
+ port = "video_port"
+ address_mode = "host"
+ name = "video-jitsi"
+ check {
+ type = "tcp"
+ port = "video_port"
+ interval = "60s"
+ timeout = "5s"
+ }
+ }
+ }
+ }
+}
+
diff --git a/cluster/prod/app/jitsi/integration/README.md b/cluster/prod/app/jitsi/integration/README.md
new file mode 100644
index 0000000..97a559e
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/README.md
@@ -0,0 +1,91 @@
+## About Jitsi
+
+Several server components:
+ - prosody XMPP (ext)
+ - jitsi videobridge aka JVB
+ - jitsi conference focus aka jicofo aka focus
+ - jitsi meet
+ - octo
+ - jigasi
+ - jibri
+ - etc.
+
+Some libs:
+ - libjitsi seems deprecated
+ - jicoco contains some parent classes to handle Jitsi's Configuration
+ - [jitsi-utils](https://github.com/jitsi/jitsi-utils) contains the Logger definition for example
+ - [ice4j](https://github.com/jitsi/ice4j) contains jitsi's implementation of WebRTC
+ - etc.
+
+Client components:
+ - jitsi meet electron
+ - jitsi android/ios
+ - etc.
+
+## Conf
+
+Base conf:
+
+ - [videobridge.conf](https://github.com/jitsi/jitsi-videobridge/blob/master/jvb/src/main/resources/reference.conf)
+ - [jicofo.conf](https://github.com/jitsi/jicofo/blob/master/src/main/resources/reference.conf)
+
+the following is used in videobridge.conf:
+[jicoco/MucClientConfiguration](https://github.com/jitsi/jicoco/blob/master/jicoco/src/main/java/org/jitsi/xmpp/mucclient/MucClientConfiguration.java)
+
+How the new configuration is read in jicoco:
+https://github.com/jitsi/jicoco/blob/master/jicoco-config/src/main/kotlin/org/jitsi/config/JitsiConfig.kt#L83-L91
+They use this library: https://github.com/lightbend/config
+We are particularly interested by: https://github.com/lightbend/config#standard-behavior
+Using 'application.conf' with classpath does not seem to work.
+But, specifying the file path as `-Dconfig.file=/etc/jitsi/jicofo.conf` works!
+
+Some parameters are also set independently of lightbend hocon config.
+They are seen in jicofo entrypoint:
+https://github.com/jitsi/jicofo/blob/master/src/main/java/org/jitsi/jicofo/Main.java
+Many of these parameters can be in fact read from the HOCON file except one: the `--secret` parameter or the `JICOFO_SECRET` env variable.
+But we can see this is a deprecated thing, it has been already removed from master: https://github.com/jitsi/jicofo/commit/c9e5b50a8b4e77f8b8cb8831a4a044a53edfcf48
+For now (as per v5390) we will keep `JICOFO_SECRET` environment variable but will assume no other environment variable is set
+But maybe this value is deprecated: the check is still here but it is not used anymore?!
+
+## Generate certs with prosody
+```
+prosodyctl cert generate auth.jitsi
+prosodyctl cert generate jitsi
+```
+
+## An example prosody configuration file
+
+https://github.com/jitsi/jitsi-meet/blob/master/doc/example-config-files/prosody.cfg.lua.example
+
+but this one is not the one used by the [debian postinst script](https://github.com/jitsi/jitsi-meet/blob/master/debian/jitsi-meet-prosody.postinst)
+instead, we should look at this one: https://github.com/jitsi/jitsi-meet/blob/master/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example
+
+Jitsi can be configured to authenticated through tokens,
+the postinst file is here: https://github.com/jitsi/jitsi-meet/blob/master/debian/jitsi-meet-tokens.postinst
+
+## Remote debug
+
+Add this parameter to the java process you want to debug (either jicofo or jvb). It must be added by modifying the entrypoint script, next to the respective Dockerfile of each container.
+
+```
+-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005
+```
+
+## Be careful
+
+jiti-videobridge (jvb) does not start to listen on ICE ports (both TCP and UDP) at boot.
+Instead, listening is triggered on the creation of the first conference (a 2 people P2P conference is enough).
+A nice entrypoint to check with your debugger is:
+ - [Videobridge.java#XmppConnectionEventHandle.colibriConferenceIqReceived](https://github.com/jitsi/jitsi-videobridge/blob/256dc7acb7ee10440502a6073a498329eaf1e819/jvb/src/main/java/org/jitsi/videobridge/Videobridge.java#L627)
+ - [VideobridgeShim.java#VideobridgeShim.handleColibriConferenceIQ](https://github.com/jitsi/jitsi-videobridge/blob/256dc7acb7ee10440502a6073a498329eaf1e819/jvb/src/main/java/org/jitsi/videobridge/shim/VideobridgeShim.java#L251)
+ - [ConferenceShim.java#ConferenceShim.initializeSignaledEndpoints](https://github.com/jitsi/jitsi-videobridge/blob/256dc7acb7ee10440502a6073a498329eaf1e819/jvb/src/main/java/org/jitsi/videobridge/shim/ConferenceShim.java#L274)
+ - [ConferenceShim.java#ConferenceShim.ensureEndpointCreated](https://github.com/jitsi/jitsi-videobridge/blob/256dc7acb7ee10440502a6073a498329eaf1e819/jvb/src/main/java/org/jitsi/videobridge/shim/ConferenceShim.java#L312)
+ - [Conference.java#Conference.createLocalEndpoint](https://github.com/jitsi/jitsi-videobridge/blob/256dc7acb7ee10440502a6073a498329eaf1e819/jvb/src/main/java/org/jitsi/videobridge/Conference.java#L602)
+ - [Endpoint.java#Endpoint.new](https://github.com/jitsi/jitsi-videobridge/blob/256dc7acb7ee10440502a6073a498329eaf1e819/jvb/src/main/java/org/jitsi/videobridge/Endpoint.java#L254)
+ - [IceTransport.kt#IceTransport.iceAgent(init)](https://github.com/jitsi/jitsi-videobridge/blob/0c2ac250ec6b518eaf75fbc83f7936ec01e7b5f6/jvb/src/main/kotlin/org/jitsi/videobridge/transport/ice/IceTransport.kt#L99)
+ - [IceTransport.kt#companionObject.appendHarvesters](https://github.com/jitsi/jitsi-videobridge/blob/0c2ac250ec6b518eaf75fbc83f7936ec01e7b5f6/jvb/src/main/kotlin/org/jitsi/videobridge/transport/ice/IceTransport.kt#L350)
+
+## Resources to understand jitsi
+
+ - [jicofo/debian/postinst](https://github.com/jitsi/jicofo/blob/master/debian/postinst)
+ - [videobridge/debian/postinst](https://github.com/jitsi/jitsi-videobridge/blob/master/debian/postinst)
diff --git a/cluster/prod/app/jitsi/integration/docker-compose.yml b/cluster/prod/app/jitsi/integration/docker-compose.yml
new file mode 100644
index 0000000..db7bc81
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/docker-compose.yml
@@ -0,0 +1,42 @@
+version: '3.4'
+services:
+ jitsi-xmpp:
+ image: superboum/amd64_jitsi_xmpp:v9
+ volumes:
+ - "./prosody/prosody.cfg.lua:/etc/prosody/prosody.cfg.lua:ro"
+ - "./prosody/certs/jitsi.crt:/var/lib/prosody/jitsi.crt:ro"
+ - "./prosody/certs/jitsi.key:/var/lib/prosody/jitsi.key:ro"
+ - "./prosody/certs/auth.jitsi.crt:/var/lib/prosody/auth.jitsi.crt:ro"
+ - "./prosody/certs/auth.jitsi.key:/var/lib/prosody/auth.jitsi.key:ro"
+ environment:
+ - JICOFO_AUTH_PASSWORD=jicofopass
+ - JVB_AUTH_PASSWORD=jvbpass
+
+ jitsi-conference-focus:
+ image: superboum/amd64_jitsi_conference_focus:v7
+ volumes:
+ - "./prosody/certs/jitsi.crt:/usr/local/share/ca-certificates/jitsi.crt:ro"
+ - "./prosody/certs/auth.jitsi.crt:/usr/local/share/ca-certificates/auth.jitsi.crt:ro"
+ - "./jicofo/jicofo.conf:/etc/jitsi/jicofo.conf:ro"
+
+ jitsi-videobridge:
+ image: superboum/amd64_jitsi_videobridge:v17
+ volumes:
+ - "./prosody/certs/jitsi.crt:/usr/local/share/ca-certificates/jitsi.crt:ro"
+ - "./prosody/certs/auth.jitsi.crt:/usr/local/share/ca-certificates/auth.jitsi.crt:ro"
+ - "./jvb/videobridge.conf:/etc/jitsi/videobridge.conf:ro"
+ - "./jvb/logging.properties:/usr/share/jvb/lib/logging.properties:ro"
+ ports:
+ - "8089:8089/tcp"
+ - "10000:10000/udp"
+
+ jitsi-meet:
+ image: superboum/amd64_jitsi_meet:v4
+ volumes:
+ - "./prosody/certs/jitsi.crt:/etc/nginx/jitsi.crt:ro"
+ - "./prosody/certs/jitsi.key:/etc/nginx/jitsi.key:ro"
+ - "./meet/config.js:/srv/jitsi-meet/config.js:ro"
+ - "./meet/nginx.conf:/etc/nginx/nginx.conf:ro"
+ ports:
+ - "443:443"
+
diff --git a/cluster/prod/app/jitsi/integration/jicofo/jicofo.conf b/cluster/prod/app/jitsi/integration/jicofo/jicofo.conf
new file mode 100644
index 0000000..f0a817e
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/jicofo/jicofo.conf
@@ -0,0 +1,273 @@
+jicofo {
+ // Authentication with external services
+ authentication {
+ enabled = false
+ // The type of authentication. Supported values are XMPP, JWT or SHIBBOLETH (default).
+ type = SHIBBOLETH
+
+ // The pattern of authentication URL. See ShibbolethAuthAuthority for more information.
+ # login-url =
+
+ # logout-url =
+
+ authentication-lifetime = 24 hours
+ enable-auto-login = true
+ }
+ // Configuration related to jitsi-videobridge
+ bridge {
+ // The maximum number of participants in a single conference to put on one bridge (use -1 for no maximum).
+ max-bridge-participants = -1
+ // The assumed maximum packet rate that a bridge can handle.
+ max-bridge-packet-rate = 50000
+ // The assumed average packet rate per participant.
+ average-participant-packet-rate-pps = 500
+ // The assumed average stress per participant.
+ average-participant-stress = 0.01
+ // The assumed time that an endpoint takes to start contributing fully to the load on a bridge. To avoid allocating
+ // a burst of endpoints to the same bridge, the bridge stress is adjusted by adding the number of new endpoints
+ // in the last [participant-rampup-time] multiplied by [average-participant-stress].
+ participant-rampup-interval = 20 seconds
+ // The stress level above which a bridge is considered overstressed.
+ stress-threshold = 0.8
+ // The amount of to wait before retrying using a failed bridge.
+ failure-reset-threshold = 1 minute
+ // The bridge selection strategy. The built-in strategies are:
+ // SingleBridgeSelectionStrategy: Use the least loaded bridge, do not split a conference between bridges (Octo).
+ // SplitBridgeSelectionStrategy: Use a separate bridge for each participant (for testing).
+ // RegionBasedBridgeSelectionStrategy: Attempt to put each participant in a bridge in their local region (i.e. use
+ // Octo for geo-location).
+ // IntraRegionBridgeSelectionStrategy: Use additional bridges when a bridge becomes overloaded (i.e. use Octo for
+ // load balancing).
+ //
+ // Additionally, you can use the fully qualified class name for custom BridgeSelectionStrategy implementations.
+ selection-strategy = SingleBridgeSelectionStrategy
+ health-checks {
+ // Whether jicofo should perform periodic health checks to the connected bridges.
+ enabled = true
+ // The interval at which to perform health checks.
+ interval = 10 seconds
+ // When a health checks times out, jicofo will retry and only consider it fail after the retry fails. This
+ // configures the delay between the original health check timing out and the second health check being sent.
+ // It is a duration and defaults to half the [interval].
+ # retry-delay = 5 seconds
+ }
+
+ // The JID of the MUC to be used as a brewery for bridge instances.
+ brewery-jid = "jvbbrewery@internal.auth.jitsi"
+ }
+ // Configure the codecs and RTP extensions to be used in the offer sent to clients.
+ codec {
+ video {
+ vp8 {
+ enabled = true
+ pt = 100
+ // Payload type for the associated RTX stream. Set to -1 to disable RTX.
+ rtx-pt = 96
+ }
+ vp9 {
+ enabled = true
+ pt = 101
+ // Payload type for the associated RTX stream. Set to -1 to disable RTX.
+ rtx-pt = 97
+ }
+ h264 {
+ enabled = true
+ pt = 107
+ // Payload type for the associated RTX stream. Set to -1 to disable RTX.
+ rtx-pt = 99
+ }
+ }
+
+ audio {
+ isac-16000 {
+ enabled = true
+ pt = 103
+ }
+ isac-32000 {
+ enabled = true
+ pt = 104
+ }
+ opus {
+ enabled = true
+ pt = 111
+ minptime = 10
+ use-inband-fec = true
+ red {
+ enabled = false
+ pt = 112
+ }
+ }
+ telephone-event {
+ enabled = true
+ pt = 126
+ }
+ }
+
+ // RTP header extensions
+ rtp-extensions {
+ audio-level {
+ enabled = true
+ id = 1
+ }
+ tof {
+ // TOF is currently disabled, because we don't support it in the bridge
+ // (and currently clients seem to not use it when abs-send-time is
+ // available).
+ enabled = false
+ id = 2
+ }
+ abs-send-time {
+ enabled = true
+ id = 3
+ }
+ rid {
+ enabled = false
+ id = 4
+ }
+ tcc {
+ enabled = true
+ id = 5
+ }
+ video-content-type {
+ enabled = false
+ id = 7
+ }
+ framemarking {
+ enabled = false
+ id = 9
+ }
+ }
+ }
+
+ conference {
+ // Whether to automatically grant the 'owner' role to the first participant in the conference (and subsequently to
+ // the next in line when the current owner leaves).
+ enable-auto-owner = true
+
+ // How long to wait for the initial participant in a conference.
+ initial-timeout = 15 seconds
+
+ // Whether jicofo should inject a random SSRC for endpoints which don't advertise any SSRCs. This is a temporary
+ // workaround for an issue with signaling endpoints for Octo.
+ inject-ssrc-for-recv-only-endpoints = false
+
+ max-ssrcs-per-user = 20
+
+ // How long a participant's media session will be kept alive once it remains the only participant in the room.
+ single-participant-timeout = 20 seconds
+
+ // The minimum number of participants required for the conference to be started.
+ min-participants = 2
+
+ // Experimental.
+ enable-lip-sync = false
+
+ shared-document {
+ // If `true` the shared document uses a random name. Otherwise, it uses the conference name.
+ use-random-name = false
+ }
+ }
+
+ // Configuration for the internal health checks performed by jicofo.
+ health {
+ // Whether to perform health checks.
+ enabled = false
+
+ // The interval between health checks. If set to 0, periodic health checks will not be performed.
+ interval = 10 seconds
+
+ # The timeout for a health check
+ timeout = 30 seconds
+
+ # If performing a health check takes longer than this, it is considered unsuccessful.
+ max-check-duration = 20 seconds
+
+ # The prefix to use when creating MUC rooms for the purpose of health checks.
+ room-name-prefix = "__jicofo-health-check"
+ }
+
+ jibri {
+ // The JID of the MUC to be used as a brewery for jibri instances for streaming.
+ # brewery-jid = "jibribrewery@example.com"
+
+ // How many times to retry a given Jibri request before giving up. Set to -1 to allow infinite retries.
+ num-retries = 5
+
+ // How long to wait for Jibri to start recording from the time it accepts a START request.
+ pending-timeout = 90 seconds
+ }
+
+ jibri-sip {
+ // The JID of the MUC to be used as a brewery for jibri instances for SIP.
+ # brewery-jid = "jibrisipbrewery@example.com"
+ }
+
+ jigasi {
+ // The JID of the MUC to be used as a brewery for jigasi instances.
+ # brewery-jid = "jigasibrewery@example.com"
+ }
+
+ // The region in which the machine is running.
+ #local-region="us-east-1"
+
+ octo {
+ // Whether or not to use Octo. Note that when enabled, its use will be determined by
+ // $jicofo.bridge.selection-strategy.
+ enabled = false
+
+ // An identifier of the Jicofo instance, used for the purpose of generating conference IDs unique across a set of
+ // Jicofo instances. Valid values are [1, 65535]. The value 0 is used when none is explicitly configured.
+ id = 1
+ }
+
+ rest {
+ port = 8888
+ tls-port = 8843
+ }
+
+ sctp {
+ // Whether to allocate SCTP channels on the bridge (only when the client advertises support, and SCTP is
+ // enabled in the per-conference configuration).
+ enabled = true
+ }
+
+ task-pools {
+ shared-pool-max-threads = 1500
+ }
+
+ xmpp {
+ // The separate XMPP connection used for communication with clients (endpoints).
+ client {
+ enabled = true
+ hostname = "jitsi-xmpp"
+ port = 5222
+ domain = "auth.jitsi"
+ username = "focus"
+ password = "jicofopass"
+
+ // How long to wait for a response to a stanza before giving up.
+ reply-timeout = 15 seconds
+
+ // The JID/domain of the MUC service used for conferencing.
+ conference-muc-jid = conference.jitsi
+
+ // A flag to suppress the TLS certificate verification.
+ disable-certificate-verification = false
+ }
+ // The separate XMPP connection used for internal services (currently only jitsi-videobridge).
+ service {
+ enabled = false
+ hostname = "jitsi-xmpp"
+ port = 5222
+ domain = "auth.jitsi"
+ username = "focus"
+ password = "jicofopass"
+
+ // How long to wait for a response to a stanza before giving up.
+ reply-timeout = 15 seconds
+
+ // A flag to suppress the TLS certificate verification.
+ disable-certificate-verification = false
+ }
+ }
+}
diff --git a/cluster/prod/app/jitsi/integration/jvb/logging.properties b/cluster/prod/app/jitsi/integration/jvb/logging.properties
new file mode 100644
index 0000000..3453971
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/jvb/logging.properties
@@ -0,0 +1,47 @@
+handlers= java.util.logging.ConsoleHandler
+#handlers= java.util.logging.ConsoleHandler, com.agafua.syslog.SyslogHandler
+#handlers= java.util.logging.ConsoleHandler, io.sentry.jul.SentryHandler
+
+java.util.logging.ConsoleHandler.level = ALL
+java.util.logging.ConsoleHandler.formatter = org.jitsi.utils.logging2.JitsiLogFormatter
+
+net.java.sip.communicator.util.ScLogFormatter.programname=JVB
+
+# default
+.level=INFO
+# for debug
+#.level=FINE
+
+org.jitsi.videobridge.xmpp.ComponentImpl.level=FINE
+
+# All of the INFO level logs from MediaStreamImpl are unnecessary in the context of jitsi-videobridge.
+org.jitsi.impl.neomedia.MediaStreamImpl.level=WARNING
+
+# Syslog (uncomment handler to use)
+com.agafua.syslog.SyslogHandler.transport = udp
+com.agafua.syslog.SyslogHandler.facility = local0
+com.agafua.syslog.SyslogHandler.port = 514
+com.agafua.syslog.SyslogHandler.hostname = localhost
+com.agafua.syslog.SyslogHandler.formatter = org.jitsi.utils.logging2.JitsiLogFormatter
+com.agafua.syslog.SyslogHandler.escapeNewlines = false
+
+# Sentry (uncomment handler to use)
+io.sentry.jul.SentryHandler.level=WARNING
+
+# to disable double timestamps in syslog uncomment next line
+#net.java.sip.communicator.util.ScLogFormatter.disableTimestamp=true
+
+# time series logging
+java.util.logging.SimpleFormatter.format= %5$s%n
+java.util.logging.FileHandler.level = ALL
+java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
+java.util.logging.FileHandler.pattern = /tmp/jvb-series.log
+java.util.logging.FileHandler.limit = 200000000
+java.util.logging.FileHandler.count = 1
+java.util.logging.FileHandler.append = false
+
+timeseries.level=OFF
+timeseries.org.jitsi.videobridge.cc.allocation.BitrateAllocator.level=ALL
+timeseries.useParentHandlers = false
+# time series logging is disabled by default. Uncomment the line below to enable it.
+#timeseries.handlers = java.util.logging.FileHandler
diff --git a/cluster/prod/app/jitsi/integration/jvb/videobridge.conf b/cluster/prod/app/jitsi/integration/jvb/videobridge.conf
new file mode 100644
index 0000000..a11edc6
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/jvb/videobridge.conf
@@ -0,0 +1,290 @@
+videobridge {
+ entity-expiration {
+ # If an entity has no activity after this timeout, it is expired
+ timeout=1 minute
+
+ # The interval at which the videobridge will check for expired entities
+ check-interval=${videobridge.entity-expiration.timeout}
+ }
+ health {
+ # The interval between health checks
+ interval=10 seconds
+
+ # The timeout for a health check
+ timeout=30 seconds
+
+ # If performing a health check takes longer than this, it is considered unsuccessful.
+ max-check-duration=3 seconds
+
+ # Whether or not health check failures should be 'sticky'
+ # (i.e. once the bridge becomes unhealthy, it will never
+ # go back to a healthy state)
+ sticky-failures=false
+ }
+ ep-connection-status {
+ # How long we'll wait for an endpoint to *start* sending
+ # data before we consider it 'inactive'
+ first-transfer-timeout=15 seconds
+
+ # How long an endpoint can be 'inactive' before it will
+ # be considered disconnected
+ max-inactivity-limit=3 seconds
+
+ # How often we check endpoint's connectivity status
+ check-interval=500 milliseconds
+ }
+ cc {
+ bwe-change-threshold=0.15
+ thumbnail-max-height-px=180
+ onstage-ideal-height-px=1080
+ onstage-preferred-height-px=360
+ onstage-preferred-framerate=30
+ enable-onstage-video-suspend=false
+ trust-bwe=true
+
+ # How often we check to send probing data
+ padding-period=15ms
+
+ # How often we'll force recalculations of forwarded
+ # streams
+ max-time-between-calculations = 15 seconds
+
+ # A JVB-wide last-n value, observed by all endpoints. Endpoints
+ # will take the minimum of their setting and this one (-1 implies
+ # no last-n limit)
+ jvb-last-n = -1
+ }
+ # The APIs by which the JVB can be controlled
+ apis {
+ xmpp-client {
+ # The interval at which presence is published in the configured MUCs.
+ presence-interval = ${videobridge.stats.interval}
+
+ configs {
+ unique-xmpp-server {
+ hostname="jitsi-xmpp"
+ domain = "auth.jitsi"
+ username = "jvb"
+ password = "jvbpass"
+ port = 5222
+ muc_jids = "jvbbrewery@internal.auth.jitsi"
+ # The muc_nickname must be unique across all jitsi-videobridge instances
+ muc_nickname = "unique-jvb-server"
+ disable_certificate_verification = false
+ }
+ # example-connection-id {
+ # For the properties which should be
+ # filled out here, see MucClientConfiguration
+ # }
+ }
+ }
+ # The COLIBRI REST API
+ rest {
+ enabled = true
+ }
+ jvb-api {
+ enabled = true
+ }
+ }
+ # Configuration of the different REST APIs.
+ # Note that the COLIBRI REST API is configured under videobridge.apis.rest instead.
+ rest {
+ debug {
+ enabled = true
+ }
+ health {
+ enabled = true
+ }
+ shutdown {
+ # Note that the shutdown API requires the COLIBRI API to also be enabled.
+ enabled = false
+ }
+ version {
+ enabled = true
+ }
+ }
+ http-servers {
+ # The HTTP server which hosts services intended for 'public' use
+ # (e.g. websockets for the bridge channel connection)
+ public {
+ # See JettyBundleActivatorConfig in Jicoco for values
+ port = -1
+ tls-port = -1
+ }
+ # The HTTP server which hosts services intended for 'private' use
+ # (e.g. health or debug stats)
+ private {
+ # See JettyBundleActivatorConfig in Jicoco for values
+ host = 127.0.0.1
+ }
+ }
+ octo {
+ # Whether or not Octo is enabled
+ enabled=false
+
+ # A string denoting the 'region' of this JVB. This region
+ # will be used by Jicofo in the selection of a bridge for
+ # a client by comparing it to the client's region.
+ # Must be set when 'enabled' is true
+ #region="us-west-1"
+
+ # The address on which the Octo relay should bind
+ # Must be set when 'enabled' is true
+ #bind-address=198.51.100.1
+
+ # The port to which the Octo relay should bind
+ bind-port=4096
+
+ # The address which controls the public address which
+ # will be part of the Octo relayId
+ #public-address=198.51.100.1
+
+ # The size of the incoming octo queue. This queue is per-remote-endpoint,
+ # so it matches what we use for local endpoints
+ recv-queue-size=1024
+
+ # The size of the outgoing octo queue. This is a per-originating-endpoint
+ # queue, so assuming all packets are routed (as they currently are for Octo)
+ # it should be the same size as the transceiver recv queue in
+ # jitsi-media-transform. Repeating the description from there:
+ # Assuming 300pps for high-definition, 200pps for standard-definition,
+ # 100pps for low-definition and 50pps for audio, this queue is fed
+ # 650pps, so its size in terms of millis is 1024/650*1000 ~= 1575ms.
+ send-queue-size=1024
+ }
+ load-management {
+ # Whether or not the reducer will be enabled to take actions to mitigate load
+ reducer-enabled = false
+ load-measurements {
+ packet-rate {
+ # The packet rate at which we'll consider the bridge overloaded
+ load-threshold = 50000
+ # The packet rate at which we'll consider the bridge 'underloaded' enough
+ # to start recovery
+ recovery-threshold = 40000
+ }
+ }
+ load-reducers {
+ last-n {
+ # The factor by which we'll reduce the current last-n when trying to reduce load
+ reduction-scale = .75
+ # The factor by which we'll increase the current last-n when trying to recover
+ recover-scale = 1.25
+ # The minimum time in between runs of the last-n reducer to reduce or recover from
+ # load
+ impact-time = 1 minute
+ # The lowest value we'll set for last-n
+ minimum-last-n-value = 0
+ # The highest last-n value we'll enforce. Once the enforced last-n exceeds this value
+ # we'll remove the limit entirely
+ maximum-enforced-last-n-value = 40
+ }
+ }
+ }
+ sctp {
+ # Whether SCTP data channels are enabled.
+ enabled=true
+ }
+ stats {
+ # Whether periodic collection of statistics is enabled or not. When enabled they are accessible through the REST
+ # API (at `/colibri/stats`), and are available to other modules (e.g. to be pushed to callstats or in a MUC).
+ enabled = true
+
+ # The interval at which stats are gathered.
+ interval = 5 seconds
+
+ # Configuration related to pushing statistics to callstats.io.
+ callstats {
+ # An integer application ID (use 0 to disable pushing stats to callstats).
+ app-id = 0
+
+ # The shared secred to authentication with callstats.io.
+ //app-secret = "s3cret"
+
+ # ID of the key that was used to generate token.
+ //key-id = "abcd"
+
+ # The path to private key file.
+ //key-path = "/etc/jitsi/videobridge/ecpriv.jwk"
+
+ # The ID of the server instance to be used when reporting to callstats.
+ bridge-id = "jitsi"
+
+ # TODO: document
+ //conference-id-prefix = "abcd"
+
+ # The interval at which statististics will be published to callstats. This affects both per-conference and global
+ # statistics.
+ # Note that this value will be overriden if a "callstatsio" transport is defined in the parent "stats" section.
+ interval = ${videobridge.stats.interval}
+ }
+ }
+ websockets {
+ enabled=false
+ server-id="default-id"
+
+ # Optional, even when 'enabled' is set to true
+ # tls=true
+ # Must be set when enabled = true
+ #domain="some-domain"
+ }
+ ice {
+ tcp {
+ # Whether ICE/TCP is enabled.
+ enabled = true
+
+ # The port to bind to for ICE/TCP.
+ port = 8089
+
+ # An optional additional port to advertise.
+ # mapped-port = 8443
+ # Whether to use "ssltcp" or plain "tcp".
+ ssltcp = true
+ }
+
+ udp {
+ # The port for ICE/UDP.
+ port = 10000
+ }
+
+ # An optional prefix to include in STUN username fragments generated by the bridge.
+ #ufrag-prefix = "jvb-123:"
+
+ # Which candidate pairs to keep alive. The accepted values are defined in ice4j's KeepAliveStrategy:
+ # "selected_and_tcp", "selected_only", or "all_succeeded".
+ keep-alive-strategy = "selected_and_tcp"
+
+ # Whether to use the "component socket" feature of ice4j.
+ use-component-socket = true
+
+ # Whether to attempt DNS resolution for remote candidates that contain a non-literal address. When set to 'false'
+ # such candidates will be ignored.
+ resolve-remote-candidates = false
+
+ # The nomination strategy to use for ICE. THe accepted values are defined in ice4j's NominationStrategy:
+ # "NominateFirstValid", "NominateHighestPriority", "NominateFirstHostOrReflexiveValid", or "NominateBestRTT".
+ nomination-strategy = "NominateFirstValid"
+ }
+
+ transport {
+ send {
+ # The size of the dtls-transport outgoing queue. This is a per-participant
+ # queue. Packets from the egress end-up in this queue right before
+ # transmission by the outgoing srtp pipeline (which mainly consists of the
+ # packet sender).
+ #
+ # Its size needs to be of the same order of magnitude as the rtp sender
+ # queue. In a 100 participant call, assuming 300pps for the on-stage and
+ # 100pps for low-definition, last-n 20 and 2 participants talking, so
+ # 2*50pps for audio, this queue is fed 300+19*100+2*50 = 2300pps, so its
+ # size in terms of millis is 1024/2300*1000 ~= 445ms.
+ queue-size=1024
+ }
+ }
+
+ version {
+ // Wheather to announe the jitsi-videobridge version to clients in the ServerHello message.
+ announce = false
+ }
+}
+
diff --git a/cluster/prod/app/jitsi/integration/meet/config.js b/cluster/prod/app/jitsi/integration/meet/config.js
new file mode 100644
index 0000000..04414c3
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/meet/config.js
@@ -0,0 +1,773 @@
+/* eslint-disable no-unused-vars, no-var */
+
+var config = {
+ // Connection
+ //
+
+ hosts: {
+ // XMPP domain.
+ domain: 'jitsi',
+
+ // When using authentication, domain for guest users.
+ // anonymousdomain: 'guest.example.com',
+
+ // Domain for authenticated users. Defaults to <domain>.
+ // authdomain: 'jitsi-meet.example.com',
+
+ // Focus component domain. Defaults to focus.<domain>.
+ // focus: 'focus.jitsi-meet.example.com',
+
+ // XMPP MUC domain. FIXME: use XEP-0030 to discover it.
+ muc: 'conference.jitsi'
+ },
+
+ // BOSH URL. FIXME: use XEP-0156 to discover it.
+ bosh: '//rayonx.machine.deuxfleurs.fr/http-bind',
+
+ // Websocket URL
+ // websocket: 'wss://jitsi-meet.example.com/xmpp-websocket',
+
+ // The name of client node advertised in XEP-0115 'c' stanza
+ clientNode: 'http://jitsi.org/jitsimeet',
+
+ // The real JID of focus participant - can be overridden here
+ // Do not change username - FIXME: Make focus username configurable
+ // https://github.com/jitsi/jitsi-meet/issues/7376
+ // focusUserJid: 'focus@auth.jitsi-meet.example.com',
+
+
+ // Testing / experimental features.
+ //
+
+ testing: {
+ // Disables the End to End Encryption feature. Useful for debugging
+ // issues related to insertable streams.
+ // disableE2EE: false,
+
+ // P2P test mode disables automatic switching to P2P when there are 2
+ // participants in the conference.
+ p2pTestMode: false
+
+ // Enables the test specific features consumed by jitsi-meet-torture
+ // testMode: false
+
+ // Disables the auto-play behavior of *all* newly created video element.
+ // This is useful when the client runs on a host with limited resources.
+ // noAutoPlayVideo: false
+
+ // Enable / disable 500 Kbps bitrate cap on desktop tracks. When enabled,
+ // simulcast is turned off for the desktop share. If presenter is turned
+ // on while screensharing is in progress, the max bitrate is automatically
+ // adjusted to 2.5 Mbps. This takes a value between 0 and 1 which determines
+ // the probability for this to be enabled.
+ // capScreenshareBitrate: 1 // 0 to disable
+
+ // Enable callstats only for a percentage of users.
+ // This takes a value between 0 and 100 which determines the probability for
+ // the callstats to be enabled.
+ // callStatsThreshold: 5 // enable callstats for 5% of the users.
+ },
+
+ // Disables ICE/UDP by filtering out local and remote UDP candidates in
+ // signalling.
+ // webrtcIceUdpDisable: false,
+
+ // Disables ICE/TCP by filtering out local and remote TCP candidates in
+ // signalling.
+ // webrtcIceTcpDisable: false,
+
+
+ // Media
+ //
+
+ // Audio
+
+ // Disable measuring of audio levels.
+ // disableAudioLevels: false,
+ // audioLevelsInterval: 200,
+
+ // Enabling this will run the lib-jitsi-meet no audio detection module which
+ // will notify the user if the current selected microphone has no audio
+ // input and will suggest another valid device if one is present.
+ enableNoAudioDetection: true,
+
+ // Enabling this will show a "Save Logs" link in the GSM popover that can be
+ // used to collect debug information (XMPP IQs, SDP offer/answer cycles)
+ // about the call.
+ // enableSaveLogs: false,
+
+ // Enabling this will run the lib-jitsi-meet noise detection module which will
+ // notify the user if there is noise, other than voice, coming from the current
+ // selected microphone. The purpose it to let the user know that the input could
+ // be potentially unpleasant for other meeting participants.
+ enableNoisyMicDetection: true,
+
+ // Start the conference in audio only mode (no video is being received nor
+ // sent).
+ // startAudioOnly: false,
+
+ // Every participant after the Nth will start audio muted.
+ // startAudioMuted: 10,
+
+ // Start calls with audio muted. Unlike the option above, this one is only
+ // applied locally. FIXME: having these 2 options is confusing.
+ // startWithAudioMuted: false,
+
+ // Enabling it (with #params) will disable local audio output of remote
+ // participants and to enable it back a reload is needed.
+ // startSilent: false
+
+ // Sets the preferred target bitrate for the Opus audio codec by setting its
+ // 'maxaveragebitrate' parameter. Currently not available in p2p mode.
+ // Valid values are in the range 6000 to 510000
+ // opusMaxAverageBitrate: 20000,
+
+ // Enables support for opus-red (redundancy for Opus).
+ // enableOpusRed: false
+
+ // Video
+
+ // Sets the preferred resolution (height) for local video. Defaults to 720.
+ // resolution: 720,
+
+ // How many participants while in the tile view mode, before the receiving video quality is reduced from HD to SD.
+ // Use -1 to disable.
+ // maxFullResolutionParticipants: 2,
+
+ // w3c spec-compliant video constraints to use for video capture. Currently
+ // used by browsers that return true from lib-jitsi-meet's
+ // util#browser#usesNewGumFlow. The constraints are independent from
+ // this config's resolution value. Defaults to requesting an ideal
+ // resolution of 720p.
+ // constraints: {
+ // video: {
+ // height: {
+ // ideal: 720,
+ // max: 720,
+ // min: 240
+ // }
+ // }
+ // },
+
+ // Enable / disable simulcast support.
+ // disableSimulcast: false,
+
+ // Enable / disable layer suspension. If enabled, endpoints whose HD
+ // layers are not in use will be suspended (no longer sent) until they
+ // are requested again.
+ // enableLayerSuspension: false,
+
+ // Every participant after the Nth will start video muted.
+ // startVideoMuted: 10,
+
+ // Start calls with video muted. Unlike the option above, this one is only
+ // applied locally. FIXME: having these 2 options is confusing.
+ // startWithVideoMuted: false,
+
+ // If set to true, prefer to use the H.264 video codec (if supported).
+ // Note that it's not recommended to do this because simulcast is not
+ // supported when using H.264. For 1-to-1 calls this setting is enabled by
+ // default and can be toggled in the p2p section.
+ // This option has been deprecated, use preferredCodec under videoQuality section instead.
+ // preferH264: true,
+
+ // If set to true, disable H.264 video codec by stripping it out of the
+ // SDP.
+ // disableH264: false,
+
+ // Desktop sharing
+
+ // Optional desktop sharing frame rate options. Default value: min:5, max:5.
+ // desktopSharingFrameRate: {
+ // min: 5,
+ // max: 5
+ // },
+
+ // Try to start calls with screen-sharing instead of camera video.
+ // startScreenSharing: false,
+
+ // Recording
+
+ // Whether to enable file recording or not.
+ // fileRecordingsEnabled: false,
+ // Enable the dropbox integration.
+ // dropbox: {
+ // appKey: '<APP_KEY>' // Specify your app key here.
+ // // A URL to redirect the user to, after authenticating
+ // // by default uses:
+ // // 'https://jitsi-meet.example.com/static/oauth.html'
+ // redirectURI:
+ // 'https://jitsi-meet.example.com/subfolder/static/oauth.html'
+ // },
+ // When integrations like dropbox are enabled only that will be shown,
+ // by enabling fileRecordingsServiceEnabled, we show both the integrations
+ // and the generic recording service (its configuration and storage type
+ // depends on jibri configuration)
+ // fileRecordingsServiceEnabled: false,
+ // Whether to show the possibility to share file recording with other people
+ // (e.g. meeting participants), based on the actual implementation
+ // on the backend.
+ // fileRecordingsServiceSharingEnabled: false,
+
+ // Whether to enable live streaming or not.
+ // liveStreamingEnabled: false,
+
+ // Transcription (in interface_config,
+ // subtitles and buttons can be configured)
+ // transcribingEnabled: false,
+
+ // Enables automatic turning on captions when recording is started
+ // autoCaptionOnRecord: false,
+
+ // Misc
+
+ // Default value for the channel "last N" attribute. -1 for unlimited.
+ channelLastN: -1,
+
+ // Provides a way to use different "last N" values based on the number of participants in the conference.
+ // The keys in an Object represent number of participants and the values are "last N" to be used when number of
+ // participants gets to or above the number.
+ //
+ // For the given example mapping, "last N" will be set to 20 as long as there are at least 5, but less than
+ // 29 participants in the call and it will be lowered to 15 when the 30th participant joins. The 'channelLastN'
+ // will be used as default until the first threshold is reached.
+ //
+ // lastNLimits: {
+ // 5: 20,
+ // 30: 15,
+ // 50: 10,
+ // 70: 5,
+ // 90: 2
+ // },
+
+ // Specify the settings for video quality optimizations on the client.
+ // videoQuality: {
+ // // Provides a way to prevent a video codec from being negotiated on the JVB connection. The codec specified
+ // // here will be removed from the list of codecs present in the SDP answer generated by the client. If the
+ // // same codec is specified for both the disabled and preferred option, the disable settings will prevail.
+ // // Note that 'VP8' cannot be disabled since it's a mandatory codec, the setting will be ignored in this case.
+ // disabledCodec: 'H264',
+ //
+ // // Provides a way to set a preferred video codec for the JVB connection. If 'H264' is specified here,
+ // // simulcast will be automatically disabled since JVB doesn't support H264 simulcast yet. This will only
+ // // rearrange the the preference order of the codecs in the SDP answer generated by the browser only if the
+ // // preferred codec specified here is present. Please ensure that the JVB offers the specified codec for this
+ // // to take effect.
+ // preferredCodec: 'VP8',
+ //
+ // // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
+ // // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
+ // // are the max.bitrates to be set on that particular type of stream. The actual send may vary based on
+ // // the available bandwidth calculated by the browser, but it will be capped by the values specified here.
+ // // This is currently not implemented on app based clients on mobile.
+ // maxBitratesVideo: {
+ // low: 200000,
+ // standard: 500000,
+ // high: 1500000
+ // },
+ //
+ // // The options can be used to override default thresholds of video thumbnail heights corresponding to
+ // // the video quality levels used in the application. At the time of this writing the allowed levels are:
+ // // 'low' - for the low quality level (180p at the time of this writing)
+ // // 'standard' - for the medium quality level (360p)
+ // // 'high' - for the high quality level (720p)
+ // // The keys should be positive numbers which represent the minimal thumbnail height for the quality level.
+ // //
+ // // With the default config value below the application will use 'low' quality until the thumbnails are
+ // // at least 360 pixels tall. If the thumbnail height reaches 720 pixels then the application will switch to
+ // // the high quality.
+ // minHeightForQualityLvl: {
+ // 360: 'standard',
+ // 720: 'high'
+ // },
+ //
+ // // Provides a way to resize the desktop track to 720p (if it is greater than 720p) before creating a canvas
+ // // for the presenter mode (camera picture-in-picture mode with screenshare).
+ // resizeDesktopForPresenter: false
+ // },
+
+ // // Options for the recording limit notification.
+ // recordingLimit: {
+ //
+ // // The recording limit in minutes. Note: This number appears in the notification text
+ // // but doesn't enforce the actual recording time limit. This should be configured in
+ // // jibri!
+ // limit: 60,
+ //
+ // // The name of the app with unlimited recordings.
+ // appName: 'Unlimited recordings APP',
+ //
+ // // The URL of the app with unlimited recordings.
+ // appURL: 'https://unlimited.recordings.app.com/'
+ // },
+
+ // Disables or enables RTX (RFC 4588) (defaults to false).
+ // disableRtx: false,
+
+ // Disables or enables TCC support in this client (default: enabled).
+ // enableTcc: true,
+
+ // Disables or enables REMB support in this client (default: enabled).
+ // enableRemb: true,
+
+ // Enables ICE restart logic in LJM and displays the page reload overlay on
+ // ICE failure. Current disabled by default because it's causing issues with
+ // signaling when Octo is enabled. Also when we do an "ICE restart"(which is
+ // not a real ICE restart), the client maintains the TCC sequence number
+ // counter, but the bridge resets it. The bridge sends media packets with
+ // TCC sequence numbers starting from 0.
+ // enableIceRestart: false,
+
+ // Use TURN/UDP servers for the jitsi-videobridge connection (by default
+ // we filter out TURN/UDP because it is usually not needed since the
+ // bridge itself is reachable via UDP)
+ // useTurnUdp: false
+
+ // UI
+ //
+
+ // Disables responsive tiles.
+ // disableResponsiveTiles: false,
+
+ // Hides lobby button
+ // hideLobbyButton: false,
+
+ // Require users to always specify a display name.
+ // requireDisplayName: true,
+
+ // Whether to use a welcome page or not. In case it's false a random room
+ // will be joined when no room is specified.
+ enableWelcomePage: true,
+
+ // Disable app shortcuts that are registered upon joining a conference
+ // disableShortcuts: false,
+
+ // Disable initial browser getUserMedia requests.
+ // This is useful for scenarios where users might want to start a conference for screensharing only
+ // disableInitialGUM: false,
+
+ // Enabling the close page will ignore the welcome page redirection when
+ // a call is hangup.
+ // enableClosePage: false,
+
+ // Disable hiding of remote thumbnails when in a 1-on-1 conference call.
+ // disable1On1Mode: false,
+
+ // Default language for the user interface.
+ defaultLanguage: 'fr',
+
+ // Disables profile and the edit of all fields from the profile settings (display name and email)
+ // disableProfile: false,
+
+ // Whether or not some features are checked based on token.
+ // enableFeaturesBasedOnToken: false,
+
+ // When enabled the password used for locking a room is restricted to up to the number of digits specified
+ // roomPasswordNumberOfDigits: 10,
+ // default: roomPasswordNumberOfDigits: false,
+
+ // Message to show the users. Example: 'The service will be down for
+ // maintenance at 01:00 AM GMT,
+ // noticeMessage: '',
+
+ // Enables calendar integration, depends on googleApiApplicationClientID
+ // and microsoftApiApplicationClientID
+ // enableCalendarIntegration: false,
+
+ // When 'true', it shows an intermediate page before joining, where the user can configure their devices.
+ // prejoinPageEnabled: false,
+
+ // If etherpad integration is enabled, setting this to true will
+ // automatically open the etherpad when a participant joins. This
+ // does not affect the mobile app since opening an etherpad
+ // obscures the conference controls -- it's better to let users
+ // choose to open the pad on their own in that case.
+ // openSharedDocumentOnJoin: false,
+
+ // If true, shows the unsafe room name warning label when a room name is
+ // deemed unsafe (due to the simplicity in the name) and a password is not
+ // set or the lobby is not enabled.
+ // enableInsecureRoomNameWarning: false,
+
+ // Whether to automatically copy invitation URL after creating a room.
+ // Document should be focused for this option to work
+ // enableAutomaticUrlCopy: false,
+
+ // Base URL for a Gravatar-compatible service. Defaults to libravatar.
+ // gravatarBaseURL: 'https://seccdn.libravatar.org/avatar/';
+
+ // Stats
+ //
+
+ // Whether to enable stats collection or not in the TraceablePeerConnection.
+ // This can be useful for debugging purposes (post-processing/analysis of
+ // the webrtc stats) as it is done in the jitsi-meet-torture bandwidth
+ // estimation tests.
+ // gatherStats: false,
+
+ // The interval at which PeerConnection.getStats() is called. Defaults to 10000
+ // pcStatsInterval: 10000,
+
+ // To enable sending statistics to callstats.io you must provide the
+ // Application ID and Secret.
+ // callStatsID: '',
+ // callStatsSecret: '',
+
+ // Enables sending participants' display names to callstats
+ // enableDisplayNameInStats: false,
+
+ // Enables sending participants' emails (if available) to callstats and other analytics
+ // enableEmailInStats: false,
+
+ // Privacy
+ //
+
+ // If third party requests are disabled, no other server will be contacted.
+ // This means avatars will be locally generated and callstats integration
+ // will not function.
+ // disableThirdPartyRequests: false,
+
+
+ // Peer-To-Peer mode: used (if enabled) when there are just 2 participants.
+ //
+
+ p2p: {
+ // Enables peer to peer mode. When enabled the system will try to
+ // establish a direct connection when there are exactly 2 participants
+ // in the room. If that succeeds the conference will stop sending data
+ // through the JVB and use the peer to peer connection instead. When a
+ // 3rd participant joins the conference will be moved back to the JVB
+ // connection.
+ enabled: true,
+
+ // The STUN servers that will be used in the peer to peer connections
+ stunServers: [
+
+ // { urls: 'stun:jitsi-meet.example.com:3478' },
+ { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }
+ ]
+
+ // Sets the ICE transport policy for the p2p connection. At the time
+ // of this writing the list of possible values are 'all' and 'relay',
+ // but that is subject to change in the future. The enum is defined in
+ // the WebRTC standard:
+ // https://www.w3.org/TR/webrtc/#rtcicetransportpolicy-enum.
+ // If not set, the effective value is 'all'.
+ // iceTransportPolicy: 'all',
+
+ // If set to true, it will prefer to use H.264 for P2P calls (if H.264
+ // is supported). This setting is deprecated, use preferredCodec instead.
+ // preferH264: true
+
+ // Provides a way to set the video codec preference on the p2p connection. Acceptable
+ // codec values are 'VP8', 'VP9' and 'H264'.
+ // preferredCodec: 'H264',
+
+ // If set to true, disable H.264 video codec by stripping it out of the
+ // SDP. This setting is deprecated, use disabledCodec instead.
+ // disableH264: false,
+
+ // Provides a way to prevent a video codec from being negotiated on the p2p connection.
+ // disabledCodec: '',
+
+ // How long we're going to wait, before going back to P2P after the 3rd
+ // participant has left the conference (to filter out page reload).
+ // backToP2PDelay: 5
+ },
+
+ analytics: {
+ // The Google Analytics Tracking ID:
+ // googleAnalyticsTrackingId: 'your-tracking-id-UA-123456-1'
+
+ // Matomo configuration:
+ // matomoEndpoint: 'https://your-matomo-endpoint/',
+ // matomoSiteID: '42',
+
+ // The Amplitude APP Key:
+ // amplitudeAPPKey: '<APP_KEY>'
+
+ // Configuration for the rtcstats server:
+ // By enabling rtcstats server every time a conference is joined the rtcstats
+ // module connects to the provided rtcstatsEndpoint and sends statistics regarding
+ // PeerConnection states along with getStats metrics polled at the specified
+ // interval.
+ // rtcstatsEnabled: true,
+
+ // In order to enable rtcstats one needs to provide a endpoint url.
+ // rtcstatsEndpoint: wss://rtcstats-server-pilot.jitsi.net/,
+
+ // The interval at which rtcstats will poll getStats, defaults to 1000ms.
+ // If the value is set to 0 getStats won't be polled and the rtcstats client
+ // will only send data related to RTCPeerConnection events.
+ // rtcstatsPolIInterval: 1000
+
+ // Array of script URLs to load as lib-jitsi-meet "analytics handlers".
+ // scriptURLs: [
+ // "libs/analytics-ga.min.js", // google-analytics
+ // "https://example.com/my-custom-analytics.js"
+ // ],
+ },
+
+ // Logs that should go be passed through the 'log' event if a handler is defined for it
+ // apiLogLevels: ['warn', 'log', 'error', 'info', 'debug'],
+
+ // Information about the jitsi-meet instance we are connecting to, including
+ // the user region as seen by the server.
+ deploymentInfo: {
+ // shard: "shard1",
+ // region: "europe",
+ // userRegion: "asia"
+ },
+
+ // Decides whether the start/stop recording audio notifications should play on record.
+ // disableRecordAudioNotification: false,
+
+ // Information for the chrome extension banner
+ // chromeExtensionBanner: {
+ // // The chrome extension to be installed address
+ // url: 'https://chrome.google.com/webstore/detail/jitsi-meetings/kglhbbefdnlheedjiejgomgmfplipfeb',
+
+ // // Extensions info which allows checking if they are installed or not
+ // chromeExtensionsInfo: [
+ // {
+ // id: 'kglhbbefdnlheedjiejgomgmfplipfeb',
+ // path: 'jitsi-logo-48x48.png'
+ // }
+ // ]
+ // },
+
+ // Local Recording
+ //
+
+ // localRecording: {
+ // Enables local recording.
+ // Additionally, 'localrecording' (all lowercase) needs to be added to
+ // TOOLBAR_BUTTONS in interface_config.js for the Local Recording
+ // button to show up on the toolbar.
+ //
+ // enabled: true,
+ //
+
+ // The recording format, can be one of 'ogg', 'flac' or 'wav'.
+ // format: 'flac'
+ //
+
+ // },
+
+ // Options related to end-to-end (participant to participant) ping.
+ // e2eping: {
+ // // The interval in milliseconds at which pings will be sent.
+ // // Defaults to 10000, set to <= 0 to disable.
+ // pingInterval: 10000,
+ //
+ // // The interval in milliseconds at which analytics events
+ // // with the measured RTT will be sent. Defaults to 60000, set
+ // // to <= 0 to disable.
+ // analyticsInterval: 60000,
+ // },
+
+ // If set, will attempt to use the provided video input device label when
+ // triggering a screenshare, instead of proceeding through the normal flow
+ // for obtaining a desktop stream.
+ // NOTE: This option is experimental and is currently intended for internal
+ // use only.
+ // _desktopSharingSourceDevice: 'sample-id-or-label',
+
+ // If true, any checks to handoff to another application will be prevented
+ // and instead the app will continue to display in the current browser.
+ // disableDeepLinking: false,
+
+ // A property to disable the right click context menu for localVideo
+ // the menu has option to flip the locally seen video for local presentations
+ // disableLocalVideoFlip: false,
+
+ // Mainly privacy related settings
+
+ // Disables all invite functions from the app (share, invite, dial out...etc)
+ // disableInviteFunctions: true,
+
+ // Disables storing the room name to the recents list
+ // doNotStoreRoom: true,
+
+ // Deployment specific URLs.
+ // deploymentUrls: {
+ // // If specified a 'Help' button will be displayed in the overflow menu with a link to the specified URL for
+ // // user documentation.
+ // userDocumentationURL: 'https://docs.example.com/video-meetings.html',
+ // // If specified a 'Download our apps' button will be displayed in the overflow menu with a link
+ // // to the specified URL for an app download page.
+ // downloadAppsUrl: 'https://docs.example.com/our-apps.html'
+ // },
+
+ // Options related to the remote participant menu.
+ // remoteVideoMenu: {
+ // // If set to true the 'Kick out' button will be disabled.
+ // disableKick: true
+ // },
+
+ // If set to true all muting operations of remote participants will be disabled.
+ // disableRemoteMute: true,
+
+ // Enables support for lip-sync for this client (if the browser supports it).
+ // enableLipSync: false
+
+ /**
+ External API url used to receive branding specific information.
+ If there is no url set or there are missing fields, the defaults are applied.
+ None of the fields are mandatory and the response must have the shape:
+ {
+ // The hex value for the colour used as background
+ backgroundColor: '#fff',
+ // The url for the image used as background
+ backgroundImageUrl: 'https://example.com/background-img.png',
+ // The anchor url used when clicking the logo image
+ logoClickUrl: 'https://example-company.org',
+ // The url used for the image used as logo
+ logoImageUrl: 'https://example.com/logo-img.png'
+ }
+ */
+ // dynamicBrandingUrl: '',
+
+ // The URL of the moderated rooms microservice, if available. If it
+ // is present, a link to the service will be rendered on the welcome page,
+ // otherwise the app doesn't render it.
+ // moderatedRoomServiceUrl: 'https://moderated.jitsi-meet.example.com',
+
+ // If true, tile view will not be enabled automatically when the participants count threshold is reached.
+ // disableTileView: true,
+
+ // Hides the conference subject
+ // hideConferenceSubject: true
+
+ // Hides the conference timer.
+ // hideConferenceTimer: true,
+
+ // Hides the participants stats
+ // hideParticipantsStats: true
+
+ // Sets the conference subject
+ // subject: 'Conference Subject',
+
+ // List of undocumented settings used in jitsi-meet
+ /**
+ _immediateReloadThreshold
+ debug
+ debugAudioLevels
+ deploymentInfo
+ dialInConfCodeUrl
+ dialInNumbersUrl
+ dialOutAuthUrl
+ dialOutCodesUrl
+ disableRemoteControl
+ displayJids
+ etherpad_base
+ externalConnectUrl
+ firefox_fake_device
+ googleApiApplicationClientID
+ iAmRecorder
+ iAmSipGateway
+ microsoftApiApplicationClientID
+ peopleSearchQueryTypes
+ peopleSearchUrl
+ requireDisplayName
+ tokenAuthUrl
+ */
+
+ /**
+ * This property can be used to alter the generated meeting invite links (in combination with a branding domain
+ * which is retrieved internally by jitsi meet) (e.g. https://meet.jit.si/someMeeting
+ * can become https://brandedDomain/roomAlias)
+ */
+ // brandingRoomAlias: null,
+
+ // List of undocumented settings used in lib-jitsi-meet
+ /**
+ _peerConnStatusOutOfLastNTimeout
+ _peerConnStatusRtcMuteTimeout
+ abTesting
+ avgRtpStatsN
+ callStatsConfIDNamespace
+ callStatsCustomScriptUrl
+ desktopSharingSources
+ disableAEC
+ disableAGC
+ disableAP
+ disableHPF
+ disableNS
+ enableTalkWhileMuted
+ forceJVB121Ratio
+ forceTurnRelay
+ hiddenDomain
+ ignoreStartMuted
+ websocketKeepAlive
+ websocketKeepAliveUrl
+ */
+
+ /**
+ Use this array to configure which notifications will be shown to the user
+ The items correspond to the title or description key of that notification
+ Some of these notifications also depend on some other internal logic to be displayed or not,
+ so adding them here will not ensure they will always be displayed
+
+ A falsy value for this prop will result in having all notifications enabled (e.g null, undefined, false)
+ */
+ // notifications: [
+ // 'connection.CONNFAIL', // shown when the connection fails,
+ // 'dialog.cameraNotSendingData', // shown when there's no feed from user's camera
+ // 'dialog.kickTitle', // shown when user has been kicked
+ // 'dialog.liveStreaming', // livestreaming notifications (pending, on, off, limits)
+ // 'dialog.lockTitle', // shown when setting conference password fails
+ // 'dialog.maxUsersLimitReached', // shown when maximmum users limit has been reached
+ // 'dialog.micNotSendingData', // shown when user's mic is not sending any audio
+ // 'dialog.passwordNotSupportedTitle', // shown when setting conference password fails due to password format
+ // 'dialog.recording', // recording notifications (pending, on, off, limits)
+ // 'dialog.remoteControlTitle', // remote control notifications (allowed, denied, start, stop, error)
+ // 'dialog.reservationError',
+ // 'dialog.serviceUnavailable', // shown when server is not reachable
+ // 'dialog.sessTerminated', // shown when there is a failed conference session
+ // 'dialog.tokenAuthFailed', // show when an invalid jwt is used
+ // 'dialog.transcribing', // transcribing notifications (pending, off)
+ // 'dialOut.statusMessage', // shown when dial out status is updated.
+ // 'liveStreaming.busy', // shown when livestreaming service is busy
+ // 'liveStreaming.failedToStart', // shown when livestreaming fails to start
+ // 'liveStreaming.unavailableTitle', // shown when livestreaming service is not reachable
+ // 'lobby.joinRejectedMessage', // shown when while in a lobby, user's request to join is rejected
+ // 'lobby.notificationTitle', // shown when lobby is toggled and when join requests are allowed / denied
+ // 'localRecording.localRecording', // shown when a local recording is started
+ // 'notify.disconnected', // shown when a participant has left
+ // 'notify.grantedTo', // shown when moderator rights were granted to a participant
+ // 'notify.invitedOneMember', // shown when 1 participant has been invited
+ // 'notify.invitedThreePlusMembers', // shown when 3+ participants have been invited
+ // 'notify.invitedTwoMembers', // shown when 2 participants have been invited
+ // 'notify.kickParticipant', // shown when a participant is kicked
+ // 'notify.mutedRemotelyTitle', // shown when user is muted by a remote party
+ // 'notify.mutedTitle', // shown when user has been muted upon joining,
+ // 'notify.newDeviceAudioTitle', // prompts the user to use a newly detected audio device
+ // 'notify.newDeviceCameraTitle', // prompts the user to use a newly detected camera
+ // 'notify.passwordRemovedRemotely', // shown when a password has been removed remotely
+ // 'notify.passwordSetRemotely', // shown when a password has been set remotely
+ // 'notify.raisedHand', // shown when a partcipant used raise hand,
+ // 'notify.startSilentTitle', // shown when user joined with no audio
+ // 'prejoin.errorDialOut',
+ // 'prejoin.errorDialOutDisconnected',
+ // 'prejoin.errorDialOutFailed',
+ // 'prejoin.errorDialOutStatus',
+ // 'prejoin.errorStatusCode',
+ // 'prejoin.errorValidation',
+ // 'recording.busy', // shown when recording service is busy
+ // 'recording.failedToStart', // shown when recording fails to start
+ // 'recording.unavailableTitle', // shown when recording service is not reachable
+ // 'toolbar.noAudioSignalTitle', // shown when a broken mic is detected
+ // 'toolbar.noisyAudioInputTitle', // shown when noise is detected for the current microphone
+ // 'toolbar.talkWhileMutedPopup', // shown when user tries to speak while muted
+ // 'transcribing.failedToStart' // shown when transcribing fails to start
+ // ]
+
+ // Allow all above example options to include a trailing comma and
+ // prevent fear when commenting out the last value.
+ makeJsonParserHappy: 'even if last key had a trailing comma'
+
+ // no configuration value should follow this line.
+};
+
+/* eslint-enable no-unused-vars, no-var */
diff --git a/cluster/prod/app/jitsi/integration/meet/nginx.conf b/cluster/prod/app/jitsi/integration/meet/nginx.conf
new file mode 100644
index 0000000..16a63f9
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/meet/nginx.conf
@@ -0,0 +1,72 @@
+# some doc: https://www.nginx.com/resources/wiki/start/topics/examples/full/
+error_log /dev/stderr;
+
+events {}
+
+http {
+ ##
+ # Basic Settings
+ ##
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+
+
+ # mimetypes, required by jitsi!
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ types {
+ application/wasm wasm;
+ }
+
+ ##
+ # SSL Settings
+ ##
+
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
+ ssl_prefer_server_ciphers on;
+
+ ##
+ # Gzip Settings
+ ##
+ gzip on;
+
+ access_log /dev/stdout;
+ server_names_hash_bucket_size 64;
+
+ server {
+ listen 0.0.0.0:443 ssl http2 default_server;
+ listen [::]:443 ssl http2 default_server;
+ server_name _;
+ ssl_certificate /etc/nginx/jitsi.crt;
+ ssl_certificate_key /etc/nginx/jitsi.key;
+ root /srv/jitsi-meet;
+ index index.html;
+
+ # lot of work would be needed to improve location rules
+ # - in order to allow - and _ in the URL, even space
+ # - while not shadowing other files (.js and following locations)
+ # - passed some times twice on the problem, not as easy as it seems
+ location ~ ^/([a-zA-Z0-9=\?]+)$ {
+ rewrite ^/(.*)$ / break;
+ }
+ location / {
+ ssi on;
+ }
+
+ location /external_api.js {
+ alias /srv/jitsi-meet/libs/external_api.min.js;
+ }
+
+ location /http-bind {
+ proxy_pass http://jitsi-xmpp:5280/http-bind;
+ proxy_set_header X-Forwarded-For \$remote_addr;
+ proxy_set_header Host \$http_host;
+ }
+
+
+ }
+}
diff --git a/cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua b/cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua
new file mode 100644
index 0000000..b5bc0b9
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua
@@ -0,0 +1,137 @@
+modules_enabled = {
+ "roster"; -- Allow users to have a roster. Recommended ;)
+ "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+ "tls"; -- Add support for secure TLS on c2s/s2s connections
+ "dialback"; -- s2s dialback support
+ "disco"; -- Service discovery
+ "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
+ "version"; -- Replies to server version requests
+ "uptime"; -- Report how long server has been running
+ "time"; -- Let others know the time here on this server
+ "ping"; -- Replies to XMPP pings with pongs
+ "pep"; -- Enables users to publish their mood, activity, playing music and more
+ -- jitsi
+ --"smacks"; -- not shipped with prosody
+ "carbons";
+ "mam";
+ "lastactivity";
+ "offline";
+ "pubsub";
+ "adhoc";
+ "websocket";
+ --"http_altconnect"; -- not shipped with prosody
+}
+modules_disabled = { "s2s" }
+
+plugin_paths = { "/usr/share/jitsi-meet/prosody-plugins/" }
+
+log = {
+ --log less on console with warn="*console"; or err="*console" or more with debug="*console"
+ info="*console";
+}
+daemonize = false
+use_libevent = true
+
+-- domain mapper options, must at least have domain base set to use the mapper
+muc_mapper_domain_base = "jitsi.deuxfleurs.fr";
+
+--@FIXME would be great to configure it
+--turncredentials_secret = "__turnSecret__";
+
+--turncredentials = {
+-- { type = "stun", host = "jitmeet.example.com", port = "3478" },
+-- { type = "turn", host = "jitmeet.example.com", port = "3478", transport = "udp" },
+-- { type = "turns", host = "jitmeet.example.com", port = "5349", transport = "tcp" }
+--};
+
+cross_domain_bosh = false;
+consider_bosh_secure = true;
+--component_ports = { 5347 }
+component_ports = { } -- it seems we don't need external components for now...
+https_ports = { } -- we don't need http
+http_ports = { 5280 }
+c2s_ports = { 5222 }
+s2s_ports = { }
+
+
+-- https://ssl-config.mozilla.org/#server=haproxy&version=2.1&config=intermediate&openssl=1.1.0g&guideline=5.4
+ssl = {
+ protocol = "tlsv1_2+";
+ ciphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
+}
+
+VirtualHost "jitsi"
+ enabled = true -- Remove this line to enable this host
+ authentication = "anonymous"
+ -- Properties below are modified by jitsi-meet-tokens package config
+ -- and authentication above is switched to "token"
+ --app_id="example_app_id"
+ --app_secret="example_app_secret"
+ -- Assign this host a certificate for TLS, otherwise it would use the one
+ -- set in the global section (if any).
+ -- Note that old-style SSL on port 5223 only supports one certificate, and will always
+ -- use the global one.
+ ssl = {
+ key = "/var/lib/prosody/jitsi.key";
+ certificate = "/var/lib/prosody/jitsi.crt";
+ }
+ speakerstats_component = "speakerstats.jitsi"
+ conference_duration_component = "conferenceduration.jitsi"
+ -- we need bosh
+ modules_enabled = {
+ "bosh";
+ "pubsub";
+ "ping"; -- Enable mod_ping
+ "speakerstats";
+ --"turncredentials"; not supported yet
+ "conference_duration";
+ "muc_lobby_rooms";
+ }
+ c2s_require_encryption = false
+ lobby_muc = "lobby.jitsi"
+ main_muc = "conference.jitsi"
+ -- muc_lobby_whitelist = { "recorder.jitmeet.example.com" } -- Here we can whitelist jibri to enter lobby enabled rooms
+
+Component "conference.jitsi" "muc"
+ storage = "memory"
+ modules_enabled = {
+ "muc_meeting_id";
+ "muc_domain_mapper";
+ --"token_verification";
+ }
+ admins = { "focus@auth.jitsi" }
+ muc_room_locking = false
+ muc_room_default_public_jids = true
+
+-- internal muc component
+Component "internal.auth.jitsi" "muc"
+ storage = "memory"
+ modules_enabled = {
+ "ping";
+ }
+ admins = { "focus@auth.jitsi", "jvb@auth.jitsi" }
+ muc_room_locking = false
+ muc_room_default_public_jids = true
+
+VirtualHost "auth.jitsi"
+ ssl = {
+ key = "/var/lib/prosody/auth.jitsi.key";
+ certificate = "/var/lib/prosody/auth.jitsi.crt";
+ }
+ authentication = "internal_plain"
+
+Component "focus.jitsi" "client_proxy"
+ target_address = "focus@auth.jitsi"
+
+Component "speakerstats.jitsi" "speakerstats_component"
+ muc_component = "conference.jitsi"
+
+Component "conferenceduration.jitsi" "conference_duration_component"
+ muc_component = "conference.jitsi"
+
+Component "lobby.jitsi" "muc"
+ storage = "memory"
+ restrict_room_creation = true
+ muc_room_locking = false
+ muc_room_default_public_jids = true
+
diff --git a/cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua.back b/cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua.back
new file mode 100644
index 0000000..d03d7c9
--- /dev/null
+++ b/cluster/prod/app/jitsi/integration/prosody/prosody.cfg.lua.back
@@ -0,0 +1,64 @@
+daemonize = false
+allow_registration = false
+use_libevent = true
+component_interface = "0.0.0.0"
+component_ports = { 5347 }
+http_ports = { 5280 }
+https_ports = {}
+
+-- Not sure all modules are required
+modules_enabled = {
+ "roster"; -- Allow users to have a roster. Recommended ;)
+ "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
+ "tls"; -- Add support for secure TLS on c2s/s2s connections
+ "dialback"; -- s2s dialback support
+ "disco"; -- Service discovery
+ "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
+ "version"; -- Replies to server version requests
+ "uptime"; -- Report how long server has been running
+ "time"; -- Let others know the time here on this server
+ "ping"; -- Replies to XMPP pings with pongs
+ "pep"; -- Enables users to publish their mood, activity, playing music and more
+ -- jitsi
+ --"smacks"; -- not shipped with prosody
+ "carbons";
+ "mam";
+ "lastactivity";
+ "offline";
+ "pubsub";
+ "adhoc";
+ "websocket";
+ --"http_altconnect"; -- not shipped with prosody
+}
+
+log = {
+ --log less on console with warn="*console"; or err="*console" or more with debug="*console"
+ debug="*console";
+}
+
+VirtualHost "jitsi"
+ authentication = "anonymous"
+ ssl = {
+ key = "/var/lib/prosody/jitsi.key";
+ certificate = "/var/lib/prosody/jitsi.crt";
+ }
+ modules_enabled = {
+ "bosh";
+ "pubsub";
+ }
+ c2s_require_encryption = false
+
+VirtualHost "auth.jitsi"
+ ssl = {
+ key = "/var/lib/prosody/auth.jitsi.key";
+ certificate = "/var/lib/prosody/auth.jitsi.crt";
+ }
+ authentication = "internal_plain"
+ admins = { "focus@auth.jitsi"}
+
+Component "conference.jitsi" "muc"
+Component "internal.auth.jitsi" "muc"
+ storage = "memory"
+ modules_enabled = { "ping"; }
+ admins = { "focus@auth.jitsi", "jvb@auth.jitsi" }
+
diff --git a/cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.crt b/cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.crt
new file mode 100644
index 0000000..f4ab925
--- /dev/null
+++ b/cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.crt
@@ -0,0 +1 @@
+SSL_CERT jitsi_auth auth.jitsi
diff --git a/cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.key b/cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.key
new file mode 100644
index 0000000..82e7b6b
--- /dev/null
+++ b/cluster/prod/app/jitsi/secrets/jitsi/auth.jitsi.key
@@ -0,0 +1 @@
+SSL_KEY jitsi_auth auth.jitsi
diff --git a/cluster/prod/app/jitsi/secrets/jitsi/jicofo_pass b/cluster/prod/app/jitsi/secrets/jitsi/jicofo_pass
new file mode 100644
index 0000000..6a0f5fc
--- /dev/null
+++ b/cluster/prod/app/jitsi/secrets/jitsi/jicofo_pass
@@ -0,0 +1 @@
+CMD openssl rand -base64 24
diff --git a/cluster/prod/app/jitsi/secrets/jitsi/jitsi.crt b/cluster/prod/app/jitsi/secrets/jitsi/jitsi.crt
new file mode 100644
index 0000000..2eed97c
--- /dev/null
+++ b/cluster/prod/app/jitsi/secrets/jitsi/jitsi.crt
@@ -0,0 +1 @@
+SSL_CERT jitsi jitsi
diff --git a/cluster/prod/app/jitsi/secrets/jitsi/jitsi.key b/cluster/prod/app/jitsi/secrets/jitsi/jitsi.key
new file mode 100644
index 0000000..af53ca0
--- /dev/null
+++ b/cluster/prod/app/jitsi/secrets/jitsi/jitsi.key
@@ -0,0 +1 @@
+SSL_KEY jitsi jitsi
diff --git a/cluster/prod/app/jitsi/secrets/jitsi/jvb_pass b/cluster/prod/app/jitsi/secrets/jitsi/jvb_pass
new file mode 100644
index 0000000..6a0f5fc
--- /dev/null
+++ b/cluster/prod/app/jitsi/secrets/jitsi/jvb_pass
@@ -0,0 +1 @@
+CMD openssl rand -base64 24
diff --git a/cluster/prod/garage/config/garage.toml b/cluster/prod/garage/config/garage.toml
new file mode 100644
index 0000000..a721886
--- /dev/null
+++ b/cluster/prod/garage/config/garage.toml
@@ -0,0 +1,24 @@
+block_size = 1048576
+
+metadata_dir = "/meta"
+data_dir = "/data"
+
+replication_mode = "3"
+
+rpc_bind_addr = "[::]:3901"
+rpc_secret = "{{ key "secrets/garage/rpc_secret" | trimSpace }}"
+
+sled_cache_capacity = 536870912
+sled_sync_interval_ms = 10000
+
+[s3_api]
+s3_region = "garage"
+api_bind_addr = "[::]:3900"
+root_domain = ".garage.deuxfleurs.fr"
+
+[s3_web]
+bind_addr = "[::]:3902"
+root_domain = ".web.deuxfleurs.fr"
+
+[admin]
+api_bind_addr = "[::1]:3903"
diff --git a/cluster/prod/garage/deploy/garage.hcl b/cluster/prod/garage/deploy/garage.hcl
new file mode 100644
index 0000000..665515a
--- /dev/null
+++ b/cluster/prod/garage/deploy/garage.hcl
@@ -0,0 +1,131 @@
+job "garage" {
+ datacenters = ["dc1", "saturne", "neptune"]
+ type = "system"
+ priority = 80
+
+ constraint {
+ attribute = "${attr.cpu.arch}"
+ value = "amd64"
+ }
+
+ group "garage" {
+ network {
+ port "s3" { static = 3900 }
+ port "rpc" { static = 3901 }
+ port "web" { static = 3902 }
+ }
+
+ update {
+ max_parallel = 1
+ min_healthy_time = "30s"
+ healthy_deadline = "5m"
+ }
+
+ task "server" {
+ driver = "docker"
+ config {
+ advertise_ipv6_address = true
+ image = "dxflrs/amd64_garage:v0.7.1"
+ command = "/garage"
+ args = [ "server" ]
+ network_mode = "host"
+ volumes = [
+ "/mnt/storage/garage/data:/data",
+ "/mnt/ssd/garage/meta:/meta",
+ "secrets/garage.toml:/etc/garage.toml",
+ ]
+ logging {
+ type = "journald"
+ }
+ }
+
+ template {
+ data = file("../config/garage.toml")
+ destination = "secrets/garage.toml"
+ }
+
+ resources {
+ memory = 1500
+ cpu = 1000
+ }
+
+ kill_signal = "SIGINT"
+ kill_timeout = "20s"
+
+ service {
+ tags = [
+ "garage_api",
+ "tricot garage.deuxfleurs.fr",
+ "tricot *.garage.deuxfleurs.fr",
+ ]
+ port = 3900
+ address_mode = "driver"
+ name = "garage-api"
+ check {
+ type = "tcp"
+ port = 3900
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ service {
+ tags = ["garage-rpc"]
+ port = 3901
+ address_mode = "driver"
+ name = "garage-rpc"
+ check {
+ type = "tcp"
+ port = 3901
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ service {
+ tags = [
+ "garage-web",
+ "tricot * 1",
+ "tricot-add-header Content-Security-Policy default-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://code.jquery.com/; frame-ancestors 'self'",
+ "tricot-add-header Strict-Transport-Security max-age=63072000; includeSubDomains; preload",
+ "tricot-add-header X-Frame-Options SAMEORIGIN",
+ "tricot-add-header X-XSS-Protection 1; mode=block",
+ ]
+ port = 3902
+ address_mode = "driver"
+ name = "garage-web"
+ check {
+ type = "tcp"
+ port = 3902
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ restart {
+ interval = "30m"
+ attempts = 10
+ delay = "15s"
+ mode = "delay"
+ }
+ }
+ }
+}
diff --git a/cluster/prod/garage/secrets/garage/rpc_secret b/cluster/prod/garage/secrets/garage/rpc_secret
new file mode 100644
index 0000000..d831d53
--- /dev/null
+++ b/cluster/prod/garage/secrets/garage/rpc_secret
@@ -0,0 +1 @@
+CMD_ONCE openssl rand -hex 32
diff --git a/cluster/staging/app/directory/config/bottin/config.json.tpl b/cluster/staging/app/directory/config/bottin/config.json.tpl
new file mode 100644
index 0000000..844f7b7
--- /dev/null
+++ b/cluster/staging/app/directory/config/bottin/config.json.tpl
@@ -0,0 +1,26 @@
+{
+ "suffix": "{{ key "secrets/directory/ldap_base_dn" }}",
+ "bind": "0.0.0.0:389",
+ "log_level": "debug",
+ "acl": [
+ "*,{{ key "secrets/directory/ldap_base_dn" }}::read:*:* !userpassword !user_secret !alternate_user_secrets !garage_s3_secret_key",
+ "*::read modify:SELF:*",
+ "ANONYMOUS::bind:*,ou=users,{{ key "secrets/directory/ldap_base_dn" }}:",
+ "ANONYMOUS::bind:cn=admin,{{ key "secrets/directory/ldap_base_dn" }}:",
+ "*,ou=services,ou=users,{{ key "secrets/directory/ldap_base_dn" }}::bind:*,ou=users,{{ key "secrets/directory/ldap_base_dn" }}:*",
+ "*,ou=services,ou=users,{{ key "secrets/directory/ldap_base_dn" }}::read:*:*",
+
+ "*:cn=asso_deuxfleurs,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}:add:*,ou=invitations,{{ key "secrets/directory/ldap_base_dn" }}:*",
+ "ANONYMOUS::bind:*,ou=invitations,{{ key "secrets/directory/ldap_base_dn" }}:",
+ "*,ou=invitations,{{ key "secrets/directory/ldap_base_dn" }}::delete:SELF:*",
+
+ "*:cn=asso_deuxfleurs,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}:add:*,ou=users,{{ key "secrets/directory/ldap_base_dn" }}:*",
+ "*,ou=invitations,{{ key "secrets/directory/ldap_base_dn" }}::add:*,ou=users,{{ key "secrets/directory/ldap_base_dn" }}:*",
+
+ "*:cn=asso_deuxfleurs,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}:modifyAdd:cn=email,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}:*",
+ "*,ou=invitations,{{ key "secrets/directory/ldap_base_dn" }}::modifyAdd:cn=email,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}:*",
+
+ "cn=admin,{{ key "secrets/directory/ldap_base_dn" }}::read add modify delete:*:*",
+ "*:cn=admin,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}:read add modify delete:*:*"
+ ]
+}
diff --git a/cluster/staging/app/directory/config/guichet/config.json.tpl b/cluster/staging/app/directory/config/guichet/config.json.tpl
new file mode 100644
index 0000000..1a843a8
--- /dev/null
+++ b/cluster/staging/app/directory/config/guichet/config.json.tpl
@@ -0,0 +1,34 @@
+{
+ "http_bind_addr": ":9991",
+ "ldap_server_addr": "ldap://bottin.service.staging.consul:389",
+
+ "base_dn": "{{ key "secrets/directory/ldap_base_dn" }}",
+ "user_base_dn": "ou=users,{{ key "secrets/directory/ldap_base_dn" }}",
+ "user_name_attr": "cn",
+ "group_base_dn": "ou=groups,{{ key "secrets/directory/ldap_base_dn" }}",
+ "group_name_attr": "cn",
+
+ "invitation_base_dn": "ou=invitations,{{ key "secrets/directory/ldap_base_dn" }}",
+ "invitation_name_attr": "cn",
+ "invited_mail_format": "{}@{{ key "secrets/directory/guichet/mail_domain" | trimSpace }}",
+ "invited_auto_groups": [
+ "cn=email,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}"
+ ],
+
+ "web_address": "https://{{ key "secrets/directory/guichet/web_hostname" }}",
+ "mail_from": "{{ key "secrets/directory/guichet/mail_from" }}",
+ "smtp_server": "{{ key "secrets/directory/guichet/smtp_server" }}",
+ "smtp_username": "{{ key "secrets/directory/guichet/smtp_user" | trimSpace }}",
+ "smtp_password": "{{ key "secrets/directory/guichet/smtp_pass" | trimSpace }}",
+
+ "admin_account": "cn=admin,{{ key "secrets/directory/ldap_base_dn" }}",
+ "group_can_admin": "cn=admin,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}",
+ "group_can_invite": "cn=asso_deuxfleurs,ou=groups,{{ key "secrets/directory/ldap_base_dn" }}",
+
+ "s3_endpoint": "{{ key "secrets/directory/guichet/s3_endpoint" }}",
+ "s3_access_key": "{{ key "secrets/directory/guichet/s3_access_key" | trimSpace }}",
+ "s3_secret_key": "{{ key "secrets/directory/guichet/s3_secret_key" | trimSpace }}",
+ "s3_region": "{{ key "secrets/directory/guichet/s3_region" }}",
+ "s3_bucket": "{{ key "secrets/directory/guichet/s3_bucket" }}"
+}
+
diff --git a/cluster/staging/app/directory/deploy/directory.hcl b/cluster/staging/app/directory/deploy/directory.hcl
new file mode 100644
index 0000000..405c321
--- /dev/null
+++ b/cluster/staging/app/directory/deploy/directory.hcl
@@ -0,0 +1,141 @@
+job "directory" {
+ datacenters = ["dc1", "neptune"]
+ type = "service"
+ priority = 90
+
+ constraint {
+ attribute = "${attr.cpu.arch}"
+ value = "amd64"
+ }
+
+ group "bottin" {
+ count = 1
+
+ network {
+ port "ldap_port" {
+ static = 389
+ to = 389
+ }
+ }
+
+ task "bottin" {
+ driver = "docker"
+ config {
+ image = "superboum/bottin_amd64:22"
+ network_mode = "host"
+ readonly_rootfs = true
+ ports = [ "ldap_port" ]
+ volumes = [
+ "secrets/config.json:/config.json",
+ "secrets:/etc/bottin",
+ ]
+ }
+
+ resources {
+ memory = 100
+ }
+
+ template {
+ data = file("../config/bottin/config.json.tpl")
+ destination = "secrets/config.json"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-ca.crt\" }}"
+ destination = "secrets/consul-ca.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-client.crt\" }}"
+ destination = "secrets/consul-client.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-client.key\" }}"
+ destination = "secrets/consul-client.key"
+ }
+
+ template {
+ data = <<EOH
+CONSUL_HTTP_ADDR=https://localhost:8501
+CONSUL_HTTP_SSL=true
+CONSUL_CACERT=/etc/bottin/consul-ca.crt
+CONSUL_CLIENT_CERT=/etc/bottin/consul-client.crt
+CONSUL_CLIENT_KEY=/etc/bottin/consul-client.key
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ service {
+ tags = ["bottin"]
+ port = "ldap_port"
+ address_mode = "host"
+ name = "bottin"
+ check {
+ type = "tcp"
+ port = "ldap_port"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+ }
+ }
+
+ group "guichet" {
+ count = 1
+
+ network {
+ port "web_port" { to = 9991 }
+ }
+
+ task "guichet" {
+ driver = "docker"
+ config {
+ image = "superboum/guichet_amd64:15"
+ readonly_rootfs = true
+ ports = [ "web_port" ]
+ volumes = [
+ "secrets/config.json:/config.json"
+ ]
+ }
+
+ template {
+ data = file("../config/guichet/config.json.tpl")
+ destination = "secrets/config.json"
+ }
+
+ resources {
+ memory = 200
+ }
+
+ service {
+ name = "guichet"
+ tags = [
+ "guichet",
+ "tricot guichet-staging.home.adnab.me",
+ "tricot guichet.staging.deuxfleurs.org",
+ ]
+ port = "web_port"
+ address_mode = "host"
+ check {
+ type = "tcp"
+ port = "web_port"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/mail_domain b/cluster/staging/app/directory/secrets/directory/guichet/mail_domain
new file mode 100644
index 0000000..5db1ba3
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/mail_domain
@@ -0,0 +1 @@
+USER E-mail domain for new users (e.g. example.com)
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/mail_from b/cluster/staging/app/directory/secrets/directory/guichet/mail_from
new file mode 100644
index 0000000..9075cbf
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/mail_from
@@ -0,0 +1 @@
+USER E-mail address from which to send welcome emails to new users
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/s3_access_key b/cluster/staging/app/directory/secrets/directory/guichet/s3_access_key
new file mode 100644
index 0000000..e5b37ff
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/s3_access_key
@@ -0,0 +1 @@
+USER Garage access key for Guichet profile pictures
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/s3_bucket b/cluster/staging/app/directory/secrets/directory/guichet/s3_bucket
new file mode 100644
index 0000000..cb059cf
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/s3_bucket
@@ -0,0 +1 @@
+USER S3 bucket in which to store data files (such as profile pictures)
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/s3_endpoint b/cluster/staging/app/directory/secrets/directory/guichet/s3_endpoint
new file mode 100644
index 0000000..b414269
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/s3_endpoint
@@ -0,0 +1 @@
+USER S3 endpoint URL
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/s3_region b/cluster/staging/app/directory/secrets/directory/guichet/s3_region
new file mode 100644
index 0000000..ef16924
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/s3_region
@@ -0,0 +1 @@
+USER S3 region
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/s3_secret_key b/cluster/staging/app/directory/secrets/directory/guichet/s3_secret_key
new file mode 100644
index 0000000..f3e7f0f
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/s3_secret_key
@@ -0,0 +1 @@
+USER Garage secret key for Guichet profile pictures
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/smtp_pass b/cluster/staging/app/directory/secrets/directory/guichet/smtp_pass
new file mode 100644
index 0000000..fc9d1e3
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/smtp_pass
@@ -0,0 +1 @@
+USER SMTP password
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/smtp_server b/cluster/staging/app/directory/secrets/directory/guichet/smtp_server
new file mode 100644
index 0000000..c453935
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/smtp_server
@@ -0,0 +1 @@
+USER SMTP server address (hostname:port)
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/smtp_user b/cluster/staging/app/directory/secrets/directory/guichet/smtp_user
new file mode 100644
index 0000000..c9c8bd0
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/smtp_user
@@ -0,0 +1 @@
+USER SMTP username
diff --git a/cluster/staging/app/directory/secrets/directory/guichet/web_hostname b/cluster/staging/app/directory/secrets/directory/guichet/web_hostname
new file mode 100644
index 0000000..afe2512
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/guichet/web_hostname
@@ -0,0 +1 @@
+USER Public hostname from which Guichet is accessible via HTTP (e.g. guichet.example.com)
diff --git a/cluster/staging/app/directory/secrets/directory/ldap_base_dn b/cluster/staging/app/directory/secrets/directory/ldap_base_dn
new file mode 100644
index 0000000..ea5c7ae
--- /dev/null
+++ b/cluster/staging/app/directory/secrets/directory/ldap_base_dn
@@ -0,0 +1 @@
+USER LDAP base DN for everything (e.g. dc=example,dc=com)
diff --git a/cluster/staging/app/docker-compose.yml b/cluster/staging/app/docker-compose.yml
new file mode 100644
index 0000000..812c148
--- /dev/null
+++ b/cluster/staging/app/docker-compose.yml
@@ -0,0 +1,16 @@
+version: '3.4'
+services:
+
+ synapse:
+ build:
+ context: ./im/build/matrix-synapse
+ args:
+ # https://github.com/matrix-org/synapse/releases
+ VERSION: 1.49.2
+ # https://github.com/matrix-org/synapse-s3-storage-provider/commits/main
+ # Update with the latest commit on main each time you update the synapse version
+ # otherwise synapse may fail to launch due to incompatibility issues
+ # see this issue for an example: https://github.com/matrix-org/synapse-s3-storage-provider/issues/64
+ S3_VERSION: 8926b4e4178edcda1a32fdb39bd36cef1a1a9d40
+ image: lxpz/amd64_synapse:1.49.2-4
+
diff --git a/cluster/staging/app/drone-ci/build/.gitignore b/cluster/staging/app/drone-ci/build/.gitignore
new file mode 100644
index 0000000..ef92077
--- /dev/null
+++ b/cluster/staging/app/drone-ci/build/.gitignore
@@ -0,0 +1,2 @@
+result/
+*.qcow2.zst
diff --git a/cluster/staging/app/drone-ci/build/Makefile b/cluster/staging/app/drone-ci/build/Makefile
new file mode 100644
index 0000000..2814a0d
--- /dev/null
+++ b/cluster/staging/app/drone-ci/build/Makefile
@@ -0,0 +1,8 @@
+.PHONY: all
+
+all:
+ nix-build '<nixpkgs/nixos>' -A config.system.build.qcow2 --arg configuration "{ imports = [ ./build-qcow2.nix ]; }" --show-trace
+ zstd -7 -i result/nixos.qcow2 -o drone-runner.qcow2.zst -f
+ RESULTPATH=`readlink result`; rm result; nix-store --delete $$RESULTPATH
+ rclone copy drone-runner.qcow2.zst grgdf:alex/ -vv
+
diff --git a/cluster/staging/app/drone-ci/build/build-qcow2.nix b/cluster/staging/app/drone-ci/build/build-qcow2.nix
new file mode 100644
index 0000000..3ad45f4
--- /dev/null
+++ b/cluster/staging/app/drone-ci/build/build-qcow2.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+ imports =
+ [
+ <nixpkgs/nixos/modules/installer/cd-dvd/channel.nix>
+ ./machine-config.nix
+ ];
+
+ system.build.qcow2 = import <nixpkgs/nixos/lib/make-disk-image.nix> {
+ inherit lib config;
+ pkgs = import <nixpkgs> { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
+ diskSize = 32768;
+ format = "qcow2";
+ configFile = pkgs.writeText "configuration.nix"
+ ''
+ {
+ imports = [ <./machine-config.nix> ];
+ }
+ '';
+ };
+}
diff --git a/cluster/staging/app/drone-ci/build/machine-config.nix b/cluster/staging/app/drone-ci/build/machine-config.nix
new file mode 100644
index 0000000..73d3f09
--- /dev/null
+++ b/cluster/staging/app/drone-ci/build/machine-config.nix
@@ -0,0 +1,89 @@
+{ pkgs, lib, ... }:
+
+with lib;
+
+{
+ imports = [
+ <nixpkgs/nixos/modules/profiles/qemu-guest.nix>
+ ];
+
+ config = {
+ fileSystems."/" = {
+ device = "/dev/disk/by-label/nixos";
+ fsType = "ext4";
+ autoResize = true;
+ };
+
+ fileSystems."/secrets" = {
+ device = "/dev/disk/by-label/QEMU\\x20VVFAT";
+ fsType = "vfat";
+ };
+
+ boot.growPartition = true;
+ boot.kernelParams = [ "console=ttyS0" ];
+ boot.loader.grub.device = "/dev/vda";
+ boot.loader.timeout = 0;
+
+ environment.systemPackages = with pkgs; [
+ iotop
+ jnettop
+ htop
+ ];
+
+ users.extraUsers.root.openssh.authorizedKeys.keys = [
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJpaBZdYxHqMxhv2RExAOa7nkKhPBOHupMP3mYaZ73w9 lx@lindy"
+ ];
+ services.openssh.enable = true;
+ services.openssh.permitRootLogin = "prohibit-password";
+ networking.firewall = {
+ enable = true;
+ allowedTCPPorts = [ 22 ];
+ };
+
+ virtualisation.docker.enable = true;
+ virtualisation.oci-containers.backend = "docker";
+ systemd.services.drone_nix_setup = {
+ enable = true;
+ path = [
+ pkgs.docker
+ ];
+ script = ''
+ docker run --rm -v /var/lib/drone/nix:/mnt nixpkgs/nix:nixos-21.05 cp -r /nix/{store,var} /mnt/
+ '';
+ wantedBy = [ "multi-user.target" ];
+ };
+ virtualisation.oci-containers.containers = {
+ drone_runner = {
+ image = "drone/drone-runner-docker:1.4.0";
+ volumes = [
+ "/var/lib/drone/nix:/nix"
+ "/var/run/docker.sock:/var/run/docker.sock"
+ ];
+ environment = {
+ DRONE_RPC_PROTO = "https";
+ DRONE_RPC_HOST = "drone.deuxfleurs.fr";
+ DRONE_RUNNER_CAPACITY = "1";
+ DRONE_DEBUG = "true";
+ DRONE_LOGS_TRACE = "true";
+ DRONE_RPC_DUMP_HTTP = "true";
+ DRONE_RPC_DUMP_HTTP_BODY = "true";
+ DRONE_RUNNER_LABELS = "nix:1";
+ };
+ environmentFiles = [
+ "/secrets/secret_env"
+ ];
+ };
+ drone_gc = {
+ image = "drone/gc:latest";
+ volumes = [
+ "/var/run/docker.sock:/var/run/docker.sock"
+ ];
+ environment = {
+ GC_DEBUG = "true";
+ GC_CACHE = "10gb";
+ GC_INTERVAL = "10m";
+ };
+ };
+ };
+ };
+}
diff --git a/cluster/staging/app/drone-ci/deploy/bad-runner-vm.hcl b/cluster/staging/app/drone-ci/deploy/bad-runner-vm.hcl
new file mode 100644
index 0000000..7c3a7e2
--- /dev/null
+++ b/cluster/staging/app/drone-ci/deploy/bad-runner-vm.hcl
@@ -0,0 +1,48 @@
+job "drone-runner" {
+ datacenters = ["neptune"]
+ type = "system"
+
+ group "runner-vm" {
+ network {
+ port "ssh" {
+ static = 22544
+ }
+ }
+
+ task "drone-runner-vm" {
+ driver = "qemu"
+
+ config {
+ image_path = "local/drone-runner.qcow2"
+ accelerator = "kvm"
+ args = [
+ "-drive", "index=1,file=fat:rw:/var/lib/nomad/alloc/${NOMAD_ALLOC_ID}/${NOMAD_TASK_NAME}/secrets,format=raw,media=disk",
+ "-device", "e1000,netdev=user.0",
+ "-netdev", "user,id=user.0,hostfwd=tcp::${NOMAD_PORT_ssh}-:22",
+ "-smp", "2",
+ ]
+ port_map {
+ ssh = 22
+ }
+ }
+
+ artifact {
+ source = "https://alex.web.deuxfleurs.fr/drone-runner.qcow2.zst"
+ destination = "local/drone-runner.qcow2"
+ mode = "file"
+ }
+
+ template {
+ data = <<EOH
+DRONE_RPC_SECRET={{ key "secrets/drone-ci/rpc_secret" | trimSpace }}
+DRONE_RUNNER_NAME={{ env "attr.unique.hostname" }}
+EOH
+ destination = "secrets/secret_env"
+ }
+
+ resources {
+ memory = 2000
+ }
+ }
+ }
+}
diff --git a/cluster/staging/app/drone-ci/deploy/runner-docker.hcl b/cluster/staging/app/drone-ci/deploy/runner-docker.hcl
new file mode 100644
index 0000000..d7c6ef4
--- /dev/null
+++ b/cluster/staging/app/drone-ci/deploy/runner-docker.hcl
@@ -0,0 +1,91 @@
+job "drone-runner" {
+ datacenters = ["neptune"]
+ type = "system"
+
+ group "runner" {
+
+ task "populate-nix-store" {
+ lifecycle {
+ hook = "prestart"
+ sidecar = false
+ }
+
+ driver = "docker"
+ config {
+ image = "nixpkgs/nix:nixos-21.05"
+ command = "sh"
+ args = [
+ "-c", "test -d /mnt/store || cp -rv /nix/{store,var} /mnt/"
+ ]
+ volumes = [
+ "/var/lib/drone/nix:/mnt",
+ ]
+ }
+
+ resources {
+ memory = 100
+ cpu = 100
+ }
+ }
+
+ task "drone-runner" {
+ driver = "docker"
+ config {
+ image = "drone/drone-runner-docker:1.8.1"
+
+ volumes = [
+ "/var/lib/drone/nix:/nix",
+ "/var/run/docker.sock:/var/run/docker.sock"
+ ]
+ }
+
+ template {
+ data = <<EOH
+DRONE_RPC_PROTO=https
+DRONE_RPC_HOST=drone.deuxfleurs.fr
+DRONE_RPC_SECRET={{ key "secrets/drone-ci/rpc_secret" | trimSpace }}
+DRONE_RUNNER_CAPACITY=1
+DRONE_DEBUG=true
+DRONE_LOGS_TRACE=true
+DRONE_RPC_DUMP_HTTP=true
+DRONE_RPC_DUMP_HTTP_BODY=true
+DRONE_RUNNER_NAME={{ env "attr.unique.hostname" }}
+DRONE_RUNNER_LABELS=nix:1
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ resources {
+ memory = 200
+ cpu = 100
+ }
+ }
+
+ task "drone-gc" {
+ driver = "docker"
+ config {
+ image = "drone/gc:latest"
+
+ volumes = [
+ "/var/run/docker.sock:/var/run/docker.sock"
+ ]
+ }
+
+ template {
+ data = <<EOH
+GC_DEBUG=true
+GC_CACHE=10gb
+GC_INTERVAL=10m
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ resources {
+ memory = 100
+ cpu = 100
+ }
+ }
+ }
+}
diff --git a/cluster/staging/app/frontend/deploy/frontend-tricot.hcl b/cluster/staging/app/frontend/deploy/frontend-tricot.hcl
new file mode 100644
index 0000000..745e77c
--- /dev/null
+++ b/cluster/staging/app/frontend/deploy/frontend-tricot.hcl
@@ -0,0 +1,89 @@
+job "frontend" {
+ datacenters = ["neptune"]
+ type = "service"
+ priority = 90
+
+ group "tricot" {
+ constraint {
+ attribute = "${attr.unique.hostname}"
+ value = "caribou"
+ }
+
+ network {
+ port "http_port" { static = 80 }
+ port "https_port" { static = 443 }
+ }
+
+ task "server" {
+ driver = "docker"
+
+ config {
+ image = "lxpz/amd64_tricot:41"
+ network_mode = "host"
+ readonly_rootfs = true
+ ports = [ "http_port", "https_port" ]
+ volumes = [
+ "secrets:/etc/tricot",
+ ]
+ }
+
+ resources {
+ cpu = 2000
+ memory = 200
+ }
+
+ restart {
+ interval = "30m"
+ attempts = 2
+ delay = "15s"
+ mode = "delay"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-ca.crt\" }}"
+ destination = "secrets/consul-ca.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-client.crt\" }}"
+ destination = "secrets/consul-client.crt"
+ }
+
+ template {
+ data = "{{ key \"secrets/consul/consul-client.key\" }}"
+ destination = "secrets/consul-client.key"
+ }
+
+ template {
+ data = <<EOH
+TRICOT_NODE_NAME={{ env "attr.unique.consul.name" }}
+TRICOT_LETSENCRYPT_EMAIL=alex@adnab.me
+TRICOT_ENABLE_COMPRESSION=true
+TRICOT_CONSUL_HOST=https://localhost:8501
+TRICOT_CONSUL_CA_CERT=/etc/tricot/consul-ca.crt
+TRICOT_CONSUL_CLIENT_CERT=/etc/tricot/consul-client.crt
+TRICOT_CONSUL_CLIENT_KEY=/etc/tricot/consul-client.key
+TRICOT_HTTP_BIND_ADDR=[::]:80
+TRICOT_HTTPS_BIND_ADDR=[::]:443
+RUST_LOG=tricot=debug
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ service {
+ name = "tricot-http"
+ port = "http_port"
+ /* tags = [ "(diplonat (tcp_port 80))" ] */
+ address_mode = "host"
+ }
+
+ service {
+ name = "tricot-https"
+ port = "https_port"
+ /* tags = [ "(diplonat (tcp_port 443))" ] */
+ address_mode = "host"
+ }
+ }
+ }
+}
diff --git a/cluster/staging/app/garage/config/garage.toml b/cluster/staging/app/garage/config/garage.toml
new file mode 100644
index 0000000..60ab797
--- /dev/null
+++ b/cluster/staging/app/garage/config/garage.toml
@@ -0,0 +1,31 @@
+block_size = 1048576
+
+metadata_dir = "/meta"
+data_dir = "/data"
+
+replication_mode = "3"
+
+rpc_bind_addr = "0.0.0.0:3991"
+rpc_secret = "{{ key "secrets/garage-staging/rpc_secret" | trimSpace }}"
+
+#consul_host = "localhost:8500"
+#consul_service_name = "garage-staging-rpc-self-advertised"
+
+bootstrap_peers = []
+
+[s3_api]
+s3_region = "garage-staging"
+api_bind_addr = "0.0.0.0:3990"
+
+[k2v_api]
+api_bind_addr = "0.0.0.0:3993"
+
+[s3_web]
+bind_addr = "0.0.0.0:3992"
+root_domain = ".web.staging.deuxfleurs.org"
+index = "index.html"
+
+[admin]
+api_bind_addr = "0.0.0.0:3909"
+admin_token = "{{ key "secrets/garage-staging/admin_token" | trimSpace }}"
+trace_sink = "http://{{ env "attr.unique.network.ip-address" }}:4317"
diff --git a/cluster/staging/app/garage/deploy/garage.hcl b/cluster/staging/app/garage/deploy/garage.hcl
new file mode 100644
index 0000000..a1907d4
--- /dev/null
+++ b/cluster/staging/app/garage/deploy/garage.hcl
@@ -0,0 +1,195 @@
+job "garage-staging" {
+ type = "system"
+ #datacenters = [ "neptune", "pluton" ]
+ datacenters = [ "neptune" ]
+
+ priority = 80
+
+ constraint {
+ attribute = "${attr.cpu.arch}"
+ value = "amd64"
+ }
+
+ group "garage-staging" {
+ network {
+ port "s3" { static = 3990 }
+ port "rpc" { static = 3991 }
+ port "web" { static = 3992 }
+ port "k2v" { static = 3993 }
+ port "admin" { static = 3909 }
+ }
+
+ update {
+ max_parallel = 1
+ min_healthy_time = "30s"
+ healthy_deadline = "5m"
+ }
+
+ # task "repair" {
+ # lifecycle {
+ # hook = "prestart"
+ # sidecar = false
+ # }
+
+ # driver = "docker"
+
+ # config {
+ # image = "dxflrs/amd64_garage:v0.7.99-k2v"
+ # command = "/garage"
+ # args = [ "offline-repair", "--yes", "object_counters" ]
+ # network_mode = "host"
+ # volumes = [
+ # "/mnt/storage/garage-staging/data:/data",
+ # "/mnt/ssd/garage-staging/meta:/meta",
+ # "secrets/garage.toml:/etc/garage.toml",
+ # ]
+ # }
+
+ # template {
+ # data = file("../config/garage.toml")
+ # destination = "secrets/garage.toml"
+ # }
+
+ # resources {
+ # memory = 2000
+ # cpu = 1000
+ # }
+ # }
+
+ task "server" {
+ driver = "docker"
+
+ config {
+ image = "dxflrs/amd64_garage:v0.7.99.2-k2v"
+ command = "/garage"
+ args = [ "server" ]
+ network_mode = "host"
+ volumes = [
+ "/mnt/storage/garage-staging/data:/data",
+ "/mnt/ssd/garage-staging/meta:/meta",
+ "secrets/garage.toml:/etc/garage.toml",
+ ]
+ }
+
+ template {
+ data = file("../config/garage.toml")
+ destination = "secrets/garage.toml"
+ }
+
+ resources {
+ memory = 1000
+ cpu = 1000
+ }
+
+ kill_signal = "SIGINT"
+ kill_timeout = "20s"
+
+ service {
+ tags = [
+ "garage-staging-api",
+ "tricot garage-staging.home.adnab.me",
+ "tricot garage.staging.deuxfleurs.org",
+ "tricot-add-header Access-Control-Allow-Origin *",
+ ]
+ port = 3990
+ address_mode = "driver"
+ name = "garage-staging-api"
+ check {
+ type = "tcp"
+ port = 3990
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ service {
+ tags = [
+ "garage-staging-k2v-api",
+ "tricot k2v.staging.deuxfleurs.org",
+ "tricot-add-header Access-Control-Allow-Origin *",
+ ]
+ port = 3993
+ address_mode = "driver"
+ name = "garage-staging-k2v-api"
+ check {
+ type = "tcp"
+ port = 3993
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ service {
+ tags = ["garage-staging-rpc"]
+ port = 3991
+ address_mode = "driver"
+ name = "garage-staging-rpc"
+ check {
+ type = "tcp"
+ port = 3991
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ service {
+ tags = [
+ "garage-staging-web",
+ "tricot *.web.staging.deuxfleurs.org",
+ "tricot staging.deuxfleurs.org",
+ "tricot matrix.home.adnab.me/.well-known/matrix/server",
+ "tricot-add-header Access-Control-Allow-Origin *",
+ ]
+ port = 3992
+ address_mode = "driver"
+ name = "garage-staging-web"
+ check {
+ type = "tcp"
+ port = 3992
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+
+ service {
+ tags = [
+ "garage-staging-admin",
+ ]
+ port = 3909
+ address_mode = "driver"
+ name = "garage-staging-admin"
+ }
+
+ restart {
+ interval = "30m"
+ attempts = 10
+ delay = "15s"
+ mode = "delay"
+ }
+ }
+ }
+}
diff --git a/cluster/staging/app/garage/secrets/garage-staging/rpc_secret b/cluster/staging/app/garage/secrets/garage-staging/rpc_secret
new file mode 100644
index 0000000..d831d53
--- /dev/null
+++ b/cluster/staging/app/garage/secrets/garage-staging/rpc_secret
@@ -0,0 +1 @@
+CMD_ONCE openssl rand -hex 32
diff --git a/cluster/staging/app/im/build/matrix-synapse/Dockerfile b/cluster/staging/app/im/build/matrix-synapse/Dockerfile
new file mode 100644
index 0000000..0496b19
--- /dev/null
+++ b/cluster/staging/app/im/build/matrix-synapse/Dockerfile
@@ -0,0 +1,54 @@
+FROM amd64/debian:buster as builder
+
+ARG VERSION
+ARG S3_VERSION
+RUN apt-get update && \
+ apt-get -qq -y full-upgrade && \
+ apt-get install -y \
+ python3 \
+ python3-pip \
+ python3-dev \
+ python3-setuptools \
+ libffi-dev \
+ build-essential \
+ libssl-dev \
+ libjpeg-dev \
+ libjpeg62-turbo-dev \
+ libxml2-dev \
+ zlib1g-dev \
+ # postgresql-dev \
+ libpq-dev \
+ virtualenv \
+ libxslt1-dev \
+ git
+
+RUN virtualenv /root/matrix-env -p /usr/bin/python3
+RUN . /root/matrix-env/bin/activate && \
+ pip3 install \
+ https://github.com/matrix-org/synapse/archive/v${VERSION}.tar.gz#egg=matrix-synapse[matrix-synapse-ldap3,postgres,resources.consent,saml2,url_preview] && \
+ pip3 install \
+ git+https://github.com/matrix-org/synapse-s3-storage-provider.git@${S3_VERSION}
+
+FROM amd64/debian:buster
+
+RUN apt-get update && \
+ apt-get -qq -y full-upgrade && \
+ apt-get install -y \
+ python3 \
+ python3-distutils \
+ libffi6 \
+ libjpeg62-turbo \
+ libssl1.1 \
+ libxslt1.1 \
+ libpq5 \
+ zlib1g \
+ libjemalloc2 \
+ ca-certificates
+
+ENV LD_PRELOAD /usr/lib/x86_64-linux-gnu/libjemalloc.so.2
+COPY --from=builder /root/matrix-env /root/matrix-env
+COPY matrix-s3-async /usr/local/bin/matrix-s3-async
+COPY matrix-s3-async-sqlite /usr/local/bin/matrix-s3-async-sqlite
+COPY entrypoint.sh /usr/local/bin/entrypoint
+
+ENTRYPOINT ["/usr/local/bin/entrypoint"]
diff --git a/cluster/staging/app/im/build/matrix-synapse/entrypoint.sh b/cluster/staging/app/im/build/matrix-synapse/entrypoint.sh
new file mode 100755
index 0000000..b93a702
--- /dev/null
+++ b/cluster/staging/app/im/build/matrix-synapse/entrypoint.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+. /root/matrix-env/bin/activate
+exec "$@"
diff --git a/cluster/staging/app/im/build/matrix-synapse/matrix-s3-async b/cluster/staging/app/im/build/matrix-synapse/matrix-s3-async
new file mode 100755
index 0000000..e435144
--- /dev/null
+++ b/cluster/staging/app/im/build/matrix-synapse/matrix-s3-async
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+cat > database.yaml <<EOF
+user: $PG_USER
+password: $PG_PASS
+database: $PG_DB
+host: $PG_HOST
+port: $PG_PORT
+EOF
+
+while true; do
+ /root/matrix-env/bin/s3_media_upload update-db 0d
+ /root/matrix-env/bin/s3_media_upload --no-progress check-deleted /var/lib/matrix-synapse/media
+ /root/matrix-env/bin/s3_media_upload --no-progress upload /var/lib/matrix-synapse/media matrix --delete --endpoint-url https://garage.deuxfleurs.fr
+ sleep 600
+done
diff --git a/cluster/staging/app/im/build/matrix-synapse/matrix-s3-async-sqlite b/cluster/staging/app/im/build/matrix-synapse/matrix-s3-async-sqlite
new file mode 100755
index 0000000..4bba072
--- /dev/null
+++ b/cluster/staging/app/im/build/matrix-synapse/matrix-s3-async-sqlite
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+cat > database.yaml <<EOF
+sqlite:
+ database: $SYNAPSE_SQLITE_DB
+EOF
+
+while true; do
+ /root/matrix-env/bin/s3_media_upload update-db 0d
+ /root/matrix-env/bin/s3_media_upload --no-progress check-deleted $SYNAPSE_MEDIA_STORE
+ /root/matrix-env/bin/s3_media_upload --no-progress upload $SYNAPSE_MEDIA_STORE $SYNAPSE_MEDIA_S3_BUCKET --delete --endpoint-url $S3_ENDPOINT
+ sleep 600
+done
diff --git a/cluster/staging/app/im/config/homeserver.yaml b/cluster/staging/app/im/config/homeserver.yaml
new file mode 100644
index 0000000..38db527
--- /dev/null
+++ b/cluster/staging/app/im/config/homeserver.yaml
@@ -0,0 +1,2651 @@
+# Configuration file for Synapse.
+#
+# This is a YAML file: see [1] for a quick introduction. Note in particular
+# that *indentation is important*: all the elements of a list or dictionary
+# should have the same indentation.
+#
+# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
+
+
+## Modules ##
+
+# Server admins can expand Synapse's functionality with external modules.
+#
+# See https://matrix-org.github.io/synapse/latest/modules.html for more
+# documentation on how to configure or create custom modules for Synapse.
+#
+modules:
+ # - module: my_super_module.MySuperClass
+ # config:
+ # do_thing: true
+ # - module: my_other_super_module.SomeClass
+ # config: {}
+
+
+## Server ##
+
+# The public-facing domain of the server
+#
+# The server_name name will appear at the end of usernames and room addresses
+# created on this server. For example if the server_name was example.com,
+# usernames on this server would be in the format @user:example.com
+#
+# In most cases you should avoid using a matrix specific subdomain such as
+# matrix.example.com or synapse.example.com as the server_name for the same
+# reasons you wouldn't use user@email.example.com as your email address.
+# See https://matrix-org.github.io/synapse/latest/delegate.html
+# for information on how to host Synapse on a subdomain while preserving
+# a clean server_name.
+#
+# The server_name cannot be changed later so it is important to
+# configure this correctly before you start Synapse. It should be all
+# lowercase and may contain an explicit port.
+# Examples: matrix.org, localhost:8080
+#
+server_name: "matrix.home.adnab.me"
+
+# When running as a daemon, the file to store the pid in
+#
+pid_file: /data/homeserver.pid
+
+# The absolute URL to the web client which /_matrix/client will redirect
+# to if 'webclient' is configured under the 'listeners' configuration.
+#
+# This option can be also set to the filesystem path to the web client
+# which will be served at /_matrix/client/ if 'webclient' is configured
+# under the 'listeners' configuration, however this is a security risk:
+# https://github.com/matrix-org/synapse#security-note
+#
+#web_client_location: https://riot.example.com/
+
+# The public-facing base URL that clients use to access this Homeserver (not
+# including _matrix/...). This is the same URL a user might enter into the
+# 'Custom Homeserver URL' field on their client. If you use Synapse with a
+# reverse proxy, this should be the URL to reach Synapse via the proxy.
+# Otherwise, it should be the URL to reach Synapse's client HTTP listener (see
+# 'listeners' below).
+#
+# Defaults to 'https://<server_name>/'.
+#
+#public_baseurl: https://example.com/
+
+# Uncomment the following to tell other servers to send federation traffic on
+# port 443.
+#
+# By default, other servers will try to reach our server on port 8448, which can
+# be inconvenient in some environments.
+#
+# Provided 'https://<server_name>/' on port 443 is routed to Synapse, this
+# option configures Synapse to serve a file at
+# 'https://<server_name>/.well-known/matrix/server'. This will tell other
+# servers to send traffic to port 443 instead.
+#
+# See https://matrix-org.github.io/synapse/latest/delegate.html for more
+# information.
+#
+# Defaults to 'false'.
+#
+#serve_server_wellknown: true
+
+# Set the soft limit on the number of file descriptors synapse can use
+# Zero is used to indicate synapse should set the soft limit to the
+# hard limit.
+#
+#soft_file_limit: 0
+
+# Presence tracking allows users to see the state (e.g online/offline)
+# of other local and remote users.
+#
+presence:
+ # Uncomment to disable presence tracking on this homeserver. This option
+ # replaces the previous top-level 'use_presence' option.
+ #
+ #enabled: false
+
+# Whether to require authentication to retrieve profile data (avatars,
+# display names) of other users through the client API. Defaults to
+# 'false'. Note that profile data is also available via the federation
+# API, unless allow_profile_lookup_over_federation is set to false.
+#
+#require_auth_for_profile_requests: true
+
+# Uncomment to require a user to share a room with another user in order
+# to retrieve their profile information. Only checked on Client-Server
+# requests. Profile requests from other servers should be checked by the
+# requesting server. Defaults to 'false'.
+#
+#limit_profile_requests_to_users_who_share_rooms: true
+
+# Uncomment to prevent a user's profile data from being retrieved and
+# displayed in a room until they have joined it. By default, a user's
+# profile data is included in an invite event, regardless of the values
+# of the above two settings, and whether or not the users share a server.
+# Defaults to 'true'.
+#
+#include_profile_data_on_invite: false
+
+# If set to 'true', removes the need for authentication to access the server's
+# public rooms directory through the client API, meaning that anyone can
+# query the room directory. Defaults to 'false'.
+#
+#allow_public_rooms_without_auth: true
+
+# If set to 'true', allows any other homeserver to fetch the server's public
+# rooms directory via federation. Defaults to 'false'.
+#
+#allow_public_rooms_over_federation: true
+
+# The default room version for newly created rooms.
+#
+# Known room versions are listed here:
+# https://matrix.org/docs/spec/#complete-list-of-room-versions
+#
+# For example, for room version 1, default_room_version should be set
+# to "1".
+#
+#default_room_version: "6"
+
+# The GC threshold parameters to pass to `gc.set_threshold`, if defined
+#
+#gc_thresholds: [700, 10, 10]
+
+# The minimum time in seconds between each GC for a generation, regardless of
+# the GC thresholds. This ensures that we don't do GC too frequently.
+#
+# A value of `[1s, 10s, 30s]` indicates that a second must pass between consecutive
+# generation 0 GCs, etc.
+#
+# Defaults to `[1s, 10s, 30s]`.
+#
+#gc_min_interval: [0.5s, 30s, 1m]
+
+# Set the limit on the returned events in the timeline in the get
+# and sync operations. The default value is 100. -1 means no upper limit.
+#
+# Uncomment the following to increase the limit to 5000.
+#
+#filter_timeline_limit: 5000
+
+# Whether room invites to users on this server should be blocked
+# (except those sent by local server admins). The default is False.
+#
+#block_non_admin_invites: true
+
+# Room searching
+#
+# If disabled, new messages will not be indexed for searching and users
+# will receive errors when searching for messages. Defaults to enabled.
+#
+#enable_search: false
+
+# Prevent outgoing requests from being sent to the following blacklisted IP address
+# CIDR ranges. If this option is not specified then it defaults to private IP
+# address ranges (see the example below).
+#
+# The blacklist applies to the outbound requests for federation, identity servers,
+# push servers, and for checking key validity for third-party invite events.
+#
+# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
+# listed here, since they correspond to unroutable addresses.)
+#
+# This option replaces federation_ip_range_blacklist in Synapse v1.25.0.
+#
+# Note: The value is ignored when an HTTP proxy is in use
+#
+#ip_range_blacklist:
+# - '127.0.0.0/8'
+# - '10.0.0.0/8'
+# - '172.16.0.0/12'
+# - '192.168.0.0/16'
+# - '100.64.0.0/10'
+# - '192.0.0.0/24'
+# - '169.254.0.0/16'
+# - '192.88.99.0/24'
+# - '198.18.0.0/15'
+# - '192.0.2.0/24'
+# - '198.51.100.0/24'
+# - '203.0.113.0/24'
+# - '224.0.0.0/4'
+# - '::1/128'
+# - 'fe80::/10'
+# - 'fc00::/7'
+# - '2001:db8::/32'
+# - 'ff00::/8'
+# - 'fec0::/10'
+
+# List of IP address CIDR ranges that should be allowed for federation,
+# identity servers, push servers, and for checking key validity for
+# third-party invite events. This is useful for specifying exceptions to
+# wide-ranging blacklisted target IP ranges - e.g. for communication with
+# a push server only visible in your network.
+#
+# This whitelist overrides ip_range_blacklist and defaults to an empty
+# list.
+#
+#ip_range_whitelist:
+# - '192.168.1.1'
+
+# List of ports that Synapse should listen on, their purpose and their
+# configuration.
+#
+# Options for each listener include:
+#
+# port: the TCP port to bind to
+#
+# bind_addresses: a list of local addresses to listen on. The default is
+# 'all local interfaces'.
+#
+# type: the type of listener. Normally 'http', but other valid options are:
+# 'manhole' (see https://matrix-org.github.io/synapse/latest/manhole.html),
+# 'metrics' (see https://matrix-org.github.io/synapse/latest/metrics-howto.html),
+# 'replication' (see https://matrix-org.github.io/synapse/latest/workers.html).
+#
+# tls: set to true to enable TLS for this listener. Will use the TLS
+# key/cert specified in tls_private_key_path / tls_certificate_path.
+#
+# x_forwarded: Only valid for an 'http' listener. Set to true to use the
+# X-Forwarded-For header as the client IP. Useful when Synapse is
+# behind a reverse-proxy.
+#
+# resources: Only valid for an 'http' listener. A list of resources to host
+# on this port. Options for each resource are:
+#
+# names: a list of names of HTTP resources. See below for a list of
+# valid resource names.
+#
+# compress: set to true to enable HTTP compression for this resource.
+#
+# additional_resources: Only valid for an 'http' listener. A map of
+# additional endpoints which should be loaded via dynamic modules.
+#
+# Valid resource names are:
+#
+# client: the client-server API (/_matrix/client), and the synapse admin
+# API (/_synapse/admin). Also implies 'media' and 'static'.
+#
+# consent: user consent forms (/_matrix/consent).
+# See https://matrix-org.github.io/synapse/latest/consent_tracking.html.
+#
+# federation: the server-server API (/_matrix/federation). Also implies
+# 'media', 'keys', 'openid'
+#
+# keys: the key discovery API (/_matrix/keys).
+#
+# media: the media API (/_matrix/media).
+#
+# metrics: the metrics interface.
+# See https://matrix-org.github.io/synapse/latest/metrics-howto.html.
+#
+# openid: OpenID authentication.
+#
+# replication: the HTTP replication API (/_synapse/replication).
+# See https://matrix-org.github.io/synapse/latest/workers.html.
+#
+# static: static resources under synapse/static (/_matrix/static). (Mostly
+# useful for 'fallback authentication'.)
+#
+# webclient: A web client. Requires web_client_location to be set.
+#
+listeners:
+ # TLS-enabled listener: for when matrix traffic is sent directly to synapse.
+ #
+ # Disabled by default. To enable it, uncomment the following. (Note that you
+ # will also need to give Synapse a TLS key and certificate: see the TLS section
+ # below.)
+ #
+ #- port: 8448
+ # type: http
+ # tls: true
+ # resources:
+ # - names: [client, federation]
+
+ # Unsecure HTTP listener: for when matrix traffic passes through a reverse proxy
+ # that unwraps TLS.
+ #
+ # If you plan to use a reverse proxy, please see
+ # https://matrix-org.github.io/synapse/latest/reverse_proxy.html.
+ #
+ - port: 8008
+ tls: false
+ type: http
+ x_forwarded: true
+
+ resources:
+ - names: [client, federation]
+ compress: false
+
+ # example additional_resources:
+ #
+ #additional_resources:
+ # "/_matrix/my/custom/endpoint":
+ # module: my_module.CustomRequestHandler
+ # config: {}
+
+ # Turn on the twisted ssh manhole service on localhost on the given
+ # port.
+ #
+ #- port: 9000
+ # bind_addresses: ['::1', '127.0.0.1']
+ # type: manhole
+
+# Connection settings for the manhole
+#
+manhole_settings:
+ # The username for the manhole. This defaults to 'matrix'.
+ #
+ #username: manhole
+
+ # The password for the manhole. This defaults to 'rabbithole'.
+ #
+ #password: mypassword
+
+ # The private and public SSH key pair used to encrypt the manhole traffic.
+ # If these are left unset, then hardcoded and non-secret keys are used,
+ # which could allow traffic to be intercepted if sent over a public network.
+ #
+ #ssh_priv_key_path: /data/id_rsa
+ #ssh_pub_key_path: /data/id_rsa.pub
+
+# Forward extremities can build up in a room due to networking delays between
+# homeservers. Once this happens in a large room, calculation of the state of
+# that room can become quite expensive. To mitigate this, once the number of
+# forward extremities reaches a given threshold, Synapse will send an
+# org.matrix.dummy_event event, which will reduce the forward extremities
+# in the room.
+#
+# This setting defines the threshold (i.e. number of forward extremities in the
+# room) at which dummy events are sent. The default value is 10.
+#
+#dummy_events_threshold: 5
+
+
+## Homeserver blocking ##
+
+# How to reach the server admin, used in ResourceLimitError
+#
+#admin_contact: 'mailto:admin@server.com'
+
+# Global blocking
+#
+#hs_disabled: false
+#hs_disabled_message: 'Human readable reason for why the HS is blocked'
+
+# Monthly Active User Blocking
+#
+# Used in cases where the admin or server owner wants to limit to the
+# number of monthly active users.
+#
+# 'limit_usage_by_mau' disables/enables monthly active user blocking. When
+# enabled and a limit is reached the server returns a 'ResourceLimitError'
+# with error type Codes.RESOURCE_LIMIT_EXCEEDED
+#
+# 'max_mau_value' is the hard limit of monthly active users above which
+# the server will start blocking user actions.
+#
+# 'mau_trial_days' is a means to add a grace period for active users. It
+# means that users must be active for this number of days before they
+# can be considered active and guards against the case where lots of users
+# sign up in a short space of time never to return after their initial
+# session.
+#
+# 'mau_limit_alerting' is a means of limiting client side alerting
+# should the mau limit be reached. This is useful for small instances
+# where the admin has 5 mau seats (say) for 5 specific people and no
+# interest increasing the mau limit further. Defaults to True, which
+# means that alerting is enabled
+#
+#limit_usage_by_mau: false
+#max_mau_value: 50
+#mau_trial_days: 2
+#mau_limit_alerting: false
+
+# If enabled, the metrics for the number of monthly active users will
+# be populated, however no one will be limited. If limit_usage_by_mau
+# is true, this is implied to be true.
+#
+#mau_stats_only: false
+
+# Sometimes the server admin will want to ensure certain accounts are
+# never blocked by mau checking. These accounts are specified here.
+#
+#mau_limit_reserved_threepids:
+# - medium: 'email'
+# address: 'reserved_user@example.com'
+
+# Used by phonehome stats to group together related servers.
+#server_context: context
+
+# Resource-constrained homeserver settings
+#
+# When this is enabled, the room "complexity" will be checked before a user
+# joins a new remote room. If it is above the complexity limit, the server will
+# disallow joining, or will instantly leave.
+#
+# Room complexity is an arbitrary measure based on factors such as the number of
+# users in the room.
+#
+limit_remote_rooms:
+ # Uncomment to enable room complexity checking.
+ #
+ #enabled: true
+
+ # the limit above which rooms cannot be joined. The default is 1.0.
+ #
+ #complexity: 0.5
+
+ # override the error which is returned when the room is too complex.
+ #
+ #complexity_error: "This room is too complex."
+
+ # allow server admins to join complex rooms. Default is false.
+ #
+ #admins_can_join: true
+
+# Whether to require a user to be in the room to add an alias to it.
+# Defaults to 'true'.
+#
+#require_membership_for_aliases: false
+
+# Whether to allow per-room membership profiles through the send of membership
+# events with profile information that differ from the target's global profile.
+# Defaults to 'true'.
+#
+#allow_per_room_profiles: false
+
+# How long to keep redacted events in unredacted form in the database. After
+# this period redacted events get replaced with their redacted form in the DB.
+#
+# Defaults to `7d`. Set to `null` to disable.
+#
+#redaction_retention_period: 28d
+
+# How long to track users' last seen time and IPs in the database.
+#
+# Defaults to `28d`. Set to `null` to disable clearing out of old rows.
+#
+#user_ips_max_age: 14d
+
+# Inhibits the /requestToken endpoints from returning an error that might leak
+# information about whether an e-mail address is in use or not on this
+# homeserver.
+# Note that for some endpoints the error situation is the e-mail already being
+# used, and for others the error is entering the e-mail being unused.
+# If this option is enabled, instead of returning an error, these endpoints will
+# act as if no error happened and return a fake session ID ('sid') to clients.
+#
+#request_token_inhibit_3pid_errors: true
+
+# A list of domains that the domain portion of 'next_link' parameters
+# must match.
+#
+# This parameter is optionally provided by clients while requesting
+# validation of an email or phone number, and maps to a link that
+# users will be automatically redirected to after validation
+# succeeds. Clients can make use this parameter to aid the validation
+# process.
+#
+# The whitelist is applied whether the homeserver or an
+# identity server is handling validation.
+#
+# The default value is no whitelist functionality; all domains are
+# allowed. Setting this value to an empty list will instead disallow
+# all domains.
+#
+#next_link_domain_whitelist: ["matrix.org"]
+
+# Templates to use when generating email or HTML page contents.
+#
+templates:
+ # Directory in which Synapse will try to find template files to use to generate
+ # email or HTML page contents.
+ # If not set, or a file is not found within the template directory, a default
+ # template from within the Synapse package will be used.
+ #
+ # See https://matrix-org.github.io/synapse/latest/templates.html for more
+ # information about using custom templates.
+ #
+ #custom_template_directory: /path/to/custom/templates/
+
+
+# Message retention policy at the server level.
+#
+# Room admins and mods can define a retention period for their rooms using the
+# 'm.room.retention' state event, and server admins can cap this period by setting
+# the 'allowed_lifetime_min' and 'allowed_lifetime_max' config options.
+#
+# If this feature is enabled, Synapse will regularly look for and purge events
+# which are older than the room's maximum retention period. Synapse will also
+# filter events received over federation so that events that should have been
+# purged are ignored and not stored again.
+#
+retention:
+ # The message retention policies feature is disabled by default. Uncomment the
+ # following line to enable it.
+ #
+ #enabled: true
+
+ # Default retention policy. If set, Synapse will apply it to rooms that lack the
+ # 'm.room.retention' state event. Currently, the value of 'min_lifetime' doesn't
+ # matter much because Synapse doesn't take it into account yet.
+ #
+ #default_policy:
+ # min_lifetime: 1d
+ # max_lifetime: 1y
+
+ # Retention policy limits. If set, and the state of a room contains a
+ # 'm.room.retention' event in its state which contains a 'min_lifetime' or a
+ # 'max_lifetime' that's out of these bounds, Synapse will cap the room's policy
+ # to these limits when running purge jobs.
+ #
+ #allowed_lifetime_min: 1d
+ #allowed_lifetime_max: 1y
+
+ # Server admins can define the settings of the background jobs purging the
+ # events which lifetime has expired under the 'purge_jobs' section.
+ #
+ # If no configuration is provided, a single job will be set up to delete expired
+ # events in every room daily.
+ #
+ # Each job's configuration defines which range of message lifetimes the job
+ # takes care of. For example, if 'shortest_max_lifetime' is '2d' and
+ # 'longest_max_lifetime' is '3d', the job will handle purging expired events in
+ # rooms whose state defines a 'max_lifetime' that's both higher than 2 days, and
+ # lower than or equal to 3 days. Both the minimum and the maximum value of a
+ # range are optional, e.g. a job with no 'shortest_max_lifetime' and a
+ # 'longest_max_lifetime' of '3d' will handle every room with a retention policy
+ # which 'max_lifetime' is lower than or equal to three days.
+ #
+ # The rationale for this per-job configuration is that some rooms might have a
+ # retention policy with a low 'max_lifetime', where history needs to be purged
+ # of outdated messages on a more frequent basis than for the rest of the rooms
+ # (e.g. every 12h), but not want that purge to be performed by a job that's
+ # iterating over every room it knows, which could be heavy on the server.
+ #
+ # If any purge job is configured, it is strongly recommended to have at least
+ # a single job with neither 'shortest_max_lifetime' nor 'longest_max_lifetime'
+ # set, or one job without 'shortest_max_lifetime' and one job without
+ # 'longest_max_lifetime' set. Otherwise some rooms might be ignored, even if
+ # 'allowed_lifetime_min' and 'allowed_lifetime_max' are set, because capping a
+ # room's policy to these values is done after the policies are retrieved from
+ # Synapse's database (which is done using the range specified in a purge job's
+ # configuration).
+ #
+ #purge_jobs:
+ # - longest_max_lifetime: 3d
+ # interval: 12h
+ # - shortest_max_lifetime: 3d
+ # interval: 1d
+
+
+## TLS ##
+
+# PEM-encoded X509 certificate for TLS.
+# This certificate, as of Synapse 1.0, will need to be a valid and verifiable
+# certificate, signed by a recognised Certificate Authority.
+#
+# Be sure to use a `.pem` file that includes the full certificate chain including
+# any intermediate certificates (for instance, if using certbot, use
+# `fullchain.pem` as your certificate, not `cert.pem`).
+#
+#tls_certificate_path: "/data/matrix.home.adnab.me.tls.crt"
+
+# PEM-encoded private key for TLS
+#
+#tls_private_key_path: "/data/matrix.home.adnab.me.tls.key"
+
+# Whether to verify TLS server certificates for outbound federation requests.
+#
+# Defaults to `true`. To disable certificate verification, uncomment the
+# following line.
+#
+#federation_verify_certificates: false
+
+# The minimum TLS version that will be used for outbound federation requests.
+#
+# Defaults to `1`. Configurable to `1`, `1.1`, `1.2`, or `1.3`. Note
+# that setting this value higher than `1.2` will prevent federation to most
+# of the public Matrix network: only configure it to `1.3` if you have an
+# entirely private federation setup and you can ensure TLS 1.3 support.
+#
+#federation_client_minimum_tls_version: 1.2
+
+# Skip federation certificate verification on the following whitelist
+# of domains.
+#
+# This setting should only be used in very specific cases, such as
+# federation over Tor hidden services and similar. For private networks
+# of homeservers, you likely want to use a private CA instead.
+#
+# Only effective if federation_verify_certicates is `true`.
+#
+#federation_certificate_verification_whitelist:
+# - lon.example.com
+# - "*.domain.com"
+# - "*.onion"
+
+# List of custom certificate authorities for federation traffic.
+#
+# This setting should only normally be used within a private network of
+# homeservers.
+#
+# Note that this list will replace those that are provided by your
+# operating environment. Certificates must be in PEM format.
+#
+#federation_custom_ca_list:
+# - myCA1.pem
+# - myCA2.pem
+# - myCA3.pem
+
+
+## Federation ##
+
+# Restrict federation to the following whitelist of domains.
+# N.B. we recommend also firewalling your federation listener to limit
+# inbound federation traffic as early as possible, rather than relying
+# purely on this application-layer restriction. If not specified, the
+# default is to whitelist everything.
+#
+#federation_domain_whitelist:
+# - lon.example.com
+# - nyc.example.com
+# - syd.example.com
+
+# Report prometheus metrics on the age of PDUs being sent to and received from
+# the following domains. This can be used to give an idea of "delay" on inbound
+# and outbound federation, though be aware that any delay can be due to problems
+# at either end or with the intermediate network.
+#
+# By default, no domains are monitored in this way.
+#
+#federation_metrics_domains:
+# - matrix.org
+# - example.com
+
+# Uncomment to disable profile lookup over federation. By default, the
+# Federation API allows other homeservers to obtain profile data of any user
+# on this homeserver. Defaults to 'true'.
+#
+#allow_profile_lookup_over_federation: false
+
+# Uncomment to disable device display name lookup over federation. By default, the
+# Federation API allows other homeservers to obtain device display names of any user
+# on this homeserver. Defaults to 'true'.
+#
+#allow_device_name_lookup_over_federation: false
+
+
+## Caching ##
+
+# Caching can be configured through the following options.
+#
+# A cache 'factor' is a multiplier that can be applied to each of
+# Synapse's caches in order to increase or decrease the maximum
+# number of entries that can be stored.
+
+# The number of events to cache in memory. Not affected by
+# caches.global_factor.
+#
+#event_cache_size: 10K
+
+caches:
+ # Controls the global cache factor, which is the default cache factor
+ # for all caches if a specific factor for that cache is not otherwise
+ # set.
+ #
+ # This can also be set by the "SYNAPSE_CACHE_FACTOR" environment
+ # variable. Setting by environment variable takes priority over
+ # setting through the config file.
+ #
+ # Defaults to 0.5, which will half the size of all caches.
+ #
+ #global_factor: 1.0
+
+ # A dictionary of cache name to cache factor for that individual
+ # cache. Overrides the global cache factor for a given cache.
+ #
+ # These can also be set through environment variables comprised
+ # of "SYNAPSE_CACHE_FACTOR_" + the name of the cache in capital
+ # letters and underscores. Setting by environment variable
+ # takes priority over setting through the config file.
+ # Ex. SYNAPSE_CACHE_FACTOR_GET_USERS_WHO_SHARE_ROOM_WITH_USER=2.0
+ #
+ # Some caches have '*' and other characters that are not
+ # alphanumeric or underscores. These caches can be named with or
+ # without the special characters stripped. For example, to specify
+ # the cache factor for `*stateGroupCache*` via an environment
+ # variable would be `SYNAPSE_CACHE_FACTOR_STATEGROUPCACHE=2.0`.
+ #
+ per_cache_factors:
+ #get_users_who_share_room_with_user: 2.0
+
+ # Controls how long an entry can be in a cache without having been
+ # accessed before being evicted. Defaults to None, which means
+ # entries are never evicted based on time.
+ #
+ #expiry_time: 30m
+
+ # Controls how long the results of a /sync request are cached for after
+ # a successful response is returned. A higher duration can help clients with
+ # intermittent connections, at the cost of higher memory usage.
+ #
+ # By default, this is zero, which means that sync responses are not cached
+ # at all.
+ #
+ #sync_response_cache_duration: 2m
+
+
+## Database ##
+
+# The 'database' setting defines the database that synapse uses to store all of
+# its data.
+#
+# 'name' gives the database engine to use: either 'sqlite3' (for SQLite) or
+# 'psycopg2' (for PostgreSQL).
+#
+# 'txn_limit' gives the maximum number of transactions to run per connection
+# before reconnecting. Defaults to 0, which means no limit.
+#
+# 'args' gives options which are passed through to the database engine,
+# except for options starting 'cp_', which are used to configure the Twisted
+# connection pool. For a reference to valid arguments, see:
+# * for sqlite: https://docs.python.org/3/library/sqlite3.html#sqlite3.connect
+# * for postgres: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
+# * for the connection pool: https://twistedmatrix.com/documents/current/api/twisted.enterprise.adbapi.ConnectionPool.html#__init__
+#
+#
+# Example SQLite configuration:
+#
+#database:
+# name: sqlite3
+# args:
+# database: /path/to/homeserver.db
+#
+#
+# Example Postgres configuration:
+#
+#database:
+# name: psycopg2
+# txn_limit: 10000
+# args:
+# user: synapse_user
+# password: secretpassword
+# database: synapse
+# host: localhost
+# port: 5432
+# cp_min: 5
+# cp_max: 10
+#
+# For more information on using Synapse with Postgres,
+# see https://matrix-org.github.io/synapse/latest/postgres.html.
+#
+
+database:
+ name: sqlite3
+ args:
+ database: "file:/ephemeral/homeserver.db?mode=rw"
+ uri: true
+
+# yugabyte
+#database:
+# name: psycopg2
+# txn_limit: 10000
+# args:
+# user: synapse
+# password: Caa9m9cWjFO5AR/9wtjCLA
+# database: synapse
+# host: 10.42.0.21
+# port: 5433
+# cp_min: 5
+# cp_max: 10
+
+
+## Logging ##
+
+# A yaml python logging config file as described by
+# https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
+#
+log_config: "/etc/matrix-synapse/synapse.log.config.yaml"
+
+
+## Ratelimiting ##
+
+# Ratelimiting settings for client actions (registration, login, messaging).
+#
+# Each ratelimiting configuration is made of two parameters:
+# - per_second: number of requests a client can send per second.
+# - burst_count: number of requests a client can send before being throttled.
+#
+# Synapse currently uses the following configurations:
+# - one for messages that ratelimits sending based on the account the client
+# is using
+# - one for registration that ratelimits registration requests based on the
+# client's IP address.
+# - one for checking the validity of registration tokens that ratelimits
+# requests based on the client's IP address.
+# - one for login that ratelimits login requests based on the client's IP
+# address.
+# - one for login that ratelimits login requests based on the account the
+# client is attempting to log into.
+# - one for login that ratelimits login requests based on the account the
+# client is attempting to log into, based on the amount of failed login
+# attempts for this account.
+# - one for ratelimiting redactions by room admins. If this is not explicitly
+# set then it uses the same ratelimiting as per rc_message. This is useful
+# to allow room admins to deal with abuse quickly.
+# - two for ratelimiting number of rooms a user can join, "local" for when
+# users are joining rooms the server is already in (this is cheap) vs
+# "remote" for when users are trying to join rooms not on the server (which
+# can be more expensive)
+# - one for ratelimiting how often a user or IP can attempt to validate a 3PID.
+# - two for ratelimiting how often invites can be sent in a room or to a
+# specific user.
+#
+# The defaults are as shown below.
+#
+#rc_message:
+# per_second: 0.2
+# burst_count: 10
+#
+#rc_registration:
+# per_second: 0.17
+# burst_count: 3
+#
+#rc_registration_token_validity:
+# per_second: 0.1
+# burst_count: 5
+#
+#rc_login:
+# address:
+# per_second: 0.17
+# burst_count: 3
+# account:
+# per_second: 0.17
+# burst_count: 3
+# failed_attempts:
+# per_second: 0.17
+# burst_count: 3
+#
+#rc_admin_redaction:
+# per_second: 1
+# burst_count: 50
+#
+#rc_joins:
+# local:
+# per_second: 0.1
+# burst_count: 10
+# remote:
+# per_second: 0.01
+# burst_count: 10
+#
+#rc_3pid_validation:
+# per_second: 0.003
+# burst_count: 5
+#
+#rc_invites:
+# per_room:
+# per_second: 0.3
+# burst_count: 10
+# per_user:
+# per_second: 0.003
+# burst_count: 5
+
+# Ratelimiting settings for incoming federation
+#
+# The rc_federation configuration is made up of the following settings:
+# - window_size: window size in milliseconds
+# - sleep_limit: number of federation requests from a single server in
+# a window before the server will delay processing the request.
+# - sleep_delay: duration in milliseconds to delay processing events
+# from remote servers by if they go over the sleep limit.
+# - reject_limit: maximum number of concurrent federation requests
+# allowed from a single server
+# - concurrent: number of federation requests to concurrently process
+# from a single server
+#
+# The defaults are as shown below.
+#
+#rc_federation:
+# window_size: 1000
+# sleep_limit: 10
+# sleep_delay: 500
+# reject_limit: 50
+# concurrent: 3
+
+# Target outgoing federation transaction frequency for sending read-receipts,
+# per-room.
+#
+# If we end up trying to send out more read-receipts, they will get buffered up
+# into fewer transactions.
+#
+#federation_rr_transactions_per_room_per_second: 50
+
+
+
+## Media Store ##
+
+# Enable the media store service in the Synapse master. Uncomment the
+# following if you are using a separate media store worker.
+#
+#enable_media_repo: false
+
+# Directory where uploaded images and attachments are stored.
+#
+media_store_path: "/ephemeral/media_store"
+
+# Media storage providers allow media to be stored in different
+# locations.
+#
+#media_storage_providers:
+# - module: file_system
+# # Whether to store newly uploaded local files
+# store_local: false
+# # Whether to store newly downloaded remote files
+# store_remote: false
+# # Whether to wait for successful storage for local uploads
+# store_synchronous: false
+# config:
+# directory: /mnt/some/other/directory
+
+media_storage_providers:
+- module: s3_storage_provider.S3StorageProviderBackend
+ store_local: True
+ store_remote: True
+ store_synchronous: True
+ config:
+ bucket: synapse-data
+ # All of the below options are optional, for use with non-AWS S3-like
+ # services, or to specify access tokens here instead of some external method.
+ region_name: garage-staging
+ endpoint_url: http://{{ env "attr.unique.network.ip-address" }}:3990
+ access_key_id: {{ key "secrets/synapse/s3_access_key" | trimSpace }}
+ secret_access_key: {{ key "secrets/synapse/s3_secret_key" | trimSpace }}
+
+# The largest allowed upload size in bytes
+#
+# If you are using a reverse proxy you may also need to set this value in
+# your reverse proxy's config. Notably Nginx has a small max body size by default.
+# See https://matrix-org.github.io/synapse/latest/reverse_proxy.html.
+#
+max_upload_size: 50M
+
+# Maximum number of pixels that will be thumbnailed
+#
+max_image_pixels: 32M
+
+# Whether to generate new thumbnails on the fly to precisely match
+# the resolution requested by the client. If true then whenever
+# a new resolution is requested by the client the server will
+# generate a new thumbnail. If false the server will pick a thumbnail
+# from a precalculated list.
+#
+dynamic_thumbnails: false
+
+# List of thumbnails to precalculate when an image is uploaded.
+#
+thumbnail_sizes:
+ - width: 32
+ height: 32
+ method: crop
+ - width: 96
+ height: 96
+ method: crop
+ - width: 320
+ height: 240
+ method: scale
+ - width: 640
+ height: 480
+ method: scale
+ - width: 800
+ height: 600
+ method: scale
+
+# Is the preview URL API enabled?
+#
+# 'false' by default: uncomment the following to enable it (and specify a
+# url_preview_ip_range_blacklist blacklist).
+#
+#url_preview_enabled: true
+
+# List of IP address CIDR ranges that the URL preview spider is denied
+# from accessing. There are no defaults: you must explicitly
+# specify a list for URL previewing to work. You should specify any
+# internal services in your network that you do not want synapse to try
+# to connect to, otherwise anyone in any Matrix room could cause your
+# synapse to issue arbitrary GET requests to your internal services,
+# causing serious security issues.
+#
+# (0.0.0.0 and :: are always blacklisted, whether or not they are explicitly
+# listed here, since they correspond to unroutable addresses.)
+#
+# This must be specified if url_preview_enabled is set. It is recommended that
+# you uncomment the following list as a starting point.
+#
+# Note: The value is ignored when an HTTP proxy is in use
+#
+#url_preview_ip_range_blacklist:
+# - '127.0.0.0/8'
+# - '10.0.0.0/8'
+# - '172.16.0.0/12'
+# - '192.168.0.0/16'
+# - '100.64.0.0/10'
+# - '192.0.0.0/24'
+# - '169.254.0.0/16'
+# - '192.88.99.0/24'
+# - '198.18.0.0/15'
+# - '192.0.2.0/24'
+# - '198.51.100.0/24'
+# - '203.0.113.0/24'
+# - '224.0.0.0/4'
+# - '::1/128'
+# - 'fe80::/10'
+# - 'fc00::/7'
+# - '2001:db8::/32'
+# - 'ff00::/8'
+# - 'fec0::/10'
+
+# List of IP address CIDR ranges that the URL preview spider is allowed
+# to access even if they are specified in url_preview_ip_range_blacklist.
+# This is useful for specifying exceptions to wide-ranging blacklisted
+# target IP ranges - e.g. for enabling URL previews for a specific private
+# website only visible in your network.
+#
+#url_preview_ip_range_whitelist:
+# - '192.168.1.1'
+
+# Optional list of URL matches that the URL preview spider is
+# denied from accessing. You should use url_preview_ip_range_blacklist
+# in preference to this, otherwise someone could define a public DNS
+# entry that points to a private IP address and circumvent the blacklist.
+# This is more useful if you know there is an entire shape of URL that
+# you know that will never want synapse to try to spider.
+#
+# Each list entry is a dictionary of url component attributes as returned
+# by urlparse.urlsplit as applied to the absolute form of the URL. See
+# https://docs.python.org/2/library/urlparse.html#urlparse.urlsplit
+# The values of the dictionary are treated as an filename match pattern
+# applied to that component of URLs, unless they start with a ^ in which
+# case they are treated as a regular expression match. If all the
+# specified component matches for a given list item succeed, the URL is
+# blacklisted.
+#
+#url_preview_url_blacklist:
+# # blacklist any URL with a username in its URI
+# - username: '*'
+#
+# # blacklist all *.google.com URLs
+# - netloc: 'google.com'
+# - netloc: '*.google.com'
+#
+# # blacklist all plain HTTP URLs
+# - scheme: 'http'
+#
+# # blacklist http(s)://www.acme.com/foo
+# - netloc: 'www.acme.com'
+# path: '/foo'
+#
+# # blacklist any URL with a literal IPv4 address
+# - netloc: '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'
+
+# The largest allowed URL preview spidering size in bytes
+#
+#max_spider_size: 10M
+
+# A list of values for the Accept-Language HTTP header used when
+# downloading webpages during URL preview generation. This allows
+# Synapse to specify the preferred languages that URL previews should
+# be in when communicating with remote servers.
+#
+# Each value is a IETF language tag; a 2-3 letter identifier for a
+# language, optionally followed by subtags separated by '-', specifying
+# a country or region variant.
+#
+# Multiple values can be provided, and a weight can be added to each by
+# using quality value syntax (;q=). '*' translates to any language.
+#
+# Defaults to "en".
+#
+# Example:
+#
+# url_preview_accept_language:
+# - en-UK
+# - en-US;q=0.9
+# - fr;q=0.8
+# - *;q=0.7
+#
+url_preview_accept_language:
+# - en
+
+
+# oEmbed allows for easier embedding content from a website. It can be
+# used for generating URLs previews of services which support it.
+#
+oembed:
+ # A default list of oEmbed providers is included with Synapse.
+ #
+ # Uncomment the following to disable using these default oEmbed URLs.
+ # Defaults to 'false'.
+ #
+ #disable_default_providers: true
+
+ # Additional files with oEmbed configuration (each should be in the
+ # form of providers.json).
+ #
+ # By default, this list is empty (so only the default providers.json
+ # is used).
+ #
+ #additional_providers:
+ # - oembed/my_providers.json
+
+
+## Captcha ##
+# See docs/CAPTCHA_SETUP.md for full details of configuring this.
+
+# This homeserver's ReCAPTCHA public key. Must be specified if
+# enable_registration_captcha is enabled.
+#
+#recaptcha_public_key: "YOUR_PUBLIC_KEY"
+
+# This homeserver's ReCAPTCHA private key. Must be specified if
+# enable_registration_captcha is enabled.
+#
+#recaptcha_private_key: "YOUR_PRIVATE_KEY"
+
+# Uncomment to enable ReCaptcha checks when registering, preventing signup
+# unless a captcha is answered. Requires a valid ReCaptcha
+# public/private key. Defaults to 'false'.
+#
+#enable_registration_captcha: true
+
+# The API endpoint to use for verifying m.login.recaptcha responses.
+# Defaults to "https://www.recaptcha.net/recaptcha/api/siteverify".
+#
+#recaptcha_siteverify_api: "https://my.recaptcha.site"
+
+
+## TURN ##
+
+# The public URIs of the TURN server to give to clients
+#
+#turn_uris: []
+
+# The shared secret used to compute passwords for the TURN server
+#
+#turn_shared_secret: "YOUR_SHARED_SECRET"
+
+# The Username and password if the TURN server needs them and
+# does not use a token
+#
+#turn_username: "TURNSERVER_USERNAME"
+#turn_password: "TURNSERVER_PASSWORD"
+
+# How long generated TURN credentials last
+#
+#turn_user_lifetime: 1h
+
+# Whether guests should be allowed to use the TURN server.
+# This defaults to True, otherwise VoIP will be unreliable for guests.
+# However, it does introduce a slight security risk as it allows users to
+# connect to arbitrary endpoints without having first signed up for a
+# valid account (e.g. by passing a CAPTCHA).
+#
+#turn_allow_guests: true
+
+
+## Registration ##
+#
+# Registration can be rate-limited using the parameters in the "Ratelimiting"
+# section of this file.
+
+# Enable registration for new users.
+#
+#enable_registration: false
+
+# Time that a user's session remains valid for, after they log in.
+#
+# Note that this is not currently compatible with guest logins.
+#
+# Note also that this is calculated at login time: changes are not applied
+# retrospectively to users who have already logged in.
+#
+# By default, this is infinite.
+#
+#session_lifetime: 24h
+
+# The user must provide all of the below types of 3PID when registering.
+#
+#registrations_require_3pid:
+# - email
+# - msisdn
+
+# Explicitly disable asking for MSISDNs from the registration
+# flow (overrides registrations_require_3pid if MSISDNs are set as required)
+#
+#disable_msisdn_registration: true
+
+# Mandate that users are only allowed to associate certain formats of
+# 3PIDs with accounts on this server.
+#
+#allowed_local_3pids:
+# - medium: email
+# pattern: '^[^@]+@matrix\.org$'
+# - medium: email
+# pattern: '^[^@]+@vector\.im$'
+# - medium: msisdn
+# pattern: '\+44'
+
+# Enable 3PIDs lookup requests to identity servers from this server.
+#
+#enable_3pid_lookup: true
+
+# Require users to submit a token during registration.
+# Tokens can be managed using the admin API:
+# https://matrix-org.github.io/synapse/latest/usage/administration/admin_api/registration_tokens.html
+# Note that `enable_registration` must be set to `true`.
+# Disabling this option will not delete any tokens previously generated.
+# Defaults to false. Uncomment the following to require tokens:
+#
+#registration_requires_token: true
+
+# If set, allows registration of standard or admin accounts by anyone who
+# has the shared secret, even if registration is otherwise disabled.
+#
+registration_shared_secret: "{{ key "secrets/synapse/registration_shared_secret" | trimSpace }}"
+
+# Set the number of bcrypt rounds used to generate password hash.
+# Larger numbers increase the work factor needed to generate the hash.
+# The default number is 12 (which equates to 2^12 rounds).
+# N.B. that increasing this will exponentially increase the time required
+# to register or login - e.g. 24 => 2^24 rounds which will take >20 mins.
+#
+#bcrypt_rounds: 12
+
+# Allows users to register as guests without a password/email/etc, and
+# participate in rooms hosted on this server which have been made
+# accessible to anonymous users.
+#
+#allow_guest_access: false
+
+# The identity server which we suggest that clients should use when users log
+# in on this server.
+#
+# (By default, no suggestion is made, so it is left up to the client.
+# This setting is ignored unless public_baseurl is also explicitly set.)
+#
+#default_identity_server: https://matrix.org
+
+# Handle threepid (email/phone etc) registration and password resets through a set of
+# *trusted* identity servers. Note that this allows the configured identity server to
+# reset passwords for accounts!
+#
+# Be aware that if `email` is not set, and SMTP options have not been
+# configured in the email config block, registration and user password resets via
+# email will be globally disabled.
+#
+# Additionally, if `msisdn` is not set, registration and password resets via msisdn
+# will be disabled regardless, and users will not be able to associate an msisdn
+# identifier to their account. This is due to Synapse currently not supporting
+# any method of sending SMS messages on its own.
+#
+# To enable using an identity server for operations regarding a particular third-party
+# identifier type, set the value to the URL of that identity server as shown in the
+# examples below.
+#
+# Servers handling the these requests must answer the `/requestToken` endpoints defined
+# by the Matrix Identity Service API specification:
+# https://matrix.org/docs/spec/identity_service/latest
+#
+account_threepid_delegates:
+ #email: https://example.com # Delegate email sending to example.com
+ #msisdn: http://localhost:8090 # Delegate SMS sending to this local process
+
+# Whether users are allowed to change their displayname after it has
+# been initially set. Useful when provisioning users based on the
+# contents of a third-party directory.
+#
+# Does not apply to server administrators. Defaults to 'true'
+#
+#enable_set_displayname: false
+
+# Whether users are allowed to change their avatar after it has been
+# initially set. Useful when provisioning users based on the contents
+# of a third-party directory.
+#
+# Does not apply to server administrators. Defaults to 'true'
+#
+#enable_set_avatar_url: false
+
+# Whether users can change the 3PIDs associated with their accounts
+# (email address and msisdn).
+#
+# Defaults to 'true'
+#
+#enable_3pid_changes: false
+
+# Users who register on this homeserver will automatically be joined
+# to these rooms.
+#
+# By default, any room aliases included in this list will be created
+# as a publicly joinable room when the first user registers for the
+# homeserver. This behaviour can be customised with the settings below.
+# If the room already exists, make certain it is a publicly joinable
+# room. The join rule of the room must be set to 'public'.
+#
+#auto_join_rooms:
+# - "#example:example.com"
+
+# Where auto_join_rooms are specified, setting this flag ensures that the
+# the rooms exist by creating them when the first user on the
+# homeserver registers.
+#
+# By default the auto-created rooms are publicly joinable from any federated
+# server. Use the autocreate_auto_join_rooms_federated and
+# autocreate_auto_join_room_preset settings below to customise this behaviour.
+#
+# Setting to false means that if the rooms are not manually created,
+# users cannot be auto-joined since they do not exist.
+#
+# Defaults to true. Uncomment the following line to disable automatically
+# creating auto-join rooms.
+#
+#autocreate_auto_join_rooms: false
+
+# Whether the auto_join_rooms that are auto-created are available via
+# federation. Only has an effect if autocreate_auto_join_rooms is true.
+#
+# Note that whether a room is federated cannot be modified after
+# creation.
+#
+# Defaults to true: the room will be joinable from other servers.
+# Uncomment the following to prevent users from other homeservers from
+# joining these rooms.
+#
+#autocreate_auto_join_rooms_federated: false
+
+# The room preset to use when auto-creating one of auto_join_rooms. Only has an
+# effect if autocreate_auto_join_rooms is true.
+#
+# This can be one of "public_chat", "private_chat", or "trusted_private_chat".
+# If a value of "private_chat" or "trusted_private_chat" is used then
+# auto_join_mxid_localpart must also be configured.
+#
+# Defaults to "public_chat", meaning that the room is joinable by anyone, including
+# federated servers if autocreate_auto_join_rooms_federated is true (the default).
+# Uncomment the following to require an invitation to join these rooms.
+#
+#autocreate_auto_join_room_preset: private_chat
+
+# The local part of the user id which is used to create auto_join_rooms if
+# autocreate_auto_join_rooms is true. If this is not provided then the
+# initial user account that registers will be used to create the rooms.
+#
+# The user id is also used to invite new users to any auto-join rooms which
+# are set to invite-only.
+#
+# It *must* be configured if autocreate_auto_join_room_preset is set to
+# "private_chat" or "trusted_private_chat".
+#
+# Note that this must be specified in order for new users to be correctly
+# invited to any auto-join rooms which have been set to invite-only (either
+# at the time of creation or subsequently).
+#
+# Note that, if the room already exists, this user must be joined and
+# have the appropriate permissions to invite new members.
+#
+#auto_join_mxid_localpart: system
+
+# When auto_join_rooms is specified, setting this flag to false prevents
+# guest accounts from being automatically joined to the rooms.
+#
+# Defaults to true.
+#
+#auto_join_rooms_for_guests: false
+
+
+## Metrics ###
+
+# Enable collection and rendering of performance metrics
+#
+#enable_metrics: false
+
+# Enable sentry integration
+# NOTE: While attempts are made to ensure that the logs don't contain
+# any sensitive information, this cannot be guaranteed. By enabling
+# this option the sentry server may therefore receive sensitive
+# information, and it in turn may then diseminate sensitive information
+# through insecure notification channels if so configured.
+#
+#sentry:
+# dsn: "..."
+
+# Flags to enable Prometheus metrics which are not suitable to be
+# enabled by default, either for performance reasons or limited use.
+#
+metrics_flags:
+ # Publish synapse_federation_known_servers, a gauge of the number of
+ # servers this homeserver knows about, including itself. May cause
+ # performance problems on large homeservers.
+ #
+ #known_servers: true
+
+# Whether or not to report anonymized homeserver usage statistics.
+#
+report_stats: false
+
+# The endpoint to report the anonymized homeserver usage statistics to.
+# Defaults to https://matrix.org/report-usage-stats/push
+#
+#report_stats_endpoint: https://example.com/report-usage-stats/push
+
+
+## API Configuration ##
+
+# Controls for the state that is shared with users who receive an invite
+# to a room
+#
+room_prejoin_state:
+ # By default, the following state event types are shared with users who
+ # receive invites to the room:
+ #
+ # - m.room.join_rules
+ # - m.room.canonical_alias
+ # - m.room.avatar
+ # - m.room.encryption
+ # - m.room.name
+ # - m.room.create
+ #
+ # Uncomment the following to disable these defaults (so that only the event
+ # types listed in 'additional_event_types' are shared). Defaults to 'false'.
+ #
+ #disable_default_event_types: true
+
+ # Additional state event types to share with users when they are invited
+ # to a room.
+ #
+ # By default, this list is empty (so only the default event types are shared).
+ #
+ #additional_event_types:
+ # - org.example.custom.event.type
+
+
+# A list of application service config files to use
+#
+#app_service_config_files:
+# - app_service_1.yaml
+# - app_service_2.yaml
+
+# Uncomment to enable tracking of application service IP addresses. Implicitly
+# enables MAU tracking for application service users.
+#
+#track_appservice_user_ips: true
+
+
+# a secret which is used to sign access tokens. If none is specified,
+# the registration_shared_secret is used, if one is given; otherwise,
+# a secret key is derived from the signing key.
+#
+macaroon_secret_key: "{{ key "secrets/synapse/macaroon_secret_key" | trimSpace }}"
+
+# a secret which is used to calculate HMACs for form values, to stop
+# falsification of values. Must be specified for the User Consent
+# forms to work.
+#
+form_secret: "{{ key "secrets/synapse/form_secret" | trimSpace }}"
+
+## Signing Keys ##
+
+# Path to the signing key to sign messages with
+#
+signing_key_path: "/etc/matrix-synapse/signing_key"
+
+# The keys that the server used to sign messages with but won't use
+# to sign new messages.
+#
+old_signing_keys:
+ # For each key, `key` should be the base64-encoded public key, and
+ # `expired_ts`should be the time (in milliseconds since the unix epoch) that
+ # it was last used.
+ #
+ # It is possible to build an entry from an old signing.key file using the
+ # `export_signing_key` script which is provided with synapse.
+ #
+ # For example:
+ #
+ #"ed25519:id": { key: "base64string", expired_ts: 123456789123 }
+
+# How long key response published by this server is valid for.
+# Used to set the valid_until_ts in /key/v2 APIs.
+# Determines how quickly servers will query to check which keys
+# are still valid.
+#
+#key_refresh_interval: 1d
+
+# The trusted servers to download signing keys from.
+#
+# When we need to fetch a signing key, each server is tried in parallel.
+#
+# Normally, the connection to the key server is validated via TLS certificates.
+# Additional security can be provided by configuring a `verify key`, which
+# will make synapse check that the response is signed by that key.
+#
+# This setting supercedes an older setting named `perspectives`. The old format
+# is still supported for backwards-compatibility, but it is deprecated.
+#
+# 'trusted_key_servers' defaults to matrix.org, but using it will generate a
+# warning on start-up. To suppress this warning, set
+# 'suppress_key_server_warning' to true.
+#
+# Options for each entry in the list include:
+#
+# server_name: the name of the server. required.
+#
+# verify_keys: an optional map from key id to base64-encoded public key.
+# If specified, we will check that the response is signed by at least
+# one of the given keys.
+#
+# accept_keys_insecurely: a boolean. Normally, if `verify_keys` is unset,
+# and federation_verify_certificates is not `true`, synapse will refuse
+# to start, because this would allow anyone who can spoof DNS responses
+# to masquerade as the trusted key server. If you know what you are doing
+# and are sure that your network environment provides a secure connection
+# to the key server, you can set this to `true` to override this
+# behaviour.
+#
+# An example configuration might look like:
+#
+#trusted_key_servers:
+# - server_name: "my_trusted_server.example.com"
+# verify_keys:
+# "ed25519:auto": "abcdefghijklmnopqrstuvwxyzabcdefghijklmopqr"
+# - server_name: "my_other_trusted_server.example.com"
+#
+trusted_key_servers:
+ - server_name: "matrix.org"
+
+# Uncomment the following to disable the warning that is emitted when the
+# trusted_key_servers include 'matrix.org'. See above.
+#
+#suppress_key_server_warning: true
+
+# The signing keys to use when acting as a trusted key server. If not specified
+# defaults to the server signing key.
+#
+# Can contain multiple keys, one per line.
+#
+#key_server_signing_keys_path: "key_server_signing_keys.key"
+
+
+## Single sign-on integration ##
+
+# The following settings can be used to make Synapse use a single sign-on
+# provider for authentication, instead of its internal password database.
+#
+# You will probably also want to set the following options to `false` to
+# disable the regular login/registration flows:
+# * enable_registration
+# * password_config.enabled
+#
+# You will also want to investigate the settings under the "sso" configuration
+# section below.
+
+# Enable SAML2 for registration and login. Uses pysaml2.
+#
+# At least one of `sp_config` or `config_path` must be set in this section to
+# enable SAML login.
+#
+# Once SAML support is enabled, a metadata file will be exposed at
+# https://<server>:<port>/_synapse/client/saml2/metadata.xml, which you may be able to
+# use to configure your SAML IdP with. Alternatively, you can manually configure
+# the IdP to use an ACS location of
+# https://<server>:<port>/_synapse/client/saml2/authn_response.
+#
+saml2_config:
+ # `sp_config` is the configuration for the pysaml2 Service Provider.
+ # See pysaml2 docs for format of config.
+ #
+ # Default values will be used for the 'entityid' and 'service' settings,
+ # so it is not normally necessary to specify them unless you need to
+ # override them.
+ #
+ sp_config:
+ # Point this to the IdP's metadata. You must provide either a local
+ # file via the `local` attribute or (preferably) a URL via the
+ # `remote` attribute.
+ #
+ #metadata:
+ # local: ["saml2/idp.xml"]
+ # remote:
+ # - url: https://our_idp/metadata.xml
+
+ # Allowed clock difference in seconds between the homeserver and IdP.
+ #
+ # Uncomment the below to increase the accepted time difference from 0 to 3 seconds.
+ #
+ #accepted_time_diff: 3
+
+ # By default, the user has to go to our login page first. If you'd like
+ # to allow IdP-initiated login, set 'allow_unsolicited: true' in a
+ # 'service.sp' section:
+ #
+ #service:
+ # sp:
+ # allow_unsolicited: true
+
+ # The examples below are just used to generate our metadata xml, and you
+ # may well not need them, depending on your setup. Alternatively you
+ # may need a whole lot more detail - see the pysaml2 docs!
+
+ #description: ["My awesome SP", "en"]
+ #name: ["Test SP", "en"]
+
+ #ui_info:
+ # display_name:
+ # - lang: en
+ # text: "Display Name is the descriptive name of your service."
+ # description:
+ # - lang: en
+ # text: "Description should be a short paragraph explaining the purpose of the service."
+ # information_url:
+ # - lang: en
+ # text: "https://example.com/terms-of-service"
+ # privacy_statement_url:
+ # - lang: en
+ # text: "https://example.com/privacy-policy"
+ # keywords:
+ # - lang: en
+ # text: ["Matrix", "Element"]
+ # logo:
+ # - lang: en
+ # text: "https://example.com/logo.svg"
+ # width: "200"
+ # height: "80"
+
+ #organization:
+ # name: Example com
+ # display_name:
+ # - ["Example co", "en"]
+ # url: "http://example.com"
+
+ #contact_person:
+ # - given_name: Bob
+ # sur_name: "the Sysadmin"
+ # email_address": ["admin@example.com"]
+ # contact_type": technical
+
+ # Instead of putting the config inline as above, you can specify a
+ # separate pysaml2 configuration file:
+ #
+ #config_path: "/data/sp_conf.py"
+
+ # The lifetime of a SAML session. This defines how long a user has to
+ # complete the authentication process, if allow_unsolicited is unset.
+ # The default is 15 minutes.
+ #
+ #saml_session_lifetime: 5m
+
+ # An external module can be provided here as a custom solution to
+ # mapping attributes returned from a saml provider onto a matrix user.
+ #
+ user_mapping_provider:
+ # The custom module's class. Uncomment to use a custom module.
+ #
+ #module: mapping_provider.SamlMappingProvider
+
+ # Custom configuration values for the module. Below options are
+ # intended for the built-in provider, they should be changed if
+ # using a custom module. This section will be passed as a Python
+ # dictionary to the module's `parse_config` method.
+ #
+ config:
+ # The SAML attribute (after mapping via the attribute maps) to use
+ # to derive the Matrix ID from. 'uid' by default.
+ #
+ # Note: This used to be configured by the
+ # saml2_config.mxid_source_attribute option. If that is still
+ # defined, its value will be used instead.
+ #
+ #mxid_source_attribute: displayName
+
+ # The mapping system to use for mapping the saml attribute onto a
+ # matrix ID.
+ #
+ # Options include:
+ # * 'hexencode' (which maps unpermitted characters to '=xx')
+ # * 'dotreplace' (which replaces unpermitted characters with
+ # '.').
+ # The default is 'hexencode'.
+ #
+ # Note: This used to be configured by the
+ # saml2_config.mxid_mapping option. If that is still defined, its
+ # value will be used instead.
+ #
+ #mxid_mapping: dotreplace
+
+ # In previous versions of synapse, the mapping from SAML attribute to
+ # MXID was always calculated dynamically rather than stored in a
+ # table. For backwards- compatibility, we will look for user_ids
+ # matching such a pattern before creating a new account.
+ #
+ # This setting controls the SAML attribute which will be used for this
+ # backwards-compatibility lookup. Typically it should be 'uid', but if
+ # the attribute maps are changed, it may be necessary to change it.
+ #
+ # The default is 'uid'.
+ #
+ #grandfathered_mxid_source_attribute: upn
+
+ # It is possible to configure Synapse to only allow logins if SAML attributes
+ # match particular values. The requirements can be listed under
+ # `attribute_requirements` as shown below. All of the listed attributes must
+ # match for the login to be permitted.
+ #
+ #attribute_requirements:
+ # - attribute: userGroup
+ # value: "staff"
+ # - attribute: department
+ # value: "sales"
+
+ # If the metadata XML contains multiple IdP entities then the `idp_entityid`
+ # option must be set to the entity to redirect users to.
+ #
+ # Most deployments only have a single IdP entity and so should omit this
+ # option.
+ #
+ #idp_entityid: 'https://our_idp/entityid'
+
+
+# List of OpenID Connect (OIDC) / OAuth 2.0 identity providers, for registration
+# and login.
+#
+# Options for each entry include:
+#
+# idp_id: a unique identifier for this identity provider. Used internally
+# by Synapse; should be a single word such as 'github'.
+#
+# Note that, if this is changed, users authenticating via that provider
+# will no longer be recognised as the same user!
+#
+# (Use "oidc" here if you are migrating from an old "oidc_config"
+# configuration.)
+#
+# idp_name: A user-facing name for this identity provider, which is used to
+# offer the user a choice of login mechanisms.
+#
+# idp_icon: An optional icon for this identity provider, which is presented
+# by clients and Synapse's own IdP picker page. If given, must be an
+# MXC URI of the format mxc://<server-name>/<media-id>. (An easy way to
+# obtain such an MXC URI is to upload an image to an (unencrypted) room
+# and then copy the "url" from the source of the event.)
+#
+# idp_brand: An optional brand for this identity provider, allowing clients
+# to style the login flow according to the identity provider in question.
+# See the spec for possible options here.
+#
+# discover: set to 'false' to disable the use of the OIDC discovery mechanism
+# to discover endpoints. Defaults to true.
+#
+# issuer: Required. The OIDC issuer. Used to validate tokens and (if discovery
+# is enabled) to discover the provider's endpoints.
+#
+# client_id: Required. oauth2 client id to use.
+#
+# client_secret: oauth2 client secret to use. May be omitted if
+# client_secret_jwt_key is given, or if client_auth_method is 'none'.
+#
+# client_secret_jwt_key: Alternative to client_secret: details of a key used
+# to create a JSON Web Token to be used as an OAuth2 client secret. If
+# given, must be a dictionary with the following properties:
+#
+# key: a pem-encoded signing key. Must be a suitable key for the
+# algorithm specified. Required unless 'key_file' is given.
+#
+# key_file: the path to file containing a pem-encoded signing key file.
+# Required unless 'key' is given.
+#
+# jwt_header: a dictionary giving properties to include in the JWT
+# header. Must include the key 'alg', giving the algorithm used to
+# sign the JWT, such as "ES256", using the JWA identifiers in
+# RFC7518.
+#
+# jwt_payload: an optional dictionary giving properties to include in
+# the JWT payload. Normally this should include an 'iss' key.
+#
+# client_auth_method: auth method to use when exchanging the token. Valid
+# values are 'client_secret_basic' (default), 'client_secret_post' and
+# 'none'.
+#
+# scopes: list of scopes to request. This should normally include the "openid"
+# scope. Defaults to ["openid"].
+#
+# authorization_endpoint: the oauth2 authorization endpoint. Required if
+# provider discovery is disabled.
+#
+# token_endpoint: the oauth2 token endpoint. Required if provider discovery is
+# disabled.
+#
+# userinfo_endpoint: the OIDC userinfo endpoint. Required if discovery is
+# disabled and the 'openid' scope is not requested.
+#
+# jwks_uri: URI where to fetch the JWKS. Required if discovery is disabled and
+# the 'openid' scope is used.
+#
+# skip_verification: set to 'true' to skip metadata verification. Use this if
+# you are connecting to a provider that is not OpenID Connect compliant.
+# Defaults to false. Avoid this in production.
+#
+# user_profile_method: Whether to fetch the user profile from the userinfo
+# endpoint. Valid values are: 'auto' or 'userinfo_endpoint'.
+#
+# Defaults to 'auto', which fetches the userinfo endpoint if 'openid' is
+# included in 'scopes'. Set to 'userinfo_endpoint' to always fetch the
+# userinfo endpoint.
+#
+# allow_existing_users: set to 'true' to allow a user logging in via OIDC to
+# match a pre-existing account instead of failing. This could be used if
+# switching from password logins to OIDC. Defaults to false.
+#
+# user_mapping_provider: Configuration for how attributes returned from a OIDC
+# provider are mapped onto a matrix user. This setting has the following
+# sub-properties:
+#
+# module: The class name of a custom mapping module. Default is
+# 'synapse.handlers.oidc.JinjaOidcMappingProvider'.
+# See https://matrix-org.github.io/synapse/latest/sso_mapping_providers.html#openid-mapping-providers
+# for information on implementing a custom mapping provider.
+#
+# config: Configuration for the mapping provider module. This section will
+# be passed as a Python dictionary to the user mapping provider
+# module's `parse_config` method.
+#
+# For the default provider, the following settings are available:
+#
+# subject_claim: name of the claim containing a unique identifier
+# for the user. Defaults to 'sub', which OpenID Connect
+# compliant providers should provide.
+#
+# localpart_template: Jinja2 template for the localpart of the MXID.
+# If this is not set, the user will be prompted to choose their
+# own username (see 'sso_auth_account_details.html' in the 'sso'
+# section of this file).
+#
+# display_name_template: Jinja2 template for the display name to set
+# on first login. If unset, no displayname will be set.
+#
+# email_template: Jinja2 template for the email address of the user.
+# If unset, no email address will be added to the account.
+#
+# extra_attributes: a map of Jinja2 templates for extra attributes
+# to send back to the client during login.
+# Note that these are non-standard and clients will ignore them
+# without modifications.
+#
+# When rendering, the Jinja2 templates are given a 'user' variable,
+# which is set to the claims returned by the UserInfo Endpoint and/or
+# in the ID Token.
+#
+# It is possible to configure Synapse to only allow logins if certain attributes
+# match particular values in the OIDC userinfo. The requirements can be listed under
+# `attribute_requirements` as shown below. All of the listed attributes must
+# match for the login to be permitted. Additional attributes can be added to
+# userinfo by expanding the `scopes` section of the OIDC config to retrieve
+# additional information from the OIDC provider.
+#
+# If the OIDC claim is a list, then the attribute must match any value in the list.
+# Otherwise, it must exactly match the value of the claim. Using the example
+# below, the `family_name` claim MUST be "Stephensson", but the `groups`
+# claim MUST contain "admin".
+#
+# attribute_requirements:
+# - attribute: family_name
+# value: "Stephensson"
+# - attribute: groups
+# value: "admin"
+#
+# See https://matrix-org.github.io/synapse/latest/openid.html
+# for information on how to configure these options.
+#
+# For backwards compatibility, it is also possible to configure a single OIDC
+# provider via an 'oidc_config' setting. This is now deprecated and admins are
+# advised to migrate to the 'oidc_providers' format. (When doing that migration,
+# use 'oidc' for the idp_id to ensure that existing users continue to be
+# recognised.)
+#
+oidc_providers:
+ # Generic example
+ #
+ #- idp_id: my_idp
+ # idp_name: "My OpenID provider"
+ # idp_icon: "mxc://example.com/mediaid"
+ # discover: false
+ # issuer: "https://accounts.example.com/"
+ # client_id: "provided-by-your-issuer"
+ # client_secret: "provided-by-your-issuer"
+ # client_auth_method: client_secret_post
+ # scopes: ["openid", "profile"]
+ # authorization_endpoint: "https://accounts.example.com/oauth2/auth"
+ # token_endpoint: "https://accounts.example.com/oauth2/token"
+ # userinfo_endpoint: "https://accounts.example.com/userinfo"
+ # jwks_uri: "https://accounts.example.com/.well-known/jwks.json"
+ # skip_verification: true
+ # user_mapping_provider:
+ # config:
+ # subject_claim: "id"
+ # localpart_template: "{ { user.login } }"
+ # display_name_template: "{ { user.name } }"
+ # email_template: "{ { user.email } }"
+ # attribute_requirements:
+ # - attribute: userGroup
+ # value: "synapseUsers"
+
+
+# Enable Central Authentication Service (CAS) for registration and login.
+#
+cas_config:
+ # Uncomment the following to enable authorization against a CAS server.
+ # Defaults to false.
+ #
+ #enabled: true
+
+ # The URL of the CAS authorization endpoint.
+ #
+ #server_url: "https://cas-server.com"
+
+ # The attribute of the CAS response to use as the display name.
+ #
+ # If unset, no displayname will be set.
+ #
+ #displayname_attribute: name
+
+ # It is possible to configure Synapse to only allow logins if CAS attributes
+ # match particular values. All of the keys in the mapping below must exist
+ # and the values must match the given value. Alternately if the given value
+ # is None then any value is allowed (the attribute just must exist).
+ # All of the listed attributes must match for the login to be permitted.
+ #
+ #required_attributes:
+ # userGroup: "staff"
+ # department: None
+
+
+# Additional settings to use with single-sign on systems such as OpenID Connect,
+# SAML2 and CAS.
+#
+# Server admins can configure custom templates for pages related to SSO. See
+# https://matrix-org.github.io/synapse/latest/templates.html for more information.
+#
+sso:
+ # A list of client URLs which are whitelisted so that the user does not
+ # have to confirm giving access to their account to the URL. Any client
+ # whose URL starts with an entry in the following list will not be subject
+ # to an additional confirmation step after the SSO login is completed.
+ #
+ # WARNING: An entry such as "https://my.client" is insecure, because it
+ # will also match "https://my.client.evil.site", exposing your users to
+ # phishing attacks from evil.site. To avoid this, include a slash after the
+ # hostname: "https://my.client/".
+ #
+ # The login fallback page (used by clients that don't natively support the
+ # required login flows) is whitelisted in addition to any URLs in this list.
+ #
+ # By default, this list contains only the login fallback page.
+ #
+ #client_whitelist:
+ # - https://riot.im/develop
+ # - https://my.custom.client/
+
+ # Uncomment to keep a user's profile fields in sync with information from
+ # the identity provider. Currently only syncing the displayname is
+ # supported. Fields are checked on every SSO login, and are updated
+ # if necessary.
+ #
+ # Note that enabling this option will override user profile information,
+ # regardless of whether users have opted-out of syncing that
+ # information when first signing in. Defaults to false.
+ #
+ #update_profile_information: true
+
+
+# JSON web token integration. The following settings can be used to make
+# Synapse JSON web tokens for authentication, instead of its internal
+# password database.
+#
+# Each JSON Web Token needs to contain a "sub" (subject) claim, which is
+# used as the localpart of the mxid.
+#
+# Additionally, the expiration time ("exp"), not before time ("nbf"),
+# and issued at ("iat") claims are validated if present.
+#
+# Note that this is a non-standard login type and client support is
+# expected to be non-existent.
+#
+# See https://matrix-org.github.io/synapse/latest/jwt.html.
+#
+#jwt_config:
+ # Uncomment the following to enable authorization using JSON web
+ # tokens. Defaults to false.
+ #
+ #enabled: true
+
+ # This is either the private shared secret or the public key used to
+ # decode the contents of the JSON web token.
+ #
+ # Required if 'enabled' is true.
+ #
+ #secret: "provided-by-your-issuer"
+
+ # The algorithm used to sign the JSON web token.
+ #
+ # Supported algorithms are listed at
+ # https://pyjwt.readthedocs.io/en/latest/algorithms.html
+ #
+ # Required if 'enabled' is true.
+ #
+ #algorithm: "provided-by-your-issuer"
+
+ # Name of the claim containing a unique identifier for the user.
+ #
+ # Optional, defaults to `sub`.
+ #
+ #subject_claim: "sub"
+
+ # The issuer to validate the "iss" claim against.
+ #
+ # Optional, if provided the "iss" claim will be required and
+ # validated for all JSON web tokens.
+ #
+ #issuer: "provided-by-your-issuer"
+
+ # A list of audiences to validate the "aud" claim against.
+ #
+ # Optional, if provided the "aud" claim will be required and
+ # validated for all JSON web tokens.
+ #
+ # Note that if the "aud" claim is included in a JSON web token then
+ # validation will fail without configuring audiences.
+ #
+ #audiences:
+ # - "provided-by-your-issuer"
+
+
+password_config:
+ # Uncomment to disable password login
+ #
+ #enabled: false
+
+ # Uncomment to disable authentication against the local password
+ # database. This is ignored if `enabled` is false, and is only useful
+ # if you have other password_providers.
+ #
+ #localdb_enabled: false
+
+ # Uncomment and change to a secret random string for extra security.
+ # DO NOT CHANGE THIS AFTER INITIAL SETUP!
+ #
+ #pepper: "EVEN_MORE_SECRET"
+
+ # Define and enforce a password policy. Each parameter is optional.
+ # This is an implementation of MSC2000.
+ #
+ policy:
+ # Whether to enforce the password policy.
+ # Defaults to 'false'.
+ #
+ #enabled: true
+
+ # Minimum accepted length for a password.
+ # Defaults to 0.
+ #
+ #minimum_length: 15
+
+ # Whether a password must contain at least one digit.
+ # Defaults to 'false'.
+ #
+ #require_digit: true
+
+ # Whether a password must contain at least one symbol.
+ # A symbol is any character that's not a number or a letter.
+ # Defaults to 'false'.
+ #
+ #require_symbol: true
+
+ # Whether a password must contain at least one lowercase letter.
+ # Defaults to 'false'.
+ #
+ #require_lowercase: true
+
+ # Whether a password must contain at least one uppercase letter.
+ # Defaults to 'false'.
+ #
+ #require_uppercase: true
+
+ui_auth:
+ # The amount of time to allow a user-interactive authentication session
+ # to be active.
+ #
+ # This defaults to 0, meaning the user is queried for their credentials
+ # before every action, but this can be overridden to allow a single
+ # validation to be re-used. This weakens the protections afforded by
+ # the user-interactive authentication process, by allowing for multiple
+ # (and potentially different) operations to use the same validation session.
+ #
+ # This is ignored for potentially "dangerous" operations (including
+ # deactivating an account, modifying an account password, and
+ # adding a 3PID).
+ #
+ # Uncomment below to allow for credential validation to last for 15
+ # seconds.
+ #
+ #session_timeout: "15s"
+
+
+# Configuration for sending emails from Synapse.
+#
+# Server admins can configure custom templates for email content. See
+# https://matrix-org.github.io/synapse/latest/templates.html for more information.
+#
+email:
+ # The hostname of the outgoing SMTP server to use. Defaults to 'localhost'.
+ #
+ #smtp_host: mail.server
+
+ # The port on the mail server for outgoing SMTP. Defaults to 25.
+ #
+ #smtp_port: 587
+
+ # Username/password for authentication to the SMTP server. By default, no
+ # authentication is attempted.
+ #
+ #smtp_user: "exampleusername"
+ #smtp_pass: "examplepassword"
+
+ # Uncomment the following to require TLS transport security for SMTP.
+ # By default, Synapse will connect over plain text, and will then switch to
+ # TLS via STARTTLS *if the SMTP server supports it*. If this option is set,
+ # Synapse will refuse to connect unless the server supports STARTTLS.
+ #
+ #require_transport_security: true
+
+ # Uncomment the following to disable TLS for SMTP.
+ #
+ # By default, if the server supports TLS, it will be used, and the server
+ # must present a certificate that is valid for 'smtp_host'. If this option
+ # is set to false, TLS will not be used.
+ #
+ #enable_tls: false
+
+ # notif_from defines the "From" address to use when sending emails.
+ # It must be set if email sending is enabled.
+ #
+ # The placeholder '%(app)s' will be replaced by the application name,
+ # which is normally 'app_name' (below), but may be overridden by the
+ # Matrix client application.
+ #
+ # Note that the placeholder must be written '%(app)s', including the
+ # trailing 's'.
+ #
+ #notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
+
+ # app_name defines the default value for '%(app)s' in notif_from and email
+ # subjects. It defaults to 'Matrix'.
+ #
+ #app_name: my_branded_matrix_server
+
+ # Uncomment the following to enable sending emails for messages that the user
+ # has missed. Disabled by default.
+ #
+ #enable_notifs: true
+
+ # Uncomment the following to disable automatic subscription to email
+ # notifications for new users. Enabled by default.
+ #
+ #notif_for_new_users: false
+
+ # Custom URL for client links within the email notifications. By default
+ # links will be based on "https://matrix.to".
+ #
+ # (This setting used to be called riot_base_url; the old name is still
+ # supported for backwards-compatibility but is now deprecated.)
+ #
+ #client_base_url: "http://localhost/riot"
+
+ # Configure the time that a validation email will expire after sending.
+ # Defaults to 1h.
+ #
+ #validation_token_lifetime: 15m
+
+ # The web client location to direct users to during an invite. This is passed
+ # to the identity server as the org.matrix.web_client_location key. Defaults
+ # to unset, giving no guidance to the identity server.
+ #
+ #invite_client_location: https://app.element.io
+
+ # Subjects to use when sending emails from Synapse.
+ #
+ # The placeholder '%(app)s' will be replaced with the value of the 'app_name'
+ # setting above, or by a value dictated by the Matrix client application.
+ #
+ # If a subject isn't overridden in this configuration file, the value used as
+ # its example will be used.
+ #
+ #subjects:
+
+ # Subjects for notification emails.
+ #
+ # On top of the '%(app)s' placeholder, these can use the following
+ # placeholders:
+ #
+ # * '%(person)s', which will be replaced by the display name of the user(s)
+ # that sent the message(s), e.g. "Alice and Bob".
+ # * '%(room)s', which will be replaced by the name of the room the
+ # message(s) have been sent to, e.g. "My super room".
+ #
+ # See the example provided for each setting to see which placeholder can be
+ # used and how to use them.
+ #
+ # Subject to use to notify about one message from one or more user(s) in a
+ # room which has a name.
+ #message_from_person_in_room: "[%(app)s] You have a message on %(app)s from %(person)s in the %(room)s room..."
+ #
+ # Subject to use to notify about one message from one or more user(s) in a
+ # room which doesn't have a name.
+ #message_from_person: "[%(app)s] You have a message on %(app)s from %(person)s..."
+ #
+ # Subject to use to notify about multiple messages from one or more users in
+ # a room which doesn't have a name.
+ #messages_from_person: "[%(app)s] You have messages on %(app)s from %(person)s..."
+ #
+ # Subject to use to notify about multiple messages in a room which has a
+ # name.
+ #messages_in_room: "[%(app)s] You have messages on %(app)s in the %(room)s room..."
+ #
+ # Subject to use to notify about multiple messages in multiple rooms.
+ #messages_in_room_and_others: "[%(app)s] You have messages on %(app)s in the %(room)s room and others..."
+ #
+ # Subject to use to notify about multiple messages from multiple persons in
+ # multiple rooms. This is similar to the setting above except it's used when
+ # the room in which the notification was triggered has no name.
+ #messages_from_person_and_others: "[%(app)s] You have messages on %(app)s from %(person)s and others..."
+ #
+ # Subject to use to notify about an invite to a room which has a name.
+ #invite_from_person_to_room: "[%(app)s] %(person)s has invited you to join the %(room)s room on %(app)s..."
+ #
+ # Subject to use to notify about an invite to a room which doesn't have a
+ # name.
+ #invite_from_person: "[%(app)s] %(person)s has invited you to chat on %(app)s..."
+
+ # Subject for emails related to account administration.
+ #
+ # On top of the '%(app)s' placeholder, these one can use the
+ # '%(server_name)s' placeholder, which will be replaced by the value of the
+ # 'server_name' setting in your Synapse configuration.
+ #
+ # Subject to use when sending a password reset email.
+ #password_reset: "[%(server_name)s] Password reset"
+ #
+ # Subject to use when sending a verification email to assert an address's
+ # ownership.
+ #email_validation: "[%(server_name)s] Validate your email"
+
+
+
+## Push ##
+
+push:
+ # Clients requesting push notifications can either have the body of
+ # the message sent in the notification poke along with other details
+ # like the sender, or just the event ID and room ID (`event_id_only`).
+ # If clients choose the former, this option controls whether the
+ # notification request includes the content of the event (other details
+ # like the sender are still included). For `event_id_only` push, it
+ # has no effect.
+ #
+ # For modern android devices the notification content will still appear
+ # because it is loaded by the app. iPhone, however will send a
+ # notification saying only that a message arrived and who it came from.
+ #
+ # The default value is "true" to include message details. Uncomment to only
+ # include the event ID and room ID in push notification payloads.
+ #
+ #include_content: false
+
+ # When a push notification is received, an unread count is also sent.
+ # This number can either be calculated as the number of unread messages
+ # for the user, or the number of *rooms* the user has unread messages in.
+ #
+ # The default value is "true", meaning push clients will see the number of
+ # rooms with unread messages in them. Uncomment to instead send the number
+ # of unread messages.
+ #
+ #group_unread_count_by_room: false
+
+
+## Rooms ##
+
+# Controls whether locally-created rooms should be end-to-end encrypted by
+# default.
+#
+# Possible options are "all", "invite", and "off". They are defined as:
+#
+# * "all": any locally-created room
+# * "invite": any room created with the "private_chat" or "trusted_private_chat"
+# room creation presets
+# * "off": this option will take no effect
+#
+# The default value is "off".
+#
+# Note that this option will only affect rooms created after it is set. It
+# will also not affect rooms created by other servers.
+#
+#encryption_enabled_by_default_for_room_type: invite
+
+
+# Uncomment to allow non-server-admin users to create groups on this server
+#
+#enable_group_creation: true
+
+# If enabled, non server admins can only create groups with local parts
+# starting with this prefix
+#
+#group_creation_prefix: "unofficial_"
+
+
+
+# User Directory configuration
+#
+user_directory:
+ # Defines whether users can search the user directory. If false then
+ # empty responses are returned to all queries. Defaults to true.
+ #
+ # Uncomment to disable the user directory.
+ #
+ #enabled: false
+
+ # Defines whether to search all users visible to your HS when searching
+ # the user directory. If false, search results will only contain users
+ # visible in public rooms and users sharing a room with the requester.
+ # Defaults to false.
+ #
+ # NB. If you set this to true, and the last time the user_directory search
+ # indexes were (re)built was before Synapse 1.44, you'll have to
+ # rebuild the indexes in order to search through all known users.
+ # These indexes are built the first time Synapse starts; admins can
+ # manually trigger a rebuild via API following the instructions at
+ # https://matrix-org.github.io/synapse/latest/usage/administration/admin_api/background_updates.html#run
+ #
+ # Uncomment to return search results containing all known users, even if that
+ # user does not share a room with the requester.
+ #
+ #search_all_users: true
+
+ # Defines whether to prefer local users in search query results.
+ # If True, local users are more likely to appear above remote users
+ # when searching the user directory. Defaults to false.
+ #
+ # Uncomment to prefer local over remote users in user directory search
+ # results.
+ #
+ #prefer_local_users: true
+
+
+# User Consent configuration
+#
+# for detailed instructions, see
+# https://matrix-org.github.io/synapse/latest/consent_tracking.html
+#
+# Parts of this section are required if enabling the 'consent' resource under
+# 'listeners', in particular 'template_dir' and 'version'.
+#
+# 'template_dir' gives the location of the templates for the HTML forms.
+# This directory should contain one subdirectory per language (eg, 'en', 'fr'),
+# and each language directory should contain the policy document (named as
+# '<version>.html') and a success page (success.html).
+#
+# 'version' specifies the 'current' version of the policy document. It defines
+# the version to be served by the consent resource if there is no 'v'
+# parameter.
+#
+# 'server_notice_content', if enabled, will send a user a "Server Notice"
+# asking them to consent to the privacy policy. The 'server_notices' section
+# must also be configured for this to work. Notices will *not* be sent to
+# guest users unless 'send_server_notice_to_guests' is set to true.
+#
+# 'block_events_error', if set, will block any attempts to send events
+# until the user consents to the privacy policy. The value of the setting is
+# used as the text of the error.
+#
+# 'require_at_registration', if enabled, will add a step to the registration
+# process, similar to how captcha works. Users will be required to accept the
+# policy before their account is created.
+#
+# 'policy_name' is the display name of the policy users will see when registering
+# for an account. Has no effect unless `require_at_registration` is enabled.
+# Defaults to "Privacy Policy".
+#
+#user_consent:
+# template_dir: res/templates/privacy
+# version: 1.0
+# server_notice_content:
+# msgtype: m.text
+# body: >-
+# To continue using this homeserver you must review and agree to the
+# terms and conditions at %(consent_uri)s
+# send_server_notice_to_guests: true
+# block_events_error: >-
+# To continue using this homeserver you must review and agree to the
+# terms and conditions at %(consent_uri)s
+# require_at_registration: false
+# policy_name: Privacy Policy
+#
+
+
+
+# Settings for local room and user statistics collection. See
+# https://matrix-org.github.io/synapse/latest/room_and_user_statistics.html.
+#
+stats:
+ # Uncomment the following to disable room and user statistics. Note that doing
+ # so may cause certain features (such as the room directory) not to work
+ # correctly.
+ #
+ #enabled: false
+
+
+# Server Notices room configuration
+#
+# Uncomment this section to enable a room which can be used to send notices
+# from the server to users. It is a special room which cannot be left; notices
+# come from a special "notices" user id.
+#
+# If you uncomment this section, you *must* define the system_mxid_localpart
+# setting, which defines the id of the user which will be used to send the
+# notices.
+#
+# It's also possible to override the room name, the display name of the
+# "notices" user, and the avatar for the user.
+#
+#server_notices:
+# system_mxid_localpart: notices
+# system_mxid_display_name: "Server Notices"
+# system_mxid_avatar_url: "mxc://server.com/oumMVlgDnLYFaPVkExemNVVZ"
+# room_name: "Server Notices"
+
+
+
+# Uncomment to disable searching the public room list. When disabled
+# blocks searching local and remote room lists for local and remote
+# users by always returning an empty list for all queries.
+#
+#enable_room_list_search: false
+
+# The `alias_creation` option controls who's allowed to create aliases
+# on this server.
+#
+# The format of this option is a list of rules that contain globs that
+# match against user_id, room_id and the new alias (fully qualified with
+# server name). The action in the first rule that matches is taken,
+# which can currently either be "allow" or "deny".
+#
+# Missing user_id/room_id/alias fields default to "*".
+#
+# If no rules match the request is denied. An empty list means no one
+# can create aliases.
+#
+# Options for the rules include:
+#
+# user_id: Matches against the creator of the alias
+# alias: Matches against the alias being created
+# room_id: Matches against the room ID the alias is being pointed at
+# action: Whether to "allow" or "deny" the request if the rule matches
+#
+# The default is:
+#
+#alias_creation_rules:
+# - user_id: "*"
+# alias: "*"
+# room_id: "*"
+# action: allow
+
+# The `room_list_publication_rules` option controls who can publish and
+# which rooms can be published in the public room list.
+#
+# The format of this option is the same as that for
+# `alias_creation_rules`.
+#
+# If the room has one or more aliases associated with it, only one of
+# the aliases needs to match the alias rule. If there are no aliases
+# then only rules with `alias: *` match.
+#
+# If no rules match the request is denied. An empty list means no one
+# can publish rooms.
+#
+# Options for the rules include:
+#
+# user_id: Matches against the creator of the alias
+# room_id: Matches against the room ID being published
+# alias: Matches against any current local or canonical aliases
+# associated with the room
+# action: Whether to "allow" or "deny" the request if the rule matches
+#
+# The default is:
+#
+#room_list_publication_rules:
+# - user_id: "*"
+# alias: "*"
+# room_id: "*"
+# action: allow
+
+
+## Opentracing ##
+
+# These settings enable opentracing, which implements distributed tracing.
+# This allows you to observe the causal chains of events across servers
+# including requests, key lookups etc., across any server running
+# synapse or any other other services which supports opentracing
+# (specifically those implemented with Jaeger).
+#
+opentracing:
+ # tracing is disabled by default. Uncomment the following line to enable it.
+ #
+ #enabled: true
+
+ # The list of homeservers we wish to send and receive span contexts and span baggage.
+ # See https://matrix-org.github.io/synapse/latest/opentracing.html.
+ #
+ # This is a list of regexes which are matched against the server_name of the
+ # homeserver.
+ #
+ # By default, it is empty, so no servers are matched.
+ #
+ #homeserver_whitelist:
+ # - ".*"
+
+ # A list of the matrix IDs of users whose requests will always be traced,
+ # even if the tracing system would otherwise drop the traces due to
+ # probabilistic sampling.
+ #
+ # By default, the list is empty.
+ #
+ #force_tracing_for_users:
+ # - "@user1:server_name"
+ # - "@user2:server_name"
+
+ # Jaeger can be configured to sample traces at different rates.
+ # All configuration options provided by Jaeger can be set here.
+ # Jaeger's configuration is mostly related to trace sampling which
+ # is documented here:
+ # https://www.jaegertracing.io/docs/latest/sampling/.
+ #
+ #jaeger_config:
+ # sampler:
+ # type: const
+ # param: 1
+ # logging:
+ # false
+
+
+## Workers ##
+
+# Disables sending of outbound federation transactions on the main process.
+# Uncomment if using a federation sender worker.
+#
+#send_federation: false
+
+# It is possible to run multiple federation sender workers, in which case the
+# work is balanced across them.
+#
+# This configuration must be shared between all federation sender workers, and if
+# changed all federation sender workers must be stopped at the same time and then
+# started, to ensure that all instances are running with the same config (otherwise
+# events may be dropped).
+#
+#federation_sender_instances:
+# - federation_sender1
+
+# When using workers this should be a map from `worker_name` to the
+# HTTP replication listener of the worker, if configured.
+#
+#instance_map:
+# worker1:
+# host: localhost
+# port: 8034
+
+# Experimental: When using workers you can define which workers should
+# handle event persistence and typing notifications. Any worker
+# specified here must also be in the `instance_map`.
+#
+#stream_writers:
+# events: worker1
+# typing: worker1
+
+# The worker that is used to run background tasks (e.g. cleaning up expired
+# data). If not provided this defaults to the main process.
+#
+#run_background_tasks_on: worker1
+
+# A shared secret used by the replication APIs to authenticate HTTP requests
+# from workers.
+#
+# By default this is unused and traffic is not authenticated.
+#
+#worker_replication_secret: ""
+
+
+# Configuration for Redis when using workers. This *must* be enabled when
+# using workers (unless using old style direct TCP configuration).
+#
+redis:
+ # Uncomment the below to enable Redis support.
+ #
+ #enabled: true
+
+ # Optional host and port to use to connect to redis. Defaults to
+ # localhost and 6379
+ #
+ #host: localhost
+ #port: 6379
+
+ # Optional password if configured on the Redis instance
+ #
+ #password: <secret_password>
+
+
+# vim:ft=yaml
diff --git a/cluster/staging/app/im/config/litestream.yml b/cluster/staging/app/im/config/litestream.yml
new file mode 100644
index 0000000..e444e38
--- /dev/null
+++ b/cluster/staging/app/im/config/litestream.yml
@@ -0,0 +1,10 @@
+dbs:
+ - path: /ephemeral/homeserver.db
+ replicas:
+ - url: s3://synapse-db/homeserver.db
+ region: garage-staging
+ endpoint: http://{{ env "attr.unique.network.ip-address" }}:3990
+ access-key-id: {{ key "secrets/synapse/s3_access_key" | trimSpace }}
+ secret-access-key: {{ key "secrets/synapse/s3_secret_key" | trimSpace }}
+ force-path-style: true
+ sync-interval: 60s
diff --git a/cluster/staging/app/im/config/synapse.log.config.yaml b/cluster/staging/app/im/config/synapse.log.config.yaml
new file mode 100644
index 0000000..0b5622e
--- /dev/null
+++ b/cluster/staging/app/im/config/synapse.log.config.yaml
@@ -0,0 +1,23 @@
+version: 1
+
+formatters:
+ precise:
+ format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
+
+
+handlers:
+ console:
+ class: logging.StreamHandler
+ formatter: precise
+
+loggers:
+ synapse.storage.SQL:
+ # beware: increasing this to DEBUG will make synapse log sensitive
+ # information such as access tokens.
+ level: INFO
+
+root:
+ level: INFO
+ handlers: [console]
+
+disable_existing_loggers: false
diff --git a/cluster/staging/app/im/deploy/im.hcl b/cluster/staging/app/im/deploy/im.hcl
new file mode 100644
index 0000000..c60b095
--- /dev/null
+++ b/cluster/staging/app/im/deploy/im.hcl
@@ -0,0 +1,165 @@
+job "im" {
+ datacenters = ["neptune"]
+ type = "service"
+
+ group "synapse" {
+ count = 1
+
+ network {
+ port "http" {
+ to = 8008
+ }
+ }
+
+ ephemeral_disk {
+ size = 10000
+ }
+
+ restart {
+ attempts = 10
+ delay = "30s"
+ }
+
+ task "restore-db" {
+ lifecycle {
+ hook = "prestart"
+ sidecar = false
+ }
+
+ driver = "docker"
+ config {
+ image = "litestream/litestream:0.3.7"
+ args = [
+ "restore", "-config", "/etc/litestream.yml", "/ephemeral/homeserver.db"
+ ]
+ volumes = [
+ "../alloc/data:/ephemeral",
+ "secrets/litestream.yml:/etc/litestream.yml"
+ ]
+ }
+
+ template {
+ data = file("../config/litestream.yml")
+ destination = "secrets/litestream.yml"
+ }
+
+ resources {
+ memory = 200
+ cpu = 1000
+ }
+ }
+
+ task "synapse" {
+ driver = "docker"
+ config {
+ image = "lxpz/amd64_synapse:1.49.2-4"
+ ports = [ "http" ]
+
+ command = "python"
+ args = [
+ "-m", "synapse.app.homeserver",
+ "-n",
+ "-c", "/etc/matrix-synapse/homeserver.yaml"
+ ]
+
+ volumes = [
+ "secrets:/etc/matrix-synapse",
+ "../alloc/data:/ephemeral",
+ ]
+ }
+
+ template {
+ data = file("../config/homeserver.yaml")
+ destination = "secrets/homeserver.yaml"
+ }
+
+ template {
+ data = file("../config/synapse.log.config.yaml")
+ destination = "secrets/synapse.log.config.yaml"
+ }
+
+ template {
+ data = "{{ key \"secrets/synapse/signing_key\" }}"
+ destination = "secrets/signing_key"
+ }
+
+ resources {
+ memory = 2000
+ cpu = 1000
+ }
+
+ service {
+ port = "http"
+ tags = [
+ "tricot matrix.home.adnab.me 100",
+ "tricot matrix.home.adnab.me:443 100",
+ "tricot-add-header Access-Control-Allow-Origin *",
+ ]
+ check {
+ type = "http"
+ path = "/"
+ interval = "10s"
+ timeout = "2s"
+ }
+ }
+ }
+
+ task "media-async-upload" {
+ driver = "docker"
+
+ config {
+ image = "lxpz/amd64_synapse:1.49.2-4"
+ readonly_rootfs = true
+ command = "/usr/local/bin/matrix-s3-async-sqlite"
+ work_dir = "/ephemeral"
+ volumes = [
+ "../alloc/data:/ephemeral",
+ ]
+ }
+
+ resources {
+ cpu = 100
+ memory = 100
+ }
+
+ template {
+ data = <<EOH
+SYNAPSE_SQLITE_DB=/ephemeral/homeserver.db
+SYNAPSE_MEDIA_STORE=/ephemeral/media_store
+SYNAPSE_MEDIA_S3_BUCKET=synapse-data
+AWS_ACCESS_KEY_ID={{ key "secrets/synapse/s3_access_key" | trimSpace }}
+AWS_SECRET_ACCESS_KEY={{ key "secrets/synapse/s3_secret_key" | trimSpace }}
+AWS_DEFAULT_REGION=garage-staging
+S3_ENDPOINT=http://{{ env "attr.unique.network.ip-address" }}:3990
+
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+ }
+
+ task "replicate-db" {
+ driver = "docker"
+ config {
+ image = "litestream/litestream:0.3.7"
+ args = [
+ "replicate", "-config", "/etc/litestream.yml"
+ ]
+ volumes = [
+ "../alloc/data:/ephemeral",
+ "secrets/litestream.yml:/etc/litestream.yml"
+ ]
+ }
+
+ template {
+ data = file("../config/litestream.yml")
+ destination = "secrets/litestream.yml"
+ }
+
+ resources {
+ memory = 250
+ cpu = 100
+ }
+ }
+ }
+}
diff --git a/cluster/staging/app/im/secrets/synapse/form_secret b/cluster/staging/app/im/secrets/synapse/form_secret
new file mode 100644
index 0000000..37cf6ed
--- /dev/null
+++ b/cluster/staging/app/im/secrets/synapse/form_secret
@@ -0,0 +1 @@
+USER Synapse's `form_secret` configuration parameter
diff --git a/cluster/staging/app/im/secrets/synapse/macaroon_secret_key b/cluster/staging/app/im/secrets/synapse/macaroon_secret_key
new file mode 100644
index 0000000..5f7f959
--- /dev/null
+++ b/cluster/staging/app/im/secrets/synapse/macaroon_secret_key
@@ -0,0 +1 @@
+USER Synapse's `macaroon_secret_key` parameter
diff --git a/cluster/staging/app/im/secrets/synapse/registration_shared_secret b/cluster/staging/app/im/secrets/synapse/registration_shared_secret
new file mode 100644
index 0000000..60edd0e
--- /dev/null
+++ b/cluster/staging/app/im/secrets/synapse/registration_shared_secret
@@ -0,0 +1 @@
+USER Synapse's `registration_shared_secret` parameter
diff --git a/cluster/staging/app/im/secrets/synapse/s3_access_key b/cluster/staging/app/im/secrets/synapse/s3_access_key
new file mode 100644
index 0000000..692dc34
--- /dev/null
+++ b/cluster/staging/app/im/secrets/synapse/s3_access_key
@@ -0,0 +1 @@
+USER S3 access key ID for database storage
diff --git a/cluster/staging/app/im/secrets/synapse/s3_secret_key b/cluster/staging/app/im/secrets/synapse/s3_secret_key
new file mode 100644
index 0000000..8bef13c
--- /dev/null
+++ b/cluster/staging/app/im/secrets/synapse/s3_secret_key
@@ -0,0 +1 @@
+USER S3 secret key for database storage
diff --git a/cluster/staging/app/im/secrets/synapse/signing_key b/cluster/staging/app/im/secrets/synapse/signing_key
new file mode 100644
index 0000000..6821360
--- /dev/null
+++ b/cluster/staging/app/im/secrets/synapse/signing_key
@@ -0,0 +1 @@
+USER Signing key for messages
diff --git a/cluster/staging/app/telemetry/config/apm-config.yaml b/cluster/staging/app/telemetry/config/apm-config.yaml
new file mode 100644
index 0000000..07a88bd
--- /dev/null
+++ b/cluster/staging/app/telemetry/config/apm-config.yaml
@@ -0,0 +1,20 @@
+apm-server:
+ # Defines the host and port the server is listening on. Use "unix:/path/to.sock" to listen on a unix domain socket.
+ host: "0.0.0.0:8200"
+#-------------------------- Elasticsearch output --------------------------
+output.elasticsearch:
+ # Array of hosts to connect to.
+ # Scheme and port can be left out and will be set to the default (`http` and `9200`).
+ # In case you specify and additional path, the scheme is required: `http://localhost:9200/path`.
+ # IPv6 addresses should always be defined as: `https://[2001:db8::1]:9200`.
+ hosts: ["localhost:9200"]
+ username: "elastic"
+ password: "{{ key "secrets/telemetry/elastic_passwords/elastic" }}"
+
+instrumentation:
+ enabled: true
+ environment: staging
+
+logging:
+ level: warning
+ to_stderr: true
diff --git a/cluster/staging/app/telemetry/config/filebeat.yml b/cluster/staging/app/telemetry/config/filebeat.yml
new file mode 100644
index 0000000..310afd1
--- /dev/null
+++ b/cluster/staging/app/telemetry/config/filebeat.yml
@@ -0,0 +1,46 @@
+# see https://github.com/elastic/beats/blob/master/filebeat/filebeat.reference.yml
+filebeat.modules:
+- module: system
+ syslog:
+ enabled: true
+ auth:
+ enabled: true
+
+#filebeat.inputs:
+#- type: container
+# enabled: true
+# paths:
+# -/var/lib/docker/containers/*/*.log
+# stream: all # can be all, stdout or stderr
+
+#========================== Filebeat autodiscover ==============================
+filebeat.autodiscover:
+ providers:
+ - type: docker
+ # https://www.elastic.co/guide/en/beats/filebeat/current/configuration-autodiscover-hints.html
+ # This URL alos contains instructions on multi-line logs
+ hints.enabled: true
+
+#================================ Processors ===================================
+processors:
+# - add_cloud_metadata: ~
+- add_docker_metadata: ~
+- add_locale:
+ format: offset
+- add_host_metadata:
+ netinfo.enabled: true
+
+#========================== Elasticsearch output ===============================
+output.elasticsearch:
+ hosts: ["localhost:9200"]
+ username: elastic
+ password: {{ key "secrets/telemetry/elastic_passwords/elastic" }}
+
+#============================== Dashboards =====================================
+setup.dashboards:
+ enabled: false
+
+#============================== Xpack Monitoring ===============================
+xpack.monitoring:
+ enabled: true
+ elasticsearch:
diff --git a/cluster/staging/app/telemetry/config/grafana-litestream.yml b/cluster/staging/app/telemetry/config/grafana-litestream.yml
new file mode 100644
index 0000000..a537d9c
--- /dev/null
+++ b/cluster/staging/app/telemetry/config/grafana-litestream.yml
@@ -0,0 +1,10 @@
+dbs:
+ - path: /ephemeral/grafana.db
+ replicas:
+ - url: s3://grafana-db/grafana.db
+ region: garage-staging
+ endpoint: http://{{ env "attr.unique.network.ip-address" }}:3990
+ access-key-id: {{ key "secrets/telemetry/grafana/s3_access_key" | trimSpace }}
+ secret-access-key: {{ key "secrets/telemetry/grafana/s3_secret_key" | trimSpace }}
+ force-path-style: true
+ sync-interval: 60s
diff --git a/cluster/staging/app/telemetry/config/grafana/provisioning/datasources/elastic.yaml b/cluster/staging/app/telemetry/config/grafana/provisioning/datasources/elastic.yaml
new file mode 100644
index 0000000..7d2277c
--- /dev/null
+++ b/cluster/staging/app/telemetry/config/grafana/provisioning/datasources/elastic.yaml
@@ -0,0 +1,21 @@
+apiVersion: 1
+
+datasources:
+ - name: DS_ELASTICSEARCH
+ type: elasticsearch
+ access: proxy
+ url: http://localhost:9200
+ password: '{{ key "secrets/telemetry/elastic_passwords/elastic" }}'
+ user: 'elastic'
+ database: metrics-*
+ basicAuth: false
+ isDefault: true
+ jsonData:
+ esVersion: "8.2.0"
+ includeFrozen: false
+ logLevelField: ''
+ logMessageField: ''
+ maxConcurrentShardRequests: 5
+ timeField: "@timestamp"
+ timeInterval: "5s"
+ readOnly: false
diff --git a/cluster/staging/app/telemetry/config/otel-config.yaml b/cluster/staging/app/telemetry/config/otel-config.yaml
new file mode 100644
index 0000000..bcf1baa
--- /dev/null
+++ b/cluster/staging/app/telemetry/config/otel-config.yaml
@@ -0,0 +1,56 @@
+receivers:
+ # Data sources: metrics, traces
+ otlp:
+ protocols:
+ grpc:
+ endpoint: ":4317"
+ http:
+ endpoint: ":55681"
+ # Data sources: metrics
+ prometheus:
+ config:
+ scrape_configs:
+ - job_name: "garage"
+ scrape_interval: 5s
+ static_configs:
+ - targets:
+ - "{{ env "attr.unique.network.ip-address" }}:3909"
+ - job_name: "node_exporter"
+ scrape_interval: 5s
+ static_configs:
+ - targets:
+ - "{{ env "attr.unique.network.ip-address" }}:9100"
+
+exporters:
+ logging:
+ logLevel: info
+ # see https://www.elastic.co/guide/en/apm/get-started/current/open-telemetry-elastic.html#open-telemetry-collector
+ otlp/elastic:
+ endpoint: "localhost:8200"
+ tls:
+ insecure: true
+
+processors:
+ batch:
+ probabilistic_sampler:
+ hash_seed: 42
+ sampling_percentage: 10
+
+extensions:
+ health_check:
+ pprof:
+ endpoint: :1888
+ zpages:
+ endpoint: :55679
+
+service:
+ extensions: [pprof, zpages, health_check]
+ pipelines:
+ traces:
+ receivers: [otlp]
+ processors: [probabilistic_sampler, batch]
+ exporters: [logging, otlp/elastic]
+ metrics:
+ receivers: [otlp, prometheus]
+ processors: [batch]
+ exporters: [logging, otlp/elastic]
diff --git a/cluster/staging/app/telemetry/deploy/telemetry-system.hcl b/cluster/staging/app/telemetry/deploy/telemetry-system.hcl
new file mode 100644
index 0000000..3e26c2e
--- /dev/null
+++ b/cluster/staging/app/telemetry/deploy/telemetry-system.hcl
@@ -0,0 +1,182 @@
+job "telemetry-system" {
+ datacenters = ["neptune"]
+ type = "system"
+
+ group "elasticsearch" {
+ network {
+ port "elastic" {
+ static = 9200
+ }
+ port "elastic_internal" {
+ static = 9300
+ }
+ }
+
+ task "elastic" {
+ driver = "docker"
+ config {
+ image = "docker.elastic.co/elasticsearch/elasticsearch:8.2.0"
+ network_mode = "host"
+ volumes = [
+ "/mnt/ssd/telemetry/es_data:/usr/share/elasticsearch/data",
+ "secrets/elastic-certificates.p12:/usr/share/elasticsearch/config/elastic-certificates.p12",
+ ]
+ ports = [ "elastic", "elastic_internal" ]
+ sysctl = {
+ #"vm.max_map_count" = "262144",
+ }
+ ulimit = {
+ memlock = "9223372036854775807:9223372036854775807",
+ }
+ }
+
+ user = "1000"
+
+ resources {
+ memory = 1500
+ cpu = 500
+ }
+
+ template {
+ data = "{{ key \"secrets/telemetry/elasticsearch/elastic-certificates.p12\" }}"
+ destination = "secrets/elastic-certificates.p12"
+ }
+
+ template {
+ data = <<EOH
+node.name={{ env "attr.unique.hostname" }}
+http.port=9200
+transport.port=9300
+cluster.name=es-deuxfleurs
+cluster.initial_master_nodes=carcajou,caribou,cariacou
+discovery.seed_hosts=carcajou,caribou,cariacou
+bootstrap.memory_lock=true
+xpack.security.enabled=true
+xpack.security.authc.api_key.enabled=true
+xpack.security.transport.ssl.enabled=true
+xpack.security.transport.ssl.verification_mode=certificate
+xpack.security.transport.ssl.client_authentication=required
+xpack.security.transport.ssl.keystore.path=/usr/share/elasticsearch/config/elastic-certificates.p12
+xpack.security.transport.ssl.truststore.path=/usr/share/elasticsearch/config/elastic-certificates.p12
+cluster.routing.allocation.disk.watermark.high=75%
+cluster.routing.allocation.disk.watermark.low=65%
+ES_JAVA_OPTS=-Xms512M -Xmx512M
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+ }
+ }
+
+ group "collector" {
+ network {
+ port "otel_grpc" {
+ static = 4317
+ }
+ port "apm" {
+ static = 8200
+ }
+ port "node_exporter" {
+ static = 9100
+ }
+ }
+
+ task "otel" {
+ driver = "docker"
+ config {
+ image = "otel/opentelemetry-collector-contrib:0.46.0"
+ args = [
+ "--config=/etc/otel-config.yaml",
+ ]
+ network_mode = "host"
+ ports= [ "otel_grpc" ]
+ volumes = [
+ "secrets/otel-config.yaml:/etc/otel-config.yaml"
+ ]
+ }
+
+ template {
+ data = file("../config/otel-config.yaml")
+ destination = "secrets/otel-config.yaml"
+ }
+
+ resources {
+ memory = 100
+ cpu = 100
+ }
+ }
+
+ task "apm" {
+ driver = "docker"
+ config {
+ image = "docker.elastic.co/apm/apm-server:8.2.0"
+ network_mode = "host"
+ ports = [ "apm" ]
+ args = [ "--strict.perms=false" ]
+ volumes = [
+ "secrets/apm-config.yaml:/usr/share/apm-server/apm-server.yml:ro"
+ ]
+ }
+
+ template {
+ data = file("../config/apm-config.yaml")
+ destination = "secrets/apm-config.yaml"
+ }
+
+ resources {
+ memory = 100
+ cpu = 100
+ }
+ }
+
+/*
+ task "node_exporter" {
+ driver = "docker"
+ config {
+ image = "quay.io/prometheus/node-exporter:v1.1.2"
+ network_mode = "host"
+ ports = [ "node_exporter" ]
+ volumes = [
+ "/:/host:ro,rslave"
+ ]
+ args = [ "--path.rootfs=/host" ]
+ }
+
+ resources {
+ cpu = 50
+ memory = 40
+ }
+ }
+*/
+
+ task "filebeat" {
+ driver = "docker"
+ config {
+ image = "docker.elastic.co/beats/filebeat:8.2.0"
+ network_mode = "host"
+ volumes = [
+ "/mnt/ssd/telemetry/filebeat:/usr/share/filebeat/data",
+ "secrets/filebeat.yml:/usr/share/filebeat/filebeat.yml",
+ "/var/run/docker.sock:/var/run/docker.sock",
+ "/var/lib/docker/containers/:/var/lib/docker/containers/:ro",
+ "/var/log/:/var/log/:ro",
+ ]
+ args = [ "--strict.perms=false" ]
+ privileged = true
+ }
+ user = "root"
+
+
+ template {
+ data = file("../config/filebeat.yml")
+ destination = "secrets/filebeat.yml"
+ }
+
+ resources {
+ memory = 100
+ cpu = 100
+ }
+ }
+ }
+}
+
diff --git a/cluster/staging/app/telemetry/deploy/telemetry.hcl b/cluster/staging/app/telemetry/deploy/telemetry.hcl
new file mode 100644
index 0000000..21685a1
--- /dev/null
+++ b/cluster/staging/app/telemetry/deploy/telemetry.hcl
@@ -0,0 +1,181 @@
+job "telemetry" {
+ datacenters = ["neptune"]
+ type = "service"
+
+ group "kibana" {
+ count = 1
+
+ network {
+ port "kibana" {
+ static = 5601
+ }
+ }
+
+ task "kibana" {
+ driver = "docker"
+ config {
+ image = "docker.elastic.co/kibana/kibana:8.2.0"
+ network_mode = "host"
+ ports = [ "kibana" ]
+ }
+
+ template {
+ data = <<EOH
+SERVER_NAME={{ env "attr.unique.hostname" }}
+ELASTICSEARCH_HOSTS=http://localhost:9200
+ELASTICSEARCH_USERNAME=kibana_system
+ELASTICSEARCH_PASSWORD={{ key "secrets/telemetry/elastic_passwords/kibana_system" }}
+SERVER_PUBLICBASEURL=https://kibana.home.adnab.me
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ resources {
+ memory = 1000
+ cpu = 500
+ }
+
+ service {
+ tags = [
+ "kibana",
+ "tricot kibana.staging.deuxfleurs.org",
+ ]
+ port = 5601
+ address_mode = "driver"
+ name = "kibana"
+ check {
+ type = "tcp"
+ port = 5601
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+ }
+ }
+
+ group "grafana" {
+ count = 1
+
+ network {
+ port "grafana" {
+ static = 3333
+ }
+ }
+
+ task "restore-db" {
+ lifecycle {
+ hook = "prestart"
+ sidecar = false
+ }
+
+ driver = "docker"
+ config {
+ image = "litestream/litestream:0.3.7"
+ args = [
+ "restore", "-config", "/etc/litestream.yml", "/ephemeral/grafana.db"
+ ]
+ volumes = [
+ "../alloc/data:/ephemeral",
+ "secrets/litestream.yml:/etc/litestream.yml"
+ ]
+ }
+ user = "472"
+
+ template {
+ data = file("../config/grafana-litestream.yml")
+ destination = "secrets/litestream.yml"
+ }
+
+ resources {
+ memory = 200
+ cpu = 1000
+ }
+ }
+
+ task "grafana" {
+ driver = "docker"
+ config {
+ image = "grafana/grafana:8.4.3"
+ network_mode = "host"
+ ports = [ "grafana" ]
+ volumes = [
+ "../alloc/data:/var/lib/grafana",
+ "secrets/elastic.yaml:/etc/grafana/provisioning/datasources/elastic.yaml"
+ ]
+ }
+
+ template {
+ data = file("../config/grafana/provisioning/datasources/elastic.yaml")
+ destination = "secrets/elastic.yaml"
+ }
+
+ template {
+ data = <<EOH
+GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource,grafana-piechart-panel,grafana-worldmap-panel,grafana-polystat-panel
+GF_SERVER_HTTP_PORT=3333
+EOH
+ destination = "secrets/env"
+ env = true
+ }
+
+ resources {
+ memory = 500
+ cpu = 100
+ }
+
+ service {
+ tags = [
+ "grafana",
+ "tricot grafana.staging.deuxfleurs.org",
+ ]
+ port = 3333
+ address_mode = "driver"
+ name = "grafana"
+ check {
+ type = "tcp"
+ port = 3333
+ address_mode = "driver"
+ interval = "60s"
+ timeout = "5s"
+ check_restart {
+ limit = 3
+ grace = "90s"
+ ignore_warnings = false
+ }
+ }
+ }
+ }
+
+ task "replicate-db" {
+ driver = "docker"
+ config {
+ image = "litestream/litestream:0.3.7"
+ args = [
+ "replicate", "-config", "/etc/litestream.yml"
+ ]
+ volumes = [
+ "../alloc/data:/ephemeral",
+ "secrets/litestream.yml:/etc/litestream.yml"
+ ]
+ }
+ user = "472"
+
+ template {
+ data = file("../config/grafana-litestream.yml")
+ destination = "secrets/litestream.yml"
+ }
+
+ resources {
+ memory = 200
+ cpu = 100
+ }
+ }
+ }
+}