Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

cjr Developer Guide

cjr is implemented in Rust. A single Cargo package provides the CLI and library. The user interface is limited to program execution, inspect, list and cache clean.

Update both language editions of the user guide when command behavior changes. Prefer the standard library and maintained dependencies for general infrastructure. Version metadata, official asset selection, cjv compatibility and cache policy belong to cjr.

The repository contains source code, ordinary tests and maintained documentation. Keep research directories, downloaded SDKs, probes, audit or acceptance reports and generated output in ignored local directories.

Report problems and suggestions through GitHub Issues. Read recent commit messages before committing and follow the existing style.

Build and test

Requirements

Use Rust 1.89 or newer, a C compiler and CMake. Windows requires MSVC. The TLS dependency aws-lc compiles native code.

cargo build --release --locked

The executable is target/release/cjr, or target/release/cjr.exe on Windows. Cargo.lock pins the dependency graph; commit it together with dependency changes.

Routine checks

cargo fmt --all -- --check
cargo clippy --all-targets --locked -- -D warnings
cargo test --all-targets --locked

Tests cover binary parsing, CLI arguments, download failures, cache lifecycle and process state. HTTP scenarios use mockito; arbitrary-input tests use proptest. Ordinary tests use local fixtures and mock services.

CI runs these checks and release builds on Windows, Linux, macOS ARM64 and Intel. A separate Linux job checks the minimum Rust version. The website job builds the landing page and documentation.

Implementation rules

  • Keep FFI and necessary unsafe operations inside the private platform module, behind safe interfaces with documented preconditions.
  • Hold StoreTransaction for cache mutations and usage registration. Keep cjv-owned files intact.
  • Use backon for retries, walkdir for directory traversal and object for binary formats.
  • Retain usage records when descendant liveness is uncertain. Preserve the application’s exit status even when cleanup fails.

Architecture

Modules

ModuleResponsibility
app, cliCommand parsing and orchestration
inspectPE, ELF and Mach-O version and dependency metadata
catalogExact SDK version and platform matching in official manifests
cjvcjv configuration, archives and managed installations
provisionDownloads, verification and selective extraction
storeImmutable runtime snapshots, usage records and cleanup
loaderHost compatibility, search paths and dependencies
processLaunch, signals, exit status and descendant supervision
platformNative operations behind safe interfaces

Launch flow

cjr reads metadata and checks host architecture and dependencies. When a runtime is needed, it selects an exact official asset with a checksum, reuses local content or downloads an archive, then extracts and publishes a runtime snapshot. After final dependency checks, it registers usage, releases the storage lock and launches the program.

The SDK version comes from PE .cjsdkv, ELF .cjmetadata or Mach-O __CJ_METADATA,__cjsdkversion. It identifies the specific SDK release used for asset selection. Parsers validate bounds and reject unknown layouts.

Mach-O chained fixups are decoded by object; inspect::macho::fixups maps rebases into the version table. Supported formats are DYLD_CHAINED_PTR_64, 64_OFFSET, ARM64E, ARM64E_USERLAND and ARM64E_USERLAND24, including authenticated pointers. Inspection decodes pointer bit fields through static file reads. Version entries accept rebase references to strings within the same file. Traversal has a work limit and checks file-to-virtual-address mappings, page boundaries and duplicate entries.

Cache lifecycle

StoreTransaction holds an exclusive storage lock for metadata, preparation, publication, usage registration and cleanup. Initial preparation of different versions is serialized. The lock is released while the application runs.

Runtimes live under runtimes/<bundle-id>/ with receipt.json and payload/. The manifest records content hashes, permissions and link targets. Publication uses a same-volume rename. Published runtimes are immutable and verified before each use.

reqwest performs downloads and backon manages retries. After SHA-256 verification, extraction selects runtime directories and license notices, rejecting unsafe paths, duplicate entries, special files and links escaping the payload.

cjv installation reads acquire .lifecycle.lock before .lifecycle.lookup.lock. Archive-derived snapshots are marked archive-verified; managed-installation snapshots are cjv-managed. Installation records identify cjv-managed sources; archive checksums establish byte-for-byte equality with official archives.

Loading and supervision

Environment changes apply only to the child process. Arguments pass directly to the process API as OsString values. Windows checks application-directory DLL shadowing, Linux checks RPATH/RUNPATH and preload conflicts, and macOS expands dyld path tokens and rejects recognized restricted programs.

Windows uses Job Objects and Linux uses a subreaper. Unix forwards termination signals and restores terminal foreground ownership. Usage records are retained while descendant exit states await confirmation; records from a previous boot on the same host can be reclaimed.

Supervision changes process-wide state and permits only one supervisor per process. Embedding hosts must coordinate process-wide signal dispositions. The storage trust boundary is the current user’s local filesystem: processes with the same UID can modify it, and hosts must separately accommodate network-filesystem locking semantics.

Documentation and website

The site follows cjv’s structure: web/ contains the landing page, while docs/user-guide/ and docs/dev-guide/ contain the two manuals. Each book uses src/ for Chinese and en/ for English.

The landing page uses React, TypeScript and Vite. Radix Tabs handles installation method switching, and react-i18next provides translations. React prerenders both languages during the build, and the browser hydrates those pages for interaction. Fonts are bundled locally. mdBook generates the manuals.

Local development

Install Node.js 22.22.2+, 24.15+ or 26+, npm and mdBook 0.5.4. The current Node.js LTS is recommended.

cd web
npm ci
npm run dev

Frontend source layout:

PathResponsibility
src/App.tsxPage composition
src/components/Navigation, installation methods, downloads, documentation links and command copying
src/config.tsRelease assets and installation commands
src/i18n/Translation types, language detection and instance initialization
src/locales/Chinese and English translations
scripts/Version loading, React prerendering, mdBook builds and navigation

Use npm run typecheck for strict TypeScript checks, npm run lint for code checks and npm run format to apply formatting. npm run test:unit runs interaction tests with Vitest and Testing Library.

To preview the Chinese user guide separately:

mdbook serve docs/user-guide

Build the full site

cd web
npm run build
npm run preview

The repository-root dist/ directory contains the landing page and all four books:

dist/
  index.html
  zh-CN/index.html
  en/index.html
  assets/
  book/user-guide/zh-CN/
  book/user-guide/en/
  book/dev-guide/zh-CN/
  book/dev-guide/en/

The default base path is /. For a subpath deployment such as GitHub Pages at /cjr/, set SITE_BASE=/cjr/ when building. Landing-page links and assets share that prefix.

Upload the whole dist/ directory. Keep node_modules/, dist/, .edgeone/ and mdBook book/ output in ignored local directories.

Update both languages and their SUMMARY.md files when editing documentation. Installation instructions and download buttons must point to published binaries or installer scripts.

Languages and versions

Landing-page messages live in web/src/locales/zh-CN.json and en.json, managed by i18next. Explicit language URLs take precedence, followed by the legacy ?lang= query, a saved preference and the browser language. Other languages fall back to English.

The build generates complete HTML at /zh-CN/ and /en/ using the same React components as the browser. Static HTML provides download and documentation links. Each language has an independent translation instance, and hydration uses the page’s locale. The root path redirects to the preferred language page when needed. Titles, descriptions, accessible labels and copy feedback use translation resources. The download version comes from Cargo.toml, matching the binary.

Both documentation languages use matching chapter filenames. The build adds language links to each chapter. Switching keeps the chapter and drops heading anchors that differ between translations. Home links preserve the selected language. Chinese documentation toolbar translations live in web/src/locales/book-zh-CN.json.

npm test runs Biome checks, component interaction tests, TypeScript checks, the complete site build and output tests. Tests cover keyboard tab navigation, clipboard success and failure feedback, bilingual hydration, language precedence, downloads and chapter switching. Add messages to both languages; missing translation keys fail the build. Use SITE_BASE=/cjr/ npm test to check a subpath deployment. CI and production deployment use this same command.

Production deployment

The website at cjr.zxilly.com runs on EdgeOne Makers. The cjr-web project uses a Tencent Cloud China-site account with global coverage, including mainland China.

.github/workflows/website.yml builds, tests and uploads the complete site when website, documentation, version or deployment workflow files change on master. It also supports manual runs. The release workflow calls it after publishing a release to update the version and download links. Production deployments run serially; each upload completes before the next deployment starts.

GitHub repository settings:

TypeNameValue
VariableEDGEONE_PROJECT_NAMEcjr-web
SecretEDGEONE_API_TOKENChina-site Makers API token

Bind cjr.zxilly.com to production in Makers, then add the CNAME target provided by the console to Cloudflare with DNS only enabled. Makers manages the certificate. Deployments reuse the existing DNS and domain binding settings.

To deploy an already built site manually:

npx --yes edgeone@1.6.47 login --site china --token "$EDGEONE_API_TOKEN"
npx --yes edgeone@1.6.47 makers deploy ./dist \
  --name cjr-web --area global --env production --skip-ai-gateway-sync

Deployment runs are listed in GitHub Actions. To roll back, select an earlier successful production deployment in Makers.

Releasing

package.version in Cargo.toml is the version source. Clap embeds it at compile time; cjr --version and cjr -V report the value stored in the binary.

Publish a version

  1. Update the package version in Cargo.toml and Cargo.lock, the website package version and installation examples in both languages.
  2. Run formatting, Clippy, tests and npm test in web/. Commit and push the changes.
  3. Create a matching stable tag, such as git tag -a v0.0.1 -m "cjr v0.0.1", then run git push origin v0.0.1.

release.yml checks that the tag matches the package and calls the normal CI workflow. Once checks pass, it creates a draft release. taiki-e/upload-rust-binary-action builds and uploads four platform archives and their SHA-256 files:

  • Windows x64: x86_64-pc-windows-msvc, ZIP.
  • Linux x64: x86_64-unknown-linux-musl, tar.gz.
  • macOS Apple Silicon: aarch64-apple-darwin, tar.gz.
  • macOS Intel: x86_64-apple-darwin, tar.gz.

Archives are named cjr-v<version>-<target> and contain the executable and both READMEs. The minimum macOS version is 13.0. Each release binary is run with --version. Once all jobs succeed, softprops/action-gh-release publishes the draft. Failed jobs leave it unpublished and can be rerun in Actions. Keep published tags fixed and ship subsequent fixes in a new version.

Actions are pinned to full commit SHAs and tracked by Dependabot. The workflow uses GitHub’s GITHUB_TOKEN, granting write access only to jobs that create, upload or publish the release.

Static linking

.cargo/config.toml enables crt-static for Windows MSVC and Linux musl. Windows embeds the C/C++ runtime while retaining Windows system DLL imports. Linux release binaries use fully static linking, with empty dynamic dependency and ELF interpreter entries. Rust dependencies and bundled native libraries are linked statically. macOS uses system libraries under /usr/lib/ and /System/Library/, with libSystem and system frameworks supplied by the operating system.

The release workflow reads each final binary’s dependency table and rejects a dynamic Windows CRT, any Linux dynamic dependency, or non-system macOS libraries. This policy applies to cjr itself; Cangjie applications still use their matching dynamic runtime.