Mods & game data
Mods: the complete guide
Install and declare stack, world and personal mods.
On this page8 sections
- Three layers of mods
- Life of a world mod
- Declaring mods on a server
- A resource that owns its package
- A hosted package from Warden
- An indexed package
- What a package may contain
- Worked example: a Nexus vehicle on the freeroam server
- What the launcher does with the files
- Developer builds and the local directory source
- Verifying that a mod loaded
- Troubleshooting
- Reference
Configure the mods required by a server and understand how the launcher installs them. For trust and review rules, see Server-required mods; for Lua packages, see Server resources.
Three layers of mods
A player's game folder is managed by the launcher as three layers. Knowing which layer a file belongs to answers most questions before they are asked.
| Layer | What it holds | Who decides | Installed when |
|---|---|---|---|
| Stack | Open//77 itself: RED4ext, redscript, the Open77 plugin, the WebUI host, the optional packages of the signed index (CET, compat shims, β¦) | The platform's signed mod-stack index, per channel and generation | Every launcher start-up on the official directory source |
| World | What one server requires: loaders (ArchiveXL, TweakXL), a world's archives and tweaks, a vehicle, a script fix | The server, through requiredMods and resource preload_mod declarations |
On Connect, laid over the stack |
| Own (Solo profile) | The player's personal mods, untouched by Open//77 | The player | When the player switches to Solo in the mod menu |
The launcher keeps a journal of every file it placed, tagged with its layer
(%LOCALAPPDATA%\Open77\modstore\state\projection.json, one entry per file with layer: stack
or layer: world). Two rules follow from it:
- A world projection keeps every stack file exactly as it is and only removes the previous world's files. Joining another world swaps world files; it never touches Open//77.
- The stack projection at launcher start-up owns the whole folder and removes world files, so a world's archives never leak into another world or into the player's own game.
World mods are therefore re-laid on every Connect. They are not re-downloaded: blobs are
content-addressed and stay in the player's store (modstore\blobs\<sha>), so a returning player
only pays the projection.
Life of a world mod
The path from your disk to the player's, with the file or log line to look at for each step.
-
The server declares it. Either a resource carries
preload_mod "dist/package.zip"or an operator committed a hosted package from Warden. At start-up the server inspects each archive, stages it underserver/.open77/mods/by hash and announces the set:[INF] Required mods: 5 declared, digest=eccac11fβ¦, enabled=True, unsecured=True. [INF] [resource:aventador_svj] started generation=1Every resource-owned package becomes a required mod named
<resource>-assets; the digest covers the whole set and changes whenever a package does. -
The server hosts the bytes beside its resource endpoint, immutable and content-addressed:
GET http://<host>:<resource-port>/mods/v1/blobs/<sha256>The port is the one the server announces in its heartbeat (
11779on the development server, next to game port11778). A server whose HTTP listener is down shows up in the launcher as This world's mods could not be downloaded. -
The master relays the list. The catalogue row carries
requiredMods(id, version, sha256, sizeBytes, url), the digest and theunsecuredflag. The launcher and the in-game browser read the same row. -
The launcher decides. On Connect it asks the master for a verdict on every hash, applies the capability cap (executable content needs a verified verdict, or the player's consent on an unsecured world), shows the consent gate, then fetches whatever the store lacks, hashing every byte against the announced value. Each archive is inspected β stored or freshly downloaded β and the package is laid over the stack:
unsecured world: accepting unreviewed executable content in 'tweakxl assets' ('red4ext/plugins/TweakXL/TweakXL.dll') modstack: world layer keeps 291 stack file(s) in place world 'Open77 Development': projected 54 file(s), removed 0, kept 291 in place. -
The game starts with a contract. The launcher passes the target and the digest it installed for this boot:
Variable Meaning OP77_CONNECThost:portto auto-connect toOP77_CONNECT_MASTERThe directory source the ticket came from ( alpha,development); the in-game shell enrols the identity with that masterOP77_WORLD_SERVERThe world's server id OP77_WORLD_MODS_DIGESTThe required-mod digest this boot was prepared for REDengine mounts
archive/pc/modat boot, RED4ext loads the plugins, redscript compilesr6/scripts, TweakXL appliesr6/tweaks. Nothing in this layer can be added mid-session. -
The shell checks the contract. When a player picks a world from the in-game browser, the shell compares the world's digest with
Open77.session.launcherContext().modsDigest. A match connects; a mismatch shows the Restart required prompt, which hands off to the launcher throughopen77://connect?server=β¦&endpoint=β¦&relaunch=<pid>and quits the game. See Joining from the in-game server browser.
Declaring mods on a server
A resource that owns its package
The recommended shape for anything a world needs: the package lives with the resource that needs it, the manifest states the loaders it depends on, and selecting the resource declares everything.
-- resources/assets/maps/kujira_world/open77.lua
resource "kujira_world"
version "1.0.0"
auto_start true
dependency "archivexl >=1.27.1"
dependency "tweakxl >=1.11.4"
preload_mod "dist/kujira.zip"
server_script "server/main.lua"
- Third-party packages live under
resources/assets/(loaders/,maps/,vehicles/,mods/); everything butloaders/is ignored by git, so a checkout never carries someone else's archive. See Layout of the resources folder. - The archive is a
.zipor.7zwhose entries are game-root relative paths (archive/pc/mod/β¦,r6/tweaks/β¦,r6/scripts/β¦). Nexus archives usually are already; a wrapping folder (MyMod/archive/pc/mod/β¦) must be stripped. - Up to eight packages per resource, 1 byte to 64 MiB each; every declared path must exist.
- A dependency must itself be in
resources.load. The loaders ship as resources (resources/assets/loaders/archivexl,resources/assets/loaders/tweakxl) that wrap the upstream release zip, so several worlds share one copy and the server starts them first. requiredMods.enabledmust betrue. Executable content (a.dll, a.reds, CET Lua,.asi) in a preload is refused unless the server is unsecured or the hash is verified.- Changing a package needs a server restart and a fresh launcher boot of the game.
ensurereloads the Lua, not the depot.
The load list in server.jsonc selects it:
"resources": {
"load": ["freeroam", "archivexl", "tweakxl", "kujira_world"]
}
A hosted package from Warden
For an operator without access to the resource tree: Warden β Mods β upload, read the plan
(file, root, inert/executable, adds/replaces), commit, restart. Warden writes the requiredMods
entry for you. Full walkthrough in
Add a mod from the Warden panel.
An indexed package
A package the platform distributes from its signed index is pinned by id and minVersion and
costs the operator nothing. The index carries what Open//77 is licensed to redistribute; a package
missing from it makes the world unjoinable, so check the index before pointing at it.
What a package may contain
The launcher confines every file to a known root and classifies the package by its most privileged file.
| Root | Typical content | Class |
|---|---|---|
archive/pc/mod |
.archive, .xl |
inert |
r6/tweaks |
TweakXL .tweak / .yaml |
inert |
engine/config |
INI overrides | inert |
r6/input, r6/config |
input and config XML | inert |
r6/scripts |
redscript .reds |
executable |
red4ext/plugins, bin/x64/plugins |
.dll, .asi |
executable |
bin/x64/plugins/cyber_engine_tweaks/mods |
CET Lua | executable |
engine/tools, bin/x64, mods |
tooling, loaders | executable |
Anything outside these roots, a path-traversal entry, or bytes that do not hash to the announced value refuses the package.
Worked example: a Nexus vehicle on the freeroam server
Adding Lamborghini Aventador SVJ (Nexus 11176) to the development world, spawnable with /car.
The mod requires ArchiveXL, TweakXL, RED4ext, redscript and the author's Vehicle manufacturer
fix (Nexus 28744); CET is listed only for its spawn command and is not needed here.
-
Download the archives from the author's page (the launcher never rehosts what the author did not license; keep the licence in mind before making it a public requirement). Inspect them:
KRNLNIK Lamborghini Aventador SVJ-11176-1-4.zip archive/pc/mod/aventador_svj.xl archive/pc/mod/KRNLNIK_Lamborghini_Aventador_SVJ.archive r6/tweaks/krnlnik/lamborghini_aventador_svj.tweak r6/tweaks/krnlnik/lamborghini_aventador_svj_ICONS.tweak VMF-28744-1-0.zip r6/scripts/VehicleManufacturerFix.redsBoth are game-root relative. Repack without directory entries if you want the package minimal; the layout is what matters.
-
Create one resource per package. The fix first, because the car depends on it:
-- resources/assets/mods/vehicle_manufacturer_fix/open77.lua resource "vehicle_manufacturer_fix" version "1.0.0" auto_start true preload_mod "dist/vmf.zip" server_script "server/main.lua"-- resources/assets/vehicles/aventador_svj/open77.lua resource "aventador_svj" version "1.4.0" auto_start true dependency "archivexl >=1.27.1" dependency "tweakxl >=1.11.4" dependency "vehicle_manufacturer_fix >=1.0.0" preload_mod "dist/aventador_svj.zip" server_script "server/main.lua"A
server/main.luathat prints ononResourceStartis enough; the package does the work. -
Select them and restart. Add
"vehicle_manufacturer_fix", "aventador_svj"toresources.load. The.redsfile is executable content, so this only boots on an unsecured server. Restart and read the boot log:Required mods: 5 declaredwith a new digest, onestartedline per resource. -
Keep third-party bytes out of git. The repository ignores
resources/assets/maps/kujira_world/,resources/assets/mods/vehicle_manufacturer_fix/andresources/assets/vehicles/aventador_svj/for that reason; the manifests are tiny, the archives are someone else's work. -
Connect through the launcher. The consent gate reads 5 packages, 3 executable; accept, and the log shows
projected 54 file(s), removed 0, kept 291 in place. The game boots with the archive mounted and the tweak records registered. -
Spawn it. The freeroam
/carcommand accepts anyVehicle.*record whenConfig.vehicles.allowCustomModelsistrue(resources/gamemodes/freeroam/shared/config.lua):/car Vehicle.aventador_svj_01The record name comes from the mod's tweak file.
unknown_modelmeans the record did not match^Vehicle%.[%w_]+$or the flag is off; an invisible or wrong vehicle means TweakXL did not load the record β check its log (below).
What the launcher does with the files
- Store.
%LOCALAPPDATA%\Open77\modstore\:blobs\<2 hex>\<sha256>(archives and every file inside them),pkg\<id>\<version>\modpkg.json(stack package manifests),state\projection.json(the journal). Nothing here is ever served to anyone. - Projection. Files are hardlinked from the store into the game folder when the volume allows
it, copied otherwise (
--force-copyforces copies). Overwriting a hardlinked file in place β a local build deployed by hand β mutates the store's blob; the launcher notices at the next verification (not in the store yet) and re-fetches it. A placed file that no longer hashes as recorded counts as an unmanaged copy: an exclusive file is replaced by the stack's version (the log says so), a contested one is reported and left alone. - Journal and commit. A projection writes
projection.pending.json, moves files, then commits. An interrupted run is recovered on the next plan (a pending journal was found). - Removal. Only files the launcher placed and that still hash as recorded are removed. A
package that
replacesa shipped file restores it on removal; prove that round trip on a test client before requiring such a package. - The Solo profile projects the player's own set and takes Open//77's out; browse servers puts it back. World files never appear in Solo.
Developer builds and the local directory source
The launcher's Directory source menu offers the official master and Local development
(http://127.0.0.1:8090). On the local preset the launcher skips the stack update and the launcher
self-update (a release launcher only trusts the production signing key), refuses the browser sign-in
(it goes through the production website) in favour of the e-mail/password form, and still fetches
and lays world mods. A dotnet build launcher never self-updates either; it reports version
1.0.0, which every release would otherwise out-rank.
Because the stack is not managed on that preset, branch builds deployed into the game folder stay in place. If the stack ever needs rebuilding, the headless CLI does it without the GUI:
$env:OP77_LAUNCHER_MASTER_URL = "https://master.open2077.net/"
Open77Launcher.exe --mods-apply --commit --game-dir "<game folder>"
Verifying that a mod loaded
In order of cost, stop at the first that fails.
- Launcher log (
%LOCALAPPDATA%\Open77\launcher.log) at the Connect:world '<name>': projected N file(s), removed M, kept K in place.No such line means nothing was laid; aworld mod fetch failed: <id> <reason>line names why. - Files on disk in the game folder, at the paths the archive declared
(
archive\pc\mod\<name>.archive,r6\tweaks\β¦,r6\scripts\β¦,red4ext\plugins\<loader>). - Loader logs after the game booted:
red4ext\logs\red4ext-*.loglists the plugins it loaded (ArchiveXL, TweakXL, Open77);red4ext\plugins\ArchiveXL\ArchiveXL.loglists every.xlextension and archive it registered;red4ext\plugins\TweakXL\TweakXL.loglists the tweak files it applied and any record error. A redscript compile error shows a dialog at boot naming the script. - In game. A world mod: go where the geometry is. A vehicle:
/car <record>. A tweak: the record exists (Game.GetVehicleSystemvia CET, or a server-sideOpen77.vehicles.createwith the record). - Server side.
Required mods: N declaredat boot, and eachGET /mods/v1/blobs/<sha>answering200with the archive's size.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
This world's mods could not be downloaded / log world mod fetch failed: β¦ Unreachable |
The server's resource HTTP listener is not reachable (server down, port blocked, wrong public URL in its heartbeat) | Check the server process and GET /mods/v1/blobs/<sha> from the player's side; fix the public URL |
| Game boots vanilla after a Connect (no Open77 menu) | The stack was removed from the game folder | Launchers after op77.27 lay worlds over the stack; on older ones run the stack apply above or reinstall from the launcher |
| Connected, but the world's content is missing and nothing complains | The mods were not projected on this Connect (launchers up to op77.27 only installed a package the first time it was downloaded) | Update the launcher; verify with the projected log line |
identity_proof_invalid right after a launcher-started auto-connect |
The identity was enrolled with a different master than the one the server verifies against (the page's remembered directory source overrode OP77_CONNECT_MASTER) |
Fixed in the shell (the launcher's master is pinned); on older clients pick the same master in the in-game menu before connecting |
| Restart required prompt in the in-game browser | This boot was prepared for another world's digest | Accept; the launcher relaunches into the world with its mods |
mods_relaunch_required with no launcher installed |
The game was started without the launcher | Install the launcher, or start the game through it |
required_mods_refused / a package named as contains code |
Executable content without a verified verdict on a secured server | Request a review, or run the server unsecured for a trusted community |
required_mods_consent loops |
The player declined the consent gate | Expected; the world is not joined |
mod_attestation_unavailable |
The master could not be reached for verdicts | Retry; check the directory source |
| Hash mismatch in the log | The server serves different bytes than it announced | Restart the server so it re-stages; if it persists, the package on disk changed |
/car β¦ β unknown_model |
Record name malformed or allowCustomModels off |
Use Vehicle.<id> exactly; enable the flag |
| Vehicle spawns as the default model or invisible | TweakXL did not register the record, or the archive is not mounted | Check TweakXL.log and ArchiveXL.log; confirm the files are on disk |
| redscript error dialog at boot | A .reds in a world package does not compile against this game build |
Remove the package or update it; the dialog names the file |
Server refuses to start: requiredMods / preload_mod error |
Disabled requiredMods with a preload, an executable preload on a secured server, a missing package path, an over-size archive |
Read the named directive; the boot log points at the resource |
Reference
| Where | Path |
|---|---|
| Server staging | server/.open77/mods/ |
| Server hosting | http://<host>:<game port + 1>/mods/v1/blobs/<sha256> |
| Server config | server.jsonc β requiredMods { enabled, unsecured, packages }, resources.load |
| Resource manifest | resources/<name>/open77.lua β preload_mod, dependency |
| Launcher store | %LOCALAPPDATA%\Open77\modstore\{blobs,pkg,state} |
| Launcher log | %LOCALAPPDATA%\Open77\launcher.log |
| Launcher settings | %LOCALAPPDATA%\Open77\launcher-settings.json (master, modChannel, worlds joined) |
| Game folder roots | archive/pc/mod, r6/tweaks, r6/scripts, red4ext/plugins, bin/x64/plugins, engine/config |
| Loader logs | red4ext/logs/, red4ext/plugins/ArchiveXL/ArchiveXL.log, red4ext/plugins/TweakXL/TweakXL.log |
| Boot contract | OP77_CONNECT, OP77_CONNECT_MASTER, OP77_WORLD_SERVER, OP77_WORLD_MODS_DIGEST; Open77.session.launcherContext() |
| Deep link | open77://connect?server=<guid>&endpoint=<host:port>&relaunch=<pid> |
// For agents & LLMsthis page as Markdownllms.txtllms-full.txt
