Mars Command is a Windows and Linux desktop companion for the Mars modded Minecraft server. It monitors server status, verifies the signed pack manifest, syncs an isolated game directory, and registers the installation in Minecraft Launcher.
| Minecraft | 1.21.1 |
| Loader | NeoForge 21.1.250 |
| Server | play.nexusgit.info:25565 |
| Voice | voice.nexusgit.info (not yet integrated) |
The Rust backend polls the Minecraft Java status protocol every 15 seconds and reports online state, player count, latency, MOTD, and server build. Polling avoids overlapping checks and pauses while the window is hidden. The Mars proxy requires protocol version 767 for Minecraft 1.21.1; the conventional -1 sentinel is rejected.
Mission Control lets users change the host and port used for dashboard status polling; the target is stored locally and does not change the Minecraft Launcher server entry. The Crew Channel currently shows only aggregate player presence. The server API does not yet provide roster or messaging endpoints.
The client fetches manifest.json and manifest.json.sig from the Mars API. It verifies the detached Ed25519 signature against the public key compiled into the application before trusting the manifest. Invalid signatures fail closed.
Each managed file, including mod JARs, is verified using its signed SHA-256 and size. Unlisted JARs in managed directories are reported as foreign unless they are explicitly tracked personal mods with matching local checksums and passing metadata checks. Personal mods never contribute to signed verification counts. Config and default-config files are mutable: local edits are preserved and reported as conflicts. Sync stages downloads, validates them before replacement, and removes obsolete files only when they still match the last installed hash.
The Preserve personal data setting is enabled by default. During updates it keeps existing worlds, screenshots, mod configuration, shader packs, server-list entries, and selected Minecraft options. Missing baseline files are still installed. Turn the setting off to let normal pack updates replace or remove managed files in those locations.
Setup creates a Minecraft Launcher profile named Mars Client <pack version> and an isolated game directory:
Windows: %APPDATA%\.minecraft\mars-client\<pack-version>
Linux: ~/.minecraft/mars-client/<pack-version>
The shared .minecraft/mods folder is left untouched. Setup uses the matching NeoForge version already installed in Minecraft Launcher, preserves other profiles and account data, adds or updates the Mars server entry in the isolated profile's server list, syncs the signed pack, verifies it, and opens the launcher. On Linux, the launcher must be available as minecraft-launcher on PATH. Close Minecraft Launcher before Setup or Update so its profile file is not being edited concurrently. The default maximum Java heap allocation is 6 GB.
The dashboard remembers the installation and offers:
| Action | When it appears |
|---|---|
SETUP |
No Mars installation is registered. |
UPDATE |
The signed pack version, immutable file hashes, or NeoForge version changed. |
LAUNCH |
The installation matches the current signed manifest and passes integrity checks. |
BLOCKED |
Required files or mods need attention, or there is no trusted manifest. |
Launch rechecks integrity but does not rewrite the profile or sync again. Minecraft Launcher handles Microsoft sign-in; select the Mars profile and click Play there. When enabled in Settings, Mars Command waits up to two minutes for the Java process using the configured Mars game directory, then closes itself. It does not directly start the game process.
Settings also provides reduced-motion and larger-text accessibility options, plus quick actions to open the installation folder, report a bug, and visit the project links. Fund This Project initiates website GitHub sign-in when signed out; when signed in it opens the explicitly configured GitHub Sponsors recipient. Payments are handled by GitHub, not Mars Command; this button never grants roles.
World-specific data packs are not placed into a save automatically; they remain manual until a world target is selected.
After setting up the current Mars installation, open Settings -> Personal Mods -> Select local mod JAR. The native file picker imports one local JAR at a time; nothing is uploaded to a server. Review its mod IDs, size, compatibility warnings, and trust notice, acknowledge the risks, then choose Install personal mod. Select Remove and confirm to remove a tracked mod from the current instance. Close Minecraft first; changes are blocked while its Mars game process is running.
- Limits: a non-empty
.jar, at most 64 MiB per file and 128 personal JARs per instance. Filenames must contain only letters, numbers, dots, hyphens, and underscores; hidden names and Windows device names are rejected. - Validation reads bounded NeoForge TOML metadata inside the JAR. Fabric/Quilt/plain JARs, malformed archives/metadata, Forge-only requirements, signed-pack filename collisions, duplicate filenames/checksums/mod IDs, and known incompatible client Minecraft/NeoForge/FML ranges or required dependencies are blocked before copying. Existing mods' declared conflicts with the new mod are also checked. FML is checked against the installed NeoForge launcher metadata when available.
- Missing constraints, unresolved version expressions/non-numeric Maven qualifiers, unavailable FML metadata, and bundled Jar-in-Jar modules are disclosed as compatibility warnings. Bundled dependency resolution is not certified; unresolved required top-level dependencies are blocked. Metadata cannot prove that a mod is client-only, safe to execute, or accepted by the server. The acknowledgement is not a signature or malware scan.
- Files are copied only into the standard
modsfolder of.minecraft/mars-client/<pack-version>. Arbitrary/CurseForge/shared game folders and redirected instance/mod/state directories are not personal-mod install targets. Copies are staged without overwriting existing files and revalidated against the preview checksum. The local inventory is stored separately in.mars-command/personal-mods.json, never in the signed manifest or signed installed-file list. - Normal signed updates preserve tracked personal JARs, even when Preserve personal data is off. Both dashboard UPDATE and Settings Sync / update pack create the new versioned Mars instance when needed and copy its personal inventory/JARs without deleting the previous version. A filename collision or changed/unreadable source stops migration with an error; remove the conflicting personal mod from the old instance and retry, or inspect changed files manually. A newly incompatible mod remains preserved but blocks launch until removed.
- Integrity results distinguish installed (local checksum matches, unsigned), missing, changed, unreadable, and incompatible personal mods. Unknown/untracked JARs still remain foreign and block launch. A damaged inventory fails closed. Removal never deletes untracked/signed pack files or a personal JAR whose bytes changed; inspect that file manually, and then remove its missing inventory entry in Settings if appropriate.
Personal mod management requires a trusted signed manifest. Remote storage, submission, and community browsing are not part of this local flow.
The title-bar account icon opens account controls. Sign in on website starts a desktop device request through the Rust backend and opens the configured website using the existing Tauri opener. GitHub OAuth and explicit desktop identity approval happen on the website. The backend polls the approved device request; no desktop callback protocol is registered or invented. Expiry, denial, cancellation, browser failures, malformed replies, and network/configuration failures are surfaced. Cancelling or signing out clears desktop secrets and ignores late approvals.
The access token and secret device code stay in Rust process memory, never localStorage, plaintext config, browser URLs, logs, or frontend IPC responses. Persistent login is not implemented: restart requires sign-in. The server owns token expiry; authenticated HTTP 401 responses clear the desktop session and prompt sign-in. The website's session is independent. Pack authentication is unchanged.
The reachable Community tab browses/searches public profile names/descriptions case-insensitively (server-side) without sign-in. Signed-in users can create private drafts, edit/delete their private profiles, copy public metadata into independent personal profiles, and explicitly submit non-empty drafts. Backend ownership/visibility/role enforcement remains authoritative. Mod entries are only metadata: copying/saving never installs JARs, certifies checksums, or pretends a malware scan completed. Submission in this batch is expected to fail closed with HTTP 409 scanning_not_configured. Real verified community file download/install and scanning/upload contracts remain outside batch 1; existing local Personal Mods remains available.
Set public deployment variables in the Rust build environment (compiled into installers), or export them in the desktop process environment during development. .env.example documents their names; account commands do not automatically load .env.
MARS_COMMUNITY_API_BASE: explicit server origin/base without/api. The backend appendsapi/auth/desktop,api/auth/desktop/poll, andapi/community/profiles(plus/mine,/{id},/{id}/submit). No live API origin is assumed. HTTPS is required except local loopback HTTP.MARS_WEBSITE_BASE: explicit website origin/base. The returned verification URI must be same-origin, target<base>/auth/login, and contain only the matchingrequestIdquery parameter, never the secret device code. Website OAuth callback may retainrequestIdwithauthResult=successorauthResult=errorand a safe enumeratederrorCode; callbacks belong to the website, not this client.MARS_SPONSORS_URL: explicithttps://github.com/sponsors/<recipient>; no recipient is assumed. Missing/invalid configuration is an error, not a silent fallback.
Desktop community calls use the returned Bearer token, independently of existing signed-pack requests. Profile PATCH is partial and rejects null name/description/mods. Network/configuration errors are not displayed as empty profile lists.
Focused validation: node --test src/lib/communityValidation.test.mjs, npm.cmd run build, and from src-tauri, cargo fmt --check and cargo test community::tests --lib. Live OAuth, website approval, API deployment/configuration, Sponsors eligibility, and antivirus scanning require the corresponding deployed services and are not certified by these local checks.
src-tauri/src/community_capsules.rs provides a local Rust library boundary for one selected release artifact. A future authenticated, publication-gated backend adapter must supply the opaque release identity and published lowercase SHA-256; these local types are not a new backend schema. Existing community profile entries do not establish release identity, publication, provenance/license binding, or completed scan evidence and are not install authorization.
- Staging consumes a supplied byte stream, at most 64 MiB plus one overflow-detection byte, into a temporary directory inside
.minecraft/mars-client/<pack-version>/.mars-command/community-capsules. Failed/partial streams, wrong checksums, malformed archives and incompatible mods never become active. Nothing is extracted from an archive. - Compatibility uses the existing bounded NeoForge metadata/dependency/conflict validator and an explicitly supplied installed FML version. Unlike manual Personal Mods, capsules fail closed on missing Minecraft/NeoForge constraints, unresolved ranges, and bundled Jar-in-Jar modules. Metadata cannot prove safety, client-only behavior, or server acceptance.
- A verified directory binds release identity separately from content checksum. At most four retained versions are allowed (including abandoned staging directories); a full store fails without deleting older bytes. Normal failure cleans temporary staging. Mutation serialization uses an OS-backed exclusive lock, so a process exit releases the lock and the next operation can safely reuse the persistent lock marker. Automatic garbage collection of abandoned staging directories is not implemented.
- Activation rechecks bytes and compatibility, then atomically replaces one local activation record on the same filesystem. Prior verified versions remain intact. Explicit rollback rechecks the previous release and can recover even if the current artifact is damaged. A failed pre-commit operation preserves the previous activation record. This is atomic visibility, not a guarantee against power-loss/filesystem failure or hostile concurrent local filesystem edits.
- The active-artifact accessor rechecks integrity and compatibility every time; staged-only artifacts are never returned. It is a future isolated-launch overlay input, not integrated into Minecraft Launcher yet. Signed pack files, the instance's existing
mods, personal inventory and shared.minecraft/modsare never modified. There is no Tauri command, remote-install button or production HTTP download path.
These are unsigned checksum checks, not capsule signature verification or antivirus scanning. End-to-end integration is blocked on an authenticated published-release/artifact endpoint and agreed release/provenance/license/scan-evidence contract, plus an isolated launch composition strategy that preserves the signed base and manual personal-mod behavior. No community routes are invented and the signed base-pack /api/v1/files route is not repurposed.
Focused check (from src-tauri): cargo test community_capsules::tests --lib. The existing manual Personal Mods validator remains permissive about its disclosed compatibility warnings.
The Build client installers workflow runs when a client-vX.Y.Z tag is pushed. Before tagging, set the same X.Y.Z version in package.json, both root entries in package-lock.json, src-tauri/tauri.conf.json, src-tauri/Cargo.toml, and the mars-command-client package entry in src-tauri/Cargo.lock. The workflow validates all of those surfaces, builds a Windows NSIS installer and Linux Debian package, then publishes them as setup-X.Y.Z.exe and setup-X.Y.Z.deb assets on the GitHub release. Building the tagged release publishes it; do not push a tag until both installers are intended for release.
Batch 2 client release candidate 1.3.0 hardens the existing session-only GitHub desktop authorization flow, private profile metadata proxy/UX, configured Sponsors navigation, and local capsule staging/activation/rollback boundary. It does not add live capsule upload, download, scanning, publication, backend capsule endpoints, or cryptographic capsule signing.
The in-app client update check uses stable, non-prerelease GitHub releases and selects the newest installer compatible with the current platform: setup-X.Y.Z.exe on Windows or setup-X.Y.Z.deb on Linux. For older releases it also accepts setup.exe or setup.deb when the release tag contains a valid version; a versioned asset is preferred when both names are published. Drafts, prereleases, unsupported platforms, and assets for another platform are not offered.
When an update is available, choose Open installer to open its download in your browser. After it finishes, close Mars Command, open the downloaded installer, follow its prompts, and relaunch Mars Command. No installer runs or installs silently. If the release check cannot reach GitHub, retry after checking your connection or open the GitHub releases page. If no compatible stable asset is published, check that page for setup-X.Y.Z.exe or setup-X.Y.Z.deb (or the legacy generic name), then retry the check later.
The client defaults to https://api.nexusgit.info/api/v1. The release feed exposes the signed manifest, detached signature, and files referenced by that manifest:
GET /api/v1/manifest.jsonGET /api/v1/manifest.json.sigGET /api/v1/files/<manifest-relative-path>
The API serves release artifacts but does not need the Ed25519 private key. Deploy manifest.json, manifest.json.sig, and every referenced file while preserving relative paths. Only host files you are authorized to redistribute. The client never embeds or sends the development API JWT.
Prerequisites: Node.js, Rust, and the Tauri v2 prerequisites for your platform.
npm install
npm run tauri devRun checks:
npm run build
cd src-tauri
cargo fmt --check
cargo test --all-targetsBuild Windows installers from the repository root:
$env:MARS_MANIFEST_BASE_URL = "https://api.nexusgit.info/api/v1"
$env:MARS_MANIFEST_PUBLIC_KEY = "<production public key in hex>"
npm run tauri -- buildMARS_MANIFEST_BASE_URL and MARS_MANIFEST_PUBLIC_KEY are compile-time overrides. Rust option_env! does not automatically read .env. On Linux, install the Tauri Linux prerequisites and build a Debian package with npm run tauri -- build --bundles deb. Bundles are written under src-tauri/target/release/bundle/ (NSIS/MSI on Windows and .deb on Linux).
Before public distribution, use a production keypair whose public key is embedded in the client, and code-sign the Windows installer if you want to reduce SmartScreen warnings. Never ship the private signing key or an API JWT in the app.
The maintainer tool hashes these directories from the local pack instance: mods, config, kubejs, defaultconfigs, resourcepacks, and shaderpacks. Only config and defaultconfigs are marked mutable.
From PowerShell at the repository root:
$env:MARS_SKIP_LOCAL_ENV = "1"
$env:MARS_PACKAGE_BASE_URL = "https://api.nexusgit.info/api/v1"
cargo run --manifest-path src-tauri/Cargo.toml --release --example manifest_tool -- build `
"modpack/Mars-Client-1.2.4/Mars Client" `
"1.2.4" `
"manifest-dist/manifest.json"
cargo run --manifest-path src-tauri/Cargo.toml --release --example manifest_tool -- sign `
"manifest-keys/mars-signing.key" `
"manifest-dist/manifest.json"
cargo run --manifest-path src-tauri/Cargo.toml --release --example manifest_tool -- verify `
"manifest-dist/manifest.json"MARS_SKIP_LOCAL_ENV=1 prevents the tool from loading .env. The signing command reads the private key file without printing its contents. Verify the signature against the same public key embedded in the client, then deploy the manifest, signature, and payload files to the API release directory.
The Publish pack manifest workflow publishes a GitHub release and, for non-prereleases, deploys manifest.json and manifest.json.sig to /opt/mars-package-api/releases/1.0.0. It stages both files, replaces the signature first and the manifest last, then fetches both public API URLs and compares them byte-for-byte with the build artifacts. Prereleases are not deployed to the live API.
Configure these repository Actions secrets:
| Secret | Purpose |
|---|---|
CURSEFORGE_API_KEY |
Approved Core API key used to resolve export metadata. |
MARS_SIGNING_KEY |
Ed25519 private key used to sign the manifest. |
MARS_DEPLOY_HOST |
SSH hostname of the Mars API server. |
MARS_DEPLOY_USER |
Dedicated SSH user allowed to deploy to the release directory. |
MARS_DEPLOY_SSH_KEY |
Private SSH deploy key. |
MARS_DEPLOY_KNOWN_HOSTS |
Pinned known_hosts entry for the server; do not discover it during the workflow. |
The deploy user must be able to create a run-specific staging directory, replace files in the release directory, and run sudo -n systemctl restart mars-package-api. The restart reloads the API's cached release-file inventory. Keep the SSH key limited to deployment use. The API server does not receive the manifest signing key or CurseForge API key. The workflow serializes releases so two deployments cannot overwrite each other.
- The Ed25519 signature authenticates the exact manifest bytes. Re-sign after any edit, including whitespace.
- The client verifies the signature before parsing the manifest and rejects unsafe relative paths.
- Downloads require HTTPS and are checked against signed SHA-256 hashes and sizes before installation.
- Keep the private signing key in protected release storage, never in source control or on the API server.
- Hashes detect file changes; the signature proves the trusted maintainer approved those hashes.
src/ React dashboard, settings, hooks, and UI types
src-tauri/src/
minecraft.rs DNS/SRV resolution and Java status protocol
manifest.rs Manifest schema, fetch, signature verification
integrity.rs Hash-based file and mod verification
settings.rs Persistent settings, server list, and Launcher profiles
process_detection.rs Windows/Linux Minecraft process detection
repair.rs GitHub release installer version checks
sync.rs Staged sync and installed-state tracking
lib.rs Tauri commands and Setup/Update/Launch status
src-tauri/examples/
manifest_tool.rs Maintainer manifest generator and signer
manifest-dist/ Release manifest and detached signature
- Automatic profile setup supports Windows and Linux with NeoForge. The matching NeoForge version must already be installed in Minecraft Launcher.
- Linux launcher opening expects
minecraft-launcheronPATH; other launcher locations and distributions are not auto-detected. - The Crew Channel has aggregate player count only until the server exposes a roster and messaging API.
- Sync has no progress bar or cancellation yet, and file scans do not report progress.
- Rotating the manifest signing key requires rebuilding the client with the matching public key.
- Voice relay integration is not implemented.