From c89e772ca922e5bf3c7e598b0a1bc67147c55173 Mon Sep 17 00:00:00 2001 From: blckbx <74455114+blckbx@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:21:29 +0100 Subject: [PATCH] initial template --- .github/workflows/buildService.yml | 36 +++++ .github/workflows/releaseService.yml | 71 +++++++++ .gitignore | 7 + LICENSE | 2 +- Makefile | 33 +++++ README.md | 11 +- icon.png | Bin 0 -> 32015 bytes instructions.md | 8 ++ package-lock.json | 208 +++++++++++++++++++++++++++ package.json | 23 +++ startos/actions/index.ts | 9 ++ startos/actions/nameToLogs.ts | 36 +++++ startos/actions/setName.ts | 57 ++++++++ startos/actions/showSecretPhrase.ts | 33 +++++ startos/backups.ts | 5 + startos/dependencies.ts | 9 ++ startos/file-models/config.yml.ts | 11 ++ startos/file-models/lnd.conf.ts | 50 +++++++ startos/index.ts | 11 ++ startos/init.ts | 37 +++++ startos/interfaces.ts | 25 ++++ startos/main.ts | 42 ++++++ startos/manifest.ts | 35 +++++ startos/sdk.ts | 13 ++ startos/store.ts | 30 ++++ startos/utils.ts | 4 + startos/versions/index.ts | 4 + startos/versions/v0.3.6.0.ts | 14 ++ tsconfig.json | 11 ++ 29 files changed, 832 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/buildService.yml create mode 100644 .github/workflows/releaseService.yml create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 icon.png create mode 100644 instructions.md create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 startos/actions/index.ts create mode 100644 startos/actions/nameToLogs.ts create mode 100644 startos/actions/setName.ts create mode 100644 startos/actions/showSecretPhrase.ts create mode 100644 startos/backups.ts create mode 100644 startos/dependencies.ts create mode 100644 startos/file-models/config.yml.ts create mode 100644 startos/file-models/lnd.conf.ts create mode 100644 startos/index.ts create mode 100644 startos/init.ts create mode 100644 startos/interfaces.ts create mode 100644 startos/main.ts create mode 100644 startos/manifest.ts create mode 100644 startos/sdk.ts create mode 100644 startos/store.ts create mode 100644 startos/utils.ts create mode 100644 startos/versions/index.ts create mode 100644 startos/versions/v0.3.6.0.ts create mode 100644 tsconfig.json diff --git a/.github/workflows/buildService.yml b/.github/workflows/buildService.yml new file mode 100644 index 0000000..f013351 --- /dev/null +++ b/.github/workflows/buildService.yml @@ -0,0 +1,36 @@ +name: Build Service + +on: + workflow_dispatch: + pull_request: + paths-ignore: ['*.md'] + branches: ['main', 'master'] + push: + paths-ignore: ['*.md'] + branches: ['main', 'master'] + +jobs: + BuildPackage: + runs-on: ubuntu-latest + steps: + - name: Prepare StartOS SDK + uses: Start9Labs/sdk@v1 + + - name: Checkout services repository + uses: actions/checkout@v3 + + - name: Build the service package + id: build + run: | + git submodule update --init --recursive + start-sdk init + make + PACKAGE_ID=$(yq -oy ".id" manifest.*) + echo "::set-output name=package_id::$PACKAGE_ID" + shell: bash + + - name: Upload .s9pk + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.build.outputs.package_id }}.s9pk + path: ./${{ steps.build.outputs.package_id }}.s9pk \ No newline at end of file diff --git a/.github/workflows/releaseService.yml b/.github/workflows/releaseService.yml new file mode 100644 index 0000000..ee85271 --- /dev/null +++ b/.github/workflows/releaseService.yml @@ -0,0 +1,71 @@ +name: Release Service + +on: + push: + tags: + - 'v*.*' + +jobs: + ReleasePackage: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Prepare StartOS SDK + uses: Start9Labs/sdk@v1 + + - name: Checkout services repository + uses: actions/checkout@v3 + + - name: Build the service package + run: | + git submodule update --init --recursive + start-sdk init + make + + - name: Setting package ID and title from the manifest + id: package + run: | + echo "::set-output name=package_id::$(yq -oy ".id" manifest.*)" + echo "::set-output name=package_title::$(yq -oy ".title" manifest.*)" + shell: bash + + - name: Generate sha256 checksum + run: | + PACKAGE_ID=${{ steps.package.outputs.package_id }} + sha256sum ${PACKAGE_ID}.s9pk > ${PACKAGE_ID}.s9pk.sha256 + shell: bash + + - name: Generate changelog + run: | + PACKAGE_ID=${{ steps.package.outputs.package_id }} + echo "## What's Changed" > change-log.txt + yq e '.release-notes' manifest.yaml >> change-log.txt + echo "## SHA256 Hash" >> change-log.txt + echo '```' >> change-log.txt + sha256sum ${PACKAGE_ID}.s9pk >> change-log.txt + echo '```' >> change-log.txt + shell: bash + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ github.ref_name }} + name: ${{ steps.package.outputs.package_title }} ${{ github.ref_name }} + prerelease: true + body_path: change-log.txt + files: | + ./${{ steps.package.outputs.package_id }}.s9pk + ./${{ steps.package.outputs.package_id }}.s9pk.sha256 + + - name: Publish to Registry + env: + S9USER: ${{ secrets.S9USER }} + S9PASS: ${{ secrets.S9PASS }} + S9REGISTRY: ${{ secrets.S9REGISTRY }} + run: | + if [[ -z "$S9USER" || -z "$S9PASS" || -z "$S9REGISTRY" ]]; then + echo "Publish skipped: missing registry credentials." + else + start-sdk publish https://$S9USER:$S9PASS@$S9REGISTRY ${{ steps.package.outputs.package_id }}.s9pk + fi \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2603f70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.s9pk +startos/*.js +node_modules/ +.DS_Store +.vscode/ +docker-images +javascript \ No newline at end of file diff --git a/LICENSE b/LICENSE index d4c628b..793257b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Tunnelsats +Copyright (c) 2022 Start9 Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..21ba290 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +PACKAGE_ID := tunnelsats + +# Phony targets +.PHONY: all clean install + +# Default target +all: ${PACKAGE_ID}.s9pk + +# Build targets +${PACKAGE_ID}.s9pk: $(shell start-cli s9pk list-ingredients) + start-cli s9pk pack + +javascript/index.js: $(shell git ls-files startos) tsconfig.json node_modules package.json + npm run build + +node_modules: package.json package-lock.json + npm ci + +package-lock.json: package.json + npm i + +# Clean target +clean: + rm -rf ${PACKAGE_ID}.s9pk + rm -rf javascript + rm -rf node_modules + +# Install target +install: + @if [ ! -f ~/.startos/config.yaml ]; then echo "You must define \"host: http://server-name.local\" in ~/.startos/config.yaml config file first."; exit 1; fi + @echo "\nInstalling to $$(grep -v '^#' ~/.startos/config.yaml | cut -d'/' -f3) ...\n" + @[ -f $(PACKAGE_ID).s9pk ] || ( $(MAKE) && echo "\nInstalling to $$(grep -v '^#' ~/.startos/config.yaml | cut -d'/' -f3) ...\n" ) + @start-cli package install -s $(PACKAGE_ID).s9pk diff --git a/README.md b/README.md index 8414f91..7c1fc75 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ -# startos -Providing fast, anonymous and reliable VPN for start9 OS runners +# Tunnel Sats for StartOS + +A VPN service for lightning implementations. + +## Building from source + +`npm i` +`make` + diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4f56932d96e27e1a8375b9a99f436ce779e1fc63 GIT binary patch literal 32015 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 Uy;B>g4(9-6`FC=aZ_Gpf3w@3%Jpcdz literal 0 HcmV?d00001 diff --git a/instructions.md b/instructions.md new file mode 100644 index 0000000..9540dd1 --- /dev/null +++ b/instructions.md @@ -0,0 +1,8 @@ +# Tunnel Sats for StartOS + +A VPN service for lightning implementations. + +## Building from source + +`npm i` +`make` diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fa7fa37 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,208 @@ +{ + "name": "tunnelsats-startos", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@start9labs/start-sdk": "0.3.6-alpha.22" + }, + "devDependencies": { + "@types/node": "^22.1.0", + "@vercel/ncc": "^0.38.1", + "prettier": "^3.2.5", + "typescript": "^5.4.3" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==" + }, + "node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@start9labs/start-sdk": { + "version": "0.3.6-alpha.22", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.3.6-alpha.22.tgz", + "integrity": "sha512-TmzHolOU6Uh/T5yBffh5j+h2gFKu/HEB6FBpxoYJ9je8+uXGfJ0rOwa0nFzKAcNhqpi7rgfn/gqor8+QCkZS4w==", + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "isomorphic-fetch": "^3.0.0", + "lodash.merge": "^4.6.2", + "mime-types": "^2.1.35", + "ts-matches": "^6.2.1", + "yaml": "^2.2.2" + } + }, + "node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "dev": true, + "dependencies": { + "undici-types": "~6.13.0" + } + }, + "node_modules/@vercel/ncc": { + "version": "0.38.1", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.1.tgz", + "integrity": "sha512-IBBb+iI2NLu4VQn3Vwldyi2QwaXt5+hTyh58ggAMoCGE6DJmPvwL3KPBWcJl1m9LYPChBLE980Jw+CS4Wokqxw==", + "dev": true, + "bin": { + "ncc": "dist/ncc/cli.js" + } + }, + "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==", + "dependencies": { + "node-fetch": "^2.6.1", + "whatwg-fetch": "^3.4.1" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/ts-matches": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.2.1.tgz", + "integrity": "sha512-qdnMgTHsGCEGGK6QiaNMY2vD9eQtRp2Q+pAxcOAzxHJKDKTBYsc1ISTg1zp8H2+EmtCB0eko/1TwYUA5/mUGug==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", + "dev": true + }, + "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==" + }, + "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==" + }, + "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==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..dac157a --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "hello-world-startos", + "scripts": { + "build": "rm -rf ./javascript && ncc build startos/index.ts -o ./javascript", + "prettier": "prettier --write startos", + "check": "tsc --noEmit" + }, + "dependencies": { + "@start9labs/start-sdk": "0.3.6-alpha.22" + }, + "devDependencies": { + "@types/node": "^22.1.0", + "@vercel/ncc": "^0.38.1", + "prettier": "^3.2.5", + "typescript": "^5.4.3" + }, + "prettier": { + "trailingComma": "all", + "tabWidth": 2, + "semi": false, + "singleQuote": true + } +} diff --git a/startos/actions/index.ts b/startos/actions/index.ts new file mode 100644 index 0000000..1866c78 --- /dev/null +++ b/startos/actions/index.ts @@ -0,0 +1,9 @@ +import { sdk } from '../sdk' +import { showSecretPhrase } from './showSecretPhrase' +import { setName } from './setName' +import { nameToLogs } from './nameToLogs' + +export const actions = sdk.Actions.of() + .addAction(setName) + .addAction(showSecretPhrase) + .addAction(nameToLogs) diff --git a/startos/actions/nameToLogs.ts b/startos/actions/nameToLogs.ts new file mode 100644 index 0000000..bfe74a6 --- /dev/null +++ b/startos/actions/nameToLogs.ts @@ -0,0 +1,36 @@ +import { sdk } from '../sdk' +import { yamlFile } from '../file-models/config.yml' + +export const nameToLogs = sdk.Action.withoutInput( + // id + 'name-to-logs', + + // metadata + async ({ effects }) => ({ + name: 'Print name to Logs', + description: 'Prints "Hello [Name]" to the service logs.', + warning: null, + allowedStatuses: 'only-running', + group: null, + visibility: (await sdk.store + .getOwn(effects, sdk.StorePath.nameLastUpdatedAt) + .const()) + ? 'enabled' + : { + disabled: 'Cannot print name to logs until you update your name.', + }, + }), + + // the execution function + async ({ effects }) => { + const name = (await yamlFile.read.const(effects))!.name + console.info(`Hello ${name}`) + + return { + version: '1', + title: 'Success', + message: `"Hello ${name}" has been logged. Open the Hello World service logs to view it.`, + result: null, + } + }, +) diff --git a/startos/actions/setName.ts b/startos/actions/setName.ts new file mode 100644 index 0000000..5fb74fb --- /dev/null +++ b/startos/actions/setName.ts @@ -0,0 +1,57 @@ +import { sdk } from '../sdk' +import { yamlFile } from '../file-models/config.yml' +import { getSecretPhrase } from '../utils' + +const { InputSpec, Value } = sdk + +export const inputSpec = InputSpec.of({ + name: Value.text({ + name: 'Name', + description: + 'When you launch the Hello World UI, it will display "Hello [Name]"', + required: true, + default: 'World', + }), +}) + +export const setName = sdk.Action.withInput( + // id + 'set-name', + + // metadata + async ({ effects }) => ({ + name: 'Set Name', + description: 'Set your name so Hello World can say hello to you', + warning: null, + allowedStatuses: 'any', + group: null, + visibility: 'enabled', + }), + + // form input specification + inputSpec, + + // optionally pre-fill the input form + ({ effects }) => yamlFile.read.const(effects), + + // the execution function + async ({ effects, input }) => { + const yaml = await yamlFile.read.const(effects) + + if (yaml?.name === input.name) return + + await Promise.all([ + yamlFile.merge(input), + sdk.store.setOwn( + effects, + sdk.StorePath.secretPhrase, + getSecretPhrase(input.name), + ), + sdk.store.setOwn( + effects, + sdk.StorePath.nameLastUpdatedAt, + new Date().toISOString(), + ), + ]) + }, +) diff --git a/startos/actions/showSecretPhrase.ts b/startos/actions/showSecretPhrase.ts new file mode 100644 index 0000000..092eb66 --- /dev/null +++ b/startos/actions/showSecretPhrase.ts @@ -0,0 +1,33 @@ +import { sdk } from '../sdk' + +export const showSecretPhrase = sdk.Action.withoutInput( + // id + 'show-secret-phrase', + + // metadata + async ({ effects }) => ({ + name: 'Show Secret Phrase', + description: 'Reveal the secret phrase for Hello World', + warning: null, + allowedStatuses: 'any', + group: null, + visibility: 'enabled', + }), + + // the execution function + async ({ effects }) => ({ + version: '1', + title: 'Secret Phrase', + message: + 'Below is your secret phrase. Use it to gain access to extraordinary places', + result: { + type: 'single', + value: await sdk.store + .getOwn(effects, sdk.StorePath.secretPhrase) + .const(), + copyable: true, + qr: true, + masked: true, + }, + }), +) diff --git a/startos/backups.ts b/startos/backups.ts new file mode 100644 index 0000000..6ccffb6 --- /dev/null +++ b/startos/backups.ts @@ -0,0 +1,5 @@ +import { sdk } from './sdk' + +export const { createBackup, restoreBackup } = sdk.setupBackups( + async ({ effects }) => sdk.Backups.volumes('main'), +) diff --git a/startos/dependencies.ts b/startos/dependencies.ts new file mode 100644 index 0000000..84c5f3d --- /dev/null +++ b/startos/dependencies.ts @@ -0,0 +1,9 @@ +import { sdk } from './sdk' + +export const setDependencies = sdk.setupDependencies(async ({ effects }) => ({ + lnd: { + kind: 'running', + versionRange: '>=0.17.1 <0.19.0', + healthChecks: [], + }, +})) \ No newline at end of file diff --git a/startos/file-models/config.yml.ts b/startos/file-models/config.yml.ts new file mode 100644 index 0000000..44d8da4 --- /dev/null +++ b/startos/file-models/config.yml.ts @@ -0,0 +1,11 @@ +import { matches, FileHelper } from '@start9labs/start-sdk' +const { object, string } = matches + +const shape = object({ + name: string.optional().onMismatch(undefined), +}) + +export const yamlFile = FileHelper.yaml( + '/media/startos/volumes/main/config.yml', + shape, +) diff --git a/startos/file-models/lnd.conf.ts b/startos/file-models/lnd.conf.ts new file mode 100644 index 0000000..8c9623f --- /dev/null +++ b/startos/file-models/lnd.conf.ts @@ -0,0 +1,50 @@ +import { matches, FileHelper, utils } from '@start9labs/start-sdk' + + +// Todo: adjust for TS and lnd.conf + +const { object, string, literal } = matches + +const lndRpcserver = 'lnd.startos:10009' + +const shape = object({ + uipassword: string.onMismatch(utils.getDefaultString(randomPassword)), + 'lit-dir': literal(litDir).onMismatch(litDir), + 'insecure-httplisten': + literal(insecureHttplisten).onMismatch(insecureHttplisten), + 'remote.lnd.rpcserver': literal(lndRpcserver).onMismatch(lndRpcserver), + 'remote.lnd.macaroonpath': + literal(lndMacaroonpath).onMismatch(lndMacaroonpath), + 'remote.lnd.tlscertpath': literal(lndTlscertpath).onMismatch(lndTlscertpath), +}) + +function fromLitConf(text: string): typeof shape._TYPE { + let conf: Record = {} + const lines = text.split('\n') + + for (const line of lines) { + const [key, value] = line.split('=', 2) + const trimmedKey = key.trim() + const trimmedValue = value.trim() + + conf[trimmedKey] = trimmedValue + } + + return conf as typeof shape._TYPE +} + +function toLitConf(obj: typeof shape._TYPE): string { + let litConf = '' + for (const [key, value] of Object.entries(obj)) { + litConf += `${key}=${value}\n` + } + + return litConf +} + +export const litConfig = FileHelper.raw( + `${litDir}/lit.conf`, + (obj: typeof shape._TYPE) => toLitConf(obj), + (str) => fromLitConf(str), + (value) => shape.unsafeCast(value), +) \ No newline at end of file diff --git a/startos/index.ts b/startos/index.ts new file mode 100644 index 0000000..8d32c10 --- /dev/null +++ b/startos/index.ts @@ -0,0 +1,11 @@ +/** + * Plumbing. DO NOT EDIT. + */ +export { createBackup, restoreBackup } from './backups' +export { main } from './main' +export { packageInit, packageUninit, containerInit } from './init' +export { actions } from './actions' +import { buildManifest } from '@start9labs/start-sdk' +import { manifest as sdkManifest } from './manifest' +import { versions } from './versions' +export const manifest = buildManifest(versions, sdkManifest) diff --git a/startos/init.ts b/startos/init.ts new file mode 100644 index 0000000..5939cda --- /dev/null +++ b/startos/init.ts @@ -0,0 +1,37 @@ +import { sdk } from './sdk' +import { exposedStore } from './store' +import { setDependencies } from './dependencies' +import { setInterfaces } from './interfaces' +import { versions } from './versions' +import { actions } from './actions' +import { getSecretPhrase } from './utils' +import { yamlFile } from './file-models/config.yml' + +// **** Install **** +const install = sdk.setupInstall(async ({ effects }) => { + const name = 'World' + + await yamlFile.write({ name }) + + await sdk.store.setOwn( + effects, + sdk.StorePath.secretPhrase, + getSecretPhrase(name), + ) +}) + +// **** Uninstall **** +const uninstall = sdk.setupUninstall(async ({ effects }) => {}) + +/** + * Plumbing. DO NOT EDIT. + */ +export const { packageInit, packageUninit, containerInit } = sdk.setupInit( + versions, + install, + uninstall, + setInterfaces, + setDependencies, + actions, + exposedStore, +) diff --git a/startos/interfaces.ts b/startos/interfaces.ts new file mode 100644 index 0000000..4786c30 --- /dev/null +++ b/startos/interfaces.ts @@ -0,0 +1,25 @@ +import { sdk } from './sdk' +import { uiPort } from './utils' + +export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { + const uiMulti = sdk.host.multi(effects, 'ui-multi') + const uiMultiOrigin = await uiMulti.bindPort(uiPort, { + protocol: 'http', + }) + const ui = sdk.createInterface(effects, { + name: 'Web UI', + id: 'ui', + description: 'The web interface of Hello World', + type: 'ui', + hasPrimary: false, + masked: false, + schemeOverride: null, + username: null, + path: '', + search: {}, + }) + + const uiReceipt = await uiMultiOrigin.export([ui]) + + return [uiReceipt] +}) diff --git a/startos/main.ts b/startos/main.ts new file mode 100644 index 0000000..7411e69 --- /dev/null +++ b/startos/main.ts @@ -0,0 +1,42 @@ +import { sdk } from './sdk' +import { T } from '@start9labs/start-sdk' +import { uiPort } from './utils' + +export const main = sdk.setupMain(async ({ effects, started }) => { + /** + * ======================== Setup (optional) ======================== + * + * In this section, we fetch any resources or run any desired preliminary commands. + */ + console.info('Starting TunnelSats!') + + /** + * ======================== Additional Health Checks (optional) ======================== + * + * In this section, we define *additional* health checks beyond those included with each daemon (below). + */ + const healthReceipts: T.HealthReceipt[] = [] + + /** + * ======================== Daemons ======================== + * + * In this section, we create one or more daemons that define the service runtime. + * + * Each daemon defines its own health check, which can optionally be exposed to the user. + */ + return sdk.Daemons.of(effects, started, healthReceipts).addDaemon('primary', { + image: { id: 'tunnelsats' }, // Must match an Image ID declared in the manifest. + command: ['tunnelsats'], // The command to start the daemon. + mounts: sdk.Mounts.of().addVolume('main', null, '/data', false), // Mount necessary volumes. ID must match manifest declaration. + ready: { + display: 'Web Interface', // If null, the health check will NOT be displayed to the user. If provided, this string will be the name of the health check and displayed to the user. + // A function below determines the health status of this daemon + fn: () => + sdk.healthCheck.checkPortListening(effects, uiPort, { + successMessage: 'The web interface is ready', + errorMessage: 'The web interface is not ready', + }), + }, + requires: ['lnd', 'wireguard'], // If this daemon depends on the successful initialization of one or more prior daemons, enter their IDs here. + }) +}) diff --git a/startos/manifest.ts b/startos/manifest.ts new file mode 100644 index 0000000..89bfc67 --- /dev/null +++ b/startos/manifest.ts @@ -0,0 +1,35 @@ +import { setupManifest } from '@start9labs/start-sdk' + +export const manifest = setupManifest({ + id: 'tunnelsats', + title: 'TunnelSats', + license: 'mit', + wrapperRepo: 'https://github.com/Tunnelsats/startos', + upstreamRepo: 'https://github.com/Tunnelsats/tunnelsats', + supportSite: 'https://guide.tunnelsats.com/FAQ.html', + marketingSite: 'https://tunnelsats.com/', + donationUrl: 'https://ln.tunnelsats.com/lnurlp/link/AJo7s4', + description: { + short: 'VPN Connection For Your Lightning Node', + long: 'Providing Lightning ⚡ Services is about privacy, reliability, connectivity, speed and liquidity. Relying your node connectivity to a single service Tor is a risk regarding connectivity and network stability, as anyone running a lightning node can testify. With Hybrid connectivity, you offer your payment and routing services to be faster, more reliable, and yet, there is a privacy concern when you do it with your home-IP: you both expose your rough location of your node, potentially your home and your node\'s system to attacks from the internet. With our solution Tunnel⚡Sats, you get the best of both worlds. Your node and home IP stays hidden, behind Tor and our VPS public IP address, which will be your node\'s face to the public internet, is shared with other peers. You may see higher reliability causing not only higher uptime, fewer offline peer nodes but also greater routing numbers. This isn\'t a promise, but an eventually expected outcome.', + }, + assets: [], + volumes: ['main'], + images: { + 'tunnelsats': { + source: { + dockerTag: 'start9/tunnelsats', + }, + }, + }, + hardwareRequirements: {}, + alerts: { + install: 'Optional alert to display before installing the service', + update: null, + uninstall: null, + restore: null, + start: null, + stop: null, + }, + dependencies: {}, +}) diff --git a/startos/sdk.ts b/startos/sdk.ts new file mode 100644 index 0000000..26c3015 --- /dev/null +++ b/startos/sdk.ts @@ -0,0 +1,13 @@ +import { StartSdk } from '@start9labs/start-sdk' +import { manifest } from './manifest' +import { Store } from './store' + +/** + * Plumbing. DO NOT EDIT. + * + * The exported "sdk" const is used throughout this package codebase. + */ +export const sdk = StartSdk.of() + .withManifest(manifest) + .withStore() + .build(true) diff --git a/startos/store.ts b/startos/store.ts new file mode 100644 index 0000000..7f7b4e4 --- /dev/null +++ b/startos/store.ts @@ -0,0 +1,30 @@ +import { setupExposeStore } from '@start9labs/start-sdk' + +/** + * @description The Store is used for persisting arbitrary data that are needed by the wrapper + * package but are NOT persisted by the upstream service. Do NOT persist data here that are + * already being persisted by the service itself. + * + * Store data should be kept to a minimum. Stateless packages are easier to maintain + * and eliminate unexpected behavior. + * @type {Record} + * @example + * ``` + * export type Store = { + * key1: string + * key2: boolean + * key3: number + * key4: { + * key5: string[] + * } + * } + * ``` + */ +export type Store = { + secretPhrase: string + nameLastUpdatedAt: string | null +} + +export const exposedStore = setupExposeStore((pathBuilder) => [ + pathBuilder.nameLastUpdatedAt, +]) diff --git a/startos/utils.ts b/startos/utils.ts new file mode 100644 index 0000000..c9c17fb --- /dev/null +++ b/startos/utils.ts @@ -0,0 +1,4 @@ +// Here we define any constants or functions that are shared by multiple components +// throughout the package codebase. This file will be unnecessary for many packages. + +// export const tsDir = '/data/ts' diff --git a/startos/versions/index.ts b/startos/versions/index.ts new file mode 100644 index 0000000..339e78f --- /dev/null +++ b/startos/versions/index.ts @@ -0,0 +1,4 @@ +import { VersionGraph } from '@start9labs/start-sdk' +import { v0360 } from './v0.3.6.0' + +export const versions = VersionGraph.of(v0360) diff --git a/startos/versions/v0.3.6.0.ts b/startos/versions/v0.3.6.0.ts new file mode 100644 index 0000000..ed8ffc7 --- /dev/null +++ b/startos/versions/v0.3.6.0.ts @@ -0,0 +1,14 @@ +import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk' +import { sdk } from '../sdk' +import { setName } from '../actions/setName' + +export const v0360 = VersionInfo.of({ + version: '0.3.6:0', + releaseNotes: 'Revamped for StartOS 0.3.6', + migrations: { + up: async ({ effects }) => { + await sdk.action.requestOwn(effects, setName, 'critical') + }, + down: IMPOSSIBLE, + }, +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..039d1ff --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "include": ["startos/**/*.ts", "node_modules/**/startos"], + "compilerOptions": { + "target": "ES2022", + "module": "None", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + } +}