PaperScript is a dependency-free, manually invoked Paper JAR staging tool that uses the current PaperMC Fill v3 downloads service. It downloads a verified, versioned JAR beside the one already in use; it never stops, starts, restarts, or signals the server.
It is designed for the layout where your server root stays readable, while PaperScript keeps its own files in a visible paperscript/ directory:
/anydirectory/
/anydirectory/server.properties
/anydirectory/Paper-26.2-84.jar
/anydirectory/paperscript.sh
/anydirectory/paperscript/
/anydirectory/paperscript/paperscript.py
/anydirectory/paperscript/config.example.json
/anydirectory/paperscript/config.json
/anydirectory/paperscript/state.json
/anydirectory/paperscript/last-launched-jar.txt
/anydirectory/paperscript/cache/
/anydirectory/paperscript/downloads/
/anydirectory/paperscript/backups/jars/26.2/
/anydirectory/paperscript/locks/
/anydirectory/paperscript/logs.log
/anydirectory/paperscript/todo.log
This keeps staging-tool clutter out of the server root and makes the disposable cache, download workspace, and logs easy to clean. Preserve config.json, state.json, last-launched-jar.txt, and the tracked PaperScript source files unless you intentionally want to reset them.
Python is the best fit here for this project:
- It works well on modern macOS and Ubuntu without needing
jq, npm packages, or extra shell tooling. - JSON, version sorting, checksums, prompts, logging, and future expansion are much easier to keep readable.
- The runtime stays small: one Python script plus one tiny shell launcher.
- It is much easier to maintain than a large Bash script once features like status views, integrity checks, cleanup, and per-server config exist.
Bash still has a place here, which is why the launcher remains a simple paperscript.sh.
- Uses the PaperMC Fill v3 API.
- Sends a custom User-Agent by default:
mrfloris-PaperScript/2.0 (https://github.com/mrfdev/PaperScript) - Finds the latest stable Paper version and latest stable build automatically.
- Can inspect and stage the latest preview release that is newer than the current stable line, preferring
BETAand falling back toALPHA. - Stages a newer same-version stable build only when the command is run manually.
- Prompts before staging a newer or older Minecraft version family.
- Supports forced re-download of the same build with
--force. - Requires the Paper API size and SHA-256 before staging and never writes beyond the declared size.
- Recomputes every requested latest-version/channel policy, or revalidates an explicitly requested exact build, against fresh Paper API data immediately before staging; cached metadata is never artifact authorization.
- Preflights server-root write access, durable directory sync, and enough free space for the JAR plus a safety reserve.
- Verifies the exact private staging inode against the API size/SHA-256 and checks its executable manifest, Main-Class bytecode, and every ZIP entry CRC.
- Stores staged-build identity and SHA-256 in
state.jsonfor later fail-closedverifychecks. - Caches Paper API metadata locally through an owner-checked, descriptor-bound directory and atomic private regular files so repeated read-only release checks are faster.
- Escapes untrusted terminal controls from API, HTTP, and other dynamic text before console or log output.
- Publishes the verified download atomically as
Paper-<version>-<build>.jarwithout replacing an existing JAR. - Uses the launcher's atomic
last-launched-jar.txtmarker to protect the JAR selected for the last start. - Keeps the last-launched JAR plus the newest staged JAR in the server root by default.
- While a launcher/JVM run is in flight, temporarily protects its one prior rollback JAR as well.
- Archives older exact same-version numeric builds under
paperscript/backups/jars/<version>/and keeps five by default. - Provides explicit
cleanup --server-jars --keep N --dry-runreconciliation. - Serializes staging and root-JAR cleanup with a per-server lock.
- Refuses overlapping launcher runs with a fail-closed lock held for the JVM lifetime.
- Detects likely running servers and tmux sessions for read-only status only.
- Supports
--dry-run,--quiet,--no-color, and per-server config defaults. - Supports color themes and a compact or full status view.
- Keeps each target server's runtime files isolated inside that server's
paperscript/directory.
- Python
3.9+ python3available on your pathtmuxonly if you want its session shown in read-only status
No third-party Python packages are required.
For a central checkout that targets an existing server, clone separately and pass the server root explicitly:
git clone https://github.com/mrfdev/PaperScript.git /opt/PaperScript
cd /opt/PaperScript
chmod +x paperscript.sh paperscript/paperscript.py
./paperscript.sh --server-dir /srv/minecraft/live statusFor the compact layout shown above, place paperscript.sh and the tracked paperscript/ directory directly in the server root. Do not run from a child checkout without --server-dir, because the current working directory is the default target.
If you want to initialize or repair the runtime files manually:
./paperscript.sh --server-dir /srv/minecraft/live initIn a compact in-root installation, the shorter ./paperscript.sh init is equivalent. App startup may create target-local config, directories, and logging scaffolding; init asks before creating or repairing its remaining runtime files.
If you already run a live server and want to replace an older local PaperScript checkout with a fresh copy from GitHub, the normal safe path is:
./paperscript.sh --server-dir /srv/minecraft/live status
./paperscript.sh --server-dir /srv/minecraft/live update --dry-run
./paperscript.sh --server-dir /srv/minecraft/live updateThese examples assume a central checkout. In the compact in-root layout, omit the repeated --server-dir option.
PaperScript remains a drop-in component for server roots launched through 1MB-start.sh and 1MB-minecraft.sh. Replacing paperscript.sh and the tracked program files under paperscript/ does not require a launcher edit, config migration, new package, or new command-line option. Preserve the server-local paperscript/config.json, paperscript/state.json, and paperscript/last-launched-jar.txt; the file locations, marker format, numeric JAR naming contract, and manual-start behavior are unchanged.
For servers that use the required numeric naming pattern, such as Paper-26.2-84.jar, PaperScript detects the newest local build and stages the next verified build beside it. The updated 1MB-minecraft.sh selects the greatest numeric build for its configured Minecraft version on the next manual start.
Keep your local paperscript/config.json, paperscript/state.json, and paperscript/last-launched-jar.txt when refreshing the checkout. In particular, review changes to:
default_channelcheck_latest_channel_onlytmux_session
then deleting the whole paperscript/ runtime directory will also delete those per-server preferences.
PaperScript always stages the canonical Paper-<version>-<numeric-build>.jar name. Legacy names such as Paper-26.2.jar remain launcher fallbacks, but PaperScript never overwrites them.
If you intentionally want a fresh runtime, start once with the updated 1MB-minecraft.sh before root-JAR cleanup. Until a valid marker exists, staging succeeds but automatic root cleanup is deliberately deferred.
Show the current state:
./paperscript.sh statusStage the latest stable Paper build for the launcher-selected Minecraft family:
./paperscript.sh updateForce a re-download and verification of the latest stable build for that launcher family:
./paperscript.sh --force updateInspect the latest preview release beyond stable:
./paperscript.sh experimentalDownload that preview build:
./paperscript.sh experimental --downloadChecks the server directory, finds the latest stable build for the launcher-selected family, and stages it beside existing JARs when appropriate.
Behavior:
- If no managed Paper JAR is detected, it offers the latest stable build.
- If the launcher-selected version has a newer stable build, it stages that build.
- If the newest staged build for the launcher family already matches stable,
--force updatere-downloads and verifies it without replacing the existing file. updatestays on the launcher-selected Minecraft version even when a newer family exists; cross-version staging requires an explicitdownload --version ...command.- Before network activity, PaperScript confirms that the server root can create a private file and durably sync directory entries. It also requires enough free space for the API-declared JAR size plus at least 64 MiB of remaining headroom (or 10% of the JAR size when that is larger).
- The response is capped at the API-declared size. The exact on-disk inode must match that size and SHA-256, contain one executable JAR manifest and its Main-Class bytecode, and pass CRC/decompression checks for every ZIP entry.
- The verified file is mode
0644, fsynced, then atomically published under its numeric build filename without overwriting any path that appeared concurrently. - PaperScript never asks to stop the server and contains no stop/kill path.
- After staging, valid launcher identity enables bounded root cleanup; missing/invalid identity defers cleanup without guessing.
- If
--dry-runis used, it reports JAR/archive actions without staging, moving, or pruning JARs; normal target-local config, logging, and metadata-cache activity may still occur.
Examples:
./paperscript.sh
./paperscript.sh update
./paperscript.sh --force update
./paperscript.sh update --dry-run
./paperscript.sh --no-color update
./paperscript.sh --server-dir /srv/mc/live updateShows PaperScript, launcher, staged-JAR, retention, and release state, including the newest preview release beyond stable when one exists.
The normal full view can include:
- PaperScript release
- server directory and runtime directory
- server label
- tmux session name and whether it currently exists
- manual/external lifecycle policy
- server properties detection
- configured server port
- running server detection
- newest managed jar, full path, version, build, staged channel, and SHA-256
- last launcher-selected jar, including a clearly labelled legacy marker when applicable
- the predicted next JAR for the family in the last launcher marker, including legacy fallback state; PaperScript does not execute or infer later edits to
_minecraftVersion - stored expected SHA-256 from the last PaperScript stage
- newest stable release
- update status
- newest channels for the current stable version
- newest preview release beyond stable
- server-root retention and bounded archive settings
- historical backup/archive counts and cleanup suggestions when useful
Status views:
./paperscript.sh statusFull status view./paperscript.sh status --compactShorter overview./paperscript.sh status --fullForce full mode even if config defaults to compact
Examples:
./paperscript.sh status
./paperscript.sh status --compact
./paperscript.sh --server-dir /srv/mc/live statusShows the latest stable Paper release overall and can stage it directly.
This is useful when you want a clear stable overview without running a full update flow first.
Examples:
./paperscript.sh stable
./paperscript.sh stable --download
./paperscript.sh --force stable --download
./paperscript.sh stable --download --yes --forceThis command name is kept for compatibility, but it now behaves like a preview-channel helper.
It looks for the latest non-stable Paper release that is newer than the current stable line. It prefers BETA and only falls back to ALPHA if no beta build exists.
If the current stable release is already the newest line, PaperScript says so instead of pointing you at an older beta build from that same now-stable version.
For example:
Latest channels for stable version 26.2means the channels that exist for26.2Latest preview releaseis only shown when a newer not-yet-stable line exists beyond the current stable release
Examples:
./paperscript.sh experimental
./paperscript.sh experimental --download
./paperscript.sh --yes experimental --download
./paperscript.sh experimental --download --yesUnder the per-server mutation lock, hashes the newest managed jar through a no-follow file descriptor and confirms that its path, inode, contents, and newest-target selection remain unchanged throughout verification. It compares the digest against:
- every non-empty SHA-256 recorded for that same jar in
state.jsonduring the last PaperScript stage - a valid SHA-256 fetched from the Paper API for that exact version and build through a fresh lookup that always bypasses local metadata cache contents
- the command also reports the recorded staging channel and current channel from the API
verify exits with status 0 only after the target remains stable and every applicable
digest matches. It exits with status 1 when the jar is missing or changes during the
check, recorded digest data is malformed or mismatched, the fresh API lookup fails or
cannot find the exact build, the API omits a valid SHA-256, or the jar differs from that
SHA-256. Optional historical state may be absent or name another jar; a matching fresh API
digest remains authoritative. Consequently, verify requires Paper API connectivity and
never treats cached metadata as sufficient verification.
Examples:
./paperscript.sh verify
./paperscript.sh --server-dir /srv/mc/live verifyLists every Paper version the API currently exposes.
Examples:
./paperscript.sh list-versions
./paperscript.sh list-versions --channels
./paperscript.sh list-versions --channels --limit 10
./paperscript.sh --debug-http list-versions --channels --limit 10With --channels, PaperScript also shows the newest build it can find per channel for each version.
Because --channels performs many API requests, PaperScript now:
- retries transient API and Cloudflare errors
- continues past temporary per-version failures by default
- supports
--limitfor smaller debug runs - supports
--channel-delay-msto slow the request rate when needed
This is useful for questions like:
- which versions exist at all
- whether a future
26.3.xline only exists as alpha or beta - whether
26.2is still the newest stable family or a newer preview line has appeared - whether older versions such as
1.20.4or1.19.2still have builds available
Shows the newest available build per channel for one specific version, then offers to download one interactively.
If the selected build is already staged, PaperScript can offer a direct confirmation. An existing target is never overwritten: it is accepted only when its SHA-256 already matches PaperMC.
Examples:
./paperscript.sh inspect 26.2
./paperscript.sh inspect 1.20.4
./paperscript.sh inspect 1.19.2Interactive version picker. It lists all available versions, lets you choose one by number, shows the newest builds for that version, and can then download it.
If the build you choose is already staged, explore can offer the same confirmation flow as inspect.
Examples:
./paperscript.sh exploreDownloads, verifies, and stages a chosen version or exact build on demand.
Examples:
./paperscript.sh download --version 26.2
./paperscript.sh download --version 1.20.4
./paperscript.sh download --version 1.20.4 --build 123
./paperscript.sh download --version 26.2 --channel BETA
./paperscript.sh --force download --version 26.2
./paperscript.sh --force download --version 26.2 --build 84
./paperscript.sh download --version 26.2 --build 84 --force --yesNotes:
--versiondownloads the newest build for that version on the selected channel.--builddownloads that exact build number for the version.- The default channel comes from
config.jsonand defaults toSTABLE. - Version upgrades still prompt unless you add
--yes. --forceallows an already-selected build through the command flow, but never overwrites an existing target path.- PaperScript refuses same-version build downgrades because
1MB-minecraft.shalways chooses the greatest numeric build, so an older file could not become the next launch. - Use
./paperscript.sh --force updateto re-download the current latest stable build. - Use
./paperscript.sh --force download --version <version> --build <build>to re-download one exact build.
Removes selected local runtime files and caches, or explicitly reconciles versioned JARs in the server root.
Default behavior:
./paperscript.sh cleanupCleans the safe/default targets:downloads/and Python__pycache__/
Targets:
--downloadsDelete old download workspace files indownloads/--backupsClean legacy top-level backup items while preserving the managedbackups/jars/archive--backups --keep NKeep the newestNbackups and remove older ones--server-jarsProtect the valid launcher-marked JAR, greatest numeric next-start JAR, and any in-flight launcher rollback JAR, then archive older exact numeric builds for that same version--server-jars --keep NUseNas the steady-state matching-root limit (minimum2); the default is2. When last-launched and newest are the same file, only one physical JAR is needed. An active launcher may temporarily add one prior rollback JAR, so the safe transient maximum is normally3.--server-jars --version VERSIONRestrict reconciliation to this version; it must agree with the launch marker target--allClean downloads, legacy top-level backups, metadata cache,__pycache__, logs, and JSON state/config together. It never implies--server-jarsand preservesbackups/jars/.--metadata-cacheDelete cached Paper API metadata incache/--pycacheDelete Python__pycache__/folders--logsClearlogs.log--jsonor--configDeleteconfig.jsonandstate.jsonso the next run starts fresh
Confirmation behavior:
- cleanup explains what will be removed
- cleanup asks for
y/Nconfirmation by default --yesskips the prompt--dry-runlists both root-to-archive moves and exact archive-cap prunes without removing anything
--server-jars is fail-closed: it refuses to move anything when the launcher marker is missing, malformed, points outside the server root, identifies a symlink, or names another version. Other Minecraft versions, legacy names, partial downloads, malformed names, symlinks, plugin JARs, and unknown files are never managed.
Examples:
./paperscript.sh cleanup
./paperscript.sh cleanup --all
./paperscript.sh cleanup --downloads
./paperscript.sh cleanup --backups
./paperscript.sh cleanup --metadata-cache
./paperscript.sh cleanup --backups --keep 10
./paperscript.sh cleanup --server-jars --dry-run
./paperscript.sh cleanup --server-jars --keep 2
./paperscript.sh cleanup --server-jars --version 26.2 --keep 2
./paperscript.sh cleanup --pycache
./paperscript.sh cleanup --logs
./paperscript.sh cleanup --json
./paperscript.sh cleanup --yes --downloads --pycache
./paperscript.sh cleanup --dry-run --jsonCreates or repairs local runtime files inside paperscript/.
It can create:
config.jsonstate.jsonlogs.logtodo.logdownloads/backups/
It asks before its listed repair actions unless you use --yes. PaperScript startup itself may already create target-local config, directories, and log scaffolding.
Examples:
./paperscript.sh init
./paperscript.sh --yes init
./paperscript.sh init --dry-run--server-dir PATHUse a specific server directory instead of the current directory.--contact VALUEOptional legacy contact value used to build aPaperScript/<version> (<contact>)User-Agent override.--user-agent VALUEFull custom User-Agent header. If omitted, PaperScript uses the built-in default.--tmux-session NAMEtmux session to display in read-only status. Defaults to config,PAPERSCRIPT_TMUX_SESSION, ormcserver.--timeout SECONDSHTTP timeout in seconds. Default comes fromconfig.jsonand is30unless changed.--debug-httpLog HTTP request attempts and retries for Paper API troubleshooting.--no-metadata-cacheBypass the local Paper API metadata cache for this run.--yesAccept prompts automatically where it is safe to do so.--forceAllow the same build to be selected again. PaperScript still refuses to overwrite an existing JAR whose checksum differs.--dry-runShow what would happen without downloading, moving jars, or pruning archives.--quietSuppress normal console output. Logs still go topaperscript/logs.log.--no-colorDisable ANSI colors in terminal output.
PaperScript also accepts these global flags after the command, so both styles work:
./paperscript.sh --yes --force stable --download
./paperscript.sh stable --download --yes --forcePaperScript is intended for manual invocation. This project does not recommend cron or unattended staging.
For API troubleshooting, a good pattern is:
./paperscript.sh --debug-http --timeout 5 stable
./paperscript.sh --debug-http list-versions --channels --limit 10
./paperscript.sh --no-metadata-cache --debug-http stablePaperScript uses this behavior:
- If you pass
--server-dir, that path is used. - If you run from a normal directory, the current working directory is treated as the server directory.
- If you run the Python file from inside a directory actually named
PaperScript, the parent directory is treated as the server directory.
That makes this work naturally:
cd /server
./paperscript.sh updateand also:
cd /server/paperscript
python3 paperscript.py updateIf server.properties exists, status treats the directory as a possible live server and performs read-only checks for a likely matching Java process.
It first uses the server-port value from server.properties and looks for a Java process listening on that exact TCP port. That makes it safer on a machine that runs several Minecraft servers at once.
If port-based detection does not find anything, it falls back to:
- jar-name matching
- command-line matching
- working-directory matching
Detection never controls that process. Staging does not run a stop command, send tmux keys, send Unix signals, start Java, or restart a tmux session. Stop the server manually through your normal CLI/tmux workflow when you are ready, then run 1MB-minecraft.sh; it will select the greatest numeric build for its configured version.
Examples:
./paperscript.sh --tmux-session production status
PAPERSCRIPT_TMUX_SESSION=test-server ./paperscript.sh statusPaperScript stores its runtime files inside the visible paperscript/ directory:
paperscript/config.example.jsonTracked config template for the repopaperscript/config.jsonLocal per-server config, intentionally ignored by gitpaperscript/state.jsonLast staged jar information, intentionally ignored by gitpaperscript/last-launched-jar.txtExact basename selected by the customized1MB-minecraft.sh, written atomically immediately before Java starts; a nonzero JVM exit restores the previous valid markerpaperscript/logs.logActivity logpaperscript/downloads/Legacy/diagnostic download workspace; active staging uses one private hidden temp inode in the server root so publication stays on the same filesystempaperscript/cache/Cached Paper API metadata used only to speed up discovery and read-only checks. PaperScript rejects unsafe cache directories and leaves, binds cache I/O to an opened directory descriptor, and freshly recomputes the requested staging policy.paperscript/backups/jars/<version>/Bounded archive of older exact numeric Paper builds moved out of the server rootpaperscript/locks/Per-server advisory staging/cleanup lock plus the fail-closed active-launch guardpaperscript/todo.logDeferred future ideas for the project
These runtime files are isolated on purpose so the server root stays clean and different --server-dir targets keep separate config, state, cache, logs, archive, marker, and lock files without git noise.
If an older central checkout kept config.json or state.json beside paperscript.py, the first run against a separate --server-dir prints a migration warning and leaves those legacy files untouched. Review and manually copy only the settings/state that belong to that target; PaperScript does not guess which server shared legacy state belongs to.
PaperScript creates paperscript/config.json automatically if it does not exist yet.
The repo includes a tracked template at paperscript/config.example.json.
Current default config:
{
"server_name": null,
"tmux_session": "mcserver",
"default_channel": "STABLE",
"check_latest_channel_only": "STABLE",
"allow_same_version_build_upgrade": true,
"keep_backups": 10,
"keep_server_jars": 2,
"keep_archived_jars": 5,
"reconcile_server_jars_after_stage": true,
"http_timeout_seconds": 30,
"status_show_all_channels": true,
"log_file": "logs.log",
"backup_dir": "backups",
"downloads_dir": "downloads",
"metadata_cache_dir": "cache",
"metadata_cache_enabled": true,
"metadata_cache_ttl_seconds": 300,
"confirm_before_force_download": true,
"confirm_before_downgrade": true,
"auto_detect_server_by_port": true,
"fallback_process_detection": true,
"quiet": false,
"no_color": false,
"color_theme": "default",
"default_status_view": "full",
"command_hint_mode": "auto",
"release_link_mode": "auto",
"debug_http": false,
"http_retries": 2,
"http_retry_backoff_seconds": 1.5,
"list_versions_channel_delay_ms": 150,
"list_versions_continue_on_error": true
}Useful per-server settings:
server_nameFriendly label for status outputtmux_sessionSession to display in read-only statuskeep_backupsRetention for historical files in the legacy top-levelbackups/cleanup targetkeep_server_jarsSteady-state exact same-version numeric JAR limit (minimum2): valid last-launched plus greatest numeric next-start JAR. One in-flight launcher rollback JAR may temporarily exceed it.keep_archived_jarsMaximum exact numeric Paper JARs retained per version underpaperscript/backups/jars/(minimum1)reconcile_server_jars_after_stageArchive older matching root JARs after a successful stage, but only when launcher identity validatesdefault_channelDefault download channel fordownload --versionmetadata_cache_enabledEnable or disable the local Paper API metadata cache. Cached metadata can influence display and preliminary selection, but fresh API data recomputes every latest-build policy and authenticates every exact-build staging operation.metadata_cache_ttl_secondsHow long cached metadata stays valid before PaperScript refreshes itquietSuppress normal console output while retaining the activity logno_colorDisable ANSI colors by defaultcolor_themeTheme name. Current options:default,soft,high-contrastdefault_status_viewfullorcompactcommand_hint_modeauto,always, orneverrelease_link_modeauto,always, orneverdebug_httpLog HTTP request attempts and retrieshttp_retriesRetry count for transient API or Cloudflare errorshttp_retry_backoff_secondsBase retry delay before exponential backofflist_versions_channel_delay_msDelay between per-version channel lookups duringlist-versions --channelslist_versions_continue_on_errorContinue past temporary per-version failures instead of aborting the whole listing
If a build is already selected and you want PaperScript to re-check that request, use one of these:
./paperscript.sh --force update
./paperscript.sh --force stable --download
./paperscript.sh --force experimental --download
./paperscript.sh --force download --version 26.2 --build 84
./paperscript.sh stable --download --yes --forceInside inspect and explore, PaperScript can also offer:
Download it anyway? [y/N]
when the selected build is already staged. An existing target is kept byte-for-byte and is only accepted when its checksum matches the Paper API.
Check a live server without changing server JARs or controlling its lifecycle:
./paperscript.sh status
./paperscript.sh verify
./paperscript.sh update --dry-runStage a newer build for a dev server in the current directory:
./paperscript.sh updateSee whether a newer version family exists before touching production:
./paperscript.sh list-versions --channels
./paperscript.sh stable
./paperscript.sh experimentalIf experimental reports that no preview release newer than stable exists, that means the current stable line, such as 26.2, is already the newest main target.
Inspect an older branch:
./paperscript.sh inspect 1.20.4Download an exact historical build:
./paperscript.sh download --version 1.19.2 --build 88Target a separate server directory:
./paperscript.sh --server-dir /Users/you/minecraft/test-server updateThis repository includes a customized test-instance copy of 1MB-minecraft.sh at the
server root. The copy records the SHA-256 of the original launcher it came from so its
changes can be reviewed and manually applied to the canonical 1MB source later.
For the configured _minecraftVersion, the customized launcher:
- prefers
Paper-<version>-<numeric-build>.jar - compares build numbers numerically and selects the greatest build
- ignores other Minecraft versions, partial downloads, and malformed build names
- ignores symlink JAR candidates
- falls back to the legacy
paper-<version>.jarname when no versioned build exists - writes the exact selected basename atomically to
paperscript/last-launched-jar.txtbefore invoking Java - resolves and enters its own directory first, so launching it from another working directory cannot select another server's JAR
- keeps a fail-closed launch lock for the JVM lifetime, refuses overlapping launches, and restores the prior valid marker when Java returns an error
The launcher only chooses a jar when the server is started. It does not download jars, stop a running server, or modify the external canonical 1MB source.
The launcher never guesses that an abandoned launch lock is stale: the wrapper can die while Java remains alive. If paperscript/locks/server-launch/ remains after a crash, first confirm that the Minecraft JVM is fully stopped, then remove only that lock directory before retrying the launcher.
PaperScript does not keep a Minecraft server, world, plugin directory, or reusable server template in this repository. Those files would be large, environment-specific, and too easy to mix with production data. Tests use disposable server directories instead.
Run the dependency-free unit and drift checks:
python3 -m unittest discover -s tests -p 'test_*.py' -v
bash -n paperscript.sh 1MB-minecraft.sh tests/live-smoke.sh
python3 -m py_compile paperscript/paperscript.py tests/test_paperscript.py tests/test_1mb_minecraft.pyThe unit suite verifies version ordering, stable-channel defaults, same-version build-upgrade behavior, preview selection, saved channel metadata, fresh exact-build authorization, fail-closed fresh-API verification, atomic and symlink-safe metadata caching, terminal-control neutralization, atomic state/config defaults, launcher marker publication and rollback, numeric latest-build selection, non-disruptive staging, disk/permission/durability preflight, response size caps, private-inode continuity, executable JAR/CRC validation, per-server and active-launch locking, fail-closed marker handling, symlink/version containment, dry-run pruning previews, root retention, and archive caps. Launcher tests use a disposable fake java executable and never start a real server.
Run the opt-in live PaperMC smoke test:
./tests/live-smoke.shThe live test creates a temporary server root, uses STABLE for both release checks, stages the canonical Paper-{version}-{build}.jar, verifies its SHA-256, confirms status detects its version/build/channel/path, and runs verify. It never starts or stops a server. The temporary jar and runtime files are removed afterward.
To retain the disposable directory for troubleshooting:
PAPERSCRIPT_SMOKE_KEEP=1 ./tests/live-smoke.shGitHub Actions runs the network-free unit and syntax checks on current macOS and Ubuntu runners with the minimum supported Python 3.9. The full live download remains opt-in so routine CI does not repeatedly download a Paper server jar or add avoidable load to PaperMC.
PaperScript keeps a live local todo file at paperscript/todo.log for deferred ideas that are not implemented yet.
Current completed production foundations include manual non-disruptive transactional staging, fresh staging-policy authorization, descriptor-bound private atomic metadata-cache writes, terminal-safe dynamic output, per-server locking, atomic no-overwrite publication/state, bounded JAR retention, and latest-build launcher selection with an active marker. Remaining queued ideas include:
- corrupt-cache preservation and diagnostics
- manual review/application of the launcher diff to the canonical
1MB-minecraft.sh - read-only doctor checks, machine-readable status, and granular documented exit codes
- review of the Codex Security report's Security Objectives and Assumptions sections
- broader failure-injection, CLI integration, and launcher compatibility tests
Full server, world, plugin, and BlueMap backups remain a separate operational concern so staging a new Paper jar does not wait for very large backup jobs.
PaperScript is built around the current PaperMC downloads service and its User-Agent expectations:
- Docs: https://docs.papermc.io/misc/downloads-service/
- Swagger UI: https://fill.papermc.io/swagger-ui/index.html#/
- Downloads page: https://papermc.io/downloads/paper
MIT. See LICENSE.