Frappe Lending
A comprehensive loan management system built on Frappe Framework. 100% open source and customizable for managing loans, repayments, and lending operations.
Configuration
x-custom-image: &custom_image
image: ${IMAGE_NAME:-frappe/erpnext}:${VERSION:-v15}
pull_policy: ${PULL_POLICY:-always}
deploy:
restart_policy:
condition: always
services:
backend:
<<: *custom_image
entrypoint: ["bash", "-c"]
command:
- >
cd /home/frappe/frappe-bench;
if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then
echo "Installing lending app in Python environment...";
./env/bin/pip install -e apps/lending || echo "Failed to install lending";
fi;
echo "Waiting for site to be ready...";
export start=`date +%s`;
while [ ! -f "sites/sites.txt" ] || [ ! -d "sites/${SITE_NAME}" ] || [ ! -f "sites/${SITE_NAME}/site_config.json" ]; do
echo "Waiting for site ${SITE_NAME} to be created and ready...";
sleep 2;
if (( `date +%s`-start > 300 )); then
echo "Timeout waiting for site to be ready";
echo "Checking sites directory:";
ls -la sites/ || echo "sites directory not found";
echo "Checking sites.txt:";
cat sites/sites.txt 2>/dev/null || echo "sites.txt not found";
break;
fi
done;
if [ -f "sites/sites.txt" ] && [ -d "sites/${SITE_NAME}" ] && [ -f "sites/${SITE_NAME}/site_config.json" ]; then
echo "Site ${SITE_NAME} is ready!";
echo "sites.txt contents:";
cat sites/sites.txt;
echo "Verifying site is accessible via bench...";
bench --site ${SITE_NAME} list-apps > /dev/null 2>&1 && echo "Site is accessible via bench" || echo "Warning: Site accessibility check via bench failed";
echo "Site directory structure:";
ls -la sites/${SITE_NAME}/ | head -10;
if [ -n "${MAIN_DOMAIN}" ]; then
echo "Updating host_name and SSL settings to https://${MAIN_DOMAIN} (ensuring correct domain for emails)...";
bench --site ${SITE_NAME} set-config host_name "https://${MAIN_DOMAIN}" || echo "Warning: Could not update host_name";
bench --site ${SITE_NAME} set-config host_url "https://${MAIN_DOMAIN}" || echo "Warning: Could not update host_url";
bench --site ${SITE_NAME} set-config use_ssl 1 || echo "Warning: Could not set use_ssl";
echo "Verifying configuration...";
bench --site ${SITE_NAME} get-config host_name 2>/dev/null || echo "Could not read host_name (this is OK if site is not fully initialized)";
echo "Clearing cache to apply new domain settings...";
bench --site ${SITE_NAME} clear-cache || echo "Warning: Could not clear cache";
bench --site ${SITE_NAME} clear-website-cache || echo "Warning: Could not clear website cache";
fi;
else
echo "Warning: Site may not be fully ready, but starting backend anyway";
fi;
echo "Starting bench serve (production-ready)";
export PYTHONUNBUFFERED=1;
bench serve --port 8000 2>&1 | awk '!/127\.0\.0\.1.*400/ {print}' || bench serve --port 8000;
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
environment:
SITE_NAME: ${SITE_NAME}
FRAPPE_SITE_NAME_HEADER: ""
MAIN_DOMAIN: ${MAIN_DOMAIN}
depends_on:
configurator:
condition: service_completed_successfully
required: true
create-site:
condition: service_completed_successfully
required: true
healthcheck:
test:
- CMD
- wait-for-it
- '0.0.0.0:8000'
interval: 2s
timeout: 10s
retries: 30
frontend:
<<: *custom_image
entrypoint: ["bash", "-c"]
command:
- >
echo "Starting Nginx with port_in_redirect fix...";
nginx-entrypoint.sh &
NGINX_PID=$!;
sleep 3;
if [ -f /etc/nginx/conf.d/frappe.conf ]; then
if ! grep -q "port_in_redirect off" /etc/nginx/conf.d/frappe.conf; then
echo "Applying port_in_redirect fix to prevent :8080 in URLs...";
sed -i 's/listen 8080;/listen 8080;\n port_in_redirect off;\n absolute_redirect off;/' /etc/nginx/conf.d/frappe.conf;
echo "Nginx config updated - reloading...";
sleep 1;
nginx -s reload 2>/dev/null || echo "Nginx will use updated config on next reload";
else
echo "Nginx config already has port_in_redirect settings";
fi
else
echo "Warning: frappe.conf not found yet, will check again";
sleep 2;
if [ -f /etc/nginx/conf.d/frappe.conf ]; then
if ! grep -q "port_in_redirect off" /etc/nginx/conf.d/frappe.conf; then
sed -i 's/listen 8080;/listen 8080;\n port_in_redirect off;\n absolute_redirect off;/' /etc/nginx/conf.d/frappe.conf;
nginx -s reload 2>/dev/null || true;
fi
fi
fi;
wait $NGINX_PID
depends_on:
backend:
condition: service_started
required: true
websocket:
condition: service_started
required: true
environment:
BACKEND: backend:8000
FRAPPE_SITE_NAME_HEADER: ""
SOCKETIO: websocket:9000
UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1
UPSTREAM_REAL_IP_HEADER: X-Forwarded-For
UPSTREAM_REAL_IP_RECURSIVE: "off"
USE_X_FORWARDED_HOST: "1"
PROXY_FIX: "1"
X_FORWARDED_PROTO: "https"
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
healthcheck:
test:
- CMD-SHELL
- curl -fsS http://127.0.0.1:8080/login || exit 1
interval: 30s
timeout: 5s
retries: 3
queue-default:
<<: *custom_image
entrypoint: ["bash", "-c"]
command:
- >
cd /home/frappe/frappe-bench;
export start=`date +%s`;
while [ ! -d "apps/lending" ]; do
echo "Waiting for apps to be available...";
sleep 2;
if (( `date +%s`-start > 300 )); then
echo "Timeout waiting for apps";
break;
fi;
done;
if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then
echo "Installing lending app in Python environment...";
./env/bin/pip install -e apps/lending || echo "Failed to install lending";
fi;
exec bench worker --queue default
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
healthcheck:
test:
- CMD
- wait-for-it
- 'redis-queue:6379'
interval: 2s
timeout: 10s
retries: 30
depends_on:
configurator:
condition: service_completed_successfully
required: true
queue-long:
<<: *custom_image
entrypoint: ["bash", "-c"]
command:
- >
cd /home/frappe/frappe-bench;
export start=`date +%s`;
while [ ! -d "apps/lending" ]; do
echo "Waiting for apps to be available...";
sleep 2;
if (( `date +%s`-start > 300 )); then
echo "Timeout waiting for apps";
break;
fi;
done;
if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then
echo "Installing lending app in Python environment...";
./env/bin/pip install -e apps/lending || echo "Failed to install lending";
fi;
exec bench worker --queue long
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
healthcheck:
test:
- CMD
- wait-for-it
- 'redis-queue:6379'
interval: 2s
timeout: 10s
retries: 30
depends_on:
configurator:
condition: service_completed_successfully
required: true
queue-short:
<<: *custom_image
entrypoint: ["bash", "-c"]
command:
- >
cd /home/frappe/frappe-bench;
export start=`date +%s`;
while [ ! -d "apps/lending" ]; do
echo "Waiting for apps to be available...";
sleep 2;
if (( `date +%s`-start > 300 )); then
echo "Timeout waiting for apps";
break;
fi;
done;
if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then
echo "Installing lending app in Python environment...";
./env/bin/pip install -e apps/lending || echo "Failed to install lending";
fi;
exec bench worker --queue short
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
healthcheck:
test:
- CMD
- wait-for-it
- 'redis-queue:6379'
interval: 2s
timeout: 10s
retries: 30
depends_on:
configurator:
condition: service_completed_successfully
required: true
scheduler:
<<: *custom_image
entrypoint: ["bash", "-c"]
command:
- >
cd /home/frappe/frappe-bench;
export start=`date +%s`;
while [ ! -d "apps/lending" ]; do
echo "Waiting for apps to be available...";
sleep 2;
if (( `date +%s`-start > 300 )); then
echo "Timeout waiting for apps";
break;
fi;
done;
if [ -d "apps/lending" ] && ! ./env/bin/pip show lending > /dev/null 2>&1; then
echo "Installing lending app in Python environment...";
./env/bin/pip install -e apps/lending || echo "Failed to install lending";
fi;
exec bench schedule
healthcheck:
test:
- CMD
- wait-for-it
- 'redis-queue:6379'
interval: 2s
timeout: 10s
retries: 30
depends_on:
configurator:
condition: service_completed_successfully
required: true
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
websocket:
<<: *custom_image
healthcheck:
test:
- CMD
- wait-for-it
- '0.0.0.0:9000'
interval: 2s
timeout: 10s
retries: 30
command:
- node
- /home/frappe/frappe-bench/apps/frappe/socketio.js
depends_on:
configurator:
condition: service_completed_successfully
required: true
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
configurator:
<<: *custom_image
deploy:
mode: replicated
replicas: ${CONFIGURE:-0}
restart_policy:
condition: none
entrypoint: ["bash", "-c"]
command:
- >
[[ $${REGENERATE_APPS_TXT} == "1" ]] && ls -1 apps > sites/apps.txt;
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && exit 0;
bench set-config -g db_host $$DB_HOST;
bench set-config -gp db_port $$DB_PORT;
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
bench set-config -g redis_socketio "redis://$$REDIS_QUEUE";
bench set-config -gp socketio_port $$SOCKETIO_PORT;
environment:
DB_HOST: "${DB_HOST:-db}"
DB_PORT: "3306"
REDIS_CACHE: redis-cache:6379
REDIS_QUEUE: redis-queue:6379
SOCKETIO_PORT: "9000"
REGENERATE_APPS_TXT: "${REGENERATE_APPS_TXT:-0}"
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
create-site:
<<: *custom_image
deploy:
mode: replicated
replicas: ${CREATE_SITE:-0}
restart_policy:
condition: none
entrypoint: ["bash", "-c"]
command:
- >
wait-for-it -t 120 $$DB_HOST:$$DB_PORT;
wait-for-it -t 120 redis-cache:6379;
wait-for-it -t 120 redis-queue:6379;
export start=`date +%s`;
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
do
echo "Waiting for sites/common_site_config.json to be created";
sleep 5;
if (( `date +%s`-start > 120 )); then
echo "could not find sites/common_site_config.json with required keys";
exit 1
fi
done;
echo "sites/common_site_config.json found";
cd /home/frappe/frappe-bench;
if [ -d "sites/${SITE_NAME}" ] && [ -f "sites/${SITE_NAME}/site_config.json" ]; then
echo "Site ${SITE_NAME} already exists, skipping creation";
bench use $${SITE_NAME} || echo "Warning: Could not set current site";
echo "$${SITE_NAME}" > sites/sites.txt || echo "Warning: Could not update sites.txt";
bench set-config -g default_site $${SITE_NAME} || echo "Warning: Could not set default site";
if [ -n "$${ADMIN_PASSWORD}" ]; then
echo "Updating admin password for existing site...";
bench --site $${SITE_NAME} set-admin-password $${ADMIN_PASSWORD} || echo "Warning: Could not update admin password (may need to be done manually)";
fi;
if [ -n "$${MAIN_DOMAIN}" ]; then
echo "Setting host_name and SSL settings to https://${MAIN_DOMAIN} (with HTTPS)...";
bench --site $${SITE_NAME} set-config host_name "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_name";
bench --site $${SITE_NAME} set-config host_url "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_url";
bench --site $${SITE_NAME} set-config use_ssl 1 || echo "Warning: Could not set use_ssl";
echo "Clearing cache to apply new domain settings...";
bench --site $${SITE_NAME} clear-cache || echo "Warning: Could not clear cache";
bench --site $${SITE_NAME} clear-website-cache || echo "Warning: Could not clear website cache";
fi;
echo "Verifying existing site is accessible...";
bench --site $${SITE_NAME} list-apps > /dev/null 2>&1 && echo "Existing site is accessible and working" || echo "Warning: Site accessibility check failed";
exit 0;
fi;
if [ ! -d "apps/lending" ]; then
echo "Cloning lending app...";
git clone https://github.com/frappe/lending.git apps/lending || echo "Failed to clone lending app";
fi;
if [ -d "apps/lending" ]; then
echo "Installing lending app in Python environment...";
./env/bin/pip install -e apps/lending || echo "Failed to install lending app";
fi;
echo "Regenerating apps.txt to include all apps...";
ls -1 apps > sites/apps.txt || (echo "frappe" > sites/apps.txt && echo "lending" >> sites/apps.txt);
echo "apps.txt contents:";
cat sites/apps.txt;
bench set-mariadb-host $${DB_HOST:-db};
bench new-site --mariadb-user-host-login-scope='%' --admin-password=$${ADMIN_PASSWORD} --db-root-username=root --db-root-password=$${DB_ROOT_PASSWORD} $${INSTALL_APP_ARGS} $${SITE_NAME};
if [ ! -d "sites/${SITE_NAME}" ]; then
echo "ERROR: Site ${SITE_NAME} was not created successfully";
exit 1;
fi;
echo "Site ${SITE_NAME} created successfully";
bench use $${SITE_NAME};
echo "Current site set to: $${SITE_NAME}";
if [ -d "apps/lending" ]; then
echo "Installing lending app on site...";
bench --site $${SITE_NAME} install-app lending || echo "Lending app may already be installed or failed to install";
fi;
echo "Building assets for all apps (this ensures lending JS is available)...";
bench build --apps lending || echo "Asset build completed";
echo "Clearing all caches...";
bench --site $${SITE_NAME} clear-cache || echo "Cache cleared";
bench --site $${SITE_NAME} clear-website-cache || echo "Website cache cleared";
echo "Creating sites.txt for site resolution...";
echo "$${SITE_NAME}" > sites/sites.txt || echo "Warning: Could not create sites.txt";
echo "Site registered in sites.txt:";
cat sites/sites.txt || echo "sites.txt not found";
echo "Verifying site directory and config exist...";
if [ -d "sites/${SITE_NAME}" ] && [ -f "sites/${SITE_NAME}/site_config.json" ]; then
echo "Site directory and config verified successfully";
else
echo "Warning: Site directory or config file missing";
ls -la sites/${SITE_NAME}/ 2>/dev/null || echo "Site directory listing failed";
fi;
echo "Setting default site in common_site_config.json...";
bench set-config -g default_site $${SITE_NAME} || echo "Warning: Could not set default site";
if [ -n "$${MAIN_DOMAIN}" ]; then
echo "Setting host_name and SSL settings to https://${MAIN_DOMAIN} (with HTTPS)...";
bench --site $${SITE_NAME} set-config host_name "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_name";
bench --site $${SITE_NAME} set-config host_url "https://$${MAIN_DOMAIN}" || echo "Warning: Could not set host_url";
bench --site $${SITE_NAME} set-config use_ssl 1 || echo "Warning: Could not set use_ssl";
echo "Clearing cache to apply new domain settings...";
bench --site $${SITE_NAME} clear-cache || echo "Warning: Could not clear cache";
bench --site $${SITE_NAME} clear-website-cache || echo "Warning: Could not clear website cache";
fi;
echo "Verifying site is accessible...";
bench --site $${SITE_NAME} list-apps > /dev/null 2>&1 && echo "Site is accessible and working" || echo "Warning: Site accessibility check failed";
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
environment:
SITE_NAME: ${SITE_NAME}
MAIN_DOMAIN: ${main_domain}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
DB_HOST: ${DB_HOST:-db}
DB_PORT: "${DB_PORT:-3306}"
DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
INSTALL_APP_ARGS: ${INSTALL_APP_ARGS}
migration:
<<: *custom_image
deploy:
mode: replicated
replicas: ${MIGRATE:-0}
restart_policy:
condition: none
entrypoint: ["bash", "-c"]
command:
- >
curl -f http://${SITE_NAME}:8080/api/method/ping || echo "Site busy" && exit 0;
bench --site all set-config -p maintenance_mode 1;
bench --site all set-config -p pause_scheduler 1;
bench --site all migrate;
bench --site all set-config -p maintenance_mode 0;
bench --site all set-config -p pause_scheduler 0;
volumes:
- sites:/home/frappe/frappe-bench/sites
- apps:/home/frappe/frappe-bench/apps
db:
image: mariadb:10.6
deploy:
mode: replicated
replicas: ${ENABLE_DB:-0}
restart_policy:
condition: always
healthcheck:
test: mysqladmin ping -h localhost --password=${DB_ROOT_PASSWORD}
interval: 1s
retries: 20
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed
environment:
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
- MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
volumes:
- db-data:/var/lib/mysql
redis-cache:
deploy:
restart_policy:
condition: always
image: redis:6.2-alpine
volumes:
- redis-cache-data:/data
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 5s
timeout: 5s
retries: 3
redis-queue:
deploy:
restart_policy:
condition: always
image: redis:6.2-alpine
volumes:
- redis-queue-data:/data
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 5s
timeout: 5s
retries: 3
redis-socketio:
deploy:
restart_policy:
condition: always
image: redis:6.2-alpine
volumes:
- redis-socketio-data:/data
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 5s
timeout: 5s
retries: 3
volumes:
db-data:
redis-cache-data:
redis-queue-data:
redis-socketio-data:
sites:
driver_opts:
type: "${SITE_VOLUME_TYPE}"
o: "${SITE_VOLUME_OPTS}"
device: "${SITE_VOLUME_DEV}"
apps:[variables]
main_domain = "${domain}"
site_name = "frappe.lending"
db_root_password = "${password:32}"
admin_password = "${password:32}"
[config]
env = [
"SITE_NAME=${site_name}",
"ADMIN_PASSWORD=${admin_password}",
"DB_ROOT_PASSWORD=${db_root_password}",
"MIGRATE=1",
"ENABLE_DB=1",
"DB_HOST=db",
"CREATE_SITE=1",
"CONFIGURE=1",
"REGENERATE_APPS_TXT=1",
"INSTALL_APP_ARGS=--install-app erpnext --install-app lending",
"IMAGE_NAME=frappe/erpnext",
"VERSION=latest",
"FRAPPE_SITE_NAME_HEADER=",
"MAIN_DOMAIN=${main_domain}",
]
mounts = []
[[config.domains]]
serviceName = "frontend"
port = 8_080
host = "${main_domain}"Base64
To import this template in Dokploy: create a Compose service → Advanced → Base64 import and paste the content below:
ewogICJjb21wb3NlIjogIngtY3VzdG9tLWltYWdlOiAmY3VzdG9tX2ltYWdlXG4gIGltYWdlOiAke0lNQUdFX05BTUU6LWZyYXBwZS9lcnBuZXh0fToke1ZFUlNJT046LXYxNX1cbiAgcHVsbF9wb2xpY3k6ICR7UFVMTF9QT0xJQ1k6LWFsd2F5c31cbiAgZGVwbG95OlxuICAgIHJlc3RhcnRfcG9saWN5OlxuICAgICAgY29uZGl0aW9uOiBhbHdheXNcblxuc2VydmljZXM6XG4gIGJhY2tlbmQ6XG4gICAgPDw6ICpjdXN0b21faW1hZ2VcbiAgICBlbnRyeXBvaW50OiBbXCJiYXNoXCIsIFwiLWNcIl1cbiAgICBjb21tYW5kOlxuICAgICAgLSA+XG4gICAgICAgIGNkIC9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2g7XG4gICAgICAgIGlmIFsgLWQgXCJhcHBzL2xlbmRpbmdcIiBdICYmICEgLi9lbnYvYmluL3BpcCBzaG93IGxlbmRpbmcgPiAvZGV2L251bGwgMj4mMTsgdGhlblxuICAgICAgICAgIGVjaG8gXCJJbnN0YWxsaW5nIGxlbmRpbmcgYXBwIGluIFB5dGhvbiBlbnZpcm9ubWVudC4uLlwiO1xuICAgICAgICAgIC4vZW52L2Jpbi9waXAgaW5zdGFsbCAtZSBhcHBzL2xlbmRpbmcgfHwgZWNobyBcIkZhaWxlZCB0byBpbnN0YWxsIGxlbmRpbmdcIjtcbiAgICAgICAgZmk7XG4gICAgICAgIGVjaG8gXCJXYWl0aW5nIGZvciBzaXRlIHRvIGJlIHJlYWR5Li4uXCI7XG4gICAgICAgIGV4cG9ydCBzdGFydD1gZGF0ZSArJXNgO1xuICAgICAgICB3aGlsZSBbICEgLWYgXCJzaXRlcy9zaXRlcy50eHRcIiBdIHx8IFsgISAtZCBcInNpdGVzLyR7U0lURV9OQU1FfVwiIF0gfHwgWyAhIC1mIFwic2l0ZXMvJHtTSVRFX05BTUV9L3NpdGVfY29uZmlnLmpzb25cIiBdOyBkb1xuICAgICAgICAgIGVjaG8gXCJXYWl0aW5nIGZvciBzaXRlICR7U0lURV9OQU1FfSB0byBiZSBjcmVhdGVkIGFuZCByZWFkeS4uLlwiO1xuICAgICAgICAgIHNsZWVwIDI7XG4gICAgICAgICAgaWYgKCggYGRhdGUgKyVzYC1zdGFydCA+IDMwMCApKTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIlRpbWVvdXQgd2FpdGluZyBmb3Igc2l0ZSB0byBiZSByZWFkeVwiO1xuICAgICAgICAgICAgZWNobyBcIkNoZWNraW5nIHNpdGVzIGRpcmVjdG9yeTpcIjtcbiAgICAgICAgICAgIGxzIC1sYSBzaXRlcy8gfHwgZWNobyBcInNpdGVzIGRpcmVjdG9yeSBub3QgZm91bmRcIjtcbiAgICAgICAgICAgIGVjaG8gXCJDaGVja2luZyBzaXRlcy50eHQ6XCI7XG4gICAgICAgICAgICBjYXQgc2l0ZXMvc2l0ZXMudHh0IDI+L2Rldi9udWxsIHx8IGVjaG8gXCJzaXRlcy50eHQgbm90IGZvdW5kXCI7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBmaVxuICAgICAgICBkb25lO1xuICAgICAgICBpZiBbIC1mIFwic2l0ZXMvc2l0ZXMudHh0XCIgXSAmJiBbIC1kIFwic2l0ZXMvJHtTSVRFX05BTUV9XCIgXSAmJiBbIC1mIFwic2l0ZXMvJHtTSVRFX05BTUV9L3NpdGVfY29uZmlnLmpzb25cIiBdOyB0aGVuXG4gICAgICAgICAgZWNobyBcIlNpdGUgJHtTSVRFX05BTUV9IGlzIHJlYWR5IVwiO1xuICAgICAgICAgIGVjaG8gXCJzaXRlcy50eHQgY29udGVudHM6XCI7XG4gICAgICAgICAgY2F0IHNpdGVzL3NpdGVzLnR4dDtcbiAgICAgICAgICBlY2hvIFwiVmVyaWZ5aW5nIHNpdGUgaXMgYWNjZXNzaWJsZSB2aWEgYmVuY2guLi5cIjtcbiAgICAgICAgICBiZW5jaCAtLXNpdGUgJHtTSVRFX05BTUV9IGxpc3QtYXBwcyA+IC9kZXYvbnVsbCAyPiYxICYmIGVjaG8gXCJTaXRlIGlzIGFjY2Vzc2libGUgdmlhIGJlbmNoXCIgfHwgZWNobyBcIldhcm5pbmc6IFNpdGUgYWNjZXNzaWJpbGl0eSBjaGVjayB2aWEgYmVuY2ggZmFpbGVkXCI7XG4gICAgICAgICAgZWNobyBcIlNpdGUgZGlyZWN0b3J5IHN0cnVjdHVyZTpcIjtcbiAgICAgICAgICBscyAtbGEgc2l0ZXMvJHtTSVRFX05BTUV9LyB8IGhlYWQgLTEwO1xuICAgICAgICAgIGlmIFsgLW4gXCIke01BSU5fRE9NQUlOfVwiIF07IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJVcGRhdGluZyBob3N0X25hbWUgYW5kIFNTTCBzZXR0aW5ncyB0byBodHRwczovLyR7TUFJTl9ET01BSU59IChlbnN1cmluZyBjb3JyZWN0IGRvbWFpbiBmb3IgZW1haWxzKS4uLlwiO1xuICAgICAgICAgICAgYmVuY2ggLS1zaXRlICR7U0lURV9OQU1FfSBzZXQtY29uZmlnIGhvc3RfbmFtZSBcImh0dHBzOi8vJHtNQUlOX0RPTUFJTn1cIiB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHVwZGF0ZSBob3N0X25hbWVcIjtcbiAgICAgICAgICAgIGJlbmNoIC0tc2l0ZSAke1NJVEVfTkFNRX0gc2V0LWNvbmZpZyBob3N0X3VybCBcImh0dHBzOi8vJHtNQUlOX0RPTUFJTn1cIiB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHVwZGF0ZSBob3N0X3VybFwiO1xuICAgICAgICAgICAgYmVuY2ggLS1zaXRlICR7U0lURV9OQU1FfSBzZXQtY29uZmlnIHVzZV9zc2wgMSB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHNldCB1c2Vfc3NsXCI7XG4gICAgICAgICAgICBlY2hvIFwiVmVyaWZ5aW5nIGNvbmZpZ3VyYXRpb24uLi5cIjtcbiAgICAgICAgICAgIGJlbmNoIC0tc2l0ZSAke1NJVEVfTkFNRX0gZ2V0LWNvbmZpZyBob3N0X25hbWUgMj4vZGV2L251bGwgfHwgZWNobyBcIkNvdWxkIG5vdCByZWFkIGhvc3RfbmFtZSAodGhpcyBpcyBPSyBpZiBzaXRlIGlzIG5vdCBmdWxseSBpbml0aWFsaXplZClcIjtcbiAgICAgICAgICAgIGVjaG8gXCJDbGVhcmluZyBjYWNoZSB0byBhcHBseSBuZXcgZG9tYWluIHNldHRpbmdzLi4uXCI7XG4gICAgICAgICAgICBiZW5jaCAtLXNpdGUgJHtTSVRFX05BTUV9IGNsZWFyLWNhY2hlIHx8IGVjaG8gXCJXYXJuaW5nOiBDb3VsZCBub3QgY2xlYXIgY2FjaGVcIjtcbiAgICAgICAgICAgIGJlbmNoIC0tc2l0ZSAke1NJVEVfTkFNRX0gY2xlYXItd2Vic2l0ZS1jYWNoZSB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IGNsZWFyIHdlYnNpdGUgY2FjaGVcIjtcbiAgICAgICAgICBmaTtcbiAgICAgICAgZWxzZVxuICAgICAgICAgIGVjaG8gXCJXYXJuaW5nOiBTaXRlIG1heSBub3QgYmUgZnVsbHkgcmVhZHksIGJ1dCBzdGFydGluZyBiYWNrZW5kIGFueXdheVwiO1xuICAgICAgICBmaTtcbiAgICAgICAgZWNobyBcIlN0YXJ0aW5nIGJlbmNoIHNlcnZlIChwcm9kdWN0aW9uLXJlYWR5KVwiO1xuICAgICAgICBleHBvcnQgUFlUSE9OVU5CVUZGRVJFRD0xO1xuICAgICAgICBiZW5jaCBzZXJ2ZSAtLXBvcnQgODAwMCAyPiYxIHwgYXdrICchLzEyN1xcLjBcXC4wXFwuMS4qNDAwLyB7cHJpbnR9JyB8fCBiZW5jaCBzZXJ2ZSAtLXBvcnQgODAwMDtcbiAgICB2b2x1bWVzOlxuICAgICAgLSBzaXRlczovaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL3NpdGVzXG4gICAgICAtIGFwcHM6L2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaC9hcHBzXG4gICAgZW52aXJvbm1lbnQ6XG4gICAgICBTSVRFX05BTUU6ICR7U0lURV9OQU1FfVxuICAgICAgRlJBUFBFX1NJVEVfTkFNRV9IRUFERVI6IFwiXCJcbiAgICAgIE1BSU5fRE9NQUlOOiAke01BSU5fRE9NQUlOfVxuICAgIGRlcGVuZHNfb246XG4gICAgICBjb25maWd1cmF0b3I6XG4gICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9jb21wbGV0ZWRfc3VjY2Vzc2Z1bGx5XG4gICAgICAgIHJlcXVpcmVkOiB0cnVlXG4gICAgICBjcmVhdGUtc2l0ZTpcbiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2NvbXBsZXRlZF9zdWNjZXNzZnVsbHlcbiAgICAgICAgcmVxdWlyZWQ6IHRydWVcbiAgICBoZWFsdGhjaGVjazpcbiAgICAgIHRlc3Q6XG4gICAgICAgIC0gQ01EXG4gICAgICAgIC0gd2FpdC1mb3ItaXRcbiAgICAgICAgLSAnMC4wLjAuMDo4MDAwJ1xuICAgICAgaW50ZXJ2YWw6IDJzXG4gICAgICB0aW1lb3V0OiAxMHNcbiAgICAgIHJldHJpZXM6IDMwXG5cbiAgZnJvbnRlbmQ6XG4gICAgPDw6ICpjdXN0b21faW1hZ2VcbiAgICBlbnRyeXBvaW50OiBbXCJiYXNoXCIsIFwiLWNcIl1cbiAgICBjb21tYW5kOlxuICAgICAgLSA+XG4gICAgICAgIGVjaG8gXCJTdGFydGluZyBOZ2lueCB3aXRoIHBvcnRfaW5fcmVkaXJlY3QgZml4Li4uXCI7XG4gICAgICAgIG5naW54LWVudHJ5cG9pbnQuc2ggJlxuICAgICAgICBOR0lOWF9QSUQ9JCE7XG4gICAgICAgIHNsZWVwIDM7XG4gICAgICAgIGlmIFsgLWYgL2V0Yy9uZ2lueC9jb25mLmQvZnJhcHBlLmNvbmYgXTsgdGhlblxuICAgICAgICAgIGlmICEgZ3JlcCAtcSBcInBvcnRfaW5fcmVkaXJlY3Qgb2ZmXCIgL2V0Yy9uZ2lueC9jb25mLmQvZnJhcHBlLmNvbmY7IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJBcHBseWluZyBwb3J0X2luX3JlZGlyZWN0IGZpeCB0byBwcmV2ZW50IDo4MDgwIGluIFVSTHMuLi5cIjtcbiAgICAgICAgICAgIHNlZCAtaSAncy9saXN0ZW4gODA4MDsvbGlzdGVuIDgwODA7XFxuICAgIHBvcnRfaW5fcmVkaXJlY3Qgb2ZmO1xcbiAgICBhYnNvbHV0ZV9yZWRpcmVjdCBvZmY7LycgL2V0Yy9uZ2lueC9jb25mLmQvZnJhcHBlLmNvbmY7XG4gICAgICAgICAgICBlY2hvIFwiTmdpbnggY29uZmlnIHVwZGF0ZWQgLSByZWxvYWRpbmcuLi5cIjtcbiAgICAgICAgICAgIHNsZWVwIDE7XG4gICAgICAgICAgICBuZ2lueCAtcyByZWxvYWQgMj4vZGV2L251bGwgfHwgZWNobyBcIk5naW54IHdpbGwgdXNlIHVwZGF0ZWQgY29uZmlnIG9uIG5leHQgcmVsb2FkXCI7XG4gICAgICAgICAgZWxzZVxuICAgICAgICAgICAgZWNobyBcIk5naW54IGNvbmZpZyBhbHJlYWR5IGhhcyBwb3J0X2luX3JlZGlyZWN0IHNldHRpbmdzXCI7XG4gICAgICAgICAgZmlcbiAgICAgICAgZWxzZVxuICAgICAgICAgIGVjaG8gXCJXYXJuaW5nOiBmcmFwcGUuY29uZiBub3QgZm91bmQgeWV0LCB3aWxsIGNoZWNrIGFnYWluXCI7XG4gICAgICAgICAgc2xlZXAgMjtcbiAgICAgICAgICBpZiBbIC1mIC9ldGMvbmdpbngvY29uZi5kL2ZyYXBwZS5jb25mIF07IHRoZW5cbiAgICAgICAgICAgIGlmICEgZ3JlcCAtcSBcInBvcnRfaW5fcmVkaXJlY3Qgb2ZmXCIgL2V0Yy9uZ2lueC9jb25mLmQvZnJhcHBlLmNvbmY7IHRoZW5cbiAgICAgICAgICAgICAgc2VkIC1pICdzL2xpc3RlbiA4MDgwOy9saXN0ZW4gODA4MDtcXG4gICAgcG9ydF9pbl9yZWRpcmVjdCBvZmY7XFxuICAgIGFic29sdXRlX3JlZGlyZWN0IG9mZjsvJyAvZXRjL25naW54L2NvbmYuZC9mcmFwcGUuY29uZjtcbiAgICAgICAgICAgICAgbmdpbnggLXMgcmVsb2FkIDI+L2Rldi9udWxsIHx8IHRydWU7XG4gICAgICAgICAgICBmaVxuICAgICAgICAgIGZpXG4gICAgICAgIGZpO1xuICAgICAgICB3YWl0ICROR0lOWF9QSURcbiAgICBkZXBlbmRzX29uOlxuICAgICAgYmFja2VuZDpcbiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWRcbiAgICAgICAgcmVxdWlyZWQ6IHRydWVcbiAgICAgIHdlYnNvY2tldDpcbiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX3N0YXJ0ZWRcbiAgICAgICAgcmVxdWlyZWQ6IHRydWVcbiAgICBlbnZpcm9ubWVudDpcbiAgICAgIEJBQ0tFTkQ6IGJhY2tlbmQ6ODAwMFxuICAgICAgRlJBUFBFX1NJVEVfTkFNRV9IRUFERVI6IFwiXCJcbiAgICAgIFNPQ0tFVElPOiB3ZWJzb2NrZXQ6OTAwMFxuICAgICAgVVBTVFJFQU1fUkVBTF9JUF9BRERSRVNTOiAxMjcuMC4wLjFcbiAgICAgIFVQU1RSRUFNX1JFQUxfSVBfSEVBREVSOiBYLUZvcndhcmRlZC1Gb3JcbiAgICAgIFVQU1RSRUFNX1JFQUxfSVBfUkVDVVJTSVZFOiBcIm9mZlwiXG4gICAgICBVU0VfWF9GT1JXQVJERURfSE9TVDogXCIxXCJcbiAgICAgIFBST1hZX0ZJWDogXCIxXCJcbiAgICAgIFhfRk9SV0FSREVEX1BST1RPOiBcImh0dHBzXCJcbiAgICB2b2x1bWVzOlxuICAgICAgLSBzaXRlczovaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL3NpdGVzXG4gICAgICAtIGFwcHM6L2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaC9hcHBzXG4gICAgXG4gICAgaGVhbHRoY2hlY2s6XG4gICAgICB0ZXN0OlxuICAgICAgICAtIENNRC1TSEVMTFxuICAgICAgICAtIGN1cmwgLWZzUyBodHRwOi8vMTI3LjAuMC4xOjgwODAvbG9naW4gfHwgZXhpdCAxXG4gICAgICBpbnRlcnZhbDogMzBzXG4gICAgICB0aW1lb3V0OiA1c1xuICAgICAgcmV0cmllczogM1xuXG4gIHF1ZXVlLWRlZmF1bHQ6XG4gICAgPDw6ICpjdXN0b21faW1hZ2VcbiAgICBlbnRyeXBvaW50OiBbXCJiYXNoXCIsIFwiLWNcIl1cbiAgICBjb21tYW5kOlxuICAgICAgLSA+XG4gICAgICAgIGNkIC9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2g7XG4gICAgICAgIGV4cG9ydCBzdGFydD1gZGF0ZSArJXNgO1xuICAgICAgICB3aGlsZSBbICEgLWQgXCJhcHBzL2xlbmRpbmdcIiBdOyBkb1xuICAgICAgICAgIGVjaG8gXCJXYWl0aW5nIGZvciBhcHBzIHRvIGJlIGF2YWlsYWJsZS4uLlwiO1xuICAgICAgICAgIHNsZWVwIDI7XG4gICAgICAgICAgaWYgKCggYGRhdGUgKyVzYC1zdGFydCA+IDMwMCApKTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIlRpbWVvdXQgd2FpdGluZyBmb3IgYXBwc1wiO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgZmk7XG4gICAgICAgIGRvbmU7XG4gICAgICAgIGlmIFsgLWQgXCJhcHBzL2xlbmRpbmdcIiBdICYmICEgLi9lbnYvYmluL3BpcCBzaG93IGxlbmRpbmcgPiAvZGV2L251bGwgMj4mMTsgdGhlblxuICAgICAgICAgIGVjaG8gXCJJbnN0YWxsaW5nIGxlbmRpbmcgYXBwIGluIFB5dGhvbiBlbnZpcm9ubWVudC4uLlwiO1xuICAgICAgICAgIC4vZW52L2Jpbi9waXAgaW5zdGFsbCAtZSBhcHBzL2xlbmRpbmcgfHwgZWNobyBcIkZhaWxlZCB0byBpbnN0YWxsIGxlbmRpbmdcIjtcbiAgICAgICAgZmk7XG4gICAgICAgIGV4ZWMgYmVuY2ggd29ya2VyIC0tcXVldWUgZGVmYXVsdFxuICAgIHZvbHVtZXM6XG4gICAgICAtIHNpdGVzOi9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2gvc2l0ZXNcbiAgICAgIC0gYXBwczovaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL2FwcHNcbiAgICBoZWFsdGhjaGVjazpcbiAgICAgIHRlc3Q6XG4gICAgICAgIC0gQ01EXG4gICAgICAgIC0gd2FpdC1mb3ItaXRcbiAgICAgICAgLSAncmVkaXMtcXVldWU6NjM3OSdcbiAgICAgIGludGVydmFsOiAyc1xuICAgICAgdGltZW91dDogMTBzXG4gICAgICByZXRyaWVzOiAzMFxuICAgIGRlcGVuZHNfb246XG4gICAgICBjb25maWd1cmF0b3I6XG4gICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9jb21wbGV0ZWRfc3VjY2Vzc2Z1bGx5XG4gICAgICAgIHJlcXVpcmVkOiB0cnVlXG5cbiAgcXVldWUtbG9uZzpcbiAgICA8PDogKmN1c3RvbV9pbWFnZVxuICAgIGVudHJ5cG9pbnQ6IFtcImJhc2hcIiwgXCItY1wiXVxuICAgIGNvbW1hbmQ6XG4gICAgICAtID5cbiAgICAgICAgY2QgL2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaDtcbiAgICAgICAgZXhwb3J0IHN0YXJ0PWBkYXRlICslc2A7XG4gICAgICAgIHdoaWxlIFsgISAtZCBcImFwcHMvbGVuZGluZ1wiIF07IGRvXG4gICAgICAgICAgZWNobyBcIldhaXRpbmcgZm9yIGFwcHMgdG8gYmUgYXZhaWxhYmxlLi4uXCI7XG4gICAgICAgICAgc2xlZXAgMjtcbiAgICAgICAgICBpZiAoKCBgZGF0ZSArJXNgLXN0YXJ0ID4gMzAwICkpOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiVGltZW91dCB3YWl0aW5nIGZvciBhcHBzXCI7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBmaTtcbiAgICAgICAgZG9uZTtcbiAgICAgICAgaWYgWyAtZCBcImFwcHMvbGVuZGluZ1wiIF0gJiYgISAuL2Vudi9iaW4vcGlwIHNob3cgbGVuZGluZyA+IC9kZXYvbnVsbCAyPiYxOyB0aGVuXG4gICAgICAgICAgZWNobyBcIkluc3RhbGxpbmcgbGVuZGluZyBhcHAgaW4gUHl0aG9uIGVudmlyb25tZW50Li4uXCI7XG4gICAgICAgICAgLi9lbnYvYmluL3BpcCBpbnN0YWxsIC1lIGFwcHMvbGVuZGluZyB8fCBlY2hvIFwiRmFpbGVkIHRvIGluc3RhbGwgbGVuZGluZ1wiO1xuICAgICAgICBmaTtcbiAgICAgICAgZXhlYyBiZW5jaCB3b3JrZXIgLS1xdWV1ZSBsb25nXG4gICAgdm9sdW1lczpcbiAgICAgIC0gc2l0ZXM6L2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaC9zaXRlc1xuICAgICAgLSBhcHBzOi9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2gvYXBwc1xuICAgIGhlYWx0aGNoZWNrOlxuICAgICAgdGVzdDpcbiAgICAgICAgLSBDTURcbiAgICAgICAgLSB3YWl0LWZvci1pdFxuICAgICAgICAtICdyZWRpcy1xdWV1ZTo2Mzc5J1xuICAgICAgaW50ZXJ2YWw6IDJzXG4gICAgICB0aW1lb3V0OiAxMHNcbiAgICAgIHJldHJpZXM6IDMwXG4gICAgZGVwZW5kc19vbjpcbiAgICAgIGNvbmZpZ3VyYXRvcjpcbiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2NvbXBsZXRlZF9zdWNjZXNzZnVsbHlcbiAgICAgICAgcmVxdWlyZWQ6IHRydWVcblxuICBxdWV1ZS1zaG9ydDpcbiAgICA8PDogKmN1c3RvbV9pbWFnZVxuICAgIGVudHJ5cG9pbnQ6IFtcImJhc2hcIiwgXCItY1wiXVxuICAgIGNvbW1hbmQ6XG4gICAgICAtID5cbiAgICAgICAgY2QgL2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaDtcbiAgICAgICAgZXhwb3J0IHN0YXJ0PWBkYXRlICslc2A7XG4gICAgICAgIHdoaWxlIFsgISAtZCBcImFwcHMvbGVuZGluZ1wiIF07IGRvXG4gICAgICAgICAgZWNobyBcIldhaXRpbmcgZm9yIGFwcHMgdG8gYmUgYXZhaWxhYmxlLi4uXCI7XG4gICAgICAgICAgc2xlZXAgMjtcbiAgICAgICAgICBpZiAoKCBgZGF0ZSArJXNgLXN0YXJ0ID4gMzAwICkpOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiVGltZW91dCB3YWl0aW5nIGZvciBhcHBzXCI7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBmaTtcbiAgICAgICAgZG9uZTtcbiAgICAgICAgaWYgWyAtZCBcImFwcHMvbGVuZGluZ1wiIF0gJiYgISAuL2Vudi9iaW4vcGlwIHNob3cgbGVuZGluZyA+IC9kZXYvbnVsbCAyPiYxOyB0aGVuXG4gICAgICAgICAgZWNobyBcIkluc3RhbGxpbmcgbGVuZGluZyBhcHAgaW4gUHl0aG9uIGVudmlyb25tZW50Li4uXCI7XG4gICAgICAgICAgLi9lbnYvYmluL3BpcCBpbnN0YWxsIC1lIGFwcHMvbGVuZGluZyB8fCBlY2hvIFwiRmFpbGVkIHRvIGluc3RhbGwgbGVuZGluZ1wiO1xuICAgICAgICBmaTtcbiAgICAgICAgZXhlYyBiZW5jaCB3b3JrZXIgLS1xdWV1ZSBzaG9ydFxuICAgIHZvbHVtZXM6XG4gICAgICAtIHNpdGVzOi9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2gvc2l0ZXNcbiAgICAgIC0gYXBwczovaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL2FwcHNcbiAgICBoZWFsdGhjaGVjazpcbiAgICAgIHRlc3Q6XG4gICAgICAgIC0gQ01EXG4gICAgICAgIC0gd2FpdC1mb3ItaXRcbiAgICAgICAgLSAncmVkaXMtcXVldWU6NjM3OSdcbiAgICAgIGludGVydmFsOiAyc1xuICAgICAgdGltZW91dDogMTBzXG4gICAgICByZXRyaWVzOiAzMFxuICAgIGRlcGVuZHNfb246XG4gICAgICBjb25maWd1cmF0b3I6XG4gICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9jb21wbGV0ZWRfc3VjY2Vzc2Z1bGx5XG4gICAgICAgIHJlcXVpcmVkOiB0cnVlXG5cbiAgc2NoZWR1bGVyOlxuICAgIDw8OiAqY3VzdG9tX2ltYWdlXG4gICAgZW50cnlwb2ludDogW1wiYmFzaFwiLCBcIi1jXCJdXG4gICAgY29tbWFuZDpcbiAgICAgIC0gPlxuICAgICAgICBjZCAvaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoO1xuICAgICAgICBleHBvcnQgc3RhcnQ9YGRhdGUgKyVzYDtcbiAgICAgICAgd2hpbGUgWyAhIC1kIFwiYXBwcy9sZW5kaW5nXCIgXTsgZG9cbiAgICAgICAgICBlY2hvIFwiV2FpdGluZyBmb3IgYXBwcyB0byBiZSBhdmFpbGFibGUuLi5cIjtcbiAgICAgICAgICBzbGVlcCAyO1xuICAgICAgICAgIGlmICgoIGBkYXRlICslc2Atc3RhcnQgPiAzMDAgKSk7IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJUaW1lb3V0IHdhaXRpbmcgZm9yIGFwcHNcIjtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGZpO1xuICAgICAgICBkb25lO1xuICAgICAgICBpZiBbIC1kIFwiYXBwcy9sZW5kaW5nXCIgXSAmJiAhIC4vZW52L2Jpbi9waXAgc2hvdyBsZW5kaW5nID4gL2Rldi9udWxsIDI+JjE7IHRoZW5cbiAgICAgICAgICBlY2hvIFwiSW5zdGFsbGluZyBsZW5kaW5nIGFwcCBpbiBQeXRob24gZW52aXJvbm1lbnQuLi5cIjtcbiAgICAgICAgICAuL2Vudi9iaW4vcGlwIGluc3RhbGwgLWUgYXBwcy9sZW5kaW5nIHx8IGVjaG8gXCJGYWlsZWQgdG8gaW5zdGFsbCBsZW5kaW5nXCI7XG4gICAgICAgIGZpO1xuICAgICAgICBleGVjIGJlbmNoIHNjaGVkdWxlXG4gICAgaGVhbHRoY2hlY2s6XG4gICAgICB0ZXN0OlxuICAgICAgICAtIENNRFxuICAgICAgICAtIHdhaXQtZm9yLWl0XG4gICAgICAgIC0gJ3JlZGlzLXF1ZXVlOjYzNzknXG4gICAgICBpbnRlcnZhbDogMnNcbiAgICAgIHRpbWVvdXQ6IDEwc1xuICAgICAgcmV0cmllczogMzBcbiAgICBkZXBlbmRzX29uOlxuICAgICAgY29uZmlndXJhdG9yOlxuICAgICAgICBjb25kaXRpb246IHNlcnZpY2VfY29tcGxldGVkX3N1Y2Nlc3NmdWxseVxuICAgICAgICByZXF1aXJlZDogdHJ1ZVxuICAgIHZvbHVtZXM6XG4gICAgICAtIHNpdGVzOi9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2gvc2l0ZXNcbiAgICAgIC0gYXBwczovaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL2FwcHNcblxuICB3ZWJzb2NrZXQ6XG4gICAgPDw6ICpjdXN0b21faW1hZ2VcbiAgICBoZWFsdGhjaGVjazpcbiAgICAgIHRlc3Q6XG4gICAgICAgIC0gQ01EXG4gICAgICAgIC0gd2FpdC1mb3ItaXRcbiAgICAgICAgLSAnMC4wLjAuMDo5MDAwJ1xuICAgICAgaW50ZXJ2YWw6IDJzXG4gICAgICB0aW1lb3V0OiAxMHNcbiAgICAgIHJldHJpZXM6IDMwXG4gICAgY29tbWFuZDpcbiAgICAgIC0gbm9kZVxuICAgICAgLSAvaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL2FwcHMvZnJhcHBlL3NvY2tldGlvLmpzXG4gICAgZGVwZW5kc19vbjpcbiAgICAgIGNvbmZpZ3VyYXRvcjpcbiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2NvbXBsZXRlZF9zdWNjZXNzZnVsbHlcbiAgICAgICAgcmVxdWlyZWQ6IHRydWVcbiAgICB2b2x1bWVzOlxuICAgICAgLSBzaXRlczovaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL3NpdGVzXG4gICAgICAtIGFwcHM6L2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaC9hcHBzXG5cbiAgY29uZmlndXJhdG9yOlxuICAgIDw8OiAqY3VzdG9tX2ltYWdlXG4gICAgZGVwbG95OlxuICAgICAgbW9kZTogcmVwbGljYXRlZFxuICAgICAgcmVwbGljYXM6ICR7Q09ORklHVVJFOi0wfVxuICAgICAgcmVzdGFydF9wb2xpY3k6XG4gICAgICAgIGNvbmRpdGlvbjogbm9uZVxuICAgIGVudHJ5cG9pbnQ6IFtcImJhc2hcIiwgXCItY1wiXVxuICAgIGNvbW1hbmQ6XG4gICAgICAtID5cbiAgICAgICAgW1sgJCR7UkVHRU5FUkFURV9BUFBTX1RYVH0gPT0gXCIxXCIgXV0gJiYgbHMgLTEgYXBwcyA+IHNpdGVzL2FwcHMudHh0O1xuICAgICAgICBbWyAtbiBgZ3JlcCAtaHMgXiBzaXRlcy9jb21tb25fc2l0ZV9jb25maWcuanNvbiB8IGpxIC1yIFwiLmRiX2hvc3QgLy8gZW1wdHlcImAgXV0gJiYgZXhpdCAwO1xuICAgICAgICBiZW5jaCBzZXQtY29uZmlnIC1nIGRiX2hvc3QgJCREQl9IT1NUO1xuICAgICAgICBiZW5jaCBzZXQtY29uZmlnIC1ncCBkYl9wb3J0ICQkREJfUE9SVDtcbiAgICAgICAgYmVuY2ggc2V0LWNvbmZpZyAtZyByZWRpc19jYWNoZSBcInJlZGlzOi8vJCRSRURJU19DQUNIRVwiO1xuICAgICAgICBiZW5jaCBzZXQtY29uZmlnIC1nIHJlZGlzX3F1ZXVlIFwicmVkaXM6Ly8kJFJFRElTX1FVRVVFXCI7XG4gICAgICAgIGJlbmNoIHNldC1jb25maWcgLWcgcmVkaXNfc29ja2V0aW8gXCJyZWRpczovLyQkUkVESVNfUVVFVUVcIjtcbiAgICAgICAgYmVuY2ggc2V0LWNvbmZpZyAtZ3Agc29ja2V0aW9fcG9ydCAkJFNPQ0tFVElPX1BPUlQ7XG4gICAgZW52aXJvbm1lbnQ6XG4gICAgICBEQl9IT1NUOiBcIiR7REJfSE9TVDotZGJ9XCJcbiAgICAgIERCX1BPUlQ6IFwiMzMwNlwiXG4gICAgICBSRURJU19DQUNIRTogcmVkaXMtY2FjaGU6NjM3OVxuICAgICAgUkVESVNfUVVFVUU6IHJlZGlzLXF1ZXVlOjYzNzlcbiAgICAgIFNPQ0tFVElPX1BPUlQ6IFwiOTAwMFwiXG4gICAgICBSRUdFTkVSQVRFX0FQUFNfVFhUOiBcIiR7UkVHRU5FUkFURV9BUFBTX1RYVDotMH1cIlxuICAgIHZvbHVtZXM6XG4gICAgICAtIHNpdGVzOi9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2gvc2l0ZXNcbiAgICAgIC0gYXBwczovaG9tZS9mcmFwcGUvZnJhcHBlLWJlbmNoL2FwcHNcblxuICBjcmVhdGUtc2l0ZTpcbiAgICA8PDogKmN1c3RvbV9pbWFnZVxuICAgIGRlcGxveTpcbiAgICAgIG1vZGU6IHJlcGxpY2F0ZWRcbiAgICAgIHJlcGxpY2FzOiAke0NSRUFURV9TSVRFOi0wfVxuICAgICAgcmVzdGFydF9wb2xpY3k6XG4gICAgICAgIGNvbmRpdGlvbjogbm9uZVxuICAgIGVudHJ5cG9pbnQ6IFtcImJhc2hcIiwgXCItY1wiXVxuICAgIGNvbW1hbmQ6XG4gICAgICAtID5cbiAgICAgICAgd2FpdC1mb3ItaXQgLXQgMTIwICQkREJfSE9TVDokJERCX1BPUlQ7XG4gICAgICAgIHdhaXQtZm9yLWl0IC10IDEyMCByZWRpcy1jYWNoZTo2Mzc5O1xuICAgICAgICB3YWl0LWZvci1pdCAtdCAxMjAgcmVkaXMtcXVldWU6NjM3OTtcbiAgICAgICAgZXhwb3J0IHN0YXJ0PWBkYXRlICslc2A7XG4gICAgICAgIHVudGlsIFtbIC1uIGBncmVwIC1ocyBeIHNpdGVzL2NvbW1vbl9zaXRlX2NvbmZpZy5qc29uIHwganEgLXIgXCIuZGJfaG9zdCAvLyBlbXB0eVwiYCBdXSAmJiBcXFxuICAgICAgICAgIFtbIC1uIGBncmVwIC1ocyBeIHNpdGVzL2NvbW1vbl9zaXRlX2NvbmZpZy5qc29uIHwganEgLXIgXCIucmVkaXNfY2FjaGUgLy8gZW1wdHlcImAgXV0gJiYgXFxcbiAgICAgICAgICBbWyAtbiBgZ3JlcCAtaHMgXiBzaXRlcy9jb21tb25fc2l0ZV9jb25maWcuanNvbiB8IGpxIC1yIFwiLnJlZGlzX3F1ZXVlIC8vIGVtcHR5XCJgIF1dO1xuICAgICAgICBkb1xuICAgICAgICAgIGVjaG8gXCJXYWl0aW5nIGZvciBzaXRlcy9jb21tb25fc2l0ZV9jb25maWcuanNvbiB0byBiZSBjcmVhdGVkXCI7XG4gICAgICAgICAgc2xlZXAgNTtcbiAgICAgICAgICBpZiAoKCBgZGF0ZSArJXNgLXN0YXJ0ID4gMTIwICkpOyB0aGVuXG4gICAgICAgICAgICBlY2hvIFwiY291bGQgbm90IGZpbmQgc2l0ZXMvY29tbW9uX3NpdGVfY29uZmlnLmpzb24gd2l0aCByZXF1aXJlZCBrZXlzXCI7XG4gICAgICAgICAgICBleGl0IDFcbiAgICAgICAgICBmaVxuICAgICAgICBkb25lO1xuICAgICAgICBlY2hvIFwic2l0ZXMvY29tbW9uX3NpdGVfY29uZmlnLmpzb24gZm91bmRcIjtcbiAgICAgICAgY2QgL2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaDtcbiAgICAgICAgaWYgWyAtZCBcInNpdGVzLyR7U0lURV9OQU1FfVwiIF0gJiYgWyAtZiBcInNpdGVzLyR7U0lURV9OQU1FfS9zaXRlX2NvbmZpZy5qc29uXCIgXTsgdGhlblxuICAgICAgICAgIGVjaG8gXCJTaXRlICR7U0lURV9OQU1FfSBhbHJlYWR5IGV4aXN0cywgc2tpcHBpbmcgY3JlYXRpb25cIjtcbiAgICAgICAgICBiZW5jaCB1c2UgJCR7U0lURV9OQU1FfSB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHNldCBjdXJyZW50IHNpdGVcIjtcbiAgICAgICAgICBlY2hvIFwiJCR7U0lURV9OQU1FfVwiID4gc2l0ZXMvc2l0ZXMudHh0IHx8IGVjaG8gXCJXYXJuaW5nOiBDb3VsZCBub3QgdXBkYXRlIHNpdGVzLnR4dFwiO1xuICAgICAgICAgIGJlbmNoIHNldC1jb25maWcgLWcgZGVmYXVsdF9zaXRlICQke1NJVEVfTkFNRX0gfHwgZWNobyBcIldhcm5pbmc6IENvdWxkIG5vdCBzZXQgZGVmYXVsdCBzaXRlXCI7XG4gICAgICAgICAgaWYgWyAtbiBcIiQke0FETUlOX1BBU1NXT1JEfVwiIF07IHRoZW5cbiAgICAgICAgICAgIGVjaG8gXCJVcGRhdGluZyBhZG1pbiBwYXNzd29yZCBmb3IgZXhpc3Rpbmcgc2l0ZS4uLlwiO1xuICAgICAgICAgICAgYmVuY2ggLS1zaXRlICQke1NJVEVfTkFNRX0gc2V0LWFkbWluLXBhc3N3b3JkICQke0FETUlOX1BBU1NXT1JEfSB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHVwZGF0ZSBhZG1pbiBwYXNzd29yZCAobWF5IG5lZWQgdG8gYmUgZG9uZSBtYW51YWxseSlcIjtcbiAgICAgICAgICBmaTtcbiAgICAgICAgICBpZiBbIC1uIFwiJCR7TUFJTl9ET01BSU59XCIgXTsgdGhlblxuICAgICAgICAgICAgZWNobyBcIlNldHRpbmcgaG9zdF9uYW1lIGFuZCBTU0wgc2V0dGluZ3MgdG8gaHR0cHM6Ly8ke01BSU5fRE9NQUlOfSAod2l0aCBIVFRQUykuLi5cIjtcbiAgICAgICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IHNldC1jb25maWcgaG9zdF9uYW1lIFwiaHR0cHM6Ly8kJHtNQUlOX0RPTUFJTn1cIiB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHNldCBob3N0X25hbWVcIjtcbiAgICAgICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IHNldC1jb25maWcgaG9zdF91cmwgXCJodHRwczovLyQke01BSU5fRE9NQUlOfVwiIHx8IGVjaG8gXCJXYXJuaW5nOiBDb3VsZCBub3Qgc2V0IGhvc3RfdXJsXCI7XG4gICAgICAgICAgICBiZW5jaCAtLXNpdGUgJCR7U0lURV9OQU1FfSBzZXQtY29uZmlnIHVzZV9zc2wgMSB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHNldCB1c2Vfc3NsXCI7XG4gICAgICAgICAgICBlY2hvIFwiQ2xlYXJpbmcgY2FjaGUgdG8gYXBwbHkgbmV3IGRvbWFpbiBzZXR0aW5ncy4uLlwiO1xuICAgICAgICAgICAgYmVuY2ggLS1zaXRlICQke1NJVEVfTkFNRX0gY2xlYXItY2FjaGUgfHwgZWNobyBcIldhcm5pbmc6IENvdWxkIG5vdCBjbGVhciBjYWNoZVwiO1xuICAgICAgICAgICAgYmVuY2ggLS1zaXRlICQke1NJVEVfTkFNRX0gY2xlYXItd2Vic2l0ZS1jYWNoZSB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IGNsZWFyIHdlYnNpdGUgY2FjaGVcIjtcbiAgICAgICAgICBmaTtcbiAgICAgICAgICBlY2hvIFwiVmVyaWZ5aW5nIGV4aXN0aW5nIHNpdGUgaXMgYWNjZXNzaWJsZS4uLlwiO1xuICAgICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IGxpc3QtYXBwcyA+IC9kZXYvbnVsbCAyPiYxICYmIGVjaG8gXCJFeGlzdGluZyBzaXRlIGlzIGFjY2Vzc2libGUgYW5kIHdvcmtpbmdcIiB8fCBlY2hvIFwiV2FybmluZzogU2l0ZSBhY2Nlc3NpYmlsaXR5IGNoZWNrIGZhaWxlZFwiO1xuICAgICAgICAgIGV4aXQgMDtcbiAgICAgICAgZmk7XG4gICAgICAgIGlmIFsgISAtZCBcImFwcHMvbGVuZGluZ1wiIF07IHRoZW5cbiAgICAgICAgICBlY2hvIFwiQ2xvbmluZyBsZW5kaW5nIGFwcC4uLlwiO1xuICAgICAgICAgIGdpdCBjbG9uZSBodHRwczovL2dpdGh1Yi5jb20vZnJhcHBlL2xlbmRpbmcuZ2l0IGFwcHMvbGVuZGluZyB8fCBlY2hvIFwiRmFpbGVkIHRvIGNsb25lIGxlbmRpbmcgYXBwXCI7XG4gICAgICAgIGZpO1xuICAgICAgICBpZiBbIC1kIFwiYXBwcy9sZW5kaW5nXCIgXTsgdGhlblxuICAgICAgICAgIGVjaG8gXCJJbnN0YWxsaW5nIGxlbmRpbmcgYXBwIGluIFB5dGhvbiBlbnZpcm9ubWVudC4uLlwiO1xuICAgICAgICAgIC4vZW52L2Jpbi9waXAgaW5zdGFsbCAtZSBhcHBzL2xlbmRpbmcgfHwgZWNobyBcIkZhaWxlZCB0byBpbnN0YWxsIGxlbmRpbmcgYXBwXCI7XG4gICAgICAgIGZpO1xuICAgICAgICBlY2hvIFwiUmVnZW5lcmF0aW5nIGFwcHMudHh0IHRvIGluY2x1ZGUgYWxsIGFwcHMuLi5cIjtcbiAgICAgICAgbHMgLTEgYXBwcyA+IHNpdGVzL2FwcHMudHh0IHx8IChlY2hvIFwiZnJhcHBlXCIgPiBzaXRlcy9hcHBzLnR4dCAmJiBlY2hvIFwibGVuZGluZ1wiID4+IHNpdGVzL2FwcHMudHh0KTtcbiAgICAgICAgZWNobyBcImFwcHMudHh0IGNvbnRlbnRzOlwiO1xuICAgICAgICBjYXQgc2l0ZXMvYXBwcy50eHQ7XG4gICAgICAgIGJlbmNoIHNldC1tYXJpYWRiLWhvc3QgJCR7REJfSE9TVDotZGJ9O1xuICAgICAgICBiZW5jaCBuZXctc2l0ZSAtLW1hcmlhZGItdXNlci1ob3N0LWxvZ2luLXNjb3BlPSclJyAtLWFkbWluLXBhc3N3b3JkPSQke0FETUlOX1BBU1NXT1JEfSAtLWRiLXJvb3QtdXNlcm5hbWU9cm9vdCAtLWRiLXJvb3QtcGFzc3dvcmQ9JCR7REJfUk9PVF9QQVNTV09SRH0gJCR7SU5TVEFMTF9BUFBfQVJHU30gJCR7U0lURV9OQU1FfTtcbiAgICAgICAgaWYgWyAhIC1kIFwic2l0ZXMvJHtTSVRFX05BTUV9XCIgXTsgdGhlblxuICAgICAgICAgIGVjaG8gXCJFUlJPUjogU2l0ZSAke1NJVEVfTkFNRX0gd2FzIG5vdCBjcmVhdGVkIHN1Y2Nlc3NmdWxseVwiO1xuICAgICAgICAgIGV4aXQgMTtcbiAgICAgICAgZmk7XG4gICAgICAgIGVjaG8gXCJTaXRlICR7U0lURV9OQU1FfSBjcmVhdGVkIHN1Y2Nlc3NmdWxseVwiO1xuICAgICAgICBiZW5jaCB1c2UgJCR7U0lURV9OQU1FfTtcbiAgICAgICAgZWNobyBcIkN1cnJlbnQgc2l0ZSBzZXQgdG86ICQke1NJVEVfTkFNRX1cIjtcbiAgICAgICAgaWYgWyAtZCBcImFwcHMvbGVuZGluZ1wiIF07IHRoZW5cbiAgICAgICAgICBlY2hvIFwiSW5zdGFsbGluZyBsZW5kaW5nIGFwcCBvbiBzaXRlLi4uXCI7XG4gICAgICAgICAgYmVuY2ggLS1zaXRlICQke1NJVEVfTkFNRX0gaW5zdGFsbC1hcHAgbGVuZGluZyB8fCBlY2hvIFwiTGVuZGluZyBhcHAgbWF5IGFscmVhZHkgYmUgaW5zdGFsbGVkIG9yIGZhaWxlZCB0byBpbnN0YWxsXCI7XG4gICAgICAgIGZpO1xuICAgICAgICBlY2hvIFwiQnVpbGRpbmcgYXNzZXRzIGZvciBhbGwgYXBwcyAodGhpcyBlbnN1cmVzIGxlbmRpbmcgSlMgaXMgYXZhaWxhYmxlKS4uLlwiO1xuICAgICAgICBiZW5jaCBidWlsZCAtLWFwcHMgbGVuZGluZyB8fCBlY2hvIFwiQXNzZXQgYnVpbGQgY29tcGxldGVkXCI7XG4gICAgICAgIGVjaG8gXCJDbGVhcmluZyBhbGwgY2FjaGVzLi4uXCI7XG4gICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IGNsZWFyLWNhY2hlIHx8IGVjaG8gXCJDYWNoZSBjbGVhcmVkXCI7XG4gICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IGNsZWFyLXdlYnNpdGUtY2FjaGUgfHwgZWNobyBcIldlYnNpdGUgY2FjaGUgY2xlYXJlZFwiO1xuICAgICAgICBlY2hvIFwiQ3JlYXRpbmcgc2l0ZXMudHh0IGZvciBzaXRlIHJlc29sdXRpb24uLi5cIjtcbiAgICAgICAgZWNobyBcIiQke1NJVEVfTkFNRX1cIiA+IHNpdGVzL3NpdGVzLnR4dCB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IGNyZWF0ZSBzaXRlcy50eHRcIjtcbiAgICAgICAgZWNobyBcIlNpdGUgcmVnaXN0ZXJlZCBpbiBzaXRlcy50eHQ6XCI7XG4gICAgICAgIGNhdCBzaXRlcy9zaXRlcy50eHQgfHwgZWNobyBcInNpdGVzLnR4dCBub3QgZm91bmRcIjtcbiAgICAgICAgZWNobyBcIlZlcmlmeWluZyBzaXRlIGRpcmVjdG9yeSBhbmQgY29uZmlnIGV4aXN0Li4uXCI7XG4gICAgICAgIGlmIFsgLWQgXCJzaXRlcy8ke1NJVEVfTkFNRX1cIiBdICYmIFsgLWYgXCJzaXRlcy8ke1NJVEVfTkFNRX0vc2l0ZV9jb25maWcuanNvblwiIF07IHRoZW5cbiAgICAgICAgICBlY2hvIFwiU2l0ZSBkaXJlY3RvcnkgYW5kIGNvbmZpZyB2ZXJpZmllZCBzdWNjZXNzZnVsbHlcIjtcbiAgICAgICAgZWxzZVxuICAgICAgICAgIGVjaG8gXCJXYXJuaW5nOiBTaXRlIGRpcmVjdG9yeSBvciBjb25maWcgZmlsZSBtaXNzaW5nXCI7XG4gICAgICAgICAgbHMgLWxhIHNpdGVzLyR7U0lURV9OQU1FfS8gMj4vZGV2L251bGwgfHwgZWNobyBcIlNpdGUgZGlyZWN0b3J5IGxpc3RpbmcgZmFpbGVkXCI7XG4gICAgICAgIGZpO1xuICAgICAgICBlY2hvIFwiU2V0dGluZyBkZWZhdWx0IHNpdGUgaW4gY29tbW9uX3NpdGVfY29uZmlnLmpzb24uLi5cIjtcbiAgICAgICAgYmVuY2ggc2V0LWNvbmZpZyAtZyBkZWZhdWx0X3NpdGUgJCR7U0lURV9OQU1FfSB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHNldCBkZWZhdWx0IHNpdGVcIjtcbiAgICAgICAgaWYgWyAtbiBcIiQke01BSU5fRE9NQUlOfVwiIF07IHRoZW5cbiAgICAgICAgICBlY2hvIFwiU2V0dGluZyBob3N0X25hbWUgYW5kIFNTTCBzZXR0aW5ncyB0byBodHRwczovLyR7TUFJTl9ET01BSU59ICh3aXRoIEhUVFBTKS4uLlwiO1xuICAgICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IHNldC1jb25maWcgaG9zdF9uYW1lIFwiaHR0cHM6Ly8kJHtNQUlOX0RPTUFJTn1cIiB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHNldCBob3N0X25hbWVcIjtcbiAgICAgICAgICBiZW5jaCAtLXNpdGUgJCR7U0lURV9OQU1FfSBzZXQtY29uZmlnIGhvc3RfdXJsIFwiaHR0cHM6Ly8kJHtNQUlOX0RPTUFJTn1cIiB8fCBlY2hvIFwiV2FybmluZzogQ291bGQgbm90IHNldCBob3N0X3VybFwiO1xuICAgICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IHNldC1jb25maWcgdXNlX3NzbCAxIHx8IGVjaG8gXCJXYXJuaW5nOiBDb3VsZCBub3Qgc2V0IHVzZV9zc2xcIjtcbiAgICAgICAgICBlY2hvIFwiQ2xlYXJpbmcgY2FjaGUgdG8gYXBwbHkgbmV3IGRvbWFpbiBzZXR0aW5ncy4uLlwiO1xuICAgICAgICAgIGJlbmNoIC0tc2l0ZSAkJHtTSVRFX05BTUV9IGNsZWFyLWNhY2hlIHx8IGVjaG8gXCJXYXJuaW5nOiBDb3VsZCBub3QgY2xlYXIgY2FjaGVcIjtcbiAgICAgICAgICBiZW5jaCAtLXNpdGUgJCR7U0lURV9OQU1FfSBjbGVhci13ZWJzaXRlLWNhY2hlIHx8IGVjaG8gXCJXYXJuaW5nOiBDb3VsZCBub3QgY2xlYXIgd2Vic2l0ZSBjYWNoZVwiO1xuICAgICAgICBmaTtcbiAgICAgICAgZWNobyBcIlZlcmlmeWluZyBzaXRlIGlzIGFjY2Vzc2libGUuLi5cIjtcbiAgICAgICAgYmVuY2ggLS1zaXRlICQke1NJVEVfTkFNRX0gbGlzdC1hcHBzID4gL2Rldi9udWxsIDI+JjEgJiYgZWNobyBcIlNpdGUgaXMgYWNjZXNzaWJsZSBhbmQgd29ya2luZ1wiIHx8IGVjaG8gXCJXYXJuaW5nOiBTaXRlIGFjY2Vzc2liaWxpdHkgY2hlY2sgZmFpbGVkXCI7XG4gICAgdm9sdW1lczpcbiAgICAgIC0gc2l0ZXM6L2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaC9zaXRlc1xuICAgICAgLSBhcHBzOi9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2gvYXBwc1xuICAgIGVudmlyb25tZW50OlxuICAgICAgU0lURV9OQU1FOiAke1NJVEVfTkFNRX1cbiAgICAgIE1BSU5fRE9NQUlOOiAke21haW5fZG9tYWlufVxuICAgICAgQURNSU5fUEFTU1dPUkQ6ICR7QURNSU5fUEFTU1dPUkR9XG4gICAgICBEQl9IT1NUOiAke0RCX0hPU1Q6LWRifVxuICAgICAgREJfUE9SVDogXCIke0RCX1BPUlQ6LTMzMDZ9XCJcbiAgICAgIERCX1JPT1RfUEFTU1dPUkQ6ICR7REJfUk9PVF9QQVNTV09SRH1cbiAgICAgIElOU1RBTExfQVBQX0FSR1M6ICR7SU5TVEFMTF9BUFBfQVJHU31cblxuICBtaWdyYXRpb246XG4gICAgPDw6ICpjdXN0b21faW1hZ2VcbiAgICBkZXBsb3k6XG4gICAgICBtb2RlOiByZXBsaWNhdGVkXG4gICAgICByZXBsaWNhczogJHtNSUdSQVRFOi0wfVxuICAgICAgcmVzdGFydF9wb2xpY3k6XG4gICAgICAgIGNvbmRpdGlvbjogbm9uZVxuICAgIGVudHJ5cG9pbnQ6IFtcImJhc2hcIiwgXCItY1wiXVxuICAgIGNvbW1hbmQ6XG4gICAgICAtID5cbiAgICAgICAgY3VybCAtZiBodHRwOi8vJHtTSVRFX05BTUV9OjgwODAvYXBpL21ldGhvZC9waW5nIHx8IGVjaG8gXCJTaXRlIGJ1c3lcIiAmJiBleGl0IDA7XG4gICAgICAgIGJlbmNoIC0tc2l0ZSBhbGwgc2V0LWNvbmZpZyAtcCBtYWludGVuYW5jZV9tb2RlIDE7XG4gICAgICAgIGJlbmNoIC0tc2l0ZSBhbGwgc2V0LWNvbmZpZyAtcCBwYXVzZV9zY2hlZHVsZXIgMTtcbiAgICAgICAgYmVuY2ggLS1zaXRlIGFsbCBtaWdyYXRlO1xuICAgICAgICBiZW5jaCAtLXNpdGUgYWxsIHNldC1jb25maWcgLXAgbWFpbnRlbmFuY2VfbW9kZSAwO1xuICAgICAgICBiZW5jaCAtLXNpdGUgYWxsIHNldC1jb25maWcgLXAgcGF1c2Vfc2NoZWR1bGVyIDA7XG4gICAgdm9sdW1lczpcbiAgICAgIC0gc2l0ZXM6L2hvbWUvZnJhcHBlL2ZyYXBwZS1iZW5jaC9zaXRlc1xuICAgICAgLSBhcHBzOi9ob21lL2ZyYXBwZS9mcmFwcGUtYmVuY2gvYXBwc1xuXG4gIGRiOlxuICAgIGltYWdlOiBtYXJpYWRiOjEwLjZcbiAgICBkZXBsb3k6XG4gICAgICBtb2RlOiByZXBsaWNhdGVkXG4gICAgICByZXBsaWNhczogJHtFTkFCTEVfREI6LTB9XG4gICAgICByZXN0YXJ0X3BvbGljeTpcbiAgICAgICAgY29uZGl0aW9uOiBhbHdheXNcbiAgICBoZWFsdGhjaGVjazpcbiAgICAgIHRlc3Q6IG15c3FsYWRtaW4gcGluZyAtaCBsb2NhbGhvc3QgLS1wYXNzd29yZD0ke0RCX1JPT1RfUEFTU1dPUkR9XG4gICAgICBpbnRlcnZhbDogMXNcbiAgICAgIHJldHJpZXM6IDIwXG4gICAgY29tbWFuZDpcbiAgICAgIC0gLS1jaGFyYWN0ZXItc2V0LXNlcnZlcj11dGY4bWI0XG4gICAgICAtIC0tY29sbGF0aW9uLXNlcnZlcj11dGY4bWI0X3VuaWNvZGVfY2lcbiAgICAgIC0gLS1za2lwLWNoYXJhY3Rlci1zZXQtY2xpZW50LWhhbmRzaGFrZVxuICAgICAgLSAtLXNraXAtaW5ub2RiLXJlYWQtb25seS1jb21wcmVzc2VkXG4gICAgZW52aXJvbm1lbnQ6XG4gICAgICAtIE1ZU1FMX1JPT1RfUEFTU1dPUkQ9JHtEQl9ST09UX1BBU1NXT1JEfVxuICAgICAgLSBNQVJJQURCX1JPT1RfUEFTU1dPUkQ9JHtEQl9ST09UX1BBU1NXT1JEfVxuICAgIHZvbHVtZXM6XG4gICAgICAtIGRiLWRhdGE6L3Zhci9saWIvbXlzcWxcblxuICByZWRpcy1jYWNoZTpcbiAgICBkZXBsb3k6XG4gICAgICByZXN0YXJ0X3BvbGljeTpcbiAgICAgICAgY29uZGl0aW9uOiBhbHdheXNcbiAgICBpbWFnZTogcmVkaXM6Ni4yLWFscGluZVxuICAgIHZvbHVtZXM6XG4gICAgICAtIHJlZGlzLWNhY2hlLWRhdGE6L2RhdGFcbiAgICBoZWFsdGhjaGVjazpcbiAgICAgIHRlc3Q6XG4gICAgICAgIC0gQ01EXG4gICAgICAgIC0gcmVkaXMtY2xpXG4gICAgICAgIC0gcGluZ1xuICAgICAgaW50ZXJ2YWw6IDVzXG4gICAgICB0aW1lb3V0OiA1c1xuICAgICAgcmV0cmllczogM1xuXG4gIHJlZGlzLXF1ZXVlOlxuICAgIGRlcGxveTpcbiAgICAgIHJlc3RhcnRfcG9saWN5OlxuICAgICAgICBjb25kaXRpb246IGFsd2F5c1xuICAgIGltYWdlOiByZWRpczo2LjItYWxwaW5lXG4gICAgdm9sdW1lczpcbiAgICAgIC0gcmVkaXMtcXVldWUtZGF0YTovZGF0YVxuICAgIGhlYWx0aGNoZWNrOlxuICAgICAgdGVzdDpcbiAgICAgICAgLSBDTURcbiAgICAgICAgLSByZWRpcy1jbGlcbiAgICAgICAgLSBwaW5nXG4gICAgICBpbnRlcnZhbDogNXNcbiAgICAgIHRpbWVvdXQ6IDVzXG4gICAgICByZXRyaWVzOiAzXG5cbiAgcmVkaXMtc29ja2V0aW86XG4gICAgZGVwbG95OlxuICAgICAgcmVzdGFydF9wb2xpY3k6XG4gICAgICAgIGNvbmRpdGlvbjogYWx3YXlzXG4gICAgaW1hZ2U6IHJlZGlzOjYuMi1hbHBpbmVcbiAgICB2b2x1bWVzOlxuICAgICAgLSByZWRpcy1zb2NrZXRpby1kYXRhOi9kYXRhXG4gICAgaGVhbHRoY2hlY2s6XG4gICAgICB0ZXN0OlxuICAgICAgICAtIENNRFxuICAgICAgICAtIHJlZGlzLWNsaVxuICAgICAgICAtIHBpbmdcbiAgICAgIGludGVydmFsOiA1c1xuICAgICAgdGltZW91dDogNXNcbiAgICAgIHJldHJpZXM6IDNcblxudm9sdW1lczpcbiAgZGItZGF0YTpcbiAgcmVkaXMtY2FjaGUtZGF0YTpcbiAgcmVkaXMtcXVldWUtZGF0YTpcbiAgcmVkaXMtc29ja2V0aW8tZGF0YTpcbiAgc2l0ZXM6XG4gICAgZHJpdmVyX29wdHM6XG4gICAgICB0eXBlOiBcIiR7U0lURV9WT0xVTUVfVFlQRX1cIlxuICAgICAgbzogXCIke1NJVEVfVk9MVU1FX09QVFN9XCJcbiAgICAgIGRldmljZTogXCIke1NJVEVfVk9MVU1FX0RFVn1cIlxuICBhcHBzOlxuXG4iLAogICJjb25maWciOiAiW3ZhcmlhYmxlc11cbm1haW5fZG9tYWluID0gXCIke2RvbWFpbn1cIlxuc2l0ZV9uYW1lID0gXCJmcmFwcGUubGVuZGluZ1wiXG5kYl9yb290X3Bhc3N3b3JkID0gXCIke3Bhc3N3b3JkOjMyfVwiXG5hZG1pbl9wYXNzd29yZCA9IFwiJHtwYXNzd29yZDozMn1cIlxuXG5bY29uZmlnXVxuZW52ID0gW1xuICBcIlNJVEVfTkFNRT0ke3NpdGVfbmFtZX1cIixcbiAgXCJBRE1JTl9QQVNTV09SRD0ke2FkbWluX3Bhc3N3b3JkfVwiLFxuICBcIkRCX1JPT1RfUEFTU1dPUkQ9JHtkYl9yb290X3Bhc3N3b3JkfVwiLFxuICBcIk1JR1JBVEU9MVwiLFxuICBcIkVOQUJMRV9EQj0xXCIsXG4gIFwiREJfSE9TVD1kYlwiLFxuICBcIkNSRUFURV9TSVRFPTFcIixcbiAgXCJDT05GSUdVUkU9MVwiLFxuICBcIlJFR0VORVJBVEVfQVBQU19UWFQ9MVwiLFxuICBcIklOU1RBTExfQVBQX0FSR1M9LS1pbnN0YWxsLWFwcCBlcnBuZXh0IC0taW5zdGFsbC1hcHAgbGVuZGluZ1wiLFxuICBcIklNQUdFX05BTUU9ZnJhcHBlL2VycG5leHRcIixcbiAgXCJWRVJTSU9OPWxhdGVzdFwiLFxuICBcIkZSQVBQRV9TSVRFX05BTUVfSEVBREVSPVwiLFxuICBcIk1BSU5fRE9NQUlOPSR7bWFpbl9kb21haW59XCIsXG5dXG5tb3VudHMgPSBbXVxuXG5bW2NvbmZpZy5kb21haW5zXV1cbnNlcnZpY2VOYW1lID0gXCJmcm9udGVuZFwiXG5wb3J0ID0gOF8wODBcbmhvc3QgPSBcIiR7bWFpbl9kb21haW59XCJcbiIKfQ==Links
Tags
lending, finance, loans, payments, accounting, self-hosted
Version: latest
Frappe HR
Feature rich HR & Payroll software. 100% FOSS and customizable.
FreeScout
FreeScout is a free open source help desk and shared inbox system. It's a self-hosted alternative to HelpScout, Zendesk, and similar services that allows you to manage customer communications through email and a clean web interface. FreeScout makes it easy to organize support requests, track customer conversations, and collaborate with your team.