diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e6803f..4924a53 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,8 +13,6 @@ concurrency: jobs: build: if: github.event.pull_request.draft == false - uses: start9labs/shared-workflows/.github/workflows/build.yml@master - # with: - # FREE_DISK_SPACE: true + uses: Start9Labs/start-technologies/.github/workflows/build.yml@master secrets: DEV_KEY: ${{ secrets.DEV_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b83acf..6dcef4a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,9 +7,8 @@ on: jobs: release: - uses: start9labs/shared-workflows/.github/workflows/release.yml@master + uses: Start9Labs/start-technologies/.github/workflows/release.yml@master with: - # FREE_DISK_SPACE: true RELEASE_REGISTRY: ${{ vars.RELEASE_REGISTRY }} S3_S9PKS_BASE_URL: ${{ vars.S3_S9PKS_BASE_URL }} secrets: diff --git a/.github/workflows/tagAndRelease.yml b/.github/workflows/tagAndRelease.yml index 2706fe5..c4031d3 100644 --- a/.github/workflows/tagAndRelease.yml +++ b/.github/workflows/tagAndRelease.yml @@ -11,10 +11,9 @@ concurrency: jobs: tag: - uses: start9labs/shared-workflows/.github/workflows/tagAndRelease.yml@master + uses: Start9Labs/start-technologies/.github/workflows/tagAndRelease.yml@master with: REFERENCE_REGISTRY: ${{ vars.REFERENCE_REGISTRY }} - # FREE_DISK_SPACE: true RELEASE_REGISTRY: ${{ vars.RELEASE_REGISTRY }} S3_S9PKS_BASE_URL: ${{ vars.S3_S9PKS_BASE_URL }} secrets: diff --git a/AGENTS.md b/AGENTS.md index fe7e122..9dd2957 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,19 @@ # AGENTS.md -You are working in a StartOS service-package repository — a repo that builds a `.s9pk` for installation on StartOS. +This is a StartOS service-package repository — it builds a `.s9pk` for StartOS. -**Before doing anything in this repo, read [CONTRIBUTING.md](./CONTRIBUTING.md) and every document it links to.** That covers what this package is, how it's built, how it ships, and the conventions to follow. Do not begin work until you have read them all. +Develop it inside a StartOS packaging workspace created by `start-cli s9pk init-workspace`, +which provides the packaging guide and agent context one level up. If you're reading this in a +bare clone with no workspace, the full guide is at . + +Work this package's `TODO.md` from top to bottom. Keep `README.md` (architecture, for developers and LLMs) and `instructions.md` (end-user docs) in sync with your changes. + +## This repo + +- **Package id is `bolt12-pay`.** Dependent of `lnd`: it reaches LND's REST and gRPC over the LXC bridge (importing the host ids `controlHostId` / `gRPCHostId` and the internal ports `restPort` / `gRPCPort` from `lnd-startos/startos/interfaces`, resolved in `startos/utils.ts` through the reactive `bridgeAddress` helper — a `.const()` per host that heals main onto the real address when LND binds; while unresolved the env vars are omitted) and injects those addresses into `assets/start.sh` as `LND_REST_URL` / `LND_GRPC_ADDRESS` — the app and the embedded `lndk` read them from there (the `lnd.startos` DNS name is gone in StartOS 0.4.0). Reads LND's TLS cert + admin macaroon off a read-only dependency mount at `/mnt/lnd`. +- **Supports LND 0.20 and 0.21** (`>=0.20.1-beta:12`). BOLT12 needs onion messages: LND advertises them natively from 0.21, while 0.20 needs the `protocol.custom-*` entries in `lnd.conf`. Setting those on 0.21 crash-loops LND (`feature bit: 39 already set`), so `startos/dependencies.ts` reads LND's installed version via `sdk.getServiceManifest(...).const()` and posts the Auto-Configure task **only** to pre-0.21 nodes, clearing it when the user upgrades. LND 0.21's config spec drops the `onion-messages` toggle entirely, so the typed action we import can't describe the field — the task goes through the raw `effects.action.createTask` effect rather than `sdk.action.createTask`. Don't "simplify" that back to the typed helper, and don't re-pin the dependency to `>=0.21`. +- **Upstream is a git submodule at `upstream/`.** The image is built from its `app/` subdir by the `Dockerfile`; the container runs `assets/start.sh`, which starts the uvicorn web app on `0.0.0.0:8081` and a background `lndk` process serving gRPC on `127.0.0.1:7000`. + +## Inspecting a running install + +To run a command inside the service's container (read its generated config, grep app logs), use `start-cli package attach bolt12-pay -n bolt12-pay-sub -- `. Select the subcontainer by **name** with `-n` (the name passed to `SubContainer.of` in `main.ts` — here `bolt12-pay-sub`) or by image with `-i`. Note: `-s/--subcontainer` matches the internal **Guid**, not the name, so passing a name to `-s` fails with "no matching subcontainers". diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index b39e9f3..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,42 +0,0 @@ -# Contributing - -## Keep these in sync - -- **[`README.md`](./README.md)** — what this package is and how it's built (image, volumes, interfaces, dependencies). Technical reference for developers and AI assistants. -- **[`instructions.md`](./instructions.md)** — the user-facing instructions packed into the `.s9pk` and shown on the **Instructions** tab in StartOS, for the person running the service. -- **[`TODO.md`](./TODO.md)** — pending work on this package. - -**Read all three before starting any work.** Any code change that affects user-visible behavior must update `README.md` and `instructions.md` in the same change; add to `TODO.md` when you defer work, and remove items when complete. Content rules: [Writing READMEs](https://docs.start9.com/packaging/writing-readmes.html), [Writing Instructions](https://docs.start9.com/packaging/writing-instructions.html). - -## Environment setup - -See [Environment Setup](https://docs.start9.com/packaging/environment-setup.html). You also need Docker with Buildx, since the service image is built locally from the [`Dockerfile`](./Dockerfile). - -## Building - -```bash -npm ci # install dependencies -make # build the universal .s9pk -``` - -The upstream lndk-pay project is a git submodule at [`upstream/`](./upstream); the image is built from its `app/` subdirectory by the [`Dockerfile`](./Dockerfile), with an LNDK runtime pulled from a prebuilt Docker Hub image. `npm ci` does not init submodules — if you cloned without `--recursive`, run `git submodule update --init` first. For a complete list of build options, see [Makefile](https://docs.start9.com/packaging/makefile.html). - -## Updating the upstream version - -See [UPDATING.md](./UPDATING.md) for the upstream (lndk-pay) and LNDK image bump procedure. After applying a bump, update `version` and `releaseNotes` in the file under `startos/versions/`, renaming it to the new version string. A _new_ version file is only needed when the bump requires a migration, or when you want the old release notes preserved in git history — see [Versions](https://docs.start9.com/packaging/versions.html). - -## CI/CD - -Three workflows under `.github/workflows/` wrap reusable workflows in [`start9labs/shared-workflows`](https://github.com/Start9Labs/shared-workflows): - -- **`build.yml`** — on PR, builds the `.s9pk` and uploads per-arch artifacts for sideload testing. -- **`release.yml`** — on `v*` tag, builds per arch and publishes to the test registry. -- **`tagAndRelease.yml`** — on push to `master`, tags `v` and runs `release.yml`, skipping if already in production. - -Promotion to `beta` and `prod` is a separate, manual step. - -## How to contribute - -1. Fork the repository and create a branch from `master`. -2. Make your changes — including the doc updates above. -3. Open a pull request to `master`. diff --git a/Makefile b/Makefile index 10d4360..8315da3 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,3 @@ ARCHES := x86 arm # overrides to s9pk.mk must precede the include statement -include s9pk.mk +include node_modules/@start9labs/start-sdk/s9pk.mk diff --git a/README.md b/README.md index ce52225..6af819e 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,12 @@ ## Image and Container Runtime -| Property | Value | -|----------|-------| -| Image | `main` — built from [`Dockerfile`](./Dockerfile) (upstream `app/` via the `upstream/` submodule + LNDK runtime) | -| Base | `python:3.11-slim` + LNDK runtime from `alex71btc/lndk` | -| Architectures | x86_64, aarch64 | -| Entrypoint | `/usr/local/bin/docker_entrypoint.sh` → `start.sh` | +| Property | Value | +| ------------- | --------------------------------------------------------------------------------------------------------------- | +| Image | `main` — built from [`Dockerfile`](./Dockerfile) (upstream `app/` via the `upstream/` submodule + LNDK runtime) | +| Base | `python:3.11-slim` + LNDK runtime from `alex71btc/lndk` | +| Architectures | x86_64, aarch64 | +| Entrypoint | `/usr/local/bin/docker_entrypoint.sh` → `start.sh` | The container runs two processes from [`assets/start.sh`](./assets/start.sh): @@ -47,9 +47,9 @@ The container runs two processes from [`assets/start.sh`](./assets/start.sh): ## Volume and Data Layout -| Volume | Mount Point | Purpose | -|--------|-------------|---------| -| `main` | `/data` | App config, secrets, and LNDK data dir (`/data/lndk`) | +| Volume | Mount Point | Purpose | +| ------ | ----------- | ----------------------------------------------------- | +| `main` | `/data` | App config, secrets, and LNDK data dir (`/data/lndk`) | **Dependency mounts:** @@ -59,9 +59,9 @@ The container runs two processes from [`assets/start.sh`](./assets/start.sh): ## Network Access and Interfaces -| Interface | Port | Protocol | Purpose | -|-----------|------|----------|---------| -| Web UI | 8081 | HTTP | BOLT12 Pay web interface | +| Interface | Port | Protocol | Purpose | +| --------- | ---- | -------- | ------------------------ | +| Web UI | 8081 | HTTP | BOLT12 Pay web interface | **Access methods (StartOS 0.4.0):** @@ -78,12 +78,12 @@ For Lightning Address / LNURL / `.well-known` endpoints to resolve publicly, exp ### Set Primary URL -| Property | Value | -|----------|-------| -| ID | `set-primary-url` | -| Visibility | Enabled | -| Availability | Any status | -| Purpose | Choose which non-local URL to advertise as the LNURL / Lightning Address base | +| Property | Value | +| ------------ | ----------------------------------------------------------------------------- | +| ID | `set-primary-url` | +| Visibility | Enabled | +| Availability | Any status | +| Purpose | Choose which non-local URL to advertise as the LNURL / Lightning Address base | Pick one of the service's non-local URLs (use a **clearnet or custom-domain** URL — Tor and `.local` won't resolve for external senders). The selection is stored on the `startos` volume and injected on next start as the app's native `LNURL_BASE_URL`, `LNURL_BASE_DOMAIN`, `PUBLIC_LNURL_ADDRESS`, and `PUBLIC_BIP353_ADDRESS` env vars. These are **defaults** — the in-app admin settings still override them. If a previously-selected URL is later removed, StartOS posts a task to pick a new one. @@ -93,15 +93,24 @@ Pick one of the service's non-local URLs (use a **clearnet or custom-domain** UR ### LND (`lnd`) -| Property | Value | -|----------|-------| -| **Required** | Yes | -| **Health checks** | `lnd` must pass | -| **Mounted volumes** | `lnd:main` at `/mnt/lnd` (read-only) — TLS cert and admin macaroon | -| **Reached at** | `lnd.startos` (REST `:8080`, gRPC `:10009`) | -| **Purpose** | Create and pay BOLT12 offers via LNDK | +| Property | Value | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Required** | Yes | +| **Health checks** | `lnd` must pass | +| **Mounted volumes** | `lnd:main` at `/mnt/lnd` (read-only) — TLS cert and admin macaroon | +| **Reached at** | LND's REST (`:8080`) and gRPC (`:10009`) over the StartOS LXC bridge — resolved from LND's `control`/`grpc` hosts and injected into `start.sh` as `LND_REST_URL` / `LND_GRPC_ADDRESS` (the `lnd.startos` DNS name is gone in StartOS 0.4.0) | +| **Purpose** | Create and pay BOLT12 offers via LNDK | -**LND must have onion-message support enabled.** BOLT12 offers require `protocol.custom-message=513`, `protocol.custom-nodeann=39`, and `protocol.custom-init=39` in `lnd.conf`. BOLT12 Pay configures this automatically: on startup it posts a task against LND's hidden **Auto-Configure** action, which the user approves with one click — no manual `lnd.conf` editing. The task uses `input-not-matches`, so it clears once the settings are present and reappears if they are ever removed. +**Supports LND 0.20 and 0.21** (`>=0.20.1-beta:12`). BOLT12 offers need onion-message support, and how it is obtained depends on the LND version — so `startos/dependencies.ts` reads LND's installed version and posts the Auto-Configure task only where it is needed: + +| Installed LND | Onion messages | Auto-Configure task | +| -------------- | ------------------------------------- | ------------------------------------------------------- | +| `>=0.21.0-beta:0` | Advertised natively (feature bit 39) | Not posted; cleared if one already exists | +| `<0.21.0-beta:0` | Require the `protocol.custom-*` entries in `lnd.conf` | Posted as `critical` against LND's hidden `autoconfig` action | + +Setting the `protocol.custom-*` entries on LND 0.21 is not merely redundant — LND aborts server creation (`feature bit: 39 already set`) and crash-loops. The version is read with `sdk.getServiceManifest(...).const()`, so upgrading LND from 0.20 to 0.21 re-runs init and clears the task instead of stranding the user with a critical task they cannot satisfy. + +LND 0.21's config spec removes the `onion-messages` toggle altogether, so the typed `autoconfig` action we import can no longer describe the field. The task is therefore posted through the raw `effects.action.createTask` effect (same `replayId` the SDK helper would derive, `lnd:autoconfig`), whose input is the OS's untyped `TaskInput`. See [instructions.md](./instructions.md) for the user-facing steps. @@ -119,15 +128,15 @@ LND credentials are not backed up here; they live on the LND package and are re- ## Health Checks -| Check | Display Name | Method | Messages | -|-------|--------------|--------|----------| -| Web UI | "Web UI" | Port 8081 listening | "BOLT12 Pay is ready" / "BOLT12 Pay web interface is not ready" | +| Check | Display Name | Method | Messages | +| ------ | ------------ | ------------------- | --------------------------------------------------------------- | +| Web UI | "Web UI" | Port 8081 listening | "BOLT12 Pay is ready" / "BOLT12 Pay web interface is not ready" | --- ## Limitations and Differences -1. **LND onion messages** — BOLT12 offers require LND's `protocol.custom-*` settings; BOLT12 Pay enables them via a one-click **Auto-Configure** task on LND (no manual `lnd.conf` editing). Requires an LND package recent enough to expose the Auto-Configure action. +1. **LND onion messages** — BOLT12 offers require onion-message support. LND provides it natively from 0.21; on LND 0.20 it is enabled through a one-click Auto-Configure task. BOLT12 Pay accepts LND `>=0.20.1-beta:12` and posts the task only to pre-0.21 nodes. 2. **LNURL base URL** — seeded from the **Set Primary URL** action; the in-app admin settings can still override it. All other app configuration is done inside the web UI. 3. **Mainnet only** — the LND macaroon path is pinned to `data/chain/bitcoin/mainnet`. @@ -135,7 +144,7 @@ LND credentials are not backed up here; they live on the LND package and are re- ## Contributing -See [CONTRIBUTING.md](CONTRIBUTING.md) for build instructions and development workflow. +See [AGENTS.md](AGENTS.md) for repo conventions and the [StartOS packaging guide](https://docs.start9.com/packaging) for build instructions and development workflow. --- @@ -152,12 +161,11 @@ volumes: startos: (StartOS metadata; not mounted into the container) dependency_mounts: lnd: /mnt/lnd (read-only) +lnd_reached_at: LXC bridge (REST :8080, gRPC :10009), injected into start.sh as LND_REST_URL / LND_GRPC_ADDRESS ports: ui: 8081 dependencies: - - lnd (required; onion messages enabled via a one-click task against lnd's hidden `autoconfig` action — writes protocol.custom-message=513/nodeann=39/init=39) + - lnd (required, >=0.20.1-beta:12; onion messages native on 0.21, Auto-Configure task posted on 0.20) actions: - set-primary-url -tasks_posted: - - lnd/autoconfig (critical, input-not-matches: { onion-messages: true }) ``` diff --git a/assets/start.sh b/assets/start.sh index 6a086ed..d81da3e 100755 --- a/assets/start.sh +++ b/assets/start.sh @@ -16,13 +16,13 @@ export HOST=0.0.0.0 export PORT=8081 export PYTHONPATH=/app -# LND is a required StartOS dependency, reachable at lnd.startos. Its data -# directory (TLS cert + macaroons) is mounted read-only at /mnt/lnd. +# LND is a required StartOS dependency, reached over the StartOS LXC bridge. +# LND_REST_URL (REST) and LND_GRPC_ADDRESS (gRPC) are injected by the StartOS +# service — its `lnd.startos` DNS name is gone. Its data directory (TLS cert + +# macaroons) is mounted read-only at /mnt/lnd. export LND_DIR=/mnt/lnd -export LND_HOST=lnd.startos export LND_TLS_CERT_PATH="$LND_DIR/tls.cert" export LND_MACAROON_PATH="$LND_DIR/data/chain/bitcoin/mainnet/admin.macaroon" -export LND_REST_URL="https://${LND_HOST}:8080" export LND_REST_INSECURE=true export LNDK_CLI=/usr/local/bin/lndk-cli @@ -55,18 +55,18 @@ while [ ! -f "$LND_TLS_CERT_PATH" ] || [ ! -f "$LND_MACAROON_PATH" ]; do sleep 5 done -echo "Starting LNDK background loop against ${LND_HOST}..." +echo "Starting LNDK background loop against ${LND_GRPC_ADDRESS}..." ( while true; do if ! curl -ksS --connect-timeout 3 "${LND_REST_URL}/v1/getinfo" >/dev/null 2>&1; then - echo "LND REST not ready yet on ${LND_HOST}, retrying..." + echo "LND REST not ready yet at ${LND_REST_URL}, retrying..." sleep 5 continue fi - echo "Starting LNDK against ${LND_HOST}..." + echo "Starting LNDK against ${LND_GRPC_ADDRESS}..." lndk \ - --address="https://${LND_HOST}:10009" \ + --address="${LND_GRPC_ADDRESS}" \ --cert-path="$LND_TLS_CERT_PATH" \ --macaroon-path="$LND_MACAROON_PATH" \ --data-dir=/data/lndk \ diff --git a/instructions.md b/instructions.md index d0372c5..4cd9cf4 100644 --- a/instructions.md +++ b/instructions.md @@ -2,35 +2,34 @@ ## Documentation -* [lndk-pay on GitHub](https://github.com/Alex71btc/lndk-pay#-bolt12-pay) — the upstream project. -* [LNDK](https://github.com/lndk-org/lndk) — the BOLT12 runtime BOLT12 Pay embeds. +- [lndk-pay on GitHub](https://github.com/Alex71btc/lndk-pay#-bolt12-pay) — the upstream project. +- [LNDK](https://github.com/lndk-org/lndk) — the BOLT12 runtime BOLT12 Pay embeds. ## What you get on StartOS -* A **Web UI** for a self-hosted Lightning payment endpoint: create and pay **BOLT12 offers**, plus **LNURL**, **Lightning Address** (BIP353), and **BOLT11** support. -* An embedded **LNDK** runtime that talks to your StartOS **LND** node to handle BOLT12 offers and payments. +- A **Web UI** for a self-hosted Lightning payment endpoint: create and pay **BOLT12 offers**, plus **LNURL**, **Lightning Address** (BIP353), and **BOLT11** support. +- An embedded **LNDK** runtime that talks to your StartOS **LND** node to handle BOLT12 offers and payments. -## Before you start: enable onion messages on LND +## Before you start -BOLT12 offers require onion-message support on your LND node. +BOLT12 offers require onion-message support on your LND node. BOLT12 Pay works with **LND 0.20 and 0.21**, and handles the difference for you: -You no longer need to edit `lnd.conf` by hand. When BOLT12 Pay starts, it posts a one-click task to your LND service. Open **LND** (or follow the prompt from BOLT12 Pay's dependency status) and approve LND's **Auto-Configure** task: LND writes the required onion-message settings (`protocol.custom-message=513`, `protocol.custom-nodeann=39`, `protocol.custom-init=39`) to `lnd.conf` for you. +- **LND 0.21 or newer** — onion messages are advertised natively. There is nothing to configure and no setup task appears. +- **LND 0.20** — onion messages must be switched on in `lnd.conf`. BOLT12 Pay posts a one-click **Auto-Configure** task against LND; approve it and the setting is applied for you. No SSH or manual config editing. -The settings persist across LND restarts. The task clears automatically once they're set and reappears if they're ever removed, so you can always re-enable onion messages with one click. This requires an LND package recent enough to expose the Auto-Configure action. +If you later upgrade LND from 0.20 to 0.21, the task disappears on its own — LND 0.21 rejects the old override, so BOLT12 Pay stops asking for it. ### Requirements for BOLT12 offers Creating BOLT12 offers also requires: -* at least one active public Lightning channel -* a fully synced LND node - -Without onion messages enabled, the rest of the app still works, but BOLT12 offers will not function. +- at least one active public Lightning channel +- a fully synced LND node ## Getting set up -1. Install and fully sync **LND**, then approve the onion-message task described above. -2. Install **BOLT12 Pay** and start it. It connects to LND automatically at `lnd.startos` using the read-only credentials mounted from the LND package — no macaroon copying required. +1. Install and fully sync **LND** (0.20 or newer). +2. Install **BOLT12 Pay** and start it. It connects to LND automatically over the internal network using the read-only credentials mounted from the LND package — no macaroon copying required. 3. Open the **Web UI** interface. ## Public access (LNURL & Lightning Address) @@ -46,9 +45,9 @@ For Tor or LAN-only use, BOLT12 offers and BOLT11 still work; only the public LN When using Cloudflare DNS automation, Lightning Address (BIP353), or LNURL, make sure you understand the difference between: -* Cloudflare Zone Domain -* BIP353 Address Domain -* LNURL Domain/Subdomain +- Cloudflare Zone Domain +- BIP353 Address Domain +- LNURL Domain/Subdomain Detailed setup instructions: @@ -56,12 +55,11 @@ https://github.com/Alex71btc/lndk-pay/blob/main/README.md#-domain-configuration- The guide includes: -* recommended domain structure -* root domain vs subdomain examples -* Cloudflare Zone configuration -* common setup mistakes +- recommended domain structure +- root domain vs subdomain examples +- Cloudflare Zone configuration +- common setup mistakes ## Using BOLT12 Pay The Web UI is the application itself — create offers, generate payment pages, and manage LNURL / Lightning Address settings. The upstream documentation applies once you're in. - diff --git a/package-lock.json b/package-lock.json index 2f8fe9b..6de1ee8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "bolt12-pay-startos", "dependencies": { - "@start9labs/start-sdk": "^1.5.3", + "@start9labs/start-sdk": "2.0.6", "bitcoin-core-startos": "github:Start9Labs/bitcoin-core-startos#next/28.x", "lnd-startos": "github:Start9Labs/lnd-startos#next" }, @@ -14,72 +14,1520 @@ "@types/node": "^22.19.0", "@vercel/ncc": "^0.38.4", "prettier": "^3.6.2", - "typescript": "^5.9.3" + "typescript": "^6.0.3" } }, - "node_modules/@iarna/toml": { + "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/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-2.0.6.tgz", + "integrity": "sha512-0aTCnCvDZWp3UGqmyB8xQL1A0mWYlIwbTdHyQQkha/aOAzFpTIC6MIwkDCLnXLpoFqhF6btbBPp9hIMKwsRBOw==", + "bundleDependencies": [ + "@start9labs/start-core", + "eslint", + "typescript-eslint" + ], + "license": "MIT", + "dependencies": { + "@iarna/toml": "^3.0.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0", + "@start9labs/start-core": "file:./node_modules/@start9labs/start-core", + "@types/ini": "^4.1.1", + "deep-equality-data-structures": "^2.0.0", + "eslint": "^9.39.4", + "fast-xml-parser": "~5.7.0", + "ini": "^5.0.0", + "isomorphic-fetch": "^3.0.0", + "mime": "^4.1.0", + "typescript-eslint": "^8.61.0", + "yaml": "^2.8.3", + "zod": "4.4.3", + "zod-deep-partial": "^1.2.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-array": { + "version": "0.21.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/core": { + "version": "0.17.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "inBundle": true, + "license": "Python-2.0" + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/js": { + "version": "9.39.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/object-schema": { + "version": "2.1.7", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanfs/core": { + "version": "0.19.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanfs/node": { + "version": "0.16.8", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanfs/types": { + "version": "0.15.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@iarna/toml": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@start9labs/start-sdk/node_modules/@noble/curves": { + "version": "1.9.7", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@noble/hashes": { + "version": "1.8.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core": { + "inBundle": true, + "license": "MIT", + "dependencies": { + "@iarna/toml": "^3.0.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0", + "deep-equality-data-structures": "^2.0.0", + "isomorphic-fetch": "^3.0.0", + "mime": "^4.1.0", + "yaml": "^2.8.3", + "zod": "4.4.3", + "zod-deep-partial": "^1.4.4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/deep-equality-data-structures": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "object-hash": "^3.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@start9labs/start-core/node_modules/zod": { + "version": "4.4.3", + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@types/estree": { + "version": "1.0.9", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/@types/json-schema": { + "version": "7.0.15", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/type-utils": "8.61.0", + "@typescript-eslint/utils": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.61.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/parser": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/project-service": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.61.0", + "@typescript-eslint/types": "^8.61.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/scope-manager": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/type-utils": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/utils": "8.61.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/types": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.61.0", + "@typescript-eslint/tsconfig-utils": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/visitor-keys": "8.61.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/utils": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.61.0", + "@typescript-eslint/types": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.61.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/acorn": { + "version": "8.16.0", + "inBundle": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/acorn-jsx": { + "version": "5.3.2", + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ajv": { + "version": "6.15.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/callsites": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/chalk": { + "version": "4.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/concat-map": { + "version": "0.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/cross-spawn": { + "version": "7.0.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/debug": { + "version": "4.4.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@start9labs/start-sdk/node_modules/deep-is": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint": { + "version": "9.39.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint-scope": { + "version": "8.4.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/espree": { + "version": "10.4.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/esquery": { + "version": "1.7.0", + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/esrecurse": { + "version": "4.3.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/estraverse": { + "version": "5.3.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/esutils": { + "version": "2.0.3", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/fast-deep-equal": { + "version": "3.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/fast-levenshtein": { + "version": "2.0.6", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/fdir": { + "version": "6.5.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@start9labs/start-sdk/node_modules/file-entry-cache": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/flat-cache": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/flatted": { + "version": "3.4.2", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@start9labs/start-sdk/node_modules/globals": { + "version": "14.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/has-flag": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ignore": { + "version": "5.3.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/import-fresh": { + "version": "3.3.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/is-extglob": { + "version": "2.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/is-glob": { + "version": "4.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/@start9labs/start-sdk/node_modules/isomorphic-fetch": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/json-buffer": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/json-schema-traverse": { + "version": "0.4.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/keyv": { + "version": "4.5.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/levn": { + "version": "0.4.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/lodash.merge": { + "version": "4.6.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/mime": { + "version": "4.1.0", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "inBundle": true, + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/natural-compare": { + "version": "1.4.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/node-fetch": { + "version": "2.7.0", + "inBundle": true, + "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/@start9labs/start-sdk/node_modules/object-hash": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/optionator": { + "version": "0.9.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/p-limit": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/parent-module": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/path-exists": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/picomatch": { + "version": "4.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/prelude-ls": { + "version": "1.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/punycode": { + "version": "2.3.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/shebang-regex": { "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" + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "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==", + "node_modules/@start9labs/start-sdk/node_modules/strip-json-comments": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/supports-color": { + "version": "7.2.0", + "inBundle": true, "license": "MIT", "dependencies": { - "@noble/hashes": "1.8.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "^14.21.3 || >=16" + "node": ">=8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/tinyglobby": { + "version": "0.2.17", + "inBundle": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" }, "funding": { - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "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==", + "node_modules/@start9labs/start-sdk/node_modules/tr46": { + "version": "0.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/@start9labs/start-sdk/node_modules/ts-api-utils": { + "version": "2.5.0", + "inBundle": true, "license": "MIT", "engines": { - "node": "^14.21.3 || >=16" + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/type-check": { + "version": "0.4.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/typescript-eslint": { + "version": "8.61.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.61.0", + "@typescript-eslint/parser": "8.61.0", + "@typescript-eslint/typescript-estree": "8.61.0", + "@typescript-eslint/utils": "8.61.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://paulmillr.com/funding/" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@nodable/entities": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.1.tgz", - "integrity": "sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], + "node_modules/@start9labs/start-sdk/node_modules/uri-js": { + "version": "4.4.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/webidl-conversions": { + "version": "3.0.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/@start9labs/start-sdk/node_modules/whatwg-fetch": { + "version": "3.6.20", + "inBundle": true, "license": "MIT" }, - "node_modules/@start9labs/start-sdk": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-1.5.3.tgz", - "integrity": "sha512-OyHe9J6hMvyA5ZavcLkxdVQvZcuTH9J9kagV6NDI83eAG/YpJFIq62gP/n/2PPNdHWwNSXVQmSwnsvsV8Gyg+A==", + "node_modules/@start9labs/start-sdk/node_modules/whatwg-url": { + "version": "5.0.0", + "inBundle": true, "license": "MIT", "dependencies": { - "@iarna/toml": "^3.0.0", - "@noble/curves": "^1.9.7", - "@noble/hashes": "^1.8.0", - "@types/ini": "^4.1.1", - "deep-equality-data-structures": "^2.0.0", - "fast-xml-parser": "~5.7.0", - "ini": "^5.0.0", - "isomorphic-fetch": "^3.0.0", - "mime": "^4.1.0", - "yaml": "^2.8.3", - "zod": "4.3.6", - "zod-deep-partial": "^1.2.0" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/word-wrap": { + "version": "1.2.5", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/yaml": { + "version": "2.9.0", + "inBundle": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/yocto-queue": { + "version": "0.1.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@start9labs/start-sdk/node_modules/zod-deep-partial": { + "version": "1.4.4", + "inBundle": true, + "license": "MIT", + "peerDependencies": { + "zod": "^4.1.13" } }, "node_modules/@types/ini": { @@ -89,9 +1537,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", - "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", + "version": "22.20.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.0.tgz", + "integrity": "sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==", "dev": true, "license": "MIT", "dependencies": { @@ -108,32 +1556,24 @@ "ncc": "dist/ncc/cli.js" } }, - "node_modules/bitcoin-core-startos": { - "name": "bitcoin-core", - "resolved": "git+ssh://git@github.com/Start9Labs/bitcoin-core-startos.git#cfe246bbb5e9806fb4498b02ec9d542452c25b2d", - "dependencies": { - "@start9labs/start-sdk": "1.5.2", - "diskusage": "^1.2.0" - } + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" }, - "node_modules/bitcoin-core-startos/node_modules/@start9labs/start-sdk": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-1.5.2.tgz", - "integrity": "sha512-3L4OKuH9kUtuH6G20BSPk85yN/jS3+0WNkP19ahwO9Nc7L6STel4hujvKii3osf0p0tU2q5Mk2Y8b9f2dVR1ng==", - "license": "MIT", + "node_modules/bitcoin-core-startos": { + "resolved": "git+ssh://git@github.com/Start9Labs/bitcoin-core-startos.git#b6bea2df9716acbebddda444299ac2838c05f2f7", "dependencies": { - "@iarna/toml": "^3.0.0", - "@noble/curves": "^1.9.7", - "@noble/hashes": "^1.8.0", - "@types/ini": "^4.1.1", - "deep-equality-data-structures": "^2.0.0", - "fast-xml-parser": "~5.7.0", - "ini": "^5.0.0", - "isomorphic-fetch": "^3.0.0", - "mime": "^4.1.0", - "yaml": "^2.8.3", - "zod": "4.3.6", - "zod-deep-partial": "^1.2.0" + "@start9labs/start-sdk": "2.0.6", + "diskusage": "^1.2.0", + "tor-startos": "github:Start9Labs/tor-startos#next" } }, "node_modules/deep-equality-data-structures": { @@ -163,9 +1603,9 @@ "license": "MIT" }, "node_modules/fast-xml-builder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.1.tgz", + "integrity": "sha512-tPb5TTWfgfVx5BNSi2xV0eLr89POeXXn0dXIsCJ9m1narrWxeIyx6je9d7Rce/3NyXLbvuQmLkxq+RuxMWejvw==", "funding": [ { "type": "github", @@ -208,23 +1648,14 @@ "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#1453fd1030ccff543b7ec13e6a7250ae7ab76d7d", + "resolved": "git+ssh://git@github.com/Start9Labs/lnd-startos.git#43233befea6f6148f9aefa8a892cca31d72b551e", "dependencies": { - "@start9labs/start-sdk": "1.5.3", + "@start9labs/start-sdk": "2.0.6", "bitcoin-core-startos": "github:Start9Labs/bitcoin-core-startos#next/28.x", "mime": "^4.0.7", - "rfc4648": "1.5.4" + "rfc4648": "1.5.4", + "tor-startos": "github:Start9Labs/tor-startos#next" } }, "node_modules/mime": { @@ -243,31 +1674,11 @@ } }, "node_modules/nan": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", - "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==", + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", + "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", "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", @@ -278,9 +1689,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.1.tgz", + "integrity": "sha512-h7bxdzhHk8Knyc4Tj+jMaa7fEEoUJy7p1qtbVgkYg1Uhpe5Np5VuGXCRZnkZvU+Q42M1vStt0ifa3ueykRJPmQ==", "funding": [ { "type": "github", @@ -293,9 +1704,9 @@ } }, "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.4.tgz", + "integrity": "sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==", "dev": true, "license": "MIT", "bin": { @@ -315,28 +1726,32 @@ "license": "MIT" }, "node_modules/strnum": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } }, - "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/tor-startos": { + "resolved": "git+ssh://git@github.com/Start9Labs/tor-startos.git#d13799d9b08f8da41d27d9ec0f47d9c80c8e0199", + "dependencies": { + "@noble/curves": "*", + "@start9labs/start-sdk": "2.0.6", + "rfc4648": "^1.5.4" + } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -353,28 +1768,6 @@ "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/xml-naming": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", @@ -390,39 +1783,14 @@ "node": ">=16.0.0" } }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "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==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "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 index f7dba21..e0e7398 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^1.5.3", + "@start9labs/start-sdk": "2.0.6", "bitcoin-core-startos": "github:Start9Labs/bitcoin-core-startos#next/28.x", "lnd-startos": "github:Start9Labs/lnd-startos#next" }, @@ -14,7 +14,7 @@ "@types/node": "^22.19.0", "@vercel/ncc": "^0.38.4", "prettier": "^3.6.2", - "typescript": "^5.9.3" + "typescript": "^6.0.3" }, "prettier": { "trailingComma": "all", diff --git a/s9pk.mk b/s9pk.mk deleted file mode 100644 index 25538af..0000000 --- a/s9pk.mk +++ /dev/null @@ -1,141 +0,0 @@ -# ** 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) -# Resolve the actual git dir so this works inside git worktrees, where .git -# is a file pointing at
/.git/worktrees/ rather than a directory. -GIT_DIR := $(shell git rev-parse --git-dir 2>/dev/null) -GIT_DEPS := $(if $(GIT_DIR),$(GIT_DIR)/HEAD $(GIT_DIR)/index) -ARCHES ?= x86 arm riscv -# TARGETS is the list of leaf make-targets the build matrix fans out over. -# Defaults to the arches; variant packages override (e.g. immich, ollama, vllm -# set this to a list of variant or variant-arch leaf targets). -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) - -# Generic make-variable introspection. Used by the release workflow to -# read $(TARGETS) and fan out one matrix runner per target. `make -s -# print-TARGETS` echoes the list with no other output. -print-%: - @echo '$($*)' - -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_DEPS) - @$(MAKE) --no-print-directory ingredients - @echo " Packing '$@'..." - start-cli s9pk pack -o $@ - -$(BASE_NAME)_%.s9pk: $(INGREDIENTS) $(GIT_DEPS) - @$(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 check - 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/startos/dependencies.ts b/startos/dependencies.ts index 447785e..2a5ce58 100644 --- a/startos/dependencies.ts +++ b/startos/dependencies.ts @@ -1,17 +1,44 @@ -import { autoconfig } from 'lnd-startos/startos/actions/config/autoconfig' +import { ExtendedVersion, VersionRange } from '@start9labs/start-sdk' import { sdk } from './sdk' +// LND advertises onion messages natively from 0.21 (feature bit 39); below that +// BOLT12 needs them turned on in lnd.conf. Setting them on 0.21 is not merely +// redundant — LND aborts server creation ("feature bit: 39 already set") and +// crash-loops — so the Auto-Configure task is posted only to pre-0.21 nodes, and +// cleared once the user upgrades. LND 0.21's config spec drops the toggle +// entirely, which is why the task is posted through the raw effect rather than +// `sdk.action.createTask`: the typed action we import is 0.21's, and it can no +// longer describe a field that only pre-0.21 LND accepts. +const nativeOnionMessages = VersionRange.parse('>=0.21.0-beta:0') + export const setDependencies = sdk.setupDependencies(async ({ effects }) => { - // BOLT12 offers require onion-message support on LND. Post a one-click task - // against LND's hidden autoconfig action instead of asking the user to edit - // lnd.conf over SSH. With `input-not-matches`, the task clears once onion - // messages are enabled and re-appears if the setting is ever reverted. - await sdk.action.createTask(effects, 'lnd', autoconfig, 'critical', { - input: { kind: 'partial', value: { 'onion-messages': true } }, - when: { condition: 'input-not-matches', once: false }, - reason: - 'BOLT12 Pay needs onion messages enabled on LND to create and pay BOLT12 offers.', - }) + // `.const()` re-runs init when LND's version changes, so upgrading LND to 0.21 + // clears the task rather than leaving a critical one the user cannot satisfy. + const lndVersion = await sdk + .getServiceManifest(effects, 'lnd', (m) => m?.version ?? null) + .const() + + if ( + lndVersion && + !ExtendedVersion.parse(lndVersion).satisfies(nativeOnionMessages) + ) { + await effects.action.createTask({ + replayId: 'lnd:autoconfig', + packageId: 'lnd', + actionId: 'autoconfig', + severity: 'critical', + when: { condition: 'input-not-matches', once: false }, + input: { + kind: 'partial', + accept: [{ 'onion-messages': true }], + set: { 'onion-messages': true }, + }, + reason: + 'BOLT12 Pay needs onion messages enabled on LND to create and pay BOLT12 offers.', + }) + } else { + await sdk.action.clearTask(effects, 'lnd:autoconfig') + } return { lnd: { diff --git a/startos/interfaces.ts b/startos/interfaces.ts index 0e24bee..7258bd5 100644 --- a/startos/interfaces.ts +++ b/startos/interfaces.ts @@ -1,15 +1,15 @@ import { sdk } from './sdk' -import { uiPort } from './utils' +import { uiHostId, uiInterfaceId, uiPort } from './utils' export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { - const uiMulti = sdk.MultiHost.of(effects, 'main') + const uiMulti = sdk.MultiHost.of(effects, uiHostId) const uiMultiOrigin = await uiMulti.bindPort(uiPort, { protocol: 'http', }) const ui = sdk.createInterface(effects, { name: 'Web UI', - id: 'ui', + id: uiInterfaceId, description: 'The web interface of BOLT12 Pay', type: 'ui', masked: false, diff --git a/startos/main.ts b/startos/main.ts index c7f8401..f21e33f 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -1,6 +1,6 @@ import { sdk } from './sdk' import { storeJson } from './fileModels/store.json' -import { lnurlEnv, uiPort } from './utils' +import { lndBridgeEnv, lnurlEnv, uiPort } from './utils' export const main = sdk.setupMain(async ({ effects }) => { console.info('Starting BOLT12 Pay!') @@ -9,8 +9,12 @@ export const main = sdk.setupMain(async ({ effects }) => { // env defaults. Re-runs main when the "Set Primary URL" action changes it. const primaryUrl = await storeJson.read((s) => s.primaryUrl).const(effects) + // LND's `lnd.startos` DNS name is gone in 2.0; resolve its REST + gRPC over + // the LXC bridge and hand them to start.sh. Re-runs main if either moves. + const lndEnv = await lndBridgeEnv(effects) + return sdk.Daemons.of(effects).addDaemon('primary', { - subcontainer: await sdk.SubContainer.of( + subcontainer: sdk.SubContainer.of( effects, { imageId: 'main' }, sdk.Mounts.of() @@ -31,7 +35,7 @@ export const main = sdk.setupMain(async ({ effects }) => { ), exec: { command: ['/usr/local/bin/docker_entrypoint.sh'], - env: lnurlEnv(primaryUrl), + env: { ...lndEnv, ...lnurlEnv(primaryUrl) }, }, ready: { display: 'Web UI', diff --git a/startos/manifest/i18n.ts b/startos/manifest/i18n.ts index 485a3c2..3095301 100644 --- a/startos/manifest/i18n.ts +++ b/startos/manifest/i18n.ts @@ -22,13 +22,13 @@ export const long = { export const depLndDescription = { en_US: - 'Used to create and pay BOLT12 offers via LNDK. LND must have onion-message support enabled (protocol.custom-message=513, protocol.custom-nodeann=39, protocol.custom-init=39).', + 'Used to create and pay BOLT12 offers via LNDK. Works with LND 0.20 and 0.21. LND 0.21 supports the onion messages BOLT12 needs natively; on LND 0.20 a one-click Auto-Configure task enables them in lnd.conf for you.', es_ES: - 'Se utiliza para crear y pagar ofertas BOLT12 mediante LNDK. LND debe tener habilitado el soporte de mensajes onion (protocol.custom-message=513, protocol.custom-nodeann=39, protocol.custom-init=39).', + 'Se utiliza para crear y pagar ofertas BOLT12 mediante LNDK. Funciona con LND 0.20 y 0.21. LND 0.21 admite de forma nativa los mensajes onion que BOLT12 necesita; en LND 0.20, una tarea de Configuración Automática de un clic los habilita por usted en lnd.conf.', de_DE: - 'Wird verwendet, um BOLT12-Angebote über LNDK zu erstellen und zu bezahlen. LND muss die Onion-Message-Unterstützung aktiviert haben (protocol.custom-message=513, protocol.custom-nodeann=39, protocol.custom-init=39).', + 'Wird verwendet, um BOLT12-Angebote über LNDK zu erstellen und zu bezahlen. Funktioniert mit LND 0.20 und 0.21. LND 0.21 unterstützt die von BOLT12 benötigten Onion-Nachrichten nativ; bei LND 0.20 aktiviert eine Ein-Klick-Aufgabe zur automatischen Konfiguration sie für Sie in der lnd.conf.', pl_PL: - 'Używany do tworzenia i opłacania ofert BOLT12 za pośrednictwem LNDK. LND musi mieć włączoną obsługę wiadomości onion (protocol.custom-message=513, protocol.custom-nodeann=39, protocol.custom-init=39).', + 'Używany do tworzenia i opłacania ofert BOLT12 za pośrednictwem LNDK. Działa z LND 0.20 i 0.21. LND 0.21 natywnie obsługuje wiadomości onion potrzebne dla BOLT12; w LND 0.20 jednokliknięciowe zadanie automatycznej konfiguracji włącza je za Ciebie w lnd.conf.', fr_FR: - 'Utilisé pour créer et payer des offres BOLT12 via LNDK. LND doit avoir la prise en charge des messages onion activée (protocol.custom-message=513, protocol.custom-nodeann=39, protocol.custom-init=39).', + 'Utilisé pour créer et payer des offres BOLT12 via LNDK. Fonctionne avec LND 0.20 et 0.21. LND 0.21 prend en charge nativement les messages onion dont BOLT12 a besoin ; sur LND 0.20, une tâche de configuration automatique en un clic les active pour vous dans lnd.conf.', } diff --git a/startos/utils.ts b/startos/utils.ts index 4528f12..b452523 100644 --- a/startos/utils.ts +++ b/startos/utils.ts @@ -1,14 +1,113 @@ import { T } from '@start9labs/start-sdk' +import { + controlHostId, + gRPCHostId, + gRPCPort, + restPort, +} from 'lnd-startos/startos/interfaces' import { sdk } from './sdk' export const uiPort = 8081 +// Host id (the `sdk.MultiHost.of` group) carrying the ui interface — distinct +// from the interface id exported on it. Used for `sdk.host.getOwn` lookups. +export const uiHostId = 'main' +export const uiInterfaceId = 'ui' + export async function getNonLocalUrls(effects: T.Effects): Promise { - return sdk.serviceInterface - .getOwn(effects, 'ui', (i) => i?.addressInfo?.nonLocal.format() || []) + return sdk.host + .getOwn(effects, uiHostId, (host) => { + const iface = + host && + Object.values(host.bindings) + .flatMap((b) => Object.values(b.interfaces)) + .find((i) => i.id === uiInterfaceId) + return iface ? iface.addressInfo.nonLocal.format() : [] + }) .const() } +/** + * Bridge address (`10.0.3.1:`) of a dependency's + * binding, as a minimal reactive value. Chain `.const()` in main: the mapped + * string only changes when the address itself does, so main restarts exactly + * on dependency install/uninstall/port-change and never on dependency + * updates. Chain `.once()` in an action context. `fallbackPort` keeps the + * value non-null while the dependency is absent — sanctioned only for tor's + * allocator-guaranteed SOCKS 9050. Drop-in for the planned SDK + * `sdk.host.getBridgeAddress` helper. + */ +export function bridgeAddress( + effects: T.Effects, + opts: { + packageId: string + hostId: string + internalPort: number + fallbackPort: number + }, +): { const(): Promise; once(): Promise } +export function bridgeAddress( + effects: T.Effects, + opts: { packageId: string; hostId: string; internalPort: number }, +): { const(): Promise; once(): Promise } +export function bridgeAddress( + effects: T.Effects, + opts: { + packageId: string + hostId: string + internalPort: number + fallbackPort?: number + }, +) { + const watchable = async () => { + const osIp = await sdk.getOsIp(effects) + return sdk.host.get( + effects, + { packageId: opts.packageId, hostId: opts.hostId }, + (host) => { + const port = + host?.bindings[opts.internalPort]?.net.assignedPort ?? + opts.fallbackPort + if (port == null) return null + return `${osIp}:${port}` + }, + ) + } + return { + const: async () => (await watchable()).const(), + once: async () => (await watchable()).once(), + } +} + +/** + * LND's REST + gRPC addresses over the LXC bridge, injected into start.sh as + * LND_REST_URL / LND_GRPC_ADDRESS. LND's `lnd.startos` DNS name is gone in 2.0; + * its StartOS-issued TLS cert now covers the bridge address, so lndk and curl + * still pin it. LND's REST/gRPC bindings appear only once its wallet macaroon + * exists, so each address resolves null until the first unlock — the env var is + * omitted meanwhile (start.sh blocks on its own cert/macaroon wait and the + * health check stays red until LND is reachable), and the `.const()` heals main + * onto the real address (one restart) when LND binds. + */ +export async function lndBridgeEnv( + effects: T.Effects, +): Promise> { + const rest = await bridgeAddress(effects, { + packageId: 'lnd', + hostId: controlHostId, + internalPort: restPort, + }).const() + const grpc = await bridgeAddress(effects, { + packageId: 'lnd', + hostId: gRPCHostId, + internalPort: gRPCPort, + }).const() + return { + ...(rest && { LND_REST_URL: `https://${rest}` }), + ...(grpc && { LND_GRPC_ADDRESS: `https://${grpc}` }), + } +} + /** * Map the StartOS primary URL onto the app's native LNURL / BIP353 env vars. * Upstream reads these as defaults; the in-app admin config still overrides diff --git a/startos/versions/current.ts b/startos/versions/current.ts index 4d37460..ba006c9 100644 --- a/startos/versions/current.ts +++ b/startos/versions/current.ts @@ -1,13 +1,20 @@ import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk' export const current = VersionInfo.of({ - version: '0.3.0:2', + version: '0.3.0:3', - -releaseNotes: { - en_US: - 'Follow-up release for Transaction History. Increases the default Transaction History API limit from 10 to 200 entries and restores Nostr Wallet Connect (NWC) initialization after the admin page startup refactor. Re-tested on LND v0.20.x and v0.21.x with BOLT12 Offers, LNURL, Lightning Address, BOLT11, NWC, Keysend and external wallet payments.', -}, + releaseNotes: { + en_US: + 'Fixes the Auto-Configure task on LND 0.21. LND advertises onion messages natively from 0.21 and no longer exposes the setting at all, so the task was being posted to 0.21 nodes as a critical item that could never be satisfied — and enabling it anyway crash-loops LND. The task is now posted only to LND versions that actually need it (below 0.21), and clears itself automatically when LND is upgraded. Dependency addresses are also resolved reactively now, so BOLT12 Pay no longer restarts every time Bitcoin Core or LND updates, and recovers on its own when a dependency is installed after it, removed, or changes port. Also includes internal updates for start-sdk 2.0.x.', + es_ES: + 'Corrige la tarea de Configuración automática en LND 0.21. LND anuncia los mensajes onion de forma nativa a partir de la 0.21 y ya no expone ese ajuste, por lo que la tarea se enviaba a los nodos 0.21 como un elemento crítico imposible de satisfacer; y activarlo de todos modos hace que LND entre en un bucle de fallos. Ahora la tarea solo se envía a las versiones de LND que realmente la necesitan (inferiores a la 0.21) y se borra automáticamente al actualizar LND. Las direcciones de las dependencias también se resuelven ahora de forma reactiva, por lo que BOLT12 Pay ya no se reinicia cada vez que se actualizan Bitcoin Core o LND, y se recupera por sí solo cuando una dependencia se instala después, se elimina o cambia de puerto. También incluye actualizaciones internas para start-sdk 2.0.x.', + de_DE: + 'Behebt die Auto-Konfigurations-Aufgabe unter LND 0.21. LND kündigt Onion-Nachrichten ab 0.21 nativ an und stellt die Einstellung gar nicht mehr bereit, sodass die Aufgabe auf 0.21-Knoten als kritischer Punkt erschien, der nie erfüllt werden konnte — und wird sie dennoch aktiviert, gerät LND in eine Absturzschleife. Die Aufgabe wird jetzt nur noch an LND-Versionen gestellt, die sie tatsächlich benötigen (unter 0.21), und löscht sich beim Upgrade von LND automatisch. Abhängigkeitsadressen werden außerdem jetzt reaktiv aufgelöst, sodass BOLT12 Pay nicht mehr bei jedem Update von Bitcoin Core oder LND neu startet und sich selbst erholt, wenn eine Abhängigkeit nachträglich installiert oder entfernt wird oder ihren Port ändert. Enthält außerdem interne Aktualisierungen für start-sdk 2.0.x.', + pl_PL: + 'Naprawia zadanie Automatycznej konfiguracji na LND 0.21. Od wersji 0.21 LND natywnie ogłasza wiadomości onion i w ogóle nie udostępnia już tego ustawienia, więc zadanie trafiało na węzły 0.21 jako element krytyczny, którego nie dało się spełnić — a włączenie go mimo to powoduje pętlę awarii LND. Zadanie jest teraz wysyłane tylko do tych wersji LND, które faktycznie go potrzebują (poniżej 0.21), i samo znika po aktualizacji LND. Adresy zależności są teraz również rozwiązywane reaktywnie, więc BOLT12 Pay nie uruchamia się ponownie przy każdej aktualizacji Bitcoin Core czy LND i sam wraca do działania, gdy zależność zostanie zainstalowana później, usunięta lub zmieni port. Zawiera również wewnętrzne aktualizacje dla start-sdk 2.0.x.', + fr_FR: + "Corrige la tâche de Configuration automatique sur LND 0.21. Depuis la 0.21, LND annonce nativement les messages onion et n'expose plus du tout ce réglage : la tâche était donc proposée aux nœuds 0.21 comme un élément critique impossible à satisfaire — et l'activer malgré tout fait entrer LND dans une boucle de plantage. La tâche n'est désormais proposée qu'aux versions de LND qui en ont réellement besoin (antérieures à la 0.21) et disparaît automatiquement lors de la mise à niveau de LND. Les adresses des dépendances sont également résolues de façon réactive : BOLT12 Pay ne redémarre plus à chaque mise à jour de Bitcoin Core ou de LND et se rétablit tout seul lorsqu'une dépendance est installée après lui, supprimée, ou change de port. Inclut également des mises à jour internes pour start-sdk 2.0.x.", + }, migrations: { up: async () => {}, diff --git a/tsconfig.json b/tsconfig.json index a2945a5..f032dc1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,4 @@ { - "include": ["startos/**/*.ts", "node_modules/**/startos"], - "compilerOptions": { - "target": "ES2018", - "module": "CommonJS", - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true - } + "extends": "@start9labs/start-sdk/tsconfig.base.json", + "include": ["startos/**/*.ts", "node_modules/**/startos"] } diff --git a/upstream b/upstream index 54a4b5f..69d0f9d 160000 --- a/upstream +++ b/upstream @@ -1 +1 @@ -Subproject commit 54a4b5f5cb5858b48f8064cd490d6b775dc708f6 +Subproject commit 69d0f9d24fb8f957874a37e7b807c019e2fd36d7