diff --git a/.github/workflows/buildService.yml b/.github/workflows/buildService.yml index f013351..09dc0ac 100644 --- a/.github/workflows/buildService.yml +++ b/.github/workflows/buildService.yml @@ -4,33 +4,18 @@ on: workflow_dispatch: pull_request: paths-ignore: ['*.md'] - branches: ['main', 'master'] + branches: ['master'] push: paths-ignore: ['*.md'] - branches: ['main', 'master'] + branches: ['master'] -jobs: - BuildPackage: - runs-on: ubuntu-latest - steps: - - name: Prepare StartOS SDK - uses: Start9Labs/sdk@v1 - - - name: Checkout services repository - uses: actions/checkout@v3 +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true - - name: Build the service package - id: build - run: | - git submodule update --init --recursive - start-sdk init - make - PACKAGE_ID=$(yq -oy ".id" manifest.*) - echo "::set-output name=package_id::$PACKAGE_ID" - shell: bash - - - name: Upload .s9pk - uses: actions/upload-artifact@v3 - with: - name: ${{ steps.build.outputs.package_id }}.s9pk - path: ./${{ steps.build.outputs.package_id }}.s9pk \ No newline at end of file +jobs: + build: + if: github.event.pull_request.draft == false + uses: start9labs/shared-workflows/.github/workflows/buildService.yml@master + secrets: + DEV_KEY: ${{ secrets.DEV_KEY }} diff --git a/.github/workflows/releaseService.yml b/.github/workflows/releaseService.yml index ee85271..d3bbee7 100644 --- a/.github/workflows/releaseService.yml +++ b/.github/workflows/releaseService.yml @@ -6,66 +6,14 @@ on: - 'v*.*' jobs: - ReleasePackage: - runs-on: ubuntu-latest + release: + uses: start9labs/shared-workflows/.github/workflows/releaseService.yml@master + with: + REGISTRY: ${{ vars.REGISTRY }} + S3_S9PKS_BASE_URL: ${{ vars.S3_S9PKS_BASE_URL }} + secrets: + DEV_KEY: ${{ secrets.DEV_KEY }} + S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} + S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} permissions: contents: write - steps: - - name: Prepare StartOS SDK - uses: Start9Labs/sdk@v1 - - - name: Checkout services repository - uses: actions/checkout@v3 - - - name: Build the service package - run: | - git submodule update --init --recursive - start-sdk init - make - - - name: Setting package ID and title from the manifest - id: package - run: | - echo "::set-output name=package_id::$(yq -oy ".id" manifest.*)" - echo "::set-output name=package_title::$(yq -oy ".title" manifest.*)" - shell: bash - - - name: Generate sha256 checksum - run: | - PACKAGE_ID=${{ steps.package.outputs.package_id }} - sha256sum ${PACKAGE_ID}.s9pk > ${PACKAGE_ID}.s9pk.sha256 - shell: bash - - - name: Generate changelog - run: | - PACKAGE_ID=${{ steps.package.outputs.package_id }} - echo "## What's Changed" > change-log.txt - yq e '.release-notes' manifest.yaml >> change-log.txt - echo "## SHA256 Hash" >> change-log.txt - echo '```' >> change-log.txt - sha256sum ${PACKAGE_ID}.s9pk >> change-log.txt - echo '```' >> change-log.txt - shell: bash - - - name: Create GitHub Release - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ github.ref_name }} - name: ${{ steps.package.outputs.package_title }} ${{ github.ref_name }} - prerelease: true - body_path: change-log.txt - files: | - ./${{ steps.package.outputs.package_id }}.s9pk - ./${{ steps.package.outputs.package_id }}.s9pk.sha256 - - - name: Publish to Registry - env: - S9USER: ${{ secrets.S9USER }} - S9PASS: ${{ secrets.S9PASS }} - S9REGISTRY: ${{ secrets.S9REGISTRY }} - run: | - if [[ -z "$S9USER" || -z "$S9PASS" || -z "$S9REGISTRY" ]]; then - echo "Publish skipped: missing registry credentials." - else - start-sdk publish https://$S9USER:$S9PASS@$S9REGISTRY ${{ steps.package.outputs.package_id }}.s9pk - fi \ No newline at end of file diff --git a/.gitignore b/.gitignore index 57c2677..4dad48c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ -lightning-terminal.s9pk -image.tar +*.s9pk +startos/*.js +node_modules/ +.DS_Store .vscode/ -docker-images/ +docker-images +javascript +ncc-cache \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8b13789..0000000 --- a/.gitmodules +++ /dev/null @@ -1 +0,0 @@ - diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..841c29b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,2 @@ +## How the upstream version is pulled +- dockerTag in `startos/manifest/index.ts`: `lightninglabs/lightning-terminal:v` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..cbc38a7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# Contributing + +## Building and Development + +See the [StartOS Packaging Guide](https://docs.start9.com/packaging/) for complete environment setup and build instructions. + +### Quick Start + +```bash +# Install dependencies +npm ci + +# Build universal package +make +``` diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index cee6494..0000000 --- a/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM lightninglabs/lightning-terminal:v0.12.4-alpha -# arm64 or amd64 -ARG PLATFORM -ARG ARCH - -RUN apk add --no-cache yq xxd curl - -# Import Entrypoint and give permissions -ADD ./docker_entrypoint.sh /usr/local/bin/docker_entrypoint.sh -ADD ./check-lnd.sh /usr/local/bin/check-lnd.sh -RUN chmod a+x /usr/local/bin/*.sh diff --git a/Makefile b/Makefile index 1510140..10d4360 100644 --- a/Makefile +++ b/Makefile @@ -1,34 +1,3 @@ -PKG_ID := $(shell yq e ".id" manifest.yaml) -PKG_VERSION := $(shell yq e ".version" manifest.yaml) -TS_FILES := $(shell find ./ -name \*.ts) - -# delete the target of a rule if it has changed and its recipe exits with a nonzero exit status -.DELETE_ON_ERROR: - -all: verify - -verify: $(PKG_ID).s9pk - start-sdk verify s9pk $(PKG_ID).s9pk - -install: $(PKG_ID).s9pk - start-cli package install $(PKG_ID).s9pk - -clean: - rm -rf docker-images - rm -f image.tar - rm -f $(PKG_ID).s9pk - rm -f scripts/*.js - -scripts/embassy.js: $(TS_FILES) - deno bundle scripts/embassy.ts scripts/embassy.js - -docker-images/x86_64.tar: Dockerfile docker_entrypoint.sh - mkdir -p docker-images - docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --platform=linux/amd64 --build-arg PLATFORM=amd64 -o type=docker,dest=docker-images/x86_64.tar . - -docker-images/aarch64.tar: Dockerfile docker_entrypoint.sh - mkdir -p docker-images - docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --platform=linux/arm64 --build-arg PLATFORM=arm64 -o type=docker,dest=docker-images/aarch64.tar . - -$(PKG_ID).s9pk: manifest.yaml instructions.md LICENSE icon.png scripts/embassy.js docker-images/aarch64.tar docker-images/x86_64.tar - start-sdk pack +ARCHES := x86 arm +# overrides to s9pk.mk must precede the include statement +include s9pk.mk diff --git a/README.md b/README.md index 2a9df1a..8e40dd3 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,152 @@ -# Wrapper for Lightning Terminal +

+ Lightning Terminal Logo +

-`Lightning Terminal (LiT)` is a browser-based interface for managing channel liquidity. This .s9pk wrapper will allow you to run LiT on your Start9 Server. +# Lightning Terminal on StartOS -## StartOS Service Pre-Requisites: +> **Upstream docs:** +> +> Everything not listed in this document should behave the same as upstream +> Lightning Terminal. If a feature, setting, or behavior is not mentioned here, the +> upstream documentation is accurate and fully applicable. -- [lnd](https://github.com/Start9Labs/lnd-wrapper) +A browser-based interface for managing channel liquidity on a self-hosted LND node. See the [upstream repo](https://github.com/lightninglabs/lightning-terminal) for general Lightning Terminal documentation. -## Dependencies +--- -- [docker](https://docs.docker.com/get-docker) -- [docker-buildx](https://docs.docker.com/buildx/working-with-buildx/) -- [yq](https://mikefarah.gitbook.io/yq) -- [toml](https://crates.io/crates/toml-cli) -- [start-sdk](https://github.com/Start9Labs/start-os/tree/master/backend) -- [make](https://www.gnu.org/software/make/) +## Table of Contents -## Build enviroment +- [Image and Container Runtime](#image-and-container-runtime) +- [Volume and Data Layout](#volume-and-data-layout) +- [Installation and First-Run Flow](#installation-and-first-run-flow) +- [Configuration Management](#configuration-management) +- [Network Access and Interfaces](#network-access-and-interfaces) +- [Actions](#actions-startos-ui) +- [Backups and Restore](#backups-and-restore) +- [Health Checks](#health-checks) +- [Dependencies](#dependencies) +- [Limitations and Differences](#limitations-and-differences) +- [What Is Unchanged from Upstream](#what-is-unchanged-from-upstream) +- [Contributing](#contributing) +- [Quick Reference for AI Consumers](#quick-reference-for-ai-consumers) -Before building the lightning terminal package, your build environment must be setup for building StartOS services. Instructions for setting up the proper build environment can be found in the [Developer Docs](https://docs.start9.com/latest/developer-docs/packaging). +--- +## Image and Container Runtime -## Cloning +| Property | Value | +|----------|-------| +| Image | `lightninglabs/lightning-terminal` (upstream, unmodified) | +| Architectures | x86_64, aarch64 | +| Entrypoint | `/bin/litd` | -Clone the project locally. Note the submodule link to the original project(s). +## Volume and Data Layout -``` -git clone https://github.com/Start9Labs/lightning-terminal-wrapper.git -cd lightning-terminal-wrapper -git submodule update --init --recursive -``` +| Volume | Mount Point | Purpose | +|--------|-------------|---------| +| `main` | `/root` | All LiT data (configuration, application state) | -## Building +StartOS-specific files on the `main` volume: -To build the project, run the following commands: +| File | Purpose | +|------|---------| +| `.lit/lit.conf` | LiT configuration (managed by StartOS) | -``` -make -``` +The LND `main` volume is mounted read-only at `/mnt/lnd` for macaroon and TLS certificate access. -## Installing (on StartOS) +## Installation and First-Run Flow -Run the following commands to determine successful install: -> :information_source: Change server-name.local to your Start9 server address +1. On install, StartOS creates a **critical task** prompting the user to set an admin password via the **Create Password** action +2. The `.lit/lit.conf` file is written with default configuration and the generated password +3. LiT connects to the running LND node using the mounted macaroon and TLS certificate -``` -start-cli auth login -#Enter your StartOS password -start-cli --host https://server-name.local package install lightning-terminal.s9pk -``` -**Tip:** You can also install the lightning-terminal.s9pk using **Sideload Service** under the **System > Manage** section. +## Configuration Management + +LiT is configured via the `.lit/lit.conf` file, managed by StartOS. There are no user-configurable settings exposed through StartOS actions — the admin password is the only managed value. + +Settings managed by StartOS (hardcoded): + +| Setting | Value | Reason | +|---------|-------|--------| +| `uipassword` | Auto-generated | Set via Create/Reset Password action | +| `lit-dir` | `/root` | Maps to the mounted volume | +| `insecure-httplisten` | `lightning-terminal.startos:8443` | StartOS service networking | +| `remote.lnd.rpcserver` | `lnd.startos:10009` | StartOS service networking | +| `remote.lnd.macaroonpath` | `/mnt/lnd/data/chain/bitcoin/mainnet/admin.macaroon` | Mounted dependency volume | +| `remote.lnd.tlscertpath` | `/mnt/lnd/tls.cert` | Mounted dependency volume | + +## Network Access and Interfaces + +| Interface | Port | Protocol | Purpose | +|-----------|------|----------|---------| +| Web UI | 8443 | HTTP | Lightning Terminal web interface | + +## Actions (StartOS UI) + +| Action | Purpose | Availability | Inputs | +|--------|---------|-------------|--------| +| **Create/Reset Password** | Generate a random 22-character admin password for the web UI | Any | None | + +The action name changes dynamically: "Create Password" on first use, "Reset Password" thereafter. The generated password is displayed once as a copyable value. + +## Backups and Restore -## Verify Install +**Backed up:** The entire `main` volume (configuration, application data). -Go to your StartOS Services page, select lightning-terminal and start the service. +**Restore behavior:** Standard restore — the configuration and data are restored as-is. The configured LND node must be available. -**Done!** +## Health Checks + +| Check | Method | Messages | +|-------|--------|----------| +| **Web Interface** | Port listening (8443) | Ready: "The web interface is ready" | + +## Dependencies + +| Dependency | Required | Purpose | +|------------|----------|---------| +| LND | Required | Lightning Network node access via gRPC | + +LND must be installed and running. LiT connects to LND using the admin macaroon and TLS certificate from the mounted volume. + +## Limitations and Differences + +1. **Remote mode only** — LiT runs in remote mode connecting to a separate LND instance; integrated mode (where LiT runs its own LND) is not available +2. **No user-configurable settings** — all configuration is managed by StartOS; the only user action is password management +3. **Password-only authentication** — the UI password is auto-generated via the StartOS action; there is no option to set a custom password manually + +## What Is Unchanged from Upstream + +- Lightning Loop (submarine swaps — Loop In and Loop Out) +- Lightning Pool (channel liquidity marketplace) +- Channel visualization and balance management +- All web UI functionality +- Faraday (channel analytics) + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md) for build instructions and development workflow. + +--- + +## Quick Reference for AI Consumers + +```yaml +package_id: lightning-terminal +image: lightninglabs/lightning-terminal +architectures: [x86_64, aarch64] +volumes: + main: /root +ports: + ui: 8443 +dependencies: + - lnd (required) +startos_managed_files: + - .lit/lit.conf +actions: + - reset-password +health_checks: + - port_listening: 8443 +backup_volumes: + - main +``` diff --git a/assets/ABOUT.md b/assets/ABOUT.md new file mode 100644 index 0000000..4fbfc10 --- /dev/null +++ b/assets/ABOUT.md @@ -0,0 +1 @@ +Use the `/assets` directory to include additional files or scripts needed by your service. diff --git a/check-lnd.sh b/check-lnd.sh deleted file mode 100644 index 70b6865..0000000 --- a/check-lnd.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -MACAROON_HEADER="Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 /mnt/lnd/admin.macaroon)" -DURATION=$(/dev/null - WEB_RES=$? - if [ $WEB_RES != 0 ]; then - echo "LND Server is unreachable" >&2 - exit 1 - fi -fi diff --git a/docker_entrypoint.sh b/docker_entrypoint.sh deleted file mode 100755 index 0864c72..0000000 --- a/docker_entrypoint.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -_term() { - echo "Caught SIGTERM signal!" - kill -TERM "$lightning_terminal_process" 2>/dev/null -} -# Setting variables -LND_ADDRESS='lnd.embassy' -export LITD_PASS=$(yq e '.password' /root/start9/config.yaml) -RPC_USER=$(yq e '.bitcoind-user' /root/start9/config.yaml) -RPC_PASS=$(yq e '.bitcoind-password' /root/start9/config.yaml) -RPC_HOST="bitcoind.embassy" -MACAROON_HEADER="Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 /mnt/lnd/admin.macaroon)" - -# Creating lit.conf -mkdir -p /root/.lit -echo " -remote.lnd.rpcserver="$LND_ADDRESS":10009 -remote.lnd.macaroonpath=/mnt/lnd/admin.macaroon -remote.lnd.tlscertpath=/mnt/lnd/tls.cert -" > /root/.lit/lit.conf -until curl --silent --fail --cacert /mnt/lnd/tls.cert --header "$MACAROON_HEADER" https://lnd.embassy:8080/v1/getinfo &>/dev/null -do - echo "LND Server is unreachable. Are you sure the LND service is running?" - sleep 5 -done -echo "Configuring LiT..." -# Removing any old data in the lit folder -rm -f /root/.lit/mainnet/lit.macaroon -# Properties Page showing password to be used for login - echo 'version: 2' > /root/start9/stats.yaml - echo 'data:' >> /root/start9/stats.yaml - echo ' Password: ' >> /root/start9/stats.yaml - echo ' type: string' >> /root/start9/stats.yaml - echo " value: \"$LITD_PASS\"" >> /root/start9/stats.yaml - echo ' description: This is your admin password for Lightning Terminal. Please use caution when sharing this password, you could lose your funds!' >> /root/start9/stats.yaml - echo ' copyable: true' >> /root/start9/stats.yaml - echo ' masked: true' >> /root/start9/stats.yaml - echo ' qr: false' >> /root/start9/stats.yaml - -echo "Starting LiT..." -/bin/litd --uipassword_env=LITD_PASS --macaroonpath=/root/.lit/mainnet/lit.macaroon --lit-dir=/root/.lit --tlscertpath=/root/.lit/tls.cert --tlskeypath=/root/.lit/tls.key --insecure-httplisten=lightning-terminal.embassy:8443 & -lightning_terminal_process=$! - - -trap _term SIGTERM - -wait $lightning_terminal_process diff --git a/icon.png b/icon.png deleted file mode 100644 index e5863a4..0000000 Binary files a/icon.png and /dev/null differ diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..449191a --- /dev/null +++ b/icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/instructions.md b/instructions.md deleted file mode 100644 index 521117c..0000000 --- a/instructions.md +++ /dev/null @@ -1,9 +0,0 @@ -## Welcome to Lightning Terminal! - -`Lightning Terminal` is a powerful web all-in-one interface that integrates Lightning Pool and Lightning Loop. - -> **Lightning Loop:** Make a Lightning transaction to an on-chain bitcoin address (Loop Out) or send on-chain bitcoin directly into a Lightning channel (Loop In). -> -> **Lightning Pool:** Marketplace for buying and selling channel liquidity. Lightning Terminal allows users to place their own asks and bids through the web browser. - -### You may also reference the [Walkthrough](https://docs.lightning.engineering/lightning-network-tools/lightning-terminal/get-lit) document for additional guidance on how to use the product. diff --git a/manifest.yaml b/manifest.yaml deleted file mode 100644 index 3b02639..0000000 --- a/manifest.yaml +++ /dev/null @@ -1,93 +0,0 @@ -id: lightning-terminal -title: Lightning Terminal -version: 0.12.4 -release-notes: | - * Update upstream to v0.12.4 [Release Notes](https://github.com/lightninglabs/lightning-terminal/releases/tag/v0.12.4-alpha) - * Fix backup bug -license: mit -wrapper-repo: https://github.com/Start9Labs/lightning-terminal-wrapper -upstream-repo: https://github.com/lightninglabs/lightning-terminal -support-site: https://github.com/lightninglabs/lightning-terminal/issues -marketing-site: https://lightning.engineering/ -build: ["make"] -description: - short: Your Home for Lightning Liquidity - long: A browser-based interface for managing channel liquidity on your self-hosted LND node, Visualize your channels and balances, Perform submarine swaps via the Lightning Loop service, Classify channels according to your node's operating mode, Run a single binary that integrates loopd and poold daemons all in one, Access a preview release of the Pool UI, Use Pool to earn sats by opening channels to those needing inbound liquidity. -assets: - license: LICENSE - icon: icon.png - instructions: instructions.md -main: - type: docker - image: main - entrypoint: docker_entrypoint.sh - args: [] - mounts: - main: /root - lnd: /mnt/lnd -health-checks: - web-ui: - name: Web Interface - success-message: The Lightning Terminal UI is ready to visit in a web browser - type: script - lnd: - name: LND API - success-message: The LND Server is active and accepting requests - type: docker - image: main - entrypoint: check-lnd.sh - args: [] - io-format: json - inject: true -config: - get: - type: script - set: - type: script -properties: - type: script -volumes: - main: - type: data - lnd: - type: pointer - package-id: lnd - volume-id: main - path: "/public" - readonly: false -interfaces: - main: - name: Lightning Terminal Web Interface - description: Specifies the LiT interface to listen on for HTTP connections. - tor-config: - port-mapping: - 80: "8443" - lan-config: - 443: - ssl: true - internal: 8443 - ui: true - protocols: - - tcp - - http -dependencies: - lnd: - version: ">=0.16.0 <0.18.0" - description: Used to communicate with the Lightning Network. - requirement: - type: required - config: ~ -backup: - create: - type: script - restore: - type: script -migrations: - from: - "*": - type: script - args: ["from"] - to: - "*": - type: script - args: ["to"] diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..909b209 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,388 @@ +{ + "name": "lightning-terminal-startos", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@start9labs/start-sdk": "^0.4.0-beta.66", + "bitcoind-startos": "git+https://github.com/Start9Labs/bitcoind-startos.git#040/30.2", + "lnd-startos": "git+https://github.com/Start9Labs/lnd-startos.git#update/040", + "tor-startos": "git+https://github.com/Start9Labs/tor-startos.git#update/040" + }, + "devDependencies": { + "@types/node": "^22.1.0", + "@vercel/ncc": "^0.38.1", + "prettier": "^3.3.3", + "typescript": "^5.5.4" + } + }, + "node_modules/@iarna/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-td6ZUkz2oS3VeleBcN+m//Q6HlCFCPrnI0FZhrt/h4XqLEdOyYp2u21nd8MdsR+WJy5r9PTDaHTDDfhf4H4l6Q==", + "license": "ISC" + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@start9labs/start-sdk": { + "version": "0.4.0-beta.66", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.66.tgz", + "integrity": "sha512-fsp7d3nqFapLohaKbNk1frW8fbVmjr2CBehVXVUk6JS/LSerogqlAYUFE5LKp5vJoTkuSnZbF5np4m+oxCmwCw==", + "license": "MIT", + "dependencies": { + "@iarna/toml": "^3.0.0", + "@noble/curves": "^1.8.2", + "@noble/hashes": "^1.7.2", + "@types/ini": "^4.1.1", + "deep-equality-data-structures": "^2.0.0", + "fast-xml-parser": "^5.5.6", + "ini": "^5.0.0", + "isomorphic-fetch": "^3.0.0", + "mime": "^4.0.7", + "yaml": "^2.7.1", + "zod": "^4.3.6", + "zod-deep-partial": "^1.2.0" + } + }, + "node_modules/@types/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vercel/ncc": { + "version": "0.38.4", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.4.tgz", + "integrity": "sha512-8LwjnlP39s08C08J5NstzriPvW1SP8Zfpp1BvC2sI35kPeZnHfxVkCwu4/+Wodgnd60UtT1n8K8zw+Mp7J9JmQ==", + "dev": true, + "license": "MIT", + "bin": { + "ncc": "dist/ncc/cli.js" + } + }, + "node_modules/bitcoind-startos": { + "name": "bitcoin-core", + "resolved": "git+ssh://git@github.com/Start9Labs/bitcoind-startos.git#113731eaf28e13e6a6ecd343c409a9528f458a5b", + "dependencies": { + "@start9labs/start-sdk": "^0.4.0-beta.66", + "diskusage": "^1.2.0", + "tor-startos": "git+https://github.com/Start9Labs/tor-startos.git#update/040" + } + }, + "node_modules/deep-equality-data-structures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deep-equality-data-structures/-/deep-equality-data-structures-2.0.0.tgz", + "integrity": "sha512-qgrUr7MKXq7VRN+WUpQ48QlXVGL0KdibAoTX8KRg18lgOgqbEKMAW1WZsVCtakY4+XX42pbAJzTz/DlXEFM2Fg==", + "license": "MIT", + "dependencies": { + "object-hash": "^3.0.0" + } + }, + "node_modules/diskusage": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/diskusage/-/diskusage-1.2.0.tgz", + "integrity": "sha512-2u3OG3xuf5MFyzc4MctNRUKjjwK+UkovRYdD2ed/NZNZPrt0lqHnLKxGhlFVvAb4/oufIgQG3nWgwmeTbHOvXA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^4.2.8", + "nan": "^2.18.0" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "license": "MIT" + }, + "node_modules/fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.5.9", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.9.tgz", + "integrity": "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.2" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/isomorphic-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", + "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/lnd-startos": { + "resolved": "git+ssh://git@github.com/Start9Labs/lnd-startos.git#20ea0d169b56540c1a0eb9fd69be3c4ecd805cd4", + "dependencies": { + "@start9labs/start-sdk": "^0.4.0-beta.66", + "bitcoind-startos": "git+https://github.com/Start9Labs/bitcoind-startos.git#040/30.2", + "mime": "^4.0.7", + "rfc4648": "1.5.4" + } + }, + "node_modules/mime": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/nan": { + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz", + "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==", + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", + "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/rfc4648": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.4.tgz", + "integrity": "sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==", + "license": "MIT" + }, + "node_modules/strnum": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", + "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/tor-startos": { + "resolved": "git+ssh://git@github.com/Start9Labs/tor-startos.git#99b7401c887bb4064fa179f8678997914f484ef7", + "dependencies": { + "@noble/curves": "*", + "@start9labs/start-sdk": "^0.4.0-beta.66", + "rfc4648": "^1.5.4" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-deep-partial": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/zod-deep-partial/-/zod-deep-partial-1.4.4.tgz", + "integrity": "sha512-aWkPl7hVStgE01WzbbSxCgX4O+sSpgt8JOjvFUtMTF75VgL6MhWQbiZi+AWGN85SfSTtI9gsOtL1vInoqfDVaA==", + "license": "MIT", + "peerDependencies": { + "zod": "^4.1.13" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0c461a1 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "scripts": { + "build": "rm -rf ./javascript && ncc build startos/index.ts -o ./javascript", + "prettier": "prettier --write startos", + "check": "tsc --noEmit" + }, + "dependencies": { + "@start9labs/start-sdk": "^0.4.0-beta.66", + "bitcoind-startos": "git+https://github.com/Start9Labs/bitcoind-startos.git#040/30.2", + "lnd-startos": "git+https://github.com/Start9Labs/lnd-startos.git#update/040", + "tor-startos": "git+https://github.com/Start9Labs/tor-startos.git#update/040" + }, + "devDependencies": { + "@types/node": "^22.1.0", + "@vercel/ncc": "^0.38.1", + "prettier": "^3.3.3", + "typescript": "^5.5.4" + }, + "prettier": { + "trailingComma": "all", + "tabWidth": 2, + "semi": false, + "singleQuote": true + } +} diff --git a/s9pk.mk b/s9pk.mk new file mode 100644 index 0000000..45ebfc9 --- /dev/null +++ b/s9pk.mk @@ -0,0 +1,127 @@ +# ** Plumbing. DO NOT EDIT **. +# This file is imported by ./Makefile. Make edits there + +PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest/index.ts) +INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null) +ARCHES ?= x86 arm riscv +TARGETS ?= arches +ifdef VARIANT +BASE_NAME := $(PACKAGE_ID)_$(VARIANT) +else +BASE_NAME := $(PACKAGE_ID) +endif + +.PHONY: all arches aarch64 x86_64 riscv64 arm arm64 x86 riscv arch/* clean install check-deps check-init package ingredients +.DELETE_ON_ERROR: +.SECONDARY: + +define SUMMARY + @manifest=$$(start-cli s9pk inspect $(1) manifest); \ + size=$$(du -h $(1) | awk '{print $$1}'); \ + title=$$(printf '%s' "$$manifest" | jq -r .title); \ + version=$$(printf '%s' "$$manifest" | jq -r .version); \ + arches=$$(printf '%s' "$$manifest" | jq -r '[.images[].arch // []] | flatten | unique | join(", ")'); \ + sdkv=$$(printf '%s' "$$manifest" | jq -r .sdkVersion); \ + gitHash=$$(printf '%s' "$$manifest" | jq -r .gitHash | sed -E 's/(.*-modified)$$/\x1b[0;31m\1\x1b[0m/'); \ + printf "\n"; \ + printf "\033[1;32m✅ Build Complete!\033[0m\n"; \ + printf "\n"; \ + printf "\033[1;37m📦 $$title\033[0m \033[36mv$$version\033[0m\n"; \ + printf "───────────────────────────────\n"; \ + printf " \033[1;36mFilename:\033[0m %s\n" "$(1)"; \ + printf " \033[1;36mSize:\033[0m %s\n" "$$size"; \ + printf " \033[1;36mArch:\033[0m %s\n" "$$arches"; \ + printf " \033[1;36mSDK:\033[0m %s\n" "$$sdkv"; \ + printf " \033[1;36mGit:\033[0m %s\n" "$$gitHash"; \ + echo "" +endef + +all: $(TARGETS) + +arches: $(ARCHES) + +universal: $(BASE_NAME).s9pk + $(call SUMMARY,$<) + +arch/%: $(BASE_NAME)_%.s9pk + $(call SUMMARY,$<) + +x86 x86_64: arch/x86_64 +arm arm64 aarch64: arch/aarch64 +riscv riscv64: arch/riscv64 + +$(BASE_NAME).s9pk: $(INGREDIENTS) .git/HEAD .git/index + @$(MAKE) --no-print-directory ingredients + @echo " Packing '$@'..." + start-cli s9pk pack -o $@ + +$(BASE_NAME)_%.s9pk: $(INGREDIENTS) .git/HEAD .git/index + @$(MAKE) --no-print-directory ingredients + @echo " Packing '$@'..." + start-cli s9pk pack --arch=$* -o $@ + +ingredients: $(INGREDIENTS) + @echo " Re-evaluating ingredients..." + +install: | check-deps check-init + @HOST=$$(awk -F'/' '/^host:/ {print $$3}' ~/.startos/config.yaml); \ + if [ -z "$$HOST" ]; then \ + echo "Error: You must define \"host: http://server-name.local\" in ~/.startos/config.yaml"; \ + exit 1; \ + fi; \ + S9PK=$$(ls -t *.s9pk 2>/dev/null | head -1); \ + if [ -z "$$S9PK" ]; then \ + echo "Error: No .s9pk file found. Run 'make' first."; \ + exit 1; \ + fi; \ + printf "\n🚀 Installing %s to %s ...\n" "$$S9PK" "$$HOST"; \ + start-cli package install -s "$$S9PK" + +publish: | all + @REGISTRY=$$(awk -F'/' '/^registry:/ {print $$3}' ~/.startos/config.yaml); \ + if [ -z "$$REGISTRY" ]; then \ + echo "Error: You must define \"registry: https://my-registry.tld\" in ~/.startos/config.yaml"; \ + exit 1; \ + fi; \ + S3BASE=$$(awk -F'/' '/^s9pk-s3base:/ {print $$3}' ~/.startos/config.yaml); \ + if [ -z "$$S3BASE" ]; then \ + echo "Error: You must define \"s3base: https://s9pks.my-s3-bucket.tld\" in ~/.startos/config.yaml"; \ + exit 1; \ + fi; \ + command -v s3cmd >/dev/null || \ + (echo "Error: s3cmd not found. It must be installed to publish using s3." && exit 1); \ + printf "\n🚀 Publishing to %s; indexing on %s ...\n" "$$S3BASE" "$$REGISTRY"; \ + for s9pk in *.s9pk; do \ + age=$$(( $$(date +%s) - $$(stat -c %Y "$$s9pk") )); \ + if [ "$$age" -gt 3600 ]; then \ + printf "\033[1;33m⚠️ %s is %d minutes old. Publish anyway? [y/N] \033[0m" "$$s9pk" "$$((age / 60))"; \ + read -r ans; \ + case "$$ans" in [yY]*) ;; *) echo "Skipping $$s9pk"; continue ;; esac; \ + fi; \ + start-cli s9pk publish "$$s9pk"; \ + done + +check-deps: + @command -v start-cli >/dev/null || \ + (echo "Error: start-cli not found. Please see https://docs.start9.com/latest/developer-guide/sdk/installing-the-sdk" && exit 1) + @command -v npm >/dev/null || \ + (echo "Error: npm not found. Please install Node.js and npm." && exit 1) + +check-init: + @if [ ! -f ~/.startos/developer.key.pem ]; then \ + echo "Initializing StartOS developer environment..."; \ + start-cli init-key; \ + fi + +javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules + npm run build + +node_modules: package-lock.json + npm ci + +package-lock.json: package.json + npm i + +clean: + @echo "Cleaning up build artifacts..." + @rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk $(PACKAGE_ID)_riscv64.s9pk javascript node_modules diff --git a/scripts/.gitignore b/scripts/.gitignore deleted file mode 100644 index a6c7c28..0000000 --- a/scripts/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js diff --git a/scripts/deps.ts b/scripts/deps.ts deleted file mode 100644 index ee0dcd8..0000000 --- a/scripts/deps.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "https://deno.land/x/embassyd_sdk@v0.3.3.0.8/mod.ts"; diff --git a/scripts/embassy.ts b/scripts/embassy.ts deleted file mode 100644 index 7f7434c..0000000 --- a/scripts/embassy.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { setConfig } from "./procedures/setConfig.ts"; -export { properties } from "./procedures/properties.ts"; -export { getConfig } from "./procedures/getConfig.ts"; -export { migration } from "./procedures/migrations.ts"; -export { health } from "./procedures/healthChecks.ts"; -export { createBackup, restoreBackup } from "./procedures/backups.ts"; -// export { main } from "./procedures/main.ts"; diff --git a/scripts/procedures/backups.ts b/scripts/procedures/backups.ts deleted file mode 100644 index bb9a44a..0000000 --- a/scripts/procedures/backups.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Backups } from "../deps.ts"; - -export const { createBackup, restoreBackup } = Backups.with_options({ - exclude: [".lnd"], -}) - .volumes("main") - .build(); diff --git a/scripts/procedures/getConfig.ts b/scripts/procedures/getConfig.ts deleted file mode 100644 index fb9960b..0000000 --- a/scripts/procedures/getConfig.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { compat, types as T } from "../deps.ts"; - -export const getConfig: T.ExpectedExports.getConfig = compat.getConfig({ - "tor-address": { - "name": "Tor Address", - "description": "The Tor address of the network interface", - "type": "pointer", - "subtype": "package", - "package-id": "lightning-terminal", - "target": "tor-address", - "interface": "main", - }, - "password": { - "type": "string", - "name": "Lightning Terminal Password", - "description": "Administrator password for Lightning Terminal", - "nullable": false, - "copyable": true, - "masked": true, - "default": { - "charset": "a-z,A-Z,0-9", - "len": 22 - } - }, -}); diff --git a/scripts/procedures/healthChecks.ts b/scripts/procedures/healthChecks.ts deleted file mode 100644 index bf92dec..0000000 --- a/scripts/procedures/healthChecks.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { types as T, healthUtil } from "../deps.ts"; - -export const health: T.ExpectedExports.health = { - "web-ui": healthUtil.checkWebUrl("http://lightning-terminal.embassy:8443") -} \ No newline at end of file diff --git a/scripts/procedures/main.ts b/scripts/procedures/main.ts deleted file mode 100644 index 195b83f..0000000 --- a/scripts/procedures/main.ts +++ /dev/null @@ -1,11 +0,0 @@ -// import { types as T, util } from "../deps.ts"; - -// export const main: T.ExpectedExports.main = async (effects) => { -// await effects.runDaemon( -// { -// command: "docker_entrypoint.sh", -// args: [], -// }, -// ).wait(); -// return util.ok; -// }; diff --git a/scripts/procedures/migrations.ts b/scripts/procedures/migrations.ts deleted file mode 100644 index c96d1ec..0000000 --- a/scripts/procedures/migrations.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { compat, types as T } from "../deps.ts"; - -export const migration: T.ExpectedExports.migration = - compat.migrations.fromMapping({}, "0.12.4"); diff --git a/scripts/procedures/properties.ts b/scripts/procedures/properties.ts deleted file mode 100644 index c11acba..0000000 --- a/scripts/procedures/properties.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { compat, types as T } from "../deps.ts"; - -export const properties: T.ExpectedExports.properties = compat.properties; \ No newline at end of file diff --git a/scripts/procedures/setConfig.ts b/scripts/procedures/setConfig.ts deleted file mode 100644 index 7dc9aef..0000000 --- a/scripts/procedures/setConfig.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { - compat, -} from "../deps.ts"; -export const setConfig = compat.setConfig; diff --git a/startos/actions/index.ts b/startos/actions/index.ts new file mode 100644 index 0000000..9444842 --- /dev/null +++ b/startos/actions/index.ts @@ -0,0 +1,5 @@ +import { sdk } from '../sdk' +import { resetPassword } from './resetPassword' + +export const actions = sdk.Actions.of() + .addAction(resetPassword) diff --git a/startos/actions/resetPassword.ts b/startos/actions/resetPassword.ts new file mode 100644 index 0000000..4e17af9 --- /dev/null +++ b/startos/actions/resetPassword.ts @@ -0,0 +1,49 @@ +import { utils } from '@start9labs/start-sdk' +import { litConfig } from '../fileModels/lit.conf' +import { i18n } from '../i18n' +import { sdk } from '../sdk' + +export const resetPassword = sdk.Action.withoutInput( + // id + 'reset-password', + + // metadata + async ({ effects }) => { + const hasPass = + (await litConfig.read((c) => c.uipassword).const(effects)) !== 'null' + + return { + name: hasPass ? i18n('Reset Password') : i18n('Create Password'), + description: hasPass + ? i18n('Reset your user interface password') + : i18n('Create your user interface password'), + warning: null, + allowedStatuses: 'any', + group: null, + visibility: 'enabled', + } + }, + + // the execution function + async ({ effects }) => { + const uipassword = utils.getDefaultString({ + charset: 'a-z,A-Z,1-9,!,@,$,%,&,*', + len: 22, + }) + + await litConfig.merge(effects, { uipassword }) + + return { + version: '1', + title: i18n('Success'), + message: i18n('Your new password is below'), + result: { + type: 'single', + value: uipassword, + masked: true, + copyable: true, + qr: false, + }, + } + }, +) diff --git a/startos/backups.ts b/startos/backups.ts new file mode 100644 index 0000000..0b97d7f --- /dev/null +++ b/startos/backups.ts @@ -0,0 +1,5 @@ +import { sdk } from './sdk' + +export const { createBackup, restoreInit } = sdk.setupBackups(async () => + sdk.Backups.ofVolumes('main'), +) diff --git a/startos/dependencies.ts b/startos/dependencies.ts new file mode 100644 index 0000000..2c36827 --- /dev/null +++ b/startos/dependencies.ts @@ -0,0 +1,9 @@ +import { sdk } from './sdk' + +export const setDependencies = sdk.setupDependencies(async ({ effects }) => ({ + lnd: { + kind: 'running', + versionRange: '>=0.20.1-beta:1-beta.3', + healthChecks: ['lnd'], + }, +})) diff --git a/startos/fileModels/lit.conf.ts b/startos/fileModels/lit.conf.ts new file mode 100644 index 0000000..75994da --- /dev/null +++ b/startos/fileModels/lit.conf.ts @@ -0,0 +1,34 @@ +import { FileHelper, z } from '@start9labs/start-sdk' +import { sdk } from '../sdk' +import { litDir, lndMount, uiPort } from '../utils' + +const httpListen = `lightning-terminal.startos:${uiPort}` as const +const rpcServer = 'lnd.startos:10009' as const +const macaroonPath = + `${lndMount}/data/chain/bitcoin/mainnet/admin.macaroon` as const +const tlsCertPath = `${lndMount}/tls.cert` as const + +const shape = z.object({ + uipassword: z.string().nullable().catch(null), + 'lit-dir': z.literal(litDir).catch(litDir), + 'insecure-httplisten': z.literal(httpListen).catch(httpListen), + 'remote.lnd.rpcserver': z.literal(rpcServer).catch(rpcServer), + 'remote.lnd.macaroonpath': z.literal(macaroonPath).catch(macaroonPath), + 'remote.lnd.tlscertpath': z.literal(tlsCertPath).catch(tlsCertPath), +}) + +export const litConfig = FileHelper.ini( + { base: sdk.volumes.main, subpath: '/.lit/lit.conf' }, + shape, + { bracketedArray: false }, + { + onRead: (a) => a, + onWrite: (a) => { + const result: Record = {} + for (const [k, v] of Object.entries(a)) { + if (v != null) result[k] = String(v) + } + return result + }, + }, +) diff --git a/startos/i18n/dictionaries/default.ts b/startos/i18n/dictionaries/default.ts new file mode 100644 index 0000000..91921e2 --- /dev/null +++ b/startos/i18n/dictionaries/default.ts @@ -0,0 +1,29 @@ +export const DEFAULT_LANG = 'en_US' + +const dict = { + // main.ts + 'Starting Lightning Terminal...': 0, + 'Web Interface': 1, + 'The web interface is ready': 2, + 'The web interface is not ready': 3, + 'Create your LiT admin password': 4, + + // interfaces.ts + 'Web UI': 5, + 'The web interface of Lightning Terminal': 6, + + // actions/resetPassword.ts + 'Reset Password': 7, + 'Create Password': 8, + 'Reset your user interface password': 9, + 'Create your user interface password': 10, + Success: 11, + 'Your new password is below': 12, +} as const + +/** + * Plumbing. DO NOT EDIT. + */ +export type I18nKey = keyof typeof dict +export type LangDict = Record<(typeof dict)[I18nKey], string> +export default dict diff --git a/startos/i18n/dictionaries/translations.ts b/startos/i18n/dictionaries/translations.ts new file mode 100644 index 0000000..c3e2293 --- /dev/null +++ b/startos/i18n/dictionaries/translations.ts @@ -0,0 +1,64 @@ +import { LangDict } from './default' + +export default { + es_ES: { + 0: 'Iniciando Lightning Terminal...', + 1: 'Interfaz web', + 2: 'La interfaz web está lista', + 3: 'La interfaz web no está lista', + 4: 'Crea tu contraseña de administrador de LiT', + 5: 'Interfaz web', + 6: 'La interfaz web de Lightning Terminal', + 7: 'Restablecer contraseña', + 8: 'Crear contraseña', + 9: 'Restablece tu contraseña de interfaz de usuario', + 10: 'Crea tu contraseña de interfaz de usuario', + 11: 'Éxito', + 12: 'Tu nueva contraseña está a continuación', + } satisfies LangDict, + de_DE: { + 0: 'Lightning Terminal wird gestartet...', + 1: 'Web-Oberfläche', + 2: 'Die Web-Oberfläche ist bereit', + 3: 'Die Web-Oberfläche ist nicht bereit', + 4: 'Erstelle dein LiT-Admin-Passwort', + 5: 'Web-Oberfläche', + 6: 'Die Web-Oberfläche von Lightning Terminal', + 7: 'Passwort zurücksetzen', + 8: 'Passwort erstellen', + 9: 'Setze dein Benutzeroberflächen-Passwort zurück', + 10: 'Erstelle dein Benutzeroberflächen-Passwort', + 11: 'Erfolg', + 12: 'Dein neues Passwort wird unten angezeigt', + } satisfies LangDict, + pl_PL: { + 0: 'Uruchamianie Lightning Terminal...', + 1: 'Interfejs webowy', + 2: 'Interfejs webowy jest gotowy', + 3: 'Interfejs webowy nie jest gotowy', + 4: 'Utwórz swoje hasło administratora LiT', + 5: 'Interfejs webowy', + 6: 'Interfejs webowy Lightning Terminal', + 7: 'Resetuj hasło', + 8: 'Utwórz hasło', + 9: 'Zresetuj hasło do interfejsu użytkownika', + 10: 'Utwórz hasło do interfejsu użytkownika', + 11: 'Sukces', + 12: 'Twoje nowe hasło jest poniżej', + } satisfies LangDict, + fr_FR: { + 0: 'Démarrage de Lightning Terminal...', + 1: 'Interface web', + 2: "L'interface web est prête", + 3: "L'interface web n'est pas prête", + 4: 'Créez votre mot de passe administrateur LiT', + 5: 'Interface web', + 6: "L'interface web de Lightning Terminal", + 7: 'Réinitialiser le mot de passe', + 8: 'Créer un mot de passe', + 9: "Réinitialisez votre mot de passe d'interface utilisateur", + 10: "Créez votre mot de passe d'interface utilisateur", + 11: 'Succès', + 12: 'Votre nouveau mot de passe est ci-dessous', + } satisfies LangDict, +} as Record diff --git a/startos/i18n/index.ts b/startos/i18n/index.ts new file mode 100644 index 0000000..04cea20 --- /dev/null +++ b/startos/i18n/index.ts @@ -0,0 +1,8 @@ +/** + * Plumbing. DO NOT EDIT this file. + */ +import { setupI18n } from '@start9labs/start-sdk' +import defaultDict, { DEFAULT_LANG } from './dictionaries/default' +import translations from './dictionaries/translations' + +export const i18n = setupI18n(defaultDict, translations, DEFAULT_LANG) diff --git a/startos/index.ts b/startos/index.ts new file mode 100644 index 0000000..7af589b --- /dev/null +++ b/startos/index.ts @@ -0,0 +1,11 @@ +/** + * Plumbing. DO NOT EDIT. + */ +export { createBackup } from './backups' +export { main } from './main' +export { init, uninit } from './init' +export { actions } from './actions' +import { buildManifest } from '@start9labs/start-sdk' +import { manifest as sdkManifest } from './manifest' +import { versionGraph } from './versions' +export const manifest = buildManifest(versionGraph, sdkManifest) diff --git a/startos/init/index.ts b/startos/init/index.ts new file mode 100644 index 0000000..e4b4798 --- /dev/null +++ b/startos/init/index.ts @@ -0,0 +1,20 @@ +import { sdk } from '../sdk' +import { seedFiles } from './seedFiles' +import { taskSetPassword } from './taskSetPassword' +import { setDependencies } from '../dependencies' +import { setInterfaces } from '../interfaces' +import { versionGraph } from '../versions' +import { actions } from '../actions' +import { restoreInit } from '../backups' + +export const init = sdk.setupInit( + seedFiles, + restoreInit, + versionGraph, + setInterfaces, + setDependencies, + actions, + taskSetPassword, +) + +export const uninit = sdk.setupUninit(versionGraph) diff --git a/startos/init/seedFiles.ts b/startos/init/seedFiles.ts new file mode 100644 index 0000000..dccd55c --- /dev/null +++ b/startos/init/seedFiles.ts @@ -0,0 +1,6 @@ +import { litConfig } from '../fileModels/lit.conf' +import { sdk } from '../sdk' + +export const seedFiles = sdk.setupOnInit(async (effects) => { + await litConfig.merge(effects, {}) +}) diff --git a/startos/init/taskSetPassword.ts b/startos/init/taskSetPassword.ts new file mode 100644 index 0000000..71e8c9c --- /dev/null +++ b/startos/init/taskSetPassword.ts @@ -0,0 +1,12 @@ +import { resetPassword } from '../actions/resetPassword' +import { litConfig } from '../fileModels/lit.conf' +import { i18n } from '../i18n' +import { sdk } from '../sdk' + +export const taskSetPassword = sdk.setupOnInit(async (effects) => { + if (!(await litConfig.read((e) => e.uipassword).const(effects))) { + await sdk.action.createOwnTask(effects, resetPassword, 'critical', { + reason: i18n('Create your LiT admin password'), + }) + } +}) diff --git a/startos/interfaces.ts b/startos/interfaces.ts new file mode 100644 index 0000000..5a2c016 --- /dev/null +++ b/startos/interfaces.ts @@ -0,0 +1,25 @@ +import { sdk } from './sdk' +import { uiPort } from './utils' +import { i18n } from './i18n' + +export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { + const uiMulti = sdk.MultiHost.of(effects, 'main') + const uiMultiOrigin = await uiMulti.bindPort(uiPort, { + protocol: 'http', + }) + const ui = sdk.createInterface(effects, { + name: i18n('Web UI'), + id: 'ui', + description: i18n('The web interface of Lightning Terminal'), + type: 'ui', + masked: false, + schemeOverride: null, + username: null, + path: '', + query: {}, + }) + + const uiReceipt = await uiMultiOrigin.export([ui]) + + return [uiReceipt] +}) diff --git a/startos/main.ts b/startos/main.ts new file mode 100644 index 0000000..efa16a9 --- /dev/null +++ b/startos/main.ts @@ -0,0 +1,39 @@ +import { manifest as lndManifest } from 'lnd-startos/startos/manifest' +import { i18n } from './i18n' +import { sdk } from './sdk' +import { litDir, lndMount, uiPort } from './utils' + +export const main = sdk.setupMain(async ({ effects }) => { + console.info(i18n('Starting Lightning Terminal...')) + return sdk.Daemons.of(effects).addDaemon('lit', { + subcontainer: await sdk.SubContainer.of( + effects, + { imageId: 'lightning-terminal' }, + sdk.Mounts.of() + .mountVolume({ + volumeId: 'main', + subpath: null, + mountpoint: litDir, + readonly: false, + }) + .mountDependency({ + dependencyId: 'lnd', + volumeId: 'main', + subpath: null, + mountpoint: lndMount, + readonly: true, + }), + 'lit-sub', + ), + exec: { command: ['/bin/litd'] }, + ready: { + display: i18n('Web Interface'), + fn: () => + sdk.healthCheck.checkPortListening(effects, uiPort, { + successMessage: i18n('The web interface is ready'), + errorMessage: i18n('The web interface is not ready'), + }), + }, + requires: [], + }) +}) diff --git a/startos/manifest/i18n.ts b/startos/manifest/i18n.ts new file mode 100644 index 0000000..ddb7c75 --- /dev/null +++ b/startos/manifest/i18n.ts @@ -0,0 +1,36 @@ +export const short = { + en_US: 'Your home for Lightning liquidity', + es_ES: 'Tu hogar para la liquidez de Lightning', + de_DE: 'Ihr Zuhause für Lightning-Liquidität', + pl_PL: 'Twój dom dla płynności Lightning', + fr_FR: 'Votre plateforme pour la liquidité Lightning', +} + +export const long = { + en_US: + "A browser-based interface for managing channel liquidity on your self-hosted LND node. Visualize your channels and balances, perform submarine swaps via the Lightning Loop service, classify channels according to your node's operating mode, use Pool to earn sats by opening channels to those needing inbound liquidity.", + es_ES: + 'Una interfaz basada en navegador para gestionar la liquidez de canales en tu nodo LND auto-alojado. Visualiza tus canales y saldos, realiza intercambios submarinos a través del servicio Lightning Loop, clasifica canales según el modo de operación de tu nodo, usa Pool para ganar sats abriendo canales a quienes necesitan liquidez entrante.', + de_DE: + 'Eine browserbasierte Oberfläche zur Verwaltung der Kanal-Liquidität auf Ihrem selbst gehosteten LND-Knoten. Visualisieren Sie Ihre Kanäle und Guthaben, führen Sie U-Boot-Swaps über den Lightning Loop-Service durch, klassifizieren Sie Kanäle nach dem Betriebsmodus Ihres Knotens, nutzen Sie Pool, um Sats zu verdienen, indem Sie Kanäle für diejenigen öffnen, die eingehende Liquidität benötigen.', + pl_PL: + 'Interfejs oparty na przeglądarce do zarządzania płynnością kanałów na samodzielnie hostowanym węźle LND. Wizualizuj swoje kanały i salda, wykonuj wymiany podwodne przez usługę Lightning Loop, klasyfikuj kanały według trybu pracy węzła, używaj Pool do zarabiania satoshi otwierając kanały dla potrzebujących płynności przychodzącej.', + fr_FR: + "Une interface basée sur navigateur pour gérer la liquidité des canaux sur votre nœud LND auto-hébergé. Visualisez vos canaux et soldes, effectuez des swaps sous-marins via le service Lightning Loop, classifiez les canaux selon le mode d'exploitation de votre nœud, utilisez Pool pour gagner des sats en ouvrant des canaux à ceux qui ont besoin de liquidité entrante.", +} + +export const depLndTitle = { + en_US: 'LND', + es_ES: 'LND', + de_DE: 'LND', + pl_PL: 'LND', + fr_FR: 'LND', +} + +export const depLndDescription = { + en_US: 'Needed to communicate with the Lightning Network', + es_ES: 'Necesario para comunicarse con la Red Lightning', + de_DE: 'Benötigt für die Kommunikation mit dem Lightning-Netzwerk', + pl_PL: 'Potrzebny do komunikacji z siecią Lightning', + fr_FR: 'Nécessaire pour communiquer avec le réseau Lightning', +} diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts new file mode 100644 index 0000000..903318b --- /dev/null +++ b/startos/manifest/index.ts @@ -0,0 +1,36 @@ +import { setupManifest } from '@start9labs/start-sdk' +import { depLndDescription, depLndTitle, long, short } from './i18n' + +export const manifest = setupManifest({ + id: 'lightning-terminal', + title: 'Lightning Terminal', + license: 'mit', + packageRepo: + 'https://github.com/Start9Labs/lightning-terminal-startos/tree/update/040', + upstreamRepo: 'https://github.com/lightninglabs/lightning-terminal', + marketingUrl: 'https://lightning.engineering/', + donationUrl: null, + docsUrls: [ + 'https://docs.lightning.engineering/lightning-network-tools/lightning-terminal', + ], + description: { short, long }, + volumes: ['main'], + images: { + 'lightning-terminal': { + source: { + dockerTag: 'lightninglabs/lightning-terminal:v0.16.1-alpha', + }, + arch: ['aarch64', 'x86_64'], + }, + }, + dependencies: { + lnd: { + description: depLndDescription, + optional: false, + metadata: { + title: depLndTitle, + icon: 'https://raw.githubusercontent.com/Start9Labs/lnd-startos/6a24e93761aa9046d427d0e62021defcaf9b47f3/icon.svg', + }, + }, + }, +}) diff --git a/startos/sdk.ts b/startos/sdk.ts new file mode 100644 index 0000000..04ae4b1 --- /dev/null +++ b/startos/sdk.ts @@ -0,0 +1,9 @@ +import { StartSdk } from '@start9labs/start-sdk' +import { manifest } from './manifest' + +/** + * Plumbing. DO NOT EDIT. + * + * The exported "sdk" const is used throughout this package codebase. + */ +export const sdk = StartSdk.of().withManifest(manifest).build(true) diff --git a/startos/utils.ts b/startos/utils.ts new file mode 100644 index 0000000..aa71e24 --- /dev/null +++ b/startos/utils.ts @@ -0,0 +1,3 @@ +export const litDir = '/root' +export const lndMount = '/mnt/lnd' +export const uiPort = 8443 diff --git a/startos/versions/index.ts b/startos/versions/index.ts new file mode 100644 index 0000000..0a087c5 --- /dev/null +++ b/startos/versions/index.ts @@ -0,0 +1,7 @@ +import { VersionGraph } from '@start9labs/start-sdk' +import { v_0_16_1_alpha_1_b4 } from './v0.16.1.alpha.1.b4' + +export const versionGraph = VersionGraph.of({ + current: v_0_16_1_alpha_1_b4, + other: [], +}) diff --git a/startos/versions/v0.16.1.alpha.1.b4.ts b/startos/versions/v0.16.1.alpha.1.b4.ts new file mode 100644 index 0000000..7691c89 --- /dev/null +++ b/startos/versions/v0.16.1.alpha.1.b4.ts @@ -0,0 +1,39 @@ +import { VersionInfo, IMPOSSIBLE, YAML } from '@start9labs/start-sdk' +import { readFile, rm } from 'fs/promises' +import { litConfig } from '../fileModels/lit.conf' + +export const v_0_16_1_alpha_1_b4 = VersionInfo.of({ + version: '0.16.1-alpha:1-beta.4', + releaseNotes: { + en_US: 'Update to StartOS SDK beta.65', + es_ES: 'Actualización a StartOS SDK beta.65', + de_DE: 'Update auf StartOS SDK beta.65', + pl_PL: 'Aktualizacja do StartOS SDK beta.65', + fr_FR: 'Mise à jour vers StartOS SDK beta.65', + }, + migrations: { + up: async ({ effects }) => { + // get old config.yaml + const configYaml: + | { + password: string + } + | undefined = await readFile( + '/media/startos/volumes/main/start9/config.yaml', + 'utf-8', + ).then(YAML.parse, () => undefined) + + if (configYaml) { + await litConfig.merge(effects, { + uipassword: configYaml.password, + }) + + // remove old start9 dir + await rm('/media/startos/volumes/main/start9', { + recursive: true, + }) + } + }, + down: IMPOSSIBLE, + }, +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9a19690 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "include": ["startos/**/*.ts", "node_modules/**/startos"], + "compilerOptions": { + "target": "es2022", + "module": "None", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + } +}