Browse Source

Add app framework (#333)

btc-rpc-explorer
Luke Childs 4 years ago
committed by GitHub
parent
commit
ffe9e2f15c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitignore
  2. 19
      NETWORKING.md
  3. 0
      app-data/.gitkeep
  4. 426
      apps/README.md
  5. 6
      apps/docker-compose.common.yml
  6. 2
      apps/registry.json
  7. 77
      docker-compose.yml
  8. 9
      events/triggers/app
  9. 11
      karen
  10. 0
      nginx/.gitkeep
  11. 27
      nginx/conf.d/default.conf
  12. 139
      nginx/mime.types
  13. 23
      nginx/nginx.conf
  14. 1
      nginx/www/index.html
  15. 167
      scripts/app
  16. 2
      scripts/backup/backup
  17. 54
      scripts/configure
  18. 6
      scripts/start
  19. 5
      scripts/stop
  20. 1
      scripts/update/.updateignore
  21. 7
      scripts/update/01-run.sh
  22. 20
      templates/.env-sample
  23. 8
      templates/bitcoin-sample.conf
  24. 4
      templates/electrs-sample.toml
  25. 18
      templates/lnd-sample.conf
  26. 40
      templates/nginx-sample.conf
  27. 22
      templates/torrc-sample

2
.gitignore

@ -22,11 +22,13 @@ docker-compose.override.yml
bitcoin/*
db/*
electrs/*
nginx/*
events/signals/*
lnd/*
logs/*
statuses/*
tor/*
app-data/*
# Commit these files

19
NETWORKING.md

@ -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 |

0
nginx/log/.gitkeep → app-data/.gitkeep

426
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/<username>/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: <docker-image>:<tag>
logging: *default-logging
restart: on-failure
stop_grace_period: 5m
ports:
# Replace <port> 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.
- <port>:<port>
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: <docker-image>:<tag>
# ...
```
4\. For our app, we'll update `<docker-image>` with `getumbrel/btc-rpc-explorer`, `<tag>` with `v2.0.2`, and `<port>` 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. 🛠&nbsp;&nbsp;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 <username> git@github.com:<username>/umbrel.git
git fetch <username> btc-rpc-explorer
git checkout <username>/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/<app-id>/` 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 <username>/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 <app-id> && scripts/app start <app-id>`. 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. 🚀&nbsp;&nbsp;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.

6
apps/docker-compose.common.yml

@ -0,0 +1,6 @@
version: "3.7"
networks:
default:
external:
name: umbrel_main_network

2
apps/registry.json

@ -0,0 +1,2 @@
[
]

77
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:

9
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}"

11
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

0
nginx/.gitkeep

27
nginx/conf.d/default.conf

@ -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";
}
}

139
nginx/mime.types

@ -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;
}

23
nginx/nginx.conf

@ -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;
}

1
nginx/www/index.html

@ -1 +0,0 @@
This is the test of the NGINX container system

167
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 <command> <app> [<arguments>]
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

2
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"

54
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>/${GATEWAY_IP}/g" "${template}"
sed -i "s/<nginx-ip>/${NGINX_IP}/g" "${template}"
sed -i "s/<dashboard-ip>/${DASHBOARD_IP}/g" "${template}"
sed -i "s/<manager-ip>/${MANAGER_IP}/g" "${template}"
sed -i "s/<middleware-ip>/${MIDDLEWARE_IP}/g" "${template}"
sed -i "s/<neutrino-switcher-ip>/${NEUTRINO_SWITCHER_IP}/g" "${template}"
sed -i "s/<frontail-ip>/${FRONTAIL_IP}/g" "${template}"
sed -i "s/<bitcoin-ip>/${BITCOIN_IP}/g" "${template}"
sed -i "s/<lnd-ip>/${LND_IP}/g" "${template}"
sed -i "s/<lnd-grpc-port>/${LND_GRPC_PORT}/g" "${template}"
sed -i "s/<lnd-rest-port>/${LND_REST_PORT}/g" "${template}"
sed -i "s/<electrum-ip>/${ELECTRUM_IP}/g" "${template}"
sed -i "s/<electrum-port>/${ELECTRUM_PORT}/g" "${template}"
sed -i "s/<tor-proxy-ip>/${TOR_PROXY_IP}/g" "${template}"
sed -i "s/<tor-proxy-port>/${TOR_PROXY_PORT}/g" "${template}"
# Apps
done
##########################################################
############### Performance optimizations ################
##########################################################
@ -220,6 +263,7 @@ sed -i -e "s/dbcache=<size>/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"

6
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}"

5
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

1
scripts/update/.updateignore

@ -7,3 +7,4 @@ tor/*
electrs/*
events/signals
logs/*
app-data/*

7
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 <<EOF > "$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 <<EOF > "$UMBREL_ROOT"/statuses/update-status.json

20
templates/.env-sample

@ -1,9 +1,27 @@
#Umbrel
GATEWAY_IP="<gateway-ip>"
NGINX_IP="<nginx-ip>"
DASHBOARD_IP="<dashboard-ip>"
MANAGER_IP="<manager-ip>"
MIDDLEWARE_IP="<middleware-ip>"
NEUTRINO_SWITCHER_IP="<neutrino-switcher-ip>"
FRONTAIL_IP="<frontail-ip>"
BITCOIN_NETWORK=<network>
BITCOIN_P2P_PORT=<port>
BITCOIN_IP=<bitcoin-ip>
BITCOIN_RPC_PORT=<port>
BITCOIN_P2P_PORT=<port>
BITCOIN_RPC_USER=<username>
BITCOIN_RPC_PASS=<password>
BITCOIN_RPC_AUTH=<rpcauth>
LND_IP=<lnd-ip>
LND_GRPC_PORT=<lnd-grpc-port>
LND_REST_PORT=<lnd-rest-port>
ELECTRUM_IP=<electrum-ip>
ELECTRUM_PORT=<electrum-port>
TOR_PROXY_IP=<tor-proxy-ip>
TOR_PROXY_PORT=<tor-proxy-port>
TOR_PASSWORD=<password>
TOR_HASHED_PASSWORD=<password>
DOCKER_BINARY=<path>
# Apps

8
templates/bitcoin-sample.conf

@ -1,14 +1,14 @@
# Tor
proxy=10.11.5.1:29050
proxy=<tor-proxy-ip>:<tor-proxy-port>
listen=1
bind=10.11.1.1
bind=<bitcoin-ip>
# Connections
port=<port>
rpcport=<port>
rpcbind=10.11.1.1
rpcbind=<bitcoin-ip>
rpcbind=127.0.0.1
rpcallowip=10.11.0.0/16
rpcallowip=<gateway-ip>/16
rpcallowip=127.0.0.1
rpcauth=<rpcauth>

4
templates/electrs-sample.toml

@ -1,6 +1,6 @@
verbose = 4
network = "bitcoin"
db_dir = "/data/db"
daemon_rpc_addr = "10.11.1.1:<port>"
electrum_rpc_addr = "0.0.0.0:50001"
daemon_rpc_addr = "<bitcoin-ip>:<port>"
electrum_rpc_addr = "0.0.0.0:<electrum-port>"
server_banner = "Umbrel v<version>"

18
templates/lnd-sample.conf

@ -1,21 +1,21 @@
[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:<lnd-grpc-port>
restlisten=0.0.0.0:<lnd-rest-port>
maxpendingchannels=3
minchansize=10000
accept-keysend=true
tlsextraip=10.11.1.2
tlsextraip=<lnd-ip>
tlsextradomain=<hostname>
tlsautorefresh=1
tlsdisableautofill=1
[Bitcoind]
bitcoind.rpchost=10.11.1.1
bitcoind.rpchost=<bitcoin-ip>
bitcoind.rpcuser=<username>
bitcoind.rpcpass=<password>
bitcoind.zmqpubrawblock=tcp://10.11.1.1:28332
bitcoind.zmqpubrawtx=tcp://10.11.1.1:28333
bitcoind.zmqpubrawblock=tcp://<bitcoin-ip>:28332
bitcoind.zmqpubrawtx=tcp://<bitcoin-ip>:28333
[Bitcoin]
bitcoin.active=1
@ -34,8 +34,8 @@ bitcoin.defaultchanconfs=2
[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=<tor-proxy-ip>:29051
tor.socks=<tor-proxy-ip>:<tor-proxy-port>
tor.targetipaddress=<lnd-ip>
tor.password=<password>
tor.v3=1

40
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://<middleware-ip>:3005/;
}
location /manager-api/ {
proxy_pass http://<manager-ip>:3006/;
}
location /logs {
proxy_pass http://<frontail-ip>:9001/logs;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://<dashboard-ip>:3004/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}

22
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 "<tor-proxy-ip>" which is the tor IP within the container
SocksPort <tor-proxy-ip>:<tor-proxy-port>
ControlPort <tor-proxy-ip>:29051
# Umbrel
# Dashboard Hidden Service
HiddenServiceDir /var/lib/tor/web
HiddenServicePort 80 10.11.0.2:80
HiddenServicePort 80 <nginx-ip>:80
# Bitcoin Core P2P Hidden Service
HiddenServiceDir /var/lib/tor/bitcoin-p2p
HiddenServicePort <bitcoin-p2p-port> 10.11.1.1:<bitcoin-p2p-port>
HiddenServicePort <bitcoin-p2p-port> <bitcoin-ip>:<bitcoin-p2p-port>
# Bitcoin Core RPC Hidden Service
HiddenServiceDir /var/lib/tor/bitcoin-rpc
HiddenServicePort <bitcoin-rpc-port> 10.11.1.1:<bitcoin-rpc-port>
HiddenServicePort <bitcoin-rpc-port> <bitcoin-ip>:<bitcoin-rpc-port>
# Electrum Hidden Service
HiddenServiceDir /var/lib/tor/electrum
HiddenServicePort 50001 10.11.4.0:50001
HiddenServicePort <electrum-port> <electrum-ip>:<electrum-port>
# LND REST Hidden Service
HiddenServiceDir /var/lib/tor/lnd-rest
HiddenServicePort 8080 10.11.1.2:8080
HiddenServicePort <lnd-rest-port> <lnd-ip>:<lnd-rest-port>
# LND gRPC Hidden Service
HiddenServiceDir /var/lib/tor/lnd-grpc
HiddenServicePort 10009 10.11.1.2:10009
HiddenServicePort <lnd-grpc-port> <lnd-ip>:<lnd-grpc-port>
# Apps
HashedControlPassword <password>

Loading…
Cancel
Save