Skip to main content

Game Updater

A small, auditable updater for a game or app that you host yourself.

It does not run anything at install time. It runs only when you call the CLI or the update() function, and it refuses to install anything that is not covered by a valid Ed25519 signature and a matching SHA-256 hash.

What it does

  1. Checks the VM compatibility policy when enabled (default: off). With vm_policy="block", a detected VM stops the call before anything is downloaded, installed, or run.
  2. Downloads manifest.json over HTTPS.
  3. Verifies the manifest signature against the public key embedded in this package.
  4. Compares the remote version with the local version.json.
  5. Downloads each listed file to a staging folder.
  6. Verifies size and SHA-256 for every file.
  7. Moves the verified files into the game directory.
  8. Writes version.json with the version, sequence, notes, and file list.
  9. Optionally runs a patch executable — only if it is listed in the signed manifest, hash-verified, and --run was passed.

What it does not do

  • No setup.py / PEP 517 hooks that execute anything.
  • No .pth files, no sitecustomize.py, no persistence.
  • No obfuscation. The source is readable.
  • No unsigned downloads. Every byte is checked against the signed manifest.
  • No silent execution. --run is explicit.
  • No stealth VM detection. The policy is explicit, documented, and printed by the CLI.
  • No anti-debug, timing checks, process scanning, or security-tool interference.
  • No network requests for VM detection. It only reads local OS metadata.

Pinned hello demo (Windows, opt-in)

game-updater --hello downloads a specific demo batch file, verifies its pinned SHA-256, writes it as %TEMP%\game-updater-hello\hello.bat, and runs it. The batch file writes hello.txt next to itself and opens Notepad. Pass --dir to use a different folder:

game-updater --hello
game-updater --hello --dir "C:\Games\MyGame"

Python has the same default:

import game_updater

game_updater.run_hello()                    # %TEMP%\game-updater-hello
game_updater.run_hello(r"C:\Games\MyGame")  # custom folder
Item Value
URL https://hello-phi-amber.vercel.app/hello.bat
Size 120 bytes
SHA-256 41e425416ddb186861598dffc847c2b7d585a9af7e94e130e2354362b61259da

This is an explicit demo path, not part of the signed-manifest update flow. It does not run at install time, it does not run on import, and it does not run unless --hello (or run_hello() in Python) is called. The file is re-hashed immediately before execution. It is Windows-only because it is a .bat file.

VM compatibility policy

The check is a configurable compatibility policy, not a stealth feature. It runs at the start of update() and before the CLI reads local state.

Policy Behavior
off (default) Skip VM detection entirely.
allow Detect and report, but never block. Ordinary physical PCs and VM users both work.
block If a VM is detected, raise VMBlockedError before creating the game directory, fetching the manifest, installing files, or running a patch.

When detection runs, the CLI prints the result before doing any work. A block refusal prints the documented error instead; off prints nothing.

vm check: physical machine (no known VM indicators found)

If a VM is detected and the policy is block:

error: VM use is disabled by policy (vm_policy='block'), but this machine
appears to be a vmware virtual machine (confidence: high). Indicators:
DMI system manufacturer is VMware; DMI system product is VMware. No update
was downloaded, installed, or run. If this is a physical machine, this is a
false positive; use vm_policy='allow' or vm_policy='off' to proceed. VM
detection is best-effort and can be wrong.

CLI exit code is 3 for a VM policy block, 2 for an update/manifest error, and 0 on success.

Indicators used

Detection is conservative and uses only documented, read-only DMI/SMBIOS values exposed by the OS:

Environment Definitive indicators Supporting only
Hyper-V DMI system product Virtual Machine; DMI system version Hyper-V Hyper-V guest integration registry key
QEMU / KVM DMI system/board manufacturer QEMU; DMI product Standard PC (Q35 ...) or Standard PC (i440FX ...) BIOS vendor SeaBIOS
VMware DMI manufacturer, product, version, board, or BIOS containing VMware —
VirtualBox DMI manufacturer/board/BIOS innotek; product/version VirtualBox —

Unsupported platforms (including macOS) return UNKNOWN; they are not guessed at and are allowed under block.

Rules:

  • A VM result requires at least one definitive vendor-specific string.
  • Supporting-only evidence (for example, SeaBIOS alone, or the Hyper-V guest key alone) returns UNKNOWN, not VM.
  • Two different definitive vendors return UNKNOWN with conflicting=True.
  • A missing or unreadable DMI source returns UNKNOWN.
  • block refuses only on VM. PHYSICAL and UNKNOWN are allowed, which keeps ordinary PCs working and avoids blocking on ambiguous evidence.

What is intentionally not used

  • The CPUID hypervisor-present bit. Physical Windows machines with Hyper-V, VBS, or Memory Integrity enabled can set it, causing false positives.
  • Timing measurements, process scanning, debugger checks, or anti-analysis tricks.
  • Network requests or external services.
  • Any attempt to disable or interfere with security tools.

Limitations

VM detection is imperfect. DMI strings can be spoofed, hidden, or changed by the hypervisor, and some physical firmware exposes unusual values. A PHYSICAL result means "no known VM indicator was found", not proof. A VM result can be a false positive; vm_policy="allow" or vm_policy="off" is the documented escape hatch.

This check is not a security boundary and does not make the library impossible to crack or bypass. The real protections are the signed manifest, SHA-256 verification, HTTPS, and keeping the private signing key offline. Do not use VM detection as a substitute for those controls.

Install

The PyPI distribution is gu-7f3a2c; the import package is game_updater and the CLI command is game-updater.

pip install gu-7f3a2c

From a local checkout:

pip install .

This package is for publishers who control both the manifest host and the signing key. Only point it at manifests you signed.

Generate a signing key

python tools/keygen.py --private game_private.pem

Put the printed public key into src/game_updater/public_key.txt.

Keep game_private.pem offline. Never commit it.

Build and sign a manifest

Create manifest.json without a signature:

{
  "version": "1.4.0",
  "sequence": 3,
  "notes": "Season 3 patch",
  "files": [
    {
      "path": "game/data.pak",
      "url": "https://cdn.example.com/game/data-1.4.0.pak",
      "sha256": "…",
      "size": 12345678
    },
    {
      "path": "game/patch.exe",
      "url": "https://cdn.example.com/game/patch-1.4.0.exe",
      "sha256": "…",
      "size": 456789,
      "executable": true
    }
  ],
  "run": "game/patch.exe"
}

sequence is a signed, non-negative integer. Increase it for every release. The updater stores it in version.json and refuses a manifest whose sequence is lower than the installed one, or equal while claiming a different version. That prevents replay of an older signed manifest.

Sign it:

python tools/sign_manifest.py --key game_private.pem --manifest manifest.json --out manifest.signed.json

Upload manifest.signed.json (and the files) to your HTTPS host.

Update a game folder

game-updater --manifest https://cdn.example.com/game/manifest.signed.json --dir "C:\Games\MyGame"

Other modes:

game-updater --manifest ... --dir ... --check    # show versions only
game-updater --manifest ... --dir ... --apply    # install (asks first)
game-updater --manifest ... --dir ... --apply --yes
game-updater --manifest ... --dir ... --apply --run
game-updater --manifest ... --dir ... --apply --vm-policy block

--vm-policy accepts off (default), allow, or block.

Python API

from game_updater import update

result = update(
    manifest_url="https://cdn.example.com/game/manifest.signed.json",
    game_dir=r"C:\Games\MyGame",
    apply=True,
    run_patch=False,
)
print(result)
print(result.vm.summary() if result.vm else "vm check skipped")

To refuse updates on a detected VM:

from game_updater import VMBlockedError, update

try:
    result = update(
        manifest_url="https://cdn.example.com/game/manifest.signed.json",
        game_dir=r"C:\Games\MyGame",
        apply=True,
        vm_policy="block",
    )
except VMBlockedError as error:
    print(error)                        # clear, documented explanation
    print(error.detection.kind.value)   # e.g. "vmware"

Inspect detection without updating:

from game_updater import detect_vm, collect_dmi

print(collect_dmi())
print(detect_vm().summary())
print(detect_vm().indicators)

Layout produced in the game folder

MyGame/
  version.json      # version, sequence, notes, file list
  game/
    data.pak
    patch.exe

version.json is written last, after every file is verified.

Threat model

Attack Mitigation
Compromised CDN / HTTP host Manifest signature check
Modified file in transit SHA-256 per file
Manifest replay of an older signed version Signed monotonic sequence; lower or unchanged sequences for a different version are rejected
Path traversal (../) safe_join rejects absolute paths and ..
Unsigned patch execution run target must be in files, verified, and --run passed
Malicious local manifest URL HTTPS only (localhost allowed only with --allow-insecure)
VM use against policy vm_policy="block" stops before any work; best-effort detection, not a security boundary

Honest limitation

If someone steals your private signing key, they can sign a malicious update. Treat the key like a code-signing key: offline, backed up, never in the repo.

VM detection is a compatibility policy, not DRM. DMI values can be spoofed or withheld, so the check can be wrong in both directions. It does not make the library uncrackable; the signed manifest and SHA-256 checks are the actual integrity controls.

Metadata

Release files for gu-7f3a2c 0.4.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gu-7f3a2c 0.4.2
File Size Uploaded Provenance Actions
gu_7f3a2c-0.4.2.tar.gz 25.3 kB Without provenance Details for gu_7f3a2c-0.4.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for gu-7f3a2c 0.4.2
File Interpreter ABI Platform Actions
gu_7f3a2c-0.4.2-py3-none-any.whl Python 3 none any Details for gu_7f3a2c-0.4.2-py3-none-any.whl

Total release size: 47.6 kB

Release files / gu_7f3a2c-0.4.2.tar.gz

Download URL gu_7f3a2c-0.4.2.tar.gz
Size 25.3 kB
Tags Source
SHA-256 checksum
How to use checksums
f352d020080b54a9c3d874e42f7fa2fd963ad47f18a4d3894e720b80ebeff28b
BLAKE2b-256 checksum
How to use checksums
b4caaa2c5683be192c043aed0d1c98ea8cd7466c17f158cd6fa392bd5e985fdc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Release files / gu_7f3a2c-0.4.2-py3-none-any.whl

Download URL gu_7f3a2c-0.4.2-py3-none-any.whl
Size 22.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cffe234f87f319d62564dc830bd3f8d9bbc2deb18a3d94fa0ef05c3748a62e57
BLAKE2b-256 checksum
How to use checksums
176845813b6ee03f84e9ad882d94ae335ba4b382519d0eaaa5f9d873c1126db8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.11

Security for gu-7f3a2c 0.4.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page