diff --git a/.gitignore b/.gitignore index ce904e9..f964293 100644 --- a/.gitignore +++ b/.gitignore @@ -22,11 +22,13 @@ docker-compose.override.yml bitcoin/* db/* electrs/* +nginx/* events/signals/* lnd/* logs/* statuses/* tor/* +app-data/* # Commit these files diff --git a/NETWORKING.md b/NETWORKING.md deleted file mode 100644 index aee54c3..0000000 --- a/NETWORKING.md +++ /dev/null @@ -1,19 +0,0 @@ -# Docker network setup - -This is the current network setup for docker-compose. You can also refer to them by name as well within the containers (eventually this will happen). An alternate mirror can be found [here](https://github.com/getumbrel/umbrel/wiki/Docker-Compose-networking). - -## Default configuration - -**Subnet mask:** 10.11.0.0/16 (10.11.* range for those who don't speak CIDR) - -Box | IP Address | ------------| -----------| -tor | 10.11.5.1 | -nginx | 10.11.0.2 | -bitcoin | 10.11.1.1 | -lnd | 10.11.1.2 | -dashboard | 10.11.0.3 | -manager | 10.11.2.1 | -middleware | 10.11.2.2 | -neutrino-switcher | 10.11.5.2 | -frontail | 10.11.3.0 | diff --git a/nginx/log/.gitkeep b/app-data/.gitkeep similarity index 100% rename from nginx/log/.gitkeep rename to app-data/.gitkeep diff --git a/apps/README.md b/apps/README.md new file mode 100644 index 0000000..aa8a443 --- /dev/null +++ b/apps/README.md @@ -0,0 +1,426 @@ +# Developing apps for Umbrel + +If you can code in any language, you already know how to develop an app for Umbrel. There is no restriction on the kind of programming languages, frameworks or databases that you can use. Apps run inside isolated Docker containers, and the only requirement (for now) is that they should have a web-based UI. + +> Some server apps might not have a UI at all. In that case, the app should serve a simple web page listing the connection details, QR codes, setup instructions, and anything else needed for the user to connect. The user is never expected to have CLI access on Umbrel. + +To keep this document short and easy, we won't go into the app development itself, and will instead focus on packaging an existing app. + +Let's straightaway jump into action by packaging [BTC RPC Explorer](https://github.com/janoside/btc-rpc-explorer), a Node.js based blockchain explorer, for Umbrel. + +There are 4 steps: + +1. [🛳 Containerizing the app using Docker](#1-containerizing-the-app-using-docker) +1. [☂️ Packaging the app for Umbrel](#2-%EF%B8%8Fpackaging-the-app-for-umbrel) +1. [🛠 Testing the app on Umbrel](#3-testing-the-app-on-umbrel) + 1. [Testing on Umbrel development environment (Linux or macOS)](#31-testing-the-app-on-umbrel-development-environment) + 1. [Testing on Umbrel OS (Raspberry Pi 4)](#32-testing-on-umbrel-os-raspberry-pi-4) +1. [🚀 Submitting the app](#4-submitting-the-app) + +___ + +## 1. 🛳  Containerizing the app using Docker + +1\. Let's start by cloning BTC RPC Explorer on our system: + +```sh +git clone --branch v2.0.2 https://github.com/janoside/btc-rpc-explorer.git +cd btc-rpc-explorer +``` + +2\. Next, we'll create a `Dockerfile` in the app's directory: + +```Dockerfile +FROM node:12-buster-slim AS builder + +WORKDIR /build +COPY . . +RUN apt-get update +RUN apt-get install -y git python3 build-essential +RUN npm ci --production + +FROM node:12-buster-slim + +USER 1000 +WORKDIR /build +COPY --from=builder /build . +EXPOSE 3002 +CMD ["npm", "start"] +``` + +### A good Dockerfile: + +- [x] Uses `debian:buster-slim` (or its derivatives, like `node:12-buster-slim`) as the base image — resulting in less storage consumption and faster app installs as the base image is already cached on the user's Umbrel. +- [x] Uses [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) for smaller image size. +- [x] Ensures development files are not included in the final image. +- [x] Has only one service per container. +- [x] Doesn't run the service as root. +- [x] Uses remote assets that are verified against a checksum. +- [x] Results in deterministic image builds. + +3\. We're now ready to build the Docker image of BTC RPC Explorer. Umbrel supports both 64-bit ARM and x86 architectures, so we'll use `docker buildx` to build, tag, and push multi-architecture Docker images of our app to Docker Hub. + +```sh +docker buildx build --platform linux/arm64,linux/amd64 --tag getumbrel/btc-rpc-explorer:v2.0.2 --output "type=registry" . +``` + +> You need to enable ["experimental features"](https://docs.docker.com/engine/reference/commandline/cli/#experimental-features) in Docker to use `docker buildx`. + +___ + +## 2. ☂️  Packaging the app for Umbrel + +1\. Let's fork the [getumbrel/umbrel](https://github.com/getumbrel/umbrel) repo on GitHub, clone our fork locally, create a new branch for our app, and then switch to it: + +```sh +git clone https://github.com//umbrel.git +cd umbrel +git checkout -b btc-rpc-explorer +``` + +2\. It's now time to decide an ID for our app. An app ID should only contain lowercase alphabetical characters and dashes, and should be humanly recognizable. For this app we'll go with `btc-rpc-explorer`. + +We need to create a new subdirectory in the apps directory with same name as our app ID and move into it: + +```sh +mkdir apps/btc-rpc-explorer +cd apps/btc-rpc-explorer +``` + +3\. We'll now create a `docker-compose.yml` file in this directory to define our application. + +> New to Docker Compose? It's a simple tool for defining and running Docker applications that can have multiple containers. Follow along the tutorial, we promise it's not hard if you already understand the basics of Docker. + +Let's copy-paste the following template `docker-compose.yml` file in a text editor and edit it according to our app. + +```yml +version: "3.7" + +x-logging: + &default-logging + driver: journald + options: + tag: "umbrel-app {{.Name}}" + +services: + web: + image: : + logging: *default-logging + restart: on-failure + stop_grace_period: 5m + ports: + # Replace with the port that your app's web server + # is listening inside the Docker container. If you need to + # expose more ports, add them below. + - : + volumes: + # Uncomment to mount your data directories inside + # the Docker container for storing persistent data + # - ${APP_DATA_DIR}/foo:/foo + # - ${APP_DATA_DIR}/bar:/bar + + # Uncomment to mount LND's data directory as read-only + # inside the Docker container at path /lnd + # - ${LND_DATA_DIR}:/lnd:ro + + # Uncomment to mount Bitcoin Core's data directory as + # read-only inside the Docker container at path /bitcoin + # - ${BITCOIN_DATA_DIR}:/bitcoin:ro + environment: + # Pass any environment variables to your app for configuration in the form: + # VARIABLE_NAME: value + # + # Here are all the Umbrel provided variables that you can pass through to + # your app to connect to Bitcoin Core, LND, Electrum and Tor: + # + # Bitcoin Core environment variables + # $BITCOIN_NETWORK - Can be "mainnet", "testnet" or "regtest" + # $BITCOIN_IP - Local IP of Bitcoin Core + # $BITCOIN_P2P_PORT - P2P port + # $BITCOIN_RPC_PORT - RPC port + # $BITCOIN_RPC_USER - RPC username + # $BITCOIN_RPC_PASS - RPC password + # $BITCOIN_RPC_AUTH - RPC auth string + # + # LND environment variables + # $LND_IP - Local IP of LND + # $LND_GRPC_PORT - gRPC Port of LND + # $LND_REST_PORT - REST Port of LND + # + # Electrum server environment variables + # $ELECTRUM_IP - Local IP of Electrum server + # $ELECTRUM_PORT - Port of Electrum server + # + # Tor proxy environment variables + # $TOR_PROXY_IP - Local IP of Tor proxy + # $TOR_PROXY_PORT - Port of Tor proxy + # If your app has more services, like a database container, you can define those + # services below: + # db: + # image: : + # ... + +``` + +4\. For our app, we'll update `` with `getumbrel/btc-rpc-explorer`, `` with `v2.0.2`, and `` with `3002`. Since BTC RPC Explorer doesn't need to store any persistent data and doesn't require access to Bitcoin Core's or LND's data directories, we can remove the entire `volumes` block. + +BTC RPC Explorer is an application with a single Docker container, so we don't need to define any other additional services (like a database service, etc) in the compose file. + +> If BTC RPC Explorer needed to persist some data we would have created a new `data` directory next to the `docker-compose.yml` file. We'd then mount the volume `- ${APP_DATA_DIR}/data:/data` in `docker-compose.yml` to make the directory available at `/data` inside the container. + +Updated `docker-compose.yml` file: + +```yml +version: "3.7" + +x-logging: + &default-logging + driver: journald + options: + tag: "umbrel-app {{.Name}}" + +services: + web: + image: getumbrel/btc-rpc-explorer:v2.0.2 + logging: *default-logging + restart: on-failure + stop_grace_period: 5m + ports: + - 3002:3002 + environment: + +``` + +5\. Next, let's set the environment variables required by our app to connect to Bitcoin Core, Electrum server, and for app-related configuration ([as required by the app](https://github.com/janoside/btc-rpc-explorer/blob/master/.env-sample)). + +So the final version of `docker-compose.yml` would be: + +```yml +version: "3.7" + +x-logging: + &default-logging + driver: journald + options: + tag: "umbrel-app {{.Name}}" + +services: + web: + image: getumbrel/btc-rpc-explorer:v2.0.2 + logging: *default-logging + restart: on-failure + stop_grace_period: 5m + ports: + - 3002:3002 + environment: + # Bitcoin Core connection details + BTCEXP_BITCOIND_HOST: $BITCOIN_IP + BTCEXP_BITCOIND_PORT: $BITCOIN_RPC_PORT + BTCEXP_BITCOIND_USER: $BITCOIN_RPC_USER + BTCEXP_BITCOIND_PASS: $BITCOIN_RPC_PASS + + # Electrum connection details + BTCEXP_ELECTRUMX_SERVERS: "tcp://$ELECTRUM_IP:$ELECTRUM_PORT" + + # App Config + BTCEXP_HOST: 0.0.0.0 + DEBUG: "btcexp:*,electrumClient" + BTCEXP_ADDRESS_API: electrumx + BTCEXP_SLOW_DEVICE_MODE: "true" + BTCEXP_NO_INMEMORY_RPC_CACHE: "true" + BTCEXP_PRIVACY_MODE: "true" + BTCEXP_NO_RATES: "true" + BTCEXP_RPC_ALLOWALL: "false" + BTCEXP_BASIC_AUTH_PASSWORD: "" + +``` + +6\. We're pretty much done here. The next step is to commit the changes, push it to our fork's branch, and test out the app on Umbrel. + +```sh +git add . +git commit -m "Add BTC RPC Explorer" +git push origin btc-rpc-explorer +``` + +___ + +## 3. 🛠  Testing the app on Umbrel + +### 3.1 Testing the app on Umbrel development environment + +Umbrel development environment ([`umbrel-dev`](https://github.com/getumbrel/umbrel-dev)) is a lightweight regtest instance of Umbrel that runs inside a virtual machine on your system. It's currently only compatible with Linux or macOS, so if you're on Windows, you may skip this section and directly test your app on a Raspberry Pi 4 running [Umbrel OS](https://github.com/getumbrel/umbrel-os). + +1\. First, we'll install the `umbrel-dev` CLI and it's dependencies [Virtual Box](https://www.virtualbox.org) and [Vagrant](https://vagrantup.com) on our system. If you use [Homebrew](https://brew.sh) you can do that with just: + +```sh +brew install lukechilds/tap/umbrel-dev +brew cask install virtualbox vagrant +``` + +2\. Now let's initialize our development environment and boot the VM: + +```sh +mkdir umbrel-dev +cd umbrel-dev +umbrel-dev init +umbrel-dev boot +``` + +> The first `umbrel-dev` boot usually takes a while due to the initial setup and configuration of the VM. Subsequent boots are much faster. + +After the VM has booted, we can verify if the Umbrel dashboard is accessible at http://umbrel-dev.local in our browser to make sure everything is running fine. + +3\. We need to switch the Umbrel installation on `umbrel-dev` to our fork and branch: + +```sh +cd getumbrel/umbrel +git remote add git@github.com:/umbrel.git +git fetch btc-rpc-explorer +git checkout /btc-rpc-explorer +``` + +4\. And finally, it's time to install our app: + +```sh +umbrel-dev app install btc-rpc-explorer +``` + +That's it! Our BTC RPC Explorer app should now be accessible at http://umbrel-dev.local:3002 + +5\. To make changes: + +Edit your app files at `getumbrel/umbrel/apps//` and then run: + +```sh +umbrel-dev reload +``` + +Once you're happy with your changes, just commit and push. + +>Don't forget to shutdown the `umbrel-dev` virtual machine after testing with `umbrel-dev shutdown`! + +### 3.2 Testing on Umbrel OS (Raspberry Pi 4) + +1\. We'll first install and run Umbrel OS on a Raspberry Pi 4. [Full instructions can be found here](https://getumbrel.com/#start). After installation, we'll set it up on http://umbrel.local, and then SSH into the Pi: + +```sh +ssh umbrel@umbrel.local +``` + +Password `moneyprintergobrrr` + +2\. Next, we'll switch the Umbrel installation to our fork and branch: + +```sh +sudo scripts/update/update --repo /umbrel#btc-rpc-explorer +``` + +3\. Once the installation has updated, it's time to test our app: + +```sh +scripts/app install btc-rpc-explorer +``` + +The app should now be accessible at http://umbrel.local:3002 + +4\. To uninstall: + +```sh +scripts/app uninstall btc-rpc-explorer +``` + +> When testing your app, make sure to verify that any application state that needs to be persisted is in-fact being persisted in volumes. +> +> A good way to test this is to restart the app with `scripts/app stop && scripts/app start `. If any state is lost, it means that state should be mapped to a persistent volume. +> +> When stopping/starting the app, all data in volumes will be persisted and anything else will be discarded. When uninstalling/installing an app, even persistent data will be discarded. + +___ + +## 4. 🚀  Submitting the app + +We're now ready to open a pull request on the main [getumbrel/umbrel](https://github.com/getumbrel/umbrel) repo to submit our app. Let's copy-paste the following markdown for the pull request description, fill it up with the required details, and then open a pull request. + +``` +# App Submission + +### App name +... + +### Version +... + +### One line description of the app +_(max 50 characters)_ + +... + +### Summary of the app +_(50 to 200 words)_ + +... + +### Developed by +... + +### Developer website +... + +### Source code repository +... + +### Support Link +_(Link to your Telegram support channel, GitHub issues/discussions, support portal, or any other place where users could contact you for support)_ + +... + +### Uses +- [ ] Bitcoin Core +- [ ] Electrum server +- [ ] LND + +### 256x256 SVG icon +_(GitHub doesn't allow uploadig SVGs directly. Upload your file to an alternate service, like https://svgur.com, and paste the link below.)_ + +... + +### App screenshots +_(Upload 3 to 5 high-quality screenshots (at least 1280x800px) of your app in PNG format.)_ + +... + + +### I have tested my app on: +- [ ] [Umbrel dev environment](https://github.com/getumbrel/umbrel-dev) +- [ ] [Umbrel OS on a Raspberry Pi 4](https://github.com/getumbrel/umbrel-os) +- [ ] [Custom Umbrel install on Linux](https://github.com/getumbrel/umbrel#-installation) +``` + +**Here's our actual pull request submitting the BTC RPC Explorer app — [getumbrel/umbrel#999](https://github.com/getumbrel/umbrel/pulls).** + +> After you've submitted your app, we'll review your pull request, create the required Tor hidden services for it, make some adjustments in the `docker-compose.yml` file, such as removing any port conflicts with other apps, pinning Docker images to their sha256 digests, assigning unique IP addresses to the containers, etc before merging. + +🎉 Congratulations! That's all you need to do to package, test and submit your app to Umbrel. We can't wait to have you onboard! + +--- + +## FAQs + +1. **How to push app updates?** + + Every time you release a new version of your app, you should build, tag and push the new Docker images to Docker Hub. Then open a new PR on our main repo (getumbrel/umbrel) with your up-to-date docker image. For now, app updates will be bundled together in the Umbrel releases. In the future, you'll be able to ship updates independently as soon as you make a new release. + +1. **How will users install/uninstall apps?** + + Users will install and uninstall the apps via dedicated UI in their dashboard. They will not use the `scripts/app` CLI directly as it's only meant for development use. + +1. **If I submit an app, will my PR be merged for sure?** + + For now, we're only accepting app submissions from developers who have received an invitation from us. Over time, we'll allow anyone to submit their app. + + > Need an invite to submit your app? Get in touch with [@mayankchhabra](https://t.me/mayankchhabra) or [@lukechilds](https://t.me/lukechilds) on Telegram. + +1. **I need help with something else?** + + Join our [developer chat](https://keybase.io/team/getumbrel) on Keybase, or get in touch with [@mayankchhabra](https://t.me/mayankchhabra) or [@lukechilds](https://t.me/lukechilds) on Telegram. diff --git a/apps/docker-compose.common.yml b/apps/docker-compose.common.yml new file mode 100644 index 0000000..24644af --- /dev/null +++ b/apps/docker-compose.common.yml @@ -0,0 +1,6 @@ +version: "3.7" + +networks: + default: + external: + name: umbrel_main_network diff --git a/apps/registry.json b/apps/registry.json new file mode 100644 index 0000000..0d4f101 --- /dev/null +++ b/apps/registry.json @@ -0,0 +1,2 @@ +[ +] diff --git a/docker-compose.yml b/docker-compose.yml index aeb554f..13befb0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,10 +16,10 @@ services: - ${PWD}/tor/data:/var/lib/tor/ - ${PWD}/tor/run:/var/run/tor/ ports: - - "127.0.0.1:9150:29050" + - "127.0.0.1:$TOR_PROXY_PORT:$TOR_PROXY_PORT" networks: - net: - ipv4_address: 10.11.5.1 + default: + ipv4_address: $TOR_PROXY_IP nginx: container_name: nginx image: nginx:1.17.8 @@ -32,8 +32,8 @@ services: ports: - "80:80" networks: - net: - ipv4_address: 10.11.0.2 + default: + ipv4_address: $NGINX_IP bitcoin: container_name: bitcoin image: lncm/bitcoind:v0.20.1 @@ -44,10 +44,10 @@ services: restart: on-failure stop_grace_period: 15m30s ports: - - "8333:8333" + - "$BITCOIN_P2P_PORT:$BITCOIN_P2P_PORT" networks: - net: - ipv4_address: 10.11.1.1 + default: + ipv4_address: $BITCOIN_IP lnd: container_name: lnd image: lncm/lnd:v0.11.1 @@ -59,11 +59,11 @@ services: stop_grace_period: 5m30s ports: - "9735:9735" - - "8080:8080" - - "10009:10009" + - "$LND_REST_PORT:$LND_REST_PORT" + - "$LND_GRPC_PORT:$LND_GRPC_PORT" networks: - net: - ipv4_address: 10.11.1.2 + default: + ipv4_address: $LND_IP dashboard: container_name: dashboard image: getumbrel/dashboard:v0.3.13 @@ -71,8 +71,8 @@ services: restart: on-failure stop_grace_period: 1m30s networks: - net: - ipv4_address: 10.11.0.3 + default: + ipv4_address: $DASHBOARD_IP manager: container_name: manager image: getumbrel/manager:v0.2.7 @@ -85,6 +85,7 @@ services: - ${PWD}/info.json:/info.json - ${PWD}/db:/db - ${PWD}/events/signals:/signals + - ${PWD}/apps:/apps - ${PWD}/lnd:/lnd:ro - ${PWD}/statuses:/statuses - ${PWD}/tor/data:/var/lib/tor/ @@ -101,7 +102,7 @@ services: DOCKER_COMPOSE_DIRECTORY: $PWD DEVICE_HOSTS: ${DEVICE_HOSTS:-"http://umbrel.local"} DEVICE_HOSTNAME: ${DEVICE_HOSTNAME:-""} - MIDDLEWARE_API_URL: "http://10.11.2.2" + MIDDLEWARE_API_URL: "http://$MIDDLEWARE_IP" UMBREL_SEED_FILE: "/db/umbrel-seed/seed" UMBREL_DASHBOARD_HIDDEN_SERVICE_FILE: "/var/lib/tor/web/hostname" BITCOIN_P2P_HIDDEN_SERVICE_FILE: "/var/lib/tor/bitcoin-p2p/hostname" @@ -122,17 +123,18 @@ services: UPDATE_SIGNAL_FILE: "/signals/update" UPDATE_LOCK_FILE: "/statuses/update-in-progress" BACKUP_STATUS_FILE: "/statuses/backup-status.json" - TOR_PROXY_IP: "10.11.5.1" - TOR_PROXY_PORT: "29050" + TOR_PROXY_IP: "${TOR_PROXY_IP}" + TOR_PROXY_PORT: "${TOR_PROXY_PORT}" + TOR_HIDDEN_SERVICE_DIR: "/var/lib/tor" networks: - net: - ipv4_address: 10.11.2.1 + default: + ipv4_address: $MANAGER_IP middleware: container_name: middleware image: getumbrel/middleware:v0.1.7 logging: *default-logging depends_on: [ manager, bitcoin, lnd ] - command: ["./wait-for-node-manager.sh", "10.11.2.1", "npm", "start"] + command: ["./wait-for-node-manager.sh", $MANAGER_IP, "npm", "start"] restart: on-failure depends_on: [ manager ] volumes: @@ -140,17 +142,17 @@ services: - jwt-public-key:/jwt-public-key environment: PORT: "3005" - BITCOIN_HOST: "10.11.1.1" + BITCOIN_HOST: $BITCOIN_IP RPC_PORT: $BITCOIN_RPC_PORT RPC_USER: $BITCOIN_RPC_USER RPC_PASSWORD: $BITCOIN_RPC_PASS LND_NETWORK: $BITCOIN_NETWORK - LND_HOST: "10.11.1.2" + LND_HOST: "${LND_IP}" JWT_PUBLIC_KEY_FILE: "/jwt-public-key/jwt.pem" DEVICE_HOSTS: ${DEVICE_HOSTS:-"http://umbrel.local"} networks: - net: - ipv4_address: 10.11.2.2 + default: + ipv4_address: $MIDDLEWARE_IP neutrino-switcher: container_name: neutrino-switcher image: getumbrel/neutrino-switcher:v1.0.3 @@ -162,14 +164,14 @@ services: - ${PWD}/statuses:/statuses - /var/run/docker.sock:/var/run/docker.sock environment: - JSONRPCURL: "http://10.11.1.1:${BITCOIN_RPC_PORT}" + JSONRPCURL: "http://${BITCOIN_IP}:${BITCOIN_RPC_PORT}" RPCUSER: $BITCOIN_RPC_USER RPCPASS: $BITCOIN_RPC_PASS LND_CONTAINER_NAME: lnd SLEEPTIME: 3600 networks: - net: - ipv4_address: 10.11.5.2 + default: + ipv4_address: $NEUTRINO_SWITCHER_IP frontail: container_name: frontail image: getumbrel/frontail:v4.9.1 @@ -179,8 +181,8 @@ services: volumes: - /var/log/syslog:/var/log/syslog:ro networks: - net: - ipv4_address: 10.11.3.0 + default: + ipv4_address: $FRONTAIL_IP electrs: container_name: electrs image: getumbrel/electrs:v0.8.6 @@ -192,16 +194,17 @@ services: restart: on-failure stop_grace_period: 5m ports: - - "50001:50001" + - "$ELECTRUM_PORT:$ELECTRUM_PORT" networks: - net: - ipv4_address: 10.11.4.0 + default: + ipv4_address: $ELECTRUM_IP networks: - net: - ipam: - driver: default - config: - - subnet: 10.11.0.0/16 + default: + name: umbrel_main_network + ipam: + driver: default + config: + - subnet: "${GATEWAY_IP}/16" volumes: jwt-public-key: diff --git a/events/triggers/app b/events/triggers/app new file mode 100755 index 0000000..b990a72 --- /dev/null +++ b/events/triggers/app @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" + +signal="${1}" +command=${signal%%"-"*} +app=${signal#*"-"} + +"${UMBREL_ROOT}/scripts/app" "${command}" "${app}" diff --git a/karen b/karen index 53c6d08..9fc577f 100755 --- a/karen +++ b/karen @@ -52,11 +52,20 @@ fswatch -0 --event=PlatformSpecific $signal_dir | while read -d "" event; do signal="${event#"$signal_dir"}" signal="${signal#"/"}" trigger="$trigger_dir/$signal" + args="" echo "Got signal: $signal" + + app_prefix="app-" + if [[ "$signal" == "$app_prefix"* ]]; then + # This is an app signal, let the app trigger handle it + trigger="$trigger_dir/app" + args="${signal#$app_prefix}" + fi + if test -x "$trigger"; then echo "karen is getting triggered!" - "$trigger" & + "$trigger" $args & else echo "No trigger found" fi diff --git a/nginx/.gitkeep b/nginx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/nginx/conf.d/default.conf b/nginx/conf.d/default.conf deleted file mode 100644 index 26a6e9a..0000000 --- a/nginx/conf.d/default.conf +++ /dev/null @@ -1,27 +0,0 @@ -server { - listen 80 default_server; - listen [::]:80 default_server; - server_name _; - - location /api/ { - proxy_pass http://10.11.2.2:3005/; - } - - location /manager-api/ { - proxy_pass http://10.11.2.1:3006/; - } - - location /logs { - proxy_pass http://10.11.3.0:9001/logs; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - - location / { - proxy_pass http://10.11.0.3:3004/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } -} diff --git a/nginx/mime.types b/nginx/mime.types deleted file mode 100644 index 0d039d8..0000000 --- a/nginx/mime.types +++ /dev/null @@ -1,139 +0,0 @@ -types { - - # Data interchange - - application/atom+xml atom; - application/json json map topojson; - application/ld+json jsonld; - application/rss+xml rss; - # Normalize to standard type. - # https://tools.ietf.org/html/rfc7946#section-12 - application/geo+json geojson; - application/xml xml; - # Normalize to standard type. - # https://tools.ietf.org/html/rfc3870#section-2 - application/rdf+xml rdf; - - - # JavaScript - - # Servers should use text/javascript for JavaScript resources. - # https://html.spec.whatwg.org/multipage/scripting.html#scriptingLanguages - text/javascript js mjs; - application/wasm wasm; - - - # Manifest files - - application/manifest+json webmanifest; - application/x-web-app-manifest+json webapp; - text/cache-manifest appcache; - - - # Media files - - audio/midi mid midi kar; - audio/mp4 aac f4a f4b m4a; - audio/mpeg mp3; - audio/ogg oga ogg opus; - audio/x-realaudio ra; - audio/x-wav wav; - audio/x-matroska mka; - image/bmp bmp; - image/gif gif; - image/jpeg jpeg jpg; - image/jxr jxr hdp wdp; - image/png png; - image/svg+xml svg svgz; - image/tiff tif tiff; - image/vnd.wap.wbmp wbmp; - image/webp webp; - image/x-jng jng; - video/3gpp 3gp 3gpp; - video/mp4 f4p f4v m4v mp4; - video/mpeg mpeg mpg; - video/ogg ogv; - video/quicktime mov; - video/webm webm; - video/x-flv flv; - video/x-mng mng; - video/x-ms-asf asf asx; - video/x-ms-wmv wmv; - video/x-msvideo avi; - video/x-matroska mkv mk3d; - - # Serving `.ico` image files with a different media type - # prevents Internet Explorer from displaying then as images: - # https://github.com/h5bp/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee - - image/x-icon cur ico; - - - # Microsoft Office - - application/msword doc; - application/vnd.ms-excel xls; - application/vnd.ms-powerpoint ppt; - application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; - application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; - application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; - - - # Web fonts - - font/woff woff; - font/woff2 woff2; - application/vnd.ms-fontobject eot; - font/ttf ttf; - font/collection ttc; - font/otf otf; - - - # Other - - application/java-archive ear jar war; - application/mac-binhex40 hqx; - application/octet-stream bin deb dll dmg exe img iso msi msm msp safariextz; - application/pdf pdf; - application/postscript ai eps ps; - application/rtf rtf; - application/vnd.google-earth.kml+xml kml; - application/vnd.google-earth.kmz kmz; - application/vnd.wap.wmlc wmlc; - application/x-7z-compressed 7z; - application/x-bb-appworld bbaw; - application/x-bittorrent torrent; - application/x-chrome-extension crx; - application/x-cocoa cco; - application/x-java-archive-diff jardiff; - application/x-java-jnlp-file jnlp; - application/x-makeself run; - application/x-opera-extension oex; - application/x-perl pl pm; - application/x-pilot pdb prc; - application/x-rar-compressed rar; - application/x-redhat-package-manager rpm; - application/x-sea sea; - application/x-shockwave-flash swf; - application/x-stuffit sit; - application/x-tcl tcl tk; - application/x-x509-ca-cert crt der pem; - application/x-xpinstall xpi; - application/xhtml+xml xhtml; - application/xslt+xml xsl; - application/zip zip; - text/calendar ics; - text/css css; - text/csv csv; - text/html htm html shtml; - text/markdown md markdown; - text/mathml mml; - text/plain txt; - text/vcard vcard vcf; - text/vnd.rim.location.xloc xloc; - text/vnd.sun.j2me.app-descriptor jad; - text/vnd.wap.wml wml; - text/vtt vtt; - text/x-component htc; - -} diff --git a/nginx/nginx.conf b/nginx/nginx.conf deleted file mode 100644 index 0955b51..0000000 --- a/nginx/nginx.conf +++ /dev/null @@ -1,23 +0,0 @@ -user nginx; -worker_processes 1; - -error_log /etc/nginx/log/error.log warn; -pid /etc/nginx/log/nginx.pid; - -events { - worker_connections 1337; -} -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /etc/nginx/log/access.log main; - sendfile on; - keepalive_timeout 65; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/nginx/www/index.html b/nginx/www/index.html deleted file mode 100644 index 3d9fb51..0000000 --- a/nginx/www/index.html +++ /dev/null @@ -1 +0,0 @@ -This is the test of the NGINX container system diff --git a/scripts/app b/scripts/app new file mode 100755 index 0000000..58bb1fe --- /dev/null +++ b/scripts/app @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +set -euo pipefail + +UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)" +USER_FILE="${UMBREL_ROOT}/db/user.json" + +show_help() { + cat << EOF +app 0.0.1 + +CLI for managing Umbrel apps + +Usage: app [] + +Commands: + install Pulls down images for an app and starts it + uninstall Removes images and destroys all data for an app + stop Stops an installed app + start Starts an installed app + compose Passes all arguments to docker-compose +EOF +} + +check_dependencies () { + for cmd in "$@"; do + if ! command -v $cmd >/dev/null 2>&1; then + echo "This script requires \"${cmd}\" to be installed" + exit 1 + fi + done +} + +list_installed_apps() { + cat "${USER_FILE}" | jq -r 'if has("installedApps") then .installedApps else [] end | join("\n")' || true +} + +# Check dependencies +check_dependencies docker-compose jq + +if [ -z ${1+x} ]; then + command="" +else + command="$1" +fi + +if [ -z ${2+x} ]; then + show_help + exit 1 +else + app="$2" + app_dir="${UMBREL_ROOT}/apps/${app}" + app_data_dir="${UMBREL_ROOT}/app-data/${app}" + if [[ "${app}" == "installed" ]]; then + list_installed_apps | while read app; do + if [[ "${app}" != "" ]]; then + "${0}" "${1}" "${app}" "${@:3}" || true + fi + done + exit + fi + if [[ -z "${app}" ]] || [[ ! -d "${app_dir}" ]]; then + echo "Error: \"${app}\" is not a valid app" + exit 1 + fi +fi + +if [ -z ${3+x} ]; then + args="" +else + args="${@:3}" +fi + +compose() { + local app="${1}" + shift + local env_file="${UMBREL_ROOT}/.env" + local app_base_compose_file="${UMBREL_ROOT}/apps/docker-compose.common.yml" + local app_compose_file="${app_dir}/docker-compose.yml" + + export BITCOIN_DATA_DIR="${UMBREL_ROOT}/bitcoin" + export LND_DATA_DIR="${UMBREL_ROOT}/lnd" + export APP_DATA_DIR="${app_data_dir}" + docker-compose \ + --env-file "${env_file}" \ + --file "${app_base_compose_file}" \ + --file "${app_compose_file}" \ + "${@}" +} + +update_installed_apps() { + local action="${1}" + local app="${2}" + [[ "${action}" == "add" ]] && operator="+" || operator="-" + updated_json=$(cat "${USER_FILE}" | jq ".installedApps |= (. ${operator} [\"${app}\"] | unique)") + echo "${updated_json}" > "${USER_FILE}" +} + +# Pulls down images for an app and starts it +if [[ "$command" = "install" ]]; then + + echo "Pulling images for ${app}..." + compose "${app}" pull + + echo "Setting up data dir for ${app}..." + mkdir -p "${app_data_dir}" + cp -a "${app_dir}/." "${app_data_dir}" + + echo "Starting app ${app}..." + compose "${app}" up --detach + + echo "Saving installed app in DB" + update_installed_apps add "${app}" + + exit +fi + +# Removes images and destroys all data for an app +if [[ "$command" = "uninstall" ]]; then + + echo "Removing app images" + compose "${app}" down --rmi all --remove-orphans + + echo "Deleting app data for ${app}..." + if [[ -d "${app_data_dir}" ]]; then + rm -rf "${app_data_dir}" + fi + + echo "Removing installed app from DB" + update_installed_apps remove "${app}" + + exit +fi + +# Stops an installed app +if [[ "$command" = "stop" ]]; then + + echo "Stopping app ${app}..." + compose "${app}" rm --force --stop + + exit +fi + +# Starts an installed app +if [[ "$command" = "start" ]]; then + + if ! list_installed_apps | grep --quiet "^${app}$"; then + echo "Error: \"${app}\" is not installed yet" + exit 1 + fi + + echo "Starting app ${app}..." + compose "${app}" up --detach + + exit +fi + +# Passes all arguments to docker-compose +if [[ "$command" = "compose" ]]; then + compose "${app}" ${args} + + exit +fi + +# If we get here it means no valid command was supplied +# Show help and exit +show_help +exit 1 diff --git a/scripts/backup/backup b/scripts/backup/backup index 74f7d17..f27aa49 100755 --- a/scripts/backup/backup +++ b/scripts/backup/backup @@ -104,7 +104,7 @@ if [[ $BITCOIN_NETWORK == "regtest" ]]; then fi echo "Uploading backup..." -if curl --socks5 localhost:9150 -F "file=@/${BACKUP_FILE}" "${BACKUP_API_URL}/${backup_id}"; then +if curl --socks5 "localhost:${TOR_PROXY_PORT}" -F "file=@/${BACKUP_FILE}" "${BACKUP_API_URL}/${backup_id}"; then status="success" else status="failed" diff --git a/scripts/configure b/scripts/configure index 9d7ea35..f0f5dfc 100755 --- a/scripts/configure +++ b/scripts/configure @@ -68,6 +68,7 @@ echo ########################################################## # Store paths to intermediary config files +NGINX_CONF_FILE="./templates/nginx.conf" BITCOIN_CONF_FILE="./templates/bitcoin.conf" LND_CONF_FILE="./templates/lnd.conf" TOR_CONF_FILE="./templates/torrc" @@ -76,6 +77,7 @@ ENV_FILE="./templates/.env" # Remove intermediary files if they exist from any # previous unclean configuration run +[[ -f "$NGINX_CONF_FILE" ]] && rm -f "$NGINX_CONF_FILE" [[ -f "$BITCOIN_CONF_FILE" ]] && rm -f "$BITCOIN_CONF_FILE" [[ -f "$LND_CONF_FILE" ]] && rm -f "$LND_CONF_FILE" [[ -f "$TOR_CONF_FILE" ]] && rm -f "$TOR_CONF_FILE" @@ -83,6 +85,7 @@ ENV_FILE="./templates/.env" [[ -f "$ENV_FILE" ]] && rm -f "$ENV_FILE" # Copy template configs to intermediary configs +[[ -f "./templates/nginx-sample.conf" ]] && cp "./templates/nginx-sample.conf" "$NGINX_CONF_FILE" [[ -f "./templates/bitcoin-sample.conf" ]] && cp "./templates/bitcoin-sample.conf" "$BITCOIN_CONF_FILE" [[ -f "./templates/lnd-sample.conf" ]] && cp "./templates/lnd-sample.conf" "$LND_CONF_FILE" [[ -f "./templates/torrc-sample" ]] && cp "./templates/torrc-sample" "$TOR_CONF_FILE" @@ -98,9 +101,26 @@ ENV_FILE="./templates/.env" [[ -f "./.env" ]] && source "./.env" [[ ! -z ${PREV_ENV_FILE+x} ]] && [[ -f "${PREV_ENV_FILE}" ]] && source "${PREV_ENV_FILE}" -# Bitcoin Core ports -BITCOIN_RPC_PORT=8332 -BITCOIN_P2P_PORT=8333 +# Umbrel +GATEWAY_IP="10.0.0.1" +NGINX_IP="10.0.0.2" +DASHBOARD_IP="10.0.0.3" +MANAGER_IP="10.0.0.4" +MIDDLEWARE_IP="10.0.0.5" +NEUTRINO_SWITCHER_IP="10.0.0.6" +FRONTAIL_IP="10.0.0.7" +BITCOIN_IP="10.0.0.8" +BITCOIN_RPC_PORT="8332" +BITCOIN_P2P_PORT="8333" +LND_IP="10.0.0.9" +LND_GRPC_PORT="10009" +LND_REST_PORT="8080" +ELECTRUM_IP="10.0.0.10" +ELECTRUM_PORT="50001" +TOR_PROXY_IP="10.0.0.11" +TOR_PROXY_PORT="9050" + +# Apps # Generate RPC credentials if [[ -z ${BITCOIN_RPC_USER+x} ]] || [[ -z ${BITCOIN_RPC_PASS+x} ]] || [[ -z ${BITCOIN_RPC_AUTH+x} ]]; then @@ -201,6 +221,29 @@ if [[ -f "${STATUS_DIR}/node-status-bitcoind-ready" ]]; then sed -i "s/bitcoin.node=.*/bitcoin.node=bitcoind/g;" "$LND_CONF_FILE" fi +# TODO: Update all the above code to use this simpler logic +for template in "${NGINX_CONF_FILE}" "${BITCOIN_CONF_FILE}" "${LND_CONF_FILE}" "${TOR_CONF_FILE}" "${ELECTRS_CONF_FILE}" "${ENV_FILE}"; do + # Umbrel + sed -i "s//${GATEWAY_IP}/g" "${template}" + sed -i "s//${NGINX_IP}/g" "${template}" + sed -i "s//${DASHBOARD_IP}/g" "${template}" + sed -i "s//${MANAGER_IP}/g" "${template}" + sed -i "s//${MIDDLEWARE_IP}/g" "${template}" + sed -i "s//${NEUTRINO_SWITCHER_IP}/g" "${template}" + sed -i "s//${FRONTAIL_IP}/g" "${template}" + sed -i "s//${BITCOIN_IP}/g" "${template}" + sed -i "s//${LND_IP}/g" "${template}" + sed -i "s//${LND_GRPC_PORT}/g" "${template}" + sed -i "s//${LND_REST_PORT}/g" "${template}" + sed -i "s//${ELECTRUM_IP}/g" "${template}" + sed -i "s//${ELECTRUM_PORT}/g" "${template}" + sed -i "s//${TOR_PROXY_IP}/g" "${template}" + sed -i "s//${TOR_PROXY_PORT}/g" "${template}" + + # Apps +done + + ########################################################## ############### Performance optimizations ################ ########################################################## @@ -220,6 +263,7 @@ sed -i -e "s/dbcache=/dbcache=$DBCACHE_SIZE/g" "$BITCOIN_CONF_FILE" ############## Override main config files ################ ########################################################## +mv -f "$NGINX_CONF_FILE" "./nginx/nginx.conf" mv -f "$BITCOIN_CONF_FILE" "./bitcoin/bitcoin.conf" mv -f "$LND_CONF_FILE" "./lnd/lnd.conf" mv -f "$TOR_CONF_FILE" "./tor/torrc" @@ -231,10 +275,6 @@ mv -f "$ENV_FILE" "./.env" ################ Configuration complete ################## ########################################################## -echo "Pulling Umbrel Docker images" -echo -docker-compose pull --quiet - echo "Configuring permissions..." echo chown -R 1000:1000 "$UMBREL_ROOT" diff --git a/scripts/start b/scripts/start index 7089102..87601e5 100755 --- a/scripts/start +++ b/scripts/start @@ -87,6 +87,12 @@ echo docker-compose up --detach --build --remove-orphans echo +echo +echo "Starting installed apps..." +echo +"${UMBREL_ROOT}/scripts/app" start installed +echo + echo "Umbrel is now accessible at" echo " http://${DEVICE_HOSTNAME}" echo " http://${DEVICE_IP}" diff --git a/scripts/stop b/scripts/stop index 0941c9b..f563736 100755 --- a/scripts/stop +++ b/scripts/stop @@ -24,6 +24,11 @@ cd "$UMBREL_ROOT" export DOCKER_CLIENT_TIMEOUT=240 export COMPOSE_HTTP_TIMEOUT=240 +echo "Stopping installed apps..." +echo +"${UMBREL_ROOT}/scripts/app" stop installed +echo + echo "Stopping Docker services..." echo docker-compose down diff --git a/scripts/update/.updateignore b/scripts/update/.updateignore index 5bfdf2e..d6bcaf3 100644 --- a/scripts/update/.updateignore +++ b/scripts/update/.updateignore @@ -7,3 +7,4 @@ tor/* electrs/* events/signals logs/* +app-data/* diff --git a/scripts/update/01-run.sh b/scripts/update/01-run.sh index a253545..aebfd05 100755 --- a/scripts/update/01-run.sh +++ b/scripts/update/01-run.sh @@ -74,6 +74,13 @@ BITCOIN_NETWORK="mainnet" [[ -f "${PREV_ENV_FILE}" ]] && source "${PREV_ENV_FILE}" PREV_ENV_FILE="${PREV_ENV_FILE}" NETWORK=$BITCOIN_NETWORK ./scripts/configure +# Pulling new containers +echo "Pulling new containers" +cat < "$UMBREL_ROOT"/statuses/update-status.json +{"state": "installing", "progress": 50, "description": "Pulling new containers", "updateTo": "$RELEASE"} +EOF +docker-compose pull + # Stop existing containers echo "Stopping existing containers" cat < "$UMBREL_ROOT"/statuses/update-status.json diff --git a/templates/.env-sample b/templates/.env-sample index a83f15d..9a3c087 100644 --- a/templates/.env-sample +++ b/templates/.env-sample @@ -1,9 +1,27 @@ +#Umbrel +GATEWAY_IP="" +NGINX_IP="" +DASHBOARD_IP="" +MANAGER_IP="" +MIDDLEWARE_IP="" +NEUTRINO_SWITCHER_IP="" +FRONTAIL_IP="" BITCOIN_NETWORK= -BITCOIN_P2P_PORT= +BITCOIN_IP= BITCOIN_RPC_PORT= +BITCOIN_P2P_PORT= BITCOIN_RPC_USER= BITCOIN_RPC_PASS= BITCOIN_RPC_AUTH= +LND_IP= +LND_GRPC_PORT= +LND_REST_PORT= +ELECTRUM_IP= +ELECTRUM_PORT= +TOR_PROXY_IP= +TOR_PROXY_PORT= TOR_PASSWORD= TOR_HASHED_PASSWORD= DOCKER_BINARY= + +# Apps diff --git a/templates/bitcoin-sample.conf b/templates/bitcoin-sample.conf index ddd980d..7919c15 100644 --- a/templates/bitcoin-sample.conf +++ b/templates/bitcoin-sample.conf @@ -1,14 +1,14 @@ # Tor -proxy=10.11.5.1:29050 +proxy=: listen=1 -bind=10.11.1.1 +bind= # Connections port= rpcport= -rpcbind=10.11.1.1 +rpcbind= rpcbind=127.0.0.1 -rpcallowip=10.11.0.0/16 +rpcallowip=/16 rpcallowip=127.0.0.1 rpcauth= diff --git a/templates/electrs-sample.toml b/templates/electrs-sample.toml index c77ddb9..b069249 100644 --- a/templates/electrs-sample.toml +++ b/templates/electrs-sample.toml @@ -1,6 +1,6 @@ verbose = 4 network = "bitcoin" db_dir = "/data/db" -daemon_rpc_addr = "10.11.1.1:" -electrum_rpc_addr = "0.0.0.0:50001" +daemon_rpc_addr = ":" +electrum_rpc_addr = "0.0.0.0:" server_banner = "Umbrel v" diff --git a/templates/lnd-sample.conf b/templates/lnd-sample.conf index 22d1ef4..20babfb 100644 --- a/templates/lnd-sample.conf +++ b/templates/lnd-sample.conf @@ -1,41 +1,41 @@ [Application Options] listen=0.0.0.0:9735 -rpclisten=0.0.0.0:10009 -restlisten=0.0.0.0:8080 +rpclisten=0.0.0.0: +restlisten=0.0.0.0: maxpendingchannels=3 minchansize=10000 accept-keysend=true -tlsextraip=10.11.1.2 +tlsextraip= tlsextradomain= tlsautorefresh=1 tlsdisableautofill=1 [Bitcoind] -bitcoind.rpchost=10.11.1.1 +bitcoind.rpchost= bitcoind.rpcuser= bitcoind.rpcpass= -bitcoind.zmqpubrawblock=tcp://10.11.1.1:28332 -bitcoind.zmqpubrawtx=tcp://10.11.1.1:28333 +bitcoind.zmqpubrawblock=tcp://:28332 +bitcoind.zmqpubrawtx=tcp://:28333 [Bitcoin] bitcoin.active=1 bitcoin.mainnet=1 -# Default to neutrino as the node is +# Default to neutrino as the node is # automatically switched to bitcoind once # IBD is complete bitcoin.node=neutrino bitcoin.defaultchanconfs=2 # [neutrino] -# Testnet neutrino peers that are automatically +# Testnet neutrino peers that are automatically # uncommented if Umbrel is configured for testnet # neutrino.addpeer=testnet1-btcd.zaphq.io # neutrino.addpeer=testnet2-btcd.zaphq.io [tor] tor.active=1 -tor.control=10.11.5.1:29051 -tor.socks=10.11.5.1:29050 -tor.targetipaddress=10.11.1.2 +tor.control=:29051 +tor.socks=: +tor.targetipaddress= tor.password= tor.v3=1 diff --git a/templates/nginx-sample.conf b/templates/nginx-sample.conf new file mode 100644 index 0000000..d8a5081 --- /dev/null +++ b/templates/nginx-sample.conf @@ -0,0 +1,40 @@ +user nginx; +worker_processes 1; + +error_log /dev/stdout info; + +events { + worker_connections 1024; +} + +http { + access_log /dev/stdout; + + default_type application/octet-stream; + + server { + listen 80; + + location /api/ { + proxy_pass http://:3005/; + } + + location /manager-api/ { + proxy_pass http://:3006/; + } + + location /logs { + proxy_pass http://:9001/logs; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location / { + proxy_pass http://:3004/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + } +} diff --git a/templates/torrc-sample b/templates/torrc-sample index 25d92dd..afc6f13 100644 --- a/templates/torrc-sample +++ b/templates/torrc-sample @@ -1,29 +1,33 @@ -# Bind only to "10.11.5.1" which is the tor IP within the container -SocksPort 10.11.5.1:29050 -ControlPort 10.11.5.1:29051 +# Bind only to "" which is the tor IP within the container +SocksPort : +ControlPort :29051 + +# Umbrel # Dashboard Hidden Service HiddenServiceDir /var/lib/tor/web -HiddenServicePort 80 10.11.0.2:80 +HiddenServicePort 80 :80 # Bitcoin Core P2P Hidden Service HiddenServiceDir /var/lib/tor/bitcoin-p2p -HiddenServicePort 10.11.1.1: +HiddenServicePort : # Bitcoin Core RPC Hidden Service HiddenServiceDir /var/lib/tor/bitcoin-rpc -HiddenServicePort 10.11.1.1: +HiddenServicePort : # Electrum Hidden Service HiddenServiceDir /var/lib/tor/electrum -HiddenServicePort 50001 10.11.4.0:50001 +HiddenServicePort : # LND REST Hidden Service HiddenServiceDir /var/lib/tor/lnd-rest -HiddenServicePort 8080 10.11.1.2:8080 +HiddenServicePort : # LND gRPC Hidden Service HiddenServiceDir /var/lib/tor/lnd-grpc -HiddenServicePort 10009 10.11.1.2:10009 +HiddenServicePort : + +# Apps HashedControlPassword