cxpher
v2.2.3
Published
Agentics Package Manager — Encrypted native binary compiler + package manager for JavaScript
Maintainers
Readme
cXpher · The Agentics Package Manager
Just cXpher it.
The safest way to ship JavaScript. Full stop. The only JavaScript toolchain that ships encrypted, native binaries with multi-layer runtime protection, debugger detection, and zero-disk source delivery. Package manager. Bundler. Encryptor. Compiler. One tool.
What is cXpher?
cXpher is a complete JavaScript toolchain that does what no other tool on the market does: it takes your JavaScript or TypeScript project, bundles it, encrypts the source, generates a native binary with the encrypted payload embedded inside, and compiles it into a single executable wrapped in a defense-in-depth runtime envelope. Your source code never reaches disk at runtime, your binary refuses to run under debuggers, and every build is cryptographically unique.
It is also a fully functional package manager with dependency resolution, a global content-addressable store, lockfile management, and parallel installation - competing directly with npm, yarn, pnpm, and bun on the package management side, while offering a compilation pipeline that none of them have and a security posture none of them attempt.
Output is a single native binary for the OS / arch of your choice - Linux, macOS, Windows, x64, x86, ARM64 or ARM32 - produced from any Node-compatible JavaScript or TypeScript source. A single cxpher compile-all produces every binary plus a generated Node CLI wrapper into dist/ in one pass. Scripts in your package.json run with a single command (cxpher <script>, yarn-style), and the interactive surface is a clack-prompts-style flow with select menus, validated text inputs, and a clean two-accent palette.
Why cXpher Exists
Every JavaScript bundler on the market produces readable output. Minification is obfuscation theatre - any competent engineer can reverse it in an afternoon. Bytecode compilation (V8 snapshots, Bun's bytecode) is equally reversible with widely available decompilers. Even tools that produce "binaries" (pkg, nexe, bun --compile) embed the source as plaintext or trivially-decompiled bytecode inside the executable.
If you're shipping a commercial Node.js application, a proprietary CLI tool, a paid API server, or an internal enterprise tool, with every other toolchain on the market, your source code is exposed. There is currently no production-grade JavaScript packaging solution that takes source protection seriously - except cXpher.
cXpher doesn't obfuscate. It doesn't minify and hope. It encrypts with modern symmetric crypto, hardens the runtime against debuggers, removes the disk surface entirely, randomises every component of every build, and wraps the result in a binary that requires real reverse-engineering effort to crack. Software-only protection has a mathematical ceiling - but cXpher pushes that ceiling as high as it will go.
The Safest Package Manager Built for JavaScript
cXpher is the only JavaScript toolchain that combines a full package manager with a binary protection stack designed by treating "what would a determined attacker do?" as a first-principles question. The result is a defense-in-depth model with layered protections at every stage of the build and runtime lifecycle.
Defense in Depth
At build time:
- Strong, industry-standard symmetric encryption with per-build cryptographically random keying material.
- Per-build randomised key handling. No two binaries built from the same source share a key, a layout, or an ordering. Bulk extraction across versions is mathematically impossible.
- Per-build randomised IVs, per-build randomised auxiliary material. Every build is unique even to itself.
- Build artifacts isolated to a unique temporary directory under the OS temp root, wiped before the binary is returned. Nothing intermediate ever sits in your project tree.
- No escape hatch to preserve intermediate build state on disk. Once the final binary is produced, every artifact from the build pipeline is destroyed.
At runtime:
- Zero-disk source delivery. The primary execution path streams decrypted source directly into the JavaScript interpreter through an in-memory channel. No filesystem entry is ever created in the primary path - your source has no path on the disk, no entry in process listings, no name to grep for.
- Native debugger-resistance on every platform. Linux, macOS, and Windows each ship platform-specific runtime checks that detect debugger attachment and exit silently. The exits are silent on purpose: an attacker cannot learn which check fired or what triggered it.
- Timing-attack detection. The runtime self-monitors execution timing against a tight budget. Single-step debugging blows the budget and the process exits silently within milliseconds.
- Key material never exists whole. The encryption key is reconstructed inside the running process only at the moment of decryption, wiped immediately after use, and never appears as a contiguous byte sequence anywhere in the binary's data sections.
- Out-of-band argument passthrough. Command-line arguments are forwarded to the wrapped script through a channel that doesn't materialise them on disk or in process metadata.
- Hardened control flow. The runtime stub is generated with opaque predicates and a non-trivial decryption flow that resists static analysis.
At distribution time:
- One command (
cxpher compile-all) produces native binaries for every supported platform and architecture in a single pass, plus a generated Node wrapper that auto-selects the right binary at install time. Ship the entire matrix as one npm package. - Compile cache disabled by default -
--cacheis opt-in. Every fresh build is a clean build with new key material unless you explicitly opt back in.
What this means in practice
A determined reverse engineer with significant time and the right tooling can eventually defeat any software-only protection. That's not specific to cXpher, that's the mathematical floor of running encrypted code on hardware you don't control. What cXpher does is push the cost of extraction from "five minutes with a tutorial" to "one to two weeks of dedicated, skilled reverse engineering effort" - which rules out 99.9% of would-be attackers, leaves only adversaries with serious budget and intent, and protects against every realistic threat short of a nation-state.
For the remaining cases - where you need a hard floor against any conceivable attacker - cXpher is the strongest building block. Combine it with server-side key fetch, hardware TEE (SGX, TrustZone, Secure Enclave), or licensed runtime checks and you have a protection model nothing in the JavaScript ecosystem can match.
How the Pipeline Works (High Level)
The compilation flow has four high-level stages:
- Bundle. Detect your project's entry point and bundle all source files and
node_modulesinto a single JavaScript blob. Self-contained, zero external requirements. - Encrypt + randomise. The bundle is wrapped with an argv-forwarding shim and encrypted with per-build random keying material. The key is decomposed across the binary in a layout that's different for every build.
- Generate native runtime. A platform-specific runtime envelope is generated, containing the encrypted payload, the key reconstruction logic, the anti-tampering checks, and the in-memory source delivery channel.
- Compile to native binary. The envelope plus payload are compiled into a standard platform-native executable (Linux, macOS, Windows; x64, x86, arm64, arm32). The binary is fully self-contained - no runtime dependency on OpenSSL, libcrypto, or any other shared library beyond libc.
The runtime stub's internal mechanics are intentionally not enumerated. They are designed to be effective, not catalogued for would-be attackers. What matters externally: the binary runs anywhere the target platform's libc runs, your source never reaches disk, and your binary refuses to run under inspection.
Standalone Mode (Node SEA)
With --standalone, cXpher uses Node.js Single Executable Applications (SEA) to embed the entire Node.js runtime into the binary alongside the encrypted payload. The result is a single file that runs on any machine without Node.js installed:
cxpher compile --standalone
# Produces a ~50-90MB binary that runs anywhere, encrypted, zero dependenciesThe SEA path runs the same protection stack as the standard path, plus eliminates the dependency on a system Node.js. No temp files, no disk I/O for the source, pure in-memory execution inside the embedded V8 runtime.
cXpher vs Everything Else
| Capability | npm | yarn | pnpm | bun | cXpher |
|---|---|---|---|---|---|
| Package management | ✓ | ✓ | ✓ | ✓ | ✓ |
| Bundling | ✗ | ✗ | ✗ | ✓ | ✓ |
| Source encryption | ✗ | ✗ | ✗ | ✗ | ✓ (strong symmetric) |
| Native binary output | ✗ | ✗ | ✗ | ✗¹ | ✓ |
| Standalone binaries | ✗ | ✗ | ✗ | ✗ | ✓ (Node SEA) |
| Per-build randomised keying | ✗ | ✗ | ✗ | ✗ | ✓ |
| Zero-disk source delivery at runtime | ✗ | ✗ | ✗ | ✗ | ✓ |
| Native debugger detection | ✗ | ✗ | ✗ | ✗ | ✓ (Linux + macOS + Windows) |
| Anti-tampering timing checks | ✗ | ✗ | ✗ | ✗ | ✓ |
| Cross-platform compilation | ✗ | ✗ | ✗ | ✗ | ✓ (10 targets) |
| Single-pass multi-target build | ✗ | ✗ | ✗ | ✗ | ✓ (compile-all) |
| Content-addressable store | ✗ | ✗ | ✓ | ✗ | ✓ |
| Hardlink installs | ✗ | ✗ | ✓ | ✗ | ✓ |
| Parallel resolution | ✗ | ✗ | ✓ | ✓ | ✓ |
| Multi-registry fallback | ✗ | ✗ | ✗ | ✗ | ✓ |
| Local package resolution (file:/link:) | ✓ | ✓ | ✓ | partial | ✓ |
| HTTP keep-alive pooling | ✗ | ✗ | ✓ | ✓ | ✓ |
| Framework auto-detection | ✗ | ✗ | ✗ | ✗ | ✓ |
¹ Bun's bun build --compile produces a binary, but the source is embedded as bytecode that can be trivially decompiled. It is not protected.
Why not just use Bun's --compile?
Bun's compile flag produces a binary that contains JavaScriptCore bytecode. Bytecode is not source protection - it is a performance optimization. Decompilers for JS bytecode exist and are actively maintained. The original source structure, variable names, and logic are recoverable in minutes by anyone with the right tool.
cXpher ships a fundamentally different class of artifact: a compiled native binary with encrypted source, per-build randomised key material that never exists whole on disk, runtime debugger detection, timing-attack defences, and a delivery channel to Node that never touches the filesystem. Recovering source from an cXpher binary requires real reverse-engineering against a hardened runtime - typically days to weeks of dedicated effort by a skilled adversary, not minutes with a decompiler.
Why not pkg or nexe?
pkg and nexe are unmaintained, do not encrypt source code, have poor Node.js version support, and embed raw JavaScript or V8 snapshots in the binary. They solve distribution (single-file output) and nothing else.
cXpher solves both distribution AND protection. And it's also your package manager.
Installation
npm install -g cxpherQuick Start
# Initialize a project (interactive)
cxpher init
# Or scaffold instantly with sensible defaults
cxpher init -y
# Install dependencies
cxpher
# Add a package
cxpher add express
# Run any script defined in package.json - yarn-style
cxpher start
cxpher test
cxpher dev
# Build (runs your build script)
cx build
# Compile to encrypted native binary
cx compile
# Compile to standalone binary (embeds Node.js)
cX compile --standalone
# Cross-compile to a 32-bit Windows machine
cX compile -w32
# Or target a specific OS / arch combo
cXpher compile --target linux-x86
cXpher compile --target win-arm64
# Cross-compile to macOS from Linux - works bare, no wrappers
cxpher compile -m # darwin x64 (Mach-O 64-bit x86_64)
cxpher compile -m --arm # darwin arm64 (Mach-O 64-bit arm64)
cxpher install-toolchain darwin-arm64 # auto-builds the cXpher darwin toolchain + MacOSX 11.3 SDK
# Build every binary plus a CLI wrapper into dist/ in one shot
cx compile-all
# Same, plus ARM64 + ARM32 for linux and win (8 total)
cx compile-all --arm
# Just refresh the dist/cli.js wrapper, no binaries
cX compile-all --cli-only
# Add a local package (no npm round-trip, ever)
cXpher add ./packages/shared
cxpher add file:../sibling-pkg
cx add link:./local-dev-pkg # symlink instead of hardlink
cX add myname@file:./other-package # explicit aliasCommands
Package Management
| Command | Description |
|---------|-------------|
| cxpher | Install all dependencies (like yarn with no args) |
| cxpher init | Initialize a new project - interactive flow with select menus for template, module system and license |
| cxpher init -y | Scaffold with defaults, skip prompts |
| cxpher init --template <tpl> | Use a template: default, cli, server, bare |
| cxpher add <pkg> | Add a registry dependency (npm → yarn → jsr → github fallback chain) |
| cxpher add -D <pkg> | Add a dev dependency |
| cxpher add <path> | Add a local package by relative or absolute path (no registry round-trip) |
| cxpher add file:<path> | Same as above, explicit protocol - hardlinks the package tree into node_modules/ |
| cxpher add link:<path> | Symlink the local package into node_modules/ instead of hardlinking. Live edits picked up instantly. |
| cxpher add portal:<path> | Alias of link: |
| cxpher add <name>@file:<path> | Local install under an explicit dependency name |
| cxpher remove <pkg> | Remove a dependency |
| cxpher install | Install all from lockfile or package.json |
| cxpher <script> | Run any script from package.json directly - yarn-style. cxpher test runs the test script, cxpher lint runs the lint script, etc. Built-in subcommands still win precedence. |
| cxpher run <script> | Explicit form of the above |
Build & Compile
| Command | Description |
|---------|-------------|
| cxpher build | Run the project's build script (like yarn build) |
| cxpher compile | Auto-detect entry, encrypt, compile to native binary |
| cxpher compile <file> | Compile a specific file |
| cxpher compile <dir> | Compile a project directory |
| cxpher compile-all (cxpher ca) | Produce 4 binaries (linux/win × x64/x86) + a generated dist/cli.js Node wrapper in one pass. Prompts to backup or wipe an existing dist/. |
| cxpher compile-all --arm | As above plus arm64 + arm32 for both linux and win (8 binaries total). |
| cxpher compile-all --cli-only | Skip every binary, only regenerate dist/cli.js using the package name from cxpher.json / package.json. |
| cxpher compiler (cxpher cp) | Interactive picker. Multi-select every architecture (incl. macOS + ARM) plus a CLI-wrapper checkbox, then choose Standard / Standalone / Plain. Missing toolchains trigger the install-or-skip prompt before any compile fires. |
| cxpher install-toolchain (cxpher it) | Auto-detect package manager and install missing cross-compilers. Interactive multiselect when no target is specified. |
| cxpher install-toolchain --all | Install every cross-compiler cxpher knows about (linux/win × x64/x86/arm64/arm32). |
| cxpher install-toolchain --arm | Install only ARM cross-compilers (linux-arm64, linux-arm32, win-arm64, win-arm32). |
| cxpher install-toolchain linux-arm32 win-x86 | Install only the named targets. |
Compile Flags
| Flag | Description |
|------|-------------|
| --out <name> | Output binary name. When omitted and a platform flag is set, the binary is suffixed -<platform>-<arch> automatically (e.g. myapp-linux-x64, myapp-win-arm32). |
| --target <os[-arch]> | Cross-compile target. Accepts plain OS (linux, darwin, win) or compound <os>-<arch> like linux-x86, win-arm64, darwin-x64, linux-arm32. The arch portion is parsed out and applied independently. |
| -l / -w / -m | Shorthand: Linux / Windows / macOS (defaults to x64) |
| -l32 / -w32 / -m32 | Shorthand: 32-bit (x86) Linux / Windows / macOS |
| -l64 / -w64 / -m64 | Explicit 64-bit shorthand - aliases of -l / -w / -m |
| --arm | ARM target. Defaults to arm64. Combine with -l / -w for cross-compile. |
| --arm64 / --arm32 | Explicit ARM 64-bit or 32-bit. |
| --arch <arch> | Target architecture independently of --target. Accepts x64 (default), x86, arm64, arm32, plus aliases (ia32, i386, i686, amd64, x86_64, aarch64, armv7, armv7l, armhf, arm). |
| --minify | Minify source before encryption |
| --static | Static linking (musl on Linux) |
| --standalone | Embed Node.js runtime for zero-dependency distribution. Host arch must match target arch - for cross-arch use the default (non---standalone) path, which supports it via gcc / mingw. |
| --no-encrypt | Output bundled JS without encryption (debugging/development) |
| --external <pkg,pkg> | Exclude packages from the bundle |
| --no-cache | Skip the build cache |
| --entry <file> | Override entry-point detection |
| --skip-build | Skip framework auto-detection and pre-build step |
32-bit (x86) toolchain requirements
To produce x86 binaries on a 64-bit host, install the 32-bit cross-toolchain:
| Distro | Install |
|--------|---------|
| Arch / Manjaro | Enable [multilib] in /etc/pacman.conf, then pacman -S lib32-gcc-libs lib32-glibc |
| Debian / Ubuntu | apt install gcc-multilib |
| Fedora / RHEL | dnf install glibc-devel.i686 libgcc.i686 |
| Windows targets | pacman -S mingw-w64-gcc (Arch) - provides both x86_64-w64-mingw32-gcc and i686-w64-mingw32-gcc |
ARM toolchain requirements
To cross-compile to ARM from an x64 host, install the matching cross-toolchain:
| Distro | Install |
|--------|---------|
| Arch / Manjaro | pacman -S aarch64-linux-gnu-gcc arm-linux-gnueabihf-gcc |
| Debian / Ubuntu | apt install gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf |
| Fedora / RHEL | dnf install gcc-aarch64-linux-gnu gcc-arm-linux-gnu |
| Windows ARM targets | Install llvm-mingw (provides aarch64-w64-mingw32-clang and armv7-w64-mingw32-clang) |
Utilities
| Command | Description |
|---------|-------------|
| cxpher info | Show project configuration and build settings |
| cxpher why <pkg> | Explain why a package is installed |
| cxpher list | List all dependencies with installed versions |
| cxpher clean | Clear caches and build artifacts |
| cxpher clean --store | Also clear the global package store |
Framework Auto-Detection
When you run cxpher compile in a framework project, cXpher automatically detects the framework and runs the appropriate build step before compiling:
| Framework | Detection | Pre-build Command |
|-----------|-----------|-------------------|
| Next.js | next.config.{js,mjs,ts} | next build → compiles .next/standalone/server.js |
| Nuxt | nuxt.config.{js,ts} | nuxt build → compiles .output/server/index.mjs |
| Vite | vite.config.{js,ts,mjs} | vite build |
| Vue CLI | @vue/cli-service in deps | vue-cli-service build |
| Create React App | react-scripts in deps | react-scripts build |
| TypeScript | tsconfig.json | tsc → compiles from configured outDir |
If your project has a custom build script in package.json, cXpher uses that instead of the default framework command.
Configuration
cXpher uses standard package.json - no custom config files needed. Drop into any npm project and it just works. Build configuration goes under the cxpher key:
{
"name": "my-app",
"version": "0.1.0",
"main": "src/index.js",
"type": "module",
"scripts": {
"start": "node src/index.js",
"compile": "cxpher compile",
"dev": "node --watch src/index.js"
},
"cxpher": {
"encrypt": true,
"minify": false,
"target": "auto",
"static": false
},
"dependencies": {},
"devDependencies": {}
}The cxpher section defines defaults that are applied whenever cxpher compile is run. Flags passed on the command line override these defaults. The cxpher key is entirely optional - cXpher works with any standard package.json out of the box.
Architecture
Content-Addressable Store
Like pnpm, cXpher maintains a global package store at ~/.cxpher/store. Packages are extracted once into the store and hardlinked into each project's node_modules. Installing the same version of a package across 100 projects consumes disk space once.
Parallel Resolution
All dependencies at each level of the tree are resolved concurrently. Combined with HTTP connection pooling (48 concurrent keep-alive sockets) and request deduplication (multiple packages requiring the same transitive dependency result in a single registry fetch), cXpher's resolution speed scales with your CPU and network, not your dependency count.
Build Cache
cXpher hashes your source tree before compilation. If the hash matches a previous build, the cached binary is returned instantly - no bundling, no encryption, no C compilation. The cache is stored at ~/.cxpher/build-cache and is keyed on both source hash and build flags (target, minify, static, standalone, encrypt).
Lockfile
cxpher.lock uses a JSON format with integrity hashes for deterministic installs. The lockfile includes a top-level integrity hash of the entire dependency tree, enabling fast skip-resolution on subsequent installs when nothing has changed. Local-spec entries store the user-supplied spec (file:./pkg) rather than the resolved absolute path, so a project can move between machines and the local references re-resolve against the new project root.
Local Packages
cxpher add skips the registry entirely when given a local path. The package's own package.json is read off disk to pull the name, version, and transitive dependencies, and the resulting tree entry is marked local: true so the installer takes the local-install path. Three protocols are recognised:
file:- copy/hardlink the local package tree intonode_modules/. Default for bare paths (./pkg,../sibling,/abs/path,~/lib).link:- create a directory symlink instead. Live edits to the source pick up immediately. Equivalent tonpm link/yarn linkbut scoped to a single dependency.portal:- alias oflink:.
The local install hardlink skips the source package's own node_modules/, .git/, and .cxpher-stored markers so consumers don't recurse into the dependency's dependency tree.
Multi-Registry Fallback
fetchPackument walks an ordered list of registries on every lookup and short-circuits on the first 200. Default chain:
https://registry.npmjs.org(npm)https://registry.yarnpkg.com(yarn - npm mirror with different caching)https://npm.jsr.io(jsr - the JS Registry)https://npm.pkg.github.com(github packages)
404 / 401 / 403 from any registry is treated as "not here, try the next one" - the chain only throws when every registry has rejected the name. A 200 response is cached against the package name (not the registry), so subsequent lookups hit the cache regardless of which registry served the packument.
Override the chain via ~/.cxpher/config.json:
{
"registry": "https://my-private-registry.example.com",
"registries": [
{ "name": "company", "url": "https://npm.internal.example.com" }
],
"scopeRegistries": {
"@my-org": "https://npm.internal.example.com"
}
}registry is the primary and prepended to the chain. registries[] is inserted before the defaults. scopeRegistries routes specific scopes (@my-org/anything) directly at a single registry, bypassing the chain.
compile-all and the Generated CLI Wrapper
compile-all produces every binary plus a generated Node script (dist/cli.js) in a single pass. The wrapper detects process.platform and process.arch at runtime and execs the matching binary out of the same directory - install one npm package that ships all four (or eight with --arm) binaries plus the wrapper as the bin entry, and the right binary runs on every platform the user installs it on.
When dist/ already exists, a select prompt asks (the same prompt is shared with cxpher compiler):
- Install alongside existing files - overwrite only the targets being built and leave everything else in
dist/untouched. - Backup current dist - copies
dist/to the next freedist_backup_NNN/(zero-padded 3-digit, based on the highest existing number in the project root), then wipesdist/and continues. Default. - Wipe and start fresh - removes
dist/without a backup.
--cli-only skips every binary and just (re)writes dist/cli.js. Useful for refreshing the wrapper after a package rename without rebuilding everything.
The wrapper's platform / arch detection covers all 10 produced targets:
| process.platform | process.arch | Binary name |
|--------------------|----------------|-------------|
| linux | x64 | <pkg>-linux-x64 |
| linux | ia32 | <pkg>-linux-x86 |
| linux | arm64 | <pkg>-linux-arm64 |
| linux | arm | <pkg>-linux-arm32 |
| win32 | x64 | <pkg>-win-x64.exe |
| win32 | ia32 | <pkg>-win-x86.exe |
| win32 | arm64 | <pkg>-win-arm64.exe |
| win32 | arm | <pkg>-win-arm32.exe |
| darwin | x64 | <pkg>-darwin-x64 |
| darwin | arm64 | <pkg>-darwin-arm64 |
Any unsupported platform/arch combination throws a clear Unsupported platform: / Unsupported architecture: error at wrapper startup instead of silently failing.
Before any binary is built, compile-all runs a toolchain preflight check against every requested target. If any cross-compilers are missing, you get a three-way prompt:
- Install missing toolchains and build all - calls
install-toolchainfor the missing targets, then continues. - Skip missing targets, build the rest - drops the targets whose toolchains aren't available and carries on with the ones that are.
- Abort - does nothing.
--yes / -y auto-picks install. Individual compile failures during the loop are warned-and-skipped rather than fatal, so one bad target never kills the rest of the batch.
install-toolchain - auto-install cross-compilers
cxpher install-toolchain detects the host OS, distro and package manager and installs the cross-compilers required for any compile target. Supported package managers:
| OS / Distro | Manager | Notes |
|-------------|---------|-------|
| Arch / Manjaro / EndeavourOS | pacman + yay / paru | Repo packages via pacman with sudo. AUR / chaotic-aur via yay/paru without sudo |
| Debian / Ubuntu | apt-get | sudo |
| Fedora / RHEL / CentOS | dnf | sudo |
| openSUSE | zypper | sudo |
| Alpine | apk | sudo |
| macOS | brew | no sudo |
| Windows | scoop → choco → winget | first found, no sudo |
Invocations:
cxpher install-toolchain- interactive. Lists every target with installed/missing flags, multiselect to pick what to install.cxpher install-toolchain --all- install every cross-compiler cxpher knows about.cxpher install-toolchain --arm- install only ARM (linux/win × arm64/arm32).cxpher install-toolchain --x86- install only 32-bit x86 (linux + win).cxpher install-toolchain --win/--linux- install all cross-compilers for that OS.cxpher install-toolchain linux-arm32 win-x86 ...- install only the named targets.cxpher install-toolchain --yes- skip the confirm prompt.
Targets with no native package on the current distro (Windows ARM on debian/fedora, macOS cross from anything) are reported with a manual-install link rather than silently skipped.
Fallback chain
Each target carries an ordered list of install steps. The installer walks the chain in order, verifies after each attempt, and only moves on if the current step fails or installs without putting the expected binary on PATH. Example: linux-arm32 on Arch tries
- Linaro precompiled binary (
arm-linux-gnueabihf-gcc13-linaro-binfrom AUR) - fast, but depends on snapshots.linaro.org being reachable. - Official ARM GNU toolchain - downloads the
arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-linux-gnueabihf.tar.xzarchive from developer.arm.com, extracts to/opt/cxpher-toolchains/arm-none-linux-gnueabihf/, and symlinks both the Arm-official names (arm-none-linux-gnueabihf-*) and the standard names (arm-linux-gnueabihf-*) into/usr/local/bin/. Reusable forlinux-arm64(tripletaarch64-none-linux-gnu) as well. - Build from source (
arm-linux-gnueabihf-gccfrom AUR) - last resort, takes 1-2 hours.
Failed steps print a one-line reason and the chain continues. If every step fails the target is reported as still-missing in the final status section.
macOS targets - fully automated from any Linux host
On a macOS host, darwin-x64 and darwin-arm64 resolve to native clang plus xcode-select -p; the install step calls xcode-select --install to trigger the Command Line Tools GUI installer.
From a Linux host, macOS cross-compile is now a one-command setup. cxpher install-toolchain darwin-x64 (or darwin-arm64, or --all) runs a fully automated build method that:
- Detects your distro and installs the build dependencies (
clang,cmake,patch,libxml2-dev/libxml2,openssl-dev/openssl-devel,zlib-dev,bash,git,curl) via the right package manager - pacman, apt, dnf, zypper, or apk. - Assembles the cXpher darwin toolchain locally against the MacOSX 11.3 SDK - 11.x is required for
arm64(Apple introduced arm64 in SDK 11.0). - Builds the cross-toolchain from source - takes 20-40 minutes on a typical workstation.
sudoinstalls the result to the standard system prefix and symlinks every wrapper into/usr/local/bin/.
Windows hosts get a clean error pointing to WSL2 Ubuntu.
Once the darwin toolchain is installed, cxpher compile -m and cxpher compile -m --arm work bare - no external wrapper, no environment dance, no manual PATH editing, no SDKROOT exports. The compile path self-locates the toolchain, sets PATH, SDKROOT, MACOSX_DEPLOYMENT_TARGET, CC, CXX, AR, RANLIB, STRIP, GOOS, CGO_ENABLED and GOARCH to the right triple-derived values internally, then spawns the matching wrapper. The toolchain install dir does not need to be on the shell's interactive PATH.
Apple dropped 32-bit Intel and 32-bit ARM support in macOS Catalina (10.15), so no darwin-x86 or darwin-arm32 target exists.
llvm-mingw post-install symlink hook
On Arch, llvm-mingw from chaotic-aur / AUR installs into /opt/llvm-mingw/bin/ which is not on $PATH. The win-arm64 / win-arm32 install steps chain into a llvm-mingw-postlink post-install method that symlinks every aarch64-w64-mingw32-* and armv7-w64-mingw32-* binary into /usr/local/bin/, so the cross-compilers are immediately resolvable after install with no manual PATH editing.
Security Model
cXpher is the strongest software-only source protection in the JavaScript ecosystem. It is not a DRM system - pure software protection has a theoretical floor that nothing can move without external trust (server-fetched keys or hardware TEE) - but it pushes that floor as high as it goes.
What cXpher protects against:
- Direct source code reading from the distributed binary.
- Trivial decompilation (the entire class of attack that defeats every other JS packager).
- Casual filesystem extraction during runtime (your source never reaches the disk in the primary execution path).
- Common debugger inspection - Linux, macOS, and Windows binaries refuse to run when a debugger is attached and exit silently.
- Single-step debugging - runtime timing checks detect single-stepping and silently exit within milliseconds.
- Bulk extraction across versions - every build is cryptographically independent.
- Casual copying of proprietary logic by employees, customers, or anyone with the binary but without serious tooling.
- Source exposure in containerised, cloud, or shared-filesystem deployments - there is no source file to expose.
What cXpher does not protect against:
- A nation-state-level adversary with unlimited reverse-engineering budget and hardware-debugger access.
- Runtime memory inspection performed at the kernel level by an attacker with root access on the host.
- Hardware-level memory dumps from a cold-boot or DMA attack.
For the realistic threat model - protecting commercial JavaScript IP against employees, customers, competitors, security researchers without unlimited budget, and the entire long tail of opportunistic extraction attempts - cXpher is the strongest answer the JavaScript ecosystem has.
This is the same security model as any compiled C, C++, Go, or Rust application - but with the added per-build-randomised, debugger-aware, disk-free runtime envelope that no other JavaScript packager attempts.
System Requirements
- Node.js ≥ 18 (≥ 20 for
--standalone) - C compiler: gcc, clang, or MSVC (for native compilation)
- Bundler: bun or esbuild (auto-detected)
- postject (only for
--standalonemode)
Platform Support
| Platform | Native Binary | Standalone | Cross-Compile |
|----------|:---:|:---:|:---:|
| Linux x64 | ✓ | ✓ | - |
| Linux x86 (32-bit) | ✓ | - ¹ | ✓ (via gcc -m32) |
| Linux ARM64 | ✓ | ✓ | ✓ (via aarch64-linux-gnu-gcc) |
| Linux ARM32 | ✓ | - ¹ | ✓ (via arm-linux-gnueabihf-gcc) |
| macOS x64 | ✓ | ✓ | ✓ (via bundled cXpher darwin toolchain + MacOSX 11.3 SDK) |
| macOS ARM64 | ✓ | ✓ | ✓ (via bundled cXpher darwin toolchain + MacOSX 11.3 SDK) |
| Windows x64 | ✓ | ✓ | ✓ (via x86_64-w64-mingw32-gcc) |
| Windows x86 (32-bit) | ✓ | - ¹ | ✓ (via i686-w64-mingw32-gcc) |
| Windows ARM64 | ✓ ² | - | ✓ (via aarch64-w64-mingw32-clang) |
| Windows ARM32 | ✓ ² | - | ✓ (via armv7-w64-mingw32-clang) |
¹ Standalone (--standalone) embeds the host Node.js binary, and Node has not shipped 32-bit Linux or 32-bit ARM builds for years. For these targets use the default encrypted-binary path - it cross-compiles cleanly via the system C toolchain.
² Windows ARM requires the llvm-mingw toolchain. The default MinGW packages from most distros only ship x86_64 / i686 targets.
License
MIT - Agentics
Built by Connor Etherington.
