[GH-ISSUE #1598] Feature Request: Cross platform scripting #2944

Closed
opened 2026-03-14 06:03:11 +03:00 by kerem · 2 comments
Owner

Originally created by @NiceGuyIT on GitHub (Aug 15, 2023).
Original GitHub issue: https://github.com/amidaware/tacticalrmm/issues/1598

Please add scripting/programming languages that are (relatively) easy to support across all platforms. Modern languages have the ability to embed files into the binary making them truly single binary applications. Deploying the application is a matter of downloading the release file, uncompressing it if necessary, and copying the binary to a location of your choosing. Tactical can use single binary applications to provide the same functionality across many platforms.

Programming and Scripting Languages

Deno is the successor to Node.js and provides a full TypeScript engine and runtime. Libraries can be imported from NPM or CDNs. Deno has a language server to assist with coding. Deno is secure by default and permissions need to be granted.

"Nu draws inspiration from projects like PowerShell, functional programming languages, and modern CLI tools." While Deno is a full programming language, Nu is an interpreted shell. The Nu shell provides many modern functions such as HTTP requests, converting to/from many formats, working with hashes, and like PowerShell, work with data objects: Dataframe and Lazyframe.

RustPython is used to provide a working proof of concept. Similar to CPython, RustPython provides a Python interpreter, and unlike CPython, distribution is with a single binary. The project is young and they do not provide any releases. SSL is required to enable pip and pip install adds binary stubs to /usr/local/bin and installs to /usr/local/lib/rustpython3.11. For this reason (and until an alternative location can be configured) RustPython is not suitable for production.

Proof of Concept

There are 3 pieces to the proof of concept. Minor details may change as I work through the full implementation.

  1. The RustPython install script for Linux and Mac computers. This downloads rustpython, installs pip and a couple necessary modules.
  2. An exec wrapper that downloads deno or nushell, downloads a script from a URL, and executes it.
  3. A server hosting your scripts, preferably in a git repo.

This setup has the following benefits.

  • The same script can be run across all platforms. Platforms being Windows, Mac and Linux. Other platforms/architectures are supported if the binary is available.
  • The script can be versioned. Instead of referring to the main branch, you can refer to a tag or branch. A develop branch can be used in QA, and once the scripts have been verified, they can be merged to the main branch or tagged for production.
    • This can be enhanced by leveraging custom fields expanded in the environmental variables.
  • Script Manager shows only 1 version of each script, not 1 version for Windows and 1 version for Linux/macOS.
  • Save time by writing the logic once in one language.

There are some down sides to this setup, some of which can be alleviated by native support in Tactical.

  • All scripts have the same wrapper. If the wrapper needs updating, all scripts will need to be updated.
  • Script parameters are passed through environmental variables. This gets messy when the variables for the script wrapper are mixed with the variables for the actual script.
  • The binaries are hosted on my server (see the install script) due to GitHub charging for LFS usage > 1GB.
  • Manually compiling RustPython across all platforms is fraught with errors. Compiling with Docker did not turn out well because then you are cross-compiling with external libraries (OpenSSL). Note: I'm not suggesting to include RustPython in this request. RustPython is used only to bootstrap the Python exec wrapper.
  • The RustPython binaries are not statically compiled and may not work on other platforms.

Proposal

Add support for Deno and Nu to Tactical. I believe this means adding two languages to the server, and support for downloading the deno and nu binaries to the endpoint. Updates can work like MeshCentral by providing the "approved" version on the server and updating for each release.

Other considerations

RustPython may be able to solve issue #1470: Install TRMM python version on Mac and Linux.

The proof of concept partially solves issue #1206: Use Git repo for custom scripts. If the URL can be programmatically determined, the provider (GitHub, GitLab, Gitea, etc) and repo can be variables in the Global settings. The branch, and hence version or "tag", can be a custom variable that is expanded in the parameters. The only thing left is path and script name. The question becomes: do you download from the provider every time, or "fetch" a new version of the script in Script Manager?

Originally created by @NiceGuyIT on GitHub (Aug 15, 2023). Original GitHub issue: https://github.com/amidaware/tacticalrmm/issues/1598 Please add scripting/programming languages that are (relatively) easy to support across all platforms. Modern languages have the ability to embed files into the binary making them truly single binary applications. Deploying the application is a matter of downloading the release file, uncompressing it if necessary, and copying the binary to a location of your choosing. Tactical can use single binary applications to provide the same functionality across many platforms. ## Programming and Scripting Languages [Deno][] is the successor to Node.js and provides a full TypeScript engine and runtime. Libraries can be imported from [NPM][npm: specifiers] or [CDNs][npm via CDNs]. Deno has a [language server][] to assist with coding. Deno is secure by default and [permissions][] need to be granted. "[Nu][] draws inspiration from projects like PowerShell, functional programming languages, and modern CLI tools." While Deno is a full programming language, Nu is an interpreted shell. The Nu shell provides many modern functions such as [HTTP requests][], converting [to/from many formats][], working with [hashes][], and like PowerShell, work with data objects: [Dataframe][] and [Lazyframe][]. [RustPython][] is used to provide a working proof of concept. Similar to CPython, RustPython provides a Python interpreter, and unlike CPython, distribution is with a single binary. The project is young and they do not provide any releases. SSL is required to enable `pip` and `pip install` adds binary stubs to `/usr/local/bin` and installs to `/usr/local/lib/rustpython3.11`. For this reason (and until an alternative location can be configured) RustPython is not suitable for production. ## Proof of Concept There are 3 pieces to the proof of concept. Minor details may change as I work through the full implementation. 1. The RustPython [install script][] for Linux and Mac computers. This downloads `rustpython`, installs `pip` and a couple necessary modules. 2. An [exec wrapper][] that downloads `deno` or `nushell`, downloads a script from a URL, and executes it. 3. A server hosting [your scripts][], preferably in a git repo. This setup has the following benefits. - The same script can be run across all platforms. Platforms being Windows, Mac and Linux. Other platforms/architectures are supported if the binary is available. - The script can be versioned. Instead of referring to the `main` branch, you can refer to a tag or branch. A `develop` branch can be used in QA, and once the scripts have been verified, they can be merged to the `main` branch or tagged for production. - This can be enhanced by leveraging custom fields expanded in the environmental variables. - Script Manager shows only 1 version of each script, not 1 version for Windows and 1 version for Linux/macOS. - Save time by writing the logic once in one language. There are some down sides to this setup, some of which can be alleviated by native support in Tactical. - All scripts have the same wrapper. If the wrapper needs updating, all scripts will need to be updated. - Script parameters are passed through environmental variables. This gets messy when the variables for the script wrapper are mixed with the variables for the actual script. - The binaries are hosted on my server (see the install script) due to GitHub charging for LFS usage > 1GB. - Manually compiling RustPython across all platforms is fraught with errors. Compiling with Docker did not turn out well because then you are cross-compiling with external libraries (OpenSSL). Note: I'm not suggesting to include RustPython in this request. RustPython is used only to bootstrap the Python exec wrapper. - The RustPython binaries are not statically compiled and may not work on other platforms. ## Proposal Add support for Deno and Nu to Tactical. I believe this means adding two languages to the server, and support for downloading the `deno` and `nu` binaries to the endpoint. Updates can work like MeshCentral by providing the "approved" version on the server and updating for each release. ## Other considerations RustPython may be able to solve issue #1470: Install TRMM python version on Mac and Linux. The proof of concept partially solves issue #1206: Use Git repo for custom scripts. If the URL can be programmatically determined, the provider (GitHub, GitLab, Gitea, etc) and repo can be variables in the Global settings. The branch, and hence version or "tag", can be a custom variable that is expanded in the parameters. The only thing left is path and script name. The question becomes: do you download from the provider every time, or "fetch" a new version of the script in Script Manager? [Deno]: https://github.com/denoland/deno [npm: specifiers]: https://deno.land/manual@v1.36.1/node/npm_specifiers [npm via CDNs]: https://deno.land/manual@v1.36.1/node/cdns [language server]: https://deno.land/manual@v1.36.1/advanced/language_server#the-language-server [permissions]: https://deno.land/manual@v1.36.1/basics/permissions [Nu]: https://github.com/nushell/nushell [HTTP requests]: https://www.nushell.sh/commands/categories/network.html [to/from many formats]: https://www.nushell.sh/commands/categories/formats.html [hashes]: https://www.nushell.sh/commands/categories/hash.html [Dataframe]: https://www.nushell.sh/commands/categories/dataframe.html [Lazyframe]: https://www.nushell.sh/commands/categories/lazyframe.html [RustPython]: https://github.com/RustPython/RustPython [install script]: https://github.com/NiceGuyIT/pimp-my-tactical/blob/main/scripts/scripts/unix-install-rustpython.sh [exec wrapper]: https://github.com/NiceGuyIT/pimp-my-tactical/blob/main/scripts/scripts/all-exec-wrapper.py [your scripts]: https://github.com/NiceGuyIT/pimp-my-tactical/tree/main/scripts/wrapper
kerem closed this issue 2026-03-14 06:03:16 +03:00
Author
Owner

@NiceGuyIT commented on GitHub (Aug 15, 2023):

Motivation

I improved the Bitdefender GravityZone install script. Then I needed to install Bitdefender on a Mac. I installed manually because the script wasn't written for a Mac.

Same story for the Mesh (re)install script. I wrote it to fix the Mesh install on Windows, then needed the same logic for a Mac. Why waste time writing logic in a programming language for a single platform?

In my journey to find a good cross-platform environment, I took a gander into using Python. That's when I wrote the Python Module Manager and discovered Apple stopped shipping Python 2 since macOS Big Sur 11. Incidentally, Python 3 is installed with Xcode. I'm not going to install Xcode, Homebrew or a Python distribution just to run scripts. I came to the conclusion that Python is not a good cross-platform solution.

Recently I wrote a File Explorer Bookmark script. It works great and solves the user's problem... until you introduce AV. Bitdefender was blocking the script, or more specifically, the heuristics engine was detecting the script as a bad actor (it opens File Explorer windows) and killing the powershell.exe process. I adding the script, the directory of the script and the hash of the script as exclusions but it didn't matter. The only solution was to add the powershell.exe process to the exclusions. That was is not an option. What's left is to rewrite the logic in a different language and add the exe as an exception. The result confines Tactical's scripting environment to an executable that lives in a directory that I control, can be excluded in AV, and malware doesn't know about (unless they already have a foothold but that's another story).

The proposal above is a workable solution that I will continue to use and improve. Since I use TypeScript outside Tactical, I will build on that knowledge and write scripts once. For all platforms. Inside and outside Tactical.

<!-- gh-comment-id:1679733637 --> @NiceGuyIT commented on GitHub (Aug 15, 2023): ## Motivation I improved the Bitdefender GravityZone install script. Then I needed to install Bitdefender on a Mac. I installed manually because the script wasn't written for a Mac. Same story for the Mesh (re)install script. I wrote it to fix the Mesh install on Windows, then needed the same logic for a Mac. Why waste time writing logic in a programming language for a single platform? In my journey to find a good cross-platform environment, I took a gander into using Python. That's when I wrote the Python Module Manager and discovered Apple stopped [shipping Python 2][] since macOS Big Sur 11. Incidentally, Python 3 is installed with Xcode. I'm not going to install Xcode, Homebrew or a Python distribution just to run scripts. I came to the conclusion that Python is not a good cross-platform solution. Recently I wrote a [File Explorer Bookmark][] script. It works great and solves the user's problem... until you introduce AV. Bitdefender was blocking the script, or more specifically, the heuristics engine was detecting the script as a bad actor (it opens File Explorer windows) and killing the `powershell.exe` process. I adding the script, the directory of the script and the hash of the script as exclusions but it didn't matter. The only solution was to add the `powershell.exe` process to the exclusions. That ~was~ is not an option. What's left is to rewrite the logic in a different language and add the `exe` as an exception. The result confines Tactical's scripting environment to an executable that lives in a directory that I control, can be excluded in AV, and malware doesn't know about (unless they already have a foothold but that's another story). The proposal above is a workable solution that I will continue to use and improve. Since I use TypeScript outside Tactical, I will build on that knowledge and write scripts once. For all platforms. Inside and outside Tactical. [shipping Python 2]: https://developer.apple.com/documentation/macos-release-notes/macos-catalina-10_15-release-notes#Scripting-Language-Runtimes [File Explorer Bookmark]: https://github.com/NiceGuyIT/pimp-my-tactical/blob/main/scripts/scripts/user-explorer-bookmarks.ps1
Author
Owner

@NiceGuyIT commented on GitHub (Mar 28, 2024):

Release v0.18.0 was released with support for Deno and Nushell.

<!-- gh-comment-id:2024205010 --> @NiceGuyIT commented on GitHub (Mar 28, 2024): [Release v0.18.0][1] was released with support for Deno and Nushell. [1]: https://github.com/amidaware/tacticalrmm/releases/tag/v0.18.0
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tacticalrmm#2944
No description provided.