From 0eb0051d701fc8b8c1fb3bd87f4257f08a8b6ae9 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Mon, 7 Apr 2025 12:13:14 -0600 Subject: [PATCH 01/35] initial conversion --- .gitignore | 11 +- Makefile | 97 +++---- README.md | 47 +--- assets/ABOUT.md | 1 + {assets => old/assets}/compat/backup.sh | 0 {assets => old/assets}/compat/restore.sh | 0 check-syn-ack.sh => old/check-syn-ack.sh | 0 .../docker_entrypoint.sh | 0 manifest.yaml => old/manifest.yaml | 0 {scripts => old/scripts}/bundle.ts | 0 {scripts => old/scripts}/deps.ts | 0 {scripts => old/scripts}/embassy.ts | 0 .../scripts}/procedures/getConfig.ts | 0 {scripts => old/scripts}/procedures/main.ts | 0 .../scripts}/procedures/migrations.ts | 0 .../scripts}/procedures/properties.ts | 0 .../scripts}/procedures/setConfig.ts | 0 package-lock.json | 247 ++++++++++++++++++ package.json | 23 ++ startos/actions/index.ts | 3 + startos/backups.ts | 5 + startos/dependencies.ts | 5 + startos/file-models/smp.ini.ts | 143 ++++++++++ startos/file-models/xftp.ini.ts | 10 + startos/index.ts | 11 + startos/init.ts | 40 +++ startos/interfaces.ts | 61 +++++ startos/main.ts | 56 ++++ startos/manifest.ts | 37 +++ startos/sdk.ts | 13 + startos/store.ts | 5 + startos/utils.ts | 95 +++++++ startos/versions/index.ts | 4 + startos/versions/v6.3.1.1.ts | 14 + tsconfig.json | 11 + 35 files changed, 834 insertions(+), 105 deletions(-) create mode 100644 assets/ABOUT.md rename {assets => old/assets}/compat/backup.sh (100%) rename {assets => old/assets}/compat/restore.sh (100%) rename check-syn-ack.sh => old/check-syn-ack.sh (100%) rename docker_entrypoint.sh => old/docker_entrypoint.sh (100%) rename manifest.yaml => old/manifest.yaml (100%) rename {scripts => old/scripts}/bundle.ts (100%) rename {scripts => old/scripts}/deps.ts (100%) rename {scripts => old/scripts}/embassy.ts (100%) rename {scripts => old/scripts}/procedures/getConfig.ts (100%) rename {scripts => old/scripts}/procedures/main.ts (100%) rename {scripts => old/scripts}/procedures/migrations.ts (100%) rename {scripts => old/scripts}/procedures/properties.ts (100%) rename {scripts => old/scripts}/procedures/setConfig.ts (100%) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 startos/actions/index.ts create mode 100644 startos/backups.ts create mode 100644 startos/dependencies.ts create mode 100644 startos/file-models/smp.ini.ts create mode 100644 startos/file-models/xftp.ini.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/v6.3.1.1.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore index 5738189..4dad48c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ -simplex.s9pk -image.tar -scripts/*.js +*.s9pk +startos/*.js +node_modules/ .DS_Store .vscode/ docker-images -/assets/.$explainer-source.drawio.dtmp -/assets/.$explainer-source.drawio.bkp -/_site +javascript +ncc-cache \ No newline at end of file diff --git a/Makefile b/Makefile index 84d38ec..c7c81b6 100644 --- a/Makefile +++ b/Makefile @@ -1,60 +1,49 @@ -PKG_ID := $(shell yq e ".id" manifest.yaml) -PKG_VERSION := $(shell yq e ".version" manifest.yaml) -TS_FILES := $(shell find ./ -name \*.ts) +PACKAGE_ID := $(shell grep -o "id: '[^']*'" startos/manifest.ts | sed "s/id: '\([^']*\)'/\1/") +INGREDIENTS := $(shell start-cli s9pk list-ingredients 2> /dev/null) -# delete the target of a rule if it has changed and its recipe exits with a nonzero exit status -.DELETE_ON_ERROR: +.PHONY: all clean install check-deps check-init ingredients -all: verify - -verify: $(PKG_ID).s9pk - @start-sdk verify s9pk $(PKG_ID).s9pk +all: ${PACKAGE_ID}.s9pk @echo " Done!" - @echo " Filesize: $(shell du -h $(PKG_ID).s9pk) is ready" + @echo " Filesize:$(shell du -h $(PACKAGE_ID).s9pk) is ready" + +check-deps: + @if ! command -v start-cli > /dev/null; then \ + echo "Error: start-cli not found. Please install it first."; \ + exit 1; \ + fi + +check-init: + @if [ ! -f ~/.startos/developer.key.pem ]; then \ + start-cli init; \ + fi + +ingredients: $(INGREDIENTS) + @echo "Re-evaluating ingredients..." + +${PACKAGE_ID}.s9pk: $(INGREDIENTS) | check-deps check-init + @$(MAKE) --no-print-directory ingredients + start-cli s9pk pack + +javascript/index.js: $(shell git ls-files startos) tsconfig.json node_modules package.json + npm run build + +assets: + mkdir -p assets + +node_modules: package-lock.json + npm ci -install: -ifeq (,$(wildcard ~/.embassy/config.yaml)) - @echo; echo "You must define \"host: http://server-name.local\" in ~/.embassy/config.yaml config file first"; echo -else - start-cli package install $(PKG_ID).s9pk -endif +package-lock.json: package.json + npm i clean: - rm -rf docker-images - rm -f $(PKG_ID).s9pk - rm -f scripts/*.js - -arm: - @rm -f docker-images/x86_64.tar - @ARCH=aarch64 $(MAKE) - -x86: - @rm -f docker-images/aarch64.tar - @ARCH=x86_64 $(MAKE) - -scripts/embassy.js: $(TS_FILES) - deno run --allow-read --allow-write --allow-env --allow-net scripts/bundle.ts - -docker-images/x86_64.tar: Dockerfile docker_entrypoint.sh check-syn-ack.sh -ifeq ($(ARCH),aarch64) -else - mkdir -p docker-images - docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --platform=linux/amd64 -o type=docker,dest=docker-images/x86_64.tar . -endif - -docker-images/aarch64.tar: Dockerfile docker_entrypoint.sh check-syn-ack.sh -ifeq ($(ARCH),x86_64) -else - mkdir -p docker-images - docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --platform=linux/arm64 -o type=docker,dest=docker-images/aarch64.tar . -endif - -$(PKG_ID).s9pk: manifest.yaml instructions.md LICENSE icon.png scripts/embassy.js docker-images/aarch64.tar docker-images/x86_64.tar -ifeq ($(ARCH),aarch64) - @echo "start-sdk: Preparing aarch64 package ..." -else ifeq ($(ARCH),x86_64) - @echo "start-sdk: Preparing x86_64 package ..." -else - @echo "start-sdk: Preparing Universal Package ..." -endif - @start-sdk pack + rm -rf ${PACKAGE_ID}.s9pk + rm -rf javascript + rm -rf node_modules + +install: | check-deps check-init + @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 a39e74d..caf41c2 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,3 @@ -# Wrapper for SimpleX +# SimpleX for StartOS -[SimpleX logo](https://simplex.chat/) - -SimpleX is a highly secure and sovereign messenger. - -Currently the packaging process is somewhat involved, because SimpleX do not publish official multi-architecture images on Docker Hub. I was able to get support for ARM builds [merged](https://github.com/simplex-chat/simplexmq/pull/679), however as of 2023-06-07, SimpleX have an [open issue](https://github.com/simplex-chat/simplexmq/issues/740) for not publishing ARM builds to Docker Hub. All of this is further complicated by the fact that Haskell cross compilation of SimpleXMQ is fairly broken - even using 30GB of RAM on an M1 Pro was not enough resources. - -This wrapper therefore uses a multi-architecture Docker image hosted on my personal Docker Hub. This multi-architecture image can be created as follows: - -- Check out the SimpleXMQ repo on an ARM machine - `git@github.com:simplex-chat/simplexmq.git` -- Build the docker image using their instructions, which, as of writing, was `DOCKER_BUILDKIT=1 docker buildx build --platform linux/arm64 -t shyfire131/smp-server:arm --build-arg APP="smp-server" --build-arg APP_PORT="5223" . --push` -- Then, mirror the official AMD64 image as follows: -``` -docker pull simplexchat/smp-server -docker tag simplexchat/smp-server /smp-server:amd64 -docker push /smp-server:amd64 -``` -- At this point you need to do some manifest hacking: -- `docker buildx imagetools create -t shyfire131/smp-server:latest shyfire131/smp-server:arm shyfire131/smp-server:amd64` - -You can then reference `shyfire131/smp-server` in your Dockerfile, of course replacing shyfire131 with your own username in all of the above. - -Other than that, building the .s9pk is fairly straightforward and doesn't need any special dependencies. -## Cloning - -Clone the project locally: - -``` -git clone git@github.com:shyfire131/simplex-startos.git -cd simplex-startos -make -``` - -## Testing performed - -- [X] Smoke testing - DMs work on macOS, iOS and Android -- [X] Continuity testing - Server config persists across service restarts -- [X] DR testing - Backups restore successfully - -## Roadmap -- [ ] Support for XFTP Server (sending large attachments, will either be a standalone Service or bundled once multi-container services are supported) -- [ ] Server statistics (`log_stats: on` in `smp-server.ini`) -- [ ] Clearnet support will obviously be a superpower once available -- [ ] Use official multi-architecture Docker Hub once SimpleX team fixes that -- [ ] Configurable message retention period + diff --git a/assets/ABOUT.md b/assets/ABOUT.md new file mode 100644 index 0000000..4fbfc10 --- /dev/null +++ b/assets/ABOUT.md @@ -0,0 +1 @@ +Use the `/assets` directory to include additional files or scripts needed by your service. diff --git a/assets/compat/backup.sh b/old/assets/compat/backup.sh similarity index 100% rename from assets/compat/backup.sh rename to old/assets/compat/backup.sh diff --git a/assets/compat/restore.sh b/old/assets/compat/restore.sh similarity index 100% rename from assets/compat/restore.sh rename to old/assets/compat/restore.sh diff --git a/check-syn-ack.sh b/old/check-syn-ack.sh similarity index 100% rename from check-syn-ack.sh rename to old/check-syn-ack.sh diff --git a/docker_entrypoint.sh b/old/docker_entrypoint.sh similarity index 100% rename from docker_entrypoint.sh rename to old/docker_entrypoint.sh diff --git a/manifest.yaml b/old/manifest.yaml similarity index 100% rename from manifest.yaml rename to old/manifest.yaml diff --git a/scripts/bundle.ts b/old/scripts/bundle.ts similarity index 100% rename from scripts/bundle.ts rename to old/scripts/bundle.ts diff --git a/scripts/deps.ts b/old/scripts/deps.ts similarity index 100% rename from scripts/deps.ts rename to old/scripts/deps.ts diff --git a/scripts/embassy.ts b/old/scripts/embassy.ts similarity index 100% rename from scripts/embassy.ts rename to old/scripts/embassy.ts diff --git a/scripts/procedures/getConfig.ts b/old/scripts/procedures/getConfig.ts similarity index 100% rename from scripts/procedures/getConfig.ts rename to old/scripts/procedures/getConfig.ts diff --git a/scripts/procedures/main.ts b/old/scripts/procedures/main.ts similarity index 100% rename from scripts/procedures/main.ts rename to old/scripts/procedures/main.ts diff --git a/scripts/procedures/migrations.ts b/old/scripts/procedures/migrations.ts similarity index 100% rename from scripts/procedures/migrations.ts rename to old/scripts/procedures/migrations.ts diff --git a/scripts/procedures/properties.ts b/old/scripts/procedures/properties.ts similarity index 100% rename from scripts/procedures/properties.ts rename to old/scripts/procedures/properties.ts diff --git a/scripts/procedures/setConfig.ts b/old/scripts/procedures/setConfig.ts similarity index 100% rename from scripts/procedures/setConfig.ts rename to old/scripts/procedures/setConfig.ts diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b5e30fd --- /dev/null +++ b/package-lock.json @@ -0,0 +1,247 @@ +{ + "name": "hello-world-startos", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hello-world-startos", + "dependencies": { + "@start9labs/start-sdk": "0.3.6-beta.18" + }, + "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==", + "license": "ISC" + }, + "node_modules/@noble/curves": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", + "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.7.1" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@start9labs/start-sdk": { + "version": "0.3.6-beta.18", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.3.6-beta.18.tgz", + "integrity": "sha512-2DWx2mI4/X76wr8bqpqX3NAyat4h35lLtcElNCIEeEc8hF9+ftgMCyv1F5T9A8FoKHT3+XrohiNQGpPv6ed1mA==", + "license": "MIT", + "dependencies": { + "@iarna/toml": "^2.2.5", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "deep-equality-data-structures": "^1.5.1", + "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.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", + "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vercel/ncc": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.3.tgz", + "integrity": "sha512-rnK6hJBS6mwc+Bkab+PGPs9OiS0i/3kdTO+CkI8V0/VrW3vmz7O2Pxjw/owOlmo6PKEIxRSeZKv/kuL9itnpYA==", + "dev": true, + "license": "MIT", + "bin": { + "ncc": "dist/ncc/cli.js" + } + }, + "node_modules/deep-equality-data-structures": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/deep-equality-data-structures/-/deep-equality-data-structures-1.5.1.tgz", + "integrity": "sha512-P7zsL2/AbZIGHDxbo/LLEhCp11AttRp8GvzXOXudqMT/qiGCLo/pyI4lAZvjUZyQnlIbPna3fv8DMsuRvLt4ww==", + "license": "MIT", + "dependencies": { + "object-hash": "^3.0.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/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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "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==", + "license": "MIT" + }, + "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.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b319ef2 --- /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-beta.18" + }, + "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..c82cb95 --- /dev/null +++ b/startos/actions/index.ts @@ -0,0 +1,3 @@ +import { sdk } from '../sdk' + +export const actions = sdk.Actions.of() 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..7221c4b --- /dev/null +++ b/startos/dependencies.ts @@ -0,0 +1,5 @@ +import { sdk } from './sdk' + +export const setDependencies = sdk.setupDependencies( + async ({ effects }) => ({}), +) diff --git a/startos/file-models/smp.ini.ts b/startos/file-models/smp.ini.ts new file mode 100644 index 0000000..e274a6f --- /dev/null +++ b/startos/file-models/smp.ini.ts @@ -0,0 +1,143 @@ +import { matches, FileHelper } from '@start9labs/start-sdk' +import { smpConfigDefaults } from '../utils' + +const { object, string, oneOf, literal, natural, arrayOf } = matches + +const { + information, + store_log, + auth, + transport, + proxy, + inactive_clients, + web, +} = smpConfigDefaults + +const shape = object({ + information: object({ + // Basic info + LICENSE: string.optional().onMismatch(information.LICENSE), + source_code: literal(information.source_code).onMismatch( + information.source_code, + ), + + // Server usage conditions and amendments. + usage_conditions: string + .optional() + .onMismatch(information.usage_conditions), + condition_amendments: string + .optional() + .onMismatch(information.condition_amendments), + + // Server location and operator + server_country: string.optional().onMismatch(information.server_country), + operator: string.optional().onMismatch(information.operator), + operator_country: string + .optional() + .onMismatch(information.operator_country), + website: string.optional().onMismatch(information.website), + + // Administrative contacts + admin_simplex: string.optional().onMismatch(information.admin_simplex), // SimpleX address + admin_email: string.optional().onMismatch(information.admin_email), + admin_pgp: string.optional().onMismatch(information.admin_pgp), + admin_pgp_fingerprint: string + .optional() + .onMismatch(information.admin_pgp_fingerprint), + + // Contacts for complaints and feedback. + complaints_simplex: string + .optional() + .onMismatch(information.complaints_simplex), // SimpleX address + complaints_email: string + .optional() + .onMismatch(information.complaints_email), + complaints_pgp: string.optional().onMismatch(information.complaints_pgp), + complaints_pgp_fingerprint: string + .optional() + .onMismatch(information.complaints_pgp_fingerprint), + + // Hosting provider + hosting: string.optional().onMismatch(information.hosting), // entity (organization or person name) + hosting_country: string.optional().onMismatch(information.hosting_country), // ISO-3166 2-letter code + }), + store_log: object({ + enable: oneOf(literal('on'), literal('off')).onMismatch(store_log.enable), + + restore_messages: oneOf(literal('on'), literal('off')).onMismatch( + store_log.restore_messages, + ), + expire_messages_days: natural.onMismatch(store_log.expire_messages_days), + expire_ntfs_hours: natural.onMismatch(store_log.expire_ntfs_hours), + + log_stats: oneOf(literal('on'), literal('off')).onMismatch( + store_log.log_stats, + ), + }), + auth: object({ + new_queues: oneOf(literal('on'), literal('off')).onMismatch( + auth.new_queues, + ), + + create_password: string.onMismatch(auth.create_password), + + control_port_admin_password: string.onMismatch( + auth.control_port_admin_password, + ), + control_port_user_password: string.onMismatch( + auth.control_port_user_password, + ), + }), + transport: object({ + host: literal(transport.host).onMismatch(transport.host), + port: literal(transport.port).onMismatch(transport.port), + log_tls_errors: oneOf(literal('on'), literal('off')).onMismatch( + transport.log_tls_errors, + ), + + websockets: oneOf(literal('on'), literal('off')).onMismatch( + transport.websockets, + ), + control_port: literal(transport.control_port).onMismatch( + transport.control_port, + ), + }), + proxy: object({ + // Network configuration for SMP proxy client. + host_mode: oneOf(literal('public'), literal('onion')).onMismatch( + proxy.host_mode, + ), + required_host_mode: oneOf(literal('on'), literal('off')).onMismatch( + proxy.required_host_mode, + ), + + own_server_domains: arrayOf(string).onMismatch(proxy.own_server_domains), + + socks_proxy: string.onMismatch(proxy.socks_proxy), + socks_mode: oneOf(literal('onion'), literal('always')).onMismatch( + proxy.socks_mode, + ), + + client_concurrency: natural.onMismatch(proxy.client_concurrency), + }), + inactive_clients: object({ + disconnect: oneOf(literal('on'), literal('off')).onMismatch( + inactive_clients.disconnect, + ), + ttl: natural.onMismatch(inactive_clients.ttl), + check_interval: natural.onMismatch(inactive_clients.check_interval), + }), + web: object({ + static_path: literal(web.static_path), + http: literal(web.http).onMismatch(web.http), + + https: literal(web.https).onMismatch(web.https), + cert: literal(web.cert).onMismatch(web.cert), + key: literal(web.key).onMismatch(web.key), + }), +}) + +export const smpConfigFile = FileHelper.toml( + '/media/startos/volumes/main/etc/opt/simplex/smp-server.ini', + shape, +) diff --git a/startos/file-models/xftp.ini.ts b/startos/file-models/xftp.ini.ts new file mode 100644 index 0000000..37cb352 --- /dev/null +++ b/startos/file-models/xftp.ini.ts @@ -0,0 +1,10 @@ +import { matches, FileHelper } from '@start9labs/start-sdk' +const { object } = matches + +// @TODO +const shape = object({}) + +export const xftpConfigFile = FileHelper.toml( + '/media/startos/volumes/main/etc/opt/simplex/xftp-server.ini', + shape, +) 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..f5c57cd --- /dev/null +++ b/startos/init.ts @@ -0,0 +1,40 @@ +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 { smpConfigFile } from './file-models/smp.ini' +import { randomPassword, smpConfigDefaults } from './utils' +import { utils } from '@start9labs/start-sdk' + +// **** Install **** +const install = sdk.setupInstall(async ({ effects }) => { + await smpConfigFile.write(effects, { + ...smpConfigDefaults, + auth: { + ...smpConfigDefaults.auth, + create_password: utils.getDefaultString(randomPassword), + }, + proxy: { + ...smpConfigDefaults.proxy, + socks_proxy: `${await sdk.getOsIp(effects)}:9050`, + }, + }) +}) + +// **** 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..e9d2e52 --- /dev/null +++ b/startos/interfaces.ts @@ -0,0 +1,61 @@ +import { sdk } from './sdk' +import { smpPort, webPort, xftpPort } from './utils' + +export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { + // ** SMP Server ** + const smpMulti = sdk.MultiHost.of(effects, 'smp-multi') + + // smp + const smpMultiOrigin = await smpMulti.bindPort(smpPort, { + protocol: 'http', + }) + const smp = sdk.createInterface(effects, { + name: 'SMP Server', + id: 'smp', + description: 'The SMP server for SimpleX', + type: 'api', + masked: false, + schemeOverride: null, + username: null, + path: '', + search: {}, + }) + const smpReceipt = await smpMultiOrigin.export([smp]) + + // admin + const adminMultiOrigin = await smpMulti.bindPort(webPort, { + protocol: 'http', + }) + const admin = sdk.createInterface(effects, { + name: 'Admin UI', + id: 'admin', + description: 'Your SimpleX SMP server admin user interface', + type: 'ui', + masked: false, + schemeOverride: null, + username: null, + path: '', + search: {}, + }) + const adminReceipt = await adminMultiOrigin.export([admin]) + + // ** XFTP Server ** + const xftpMulti = sdk.MultiHost.of(effects, 'xftp-multi') + const xftpMultiOrigin = await xftpMulti.bindPort(xftpPort, { + protocol: 'http', + }) + const xftp = sdk.createInterface(effects, { + name: 'XFTP Server', + id: 'xftp', + description: 'The XFTP server for SimpleX', + type: 'api', + masked: false, + schemeOverride: null, + username: null, + path: '', + search: {}, + }) + const xftpReceipt = await xftpMultiOrigin.export([xftp]) + + return [smpReceipt, adminReceipt, xftpReceipt] +}) diff --git a/startos/main.ts b/startos/main.ts new file mode 100644 index 0000000..9bc367c --- /dev/null +++ b/startos/main.ts @@ -0,0 +1,56 @@ +import { sdk } from './sdk' +import { T } from '@start9labs/start-sdk' +import { smpPort, xftpPort } 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 SimpleX!') + + /** + * ======================== Additional Health Checks (optional) ======================== + * + * In this section, we define *additional* health checks beyond those included with each daemon (below). + */ + const additionalChecks: T.HealthCheck[] = [] + + /** + * ======================== 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, additionalChecks) + .addDaemon('smp', { + subcontainer: { imageId: 'simplex' }, + command: ['smp-server', 'start', '+RTS', '-N', '-RTS'], + mounts: sdk.Mounts.of().addVolume('main', null, '/data', false), + ready: { + display: 'SMP Server', + fn: () => + sdk.healthCheck.checkPortListening(effects, smpPort, { + successMessage: 'The SMP server is ready', + errorMessage: 'The SMP server is not ready', + }), + }, + requires: [], + }) + .addDaemon('xftp', { + subcontainer: { imageId: 'simplex' }, + command: ['xftp-server', 'start', '+RTS', '-N', '-RTS'], + mounts: sdk.Mounts.of().addVolume('main', null, '/data', false), + ready: { + display: 'XFTP Server', + fn: () => + sdk.healthCheck.checkPortListening(effects, xftpPort, { + successMessage: 'The XFTP server is ready', + errorMessage: 'The XFTP server is not ready', + }), + }, + requires: [], + }) +}) diff --git a/startos/manifest.ts b/startos/manifest.ts new file mode 100644 index 0000000..f5ff013 --- /dev/null +++ b/startos/manifest.ts @@ -0,0 +1,37 @@ +import { setupManifest } from '@start9labs/start-sdk' +import { source_code } from './utils' + +export const manifest = setupManifest({ + id: 'simplex', + title: 'SimpleX Server', + license: 'mit', + wrapperRepo: 'https://github.com/Start9Labs/simplex-startos', + upstreamRepo: source_code, + supportSite: 'https://github.com/simplex-chat/simplexmq/issues', + marketingSite: 'https://simplex.chat/', + donationUrl: + 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', + // @TODO + description: { + short: '', + long: '', + }, + volumes: ['main'], + images: { + simplex: { + source: { + dockerBuild: {}, + }, + }, + }, + hardwareRequirements: {}, + alerts: { + install: null, + 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..a5791a0 --- /dev/null +++ b/startos/store.ts @@ -0,0 +1,5 @@ +import { setupExposeStore } from '@start9labs/start-sdk' + +export type Store = {} + +export const exposedStore = setupExposeStore(() => []) diff --git a/startos/utils.ts b/startos/utils.ts new file mode 100644 index 0000000..8760965 --- /dev/null +++ b/startos/utils.ts @@ -0,0 +1,95 @@ +export const smpPort = 5223 +export const xftpPort = 443 +export const controlPort = 5224 +export const webPort = 443 + +export const source_code = 'https://github.com/simplex-chat/simplexmq/' + +export const randomPassword = { + charset: 'a-z,A-Z,1-9,!,$,%,&,*', + len: 21, +} + +export const smpConfigDefaults = { + information: { + // Basic info + LICENSE: undefined, + source_code, + + // Server usage conditions and amendments. + usage_conditions: undefined, + condition_amendments: undefined, + + // Server location and operator + server_country: undefined, + operator: undefined, + operator_country: undefined, + website: undefined, + + // Administrative contacts + admin_simplex: undefined, // SimpleX address + admin_email: undefined, + admin_pgp: undefined, + admin_pgp_fingerprint: undefined, + + // Contacts for complaints and feedback. + complaints_simplex: undefined, // SimpleX address + complaints_email: undefined, + complaints_pgp: undefined, + complaints_pgp_fingerprint: undefined, + + // Hosting provider + hosting: undefined, // entity organization or person name + hosting_country: undefined, // ISO-3166 2-letter code + }, + store_log: { + enable: 'on' as const, + + restore_messages: 'on' as const, + expire_messages_days: 21, + expire_ntfs_hours: 24, + + log_stats: 'on' as const, + }, + auth: { + new_queues: 'on' as const, + + create_password: '', // any printable ASCII characters without whitespace, '@', ':' and '/' + + control_port_admin_password: '', + control_port_user_password: '', + }, + transport: { + host: 'hostnames', // + port: 5223, + log_tls_errors: 'off' as const, + + websockets: 'off' as const, + control_port: controlPort, + }, + proxy: { + // Network configuration for SMP proxy client. + host_mode: 'public' as const, + required_host_mode: 'off' as const, + + own_server_domains: [], + + socks_proxy: '', + socks_mode: 'onion' as const, + + client_concurrency: 32, + }, + inactive_clients: { + disconnect: 'off' as const, + ttl: 21600, + check_interval: 3600, + }, + web: { + static_path: 'var/opt/simplex/www', + http: undefined, + + https: webPort, + cert: '', // @TODO Aiden + key: '', // @TODO Aiden + }, +} diff --git a/startos/versions/index.ts b/startos/versions/index.ts new file mode 100644 index 0000000..7428c12 --- /dev/null +++ b/startos/versions/index.ts @@ -0,0 +1,4 @@ +import { VersionGraph } from '@start9labs/start-sdk' +import { v_6_3_1_1 } from './v6.3.1.1' + +export const versions = VersionGraph.of(v_6_3_1_1) diff --git a/startos/versions/v6.3.1.1.ts b/startos/versions/v6.3.1.1.ts new file mode 100644 index 0000000..dc602c4 --- /dev/null +++ b/startos/versions/v6.3.1.1.ts @@ -0,0 +1,14 @@ +import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk' +import { rmdir } from 'fs/promises' + +export const v_6_3_1_1 = VersionInfo.of({ + version: '6.3.1:1', + releaseNotes: 'Revamped for StartOS 0.4.0', + migrations: { + up: async ({ effects }) => { + // remove old start9 dir + await rmdir('/root/start9') + }, + down: IMPOSSIBLE, + }, +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a2945a5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "include": ["startos/**/*.ts", "node_modules/**/startos"], + "compilerOptions": { + "target": "ES2018", + "module": "CommonJS", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true + } +} From 88a6600e73f90a3eb501b4a178dc0d202b3de0bc Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Mon, 21 Apr 2025 14:01:20 -0600 Subject: [PATCH 02/35] beta 5 --- package-lock.json | 77 +++++++++++++++++---------------- package.json | 2 +- startos/file-models/smp.ini.ts | 2 +- startos/file-models/xftp.ini.ts | 2 +- startos/init.ts | 13 ++++-- startos/main.ts | 13 ++++-- startos/store.ts | 2 + 7 files changed, 63 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index b5e30fd..67bda53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.3.6-beta.18" + "@start9labs/start-sdk": "0.4.0-beta.5" }, "devDependencies": { "@types/node": "^22.1.0", @@ -16,18 +16,18 @@ } }, "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==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-td6ZUkz2oS3VeleBcN+m//Q6HlCFCPrnI0FZhrt/h4XqLEdOyYp2u21nd8MdsR+WJy5r9PTDaHTDDfhf4H4l6Q==", "license": "ISC" }, "node_modules/@noble/curves": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz", - "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", + "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", "license": "MIT", "dependencies": { - "@noble/hashes": "1.7.1" + "@noble/hashes": "1.7.2" }, "engines": { "node": "^14.21.3 || >=16" @@ -37,9 +37,9 @@ } }, "node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", + "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", "license": "MIT", "engines": { "node": "^14.21.3 || >=16" @@ -49,20 +49,20 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.3.6-beta.18", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.3.6-beta.18.tgz", - "integrity": "sha512-2DWx2mI4/X76wr8bqpqX3NAyat4h35lLtcElNCIEeEc8hF9+ftgMCyv1F5T9A8FoKHT3+XrohiNQGpPv6ed1mA==", + "version": "0.4.0-beta.5", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.5.tgz", + "integrity": "sha512-4ik1WC9G2G584cWH2yEMMR5NgCBexUiiujPK3PEAI8qJmIUS6Ub2jGdHpRPtEdFepYeJLhWVdYTJ3Tb6zv+mhA==", "license": "MIT", "dependencies": { - "@iarna/toml": "^2.2.5", - "@noble/curves": "^1.4.0", - "@noble/hashes": "^1.4.0", - "deep-equality-data-structures": "^1.5.1", + "@iarna/toml": "^3.0.0", + "@noble/curves": "^1.8.2", + "@noble/hashes": "^1.7.2", + "deep-equality-data-structures": "^2.0.0", + "ini": "^5.0.0", "isomorphic-fetch": "^3.0.0", - "lodash.merge": "^4.6.2", - "mime-types": "^2.1.35", + "mime-types": "^3.0.1", "ts-matches": "^6.2.1", - "yaml": "^2.2.2" + "yaml": "^2.7.1" } }, "node_modules/@types/node": { @@ -86,14 +86,23 @@ } }, "node_modules/deep-equality-data-structures": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/deep-equality-data-structures/-/deep-equality-data-structures-1.5.1.tgz", - "integrity": "sha512-P7zsL2/AbZIGHDxbo/LLEhCp11AttRp8GvzXOXudqMT/qiGCLo/pyI4lAZvjUZyQnlIbPna3fv8DMsuRvLt4ww==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deep-equality-data-structures/-/deep-equality-data-structures-2.0.0.tgz", + "integrity": "sha512-qgrUr7MKXq7VRN+WUpQ48QlXVGL0KdibAoTX8KRg18lgOgqbEKMAW1WZsVCtakY4+XX42pbAJzTz/DlXEFM2Fg==", "license": "MIT", "dependencies": { "object-hash": "^3.0.0" } }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/isomorphic-fetch": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", @@ -104,28 +113,22 @@ "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==", - "license": "MIT" - }, "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==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "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==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { "node": ">= 0.6" diff --git a/package.json b/package.json index b319ef2..689a24e 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.3.6-beta.18" + "@start9labs/start-sdk": "0.4.0-beta.5" }, "devDependencies": { "@types/node": "^22.1.0", diff --git a/startos/file-models/smp.ini.ts b/startos/file-models/smp.ini.ts index e274a6f..edcf08e 100644 --- a/startos/file-models/smp.ini.ts +++ b/startos/file-models/smp.ini.ts @@ -137,7 +137,7 @@ const shape = object({ }), }) -export const smpConfigFile = FileHelper.toml( +export const smpConfigFile = FileHelper.ini( '/media/startos/volumes/main/etc/opt/simplex/smp-server.ini', shape, ) diff --git a/startos/file-models/xftp.ini.ts b/startos/file-models/xftp.ini.ts index 37cb352..acc8ca3 100644 --- a/startos/file-models/xftp.ini.ts +++ b/startos/file-models/xftp.ini.ts @@ -4,7 +4,7 @@ const { object } = matches // @TODO const shape = object({}) -export const xftpConfigFile = FileHelper.toml( +export const xftpConfigFile = FileHelper.ini( '/media/startos/volumes/main/etc/opt/simplex/xftp-server.ini', shape, ) diff --git a/startos/init.ts b/startos/init.ts index f5c57cd..e1f211e 100644 --- a/startos/init.ts +++ b/startos/init.ts @@ -1,5 +1,5 @@ import { sdk } from './sdk' -import { exposedStore } from './store' +import { exposedStore, initStore } from './store' import { setDependencies } from './dependencies' import { setInterfaces } from './interfaces' import { versions } from './versions' @@ -8,8 +8,8 @@ import { smpConfigFile } from './file-models/smp.ini' import { randomPassword, smpConfigDefaults } from './utils' import { utils } from '@start9labs/start-sdk' -// **** Install **** -const install = sdk.setupInstall(async ({ effects }) => { +// **** Pre Install **** +const preInstall = sdk.setupPreInstall(async ({ effects }) => { await smpConfigFile.write(effects, { ...smpConfigDefaults, auth: { @@ -23,6 +23,9 @@ const install = sdk.setupInstall(async ({ effects }) => { }) }) +// **** Post Install **** +const postInstall = sdk.setupPostInstall(async ({ effects }) => {}) + // **** Uninstall **** const uninstall = sdk.setupUninstall(async ({ effects }) => {}) @@ -31,10 +34,12 @@ const uninstall = sdk.setupUninstall(async ({ effects }) => {}) */ export const { packageInit, packageUninit, containerInit } = sdk.setupInit( versions, - install, + preInstall, + postInstall, uninstall, setInterfaces, setDependencies, actions, + initStore, exposedStore, ) diff --git a/startos/main.ts b/startos/main.ts index 9bc367c..08ceffb 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -10,6 +10,13 @@ export const main = sdk.setupMain(async ({ effects, started }) => { */ console.info('Starting SimpleX!') + const simplexSub = await sdk.SubContainer.of( + effects, + { imageId: 'simplex' }, + sdk.Mounts.of().addVolume('main', null, '/data', false), + 'simplex-sub', + ) + /** * ======================== Additional Health Checks (optional) ======================== * @@ -26,9 +33,8 @@ export const main = sdk.setupMain(async ({ effects, started }) => { */ return sdk.Daemons.of(effects, started, additionalChecks) .addDaemon('smp', { - subcontainer: { imageId: 'simplex' }, + subcontainer: simplexSub, command: ['smp-server', 'start', '+RTS', '-N', '-RTS'], - mounts: sdk.Mounts.of().addVolume('main', null, '/data', false), ready: { display: 'SMP Server', fn: () => @@ -40,9 +46,8 @@ export const main = sdk.setupMain(async ({ effects, started }) => { requires: [], }) .addDaemon('xftp', { - subcontainer: { imageId: 'simplex' }, + subcontainer: simplexSub, command: ['xftp-server', 'start', '+RTS', '-N', '-RTS'], - mounts: sdk.Mounts.of().addVolume('main', null, '/data', false), ready: { display: 'XFTP Server', fn: () => diff --git a/startos/store.ts b/startos/store.ts index a5791a0..984f555 100644 --- a/startos/store.ts +++ b/startos/store.ts @@ -2,4 +2,6 @@ import { setupExposeStore } from '@start9labs/start-sdk' export type Store = {} +export const initStore: Store = {} + export const exposedStore = setupExposeStore(() => []) From 3c9f7b3ca607cc908f9aa4a87296a4fcbb30106d Mon Sep 17 00:00:00 2001 From: Mariusz Kogen Date: Sun, 4 May 2025 12:33:57 +0200 Subject: [PATCH 03/35] Update to v6.3.2 --- {.github => old/.github}/Dockerfile.simplex | 0 {.github => old/.github}/workflows/build-simplex-server.yml | 0 {.github => old/.github}/workflows/buildService.yml | 0 {.github => old/.github}/workflows/releaseService.yml | 0 simplexmq | 2 +- startos/manifest.ts | 2 +- startos/versions/index.ts | 4 ++-- startos/versions/{v6.3.1.1.ts => v6.3.2.1.ts} | 4 ++-- 8 files changed, 6 insertions(+), 6 deletions(-) rename {.github => old/.github}/Dockerfile.simplex (100%) rename {.github => old/.github}/workflows/build-simplex-server.yml (100%) rename {.github => old/.github}/workflows/buildService.yml (100%) rename {.github => old/.github}/workflows/releaseService.yml (100%) rename startos/versions/{v6.3.1.1.ts => v6.3.2.1.ts} (82%) diff --git a/.github/Dockerfile.simplex b/old/.github/Dockerfile.simplex similarity index 100% rename from .github/Dockerfile.simplex rename to old/.github/Dockerfile.simplex diff --git a/.github/workflows/build-simplex-server.yml b/old/.github/workflows/build-simplex-server.yml similarity index 100% rename from .github/workflows/build-simplex-server.yml rename to old/.github/workflows/build-simplex-server.yml diff --git a/.github/workflows/buildService.yml b/old/.github/workflows/buildService.yml similarity index 100% rename from .github/workflows/buildService.yml rename to old/.github/workflows/buildService.yml diff --git a/.github/workflows/releaseService.yml b/old/.github/workflows/releaseService.yml similarity index 100% rename from .github/workflows/releaseService.yml rename to old/.github/workflows/releaseService.yml diff --git a/simplexmq b/simplexmq index 79e9447..9abc0fa 160000 --- a/simplexmq +++ b/simplexmq @@ -1 +1 @@ -Subproject commit 79e9447b73cc315ce35042b0a5f210c07ea39b07 +Subproject commit 9abc0fa88dd70a7e30a041697335bb663c1140b7 diff --git a/startos/manifest.ts b/startos/manifest.ts index f5ff013..edaa1f2 100644 --- a/startos/manifest.ts +++ b/startos/manifest.ts @@ -4,7 +4,7 @@ import { source_code } from './utils' export const manifest = setupManifest({ id: 'simplex', title: 'SimpleX Server', - license: 'mit', + license: 'MIT', wrapperRepo: 'https://github.com/Start9Labs/simplex-startos', upstreamRepo: source_code, supportSite: 'https://github.com/simplex-chat/simplexmq/issues', diff --git a/startos/versions/index.ts b/startos/versions/index.ts index 7428c12..79cd0f4 100644 --- a/startos/versions/index.ts +++ b/startos/versions/index.ts @@ -1,4 +1,4 @@ import { VersionGraph } from '@start9labs/start-sdk' -import { v_6_3_1_1 } from './v6.3.1.1' +import { v_6_3_2_1 } from './v6.3.2.1' -export const versions = VersionGraph.of(v_6_3_1_1) +export const versions = VersionGraph.of(v_6_3_2_1) diff --git a/startos/versions/v6.3.1.1.ts b/startos/versions/v6.3.2.1.ts similarity index 82% rename from startos/versions/v6.3.1.1.ts rename to startos/versions/v6.3.2.1.ts index dc602c4..a9d695b 100644 --- a/startos/versions/v6.3.1.1.ts +++ b/startos/versions/v6.3.2.1.ts @@ -1,8 +1,8 @@ import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk' import { rmdir } from 'fs/promises' -export const v_6_3_1_1 = VersionInfo.of({ - version: '6.3.1:1', +export const v_6_3_2_1 = VersionInfo.of({ + version: '6.3.2:1', releaseNotes: 'Revamped for StartOS 0.4.0', migrations: { up: async ({ effects }) => { From f2a7d65400cc729327bf8699275f490b8b8aa177 Mon Sep 17 00:00:00 2001 From: Mariusz Kogen Date: Sun, 4 May 2025 15:18:53 +0200 Subject: [PATCH 04/35] Update dependencies --- Dockerfile | 8 ++++---- package-lock.json | 37 ++++++++++++++++++++++--------------- package.json | 10 +++++----- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3a248f4..bf71a4e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,8 +55,8 @@ COPY --from=build /final /usr/local/bin/ # simplexmq requires using SIGINT to correctly preserve undelivered messages and restore them on restart STOPSIGNAL SIGINT -ADD ./check-syn-ack.sh /usr/local/bin/check-syn-ack.sh -RUN chmod a+x /usr/local/bin/check-syn-ack.sh +# ADD ./check-syn-ack.sh /usr/local/bin/check-syn-ack.sh +# RUN chmod a+x /usr/local/bin/check-syn-ack.sh -ADD ./docker_entrypoint.sh /usr/local/bin/docker_entrypoint.sh -RUN chmod a+x /usr/local/bin/docker_entrypoint.sh +# ADD ./docker_entrypoint.sh /usr/local/bin/docker_entrypoint.sh +# RUN chmod a+x /usr/local/bin/docker_entrypoint.sh diff --git a/package-lock.json b/package-lock.json index 67bda53..7d1bb7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,13 +6,13 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.5" + "@start9labs/start-sdk": "0.4.0-beta.16" }, "devDependencies": { - "@types/node": "^22.1.0", - "@vercel/ncc": "^0.38.1", - "prettier": "^3.2.5", - "typescript": "^5.4.3" + "@types/node": "^22.15.3", + "@vercel/ncc": "^0.38.3", + "prettier": "^3.5.3", + "typescript": "^5.8.3" } }, "node_modules/@iarna/toml": { @@ -49,26 +49,33 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.5", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.5.tgz", - "integrity": "sha512-4ik1WC9G2G584cWH2yEMMR5NgCBexUiiujPK3PEAI8qJmIUS6Ub2jGdHpRPtEdFepYeJLhWVdYTJ3Tb6zv+mhA==", + "version": "0.4.0-beta.16", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.16.tgz", + "integrity": "sha512-O/Iwpv+nppnDTe/XM3eQtiJuQEIofxR2Ro3RT4FFjuswlWcl+UcqkDkuOu+5dZeQqJ34XENO2z+md9ltoMwztg==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", "@noble/curves": "^1.8.2", "@noble/hashes": "^1.7.2", + "@types/ini": "^4.1.1", "deep-equality-data-structures": "^2.0.0", "ini": "^5.0.0", "isomorphic-fetch": "^3.0.0", "mime-types": "^3.0.1", - "ts-matches": "^6.2.1", + "ts-matches": "^6.3.2", "yaml": "^2.7.1" } }, + "node_modules/@types/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==", + "license": "MIT" + }, "node_modules/@types/node": { - "version": "22.14.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", - "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "version": "22.15.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", + "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", "dev": true, "license": "MIT", "dependencies": { @@ -186,9 +193,9 @@ "license": "MIT" }, "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==", + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.3.2.tgz", + "integrity": "sha512-UhSgJymF8cLd4y0vV29qlKVCkQpUtekAaujXbQVc729FezS8HwqzepqvtjzQ3HboatIqN/Idor85O2RMwT7lIQ==", "license": "MIT" }, "node_modules/typescript": { diff --git a/package.json b/package.json index 689a24e..1cf9561 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.5" + "@start9labs/start-sdk": "0.4.0-beta.16" }, "devDependencies": { - "@types/node": "^22.1.0", - "@vercel/ncc": "^0.38.1", - "prettier": "^3.2.5", - "typescript": "^5.4.3" + "@types/node": "^22.15.3", + "@vercel/ncc": "^0.38.3", + "prettier": "^3.5.3", + "typescript": "^5.8.3" }, "prettier": { "trailingComma": "all", From bca39796a3c31044b287927744f4e9219a2026ad Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 10 May 2025 08:41:54 -0600 Subject: [PATCH 05/35] beta 20 --- Makefile | 6 ++++++ package-lock.json | 8 ++++---- package.json | 2 +- simplexmq | 2 +- startos/init.ts | 3 --- startos/main.ts | 7 ++++++- startos/sdk.ts | 6 +----- startos/store.ts | 7 ------- 8 files changed, 19 insertions(+), 22 deletions(-) delete mode 100644 startos/store.ts diff --git a/Makefile b/Makefile index c7c81b6..c283153 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,8 @@ INGREDIENTS := $(shell start-cli s9pk list-ingredients 2> /dev/null) .PHONY: all clean install check-deps check-init ingredients +.DELETE_ON_ERROR: + all: ${PACKAGE_ID}.s9pk @echo " Done!" @echo " Filesize:$(shell du -h $(PACKAGE_ID).s9pk) is ready" @@ -12,6 +14,10 @@ check-deps: echo "Error: start-cli not found. Please install it first."; \ exit 1; \ fi + @if ! command -v npm > /dev/null; then \ + echo "Error: npm (Node Package Manager) not found. Please install Node.js and npm."; \ + exit 1; \ + fi check-init: @if [ ! -f ~/.startos/developer.key.pem ]; then \ diff --git a/package-lock.json b/package-lock.json index 7d1bb7c..3abf020 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.16" + "@start9labs/start-sdk": "0.4.0-beta.20" }, "devDependencies": { "@types/node": "^22.15.3", @@ -49,9 +49,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.16", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.16.tgz", - "integrity": "sha512-O/Iwpv+nppnDTe/XM3eQtiJuQEIofxR2Ro3RT4FFjuswlWcl+UcqkDkuOu+5dZeQqJ34XENO2z+md9ltoMwztg==", + "version": "0.4.0-beta.20", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.20.tgz", + "integrity": "sha512-xh/8Is/N5d/PrPnLZeUdzsVGWUOQH2Ncxt27YqCuy1y7MnFGEuPF8x+cWbufojBN14Dl2fTfr5DrTUX/suKF1g==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/package.json b/package.json index 1cf9561..16cf1ed 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.16" + "@start9labs/start-sdk": "0.4.0-beta.20" }, "devDependencies": { "@types/node": "^22.15.3", diff --git a/simplexmq b/simplexmq index 9abc0fa..79e9447 160000 --- a/simplexmq +++ b/simplexmq @@ -1 +1 @@ -Subproject commit 9abc0fa88dd70a7e30a041697335bb663c1140b7 +Subproject commit 79e9447b73cc315ce35042b0a5f210c07ea39b07 diff --git a/startos/init.ts b/startos/init.ts index e1f211e..97926a4 100644 --- a/startos/init.ts +++ b/startos/init.ts @@ -1,5 +1,4 @@ import { sdk } from './sdk' -import { exposedStore, initStore } from './store' import { setDependencies } from './dependencies' import { setInterfaces } from './interfaces' import { versions } from './versions' @@ -40,6 +39,4 @@ export const { packageInit, packageUninit, containerInit } = sdk.setupInit( setInterfaces, setDependencies, actions, - initStore, - exposedStore, ) diff --git a/startos/main.ts b/startos/main.ts index 08ceffb..befa1d7 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -13,7 +13,12 @@ export const main = sdk.setupMain(async ({ effects, started }) => { const simplexSub = await sdk.SubContainer.of( effects, { imageId: 'simplex' }, - sdk.Mounts.of().addVolume('main', null, '/data', false), + sdk.Mounts.of().mountVolume({ + volumeId: 'main', + subpath: null, + mountpoint: '/data', + readonly: false, + }), 'simplex-sub', ) diff --git a/startos/sdk.ts b/startos/sdk.ts index 26c3015..04ae4b1 100644 --- a/startos/sdk.ts +++ b/startos/sdk.ts @@ -1,13 +1,9 @@ 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) +export const sdk = StartSdk.of().withManifest(manifest).build(true) diff --git a/startos/store.ts b/startos/store.ts deleted file mode 100644 index 984f555..0000000 --- a/startos/store.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { setupExposeStore } from '@start9labs/start-sdk' - -export type Store = {} - -export const initStore: Store = {} - -export const exposedStore = setupExposeStore(() => []) From 2d2f660d28c5103d5049e0b3b7bc22d9c0e7e7b6 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 21 May 2025 20:45:55 -0600 Subject: [PATCH 06/35] beta 24 --- package-lock.json | 8 ++-- package.json | 2 +- startos/backups.ts | 2 +- .../{file-models => fileModels}/smp.ini.ts | 0 .../{file-models => fileModels}/xftp.ini.ts | 5 ++- startos/index.ts | 8 ++-- startos/init.ts | 42 ------------------- startos/init/index.ts | 16 +++++++ startos/install/versionGraph.ts | 23 ++++++++++ startos/install/versions/index.ts | 2 + startos/{ => install}/versions/v6.3.2.1.ts | 5 +-- startos/interfaces.ts | 6 +-- startos/versions/index.ts | 4 -- 13 files changed, 60 insertions(+), 63 deletions(-) rename startos/{file-models => fileModels}/smp.ini.ts (100%) rename startos/{file-models => fileModels}/xftp.ini.ts (69%) delete mode 100644 startos/init.ts create mode 100644 startos/init/index.ts create mode 100644 startos/install/versionGraph.ts create mode 100644 startos/install/versions/index.ts rename startos/{ => install}/versions/v6.3.2.1.ts (71%) delete mode 100644 startos/versions/index.ts diff --git a/package-lock.json b/package-lock.json index 3abf020..90487c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.20" + "@start9labs/start-sdk": "0.4.0-beta.24" }, "devDependencies": { "@types/node": "^22.15.3", @@ -49,9 +49,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.20", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.20.tgz", - "integrity": "sha512-xh/8Is/N5d/PrPnLZeUdzsVGWUOQH2Ncxt27YqCuy1y7MnFGEuPF8x+cWbufojBN14Dl2fTfr5DrTUX/suKF1g==", + "version": "0.4.0-beta.24", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.24.tgz", + "integrity": "sha512-dQdj249IkHdigOZOkuGrjuacj5+XG7KUJ+sO/9sIqER3f6pwoWXSgSLfHr/zsUG/Vb8Cp/c3puKJnJoVP9p31g==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/package.json b/package.json index 16cf1ed..66e65b1 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.20" + "@start9labs/start-sdk": "0.4.0-beta.24" }, "devDependencies": { "@types/node": "^22.15.3", diff --git a/startos/backups.ts b/startos/backups.ts index 6ccffb6..f25f5b6 100644 --- a/startos/backups.ts +++ b/startos/backups.ts @@ -1,5 +1,5 @@ import { sdk } from './sdk' -export const { createBackup, restoreBackup } = sdk.setupBackups( +export const { createBackup, restoreInit } = sdk.setupBackups( async ({ effects }) => sdk.Backups.volumes('main'), ) diff --git a/startos/file-models/smp.ini.ts b/startos/fileModels/smp.ini.ts similarity index 100% rename from startos/file-models/smp.ini.ts rename to startos/fileModels/smp.ini.ts diff --git a/startos/file-models/xftp.ini.ts b/startos/fileModels/xftp.ini.ts similarity index 69% rename from startos/file-models/xftp.ini.ts rename to startos/fileModels/xftp.ini.ts index acc8ca3..6102a08 100644 --- a/startos/file-models/xftp.ini.ts +++ b/startos/fileModels/xftp.ini.ts @@ -5,6 +5,9 @@ const { object } = matches const shape = object({}) export const xftpConfigFile = FileHelper.ini( - '/media/startos/volumes/main/etc/opt/simplex/xftp-server.ini', + { + volumeId: 'main', + subpath: '/etc/opt/simplex/xftp-server.ini', + }, shape, ) diff --git a/startos/index.ts b/startos/index.ts index 8d32c10..5bc2685 100644 --- a/startos/index.ts +++ b/startos/index.ts @@ -1,11 +1,11 @@ /** * Plumbing. DO NOT EDIT. */ -export { createBackup, restoreBackup } from './backups' +export { createBackup } from './backups' export { main } from './main' -export { packageInit, packageUninit, containerInit } from './init' +export { init, uninit } 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) +import { versionGraph } from './install/versionGraph' +export const manifest = buildManifest(versionGraph, sdkManifest) diff --git a/startos/init.ts b/startos/init.ts deleted file mode 100644 index 97926a4..0000000 --- a/startos/init.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { sdk } from './sdk' -import { setDependencies } from './dependencies' -import { setInterfaces } from './interfaces' -import { versions } from './versions' -import { actions } from './actions' -import { smpConfigFile } from './file-models/smp.ini' -import { randomPassword, smpConfigDefaults } from './utils' -import { utils } from '@start9labs/start-sdk' - -// **** Pre Install **** -const preInstall = sdk.setupPreInstall(async ({ effects }) => { - await smpConfigFile.write(effects, { - ...smpConfigDefaults, - auth: { - ...smpConfigDefaults.auth, - create_password: utils.getDefaultString(randomPassword), - }, - proxy: { - ...smpConfigDefaults.proxy, - socks_proxy: `${await sdk.getOsIp(effects)}:9050`, - }, - }) -}) - -// **** Post Install **** -const postInstall = sdk.setupPostInstall(async ({ effects }) => {}) - -// **** Uninstall **** -const uninstall = sdk.setupUninstall(async ({ effects }) => {}) - -/** - * Plumbing. DO NOT EDIT. - */ -export const { packageInit, packageUninit, containerInit } = sdk.setupInit( - versions, - preInstall, - postInstall, - uninstall, - setInterfaces, - setDependencies, - actions, -) diff --git a/startos/init/index.ts b/startos/init/index.ts new file mode 100644 index 0000000..f69244d --- /dev/null +++ b/startos/init/index.ts @@ -0,0 +1,16 @@ +import { sdk } from '../sdk' +import { setDependencies } from '../dependencies' +import { setInterfaces } from '../interfaces' +import { versionGraph } from '../install/versionGraph' +import { actions } from '../actions' +import { restoreInit } from '../backups' + +export const init = sdk.setupInit( + restoreInit, + versionGraph, + setInterfaces, + setDependencies, + actions, +) + +export const uninit = sdk.setupUninit(versionGraph) diff --git a/startos/install/versionGraph.ts b/startos/install/versionGraph.ts new file mode 100644 index 0000000..3204c2c --- /dev/null +++ b/startos/install/versionGraph.ts @@ -0,0 +1,23 @@ +import { utils, VersionGraph } from '@start9labs/start-sdk' +import { current, other } from './versions' +import { randomPassword, smpConfigDefaults } from '../utils' +import { smpConfigFile } from '../fileModels/smp.ini' +import { sdk } from '../sdk' + +export const versionGraph = VersionGraph.of({ + current, + other, + preInstall: async (effects) => { + await smpConfigFile.write(effects, { + ...smpConfigDefaults, + auth: { + ...smpConfigDefaults.auth, + create_password: utils.getDefaultString(randomPassword), + }, + proxy: { + ...smpConfigDefaults.proxy, + socks_proxy: `${await sdk.getOsIp(effects)}:9050`, + }, + }) + }, +}) diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts new file mode 100644 index 0000000..7c3e78a --- /dev/null +++ b/startos/install/versions/index.ts @@ -0,0 +1,2 @@ +export { v_6_3_2_1 as current } from './v6.3.2.1' +export const other = [] diff --git a/startos/versions/v6.3.2.1.ts b/startos/install/versions/v6.3.2.1.ts similarity index 71% rename from startos/versions/v6.3.2.1.ts rename to startos/install/versions/v6.3.2.1.ts index a9d695b..acc1ded 100644 --- a/startos/versions/v6.3.2.1.ts +++ b/startos/install/versions/v6.3.2.1.ts @@ -1,13 +1,12 @@ import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk' -import { rmdir } from 'fs/promises' +import { rm } from 'fs/promises' export const v_6_3_2_1 = VersionInfo.of({ version: '6.3.2:1', releaseNotes: 'Revamped for StartOS 0.4.0', migrations: { up: async ({ effects }) => { - // remove old start9 dir - await rmdir('/root/start9') + await rm('/data/start9', { recursive: true }).catch(console.error) }, down: IMPOSSIBLE, }, diff --git a/startos/interfaces.ts b/startos/interfaces.ts index e9d2e52..300a42f 100644 --- a/startos/interfaces.ts +++ b/startos/interfaces.ts @@ -18,7 +18,7 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { schemeOverride: null, username: null, path: '', - search: {}, + query: {}, }) const smpReceipt = await smpMultiOrigin.export([smp]) @@ -35,7 +35,7 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { schemeOverride: null, username: null, path: '', - search: {}, + query: {}, }) const adminReceipt = await adminMultiOrigin.export([admin]) @@ -53,7 +53,7 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { schemeOverride: null, username: null, path: '', - search: {}, + query: {}, }) const xftpReceipt = await xftpMultiOrigin.export([xftp]) diff --git a/startos/versions/index.ts b/startos/versions/index.ts deleted file mode 100644 index 79cd0f4..0000000 --- a/startos/versions/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { VersionGraph } from '@start9labs/start-sdk' -import { v_6_3_2_1 } from './v6.3.2.1' - -export const versions = VersionGraph.of(v_6_3_2_1) From 832d94dcd9a04c04d09fa7f75b630f8f35a3612f Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 21 May 2025 21:39:24 -0600 Subject: [PATCH 07/35] better file model --- startos/fileModels/smp.ini.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/startos/fileModels/smp.ini.ts b/startos/fileModels/smp.ini.ts index edcf08e..745b8ec 100644 --- a/startos/fileModels/smp.ini.ts +++ b/startos/fileModels/smp.ini.ts @@ -138,6 +138,9 @@ const shape = object({ }) export const smpConfigFile = FileHelper.ini( - '/media/startos/volumes/main/etc/opt/simplex/smp-server.ini', + { + volumeId: 'main', + subpath: '/etc/opt/simplex/smp-server.ini', + }, shape, ) From e6898c8e8ac5484ad230943f10d9f65ac4023d24 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 7 Jun 2025 15:32:08 -0600 Subject: [PATCH 08/35] beta 27 --- package-lock.json | 36 ++++++++++++---------------- package.json | 2 +- startos/install/versions/v6.3.2.1.ts | 4 +++- startos/main.ts | 4 ++-- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 90487c8..29a48dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.24" + "@start9labs/start-sdk": "^0.4.0-beta.27" }, "devDependencies": { "@types/node": "^22.15.3", @@ -49,9 +49,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.24", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.24.tgz", - "integrity": "sha512-dQdj249IkHdigOZOkuGrjuacj5+XG7KUJ+sO/9sIqER3f6pwoWXSgSLfHr/zsUG/Vb8Cp/c3puKJnJoVP9p31g==", + "version": "0.4.0-beta.27", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.27.tgz", + "integrity": "sha512-3ct1lr40gvU6QVHtggP/voV/X6agmTVq1KWeKTwuyPeA4R9PoghYq3gRCGBs0J/414vajRrCX8ZVtwhPgciNIw==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -61,7 +61,7 @@ "deep-equality-data-structures": "^2.0.0", "ini": "^5.0.0", "isomorphic-fetch": "^3.0.0", - "mime-types": "^3.0.1", + "mime": "^4.0.7", "ts-matches": "^6.3.2", "yaml": "^2.7.1" } @@ -120,25 +120,19 @@ "whatwg-fetch": "^3.4.1" } }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/mime": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", + "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" + "bin": { + "mime": "bin/cli.js" }, "engines": { - "node": ">= 0.6" + "node": ">=16" } }, "node_modules/node-fetch": { diff --git a/package.json b/package.json index 66e65b1..27cc438 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.24" + "@start9labs/start-sdk": "^0.4.0-beta.27" }, "devDependencies": { "@types/node": "^22.15.3", diff --git a/startos/install/versions/v6.3.2.1.ts b/startos/install/versions/v6.3.2.1.ts index acc1ded..80efbd1 100644 --- a/startos/install/versions/v6.3.2.1.ts +++ b/startos/install/versions/v6.3.2.1.ts @@ -6,7 +6,9 @@ export const v_6_3_2_1 = VersionInfo.of({ releaseNotes: 'Revamped for StartOS 0.4.0', migrations: { up: async ({ effects }) => { - await rm('/data/start9', { recursive: true }).catch(console.error) + await rm('/media/startos/volumes/main/start9', { recursive: true }).catch( + console.error, + ) }, down: IMPOSSIBLE, }, diff --git a/startos/main.ts b/startos/main.ts index befa1d7..1d6a103 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -39,7 +39,7 @@ export const main = sdk.setupMain(async ({ effects, started }) => { return sdk.Daemons.of(effects, started, additionalChecks) .addDaemon('smp', { subcontainer: simplexSub, - command: ['smp-server', 'start', '+RTS', '-N', '-RTS'], + exec: { command: ['smp-server', 'start', '+RTS', '-N', '-RTS'] }, ready: { display: 'SMP Server', fn: () => @@ -52,7 +52,7 @@ export const main = sdk.setupMain(async ({ effects, started }) => { }) .addDaemon('xftp', { subcontainer: simplexSub, - command: ['xftp-server', 'start', '+RTS', '-N', '-RTS'], + exec: { command: ['xftp-server', 'start', '+RTS', '-N', '-RTS'] }, ready: { display: 'XFTP Server', fn: () => From 108930d9a9549259db6c2deb029b2d0138e102e2 Mon Sep 17 00:00:00 2001 From: Mariusz Kogen Date: Wed, 3 Sep 2025 21:31:43 +0200 Subject: [PATCH 09/35] Makefile refactor --- Makefile | 101 +++++++++++++++++++++++++++++--------------- startos/main.ts | 2 +- startos/manifest.ts | 13 +++++- 3 files changed, 79 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index c283153..d4a54d6 100644 --- a/Makefile +++ b/Makefile @@ -1,42 +1,82 @@ -PACKAGE_ID := $(shell grep -o "id: '[^']*'" startos/manifest.ts | sed "s/id: '\([^']*\)'/\1/") -INGREDIENTS := $(shell start-cli s9pk list-ingredients 2> /dev/null) +PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest.ts) +INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null) -.PHONY: all clean install check-deps check-init ingredients +CMD_ARCH_GOAL := $(filter aarch64 x86_64 arm x86, $(MAKECMDGOALS)) +ifeq ($(CMD_ARCH_GOAL),) + BUILD := universal + S9PK := $(PACKAGE_ID).s9pk +else + RAW_ARCH := $(firstword $(CMD_ARCH_GOAL)) + ACTUAL_ARCH := $(subst x86,x86_64,$(subst arm,aarch64,$(RAW_ARCH))) + BUILD := $(ACTUAL_ARCH) + S9PK := $(PACKAGE_ID)_$(BUILD).s9pk +endif +.PHONY: all aarch64 x86_64 arm x86 clean install check-deps check-init package ingredients .DELETE_ON_ERROR: -all: ${PACKAGE_ID}.s9pk - @echo " Done!" - @echo " Filesize:$(shell du -h $(PACKAGE_ID).s9pk) is ready" +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 '.hardwareRequirements.arch | 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 -check-deps: - @if ! command -v start-cli > /dev/null; then \ - echo "Error: start-cli not found. Please install it first."; \ - exit 1; \ - fi - @if ! command -v npm > /dev/null; then \ - echo "Error: npm (Node Package Manager) not found. Please install Node.js and npm."; \ +all: $(PACKAGE_ID).s9pk + $(call SUMMARY,$(S9PK)) + +$(BUILD): $(PACKAGE_ID)_$(BUILD).s9pk + $(call SUMMARY,$(S9PK)) + +x86: x86_64 +arm: aarch64 + +$(S9PK): $(INGREDIENTS) .git/HEAD .git/index + @$(MAKE) --no-print-directory ingredients + @echo " Packing '$(S9PK)'..." + BUILD=$(BUILD) start-cli s9pk pack -o $(S9PK) + +ingredients: $(INGREDIENTS) + @echo " Re-evaluating ingredients..." + +install: package | 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 + fi; \ + echo "\n🚀 Installing to $$HOST ..."; \ + start-cli package install -s $(S9PK) + +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; \ fi -ingredients: $(INGREDIENTS) - @echo "Re-evaluating ingredients..." - -${PACKAGE_ID}.s9pk: $(INGREDIENTS) | check-deps check-init - @$(MAKE) --no-print-directory ingredients - start-cli s9pk pack - -javascript/index.js: $(shell git ls-files startos) tsconfig.json node_modules package.json +javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules npm run build -assets: - mkdir -p assets - node_modules: package-lock.json npm ci @@ -44,12 +84,5 @@ package-lock.json: package.json npm i clean: - rm -rf ${PACKAGE_ID}.s9pk - rm -rf javascript - rm -rf node_modules - -install: | check-deps check-init - @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 + @echo "Cleaning up build artifacts..." + @rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk javascript node_modules \ No newline at end of file diff --git a/startos/main.ts b/startos/main.ts index 1d6a103..2c9059a 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -8,7 +8,7 @@ export const main = sdk.setupMain(async ({ effects, started }) => { * * In this section, we fetch any resources or run any desired preliminary commands. */ - console.info('Starting SimpleX!') + console.info('[i] Starting SimpleX!') const simplexSub = await sdk.SubContainer.of( effects, diff --git a/startos/manifest.ts b/startos/manifest.ts index edaa1f2..6c0afef 100644 --- a/startos/manifest.ts +++ b/startos/manifest.ts @@ -1,6 +1,12 @@ import { setupManifest } from '@start9labs/start-sdk' +import { SDKImageInputSpec } from '@start9labs/start-sdk/base/lib/types/ManifestTypes' import { source_code } from './utils' +const BUILD = process.env.BUILD || '' + +const architectures = + BUILD === 'x86_64' || BUILD === 'aarch64' ? [BUILD] : ['x86_64', 'aarch64'] + export const manifest = setupManifest({ id: 'simplex', title: 'SimpleX Server', @@ -22,9 +28,12 @@ export const manifest = setupManifest({ source: { dockerBuild: {}, }, - }, + arch: architectures, + } as SDKImageInputSpec, + }, + hardwareRequirements: { + arch: architectures, }, - hardwareRequirements: {}, alerts: { install: null, update: null, From 789c07406265a7412c20d2b195b8d04282f5fed6 Mon Sep 17 00:00:00 2001 From: Mariusz Kogen Date: Wed, 3 Sep 2025 21:34:26 +0200 Subject: [PATCH 10/35] bump sdk.36 and devDeps --- package-lock.json | 60 ++++++++++++++++++++++----------------------- package.json | 8 +++--- startos/backups.ts | 2 +- startos/main.ts | 2 +- startos/manifest.ts | 2 ++ 5 files changed, 38 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29a48dd..ce8fb98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,13 +6,13 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.27" + "@start9labs/start-sdk": "^0.4.0-beta.36" }, "devDependencies": { - "@types/node": "^22.15.3", + "@types/node": "^22.18.0", "@vercel/ncc": "^0.38.3", - "prettier": "^3.5.3", - "typescript": "^5.8.3" + "prettier": "^3.6.2", + "typescript": "^5.9.2" } }, "node_modules/@iarna/toml": { @@ -22,12 +22,12 @@ "license": "ISC" }, "node_modules/@noble/curves": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", - "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", + "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.7.2" + "@noble/hashes": "1.8.0" }, "engines": { "node": "^14.21.3 || >=16" @@ -37,9 +37,9 @@ } }, "node_modules/@noble/hashes": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", - "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", + "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" @@ -49,9 +49,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.27", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.27.tgz", - "integrity": "sha512-3ct1lr40gvU6QVHtggP/voV/X6agmTVq1KWeKTwuyPeA4R9PoghYq3gRCGBs0J/414vajRrCX8ZVtwhPgciNIw==", + "version": "0.4.0-beta.36", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.36.tgz", + "integrity": "sha512-26C1NGJBy/yubp88SriuX2wfVDAM6/1rDx47wdypR2KEqGzhOrMRQC5hJDAMU8BeRAiL6ED8/Ca9CP+5+8BBBw==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -73,9 +73,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.3.tgz", - "integrity": "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==", + "version": "22.18.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz", + "integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -165,9 +165,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -187,15 +187,15 @@ "license": "MIT" }, "node_modules/ts-matches": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.3.2.tgz", - "integrity": "sha512-UhSgJymF8cLd4y0vV29qlKVCkQpUtekAaujXbQVc729FezS8HwqzepqvtjzQ3HboatIqN/Idor85O2RMwT7lIQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.5.0.tgz", + "integrity": "sha512-MhuobYhHYn6MlOTPAF/qk3tsRRioPac5ofYn68tc3rAJaGjsw1MsX1MOSep52DkvNJPgNV0F73zfgcQfYTVeyQ==", "license": "MIT" }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -236,15 +236,15 @@ } }, "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } } } diff --git a/package.json b/package.json index 27cc438..4af7bb8 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.27" + "@start9labs/start-sdk": "^0.4.0-beta.36" }, "devDependencies": { - "@types/node": "^22.15.3", + "@types/node": "^22.18.0", "@vercel/ncc": "^0.38.3", - "prettier": "^3.5.3", - "typescript": "^5.8.3" + "prettier": "^3.6.2", + "typescript": "^5.9.2" }, "prettier": { "trailingComma": "all", diff --git a/startos/backups.ts b/startos/backups.ts index f25f5b6..0a90b1e 100644 --- a/startos/backups.ts +++ b/startos/backups.ts @@ -1,5 +1,5 @@ import { sdk } from './sdk' export const { createBackup, restoreInit } = sdk.setupBackups( - async ({ effects }) => sdk.Backups.volumes('main'), + async ({ effects }) => sdk.Backups.ofVolumes('main'), ) diff --git a/startos/main.ts b/startos/main.ts index 2c9059a..523052c 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -36,7 +36,7 @@ export const main = sdk.setupMain(async ({ effects, started }) => { * * Each daemon defines its own health check, which can optionally be exposed to the user. */ - return sdk.Daemons.of(effects, started, additionalChecks) + return sdk.Daemons.of(effects, started) .addDaemon('smp', { subcontainer: simplexSub, exec: { command: ['smp-server', 'start', '+RTS', '-N', '-RTS'] }, diff --git a/startos/manifest.ts b/startos/manifest.ts index 6c0afef..e8960f3 100644 --- a/startos/manifest.ts +++ b/startos/manifest.ts @@ -15,6 +15,8 @@ export const manifest = setupManifest({ upstreamRepo: source_code, supportSite: 'https://github.com/simplex-chat/simplexmq/issues', marketingSite: 'https://simplex.chat/', + docsUrl: + 'https://github.com/Start9Labs/simplex-startos/blob/master/instructions.md', donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', // @TODO From 99d9f5e7679227e74fb64db186ea027fafa26f3b Mon Sep 17 00:00:00 2001 From: Mariusz Kogen Date: Tue, 23 Sep 2025 18:00:48 +0200 Subject: [PATCH 11/35] update for latest Makefile, sdk.beta41 and devDeps --- .dockerignore | 2 ++ Makefile | 2 +- assets/{ABOUT.md => README.md} | 0 instructions.md => docs/README.md | 0 package-lock.json | 30 +++++++++++++++--------------- package.json | 6 +++--- startos/manifest.ts | 2 +- 7 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 .dockerignore rename assets/{ABOUT.md => README.md} (100%) rename instructions.md => docs/README.md (100%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0a17f46 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +.gitmodules \ No newline at end of file diff --git a/Makefile b/Makefile index d4a54d6..3a64f2b 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ ifeq ($(CMD_ARCH_GOAL),) S9PK := $(PACKAGE_ID).s9pk else RAW_ARCH := $(firstword $(CMD_ARCH_GOAL)) - ACTUAL_ARCH := $(subst x86,x86_64,$(subst arm,aarch64,$(RAW_ARCH))) + ACTUAL_ARCH := $(patsubst x86,x86_64,$(patsubst arm,aarch64,$(RAW_ARCH))) BUILD := $(ACTUAL_ARCH) S9PK := $(PACKAGE_ID)_$(BUILD).s9pk endif diff --git a/assets/ABOUT.md b/assets/README.md similarity index 100% rename from assets/ABOUT.md rename to assets/README.md diff --git a/instructions.md b/docs/README.md similarity index 100% rename from instructions.md rename to docs/README.md diff --git a/package-lock.json b/package-lock.json index ce8fb98..d55e047 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,11 +6,11 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.36" + "@start9labs/start-sdk": "^0.4.0-beta.41" }, "devDependencies": { - "@types/node": "^22.18.0", - "@vercel/ncc": "^0.38.3", + "@types/node": "^22.18.6", + "@vercel/ncc": "^0.38.4", "prettier": "^3.6.2", "typescript": "^5.9.2" } @@ -49,9 +49,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.36", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.36.tgz", - "integrity": "sha512-26C1NGJBy/yubp88SriuX2wfVDAM6/1rDx47wdypR2KEqGzhOrMRQC5hJDAMU8BeRAiL6ED8/Ca9CP+5+8BBBw==", + "version": "0.4.0-beta.41", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.41.tgz", + "integrity": "sha512-nbr6fT8qtAr04lkFBMnUARAAVrTYdjoXA61j/1IzIvA9vkrkras985SE3YSAkgQPYfaAI27wLHQcAn8U0Kk01g==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -73,9 +73,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.18.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.0.tgz", - "integrity": "sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==", + "version": "22.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.6.tgz", + "integrity": "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -83,9 +83,9 @@ } }, "node_modules/@vercel/ncc": { - "version": "0.38.3", - "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.3.tgz", - "integrity": "sha512-rnK6hJBS6mwc+Bkab+PGPs9OiS0i/3kdTO+CkI8V0/VrW3vmz7O2Pxjw/owOlmo6PKEIxRSeZKv/kuL9itnpYA==", + "version": "0.38.4", + "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.4.tgz", + "integrity": "sha512-8LwjnlP39s08C08J5NstzriPvW1SP8Zfpp1BvC2sI35kPeZnHfxVkCwu4/+Wodgnd60UtT1n8K8zw+Mp7J9JmQ==", "dev": true, "license": "MIT", "bin": { @@ -121,9 +121,9 @@ } }, "node_modules/mime": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", - "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", "funding": [ "https://github.com/sponsors/broofa" ], diff --git a/package.json b/package.json index 4af7bb8..525cd21 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.36" + "@start9labs/start-sdk": "^0.4.0-beta.41" }, "devDependencies": { - "@types/node": "^22.18.0", - "@vercel/ncc": "^0.38.3", + "@types/node": "^22.18.6", + "@vercel/ncc": "^0.38.4", "prettier": "^3.6.2", "typescript": "^5.9.2" }, diff --git a/startos/manifest.ts b/startos/manifest.ts index e8960f3..0176cc0 100644 --- a/startos/manifest.ts +++ b/startos/manifest.ts @@ -16,7 +16,7 @@ export const manifest = setupManifest({ supportSite: 'https://github.com/simplex-chat/simplexmq/issues', marketingSite: 'https://simplex.chat/', docsUrl: - 'https://github.com/Start9Labs/simplex-startos/blob/master/instructions.md', + 'https://github.com/Start9Labs/simplex-startos/blob/update/040/docs/README.md', donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', // @TODO From ab9d674a6516658bddfa80fe43603ddf3aa3d3db Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sun, 21 Dec 2025 22:21:15 -0700 Subject: [PATCH 12/35] beta 45 --- Makefile | 53 +++++++++++++++++++++++---------------------- README.md | 16 ++++++++++++-- package-lock.json | 35 ++++++++++++++++-------------- package.json | 2 +- startos/main.ts | 11 ++-------- startos/manifest.ts | 20 +---------------- 6 files changed, 64 insertions(+), 73 deletions(-) diff --git a/Makefile b/Makefile index 3a64f2b..295e219 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,16 @@ PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest.ts) INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null) -CMD_ARCH_GOAL := $(filter aarch64 x86_64 arm x86, $(MAKECMDGOALS)) -ifeq ($(CMD_ARCH_GOAL),) - BUILD := universal - S9PK := $(PACKAGE_ID).s9pk -else - RAW_ARCH := $(firstword $(CMD_ARCH_GOAL)) - ACTUAL_ARCH := $(patsubst x86,x86_64,$(patsubst arm,aarch64,$(RAW_ARCH))) - BUILD := $(ACTUAL_ARCH) - S9PK := $(PACKAGE_ID)_$(BUILD).s9pk -endif - -.PHONY: all aarch64 x86_64 arm x86 clean install check-deps check-init package ingredients +.PHONY: all 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 '.hardwareRequirements.arch | join(", ")'); \ + 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"; \ @@ -37,30 +27,41 @@ define SUMMARY endef all: $(PACKAGE_ID).s9pk - $(call SUMMARY,$(S9PK)) + $(call SUMMARY,$<) + +arch/%: $(PACKAGE_ID)_%.s9pk + $(call SUMMARY,$<) -$(BUILD): $(PACKAGE_ID)_$(BUILD).s9pk - $(call SUMMARY,$(S9PK)) +x86 x86_64: arch/x86_64 +arm arm64 aarch64: arch/aarch64 +riscv riscv64: arch/riscv64 -x86: x86_64 -arm: aarch64 +$(PACKAGE_ID).s9pk: $(INGREDIENTS) .git/HEAD .git/index + @$(MAKE) --no-print-directory ingredients + @echo " Packing '$@'..." + start-cli s9pk pack -o $@ -$(S9PK): $(INGREDIENTS) .git/HEAD .git/index +$(PACKAGE_ID)_%.s9pk: $(INGREDIENTS) .git/HEAD .git/index @$(MAKE) --no-print-directory ingredients - @echo " Packing '$(S9PK)'..." - BUILD=$(BUILD) start-cli s9pk pack -o $(S9PK) + @echo " Packing '$@'..." + start-cli s9pk pack --arch=$* -o $@ ingredients: $(INGREDIENTS) @echo " Re-evaluating ingredients..." -install: package | check-deps check-init +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; \ - echo "\n🚀 Installing to $$HOST ..."; \ - start-cli package install -s $(S9PK) + 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" check-deps: @command -v start-cli >/dev/null || \ @@ -71,7 +72,7 @@ check-deps: check-init: @if [ ! -f ~/.startos/developer.key.pem ]; then \ echo "Initializing StartOS developer environment..."; \ - start-cli init; \ + start-cli init-key; \ fi javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules @@ -85,4 +86,4 @@ package-lock.json: package.json clean: @echo "Cleaning up build artifacts..." - @rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk javascript node_modules \ No newline at end of file + @rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk $(PACKAGE_ID)_riscv64.s9pk javascript node_modules diff --git a/README.md b/README.md index caf41c2..f54ca3d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,15 @@ -# SimpleX for StartOS +

+ Project Logo +

- +## Building from source + +1. Set up your [environment](https://docs.start9.com/packaging-guide/environment-setup.html). + +1. Clone this repository and `cd` into it. + +1. run `make`. + +1. The resulting `.s9pk` can be side loaded into StartOS. + +For a complete list of build options, see the [docs](https://docs.start9.com/packaging-guide/building.html) diff --git a/package-lock.json b/package-lock.json index d55e047..0443869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.41" + "@start9labs/start-sdk": "^0.4.0-beta.45" }, "devDependencies": { "@types/node": "^22.18.6", @@ -49,9 +49,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.41", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.41.tgz", - "integrity": "sha512-nbr6fT8qtAr04lkFBMnUARAAVrTYdjoXA61j/1IzIvA9vkrkras985SE3YSAkgQPYfaAI27wLHQcAn8U0Kk01g==", + "version": "0.4.0-beta.45", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.45.tgz", + "integrity": "sha512-N4iIifr5f1ElB4zcZnW8hCkPOAsfCxf+1UGqp249Tzm+gIRj45aq1wiE+vGCeSyGIuHsfMp3h5Pouyvfn33FOQ==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -73,9 +73,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.18.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.6.tgz", - "integrity": "sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==", + "version": "22.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", + "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", "dev": true, "license": "MIT", "dependencies": { @@ -165,9 +165,9 @@ } }, "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", "bin": { @@ -193,9 +193,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -236,15 +236,18 @@ } }, "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" } } } diff --git a/package.json b/package.json index 525cd21..c8a3414 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.41" + "@start9labs/start-sdk": "^0.4.0-beta.45" }, "devDependencies": { "@types/node": "^22.18.6", diff --git a/startos/main.ts b/startos/main.ts index 523052c..79765e7 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -2,7 +2,7 @@ import { sdk } from './sdk' import { T } from '@start9labs/start-sdk' import { smpPort, xftpPort } from './utils' -export const main = sdk.setupMain(async ({ effects, started }) => { +export const main = sdk.setupMain(async ({ effects }) => { /** * ======================== Setup (optional) ======================== * @@ -22,13 +22,6 @@ export const main = sdk.setupMain(async ({ effects, started }) => { 'simplex-sub', ) - /** - * ======================== Additional Health Checks (optional) ======================== - * - * In this section, we define *additional* health checks beyond those included with each daemon (below). - */ - const additionalChecks: T.HealthCheck[] = [] - /** * ======================== Daemons ======================== * @@ -36,7 +29,7 @@ export const main = sdk.setupMain(async ({ effects, started }) => { * * Each daemon defines its own health check, which can optionally be exposed to the user. */ - return sdk.Daemons.of(effects, started) + return sdk.Daemons.of(effects) .addDaemon('smp', { subcontainer: simplexSub, exec: { command: ['smp-server', 'start', '+RTS', '-N', '-RTS'] }, diff --git a/startos/manifest.ts b/startos/manifest.ts index 0176cc0..4e7ef1a 100644 --- a/startos/manifest.ts +++ b/startos/manifest.ts @@ -1,12 +1,6 @@ import { setupManifest } from '@start9labs/start-sdk' -import { SDKImageInputSpec } from '@start9labs/start-sdk/base/lib/types/ManifestTypes' import { source_code } from './utils' -const BUILD = process.env.BUILD || '' - -const architectures = - BUILD === 'x86_64' || BUILD === 'aarch64' ? [BUILD] : ['x86_64', 'aarch64'] - export const manifest = setupManifest({ id: 'simplex', title: 'SimpleX Server', @@ -30,19 +24,7 @@ export const manifest = setupManifest({ source: { dockerBuild: {}, }, - arch: architectures, - } as SDKImageInputSpec, - }, - hardwareRequirements: { - arch: architectures, - }, - alerts: { - install: null, - update: null, - uninstall: null, - restore: null, - start: null, - stop: null, + }, }, dependencies: {}, }) From b870fc10ce85cdb9caf36dae1d72b4469778ae98 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sun, 21 Dec 2025 22:21:24 -0700 Subject: [PATCH 13/35] workflows --- .github/workflows/buildService.yml | 16 ++++++++++++++++ .github/workflows/releaseService.yml | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 .github/workflows/buildService.yml create mode 100644 .github/workflows/releaseService.yml diff --git a/.github/workflows/buildService.yml b/.github/workflows/buildService.yml new file mode 100644 index 0000000..5d19b77 --- /dev/null +++ b/.github/workflows/buildService.yml @@ -0,0 +1,16 @@ +name: Build Service + +on: + workflow_dispatch: + pull_request: + paths-ignore: ['*.md'] + branches: ['main', 'master', 'update/040'] + push: + paths-ignore: ['*.md'] + branches: ['main', 'master', 'update/040'] + +jobs: + build: + uses: start9labs/shared-workflows/.github/workflows/buildService.yml@master + secrets: + DEV_KEY: ${{ secrets.DEV_KEY }} diff --git a/.github/workflows/releaseService.yml b/.github/workflows/releaseService.yml new file mode 100644 index 0000000..4764532 --- /dev/null +++ b/.github/workflows/releaseService.yml @@ -0,0 +1,16 @@ +name: Release Service + +on: + push: + tags: + - 'v*.*' + +jobs: + release: + uses: start9labs/shared-workflows/.github/workflows/releaseService.yml@master + with: + REGISTRY: ${{ vars.REGISTRY }} # Optional. Defaults to https://alpha-registry-x.start9.com + secrets: + DEV_KEY: ${{ secrets.DEV_KEY }} # Required + permissions: + contents: write From 34891bcc25e480eb4cb4e107a61b2dec580766ed Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Fri, 26 Dec 2025 11:32:51 -0700 Subject: [PATCH 14/35] beta ready --- .gitmodules | 3 - Dockerfile | 62 ------ old/.github/Dockerfile.simplex | 61 ------ .../workflows/build-simplex-server.yml | 78 -------- old/.github/workflows/buildService.yml | 37 ---- old/.github/workflows/releaseService.yml | 72 ------- old/assets/compat/backup.sh | 9 - old/assets/compat/restore.sh | 7 - old/check-syn-ack.sh | 17 -- old/docker_entrypoint.sh | 124 ------------ old/manifest.yaml | 131 ------------ old/scripts/bundle.ts | 6 - old/scripts/deps.ts | 2 - old/scripts/embassy.ts | 5 - old/scripts/procedures/getConfig.ts | 24 --- old/scripts/procedures/main.ts | 7 - old/scripts/procedures/migrations.ts | 28 --- old/scripts/procedures/properties.ts | 3 - old/scripts/procedures/setConfig.ts | 9 - package-lock.json | 39 +++- package.json | 3 +- simplexmq | 1 - startos/backups.ts | 9 +- startos/fileModels/fileServer.ini.ts | 84 ++++++++ startos/fileModels/ini-lib.ts | 157 +++++++++++++++ startos/fileModels/smp.ini.ts | 146 -------------- startos/fileModels/smpServer.ini.ts | 146 ++++++++++++++ startos/fileModels/xftp.ini.ts | 13 -- startos/init/index.ts | 2 + startos/init/initServers.ts | 64 ++++++ startos/install/versionGraph.ts | 19 +- startos/install/versions/index.ts | 2 +- startos/install/versions/v6.3.2.1.ts | 15 -- startos/install/versions/v6.4.5.0.a0.ts | 121 +++++++++++ startos/interfaces.ts | 92 ++++++--- startos/main.ts | 49 +++-- startos/manifest.ts | 35 +++- startos/utils.ts | 189 ++++++++++-------- 38 files changed, 838 insertions(+), 1033 deletions(-) delete mode 100644 .gitmodules delete mode 100644 Dockerfile delete mode 100644 old/.github/Dockerfile.simplex delete mode 100644 old/.github/workflows/build-simplex-server.yml delete mode 100644 old/.github/workflows/buildService.yml delete mode 100644 old/.github/workflows/releaseService.yml delete mode 100755 old/assets/compat/backup.sh delete mode 100755 old/assets/compat/restore.sh delete mode 100755 old/check-syn-ack.sh delete mode 100755 old/docker_entrypoint.sh delete mode 100644 old/manifest.yaml delete mode 100644 old/scripts/bundle.ts delete mode 100644 old/scripts/deps.ts delete mode 100644 old/scripts/embassy.ts delete mode 100644 old/scripts/procedures/getConfig.ts delete mode 100644 old/scripts/procedures/main.ts delete mode 100644 old/scripts/procedures/migrations.ts delete mode 100644 old/scripts/procedures/properties.ts delete mode 100644 old/scripts/procedures/setConfig.ts delete mode 160000 simplexmq create mode 100644 startos/fileModels/fileServer.ini.ts create mode 100644 startos/fileModels/ini-lib.ts delete mode 100644 startos/fileModels/smp.ini.ts create mode 100644 startos/fileModels/smpServer.ini.ts delete mode 100644 startos/fileModels/xftp.ini.ts create mode 100644 startos/init/initServers.ts delete mode 100644 startos/install/versions/v6.3.2.1.ts create mode 100644 startos/install/versions/v6.4.5.0.a0.ts diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 0b0940c..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "simplexmq"] - path = simplexmq - url = https://github.com/simplex-chat/simplexmq.git diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index bf71a4e..0000000 --- a/Dockerfile +++ /dev/null @@ -1,62 +0,0 @@ -ARG TAG=22.04 - -FROM ubuntu:${TAG} AS build - -### Build stage - -# Install curl and git and simplexmq dependencies -RUN apt-get update && apt-get install -y curl git build-essential libgmp3-dev zlib1g-dev llvm-12 llvm-12-dev libnuma-dev libssl-dev - -# Specify bootstrap Haskell versions -ENV BOOTSTRAP_HASKELL_GHC_VERSION=9.6.3 -ENV BOOTSTRAP_HASKELL_CABAL_VERSION=3.10.1.0 - -# Install ghcup -RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh - -# Adjust PATH -ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH" - -# Set both as default -RUN ghcup set ghc "${BOOTSTRAP_HASKELL_GHC_VERSION}" && \ - ghcup set cabal "${BOOTSTRAP_HASKELL_CABAL_VERSION}" - -COPY ./simplexmq /project -WORKDIR /project - -# Compile apps -RUN cabal update -RUN cabal build exe:smp-server -RUN cabal build exe:xftp-server - -# Create new path containing all files needed -RUN mkdir /final -WORKDIR /final - -# Strip the binaries from debug symbols to reduce size -RUN for app in smp-server xftp-server; do \ - bin=$(find /project/dist-newstyle -name "$app" -type f -executable) && \ - mv "$bin" ./ && \ - strip ./"$app" &&\ - mv /project/scripts/docker/entrypoint-"$app" ./entrypoint; \ -done - -### Final stage -FROM ubuntu:${TAG} - -# Install OpenSSL dependency -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssl libnuma-dev tini netcat \ - && rm -rf /var/lib/apt/lists/* - -# Copy compiled apps from build stage -COPY --from=build /final /usr/local/bin/ - -# simplexmq requires using SIGINT to correctly preserve undelivered messages and restore them on restart -STOPSIGNAL SIGINT - -# ADD ./check-syn-ack.sh /usr/local/bin/check-syn-ack.sh -# RUN chmod a+x /usr/local/bin/check-syn-ack.sh - -# ADD ./docker_entrypoint.sh /usr/local/bin/docker_entrypoint.sh -# RUN chmod a+x /usr/local/bin/docker_entrypoint.sh diff --git a/old/.github/Dockerfile.simplex b/old/.github/Dockerfile.simplex deleted file mode 100644 index db08ac5..0000000 --- a/old/.github/Dockerfile.simplex +++ /dev/null @@ -1,61 +0,0 @@ -ARG TAG=22.04 - -FROM ubuntu:${TAG} AS build - -### Build stage - -# Install curl and git and simplexmq dependencies -RUN apt-get update && apt-get install -y curl git build-essential libgmp3-dev zlib1g-dev llvm-12 llvm-12-dev libnuma-dev libssl-dev - -# Specify bootstrap Haskell versions -ENV BOOTSTRAP_HASKELL_GHC_VERSION=9.6.3 -ENV BOOTSTRAP_HASKELL_CABAL_VERSION=3.10.1.0 - -# Install ghcup -RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh - -# Adjust PATH -ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH" - -# Set both as default -RUN ghcup set ghc "${BOOTSTRAP_HASKELL_GHC_VERSION}" && \ - ghcup set cabal "${BOOTSTRAP_HASKELL_CABAL_VERSION}" - -COPY ./simplex-chat /project -WORKDIR /project - -# Compile apps -RUN cabal update -RUN cabal build exe:smp-server -RUN cabal build exe:xftp-server - -# Create new path containing all files needed -RUN mkdir /final -WORKDIR /final - -# Strip the binaries from debug symbols to reduce size -RUN for app in smp-server xftp-server; do \ - bin=$(find / -name "$app" -type f -executable 2>/dev/null) && \ - if [ -n "$bin" ]; then \ - mv "$bin" ./ && \ - strip ./"$app" && \ - mv /project/scripts/docker/entrypoint-"$app" ./entrypoint; \ - else \ - echo "Binary $app not found"; \ - exit 1; \ - fi; \ -done - -### Final stage -FROM ubuntu:${TAG} - -# Install OpenSSL dependency -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openssl libnuma-dev netcat \ - && rm -rf /var/lib/apt/lists/* - -# Copy compiled apps from build stage -COPY --from=build /final /usr/local/bin/ - -# simplexmq requires using SIGINT to correctly preserve undelivered messages and restore them on restart -STOPSIGNAL SIGINT diff --git a/old/.github/workflows/build-simplex-server.yml b/old/.github/workflows/build-simplex-server.yml deleted file mode 100644 index f2a2241..0000000 --- a/old/.github/workflows/build-simplex-server.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Build and Publish SimpleX Docker Image - -on: - workflow_dispatch: - inputs: - simplex_version: - description: 'SimpleX Server version to build (e.g. v6.0.4 or v6.1.0-beta.1). Use "latest" to build the most recent version' - required: true - default: 'latest' - -jobs: - build: - runs-on: ubuntu-latest - - permissions: - contents: read - packages: write - id-token: write - - env: - REPO_URL: https://github.com/simplex-chat/simplex-chat.git - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: all - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - install: true - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Fetch latest tags from SimpleX repository - run: | - git clone --depth=1 --tags $REPO_URL - cd simplex-chat - # Find the latest tag, filtering out any non-version tags like 'v*' - LATEST_TAG=$(git tag -l --sort=-v:refname 'v*' | head -n 1) - echo "Latest tag found: $LATEST_TAG" - echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_ENV - - - name: Determine version to build - run: | - if [ "${{ github.event.inputs.simplex_version }}" == "latest" ]; then - echo "Using latest tag: $LATEST_TAG" - echo "SIMPLEX_VERSION=$LATEST_TAG" >> $GITHUB_ENV - else - echo "Using specified version: ${{ github.event.inputs.simplex_version }}" - echo "SIMPLEX_VERSION=${{ github.event.inputs.simplex_version }}" >> $GITHUB_ENV - fi - - - name: Build and push Docker image for x86 and arm - run: | - echo "Building SimpleX version: $SIMPLEX_VERSION" - - REPO_NAME=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') - - docker buildx build \ - --platform linux/amd64,linux/arm64 \ - --file .github/Dockerfile.simplex \ - --tag ghcr.io/${REPO_NAME}:$SIMPLEX_VERSION \ - --build-arg SIMPLEX_VERSION=$SIMPLEX_VERSION \ - --push \ - . - - - name: Image build complete - run: echo "Docker image has been built and published." diff --git a/old/.github/workflows/buildService.yml b/old/.github/workflows/buildService.yml deleted file mode 100644 index ab6e729..0000000 --- a/old/.github/workflows/buildService.yml +++ /dev/null @@ -1,37 +0,0 @@ -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@v4 - - - name: Build the service package - id: build - run: | - git submodule update --init --recursive - start-sdk init - make - PACKAGE_ID=$(yq -oy ".id" manifest.*) - echo "package_id=$PACKAGE_ID" >> $GITHUB_ENV - printf "\n SHA256SUM: $(sha256sum ${PACKAGE_ID}.s9pk) \n" - shell: bash - - - name: Upload .s9pk - uses: actions/upload-artifact@v4 - with: - name: ${{ env.package_id }}.s9pk - path: ./${{ env.package_id }}.s9pk diff --git a/old/.github/workflows/releaseService.yml b/old/.github/workflows/releaseService.yml deleted file mode 100644 index 6cf91f2..0000000 --- a/old/.github/workflows/releaseService.yml +++ /dev/null @@ -1,72 +0,0 @@ -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@v4 - - - 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 "package_id=$(yq -oy ".id" manifest.*)" >> $GITHUB_ENV - echo "package_title=$(yq -oy ".title" manifest.*)" >> $GITHUB_ENV - shell: bash - - - name: Generate sha256 checksum - run: | - PACKAGE_ID=${{ env.package_id }} - printf "\n SHA256SUM: $(sha256sum ${PACKAGE_ID}.s9pk) \n" - sha256sum ${PACKAGE_ID}.s9pk > ${PACKAGE_ID}.s9pk.sha256 - shell: bash - - - name: Generate changelog - run: | - PACKAGE_ID=${{ env.package_id }} - echo "## What's Changed" > change-log.txt - yq -oy '.release-notes' manifest.* >> 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@v2 - with: - tag_name: ${{ github.ref_name }} - name: ${{ env.package_title }} ${{ github.ref_name }} - prerelease: true - body_path: change-log.txt - files: | - ./${{ env.package_id }}.s9pk - ./${{ env.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 ${{ env.package_id }}.s9pk - fi diff --git a/old/assets/compat/backup.sh b/old/assets/compat/backup.sh deleted file mode 100755 index 8b3436f..0000000 --- a/old/assets/compat/backup.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -set -e - -mkdir -p /mnt/backup/etc -mkdir -p /mnt/backup/log -compat duplicity create /mnt/backup/etc /etc/opt/simplex -compat duplicity create /mnt/backup/etc /etc/opt/simplex-xftp -compat duplicity create /mnt/backup/log /var/opt/simplex \ No newline at end of file diff --git a/old/assets/compat/restore.sh b/old/assets/compat/restore.sh deleted file mode 100755 index e9fdcef..0000000 --- a/old/assets/compat/restore.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -set -e - -compat duplicity restore /mnt/backup/etc /etc/opt/simplex -compat duplicity restore /mnt/backup/etc /etc/opt/simplex-xftp -compat duplicity restore /mnt/backup/log /var/opt/simplex \ No newline at end of file diff --git a/old/check-syn-ack.sh b/old/check-syn-ack.sh deleted file mode 100755 index 90f28b5..0000000 --- a/old/check-syn-ack.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -read DURATION -if [ "$DURATION" -le 10000 ]; then - exit 60 -else - output=$(nc -zv 127.0.0.1 5223 2>&1) - - expected_output="Connection to 127.0.0.1 5223 port [tcp/*] succeeded!" - - if [ "$output" == "$expected_output" ]; then - exit 0 - else - echo "The smp-server process does not seem to be responding to connection requests" >&2 - exit 1 - fi -fi \ No newline at end of file diff --git a/old/docker_entrypoint.sh b/old/docker_entrypoint.sh deleted file mode 100755 index 2b2e367..0000000 --- a/old/docker_entrypoint.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env sh - -set -e -printf "\n\n [i] Starting SimpleX ...\n\n" -confd="/etc/opt/simplex" -xftp="/etc/opt/simplex-xftp" -logd="/var/opt/simplex" -# Check if smp-server has been initialized -if [ ! -f "$confd/smp-server.ini" ]; then - # Set a 15 digit server password. See the comments in smp-server.ini for a description of what this does - export PASS=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15) - # Init certificates and configs - smp-server init -y -l --password $PASS -else - export PASS=$(grep -i "^create_password" $confd/smp-server.ini | awk -F ':' '{print $2}' | awk '{$1=$1};1') -fi - -# Function to add or update a section in the smp-server.ini config file -add_or_update_section() { - local file="$1" - local section="$2" - local content="$3" - - if grep -q "^\[$section\]" "$file"; then - # Section exists, update it - awk -v section="$section" -v content="$content" ' - BEGIN { in_section = 0; printed = 0 } - /^\[/ { if (in_section && !printed) { print content; printed = 1 }; in_section = 0 } - /^\['"$section"'\]/ { in_section = 1; print; print content; printed = 1; next } - { if (!in_section || !printed) print } - END { if (in_section && !printed) print content } - ' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file" - echo "$section section updated in $file" - else - # Section doesn't exist, add it - echo "\n[$section]\n$content" >> "$file" - echo "$section section added to $file" - fi -} -# Add or update INFORMATION section -add_or_update_section "$confd/smp-server.ini" "INFORMATION" "source_code: https://github.com/simplex-chat/simplexmq" - -# Add or update PROXY section -add_or_update_section "$confd/smp-server.ini" "PROXY" "socks_proxy: 172.18.0.1:9050\nsocks_mode: onion" - -# Add or update WEB section -add_or_update_section "$confd/smp-server.ini" "WEB" "static_path: /var/opt/simplex/www" - -# Add or update TRANSPORT section with correct port -add_or_update_section "$confd/smp-server.ini" "TRANSPORT" "host: \nport: 5223\nlog_tls_errors: off\nwebsockets: off" - -# Check if xftp-server has been initialized -if [ ! -f "$xftp/file-server.ini" ]; then - # Init certificates and configs - mkdir -p /root/xftp - xftp-server init -q '10gb' -p /root/xftp/ - - else - echo "All good! - XFTP initialized" -fi - -# Backup store log just in case -# -# Uses the UTC (universal) time zone and this -# format: YYYY-mm-dd'T'HH:MM:SS -# year, month, day, letter T, hour, minute, second -# -# This is the ISO 8601 format without the time zone at the end. -# -_file="${logd}/smp-server-store.log" -if [ -f "${_file}" ]; then - _backup_extension="$(date -u '+%Y-%m-%dT%H:%M:%S')" - cp -v -p "${_file}" "${_file}.${_backup_extension:-date-failed}" - unset -v _backup_extension -fi -unset -v _file - -TOR_ADDRESS=$(sed -n -e 's/^tor-address: \(.*\)/\1/p' /root/start9/config.yaml) -XFTP_ADDRESS=$(sed -n -e 's/^xftp-address: \(.*\)/\1/p' /root/start9/config.yaml) -SMP_FINGERPRINT=$(cat $confd/fingerprint) -XFTP_FINGERPRINT=$(cat $xftp/fingerprint) - -SMP_URL="smp://$SMP_FINGERPRINT:$PASS@$TOR_ADDRESS" -XFTP_URL="xftp://$XFTP_FINGERPRINT@$XFTP_ADDRESS" -mkdir -p /root/start9 - -cat << EOF > /root/start9/stats.yaml ---- -version: 2 -data: - SimpleX SMP Server Address: - type: string - value: $SMP_URL - description: Your SMP Server address, used in client applications. - copyable: true - qr: true - masked: true - SimpleX XFTP Server Address: - type: string - value: $XFTP_URL - description: Your XFTP Server address, used in client applications. - copyable: true - qr: true - masked: true -EOF - -# Run smp-server and xftp-server as background processes -# Set up a trap to catch INT signal for graceful shutdown - -_term() { - echo "Caught INT signal!" - kill -INT "$smp_process" 2>/dev/null - kill -INT "$xftp_process" 2>/dev/null -} - -smp-server start +RTS -N -RTS & -smp_process=$! - -xftp-server start +RTS -N -RTS & -xftp_process=$! - -trap _term INT - -wait $xftp_process $smp_process diff --git a/old/manifest.yaml b/old/manifest.yaml deleted file mode 100644 index d87ae9d..0000000 --- a/old/manifest.yaml +++ /dev/null @@ -1,131 +0,0 @@ -id: simplex -title: "SimpleX Server" -version: 6.2.0.7 -release-notes: | - - Updated to the latest upstream code with notable changes: - * journal storage for messages (BETA). - * prevent race condition when deleting queue and to avoid "orphan" messages. - * Full change log available [here](https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md#620) -license: MIT -wrapper-repo: "https://github.com/Start9Labs/simplex-startos" -upstream-repo: "https://github.com/simplex-chat/simplexmq/" -support-site: "https://github.com/simplex-chat/simplexmq/issues" -marketing-site: "https://simplex.chat/" -donation-url: "https://github.com/simplex-chat/simplex-chat#help-us-with-donations" -build: ["make"] -description: - short: An instant messenger without user IDs - long: | - SimpleX is a chat client that radically improves your privacy. It puts you in control of how people connect with you. -assets: - license: LICENSE - icon: icon.png - instructions: instructions.md -main: - type: docker - image: main - entrypoint: "docker_entrypoint.sh" - args: [] - mounts: - main: /root/ - conf: /etc/opt/simplex - xftp: /etc/opt/simplex-xftp - log: /var/opt/simplex -hardware-requirements: - arch: - - x86_64 - - aarch64 -health-checks: - api: - name: Listening - success-message: SimpleX Server is alive and accepting incoming connections - type: docker - image: main - system: false - entrypoint: check-syn-ack.sh - args: [] - mounts: {} - io-format: json - inject: true -config: - get: - type: script - set: - type: script -properties: - type: script -volumes: - main: - type: data - compat: - type: assets - conf: - type: data - xftp: - type: data - log: - type: data -interfaces: - main: - name: SimpleX SMP Interface - description: Do not use the URL below. Instead, look in the Properties section to get your SimpleX server address. - tor-config: - port-mapping: - 5223: "5223" - ui: false - protocols: - - tcp - xftp: - name: SimpleX XFTP Interface - description: Do not use the URL below. Instead, look in the Properties section to get your SimpleX server address. - tor-config: - port-mapping: - 443: "443" - ui: false - protocols: - - tcp -dependencies: {} -backup: - create: - type: docker - image: compat - system: true - entrypoint: /assets/backup.sh - args: - - duplicity - - create - - /mnt/backup - - /root/start9 - mounts: - BACKUP: /mnt/backup - compat: /assets - main: /root/start9 - conf: /etc/opt/simplex - xftp: /etc/opt/simplex-xftp - log: /var/opt/simplex - restore: - type: docker - image: compat - system: true - entrypoint: /assets/restore.sh - args: - - duplicity - - restore - - /mnt/backup - - /root/start9 - mounts: - BACKUP: /mnt/backup - compat: /assets - main: /root/start9 - conf: /etc/opt/simplex - xftp: /etc/opt/simplex-xftp - log: /var/opt/simplex -migrations: - from: - "*": - type: script - args: ["from"] - to: - "*": - type: script - args: ["to"] diff --git a/old/scripts/bundle.ts b/old/scripts/bundle.ts deleted file mode 100644 index 07cbf3a..0000000 --- a/old/scripts/bundle.ts +++ /dev/null @@ -1,6 +0,0 @@ -// scripts/bundle.ts -import { bundle } from "https://deno.land/x/emit@0.40.0/mod.ts"; - -const result = await bundle("scripts/embassy.ts"); - -await Deno.writeTextFile("scripts/embassy.js", result.code); diff --git a/old/scripts/deps.ts b/old/scripts/deps.ts deleted file mode 100644 index 6519c00..0000000 --- a/old/scripts/deps.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "https://deno.land/x/embassyd_sdk@v0.3.3.0.5/mod.ts"; -export * from "https://deno.land/x/embassyd_sdk@v0.3.3.0.5/util.ts"; \ No newline at end of file diff --git a/old/scripts/embassy.ts b/old/scripts/embassy.ts deleted file mode 100644 index 74f56eb..0000000 --- a/old/scripts/embassy.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { setConfig } from "./procedures/setConfig.ts"; -export { getConfig } from "./procedures/getConfig.ts"; -export { properties } from "./procedures/properties.ts"; -export { migration } from "./procedures/migrations.ts"; -export { main } from "./procedures/main.ts"; \ No newline at end of file diff --git a/old/scripts/procedures/getConfig.ts b/old/scripts/procedures/getConfig.ts deleted file mode 100644 index 2e4503c..0000000 --- a/old/scripts/procedures/getConfig.ts +++ /dev/null @@ -1,24 +0,0 @@ -// To utilize the default config system built, this file is required. It defines the *structure* of the configuration file. These structured options display as changeable UI elements within the "Config" section of the service details page in the Embassy UI. - -import { compat, types as T } from "../deps.ts"; - -export const getConfig: T.ExpectedExports.getConfig = compat.getConfig({ - "tor-address": { - "name": "SMP Tor Address", - "description": "The Tor address for SMP Server", - "type": "pointer", - "subtype": "package", - "package-id": "simplex", - "target": "tor-address", - "interface": "main" - }, - "xftp-address": { - "name": "XFTP Tor Address", - "description": "The Tor address for XFTP Server", - "type": "pointer", - "subtype": "package", - "package-id": "simplex", - "target": "tor-address", - "interface": "xftp" - } - }); diff --git a/old/scripts/procedures/main.ts b/old/scripts/procedures/main.ts deleted file mode 100644 index a426122..0000000 --- a/old/scripts/procedures/main.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { types as T, util } from "../deps.ts"; - -export const main = async (effects: T.Effects) => { - // args defaulted to [] - not necessary to include if empty - await effects.runDaemon({ command: "docker_entrypoint.sh", args: [] }).wait(); - return util.ok; -} \ No newline at end of file diff --git a/old/scripts/procedures/migrations.ts b/old/scripts/procedures/migrations.ts deleted file mode 100644 index 55a55cf..0000000 --- a/old/scripts/procedures/migrations.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { compat, types as T } from "../deps.ts"; - -export const migration: T.ExpectedExports.migration = - compat.migrations.fromMapping( - { - "5.8.2": { - up: compat.migrations.updateConfig( - (config: any) => { - config["xftp-address"]; - - return config; - }, - true, - { version: "5.8.2", type: "up" } - ), - down: compat.migrations.updateConfig( - (config: any) => { - delete config["xftp-address"]; - - return config; - }, - true, - { version: "5.8.2", type: "down" } - ), - }, - }, - "6.2.0.7" - ); diff --git a/old/scripts/procedures/properties.ts b/old/scripts/procedures/properties.ts deleted file mode 100644 index dff99aa..0000000 --- a/old/scripts/procedures/properties.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { compat, types as T } from "../deps.ts"; - -export const properties: T.ExpectedExports.properties = compat.properties; diff --git a/old/scripts/procedures/setConfig.ts b/old/scripts/procedures/setConfig.ts deleted file mode 100644 index 2cea41b..0000000 --- a/old/scripts/procedures/setConfig.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { compat, types as T } from "../deps.ts"; - -// deno-lint-ignore require-await -export const setConfig: T.ExpectedExports.setConfig = async ( - effects: T.Effects, - newConfig: T.Config, -) => { - return compat.setConfig(effects, newConfig); -}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0443869..4e9ba96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,8 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.45" + "@start9labs/start-sdk": "^0.4.0-beta.45", + "tldts": "^7.0.19" }, "devDependencies": { "@types/node": "^22.18.6", @@ -66,6 +67,15 @@ "yaml": "^2.7.1" } }, + "node_modules/@start9labs/start-sdk/node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/@types/ini": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/@types/ini/-/ini-4.1.1.tgz", @@ -101,15 +111,6 @@ "object-hash": "^3.0.0" } }, - "node_modules/ini": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", - "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/isomorphic-fetch": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", @@ -180,6 +181,24 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.19" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", + "license": "MIT" + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", diff --git a/package.json b/package.json index c8a3414..bb81a25 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.45" + "@start9labs/start-sdk": "^0.4.0-beta.45", + "tldts": "^7.0.19" }, "devDependencies": { "@types/node": "^22.18.6", diff --git a/simplexmq b/simplexmq deleted file mode 160000 index 79e9447..0000000 --- a/simplexmq +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 79e9447b73cc315ce35042b0a5f210c07ea39b07 diff --git a/startos/backups.ts b/startos/backups.ts index 0a90b1e..729c896 100644 --- a/startos/backups.ts +++ b/startos/backups.ts @@ -1,5 +1,12 @@ import { sdk } from './sdk' export const { createBackup, restoreInit } = sdk.setupBackups( - async ({ effects }) => sdk.Backups.ofVolumes('main'), + async ({ effects }) => + sdk.Backups.ofVolumes( + 'smp-configs', + 'smp-state', + 'xftp-configs', + 'xftp-state', + 'xftp-files', + ), ) diff --git a/startos/fileModels/fileServer.ini.ts b/startos/fileModels/fileServer.ini.ts new file mode 100644 index 0000000..c940d3d --- /dev/null +++ b/startos/fileModels/fileServer.ini.ts @@ -0,0 +1,84 @@ +import { matches, FileHelper } from '@start9labs/start-sdk' +import { xftpConfigDefaults } from '../utils' + +const { object, literals, natural, string, literal } = matches + +const { STORE_LOG, AUTH, TRANSPORT, FILES, INACTIVE_CLIENTS } = + xftpConfigDefaults + +const shape = object({ + STORE_LOG: object({ + /** + The server uses STM memory for persistence, + that will be lost on restart (e.g., as with redis). + This option enables saving memory to append only log, + and restoring it when the server is started. + Log is compacted on start (deleted objects are removed). + */ + enable: literals('on', 'off').onMismatch(STORE_LOG.enable), + /** + Expire files after the specified number of hours. + */ + expire_files_hours: natural + .optional() + .onMismatch(STORE_LOG.expire_files_hours), + /** + Enable xftp-server statistics for Grafana dashboard + */ + log_stats: literals('on', 'off').onMismatch(STORE_LOG.log_stats), + /** + Log interval for real-time Prometheus metrics, in seconds + */ + prometheus_interval: natural + .optional() + .onMismatch(STORE_LOG.prometheus_interval), + }), + + AUTH: object({ + /** + Set new_files option to off to completely prohibit uploading new files. + This can be useful when you want to decommission the server, but still allow downloading the existing files. + */ + new_files: literals('on', 'off').onMismatch(AUTH.new_files), + /** + Use create_password option to enable basic auth to upload new files. + The password should be used as part of server address in client configuration: + xftp://fingerprint:password@host1,host2 + The password will not be shared with file recipients, you must share it only + with the users who you want to allow uploading files to your server. + Password to upload files (any printable ASCII characters without whitespace, '@', ':' and '/') + */ + create_password: string, + control_port_admin_password: string.optional().onMismatch(AUTH.new_files), + control_port_user_password: string.optional().onMismatch(AUTH.new_files), + }), + TRANSPORT: object({ + /** + host is only used to print server address on start + */ + host: string, + port: literal(TRANSPORT.port).onMismatch(TRANSPORT.port), + log_tls_errors: literals('on', 'off').onMismatch(TRANSPORT.log_tls_errors), + control_port: natural.optional().onMismatch(TRANSPORT.control_port), + }), + FILES: object({ + path: literal(FILES.path), + storage_quota: string.onMismatch(FILES.storage_quota), + }), + INACTIVE_CLIENTS: object({ + /** + Whether or not to disconnect inactive clients. If set, ttl and check_interval should also be set (seconds) + */ + disconnect: literals(INACTIVE_CLIENTS.disconnect).onMismatch( + INACTIVE_CLIENTS.disconnect, + ), + }), +}) + +export const fileServerIni = FileHelper.ini( + { + volumeId: 'xftp-configs', + subpath: './file-server.ini', + }, + shape, +) diff --git a/startos/fileModels/ini-lib.ts b/startos/fileModels/ini-lib.ts new file mode 100644 index 0000000..14ef897 --- /dev/null +++ b/startos/fileModels/ini-lib.ts @@ -0,0 +1,157 @@ +export type IniValue = string | number | boolean | string[] +export type IniInputValue = IniValue | null | undefined +export type IniSection = Record +export type IniInputSection = Record +export type IniData = Record +export type IniInputData = Record + +export interface ParseOptions { + /** Delimiter for list values (default: ",") */ + listDelimiter?: string + /** If true, keep all values as strings without type conversion */ + rawStrings?: boolean +} + +export interface StringifyOptions { + /** Delimiter for list values (default: ",") */ + listDelimiter?: string +} + +/** + * Parse a string value into its typed equivalent + */ +function parseValue(value: string, options: ParseOptions): IniValue { + if (options.rawStrings) { + return value + } + + const trimmed = value.trim() + + // Boolean + if (trimmed.toLowerCase() === 'true') return true + if (trimmed.toLowerCase() === 'false') return false + + // Number (integers and floats, including negatives) + if (/^-?\d+(\.\d+)?$/.test(trimmed)) { + const num = parseFloat(trimmed) + if (!isNaN(num)) return num + } + + // List (contains delimiter) + const delimiter = options.listDelimiter ?? ',' + if (trimmed.includes(delimiter)) { + return trimmed.split(delimiter).map((item) => item.trim()) + } + + // Default: string + return trimmed +} + +/** + * Convert a typed value back to string + */ +function stringifyValue(value: IniValue, options: StringifyOptions): string { + if (Array.isArray(value)) { + const delimiter = options.listDelimiter ?? ',' + return value.join(delimiter) + } + return String(value) +} + +/** + * Check if a value is a section (nested object) vs a primitive value + */ +function isSection( + value: IniInputValue | IniInputSection, +): value is IniInputSection { + return typeof value === 'object' && value !== null && !Array.isArray(value) +} + +/** + * Parse an INI string that uses `: ` as the key/value separator + */ +export function parse(content: string, options: ParseOptions = {}): IniData { + const result: IniData = {} + let currentSection: string | null = null + + const lines = content.split(/\r?\n/) + + for (const line of lines) { + const trimmed = line.trim() + + // Skip empty lines and comments + if (!trimmed || trimmed.startsWith(';') || trimmed.startsWith('#')) { + continue + } + + // Check for section header + const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/) + if (sectionMatch) { + currentSection = sectionMatch[1] + result[currentSection] = {} + continue + } + + // Parse key: value pairs + const separatorIndex = trimmed.indexOf(': ') + if (separatorIndex !== -1) { + const key = trimmed.slice(0, separatorIndex).trim() + const rawValue = trimmed.slice(separatorIndex + 2) + const value = parseValue(rawValue, options) + + if (currentSection) { + ;(result[currentSection] as IniSection)[key] = value + } else { + result[key] = value + } + } + } + + return result +} + +/** + * Stringify an object to INI format using `: ` as the key/value separator + * Note: null and undefined values are omitted (INI has no native null concept) + */ +export function stringify( + data: IniInputData, + options: StringifyOptions = {}, +): string { + const lines: string[] = [] + + // First, output top-level key-value pairs (non-sections) + for (const [key, value] of Object.entries(data)) { + if (value == null) continue // filters both null and undefined + if (!isSection(value)) { + lines.push(`${key}: ${stringifyValue(value, options)}`) + } + } + + // Then, output sections + for (const [key, value] of Object.entries(data)) { + if (value == null) continue + if (isSection(value)) { + const sectionLines: string[] = [] + for (const [subKey, subValue] of Object.entries(value)) { + if (subValue == null) continue + sectionLines.push(`${subKey}: ${stringifyValue(subValue, options)}`) + } + // Only add section if it has values + if (sectionLines.length > 0) { + if (lines.length > 0) { + lines.push('') // Blank line before section + } + lines.push(`[${key}]`) + lines.push(...sectionLines) + } + } + } + + return lines.join('\n') +} + +/** + * Type guard to check if a value is a section + */ +export { isSection } diff --git a/startos/fileModels/smp.ini.ts b/startos/fileModels/smp.ini.ts deleted file mode 100644 index 745b8ec..0000000 --- a/startos/fileModels/smp.ini.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { matches, FileHelper } from '@start9labs/start-sdk' -import { smpConfigDefaults } from '../utils' - -const { object, string, oneOf, literal, natural, arrayOf } = matches - -const { - information, - store_log, - auth, - transport, - proxy, - inactive_clients, - web, -} = smpConfigDefaults - -const shape = object({ - information: object({ - // Basic info - LICENSE: string.optional().onMismatch(information.LICENSE), - source_code: literal(information.source_code).onMismatch( - information.source_code, - ), - - // Server usage conditions and amendments. - usage_conditions: string - .optional() - .onMismatch(information.usage_conditions), - condition_amendments: string - .optional() - .onMismatch(information.condition_amendments), - - // Server location and operator - server_country: string.optional().onMismatch(information.server_country), - operator: string.optional().onMismatch(information.operator), - operator_country: string - .optional() - .onMismatch(information.operator_country), - website: string.optional().onMismatch(information.website), - - // Administrative contacts - admin_simplex: string.optional().onMismatch(information.admin_simplex), // SimpleX address - admin_email: string.optional().onMismatch(information.admin_email), - admin_pgp: string.optional().onMismatch(information.admin_pgp), - admin_pgp_fingerprint: string - .optional() - .onMismatch(information.admin_pgp_fingerprint), - - // Contacts for complaints and feedback. - complaints_simplex: string - .optional() - .onMismatch(information.complaints_simplex), // SimpleX address - complaints_email: string - .optional() - .onMismatch(information.complaints_email), - complaints_pgp: string.optional().onMismatch(information.complaints_pgp), - complaints_pgp_fingerprint: string - .optional() - .onMismatch(information.complaints_pgp_fingerprint), - - // Hosting provider - hosting: string.optional().onMismatch(information.hosting), // entity (organization or person name) - hosting_country: string.optional().onMismatch(information.hosting_country), // ISO-3166 2-letter code - }), - store_log: object({ - enable: oneOf(literal('on'), literal('off')).onMismatch(store_log.enable), - - restore_messages: oneOf(literal('on'), literal('off')).onMismatch( - store_log.restore_messages, - ), - expire_messages_days: natural.onMismatch(store_log.expire_messages_days), - expire_ntfs_hours: natural.onMismatch(store_log.expire_ntfs_hours), - - log_stats: oneOf(literal('on'), literal('off')).onMismatch( - store_log.log_stats, - ), - }), - auth: object({ - new_queues: oneOf(literal('on'), literal('off')).onMismatch( - auth.new_queues, - ), - - create_password: string.onMismatch(auth.create_password), - - control_port_admin_password: string.onMismatch( - auth.control_port_admin_password, - ), - control_port_user_password: string.onMismatch( - auth.control_port_user_password, - ), - }), - transport: object({ - host: literal(transport.host).onMismatch(transport.host), - port: literal(transport.port).onMismatch(transport.port), - log_tls_errors: oneOf(literal('on'), literal('off')).onMismatch( - transport.log_tls_errors, - ), - - websockets: oneOf(literal('on'), literal('off')).onMismatch( - transport.websockets, - ), - control_port: literal(transport.control_port).onMismatch( - transport.control_port, - ), - }), - proxy: object({ - // Network configuration for SMP proxy client. - host_mode: oneOf(literal('public'), literal('onion')).onMismatch( - proxy.host_mode, - ), - required_host_mode: oneOf(literal('on'), literal('off')).onMismatch( - proxy.required_host_mode, - ), - - own_server_domains: arrayOf(string).onMismatch(proxy.own_server_domains), - - socks_proxy: string.onMismatch(proxy.socks_proxy), - socks_mode: oneOf(literal('onion'), literal('always')).onMismatch( - proxy.socks_mode, - ), - - client_concurrency: natural.onMismatch(proxy.client_concurrency), - }), - inactive_clients: object({ - disconnect: oneOf(literal('on'), literal('off')).onMismatch( - inactive_clients.disconnect, - ), - ttl: natural.onMismatch(inactive_clients.ttl), - check_interval: natural.onMismatch(inactive_clients.check_interval), - }), - web: object({ - static_path: literal(web.static_path), - http: literal(web.http).onMismatch(web.http), - - https: literal(web.https).onMismatch(web.https), - cert: literal(web.cert).onMismatch(web.cert), - key: literal(web.key).onMismatch(web.key), - }), -}) - -export const smpConfigFile = FileHelper.ini( - { - volumeId: 'main', - subpath: '/etc/opt/simplex/smp-server.ini', - }, - shape, -) diff --git a/startos/fileModels/smpServer.ini.ts b/startos/fileModels/smpServer.ini.ts new file mode 100644 index 0000000..2c77ed9 --- /dev/null +++ b/startos/fileModels/smpServer.ini.ts @@ -0,0 +1,146 @@ +import { matches, FileHelper } from '@start9labs/start-sdk' +import { smpConfigDefaults, smpStatePath } from '../utils' +import * as INI from './ini-lib' + +const { object, string, literal, literals, natural, nill } = matches + +const { + INFORMATION, + STORE_LOG, + AUTH, + TRANSPORT, + PROXY, + INACTIVE_CLIENTS, + WEB, +} = smpConfigDefaults + +const shape = object({ + INFORMATION: object({ + source_code: literal(INFORMATION.source_code).onMismatch( + INFORMATION.source_code, + ), + }), + STORE_LOG: object({ + /** + The server uses STM memory for persistence, + that will be lost on restart (e.g., as with redis). + This option enables saving memory to append only log, + and restoring it when the server is started. + Log is compacted on start (deleted objects are removed). + */ + enable: literals('on', 'off').onMismatch(STORE_LOG.enable), + store_queues: literal(STORE_LOG.store_queues).onMismatch( + STORE_LOG.store_queues, + ), + store_messages: literal(STORE_LOG.store_messages).onMismatch( + STORE_LOG.store_messages, + ), + /** + Undelivered messages are optionally saved and restored when the server restarts, + they are preserved in the .bak file until the next restart. + */ + restore_messages: literals('on', 'off').onMismatch( + STORE_LOG.restore_messages, + ), + expire_messages_days: natural + .optional() + .onMismatch(STORE_LOG.expire_messages_days), + expire_messages_on_start: literal( + STORE_LOG.expire_messages_on_start, + ).onMismatch(STORE_LOG.expire_messages_on_start), + expire_ntfs_hours: natural + .optional() + .onMismatch(STORE_LOG.expire_ntfs_hours), + /** + Log daily server statistics to CSV file + */ + log_stats: literals('on', 'off').onMismatch(STORE_LOG.log_stats), + /** + Log interval for real-time Prometheus metrics, in seconds + */ + prometheus_interval: natural + .optional() + .onMismatch(STORE_LOG.prometheus_interval), + }), + AUTH: object({ + /** + Set new_queues option to off to completely prohibit creating new messaging queues. + This can be useful when you want to decommission the server, but not all connections are switched yet. + */ + new_queues: literal(AUTH.new_queues).onMismatch(AUTH.new_queues), + /** + Use create_password option to enable basic auth to create new messaging queues. + The password should be used as part of server address in client configuration: + smp://fingerprint:password@host1,host2 + The password will not be shared with the connecting contacts, you must share it only + with the users who you want to allow creating messaging queues on your server. + */ + create_password: string, + control_port_admin_password: string + .optional() + .onMismatch(AUTH.control_port_admin_password), + control_port_user_password: string + .optional() + .onMismatch(AUTH.control_port_user_password), + }), + TRANSPORT: object({ + /** + Host is only used to print server address on start. + You can specify multiple server ports. + */ + host: string, + port: literal(TRANSPORT.port).onMismatch(TRANSPORT.port), + log_tls_errors: literal(TRANSPORT.log_tls_errors).onMismatch( + TRANSPORT.log_tls_errors, + ), + websockets: literal(TRANSPORT.websockets).onMismatch(TRANSPORT.websockets), + control_port: literal(TRANSPORT.control_port).onMismatch( + TRANSPORT.control_port, + ), + }), + PROXY: object({ + socks_proxy: literal(PROXY.socks_proxy).onMismatch(PROXY.socks_proxy), + /** + Limit number of threads a client can spawn to process proxy commands in parallel. + */ + client_concurrency: natural.onMismatch(PROXY.client_concurrency), + }), + INACTIVE_CLIENTS: object({ + /** + Whether or not to disconnect inactive clients. If set, ttl and check_interval should also be set (seconds) + */ + disconnect: literal(INACTIVE_CLIENTS.disconnect).onMismatch( + INACTIVE_CLIENTS.disconnect, + ), + }), + WEB: object({ + /** + Set path to generate static mini-site for server information and qr codes/links + */ + static_path: literal(`${smpStatePath}/www`) + .optional() + .onMismatch(WEB.static_path), + /** + Run an embedded server on this port + Onion sites can use any port and register it in the hidden service config. + Running on a port 80 may require setting process capabilities. + */ + http: literal(WEB.http).onMismatch(WEB.http), + /** + Do not permit HTTPS, since that is handled by StartOS reverse proxy + */ + https: nill.onMismatch(undefined), + cert: nill.onMismatch(undefined), + key: nill.onMismatch(undefined), + }), +}) + +export const smpServerIni = FileHelper.raw( + { + volumeId: 'smp-configs', + subpath: './smp-server.ini', + }, + (inData) => INI.stringify(inData), + (inString) => INI.parse(inString), + (data) => shape.unsafeCast(data), +) diff --git a/startos/fileModels/xftp.ini.ts b/startos/fileModels/xftp.ini.ts deleted file mode 100644 index 6102a08..0000000 --- a/startos/fileModels/xftp.ini.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { matches, FileHelper } from '@start9labs/start-sdk' -const { object } = matches - -// @TODO -const shape = object({}) - -export const xftpConfigFile = FileHelper.ini( - { - volumeId: 'main', - subpath: '/etc/opt/simplex/xftp-server.ini', - }, - shape, -) diff --git a/startos/init/index.ts b/startos/init/index.ts index f69244d..8728100 100644 --- a/startos/init/index.ts +++ b/startos/init/index.ts @@ -4,6 +4,7 @@ import { setInterfaces } from '../interfaces' import { versionGraph } from '../install/versionGraph' import { actions } from '../actions' import { restoreInit } from '../backups' +import { initServers } from './initServers' export const init = sdk.setupInit( restoreInit, @@ -11,6 +12,7 @@ export const init = sdk.setupInit( setInterfaces, setDependencies, actions, + initServers, ) export const uninit = sdk.setupUninit(versionGraph) diff --git a/startos/init/initServers.ts b/startos/init/initServers.ts new file mode 100644 index 0000000..fd69f80 --- /dev/null +++ b/startos/init/initServers.ts @@ -0,0 +1,64 @@ +import { utils } from '@start9labs/start-sdk' +import { sdk } from '../sdk' +import { + smpConfigDefaults, + smpMounts, + xftpConfigDefaults, + xftpFilePath, + xftpMounts, +} from '../utils' +import { smpServerIni } from '../fileModels/smpServer.ini' +import { fileServerIni } from '../fileModels/fileServer.ini' + +export const initServers = sdk.setupOnInit(async (effects, kind) => { + if (kind !== 'install') return + + const create_password = utils.getDefaultString({ + charset: 'a-z,A-Z,1-9,!,$,%,&,*', + len: 21, + }) + + await sdk.SubContainer.withTemp( + effects, + { imageId: 'smp' }, + smpMounts, + 'init-smp', + async (sub) => { + await sub.execFail([ + 'smp-server', + 'init', + '-y', + '-l', + '--password', + create_password, + ]) + + await smpServerIni.write(effects, { + ...smpConfigDefaults, + AUTH: { ...smpConfigDefaults.AUTH, create_password }, + }) + }, + ) + + await sdk.SubContainer.withTemp( + effects, + { imageId: 'xftp' }, + xftpMounts, + 'init-xftp', + async (sub) => { + await sub.execFail([ + 'xftp-server', + 'init', + '-p', + xftpFilePath, + '-q', + xftpConfigDefaults.FILES.storage_quota, + ]) + + await fileServerIni.write(effects, { + ...xftpConfigDefaults, + AUTH: { ...xftpConfigDefaults.AUTH, create_password }, + }) + }, + ) +}) diff --git a/startos/install/versionGraph.ts b/startos/install/versionGraph.ts index 3204c2c..1016a54 100644 --- a/startos/install/versionGraph.ts +++ b/startos/install/versionGraph.ts @@ -1,23 +1,8 @@ -import { utils, VersionGraph } from '@start9labs/start-sdk' +import { VersionGraph } from '@start9labs/start-sdk' import { current, other } from './versions' -import { randomPassword, smpConfigDefaults } from '../utils' -import { smpConfigFile } from '../fileModels/smp.ini' -import { sdk } from '../sdk' export const versionGraph = VersionGraph.of({ current, other, - preInstall: async (effects) => { - await smpConfigFile.write(effects, { - ...smpConfigDefaults, - auth: { - ...smpConfigDefaults.auth, - create_password: utils.getDefaultString(randomPassword), - }, - proxy: { - ...smpConfigDefaults.proxy, - socks_proxy: `${await sdk.getOsIp(effects)}:9050`, - }, - }) - }, + preInstall: async (effects) => {}, }) diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index 7c3e78a..11e9b4d 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v_6_3_2_1 as current } from './v6.3.2.1' +export { v_6_4_5_0_a0 as current } from './v6.4.5.0.a0' export const other = [] diff --git a/startos/install/versions/v6.3.2.1.ts b/startos/install/versions/v6.3.2.1.ts deleted file mode 100644 index 80efbd1..0000000 --- a/startos/install/versions/v6.3.2.1.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk' -import { rm } from 'fs/promises' - -export const v_6_3_2_1 = VersionInfo.of({ - version: '6.3.2:1', - releaseNotes: 'Revamped for StartOS 0.4.0', - migrations: { - up: async ({ effects }) => { - await rm('/media/startos/volumes/main/start9', { recursive: true }).catch( - console.error, - ) - }, - down: IMPOSSIBLE, - }, -}) diff --git a/startos/install/versions/v6.4.5.0.a0.ts b/startos/install/versions/v6.4.5.0.a0.ts new file mode 100644 index 0000000..7cffdf5 --- /dev/null +++ b/startos/install/versions/v6.4.5.0.a0.ts @@ -0,0 +1,121 @@ +import { VersionInfo, IMPOSSIBLE, YAML, utils } from '@start9labs/start-sdk' +import { execFile } from 'child_process' +import { readdir, readFile, rm } from 'fs/promises' +import { smpServerIni } from '../../fileModels/smpServer.ini' +import { smpConfigDefaults, xftpConfigDefaults } from '../../utils' +import { fileServerIni } from '../../fileModels/fileServer.ini' +import { join } from 'path' + +// NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. + +export const v_6_4_5_0_a0 = VersionInfo.of({ + version: '6.4.5:0-alpha.0', + releaseNotes: 'Updated for StartOS 0.4.0', + migrations: { + up: async ({ effects }) => { + // get old stats.yaml + const statsYaml: + | { + data: { + 'SimpleX SMP Server Address': { + value: string + } + } + } + | undefined = await readFile( + '/media/startos/volumes/main/start9/stats.yaml', + 'utf-8', + ).then(YAML.parse, () => undefined) + + if (statsYaml) { + // config (was used for smp-server.ini) + await new Promise((res, rej) => { + execFile( + 'sh', + [ + '-c', + 'mv /media/startos/volumes/conf/* /media/startos/volumes/smp-configs', + ], + (err) => (err ? rej(err) : res(null)), + ) + }).catch(console.error) + + // xftp (was used for file-server.ini) + await new Promise((res, rej) => { + execFile( + 'sh', + [ + '-c', + 'mv /media/startos/volumes/xftp/* /media/startos/volumes/xftp-configs', + ], + (err) => (err ? rej(err) : res(null)), + ) + }).catch(console.error) + + // log (was used for smp-state) + await new Promise((res, rej) => { + execFile( + 'sh', + [ + '-c', + 'mv /media/startos/volumes/log/* /media/startos/volumes/smp-state', + ], + (err) => (err ? rej(err) : res(null)), + ) + }).catch(console.error) + + // main (was used for xftp files) + await new Promise((res, rej) => { + execFile( + 'sh', + [ + '-c', + 'mv /media/startos/volumes/xftp/* /media/startos/volumes/xftp-files', + ], + (err) => (err ? rej(err) : res(null)), + ) + }).catch(console.error) + + const create_password = + new URL( + statsYaml.data['SimpleX SMP Server Address'].value.replace( + 'smp://', + 'https://', + ), + ).password || + utils.getDefaultString({ + charset: 'a-z,A-Z,1-9,!,$,%,&,*', + len: 21, + }) + + // overwrite smp-server.ini + await smpServerIni.write(effects, { + ...smpConfigDefaults, + AUTH: { ...smpConfigDefaults.AUTH, create_password }, + }) + + // overwrite file-server.ini + await fileServerIni.write(effects, { + ...xftpConfigDefaults, + AUTH: { ...xftpConfigDefaults.AUTH, create_password }, + }) + // remove everything from old volumes + await Promise.all( + ['conf', 'xftp', 'log', 'main'].map((vol) => + clearVolume(`/media/startos/volumes/${vol}`), + ), + ) + } + }, + down: IMPOSSIBLE, + }, +}) + +async function clearVolume(volumePath: string) { + const entries = await readdir(volumePath) + await Promise.all( + entries.map((entry) => + rm(join(volumePath, entry), { recursive: true }).catch(console.error), + ), + ) +} diff --git a/startos/interfaces.ts b/startos/interfaces.ts index 300a42f..2f6ba18 100644 --- a/startos/interfaces.ts +++ b/startos/interfaces.ts @@ -1,61 +1,93 @@ +import { FileHelper } from '@start9labs/start-sdk' import { sdk } from './sdk' import { smpPort, webPort, xftpPort } from './utils' +import { smpServerIni } from './fileModels/smpServer.ini' export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { - // ** SMP Server ** - const smpMulti = sdk.MultiHost.of(effects, 'smp-multi') + // get fingerprints + const smpFingerprint = await FileHelper.string({ + volumeId: 'smp-configs', + subpath: './fingerprint', + }) + .read() + .const(effects) + .then((f) => f?.trim()) + + const xftpFingerprint = await FileHelper.string({ + volumeId: 'xftp-configs', + subpath: './fingerprint', + }) + .read() + .const(effects) + .then((f) => f?.trim()) - // smp + // get password + const password = await smpServerIni + .read((s) => s.AUTH.create_password) + .const(effects) + + // ** SMP Server ** + const smpMulti = sdk.MultiHost.of(effects, 'main') const smpMultiOrigin = await smpMulti.bindPort(smpPort, { - protocol: 'http', + protocol: null, + addSsl: null, + preferredExternalPort: smpPort, + secure: { ssl: true }, }) const smp = sdk.createInterface(effects, { name: 'SMP Server', id: 'smp', description: 'The SMP server for SimpleX', type: 'api', - masked: false, - schemeOverride: null, - username: null, + masked: true, + schemeOverride: { ssl: 'smp', noSsl: 'smp' }, + username: `${smpFingerprint}:${password}`, path: '', query: {}, }) const smpReceipt = await smpMultiOrigin.export([smp]) - // admin - const adminMultiOrigin = await smpMulti.bindPort(webPort, { - protocol: 'http', - }) - const admin = sdk.createInterface(effects, { - name: 'Admin UI', - id: 'admin', - description: 'Your SimpleX SMP server admin user interface', - type: 'ui', - masked: false, - schemeOverride: null, - username: null, - path: '', - query: {}, - }) - const adminReceipt = await adminMultiOrigin.export([admin]) - // ** XFTP Server ** - const xftpMulti = sdk.MultiHost.of(effects, 'xftp-multi') + const xftpMulti = sdk.MultiHost.of(effects, 'xftp') const xftpMultiOrigin = await xftpMulti.bindPort(xftpPort, { - protocol: 'http', + protocol: null, + addSsl: null, + preferredExternalPort: xftpPort, + secure: { ssl: true }, }) const xftp = sdk.createInterface(effects, { name: 'XFTP Server', id: 'xftp', description: 'The XFTP server for SimpleX', type: 'api', - masked: false, - schemeOverride: null, - username: null, + masked: true, + schemeOverride: { ssl: 'xftp', noSsl: 'xftp' }, + username: `${xftpFingerprint}:${password}`, path: '', query: {}, }) const xftpReceipt = await xftpMultiOrigin.export([xftp]) - return [smpReceipt, adminReceipt, xftpReceipt] + const receipts = [smpReceipt, xftpReceipt] + + // @TODO consider implementing optional web info page + // if (store.enableWeb) { + // const infoMultiOrigin = await smpMulti.bindPort(webPort, { + // protocol: 'http', + // }) + // const info = sdk.createInterface(effects, { + // name: 'Info UI', + // id: 'info', + // description: 'Information about your SMP server', + // type: 'ui', + // masked: false, + // schemeOverride: null, + // username: null, + // path: '', + // query: {}, + // }) + // receipts.push(await infoMultiOrigin.export([info])) + // } + + return receipts }) diff --git a/startos/main.ts b/startos/main.ts index 79765e7..9f7b8cf 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -1,6 +1,7 @@ +import { fileServerIni } from './fileModels/fileServer.ini' +import { smpServerIni } from './fileModels/smpServer.ini' import { sdk } from './sdk' -import { T } from '@start9labs/start-sdk' -import { smpPort, xftpPort } from './utils' +import { smpMounts, smpPort, xftpMounts, xftpPort } from './utils' export const main = sdk.setupMain(async ({ effects }) => { /** @@ -8,19 +9,17 @@ export const main = sdk.setupMain(async ({ effects }) => { * * In this section, we fetch any resources or run any desired preliminary commands. */ - console.info('[i] Starting SimpleX!') + console.info('Starting SimpleX!') - const simplexSub = await sdk.SubContainer.of( - effects, - { imageId: 'simplex' }, - sdk.Mounts.of().mountVolume({ - volumeId: 'main', - subpath: null, - mountpoint: '/data', - readonly: false, - }), - 'simplex-sub', - ) + // confirm configuration files are present and restart service if they change + const smpIni = await smpServerIni.read().const(effects) + if (!smpIni) { + throw new Error('No smp-server.ini') + } + const fileIni = await fileServerIni.read().const(effects) + if (!fileIni) { + throw new Error('No file-server.ini') + } /** * ======================== Daemons ======================== @@ -31,8 +30,15 @@ export const main = sdk.setupMain(async ({ effects }) => { */ return sdk.Daemons.of(effects) .addDaemon('smp', { - subcontainer: simplexSub, - exec: { command: ['smp-server', 'start', '+RTS', '-N', '-RTS'] }, + subcontainer: await sdk.SubContainer.of( + effects, + { imageId: 'smp' }, + smpMounts, + 'smp-sub', + ), + exec: { + command: sdk.useEntrypoint(), + }, ready: { display: 'SMP Server', fn: () => @@ -44,8 +50,15 @@ export const main = sdk.setupMain(async ({ effects }) => { requires: [], }) .addDaemon('xftp', { - subcontainer: simplexSub, - exec: { command: ['xftp-server', 'start', '+RTS', '-N', '-RTS'] }, + subcontainer: await sdk.SubContainer.of( + effects, + { imageId: 'xftp' }, + xftpMounts, + 'xftp-sub', + ), + exec: { + command: sdk.useEntrypoint(), + }, ready: { display: 'XFTP Server', fn: () => diff --git a/startos/manifest.ts b/startos/manifest.ts index 4e7ef1a..37fe397 100644 --- a/startos/manifest.ts +++ b/startos/manifest.ts @@ -1,28 +1,41 @@ import { setupManifest } from '@start9labs/start-sdk' -import { source_code } from './utils' export const manifest = setupManifest({ id: 'simplex', title: 'SimpleX Server', license: 'MIT', wrapperRepo: 'https://github.com/Start9Labs/simplex-startos', - upstreamRepo: source_code, - supportSite: 'https://github.com/simplex-chat/simplexmq/issues', + upstreamRepo: 'https://github.com/simplex-chat/simplexmq/', + supportSite: 'https://github.com/simplex-chat/simplexmq//issues', marketingSite: 'https://simplex.chat/', - docsUrl: - 'https://github.com/Start9Labs/simplex-startos/blob/update/040/docs/README.md', + docsUrl: + 'https://github.com/Start9Labs/simplex-startos/blob/update/040/docs/instructions.md', donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', - // @TODO description: { - short: '', - long: '', + short: 'Freedom & security of your communications', + long: 'An open-source, decentralized messenger designed for maximum privacy and control.', }, - volumes: ['main'], + volumes: [ + 'smp-configs', + 'smp-state', + 'xftp-configs', + 'xftp-state', + 'xftp-files', + 'main', // migration + 'conf', // migration + 'xftp', // migration + 'log', // migration + ], images: { - simplex: { + smp: { source: { - dockerBuild: {}, + dockerTag: 'simplexchat/smp-server:v6.4.5', + }, + }, + xftp: { + source: { + dockerTag: 'simplexchat/xftp-server:v6.4.5', }, }, }, diff --git a/startos/utils.ts b/startos/utils.ts index 8760965..e1968d6 100644 --- a/startos/utils.ts +++ b/startos/utils.ts @@ -1,95 +1,116 @@ -export const smpPort = 5223 -export const xftpPort = 443 -export const controlPort = 5224 -export const webPort = 443 - -export const source_code = 'https://github.com/simplex-chat/simplexmq/' +import { sdk } from './sdk' -export const randomPassword = { - charset: 'a-z,A-Z,1-9,!,$,%,&,*', - len: 21, -} +export const smpPort = 5223 +export const smpControlPort = 5224 + +export const xftpPort = 5225 +export const xftpControlPort = 5226 + +export const smpStatePath = '/var/opt/simplex' +export const xftpFilePath = '/srv/xftp' + +export const webPort = 8000 + +export const smpMounts = sdk.Mounts.of() + .mountVolume({ + volumeId: 'smp-configs', + subpath: null, + mountpoint: '/etc/opt/simplex', + readonly: false, + }) + .mountVolume({ + volumeId: 'smp-state', + subpath: null, + mountpoint: smpStatePath, + readonly: false, + }) + +export const xftpMounts = sdk.Mounts.of() + .mountVolume({ + volumeId: 'xftp-configs', + subpath: null, + mountpoint: '/etc/opt/simplex-xftp', + readonly: false, + }) + .mountVolume({ + volumeId: 'xftp-state', + subpath: null, + mountpoint: '/var/opt/simplex-xftp', + readonly: false, + }) + .mountVolume({ + volumeId: 'xftp-files', + subpath: null, + mountpoint: xftpFilePath, + readonly: false, + }) export const smpConfigDefaults = { - information: { - // Basic info - LICENSE: undefined, - source_code, - - // Server usage conditions and amendments. - usage_conditions: undefined, - condition_amendments: undefined, - - // Server location and operator - server_country: undefined, - operator: undefined, - operator_country: undefined, - website: undefined, - - // Administrative contacts - admin_simplex: undefined, // SimpleX address - admin_email: undefined, - admin_pgp: undefined, - admin_pgp_fingerprint: undefined, - - // Contacts for complaints and feedback. - complaints_simplex: undefined, // SimpleX address - complaints_email: undefined, - complaints_pgp: undefined, - complaints_pgp_fingerprint: undefined, - - // Hosting provider - hosting: undefined, // entity organization or person name - hosting_country: undefined, // ISO-3166 2-letter code + INFORMATION: { + source_code: 'https://github.com/simplex-chat/simplexmq/', }, - store_log: { - enable: 'on' as const, - - restore_messages: 'on' as const, - expire_messages_days: 21, - expire_ntfs_hours: 24, - - log_stats: 'on' as const, + STORE_LOG: { + enable: 'on', + store_queues: 'memory', + store_messages: 'memory', + restore_messages: 'on', + expire_messages_days: 365, // @TODO ask Evgany + expire_messages_on_start: 'off', + expire_ntfs_hours: 168, + log_stats: 'on', + prometheus_interval: undefined, }, - auth: { - new_queues: 'on' as const, - - create_password: '', // any printable ASCII characters without whitespace, '@', ':' and '/' - - control_port_admin_password: '', - control_port_user_password: '', + AUTH: { + new_queues: 'on', + control_port_admin_password: undefined, + control_port_user_password: undefined, }, - transport: { - host: 'hostnames', // - port: 5223, - log_tls_errors: 'off' as const, - - websockets: 'off' as const, - control_port: controlPort, + TRANSPORT: { + host: '', + port: `${smpPort},443`, + log_tls_errors: 'off', + websockets: 'off', + control_port: smpControlPort, }, - proxy: { - // Network configuration for SMP proxy client. - host_mode: 'public' as const, - required_host_mode: 'off' as const, - - own_server_domains: [], - - socks_proxy: '', - socks_mode: 'onion' as const, - + PROXY: { + socks_proxy: '127.0.0.1:9050', client_concurrency: 32, }, - inactive_clients: { - disconnect: 'off' as const, - ttl: 21600, - check_interval: 3600, + INACTIVE_CLIENTS: { + disconnect: 'off', }, - web: { - static_path: 'var/opt/simplex/www', - http: undefined, - - https: webPort, - cert: '', // @TODO Aiden - key: '', // @TODO Aiden + WEB: { + static_path: undefined, + http: webPort, + https: undefined, + cert: undefined, + key: undefined, + }, +} as const + +export const xftpConfigDefaults = { + STORE_LOG: { + enable: 'on', + expire_files_hours: 168, + log_stats: 'off', + prometheus_interval: undefined, + }, + AUTH: { + new_files: 'on', + control_port_admin_password: undefined, + control_port_user_password: undefined, + }, + TRANSPORT: { + host: '', + port: xftpPort, + log_tls_errors: 'off', + control_port: xftpControlPort, + }, + FILES: { + path: xftpFilePath, + storage_quota: '10gb', + }, + INACTIVE_CLIENTS: { + disconnect: 'off', }, -} +} as const From d0fd2f588697fabafca49daa60905c0a1ebb3244 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 14 Jan 2026 16:56:07 -0700 Subject: [PATCH 15/35] beta 47 --- package-lock.json | 8 ++++---- package.json | 2 +- startos/fileModels/fileServer.ini.ts | 3 ++- startos/fileModels/smpServer.ini.ts | 3 ++- startos/interfaces.ts | 4 ++-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4e9ba96..0526b0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.45", + "@start9labs/start-sdk": "^0.4.0-beta.47", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.45", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.45.tgz", - "integrity": "sha512-N4iIifr5f1ElB4zcZnW8hCkPOAsfCxf+1UGqp249Tzm+gIRj45aq1wiE+vGCeSyGIuHsfMp3h5Pouyvfn33FOQ==", + "version": "0.4.0-beta.47", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.47.tgz", + "integrity": "sha512-GhnKJ8F/aEuW4gu0GxHXcSDrj69+H+Dysr0a743q7Hy2HZQSvndAy0B3cPmqz+0oaHKXnCL5r8nicgH8zXlENw==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/package.json b/package.json index bb81a25..449bb69 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.45", + "@start9labs/start-sdk": "^0.4.0-beta.47", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/startos/fileModels/fileServer.ini.ts b/startos/fileModels/fileServer.ini.ts index c940d3d..cb05b8c 100644 --- a/startos/fileModels/fileServer.ini.ts +++ b/startos/fileModels/fileServer.ini.ts @@ -1,5 +1,6 @@ import { matches, FileHelper } from '@start9labs/start-sdk' import { xftpConfigDefaults } from '../utils' +import { sdk } from '../sdk' const { object, literals, natural, string, literal } = matches @@ -77,7 +78,7 @@ const shape = object({ export const fileServerIni = FileHelper.ini( { - volumeId: 'xftp-configs', + base: sdk.volumes['xftp-configs'], subpath: './file-server.ini', }, shape, diff --git a/startos/fileModels/smpServer.ini.ts b/startos/fileModels/smpServer.ini.ts index 2c77ed9..a517b0f 100644 --- a/startos/fileModels/smpServer.ini.ts +++ b/startos/fileModels/smpServer.ini.ts @@ -1,6 +1,7 @@ import { matches, FileHelper } from '@start9labs/start-sdk' import { smpConfigDefaults, smpStatePath } from '../utils' import * as INI from './ini-lib' +import { sdk } from '../sdk' const { object, string, literal, literals, natural, nill } = matches @@ -137,7 +138,7 @@ const shape = object({ export const smpServerIni = FileHelper.raw( { - volumeId: 'smp-configs', + base: sdk.volumes['smp-configs'], subpath: './smp-server.ini', }, (inData) => INI.stringify(inData), diff --git a/startos/interfaces.ts b/startos/interfaces.ts index 2f6ba18..2e66a33 100644 --- a/startos/interfaces.ts +++ b/startos/interfaces.ts @@ -6,7 +6,7 @@ import { smpServerIni } from './fileModels/smpServer.ini' export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { // get fingerprints const smpFingerprint = await FileHelper.string({ - volumeId: 'smp-configs', + base: sdk.volumes['smp-configs'], subpath: './fingerprint', }) .read() @@ -14,7 +14,7 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { .then((f) => f?.trim()) const xftpFingerprint = await FileHelper.string({ - volumeId: 'xftp-configs', + base: sdk.volumes['xftp-configs'], subpath: './fingerprint', }) .read() From 56d3f1931aa97cf05c55c56bfa7c2fe2cec0dfec Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sun, 1 Feb 2026 14:37:19 -0700 Subject: [PATCH 16/35] beta 48, i18n, and more --- .github/workflows/buildService.yml | 7 ++ .github/workflows/releaseService.yml | 1 + Makefile | 92 +--------------- package-lock.json | 9 +- package.json | 2 +- s9pk.mk | 103 ++++++++++++++++++ startos/i18n/dictionaries/default.ts | 19 ++++ startos/i18n/dictionaries/translations.ts | 56 ++++++++++ startos/i18n/index.ts | 5 + startos/install/versions/index.ts | 2 +- .../{v6.4.5.0.a0.ts => v7.0.0.0.b0.ts} | 12 +- startos/interfaces.ts | 9 +- startos/main.ts | 19 ++-- startos/manifest/i18n.ts | 18 +++ startos/{manifest.ts => manifest/index.ts} | 12 +- 15 files changed, 248 insertions(+), 118 deletions(-) create mode 100644 s9pk.mk create mode 100644 startos/i18n/dictionaries/default.ts create mode 100644 startos/i18n/dictionaries/translations.ts create mode 100644 startos/i18n/index.ts rename startos/install/versions/{v6.4.5.0.a0.ts => v7.0.0.0.b0.ts} (91%) create mode 100644 startos/manifest/i18n.ts rename startos/{manifest.ts => manifest/index.ts} (76%) diff --git a/.github/workflows/buildService.yml b/.github/workflows/buildService.yml index 5d19b77..feb2ae9 100644 --- a/.github/workflows/buildService.yml +++ b/.github/workflows/buildService.yml @@ -9,8 +9,15 @@ on: paths-ignore: ['*.md'] branches: ['main', 'master', 'update/040'] +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + jobs: build: + if: github.event.pull_request.draft == false uses: start9labs/shared-workflows/.github/workflows/buildService.yml@master + # with: + # FREE_DISK_SPACE: true secrets: DEV_KEY: ${{ secrets.DEV_KEY }} diff --git a/.github/workflows/releaseService.yml b/.github/workflows/releaseService.yml index 4764532..ecc6a66 100644 --- a/.github/workflows/releaseService.yml +++ b/.github/workflows/releaseService.yml @@ -9,6 +9,7 @@ jobs: release: uses: start9labs/shared-workflows/.github/workflows/releaseService.yml@master with: + # FREE_DISK_SPACE: true REGISTRY: ${{ vars.REGISTRY }} # Optional. Defaults to https://alpha-registry-x.start9.com secrets: DEV_KEY: ${{ secrets.DEV_KEY }} # Required diff --git a/Makefile b/Makefile index 295e219..10d4360 100644 --- a/Makefile +++ b/Makefile @@ -1,89 +1,3 @@ -PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest.ts) -INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null) - -.PHONY: all 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: $(PACKAGE_ID).s9pk - $(call SUMMARY,$<) - -arch/%: $(PACKAGE_ID)_%.s9pk - $(call SUMMARY,$<) - -x86 x86_64: arch/x86_64 -arm arm64 aarch64: arch/aarch64 -riscv riscv64: arch/riscv64 - -$(PACKAGE_ID).s9pk: $(INGREDIENTS) .git/HEAD .git/index - @$(MAKE) --no-print-directory ingredients - @echo " Packing '$@'..." - start-cli s9pk pack -o $@ - -$(PACKAGE_ID)_%.s9pk: $(INGREDIENTS) .git/HEAD .git/index - @$(MAKE) --no-print-directory ingredients - @echo " Packing '$@'..." - start-cli s9pk pack --arch=$* -o $@ - -ingredients: $(INGREDIENTS) - @echo " Re-evaluating ingredients..." - -install: | check-deps check-init - @HOST=$$(awk -F'/' '/^host:/ {print $$3}' ~/.startos/config.yaml); \ - if [ -z "$$HOST" ]; then \ - echo "Error: You must define \"host: http://server-name.local\" in ~/.startos/config.yaml"; \ - exit 1; \ - fi; \ - S9PK=$$(ls -t *.s9pk 2>/dev/null | head -1); \ - if [ -z "$$S9PK" ]; then \ - echo "Error: No .s9pk file found. Run 'make' first."; \ - exit 1; \ - fi; \ - printf "\n🚀 Installing %s to %s ...\n" "$$S9PK" "$$HOST"; \ - start-cli package install -s "$$S9PK" - -check-deps: - @command -v start-cli >/dev/null || \ - (echo "Error: start-cli not found. Please see https://docs.start9.com/latest/developer-guide/sdk/installing-the-sdk" && exit 1) - @command -v npm >/dev/null || \ - (echo "Error: npm not found. Please install Node.js and npm." && exit 1) - -check-init: - @if [ ! -f ~/.startos/developer.key.pem ]; then \ - echo "Initializing StartOS developer environment..."; \ - start-cli init-key; \ - fi - -javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules - npm run build - -node_modules: package-lock.json - npm ci - -package-lock.json: package.json - npm i - -clean: - @echo "Cleaning up build artifacts..." - @rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk $(PACKAGE_ID)_riscv64.s9pk javascript node_modules +ARCHES := x86 arm +# overrides to s9pk.mk must precede the include statement +include s9pk.mk diff --git a/package-lock.json b/package-lock.json index 0526b0a..854177a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.47", + "@start9labs/start-sdk": "0.4.0-beta.48", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,10 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.47", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.47.tgz", - "integrity": "sha512-GhnKJ8F/aEuW4gu0GxHXcSDrj69+H+Dysr0a743q7Hy2HZQSvndAy0B3cPmqz+0oaHKXnCL5r8nicgH8zXlENw==", - "license": "MIT", + "version": "0.4.0-beta.48", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.48.tgz", + "integrity": "sha512-beMdwhUffhnbSm3FgkWPJjAWazMhNMzqbHtS6yK2hX6VpP39JxzDVo8vEbJAUfot6LPw+OhOUtPEu6KDaGre6A==", "dependencies": { "@iarna/toml": "^3.0.0", "@noble/curves": "^1.8.2", diff --git a/package.json b/package.json index 449bb69..2a77d83 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.47", + "@start9labs/start-sdk": "0.4.0-beta.48", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/s9pk.mk b/s9pk.mk new file mode 100644 index 0000000..9d887af --- /dev/null +++ b/s9pk.mk @@ -0,0 +1,103 @@ +# ** Plumbing. DO NOT EDIT **. +# This file is imported by ./Makefile. Make edits there + +PACKAGE_ID := $(shell awk -F"'" '/id:/ {print $$2}' startos/manifest/index.ts) +INGREDIENTS := $(shell start-cli s9pk list-ingredients 2>/dev/null) +ARCHES ?= x86 arm riscv +TARGETS ?= arches +ifdef VARIANT +BASE_NAME := $(PACKAGE_ID)_$(VARIANT) +else +BASE_NAME := $(PACKAGE_ID) +endif + +.PHONY: all arches aarch64 x86_64 riscv64 arm arm64 x86 riscv arch/* clean install check-deps check-init package ingredients +.DELETE_ON_ERROR: +.SECONDARY: + +define SUMMARY + @manifest=$$(start-cli s9pk inspect $(1) manifest); \ + size=$$(du -h $(1) | awk '{print $$1}'); \ + title=$$(printf '%s' "$$manifest" | jq -r .title); \ + version=$$(printf '%s' "$$manifest" | jq -r .version); \ + arches=$$(printf '%s' "$$manifest" | jq -r '[.images[].arch // []] | flatten | unique | join(", ")'); \ + sdkv=$$(printf '%s' "$$manifest" | jq -r .sdkVersion); \ + gitHash=$$(printf '%s' "$$manifest" | jq -r .gitHash | sed -E 's/(.*-modified)$$/\x1b[0;31m\1\x1b[0m/'); \ + printf "\n"; \ + printf "\033[1;32m✅ Build Complete!\033[0m\n"; \ + printf "\n"; \ + printf "\033[1;37m📦 $$title\033[0m \033[36mv$$version\033[0m\n"; \ + printf "───────────────────────────────\n"; \ + printf " \033[1;36mFilename:\033[0m %s\n" "$(1)"; \ + printf " \033[1;36mSize:\033[0m %s\n" "$$size"; \ + printf " \033[1;36mArch:\033[0m %s\n" "$$arches"; \ + printf " \033[1;36mSDK:\033[0m %s\n" "$$sdkv"; \ + printf " \033[1;36mGit:\033[0m %s\n" "$$gitHash"; \ + echo "" +endef + +all: $(TARGETS) + +arches: $(ARCHES) + +universal: $(BASE_NAME).s9pk + $(call SUMMARY,$<) + +arch/%: $(BASE_NAME)_%.s9pk + $(call SUMMARY,$<) + +x86 x86_64: arch/x86_64 +arm arm64 aarch64: arch/aarch64 +riscv riscv64: arch/riscv64 + +$(BASE_NAME).s9pk: $(INGREDIENTS) .git/HEAD .git/index + @$(MAKE) --no-print-directory ingredients + @echo " Packing '$@'..." + start-cli s9pk pack -o $@ + +$(BASE_NAME)_%.s9pk: $(INGREDIENTS) .git/HEAD .git/index + @$(MAKE) --no-print-directory ingredients + @echo " Packing '$@'..." + start-cli s9pk pack --arch=$* -o $@ + +ingredients: $(INGREDIENTS) + @echo " Re-evaluating ingredients..." + +install: | check-deps check-init + @HOST=$$(awk -F'/' '/^host:/ {print $$3}' ~/.startos/config.yaml); \ + if [ -z "$$HOST" ]; then \ + echo "Error: You must define \"host: http://server-name.local\" in ~/.startos/config.yaml"; \ + exit 1; \ + fi; \ + S9PK=$$(ls -t *.s9pk 2>/dev/null | head -1); \ + if [ -z "$$S9PK" ]; then \ + echo "Error: No .s9pk file found. Run 'make' first."; \ + exit 1; \ + fi; \ + printf "\n🚀 Installing %s to %s ...\n" "$$S9PK" "$$HOST"; \ + start-cli package install -s "$$S9PK" + +check-deps: + @command -v start-cli >/dev/null || \ + (echo "Error: start-cli not found. Please see https://docs.start9.com/latest/developer-guide/sdk/installing-the-sdk" && exit 1) + @command -v npm >/dev/null || \ + (echo "Error: npm not found. Please install Node.js and npm." && exit 1) + +check-init: + @if [ ! -f ~/.startos/developer.key.pem ]; then \ + echo "Initializing StartOS developer environment..."; \ + start-cli init-key; \ + fi + +javascript/index.js: $(shell find startos -type f) tsconfig.json node_modules + npm run build + +node_modules: package-lock.json + npm ci + +package-lock.json: package.json + npm i + +clean: + @echo "Cleaning up build artifacts..." + @rm -rf $(PACKAGE_ID).s9pk $(PACKAGE_ID)_x86_64.s9pk $(PACKAGE_ID)_aarch64.s9pk $(PACKAGE_ID)_riscv64.s9pk javascript node_modules diff --git a/startos/i18n/dictionaries/default.ts b/startos/i18n/dictionaries/default.ts new file mode 100644 index 0000000..dbb713e --- /dev/null +++ b/startos/i18n/dictionaries/default.ts @@ -0,0 +1,19 @@ +export const DEFAULT_LANG = 'en_US' + +const dict = { + 'Starting SimpleX!': 0, + 'No smp-server.ini': 1, + 'No file-server.ini': 2, + 'SMP Server': 3, + 'The SMP server is ready': 4, + 'The SMP server is not ready': 5, + 'XFTP Server': 6, + 'The XFTP server is ready': 7, + 'The XFTP server is not ready': 8, + 'The SMP server for SimpleX': 9, + 'The XFTP server for SimpleX': 10, +} as const + +export type I18nKey = keyof typeof dict +export type LangDict = Record<(typeof dict)[I18nKey], string> +export default dict diff --git a/startos/i18n/dictionaries/translations.ts b/startos/i18n/dictionaries/translations.ts new file mode 100644 index 0000000..d66b53e --- /dev/null +++ b/startos/i18n/dictionaries/translations.ts @@ -0,0 +1,56 @@ +import { LangDict } from './default' + +export default { + es_ES: { + 0: '¡Iniciando SimpleX!', + 1: 'Sin smp-server.ini', + 2: 'Sin file-server.ini', + 3: 'Servidor SMP', + 4: 'El servidor SMP está listo', + 5: 'El servidor SMP no está listo', + 6: 'Servidor XFTP', + 7: 'El servidor XFTP está listo', + 8: 'El servidor XFTP no está listo', + 9: 'El servidor SMP de SimpleX', + 10: 'El servidor XFTP de SimpleX', + }, + de_DE: { + 0: 'SimpleX wird gestartet!', + 1: 'Keine smp-server.ini', + 2: 'Keine file-server.ini', + 3: 'SMP-Server', + 4: 'Der SMP-Server ist bereit', + 5: 'Der SMP-Server ist nicht bereit', + 6: 'XFTP-Server', + 7: 'Der XFTP-Server ist bereit', + 8: 'Der XFTP-Server ist nicht bereit', + 9: 'Der SMP-Server für SimpleX', + 10: 'Der XFTP-Server für SimpleX', + }, + pl_PL: { + 0: 'Uruchamianie SimpleX!', + 1: 'Brak smp-server.ini', + 2: 'Brak file-server.ini', + 3: 'Serwer SMP', + 4: 'Serwer SMP jest gotowy', + 5: 'Serwer SMP nie jest gotowy', + 6: 'Serwer XFTP', + 7: 'Serwer XFTP jest gotowy', + 8: 'Serwer XFTP nie jest gotowy', + 9: 'Serwer SMP dla SimpleX', + 10: 'Serwer XFTP dla SimpleX', + }, + fr_FR: { + 0: 'Démarrage de SimpleX !', + 1: 'Pas de smp-server.ini', + 2: 'Pas de file-server.ini', + 3: 'Serveur SMP', + 4: 'Le serveur SMP est prêt', + 5: 'Le serveur SMP n\'est pas prêt', + 6: 'Serveur XFTP', + 7: 'Le serveur XFTP est prêt', + 8: 'Le serveur XFTP n\'est pas prêt', + 9: 'Le serveur SMP de SimpleX', + 10: 'Le serveur XFTP de SimpleX', + }, +} satisfies Record diff --git a/startos/i18n/index.ts b/startos/i18n/index.ts new file mode 100644 index 0000000..1849d6d --- /dev/null +++ b/startos/i18n/index.ts @@ -0,0 +1,5 @@ +import { setupI18n } from '@start9labs/start-sdk' +import defaultDict, { DEFAULT_LANG } from './dictionaries/default' +import translations from './dictionaries/translations' + +export const i18n = setupI18n(defaultDict, translations, DEFAULT_LANG) diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index 11e9b4d..7ae0e45 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v_6_4_5_0_a0 as current } from './v6.4.5.0.a0' +export { v_7_0_0_0_b0 as current } from './v7.0.0.0.b0' export const other = [] diff --git a/startos/install/versions/v6.4.5.0.a0.ts b/startos/install/versions/v7.0.0.0.b0.ts similarity index 91% rename from startos/install/versions/v6.4.5.0.a0.ts rename to startos/install/versions/v7.0.0.0.b0.ts index 7cffdf5..3f44526 100644 --- a/startos/install/versions/v6.4.5.0.a0.ts +++ b/startos/install/versions/v7.0.0.0.b0.ts @@ -8,9 +8,15 @@ import { join } from 'path' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_6_4_5_0_a0 = VersionInfo.of({ - version: '6.4.5:0-alpha.0', - releaseNotes: 'Updated for StartOS 0.4.0', +export const v_7_0_0_0_b0 = VersionInfo.of({ + version: '7.0.0:0-beta.0', + releaseNotes: { + en_US: 'Updated for StartOS 0.4.0', + es_ES: 'Actualizado para StartOS 0.4.0', + de_DE: 'Aktualisiert für StartOS 0.4.0', + pl_PL: 'Zaktualizowano dla StartOS 0.4.0', + fr_FR: 'Mis à jour pour StartOS 0.4.0', + }, migrations: { up: async ({ effects }) => { // get old stats.yaml diff --git a/startos/interfaces.ts b/startos/interfaces.ts index 2e66a33..8cb2bc5 100644 --- a/startos/interfaces.ts +++ b/startos/interfaces.ts @@ -2,6 +2,7 @@ import { FileHelper } from '@start9labs/start-sdk' import { sdk } from './sdk' import { smpPort, webPort, xftpPort } from './utils' import { smpServerIni } from './fileModels/smpServer.ini' +import { i18n } from './i18n' export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { // get fingerprints @@ -35,9 +36,9 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { secure: { ssl: true }, }) const smp = sdk.createInterface(effects, { - name: 'SMP Server', + name: i18n('SMP Server'), id: 'smp', - description: 'The SMP server for SimpleX', + description: i18n('The SMP server for SimpleX'), type: 'api', masked: true, schemeOverride: { ssl: 'smp', noSsl: 'smp' }, @@ -56,9 +57,9 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => { secure: { ssl: true }, }) const xftp = sdk.createInterface(effects, { - name: 'XFTP Server', + name: i18n('XFTP Server'), id: 'xftp', - description: 'The XFTP server for SimpleX', + description: i18n('The XFTP server for SimpleX'), type: 'api', masked: true, schemeOverride: { ssl: 'xftp', noSsl: 'xftp' }, diff --git a/startos/main.ts b/startos/main.ts index 9f7b8cf..415af7a 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -2,6 +2,7 @@ import { fileServerIni } from './fileModels/fileServer.ini' import { smpServerIni } from './fileModels/smpServer.ini' import { sdk } from './sdk' import { smpMounts, smpPort, xftpMounts, xftpPort } from './utils' +import { i18n } from './i18n' export const main = sdk.setupMain(async ({ effects }) => { /** @@ -9,16 +10,16 @@ export const main = sdk.setupMain(async ({ effects }) => { * * In this section, we fetch any resources or run any desired preliminary commands. */ - console.info('Starting SimpleX!') + console.info(i18n('Starting SimpleX!')) // confirm configuration files are present and restart service if they change const smpIni = await smpServerIni.read().const(effects) if (!smpIni) { - throw new Error('No smp-server.ini') + throw new Error(i18n('No smp-server.ini')) } const fileIni = await fileServerIni.read().const(effects) if (!fileIni) { - throw new Error('No file-server.ini') + throw new Error(i18n('No file-server.ini')) } /** @@ -40,11 +41,11 @@ export const main = sdk.setupMain(async ({ effects }) => { command: sdk.useEntrypoint(), }, ready: { - display: 'SMP Server', + display: i18n('SMP Server'), fn: () => sdk.healthCheck.checkPortListening(effects, smpPort, { - successMessage: 'The SMP server is ready', - errorMessage: 'The SMP server is not ready', + successMessage: i18n('The SMP server is ready'), + errorMessage: i18n('The SMP server is not ready'), }), }, requires: [], @@ -60,11 +61,11 @@ export const main = sdk.setupMain(async ({ effects }) => { command: sdk.useEntrypoint(), }, ready: { - display: 'XFTP Server', + display: i18n('XFTP Server'), fn: () => sdk.healthCheck.checkPortListening(effects, xftpPort, { - successMessage: 'The XFTP server is ready', - errorMessage: 'The XFTP server is not ready', + successMessage: i18n('The XFTP server is ready'), + errorMessage: i18n('The XFTP server is not ready'), }), }, requires: [], diff --git a/startos/manifest/i18n.ts b/startos/manifest/i18n.ts new file mode 100644 index 0000000..e91aa81 --- /dev/null +++ b/startos/manifest/i18n.ts @@ -0,0 +1,18 @@ +export default { + description: { + short: { + en_US: 'Freedom & security of your communications', + es_ES: 'Libertad y seguridad de tus comunicaciones', + de_DE: 'Freiheit und Sicherheit Ihrer Kommunikation', + pl_PL: 'Wolność i bezpieczeństwo Twojej komunikacji', + fr_FR: 'Liberté et sécurité de vos communications', + }, + long: { + en_US: 'An open-source, decentralized messenger designed for maximum privacy and control.', + es_ES: 'Un mensajero descentralizado y de código abierto diseñado para máxima privacidad y control.', + de_DE: 'Ein quelloffener, dezentraler Messenger für maximale Privatsphäre und Kontrolle.', + pl_PL: 'Otwartoźródłowy, zdecentralizowany komunikator zaprojektowany z myślą o maksymalnej prywatności i kontroli.', + fr_FR: 'Un messager open source et décentralisé conçu pour une confidentialité et un contrôle maximaux.', + }, + }, +} diff --git a/startos/manifest.ts b/startos/manifest/index.ts similarity index 76% rename from startos/manifest.ts rename to startos/manifest/index.ts index 37fe397..ae2d799 100644 --- a/startos/manifest.ts +++ b/startos/manifest/index.ts @@ -1,4 +1,5 @@ import { setupManifest } from '@start9labs/start-sdk' +import i18n from './i18n' export const manifest = setupManifest({ id: 'simplex', @@ -12,10 +13,7 @@ export const manifest = setupManifest({ 'https://github.com/Start9Labs/simplex-startos/blob/update/040/docs/instructions.md', donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', - description: { - short: 'Freedom & security of your communications', - long: 'An open-source, decentralized messenger designed for maximum privacy and control.', - }, + description: i18n.description, volumes: [ 'smp-configs', 'smp-state', @@ -30,13 +28,15 @@ export const manifest = setupManifest({ images: { smp: { source: { - dockerTag: 'simplexchat/smp-server:v6.4.5', + dockerTag: 'simplexchat/smp-server:v7.0.0', }, + arch: ['x86_64', 'aarch64'], }, xftp: { source: { - dockerTag: 'simplexchat/xftp-server:v6.4.5', + dockerTag: 'simplexchat/xftp-server:v7.0.0', }, + arch: ['x86_64', 'aarch64'], }, }, dependencies: {}, From 4904955d102ac7a4f5d2b6e496eebff7469e5390 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sun, 1 Feb 2026 15:21:25 -0700 Subject: [PATCH 17/35] revert to 6.4.5 --- Makefile | 2 +- startos/install/versions/index.ts | 2 +- .../versions/{v7.0.0.0.b0.ts => v6.4.5.0.b0.ts} | 4 ++-- startos/manifest/index.ts | 10 ++++++---- 4 files changed, 10 insertions(+), 8 deletions(-) rename startos/install/versions/{v7.0.0.0.b0.ts => v6.4.5.0.b0.ts} (98%) diff --git a/Makefile b/Makefile index 10d4360..927e1fc 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,3 @@ -ARCHES := x86 arm +ARCHES := x86 # overrides to s9pk.mk must precede the include statement include s9pk.mk diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index 7ae0e45..4ec2686 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v_7_0_0_0_b0 as current } from './v7.0.0.0.b0' +export { v6_4_5_0_b0 as current } from './v6.4.5.0.b0' export const other = [] diff --git a/startos/install/versions/v7.0.0.0.b0.ts b/startos/install/versions/v6.4.5.0.b0.ts similarity index 98% rename from startos/install/versions/v7.0.0.0.b0.ts rename to startos/install/versions/v6.4.5.0.b0.ts index 3f44526..e9c3b3a 100644 --- a/startos/install/versions/v7.0.0.0.b0.ts +++ b/startos/install/versions/v6.4.5.0.b0.ts @@ -8,8 +8,8 @@ import { join } from 'path' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_7_0_0_0_b0 = VersionInfo.of({ - version: '7.0.0:0-beta.0', +export const v6_4_5_0_b0 = VersionInfo.of({ + version: '6.4.5:0-beta.0', releaseNotes: { en_US: 'Updated for StartOS 0.4.0', es_ES: 'Actualizado para StartOS 0.4.0', diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts index ae2d799..55315e7 100644 --- a/startos/manifest/index.ts +++ b/startos/manifest/index.ts @@ -28,15 +28,17 @@ export const manifest = setupManifest({ images: { smp: { source: { - dockerTag: 'simplexchat/smp-server:v7.0.0', + dockerTag: 'simplexchat/smp-server:v6.4.5', }, - arch: ['x86_64', 'aarch64'], + // @TODO aarch64 coming after 6.4.5 + arch: ['x86_64'], }, xftp: { source: { - dockerTag: 'simplexchat/xftp-server:v7.0.0', + dockerTag: 'simplexchat/xftp-server:v6.4.5', }, - arch: ['x86_64', 'aarch64'], + // @TODO aarch64 coming after 6.4.5 + arch: ['x86_64'], }, }, dependencies: {}, From 959c9517ea3d06e3ff6538634c6b1b88e40ac621 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 7 Feb 2026 13:47:17 -0700 Subject: [PATCH 18/35] Update README, CONTRIBUTING, and docsUrl --- CONTRIBUTING.md | 15 +++ README.md | 192 ++++++++++++++++++++++++++++++++++++-- docs/README.md | 85 ----------------- startos/manifest/index.ts | 3 +- 4 files changed, 202 insertions(+), 93 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 docs/README.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..663911b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# Contributing + +## Building and Development + +See the [StartOS Packaging Guide](https://docs.start9.com/packaging-guide/) for complete environment setup and build instructions. + +### Quick Start + +```bash +# Install dependencies +npm ci + +# Build universal package +make +``` diff --git a/README.md b/README.md index f54ca3d..0550671 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,194 @@ Project Logo

-## Building from source +# SimpleX Server for StartOS -1. Set up your [environment](https://docs.start9.com/packaging-guide/environment-setup.html). +This repository packages [SimpleX](https://github.com/simplex-chat/simplexmq) for StartOS. This document describes what makes this package different from a default SimpleX deployment. -1. Clone this repository and `cd` into it. +For general SimpleX usage and features, see the [upstream documentation](https://simplex.chat/docs/). -1. run `make`. +## How This Differs from Upstream -1. The resulting `.s9pk` can be side loaded into StartOS. +This package runs both SMP (messaging) and XFTP (file transfer) servers with auto-generated credentials and pre-configured defaults. Connection URLs with embedded fingerprints and passwords are automatically generated for easy client configuration. -For a complete list of build options, see the [docs](https://docs.start9.com/packaging-guide/building.html) +## Container Runtime + +This package runs **2 containers**: + +| Container | Image | Purpose | +|-----------|-------|---------| +| smp | `simplexchat/smp-server:v6.4.5` | SimpleX Messaging Protocol server | +| xftp | `simplexchat/xftp-server:v6.4.5` | SimpleX File Transfer Protocol server | + +**Note:** Currently x86_64 only. ARM64 support coming after v6.4.5. + +## Volumes + +| Volume | Contents | Backed Up | +|--------|----------|-----------| +| `smp-configs` | SMP server configuration, keys, fingerprint | Yes | +| `smp-state` | SMP message queues and state | Yes | +| `xftp-configs` | XFTP server configuration, keys, fingerprint | Yes | +| `xftp-state` | XFTP file metadata and state | Yes | +| `xftp-files` | Uploaded file storage | Yes | + +## Install Flow + +On installation: +1. Generates shared authentication password +2. Initializes SMP server (`smp-server init`) - creates keys and fingerprint +3. Initializes XFTP server (`xftp-server init`) - creates keys and fingerprint +4. Writes configuration files with defaults + +## Configuration Management + +### Auto-Configured Settings + +**SMP Server:** + +| Setting | Value | Purpose | +|---------|-------|---------| +| Port | 5223 (and 443) | Client connections | +| Control port | 5224 | Server management | +| Message expiry | 365 days | Auto-delete old messages | +| Notification expiry | 168 hours (7 days) | Push notification cleanup | +| Store queues | Memory | Performance optimization | +| Password | Auto-generated | Queue creation auth | + +**XFTP Server:** + +| Setting | Value | Purpose | +|---------|-------|---------| +| Port | 5225 | File transfers | +| Control port | 5226 | Server management | +| Storage quota | 10 GB | Maximum file storage | +| File expiry | 168 hours (7 days) | Auto-delete uploaded files | +| Password | Shared with SMP | File upload auth | + +### User-Configurable Settings + +No configuration actions are provided. Advanced users can modify the INI files directly in the config volumes. + +## Network Interfaces + +| Interface | Type | Port | Scheme | Description | +|-----------|------|------|--------|-------------| +| SMP Server | api | 5223 | `smp://` | Messaging protocol | +| XFTP Server | api | 5225 | `xftp://` | File transfer protocol | + +Both interfaces are masked and include credentials in the connection URL format: +``` +smp://:@:5223 +xftp://:@:5225 +``` + +## Actions + +None. Server configuration is automatic. + +## Dependencies + +None. SimpleX servers are standalone. + +## Backups + +All server data is backed up: +- `smp-configs` - server identity and settings +- `smp-state` - message queues +- `xftp-configs` - server identity and settings +- `xftp-state` - file metadata +- `xftp-files` - uploaded files + +**Important:** The server fingerprint is part of your identity. Losing the config volumes means clients must reconnect to a "new" server. + +## Health Checks + +| Check | Method | Success Condition | +|-------|--------|-------------------| +| SMP Server | Port listening | Port 5223 responds | +| XFTP Server | Port listening | Port 5225 responds | + +## Using with SimpleX Chat + +1. Copy the SMP server URL from StartOS interfaces +2. In SimpleX Chat app: Settings → Network & servers → SMP servers +3. Add your server URL +4. Repeat for XFTP server URL under "XFTP servers" + +Your messages and files will now route through your own servers. + +## Limitations + +1. **x86_64 only**: ARM64 (aarch64) not yet supported in this version +2. **No web UI**: Server administration is config-file only +3. **Fixed storage quota**: XFTP limited to 10GB (requires config edit to change) +4. **No stats dashboard**: Prometheus metrics available but not exposed + +## What's Unchanged + +- Full SMP/XFTP protocol support +- End-to-end encryption +- Message queue functionality +- File transfer capabilities +- Client compatibility + +--- + +## Quick Reference (YAML) + +```yaml +package_id: simplex +upstream_version: 6.4.5 +arch: x86_64 only + +containers: + - name: smp + image: simplexchat/smp-server:v6.4.5 + ports: [5223, 5224] + - name: xftp + image: simplexchat/xftp-server:v6.4.5 + ports: [5225, 5226] + +volumes: + smp-configs: + backup: true + smp-state: + backup: true + xftp-configs: + backup: true + xftp-state: + backup: true + xftp-files: + backup: true + +interfaces: + smp: + type: api + port: 5223 + scheme: smp:// + includes: fingerprint, password + xftp: + type: api + port: 5225 + scheme: xftp:// + includes: fingerprint, password + +actions: [] + +dependencies: [] + +auto_configure: + - server fingerprints (generated) + - shared password (generated) + - message expiry: 365 days + - file expiry: 7 days + - storage quota: 10GB + +health_checks: + - name: SMP Server + method: port_listening + port: 5223 + - name: XFTP Server + method: port_listening + port: 5225 +``` diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 828a0ca..0000000 --- a/docs/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Getting started with SimpleX - -## About - -Your SimpleX Server comes with a [SMP server](https://simplex.chat/docs/server.html) for sending/receiving messages and an [XFTP server](https://simplex.chat/docs/xftp-server.html) for transferring files and media. - -In SimpleX, participants choose which servers to use for receiving messages and files. I receive through my private server, and you receive through your private server. Or we could both receive through the same private server. Or we could each use different public servers. - -SimpleX servers function as queues. Servers only store messages until a client retrieves them, after which they are deleted. This means message histories are stored on client devices, so it is client devices that must be backed up to prevent loss of messages. - -For detailed instructions on using SimpleX, check out the [official docs](https://simplex.chat/docs/guide/readme.html). - -## Initial setup - -1. Ensure your SimpleX Server is running, and health checks are passing. - -1. Follow instructions in the [Start9 docs](https://docs.start9.com) for running Tor on your client device (phone/laptop). - -1. Download and install the [SimpleX app](https://simplex.chat/) on your client device. - -1. During initial setup of the app, if you choose to create a profile, _do not_ create a SimpleX address. You can do this later, once you have configured your servers, since your server is included in your address. - -## Creating a fully anonymous profile - -The purpose of this profile is _total_ anonymity. For this profile, you will receive messages through your self-hosted server over Tor, and you only permit sending messages to other .onion servers. - -For sending messages to non-Tor servers, we recommend creating a separate profile entirely. - -SimpleX can be configured many different ways, depending on your threat model and privacy goals, each with different tradeoffs. - -### Enable and enforce Tor - -1. If you did not create a profile during initial setup, create one now. - -1. Navigate to `Settings > Network & servers`. - -1. For iOS users: Skip this step. For other platforms: Enable "Use SOCKS proxy". - -1. Set `Use .onion hosts` to "Required" (this might be under `SOCKS proxy settings` or `Advanced network settings`). This means that this profile can _only_ send messages to other .onion servers. - -1. Finally, in `Advanced Network Settings`, set `Private Routing` to "Never". - -### Connect your SMP server for messages - -1. Navigate to `Settings > Network & servers > Message servers`. - -1. You will see some default receiving servers (e.g. smp1, smp2, smp3). It is recommended you _delete them all_ to ensure you do not accidentally use them in the future. If you choose not to delete them, at least disable the "Use for new connections" setting on each of them. This will prevent them from being used without your explicit instruction. - -1. Click "Add Server". - -1. Tap "Scan QR Code". - -1. Scan your SMP Server Address QR code, located in StartOS UI under `SimpleX Server > Properties > SimpleX SMP Server Address`. - -1. Click on the newly added server. - -1. Click "Test server" and wait for the test to pass. - -1. Enable "Use for new connections". - -1. Navigate back and click "Save servers". - -### Connect your XFTP server for media and files - -1. Navigate to `Settings > Network & servers > Media and file servers`. - -1. You will see some default receiving servers (e.g. xftp1, xftp2, xftp3). It is recommended you _delete them all_ to ensure you do not accidentally use them in the future. If you choose not to delete them, at least disable the "Use for new connections" setting on each of them. This will prevent them from being used without your explicit instruction. - -1. Click "Add server". - -1. Tap "Scan QR Code". - -1. Scan your XFTP Server Address QR code, located in StartOS UI under `SimpleX Server > Properties > SimpleX XFTP Server Address`. - -1. Click on the newly added server. - -1. Click "Test server" and wait for the test to pass. - -1. Enable "Use for new connections". - -1. Navigate back and click "Save servers". - -### Create a SimpleX address (optional) - -You can now create a SimpleX Address if you want, but you should _not_ share the address publicly, as it will link the .onion URL to your identity. Remember, the purpose of this profile is total anonymity. diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts index 55315e7..404ef35 100644 --- a/startos/manifest/index.ts +++ b/startos/manifest/index.ts @@ -9,8 +9,7 @@ export const manifest = setupManifest({ upstreamRepo: 'https://github.com/simplex-chat/simplexmq/', supportSite: 'https://github.com/simplex-chat/simplexmq//issues', marketingSite: 'https://simplex.chat/', - docsUrl: - 'https://github.com/Start9Labs/simplex-startos/blob/update/040/docs/instructions.md', + docsUrl: 'https://simplex.chat/docs/', donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', description: i18n.description, From 3986a909c1d6bfaa72819e747a5b02db02c0a385 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 7 Feb 2026 15:01:47 -0700 Subject: [PATCH 19/35] Add S3 upload support to release workflow --- .github/workflows/releaseService.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/releaseService.yml b/.github/workflows/releaseService.yml index ecc6a66..17a0b91 100644 --- a/.github/workflows/releaseService.yml +++ b/.github/workflows/releaseService.yml @@ -10,8 +10,11 @@ jobs: uses: start9labs/shared-workflows/.github/workflows/releaseService.yml@master with: # FREE_DISK_SPACE: true - REGISTRY: ${{ vars.REGISTRY }} # Optional. Defaults to https://alpha-registry-x.start9.com + REGISTRY: ${{ vars.REGISTRY }} + S3_S9PKS_BASE_URL: ${{ vars.S3_S9PKS_BASE_URL }} secrets: - DEV_KEY: ${{ secrets.DEV_KEY }} # Required + DEV_KEY: ${{ secrets.DEV_KEY }} + S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }} + S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }} permissions: contents: write From da4e65373d3c394d8a714866f27360b8634246b4 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Sat, 7 Feb 2026 17:01:17 -0700 Subject: [PATCH 20/35] Update s9pk.mk --- s9pk.mk | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/s9pk.mk b/s9pk.mk index 9d887af..45ebfc9 100644 --- a/s9pk.mk +++ b/s9pk.mk @@ -77,6 +77,30 @@ install: | check-deps check-init 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) From 2cb94d6cdae1a9c4a6e94d3611f346342e841ee0 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Wed, 4 Mar 2026 09:44:06 -0700 Subject: [PATCH 21/35] beta 55 --- .github/workflows/buildService.yml | 4 +- README.md | 2 +- icon.png | Bin 34268 -> 0 bytes icon.svg | 11 ++ package-lock.json | 83 ++++---- package.json | 2 +- startos/fileModels/fileServer.ini.ts | 108 +++++----- startos/fileModels/smpServer.ini.ts | 187 +++++++----------- startos/install/versionGraph.ts | 1 - startos/install/versions/index.ts | 2 +- .../{v6.4.5.0.b0.ts => v6.4.5.1.b0.ts} | 14 +- startos/main.ts | 2 +- startos/manifest/index.ts | 7 +- 13 files changed, 197 insertions(+), 226 deletions(-) delete mode 100644 icon.png create mode 100644 icon.svg rename startos/install/versions/{v6.4.5.0.b0.ts => v6.4.5.1.b0.ts} (92%) diff --git a/.github/workflows/buildService.yml b/.github/workflows/buildService.yml index feb2ae9..00ab89b 100644 --- a/.github/workflows/buildService.yml +++ b/.github/workflows/buildService.yml @@ -4,10 +4,10 @@ on: workflow_dispatch: pull_request: paths-ignore: ['*.md'] - branches: ['main', 'master', 'update/040'] + branches: ['master'] push: paths-ignore: ['*.md'] - branches: ['main', 'master', 'update/040'] + branches: ['master'] concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} diff --git a/README.md b/README.md index 0550671..73c5e35 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Project Logo + Project Logo

# SimpleX Server for StartOS diff --git a/icon.png b/icon.png deleted file mode 100644 index 9c9a5c017fad7ceafcf4338e1241b179ff572f2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34268 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 dpU=T2+1%k*^#fhPOYk+IJL-&CxE{}&Yhn~VSe diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..c0dfcf3 --- /dev/null +++ b/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json index 854177a..c27e6ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.48", + "@start9labs/start-sdk": "0.4.0-beta.55", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,10 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.48", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.48.tgz", - "integrity": "sha512-beMdwhUffhnbSm3FgkWPJjAWazMhNMzqbHtS6yK2hX6VpP39JxzDVo8vEbJAUfot6LPw+OhOUtPEu6KDaGre6A==", + "version": "0.4.0-beta.55", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.55.tgz", + "integrity": "sha512-vPieBCBIhlSZDTtmDS8RDfC8gjJKaj2LtJThz/QGXN/NhFRUxUcVKBNL51S7MbOyJS7IYM/XU40Jqawdq2mqEw==", + "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", "@noble/curves": "^1.8.2", @@ -62,17 +63,9 @@ "ini": "^5.0.0", "isomorphic-fetch": "^3.0.0", "mime": "^4.0.7", - "ts-matches": "^6.3.2", - "yaml": "^2.7.1" - } - }, - "node_modules/@start9labs/start-sdk/node_modules/ini": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", - "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" + "yaml": "^2.7.1", + "zod": "^4.3.6", + "zod-deep-partial": "^1.2.0" } }, "node_modules/@types/ini": { @@ -82,9 +75,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", + "version": "22.19.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz", + "integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==", "dev": true, "license": "MIT", "dependencies": { @@ -110,6 +103,15 @@ "object-hash": "^3.0.0" } }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/isomorphic-fetch": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", @@ -165,9 +167,9 @@ } }, "node_modules/prettier": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.7.4.tgz", - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", "bin": { @@ -181,21 +183,21 @@ } }, "node_modules/tldts": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", - "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.24.tgz", + "integrity": "sha512-1r6vQTTt1rUiJkI5vX7KG8PR342Ru/5Oh13kEQP2SMbRSZpOey9SrBe27IDxkoWulx8ShWu4K6C0BkctP8Z1bQ==", "license": "MIT", "dependencies": { - "tldts-core": "^7.0.19" + "tldts-core": "^7.0.24" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", - "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", + "version": "7.0.24", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.24.tgz", + "integrity": "sha512-pj7yygNMoMRqG7ML2SDQ0xNIOfN3IBDUcPVM2Sg6hP96oFNN2nqnzHreT3z9xLq85IWJyNTvD38O002DdOrPMw==", "license": "MIT" }, "node_modules/tr46": { @@ -204,12 +206,6 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, - "node_modules/ts-matches": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ts-matches/-/ts-matches-6.5.0.tgz", - "integrity": "sha512-MhuobYhHYn6MlOTPAF/qk3tsRRioPac5ofYn68tc3rAJaGjsw1MsX1MOSep52DkvNJPgNV0F73zfgcQfYTVeyQ==", - "license": "MIT" - }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -267,6 +263,25 @@ "funding": { "url": "https://github.com/sponsors/eemeli" } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-deep-partial": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/zod-deep-partial/-/zod-deep-partial-1.4.4.tgz", + "integrity": "sha512-aWkPl7hVStgE01WzbbSxCgX4O+sSpgt8JOjvFUtMTF75VgL6MhWQbiZi+AWGN85SfSTtI9gsOtL1vInoqfDVaA==", + "license": "MIT", + "peerDependencies": { + "zod": "^4.1.13" + } } } } diff --git a/package.json b/package.json index 2a77d83..9271c55 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.48", + "@start9labs/start-sdk": "0.4.0-beta.55", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/startos/fileModels/fileServer.ini.ts b/startos/fileModels/fileServer.ini.ts index cb05b8c..e51fefe 100644 --- a/startos/fileModels/fileServer.ini.ts +++ b/startos/fileModels/fileServer.ini.ts @@ -1,81 +1,63 @@ -import { matches, FileHelper } from '@start9labs/start-sdk' +import { FileHelper, z } from '@start9labs/start-sdk' import { xftpConfigDefaults } from '../utils' import { sdk } from '../sdk' -const { object, literals, natural, string, literal } = matches - const { STORE_LOG, AUTH, TRANSPORT, FILES, INACTIVE_CLIENTS } = xftpConfigDefaults -const shape = object({ - STORE_LOG: object({ - /** - The server uses STM memory for persistence, - that will be lost on restart (e.g., as with redis). - This option enables saving memory to append only log, - and restoring it when the server is started. - Log is compacted on start (deleted objects are removed). - */ - enable: literals('on', 'off').onMismatch(STORE_LOG.enable), - /** - Expire files after the specified number of hours. - */ - expire_files_hours: natural +const shape = z.object({ + STORE_LOG: z.object({ + enable: z.enum(['on', 'off']).catch(STORE_LOG.enable), + expire_files_hours: z + .number() + .int() + .nonnegative() .optional() - .onMismatch(STORE_LOG.expire_files_hours), - /** - Enable xftp-server statistics for Grafana dashboard - */ - log_stats: literals('on', 'off').onMismatch(STORE_LOG.log_stats), - /** - Log interval for real-time Prometheus metrics, in seconds - */ - prometheus_interval: natural + .catch(STORE_LOG.expire_files_hours), + log_stats: z.enum(['on', 'off']).catch(STORE_LOG.log_stats), + prometheus_interval: z + .number() + .int() + .nonnegative() .optional() - .onMismatch(STORE_LOG.prometheus_interval), + .catch(STORE_LOG.prometheus_interval), }), - - AUTH: object({ - /** - Set new_files option to off to completely prohibit uploading new files. - This can be useful when you want to decommission the server, but still allow downloading the existing files. - */ - new_files: literals('on', 'off').onMismatch(AUTH.new_files), - /** - Use create_password option to enable basic auth to upload new files. - The password should be used as part of server address in client configuration: - xftp://fingerprint:password@host1,host2 - The password will not be shared with file recipients, you must share it only - with the users who you want to allow uploading files to your server. - Password to upload files (any printable ASCII characters without whitespace, '@', ':' and '/') - */ - create_password: string, - control_port_admin_password: string.optional().onMismatch(AUTH.new_files), - control_port_user_password: string.optional().onMismatch(AUTH.new_files), + AUTH: z.object({ + new_files: z.enum(['on', 'off']).catch(AUTH.new_files), + create_password: z.string(), + control_port_admin_password: z + .string() + .optional() + .catch(AUTH.control_port_admin_password), + control_port_user_password: z + .string() + .optional() + .catch(AUTH.control_port_user_password), }), - TRANSPORT: object({ - /** - host is only used to print server address on start - */ - host: string, - port: literal(TRANSPORT.port).onMismatch(TRANSPORT.port), - log_tls_errors: literals('on', 'off').onMismatch(TRANSPORT.log_tls_errors), - control_port: natural.optional().onMismatch(TRANSPORT.control_port), + TRANSPORT: z.object({ + host: z.string(), + port: z.literal(TRANSPORT.port).catch(TRANSPORT.port), + log_tls_errors: z.enum(['on', 'off']).catch(TRANSPORT.log_tls_errors), + control_port: z + .number() + .int() + .nonnegative() + .optional() + .catch(TRANSPORT.control_port), }), - FILES: object({ - path: literal(FILES.path), - storage_quota: string.onMismatch(FILES.storage_quota), + FILES: z.object({ + path: z.literal(FILES.path).catch(FILES.path), + storage_quota: z.string().catch(FILES.storage_quota), }), - INACTIVE_CLIENTS: object({ - /** - Whether or not to disconnect inactive clients. If set, ttl and check_interval should also be set (seconds) - */ - disconnect: literals(INACTIVE_CLIENTS.disconnect).onMismatch( - INACTIVE_CLIENTS.disconnect, - ), + INACTIVE_CLIENTS: z.object({ + disconnect: z + .literal(INACTIVE_CLIENTS.disconnect) + .catch(INACTIVE_CLIENTS.disconnect), }), }) +export type FileServerConfig = z.infer + export const fileServerIni = FileHelper.ini( { base: sdk.volumes['xftp-configs'], diff --git a/startos/fileModels/smpServer.ini.ts b/startos/fileModels/smpServer.ini.ts index a517b0f..8b90934 100644 --- a/startos/fileModels/smpServer.ini.ts +++ b/startos/fileModels/smpServer.ini.ts @@ -1,10 +1,8 @@ -import { matches, FileHelper } from '@start9labs/start-sdk' +import { FileHelper, z } from '@start9labs/start-sdk' import { smpConfigDefaults, smpStatePath } from '../utils' import * as INI from './ini-lib' import { sdk } from '../sdk' -const { object, string, literal, literals, natural, nill } = matches - const { INFORMATION, STORE_LOG, @@ -15,133 +13,100 @@ const { WEB, } = smpConfigDefaults -const shape = object({ - INFORMATION: object({ - source_code: literal(INFORMATION.source_code).onMismatch( - INFORMATION.source_code, - ), +const shape = z.object({ + INFORMATION: z.object({ + source_code: z + .literal(INFORMATION.source_code) + .catch(INFORMATION.source_code), }), - STORE_LOG: object({ - /** - The server uses STM memory for persistence, - that will be lost on restart (e.g., as with redis). - This option enables saving memory to append only log, - and restoring it when the server is started. - Log is compacted on start (deleted objects are removed). - */ - enable: literals('on', 'off').onMismatch(STORE_LOG.enable), - store_queues: literal(STORE_LOG.store_queues).onMismatch( - STORE_LOG.store_queues, - ), - store_messages: literal(STORE_LOG.store_messages).onMismatch( - STORE_LOG.store_messages, - ), - /** - Undelivered messages are optionally saved and restored when the server restarts, - they are preserved in the .bak file until the next restart. - */ - restore_messages: literals('on', 'off').onMismatch( - STORE_LOG.restore_messages, - ), - expire_messages_days: natural + STORE_LOG: z.object({ + enable: z.enum(['on', 'off']).catch(STORE_LOG.enable), + store_queues: z + .literal(STORE_LOG.store_queues) + .catch(STORE_LOG.store_queues), + store_messages: z + .literal(STORE_LOG.store_messages) + .catch(STORE_LOG.store_messages), + restore_messages: z.enum(['on', 'off']).catch(STORE_LOG.restore_messages), + expire_messages_days: z + .number() + .int() + .nonnegative() .optional() - .onMismatch(STORE_LOG.expire_messages_days), - expire_messages_on_start: literal( - STORE_LOG.expire_messages_on_start, - ).onMismatch(STORE_LOG.expire_messages_on_start), - expire_ntfs_hours: natural + .catch(STORE_LOG.expire_messages_days), + expire_messages_on_start: z + .literal(STORE_LOG.expire_messages_on_start) + .catch(STORE_LOG.expire_messages_on_start), + expire_ntfs_hours: z + .number() + .int() + .nonnegative() .optional() - .onMismatch(STORE_LOG.expire_ntfs_hours), - /** - Log daily server statistics to CSV file - */ - log_stats: literals('on', 'off').onMismatch(STORE_LOG.log_stats), - /** - Log interval for real-time Prometheus metrics, in seconds - */ - prometheus_interval: natural + .catch(STORE_LOG.expire_ntfs_hours), + log_stats: z.enum(['on', 'off']).catch(STORE_LOG.log_stats), + prometheus_interval: z + .number() + .int() + .nonnegative() .optional() - .onMismatch(STORE_LOG.prometheus_interval), + .catch(STORE_LOG.prometheus_interval), }), - AUTH: object({ - /** - Set new_queues option to off to completely prohibit creating new messaging queues. - This can be useful when you want to decommission the server, but not all connections are switched yet. - */ - new_queues: literal(AUTH.new_queues).onMismatch(AUTH.new_queues), - /** - Use create_password option to enable basic auth to create new messaging queues. - The password should be used as part of server address in client configuration: - smp://fingerprint:password@host1,host2 - The password will not be shared with the connecting contacts, you must share it only - with the users who you want to allow creating messaging queues on your server. - */ - create_password: string, - control_port_admin_password: string + AUTH: z.object({ + new_queues: z.literal(AUTH.new_queues).catch(AUTH.new_queues), + create_password: z.string(), + control_port_admin_password: z + .string() .optional() - .onMismatch(AUTH.control_port_admin_password), - control_port_user_password: string + .catch(AUTH.control_port_admin_password), + control_port_user_password: z + .string() .optional() - .onMismatch(AUTH.control_port_user_password), + .catch(AUTH.control_port_user_password), }), - TRANSPORT: object({ - /** - Host is only used to print server address on start. - You can specify multiple server ports. - */ - host: string, - port: literal(TRANSPORT.port).onMismatch(TRANSPORT.port), - log_tls_errors: literal(TRANSPORT.log_tls_errors).onMismatch( - TRANSPORT.log_tls_errors, - ), - websockets: literal(TRANSPORT.websockets).onMismatch(TRANSPORT.websockets), - control_port: literal(TRANSPORT.control_port).onMismatch( - TRANSPORT.control_port, - ), + TRANSPORT: z.object({ + host: z.string(), + port: z.literal(TRANSPORT.port).catch(TRANSPORT.port), + log_tls_errors: z + .literal(TRANSPORT.log_tls_errors) + .catch(TRANSPORT.log_tls_errors), + websockets: z.literal(TRANSPORT.websockets).catch(TRANSPORT.websockets), + control_port: z + .literal(TRANSPORT.control_port) + .catch(TRANSPORT.control_port), }), - PROXY: object({ - socks_proxy: literal(PROXY.socks_proxy).onMismatch(PROXY.socks_proxy), - /** - Limit number of threads a client can spawn to process proxy commands in parallel. - */ - client_concurrency: natural.onMismatch(PROXY.client_concurrency), + PROXY: z.object({ + socks_proxy: z.literal(PROXY.socks_proxy).catch(PROXY.socks_proxy), + client_concurrency: z + .number() + .int() + .nonnegative() + .catch(PROXY.client_concurrency), }), - INACTIVE_CLIENTS: object({ - /** - Whether or not to disconnect inactive clients. If set, ttl and check_interval should also be set (seconds) - */ - disconnect: literal(INACTIVE_CLIENTS.disconnect).onMismatch( - INACTIVE_CLIENTS.disconnect, - ), + INACTIVE_CLIENTS: z.object({ + disconnect: z + .literal(INACTIVE_CLIENTS.disconnect) + .catch(INACTIVE_CLIENTS.disconnect), }), - WEB: object({ - /** - Set path to generate static mini-site for server information and qr codes/links - */ - static_path: literal(`${smpStatePath}/www`) + WEB: z.object({ + static_path: z + .literal(`${smpStatePath}/www`) .optional() - .onMismatch(WEB.static_path), - /** - Run an embedded server on this port - Onion sites can use any port and register it in the hidden service config. - Running on a port 80 may require setting process capabilities. - */ - http: literal(WEB.http).onMismatch(WEB.http), - /** - Do not permit HTTPS, since that is handled by StartOS reverse proxy - */ - https: nill.onMismatch(undefined), - cert: nill.onMismatch(undefined), - key: nill.onMismatch(undefined), + .catch(WEB.static_path), + http: z.literal(WEB.http).catch(WEB.http), + https: z.any().optional().catch(undefined), + cert: z.any().optional().catch(undefined), + key: z.any().optional().catch(undefined), }), }) -export const smpServerIni = FileHelper.raw( +export type SmpServerConfig = z.infer + +export const smpServerIni = FileHelper.raw( { base: sdk.volumes['smp-configs'], subpath: './smp-server.ini', }, (inData) => INI.stringify(inData), (inString) => INI.parse(inString), - (data) => shape.unsafeCast(data), + (data) => shape.parse(data), ) diff --git a/startos/install/versionGraph.ts b/startos/install/versionGraph.ts index 1016a54..5a4fad7 100644 --- a/startos/install/versionGraph.ts +++ b/startos/install/versionGraph.ts @@ -4,5 +4,4 @@ import { current, other } from './versions' export const versionGraph = VersionGraph.of({ current, other, - preInstall: async (effects) => {}, }) diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index 4ec2686..bc0ec83 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v6_4_5_0_b0 as current } from './v6.4.5.0.b0' +export { v6_4_5_1_b0 as current } from './v6.4.5.1.b0' export const other = [] diff --git a/startos/install/versions/v6.4.5.0.b0.ts b/startos/install/versions/v6.4.5.1.b0.ts similarity index 92% rename from startos/install/versions/v6.4.5.0.b0.ts rename to startos/install/versions/v6.4.5.1.b0.ts index e9c3b3a..b4175c2 100644 --- a/startos/install/versions/v6.4.5.0.b0.ts +++ b/startos/install/versions/v6.4.5.1.b0.ts @@ -8,14 +8,14 @@ import { join } from 'path' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v6_4_5_0_b0 = VersionInfo.of({ - version: '6.4.5:0-beta.0', +export const v6_4_5_1_b0 = VersionInfo.of({ + version: '6.4.5:1-beta.0', releaseNotes: { - en_US: 'Updated for StartOS 0.4.0', - es_ES: 'Actualizado para StartOS 0.4.0', - de_DE: 'Aktualisiert für StartOS 0.4.0', - pl_PL: 'Zaktualizowano dla StartOS 0.4.0', - fr_FR: 'Mis à jour pour StartOS 0.4.0', + en_US: 'Revamped for StartOS 0.4.0', + es_ES: 'Renovado para StartOS 0.4.0', + de_DE: 'Überarbeitet für StartOS 0.4.0', + pl_PL: 'Przebudowany dla StartOS 0.4.0', + fr_FR: 'Remanié pour StartOS 0.4.0', }, migrations: { up: async ({ effects }) => { diff --git a/startos/main.ts b/startos/main.ts index 415af7a..7ef82ba 100644 --- a/startos/main.ts +++ b/startos/main.ts @@ -1,8 +1,8 @@ import { fileServerIni } from './fileModels/fileServer.ini' import { smpServerIni } from './fileModels/smpServer.ini' +import { i18n } from './i18n' import { sdk } from './sdk' import { smpMounts, smpPort, xftpMounts, xftpPort } from './utils' -import { i18n } from './i18n' export const main = sdk.setupMain(async ({ effects }) => { /** diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts index 404ef35..31b59b2 100644 --- a/startos/manifest/index.ts +++ b/startos/manifest/index.ts @@ -5,11 +5,10 @@ export const manifest = setupManifest({ id: 'simplex', title: 'SimpleX Server', license: 'MIT', - wrapperRepo: 'https://github.com/Start9Labs/simplex-startos', + packageRepo: 'https://github.com/Start9Labs/simplex-startos/tree/update/040', upstreamRepo: 'https://github.com/simplex-chat/simplexmq/', - supportSite: 'https://github.com/simplex-chat/simplexmq//issues', - marketingSite: 'https://simplex.chat/', - docsUrl: 'https://simplex.chat/docs/', + marketingUrl: 'https://simplex.chat/', + docsUrls: ['https://simplex.chat/docs/'], donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', description: i18n.description, From 72a1bf80b4b1ea7fbe86c032a80842fdb7786b00 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 5 Mar 2026 23:31:15 -0700 Subject: [PATCH 22/35] Update start-sdk to beta.57 Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index c27e6ce..dfdb398 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.55", + "@start9labs/start-sdk": "0.4.0-beta.57", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.55", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.55.tgz", - "integrity": "sha512-vPieBCBIhlSZDTtmDS8RDfC8gjJKaj2LtJThz/QGXN/NhFRUxUcVKBNL51S7MbOyJS7IYM/XU40Jqawdq2mqEw==", + "version": "0.4.0-beta.57", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.57.tgz", + "integrity": "sha512-hmyRUk0NlaMn60hxgfKBL8Ua64A9KqRpZQQw4VaBKIddaRGhGYN9raNS/0LpzJ4eSegTMBBqb+tkGYmPdNExcA==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -75,9 +75,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.13.tgz", - "integrity": "sha512-akNQMv0wW5uyRpD2v2IEyRSZiR+BeGuoB6L310EgGObO44HSMNT8z1xzio28V8qOrgYaopIDNA18YgdXd+qTiw==", + "version": "22.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz", + "integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 9271c55..fc92868 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.55", + "@start9labs/start-sdk": "0.4.0-beta.57", "tldts": "^7.0.19" }, "devDependencies": { From da9535ef37558c68b494b2e820e07c0b443aef59 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Fri, 6 Mar 2026 00:19:36 -0700 Subject: [PATCH 23/35] Update start-sdk to beta.58 Co-Authored-By: Claude Opus 4.6 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index dfdb398..8b26c15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.57", + "@start9labs/start-sdk": "0.4.0-beta.58", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.57", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.57.tgz", - "integrity": "sha512-hmyRUk0NlaMn60hxgfKBL8Ua64A9KqRpZQQw4VaBKIddaRGhGYN9raNS/0LpzJ4eSegTMBBqb+tkGYmPdNExcA==", + "version": "0.4.0-beta.58", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.58.tgz", + "integrity": "sha512-d8R+g9WNpdxvvPyBDL2VGVJjmhleIV0JEOKm28JrhnfmNSf9gOLY40QzUV1PuSCNXl50dYiom/SXab1q0faidw==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/package.json b/package.json index fc92868..e5b9c61 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.57", + "@start9labs/start-sdk": "0.4.0-beta.58", "tldts": "^7.0.19" }, "devDependencies": { From 2113dc25a7e4c2ae21341a5ad3053ec8bd9dda04 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Fri, 6 Mar 2026 13:47:53 -0700 Subject: [PATCH 24/35] Fix packaging guide URL in CONTRIBUTING.md Co-Authored-By: Claude Opus 4.6 --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 663911b..cbc38a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ ## Building and Development -See the [StartOS Packaging Guide](https://docs.start9.com/packaging-guide/) for complete environment setup and build instructions. +See the [StartOS Packaging Guide](https://docs.start9.com/packaging/) for complete environment setup and build instructions. ### Quick Start From 618a1e47e31d6be9391682a948d6b9e7e6906907 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Mon, 9 Mar 2026 01:03:39 -0600 Subject: [PATCH 25/35] fix docs url --- startos/manifest/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts index 31b59b2..4ffd609 100644 --- a/startos/manifest/index.ts +++ b/startos/manifest/index.ts @@ -8,7 +8,7 @@ export const manifest = setupManifest({ packageRepo: 'https://github.com/Start9Labs/simplex-startos/tree/update/040', upstreamRepo: 'https://github.com/simplex-chat/simplexmq/', marketingUrl: 'https://simplex.chat/', - docsUrls: ['https://simplex.chat/docs/'], + docsUrls: ['https://simplex.chat/docs/guide/readme.html'], donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', description: i18n.description, From 62b64ab3b2021287102f57b26b74c6943e94195b Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 10 Mar 2026 17:07:12 -0600 Subject: [PATCH 26/35] Update start-sdk to beta.59 Co-Authored-By: Claude Opus 4.6 --- README.md | 2 +- package-lock.json | 22 +++++++++---------- package.json | 2 +- startos/install/versions/index.ts | 2 +- .../{v6.4.5.1.b0.ts => v6.4.5.2.b0.ts} | 14 ++++++------ 5 files changed, 21 insertions(+), 21 deletions(-) rename startos/install/versions/{v6.4.5.1.b0.ts => v6.4.5.2.b0.ts} (91%) diff --git a/README.md b/README.md index 73c5e35..e805950 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This repository packages [SimpleX](https://github.com/simplex-chat/simplexmq) for StartOS. This document describes what makes this package different from a default SimpleX deployment. -For general SimpleX usage and features, see the [upstream documentation](https://simplex.chat/docs/). +For general SimpleX usage and features, see the [upstream documentation](https://simplex.chat/docs/guide/readme.html). ## How This Differs from Upstream diff --git a/package-lock.json b/package-lock.json index 8b26c15..2f879dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.58", + "@start9labs/start-sdk": "^0.4.0-beta.59", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.58", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.58.tgz", - "integrity": "sha512-d8R+g9WNpdxvvPyBDL2VGVJjmhleIV0JEOKm28JrhnfmNSf9gOLY40QzUV1PuSCNXl50dYiom/SXab1q0faidw==", + "version": "0.4.0-beta.59", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.59.tgz", + "integrity": "sha512-Cp8r+bg1feNAAHyWtND8kpbfrK3crL3WS6JCp1+LJdx8OvTTGXg/2znMOoAn+J9pBbAui5CtXiJigC1OaACYVw==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -183,21 +183,21 @@ } }, "node_modules/tldts": { - "version": "7.0.24", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.24.tgz", - "integrity": "sha512-1r6vQTTt1rUiJkI5vX7KG8PR342Ru/5Oh13kEQP2SMbRSZpOey9SrBe27IDxkoWulx8ShWu4K6C0BkctP8Z1bQ==", + "version": "7.0.25", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.25.tgz", + "integrity": "sha512-keinCnPbwXEUG3ilrWQZU+CqcTTzHq9m2HhoUP2l7Xmi8l1LuijAXLpAJ5zRW+ifKTNscs4NdCkfkDCBYm352w==", "license": "MIT", "dependencies": { - "tldts-core": "^7.0.24" + "tldts-core": "^7.0.25" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.24", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.24.tgz", - "integrity": "sha512-pj7yygNMoMRqG7ML2SDQ0xNIOfN3IBDUcPVM2Sg6hP96oFNN2nqnzHreT3z9xLq85IWJyNTvD38O002DdOrPMw==", + "version": "7.0.25", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.25.tgz", + "integrity": "sha512-ZjCZK0rppSBu7rjHYDYsEaMOIbbT+nWF57hKkv4IUmZWBNrBWBOjIElc0mKRgLM8bm7x/BBlof6t2gi/Oq/Asw==", "license": "MIT" }, "node_modules/tr46": { diff --git a/package.json b/package.json index e5b9c61..456faa2 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "0.4.0-beta.58", + "@start9labs/start-sdk": "^0.4.0-beta.59", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index bc0ec83..f2bd14a 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v6_4_5_1_b0 as current } from './v6.4.5.1.b0' +export { v_6_4_5_2_b0 as current } from './v6.4.5.2.b0' export const other = [] diff --git a/startos/install/versions/v6.4.5.1.b0.ts b/startos/install/versions/v6.4.5.2.b0.ts similarity index 91% rename from startos/install/versions/v6.4.5.1.b0.ts rename to startos/install/versions/v6.4.5.2.b0.ts index b4175c2..c278d82 100644 --- a/startos/install/versions/v6.4.5.1.b0.ts +++ b/startos/install/versions/v6.4.5.2.b0.ts @@ -8,14 +8,14 @@ import { join } from 'path' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v6_4_5_1_b0 = VersionInfo.of({ - version: '6.4.5:1-beta.0', +export const v_6_4_5_2_b0 = VersionInfo.of({ + version: '6.4.5:2-beta.0', releaseNotes: { - en_US: 'Revamped for StartOS 0.4.0', - es_ES: 'Renovado para StartOS 0.4.0', - de_DE: 'Überarbeitet für StartOS 0.4.0', - pl_PL: 'Przebudowany dla StartOS 0.4.0', - fr_FR: 'Remanié pour StartOS 0.4.0', + en_US: 'Update to StartOS SDK beta.59', + es_ES: 'Actualización a StartOS SDK beta.59', + de_DE: 'Update auf StartOS SDK beta.59', + pl_PL: 'Aktualizacja do StartOS SDK beta.59', + fr_FR: 'Mise à jour vers StartOS SDK beta.59', }, migrations: { up: async ({ effects }) => { From ca94a993bbfb03749623fe05791ba55cee84a02c Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Fri, 13 Mar 2026 11:00:27 -0600 Subject: [PATCH 27/35] Fix file model resilience, bump version Co-Authored-By: Claude Opus 4.6 --- startos/fileModels/fileServer.ini.ts | 108 ++++++----- startos/fileModels/smpServer.ini.ts | 182 ++++++++++-------- startos/install/versions/index.ts | 2 +- .../{v6.4.5.2.b0.ts => v6.4.5.3.b0.ts} | 10 +- 4 files changed, 165 insertions(+), 137 deletions(-) rename startos/install/versions/{v6.4.5.2.b0.ts => v6.4.5.3.b0.ts} (96%) diff --git a/startos/fileModels/fileServer.ini.ts b/startos/fileModels/fileServer.ini.ts index e51fefe..f1d6935 100644 --- a/startos/fileModels/fileServer.ini.ts +++ b/startos/fileModels/fileServer.ini.ts @@ -5,55 +5,67 @@ import { sdk } from '../sdk' const { STORE_LOG, AUTH, TRANSPORT, FILES, INACTIVE_CLIENTS } = xftpConfigDefaults +const storeLogSchema = z.object({ + enable: z.enum(['on', 'off']).catch(STORE_LOG.enable), + expire_files_hours: z + .number() + .int() + .nonnegative() + .optional() + .catch(STORE_LOG.expire_files_hours), + log_stats: z.enum(['on', 'off']).catch(STORE_LOG.log_stats), + prometheus_interval: z + .number() + .int() + .nonnegative() + .optional() + .catch(STORE_LOG.prometheus_interval), +}) + +const authSchema = z.object({ + new_files: z.enum(['on', 'off']).catch(AUTH.new_files), + create_password: z.string().catch(''), + control_port_admin_password: z + .string() + .optional() + .catch(AUTH.control_port_admin_password), + control_port_user_password: z + .string() + .optional() + .catch(AUTH.control_port_user_password), +}) + +const transportSchema = z.object({ + host: z.string().catch(TRANSPORT.host), + port: z.literal(TRANSPORT.port).catch(TRANSPORT.port), + log_tls_errors: z.enum(['on', 'off']).catch(TRANSPORT.log_tls_errors), + control_port: z + .number() + .int() + .nonnegative() + .optional() + .catch(TRANSPORT.control_port), +}) + +const filesSchema = z.object({ + path: z.literal(FILES.path).catch(FILES.path), + storage_quota: z.string().catch(FILES.storage_quota), +}) + +const inactiveClientsSchema = z.object({ + disconnect: z + .literal(INACTIVE_CLIENTS.disconnect) + .catch(INACTIVE_CLIENTS.disconnect), +}) + const shape = z.object({ - STORE_LOG: z.object({ - enable: z.enum(['on', 'off']).catch(STORE_LOG.enable), - expire_files_hours: z - .number() - .int() - .nonnegative() - .optional() - .catch(STORE_LOG.expire_files_hours), - log_stats: z.enum(['on', 'off']).catch(STORE_LOG.log_stats), - prometheus_interval: z - .number() - .int() - .nonnegative() - .optional() - .catch(STORE_LOG.prometheus_interval), - }), - AUTH: z.object({ - new_files: z.enum(['on', 'off']).catch(AUTH.new_files), - create_password: z.string(), - control_port_admin_password: z - .string() - .optional() - .catch(AUTH.control_port_admin_password), - control_port_user_password: z - .string() - .optional() - .catch(AUTH.control_port_user_password), - }), - TRANSPORT: z.object({ - host: z.string(), - port: z.literal(TRANSPORT.port).catch(TRANSPORT.port), - log_tls_errors: z.enum(['on', 'off']).catch(TRANSPORT.log_tls_errors), - control_port: z - .number() - .int() - .nonnegative() - .optional() - .catch(TRANSPORT.control_port), - }), - FILES: z.object({ - path: z.literal(FILES.path).catch(FILES.path), - storage_quota: z.string().catch(FILES.storage_quota), - }), - INACTIVE_CLIENTS: z.object({ - disconnect: z - .literal(INACTIVE_CLIENTS.disconnect) - .catch(INACTIVE_CLIENTS.disconnect), - }), + STORE_LOG: storeLogSchema.catch(() => storeLogSchema.parse({})), + AUTH: authSchema.catch(() => authSchema.parse({})), + TRANSPORT: transportSchema.catch(() => transportSchema.parse({})), + FILES: filesSchema.catch(() => filesSchema.parse({})), + INACTIVE_CLIENTS: inactiveClientsSchema.catch(() => + inactiveClientsSchema.parse({}), + ), }) export type FileServerConfig = z.infer diff --git a/startos/fileModels/smpServer.ini.ts b/startos/fileModels/smpServer.ini.ts index 8b90934..3e7b53b 100644 --- a/startos/fileModels/smpServer.ini.ts +++ b/startos/fileModels/smpServer.ini.ts @@ -13,90 +13,106 @@ const { WEB, } = smpConfigDefaults +const informationSchema = z.object({ + source_code: z + .literal(INFORMATION.source_code) + .catch(INFORMATION.source_code), +}) + +const storeLogSchema = z.object({ + enable: z.enum(['on', 'off']).catch(STORE_LOG.enable), + store_queues: z + .literal(STORE_LOG.store_queues) + .catch(STORE_LOG.store_queues), + store_messages: z + .literal(STORE_LOG.store_messages) + .catch(STORE_LOG.store_messages), + restore_messages: z.enum(['on', 'off']).catch(STORE_LOG.restore_messages), + expire_messages_days: z + .number() + .int() + .nonnegative() + .optional() + .catch(STORE_LOG.expire_messages_days), + expire_messages_on_start: z + .literal(STORE_LOG.expire_messages_on_start) + .catch(STORE_LOG.expire_messages_on_start), + expire_ntfs_hours: z + .number() + .int() + .nonnegative() + .optional() + .catch(STORE_LOG.expire_ntfs_hours), + log_stats: z.enum(['on', 'off']).catch(STORE_LOG.log_stats), + prometheus_interval: z + .number() + .int() + .nonnegative() + .optional() + .catch(STORE_LOG.prometheus_interval), +}) + +const authSchema = z.object({ + new_queues: z.literal(AUTH.new_queues).catch(AUTH.new_queues), + create_password: z.string().catch(''), + control_port_admin_password: z + .string() + .optional() + .catch(AUTH.control_port_admin_password), + control_port_user_password: z + .string() + .optional() + .catch(AUTH.control_port_user_password), +}) + +const transportSchema = z.object({ + host: z.string().catch(TRANSPORT.host), + port: z.literal(TRANSPORT.port).catch(TRANSPORT.port), + log_tls_errors: z + .literal(TRANSPORT.log_tls_errors) + .catch(TRANSPORT.log_tls_errors), + websockets: z.literal(TRANSPORT.websockets).catch(TRANSPORT.websockets), + control_port: z + .literal(TRANSPORT.control_port) + .catch(TRANSPORT.control_port), +}) + +const proxySchema = z.object({ + socks_proxy: z.literal(PROXY.socks_proxy).catch(PROXY.socks_proxy), + client_concurrency: z + .number() + .int() + .nonnegative() + .catch(PROXY.client_concurrency), +}) + +const inactiveClientsSchema = z.object({ + disconnect: z + .literal(INACTIVE_CLIENTS.disconnect) + .catch(INACTIVE_CLIENTS.disconnect), +}) + +const webSchema = z.object({ + static_path: z + .literal(`${smpStatePath}/www`) + .optional() + .catch(WEB.static_path), + http: z.literal(WEB.http).catch(WEB.http), + https: z.any().optional().catch(undefined), + cert: z.any().optional().catch(undefined), + key: z.any().optional().catch(undefined), +}) + const shape = z.object({ - INFORMATION: z.object({ - source_code: z - .literal(INFORMATION.source_code) - .catch(INFORMATION.source_code), - }), - STORE_LOG: z.object({ - enable: z.enum(['on', 'off']).catch(STORE_LOG.enable), - store_queues: z - .literal(STORE_LOG.store_queues) - .catch(STORE_LOG.store_queues), - store_messages: z - .literal(STORE_LOG.store_messages) - .catch(STORE_LOG.store_messages), - restore_messages: z.enum(['on', 'off']).catch(STORE_LOG.restore_messages), - expire_messages_days: z - .number() - .int() - .nonnegative() - .optional() - .catch(STORE_LOG.expire_messages_days), - expire_messages_on_start: z - .literal(STORE_LOG.expire_messages_on_start) - .catch(STORE_LOG.expire_messages_on_start), - expire_ntfs_hours: z - .number() - .int() - .nonnegative() - .optional() - .catch(STORE_LOG.expire_ntfs_hours), - log_stats: z.enum(['on', 'off']).catch(STORE_LOG.log_stats), - prometheus_interval: z - .number() - .int() - .nonnegative() - .optional() - .catch(STORE_LOG.prometheus_interval), - }), - AUTH: z.object({ - new_queues: z.literal(AUTH.new_queues).catch(AUTH.new_queues), - create_password: z.string(), - control_port_admin_password: z - .string() - .optional() - .catch(AUTH.control_port_admin_password), - control_port_user_password: z - .string() - .optional() - .catch(AUTH.control_port_user_password), - }), - TRANSPORT: z.object({ - host: z.string(), - port: z.literal(TRANSPORT.port).catch(TRANSPORT.port), - log_tls_errors: z - .literal(TRANSPORT.log_tls_errors) - .catch(TRANSPORT.log_tls_errors), - websockets: z.literal(TRANSPORT.websockets).catch(TRANSPORT.websockets), - control_port: z - .literal(TRANSPORT.control_port) - .catch(TRANSPORT.control_port), - }), - PROXY: z.object({ - socks_proxy: z.literal(PROXY.socks_proxy).catch(PROXY.socks_proxy), - client_concurrency: z - .number() - .int() - .nonnegative() - .catch(PROXY.client_concurrency), - }), - INACTIVE_CLIENTS: z.object({ - disconnect: z - .literal(INACTIVE_CLIENTS.disconnect) - .catch(INACTIVE_CLIENTS.disconnect), - }), - WEB: z.object({ - static_path: z - .literal(`${smpStatePath}/www`) - .optional() - .catch(WEB.static_path), - http: z.literal(WEB.http).catch(WEB.http), - https: z.any().optional().catch(undefined), - cert: z.any().optional().catch(undefined), - key: z.any().optional().catch(undefined), - }), + INFORMATION: informationSchema.catch(() => informationSchema.parse({})), + STORE_LOG: storeLogSchema.catch(() => storeLogSchema.parse({})), + AUTH: authSchema.catch(() => authSchema.parse({})), + TRANSPORT: transportSchema.catch(() => transportSchema.parse({})), + PROXY: proxySchema.catch(() => proxySchema.parse({})), + INACTIVE_CLIENTS: inactiveClientsSchema.catch(() => + inactiveClientsSchema.parse({}), + ), + WEB: webSchema.catch(() => webSchema.parse({})), }) export type SmpServerConfig = z.infer diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index f2bd14a..a374f5e 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v_6_4_5_2_b0 as current } from './v6.4.5.2.b0' +export { v_6_4_5_3_b0 as current } from './v6.4.5.3.b0' export const other = [] diff --git a/startos/install/versions/v6.4.5.2.b0.ts b/startos/install/versions/v6.4.5.3.b0.ts similarity index 96% rename from startos/install/versions/v6.4.5.2.b0.ts rename to startos/install/versions/v6.4.5.3.b0.ts index c278d82..415518e 100644 --- a/startos/install/versions/v6.4.5.2.b0.ts +++ b/startos/install/versions/v6.4.5.3.b0.ts @@ -1,15 +1,15 @@ -import { VersionInfo, IMPOSSIBLE, YAML, utils } from '@start9labs/start-sdk' +import { IMPOSSIBLE, utils, VersionInfo, YAML } from '@start9labs/start-sdk' import { execFile } from 'child_process' import { readdir, readFile, rm } from 'fs/promises' +import { join } from 'path' +import { fileServerIni } from '../../fileModels/fileServer.ini' import { smpServerIni } from '../../fileModels/smpServer.ini' import { smpConfigDefaults, xftpConfigDefaults } from '../../utils' -import { fileServerIni } from '../../fileModels/fileServer.ini' -import { join } from 'path' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_6_4_5_2_b0 = VersionInfo.of({ - version: '6.4.5:2-beta.0', +export const v_6_4_5_3_b0 = VersionInfo.of({ + version: '6.4.5:3-beta.0', releaseNotes: { en_US: 'Update to StartOS SDK beta.59', es_ES: 'Actualización a StartOS SDK beta.59', From 2633328063da8b378dca37afa0c13814217faf85 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Mon, 16 Mar 2026 12:31:01 -0600 Subject: [PATCH 28/35] Update start-sdk to beta.60 Co-Authored-By: Claude Opus 4.6 (1M context) --- package-lock.json | 22 +++++++++---------- package.json | 2 +- startos/install/versions/index.ts | 2 +- .../{v6.4.5.3.b0.ts => v6.4.5.3.b1.ts} | 14 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) rename startos/install/versions/{v6.4.5.3.b0.ts => v6.4.5.3.b1.ts} (91%) diff --git a/package-lock.json b/package-lock.json index 2f879dc..e09c80c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.59", + "@start9labs/start-sdk": "^0.4.0-beta.60", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.59", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.59.tgz", - "integrity": "sha512-Cp8r+bg1feNAAHyWtND8kpbfrK3crL3WS6JCp1+LJdx8OvTTGXg/2znMOoAn+J9pBbAui5CtXiJigC1OaACYVw==", + "version": "0.4.0-beta.60", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.60.tgz", + "integrity": "sha512-eAjVo+bHaY3ji/zWmXdVJ4rMP7PxTOr4VGm0TAjqgF8uyf90LsV7LI9/H+tdfI2Vp3b/Ymvhp7uoodenjqaZWg==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -183,21 +183,21 @@ } }, "node_modules/tldts": { - "version": "7.0.25", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.25.tgz", - "integrity": "sha512-keinCnPbwXEUG3ilrWQZU+CqcTTzHq9m2HhoUP2l7Xmi8l1LuijAXLpAJ5zRW+ifKTNscs4NdCkfkDCBYm352w==", + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.26.tgz", + "integrity": "sha512-WiGwQjr0qYdNNG8KpMKlSvpxz652lqa3Rd+/hSaDcY4Uo6SKWZq2LAF+hsAhUewTtYhXlorBKgNF3Kk8hnjGoQ==", "license": "MIT", "dependencies": { - "tldts-core": "^7.0.25" + "tldts-core": "^7.0.26" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.25", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.25.tgz", - "integrity": "sha512-ZjCZK0rppSBu7rjHYDYsEaMOIbbT+nWF57hKkv4IUmZWBNrBWBOjIElc0mKRgLM8bm7x/BBlof6t2gi/Oq/Asw==", + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.26.tgz", + "integrity": "sha512-5WJ2SqFsv4G2Dwi7ZFVRnz6b2H1od39QME1lc2y5Ew3eWiZMAeqOAfWpRP9jHvhUl881406QtZTODvjttJs+ew==", "license": "MIT" }, "node_modules/tr46": { diff --git a/package.json b/package.json index 456faa2..4ef291c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.59", + "@start9labs/start-sdk": "^0.4.0-beta.60", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index a374f5e..52d5cbb 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v_6_4_5_3_b0 as current } from './v6.4.5.3.b0' +export { v_6_4_5_3_b1 as current } from './v6.4.5.3.b1' export const other = [] diff --git a/startos/install/versions/v6.4.5.3.b0.ts b/startos/install/versions/v6.4.5.3.b1.ts similarity index 91% rename from startos/install/versions/v6.4.5.3.b0.ts rename to startos/install/versions/v6.4.5.3.b1.ts index 415518e..d9e7dbc 100644 --- a/startos/install/versions/v6.4.5.3.b0.ts +++ b/startos/install/versions/v6.4.5.3.b1.ts @@ -8,14 +8,14 @@ import { smpConfigDefaults, xftpConfigDefaults } from '../../utils' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_6_4_5_3_b0 = VersionInfo.of({ - version: '6.4.5:3-beta.0', +export const v_6_4_5_3_b1 = VersionInfo.of({ + version: '6.4.5:3-beta.1', releaseNotes: { - en_US: 'Update to StartOS SDK beta.59', - es_ES: 'Actualización a StartOS SDK beta.59', - de_DE: 'Update auf StartOS SDK beta.59', - pl_PL: 'Aktualizacja do StartOS SDK beta.59', - fr_FR: 'Mise à jour vers StartOS SDK beta.59', + en_US: 'Update to StartOS SDK beta.60', + es_ES: 'Actualización a StartOS SDK beta.60', + de_DE: 'Update auf StartOS SDK beta.60', + pl_PL: 'Aktualizacja do StartOS SDK beta.60', + fr_FR: 'Mise à jour vers StartOS SDK beta.60', }, migrations: { up: async ({ effects }) => { From 8e902a3387ac7453bfad264c0fabaaeb45103ac5 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Mon, 16 Mar 2026 23:23:20 -0600 Subject: [PATCH 29/35] Update start-sdk to beta.61 Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 5 +++++ package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..023d761 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +## How the upstream version is pulled +- dockerTags in `startos/manifest/index.ts`: + - `simplexchat/smp-server:v` + - `simplexchat/xftp-server:v` +- Both must be updated together. diff --git a/package-lock.json b/package-lock.json index e09c80c..6fcff14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.60", + "@start9labs/start-sdk": "^0.4.0-beta.61", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.60", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.60.tgz", - "integrity": "sha512-eAjVo+bHaY3ji/zWmXdVJ4rMP7PxTOr4VGm0TAjqgF8uyf90LsV7LI9/H+tdfI2Vp3b/Ymvhp7uoodenjqaZWg==", + "version": "0.4.0-beta.61", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.61.tgz", + "integrity": "sha512-mrrn4utBkRAec4hpm0psjQ46XlhiepwPYhwpf7GrMwz4c5YvHWfSPeEmLbSGWOmooYdVD0WqGcCnsVBq2EhVYA==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/package.json b/package.json index 4ef291c..5b59a16 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.60", + "@start9labs/start-sdk": "^0.4.0-beta.61", "tldts": "^7.0.19" }, "devDependencies": { From b0ec515549601f53f2177862c5edada68ad7f881 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 17 Mar 2026 22:57:33 -0600 Subject: [PATCH 30/35] Update docsUrls Co-Authored-By: Claude Opus 4.6 (1M context) --- startos/manifest/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts index 4ffd609..268dc2e 100644 --- a/startos/manifest/index.ts +++ b/startos/manifest/index.ts @@ -8,9 +8,9 @@ export const manifest = setupManifest({ packageRepo: 'https://github.com/Start9Labs/simplex-startos/tree/update/040', upstreamRepo: 'https://github.com/simplex-chat/simplexmq/', marketingUrl: 'https://simplex.chat/', - docsUrls: ['https://simplex.chat/docs/guide/readme.html'], donationUrl: 'https://github.com/simplex-chat/simplex-chat#help-us-with-donations', + docsUrls: ['https://github.com/simplex-chat/simplex-chat/tree/stable/docs'], description: i18n.description, volumes: [ 'smp-configs', From a5165106c598214b3f0a4c676620afae61543d72 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 19 Mar 2026 18:18:11 -0600 Subject: [PATCH 31/35] Update start-sdk to beta.62 Co-Authored-By: Claude Opus 4.6 (1M context) --- package-lock.json | 71 ++++++++++++++++++++++++++++++++++++++++++++--- package.json | 2 +- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6fcff14..3f99a2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.61", + "@start9labs/start-sdk": "^0.4.0-beta.62", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.61", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.61.tgz", - "integrity": "sha512-mrrn4utBkRAec4hpm0psjQ46XlhiepwPYhwpf7GrMwz4c5YvHWfSPeEmLbSGWOmooYdVD0WqGcCnsVBq2EhVYA==", + "version": "0.4.0-beta.62", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.62.tgz", + "integrity": "sha512-D1e30kr7Faojxdakwi5FZf3vSVhjaPHXwFG5a6WY2t5cJL1TG+Gw8p9RrCj9Ttvgc63yxP/CrF1xPtEGFm9vSA==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -60,6 +60,7 @@ "@noble/hashes": "^1.7.2", "@types/ini": "^4.1.1", "deep-equality-data-structures": "^2.0.0", + "fast-xml-parser": "^5.5.6", "ini": "^5.0.0", "isomorphic-fetch": "^3.0.0", "mime": "^4.0.7", @@ -103,6 +104,41 @@ "object-hash": "^3.0.0" } }, + "node_modules/fast-xml-builder": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz", + "integrity": "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", + "integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "fast-xml-builder": "^1.1.4", + "path-expression-matcher": "^1.1.3", + "strnum": "^2.2.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/ini": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", @@ -166,6 +202,21 @@ "node": ">= 6" } }, + "node_modules/path-expression-matcher": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", + "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/prettier": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", @@ -182,6 +233,18 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/strnum": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", + "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/tldts": { "version": "7.0.26", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.26.tgz", diff --git a/package.json b/package.json index 5b59a16..0f33051 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.61", + "@start9labs/start-sdk": "^0.4.0-beta.62", "tldts": "^7.0.19" }, "devDependencies": { From 3cfb7cf30bbc3e3f703cad714cfb818350b6bf79 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 19 Mar 2026 18:44:59 -0600 Subject: [PATCH 32/35] Bump version to 6.4.5:3-beta.2 Co-Authored-By: Claude Opus 4.6 (1M context) --- startos/install/versions/index.ts | 2 +- startos/install/versions/{v6.4.5.3.b1.ts => v6.4.5.3.b2.ts} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename startos/install/versions/{v6.4.5.3.b1.ts => v6.4.5.3.b2.ts} (98%) diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index 52d5cbb..6b8c82c 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v_6_4_5_3_b1 as current } from './v6.4.5.3.b1' +export { v_6_4_5_3_b2 as current } from './v6.4.5.3.b2' export const other = [] diff --git a/startos/install/versions/v6.4.5.3.b1.ts b/startos/install/versions/v6.4.5.3.b2.ts similarity index 98% rename from startos/install/versions/v6.4.5.3.b1.ts rename to startos/install/versions/v6.4.5.3.b2.ts index d9e7dbc..811b41f 100644 --- a/startos/install/versions/v6.4.5.3.b1.ts +++ b/startos/install/versions/v6.4.5.3.b2.ts @@ -8,8 +8,8 @@ import { smpConfigDefaults, xftpConfigDefaults } from '../../utils' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_6_4_5_3_b1 = VersionInfo.of({ - version: '6.4.5:3-beta.1', +export const v_6_4_5_3_b2 = VersionInfo.of({ + version: '6.4.5:3-beta.2', releaseNotes: { en_US: 'Update to StartOS SDK beta.60', es_ES: 'Actualización a StartOS SDK beta.60', From 99571e9a8d8dddcfeb88d5f3c9c2778d0c975aa7 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Mon, 23 Mar 2026 13:29:27 -0600 Subject: [PATCH 33/35] Bump SDK to beta.65, version bump, fix README Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 13 +++-- package-lock.json | 50 +++++++++---------- package.json | 2 +- startos/install/versions/index.ts | 2 +- .../{v6.4.5.3.b2.ts => v6.4.5.3.b3.ts} | 4 +- 5 files changed, 35 insertions(+), 36 deletions(-) rename startos/install/versions/{v6.4.5.3.b2.ts => v6.4.5.3.b3.ts} (98%) diff --git a/README.md b/README.md index e805950..e571f57 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,10 @@ This package runs **2 containers**: | Container | Image | Purpose | |-----------|-------|---------| -| smp | `simplexchat/smp-server:v6.4.5` | SimpleX Messaging Protocol server | -| xftp | `simplexchat/xftp-server:v6.4.5` | SimpleX File Transfer Protocol server | +| smp | `simplexchat/smp-server` | SimpleX Messaging Protocol server | +| xftp | `simplexchat/xftp-server` | SimpleX File Transfer Protocol server | -**Note:** Currently x86_64 only. ARM64 support coming after v6.4.5. +**Note:** Currently x86_64 only. ARM64 support planned for a future release. ## Volumes @@ -120,7 +120,7 @@ Your messages and files will now route through your own servers. ## Limitations -1. **x86_64 only**: ARM64 (aarch64) not yet supported in this version +1. **x86_64 only**: ARM64 (aarch64) not yet supported 2. **No web UI**: Server administration is config-file only 3. **Fixed storage quota**: XFTP limited to 10GB (requires config edit to change) 4. **No stats dashboard**: Prometheus metrics available but not exposed @@ -139,15 +139,14 @@ Your messages and files will now route through your own servers. ```yaml package_id: simplex -upstream_version: 6.4.5 arch: x86_64 only containers: - name: smp - image: simplexchat/smp-server:v6.4.5 + image: simplexchat/smp-server ports: [5223, 5224] - name: xftp - image: simplexchat/xftp-server:v6.4.5 + image: simplexchat/xftp-server ports: [5225, 5226] volumes: diff --git a/package-lock.json b/package-lock.json index 3f99a2c..4a7c60c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.62", + "@start9labs/start-sdk": "^0.4.0-beta.65", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.62", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.62.tgz", - "integrity": "sha512-D1e30kr7Faojxdakwi5FZf3vSVhjaPHXwFG5a6WY2t5cJL1TG+Gw8p9RrCj9Ttvgc63yxP/CrF1xPtEGFm9vSA==", + "version": "0.4.0-beta.65", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.65.tgz", + "integrity": "sha512-cnphP49d7ZNY3z1wdtnMD/Zr3yX26er2tJQuCRJ3xmIkfrhxoPo/wZgOfkmZRF4lNtPQmHcoMGqtgyz1TLrCew==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", @@ -120,9 +120,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.5.7", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.7.tgz", - "integrity": "sha512-LteOsISQ2GEiDHZch6L9hB0+MLoYVLToR7xotrzU0opCICBkxOPgHAy1HxAvtxfJNXDJpgAsQN30mkrfpO2Prg==", + "version": "5.5.9", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.5.9.tgz", + "integrity": "sha512-jldvxr1MC6rtiZKgrFnDSvT8xuH+eJqxqOBThUVjYrxssYTo1avZLGql5l0a0BAERR01CadYzZ83kVEkbyDg+g==", "funding": [ { "type": "github", @@ -132,8 +132,8 @@ "license": "MIT", "dependencies": { "fast-xml-builder": "^1.1.4", - "path-expression-matcher": "^1.1.3", - "strnum": "^2.2.0" + "path-expression-matcher": "^1.2.0", + "strnum": "^2.2.2" }, "bin": { "fxparser": "src/cli/cli.js" @@ -203,9 +203,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", - "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.2.0.tgz", + "integrity": "sha512-DwmPWeFn+tq7TiyJ2CxezCAirXjFxvaiD03npak3cRjlP9+OjTmSy1EpIrEbh+l6JgUundniloMLDQ/6VTdhLQ==", "funding": [ { "type": "github", @@ -234,9 +234,9 @@ } }, "node_modules/strnum": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.1.tgz", - "integrity": "sha512-BwRvNd5/QoAtyW1na1y1LsJGQNvRlkde6Q/ipqqEaivoMdV+B1OMOTVdwR+N/cwVUcIt9PYyHmV8HyexCZSupg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.2.tgz", + "integrity": "sha512-DnR90I+jtXNSTXWdwrEy9FakW7UX+qUZg28gj5fk2vxxl7uS/3bpI4fjFYVmdK9etptYBPNkpahuQnEwhwECqA==", "funding": [ { "type": "github", @@ -246,21 +246,21 @@ "license": "MIT" }, "node_modules/tldts": { - "version": "7.0.26", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.26.tgz", - "integrity": "sha512-WiGwQjr0qYdNNG8KpMKlSvpxz652lqa3Rd+/hSaDcY4Uo6SKWZq2LAF+hsAhUewTtYhXlorBKgNF3Kk8hnjGoQ==", + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", + "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", "license": "MIT", "dependencies": { - "tldts-core": "^7.0.26" + "tldts-core": "^7.0.27" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.26", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.26.tgz", - "integrity": "sha512-5WJ2SqFsv4G2Dwi7ZFVRnz6b2H1od39QME1lc2y5Ew3eWiZMAeqOAfWpRP9jHvhUl881406QtZTODvjttJs+ew==", + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", + "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", "license": "MIT" }, "node_modules/tr46": { @@ -313,9 +313,9 @@ } }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 0f33051..15cd12c 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.62", + "@start9labs/start-sdk": "^0.4.0-beta.65", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts index 6b8c82c..010b402 100644 --- a/startos/install/versions/index.ts +++ b/startos/install/versions/index.ts @@ -1,2 +1,2 @@ -export { v_6_4_5_3_b2 as current } from './v6.4.5.3.b2' +export { v_6_4_5_3_b3 as current } from './v6.4.5.3.b3' export const other = [] diff --git a/startos/install/versions/v6.4.5.3.b2.ts b/startos/install/versions/v6.4.5.3.b3.ts similarity index 98% rename from startos/install/versions/v6.4.5.3.b2.ts rename to startos/install/versions/v6.4.5.3.b3.ts index 811b41f..b79a8f8 100644 --- a/startos/install/versions/v6.4.5.3.b2.ts +++ b/startos/install/versions/v6.4.5.3.b3.ts @@ -8,8 +8,8 @@ import { smpConfigDefaults, xftpConfigDefaults } from '../../utils' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_6_4_5_3_b2 = VersionInfo.of({ - version: '6.4.5:3-beta.2', +export const v_6_4_5_3_b3 = VersionInfo.of({ + version: '6.4.5:3-beta.3', releaseNotes: { en_US: 'Update to StartOS SDK beta.60', es_ES: 'Actualización a StartOS SDK beta.60', From 0e5e560736be82d78fcc761a91561c53f0875734 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 24 Mar 2026 17:51:20 -0600 Subject: [PATCH 34/35] sdk beta.66 + version bump --- package-lock.json | 8 +- package.json | 2 +- startos/index.ts | 2 +- startos/init/index.ts | 2 +- startos/init/initServers.ts | 94 +++++++++---------- startos/install/versions/index.ts | 2 - .../versionGraph.ts => versions/index.ts} | 6 +- .../v6.4.5.3.b4.ts} | 10 +- 8 files changed, 62 insertions(+), 64 deletions(-) delete mode 100644 startos/install/versions/index.ts rename startos/{install/versionGraph.ts => versions/index.ts} (55%) rename startos/{install/versions/v6.4.5.3.b3.ts => versions/v6.4.5.3.b4.ts} (93%) diff --git a/package-lock.json b/package-lock.json index 4a7c60c..4e1fa5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "hello-world-startos", "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.65", + "@start9labs/start-sdk": "^0.4.0-beta.66", "tldts": "^7.0.19" }, "devDependencies": { @@ -50,9 +50,9 @@ } }, "node_modules/@start9labs/start-sdk": { - "version": "0.4.0-beta.65", - "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.65.tgz", - "integrity": "sha512-cnphP49d7ZNY3z1wdtnMD/Zr3yX26er2tJQuCRJ3xmIkfrhxoPo/wZgOfkmZRF4lNtPQmHcoMGqtgyz1TLrCew==", + "version": "0.4.0-beta.66", + "resolved": "https://registry.npmjs.org/@start9labs/start-sdk/-/start-sdk-0.4.0-beta.66.tgz", + "integrity": "sha512-fsp7d3nqFapLohaKbNk1frW8fbVmjr2CBehVXVUk6JS/LSerogqlAYUFE5LKp5vJoTkuSnZbF5np4m+oxCmwCw==", "license": "MIT", "dependencies": { "@iarna/toml": "^3.0.0", diff --git a/package.json b/package.json index 15cd12c..909bb3e 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.65", + "@start9labs/start-sdk": "^0.4.0-beta.66", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/startos/index.ts b/startos/index.ts index 5bc2685..7af589b 100644 --- a/startos/index.ts +++ b/startos/index.ts @@ -7,5 +7,5 @@ export { init, uninit } from './init' export { actions } from './actions' import { buildManifest } from '@start9labs/start-sdk' import { manifest as sdkManifest } from './manifest' -import { versionGraph } from './install/versionGraph' +import { versionGraph } from './versions' export const manifest = buildManifest(versionGraph, sdkManifest) diff --git a/startos/init/index.ts b/startos/init/index.ts index 8728100..f62ec06 100644 --- a/startos/init/index.ts +++ b/startos/init/index.ts @@ -1,7 +1,7 @@ import { sdk } from '../sdk' import { setDependencies } from '../dependencies' import { setInterfaces } from '../interfaces' -import { versionGraph } from '../install/versionGraph' +import { versionGraph } from '../versions' import { actions } from '../actions' import { restoreInit } from '../backups' import { initServers } from './initServers' diff --git a/startos/init/initServers.ts b/startos/init/initServers.ts index fd69f80..80d705a 100644 --- a/startos/init/initServers.ts +++ b/startos/init/initServers.ts @@ -1,7 +1,6 @@ import { utils } from '@start9labs/start-sdk' import { sdk } from '../sdk' import { - smpConfigDefaults, smpMounts, xftpConfigDefaults, xftpFilePath, @@ -11,54 +10,55 @@ import { smpServerIni } from '../fileModels/smpServer.ini' import { fileServerIni } from '../fileModels/fileServer.ini' export const initServers = sdk.setupOnInit(async (effects, kind) => { - if (kind !== 'install') return + if (kind === 'install') { + const create_password = utils.getDefaultString({ + charset: 'a-z,A-Z,1-9,!,$,%,&,*', + len: 21, + }) - const create_password = utils.getDefaultString({ - charset: 'a-z,A-Z,1-9,!,$,%,&,*', - len: 21, - }) + await sdk.SubContainer.withTemp( + effects, + { imageId: 'smp' }, + smpMounts, + 'init-smp', + async (sub) => { + await sub.execFail([ + 'smp-server', + 'init', + '-y', + '-l', + '--password', + create_password, + ]) - await sdk.SubContainer.withTemp( - effects, - { imageId: 'smp' }, - smpMounts, - 'init-smp', - async (sub) => { - await sub.execFail([ - 'smp-server', - 'init', - '-y', - '-l', - '--password', - create_password, - ]) + await smpServerIni.merge(effects, { + AUTH: { create_password }, + }) + }, + ) - await smpServerIni.write(effects, { - ...smpConfigDefaults, - AUTH: { ...smpConfigDefaults.AUTH, create_password }, - }) - }, - ) + await sdk.SubContainer.withTemp( + effects, + { imageId: 'xftp' }, + xftpMounts, + 'init-xftp', + async (sub) => { + await sub.execFail([ + 'xftp-server', + 'init', + '-p', + xftpFilePath, + '-q', + xftpConfigDefaults.FILES.storage_quota, + ]) - await sdk.SubContainer.withTemp( - effects, - { imageId: 'xftp' }, - xftpMounts, - 'init-xftp', - async (sub) => { - await sub.execFail([ - 'xftp-server', - 'init', - '-p', - xftpFilePath, - '-q', - xftpConfigDefaults.FILES.storage_quota, - ]) - - await fileServerIni.write(effects, { - ...xftpConfigDefaults, - AUTH: { ...xftpConfigDefaults.AUTH, create_password }, - }) - }, - ) + await fileServerIni.merge(effects, { + AUTH: { create_password }, + }) + }, + ) + } else { + await smpServerIni.merge(effects, {}) + await fileServerIni.merge(effects, {}) + } }) diff --git a/startos/install/versions/index.ts b/startos/install/versions/index.ts deleted file mode 100644 index 010b402..0000000 --- a/startos/install/versions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { v_6_4_5_3_b3 as current } from './v6.4.5.3.b3' -export const other = [] diff --git a/startos/install/versionGraph.ts b/startos/versions/index.ts similarity index 55% rename from startos/install/versionGraph.ts rename to startos/versions/index.ts index 5a4fad7..15223ef 100644 --- a/startos/install/versionGraph.ts +++ b/startos/versions/index.ts @@ -1,7 +1,7 @@ import { VersionGraph } from '@start9labs/start-sdk' -import { current, other } from './versions' +import { v_6_4_5_3_b4 } from './v6.4.5.3.b4' export const versionGraph = VersionGraph.of({ - current, - other, + current: v_6_4_5_3_b4, + other: [], }) diff --git a/startos/install/versions/v6.4.5.3.b3.ts b/startos/versions/v6.4.5.3.b4.ts similarity index 93% rename from startos/install/versions/v6.4.5.3.b3.ts rename to startos/versions/v6.4.5.3.b4.ts index b79a8f8..4135381 100644 --- a/startos/install/versions/v6.4.5.3.b3.ts +++ b/startos/versions/v6.4.5.3.b4.ts @@ -2,14 +2,14 @@ import { IMPOSSIBLE, utils, VersionInfo, YAML } from '@start9labs/start-sdk' import { execFile } from 'child_process' import { readdir, readFile, rm } from 'fs/promises' import { join } from 'path' -import { fileServerIni } from '../../fileModels/fileServer.ini' -import { smpServerIni } from '../../fileModels/smpServer.ini' -import { smpConfigDefaults, xftpConfigDefaults } from '../../utils' +import { fileServerIni } from '../fileModels/fileServer.ini' +import { smpServerIni } from '../fileModels/smpServer.ini' +import { smpConfigDefaults, xftpConfigDefaults } from '../utils' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_6_4_5_3_b3 = VersionInfo.of({ - version: '6.4.5:3-beta.3', +export const v_6_4_5_3_b4 = VersionInfo.of({ + version: '6.4.5:3-beta.4', releaseNotes: { en_US: 'Update to StartOS SDK beta.60', es_ES: 'Actualización a StartOS SDK beta.60', From 85dbeb5dc0d849455c8bd8ccc2425f7dab1fb73c Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Tue, 31 Mar 2026 18:15:35 -0600 Subject: [PATCH 35/35] =?UTF-8?q?Release=206.4.5:3=20=E2=80=94=20SDK=201.0?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- package.json | 2 +- startos/manifest/index.ts | 2 +- startos/versions/index.ts | 4 ++-- startos/versions/{v6.4.5.3.b4.ts => v6.4.5.3.ts} | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename startos/versions/{v6.4.5.3.b4.ts => v6.4.5.3.ts} (98%) diff --git a/package.json b/package.json index 909bb3e..2e48448 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "check": "tsc --noEmit" }, "dependencies": { - "@start9labs/start-sdk": "^0.4.0-beta.66", + "@start9labs/start-sdk": "1.0.0", "tldts": "^7.0.19" }, "devDependencies": { diff --git a/startos/manifest/index.ts b/startos/manifest/index.ts index 268dc2e..94bb939 100644 --- a/startos/manifest/index.ts +++ b/startos/manifest/index.ts @@ -5,7 +5,7 @@ export const manifest = setupManifest({ id: 'simplex', title: 'SimpleX Server', license: 'MIT', - packageRepo: 'https://github.com/Start9Labs/simplex-startos/tree/update/040', + packageRepo: 'https://github.com/Start9Labs/simplex-startos', upstreamRepo: 'https://github.com/simplex-chat/simplexmq/', marketingUrl: 'https://simplex.chat/', donationUrl: diff --git a/startos/versions/index.ts b/startos/versions/index.ts index 15223ef..c37e7b4 100644 --- a/startos/versions/index.ts +++ b/startos/versions/index.ts @@ -1,7 +1,7 @@ import { VersionGraph } from '@start9labs/start-sdk' -import { v_6_4_5_3_b4 } from './v6.4.5.3.b4' +import { v_6_4_5_3 } from './v6.4.5.3' export const versionGraph = VersionGraph.of({ - current: v_6_4_5_3_b4, + current: v_6_4_5_3, other: [], }) diff --git a/startos/versions/v6.4.5.3.b4.ts b/startos/versions/v6.4.5.3.ts similarity index 98% rename from startos/versions/v6.4.5.3.b4.ts rename to startos/versions/v6.4.5.3.ts index 4135381..21f9520 100644 --- a/startos/versions/v6.4.5.3.b4.ts +++ b/startos/versions/v6.4.5.3.ts @@ -8,8 +8,8 @@ import { smpConfigDefaults, xftpConfigDefaults } from '../utils' // NOTE, adding passwords to xftp server addresses. Previous addresses are less secure and expected to break. -export const v_6_4_5_3_b4 = VersionInfo.of({ - version: '6.4.5:3-beta.4', +export const v_6_4_5_3 = VersionInfo.of({ + version: '6.4.5:3', releaseNotes: { en_US: 'Update to StartOS SDK beta.60', es_ES: 'Actualización a StartOS SDK beta.60',