A module can ship as a folder of loose files, or as a single .hyd package.
Packaging is what you do when a module is finished and you want to hand it to someone.
Why package
- One file. A module is thousands of files — the base game module is
around 2,800 entries. Distributing that as a folder is awkward and easy to get wrong.
- Faster startup. This is the strongest practical argument. The package
stores its contents in boot order, so starting up is one sequential read through the front
of the file instead of thousands of scattered ones.
- Integrity and authorship. A signed package can be verified as yours and
unmodified.
- A higher floor. "Open the folder and copy the artwork" becomes
"understand an undocumented container".
Building one
In the module editor, the Package section under Export shows
the module, its version, the file that will be written, and whether signing is configured.
Press Build Package.
The output is named from the module id and version:
Packages/DSC-1.0.1.hyd
Version is part of the filename, so successive builds sit alongside each other rather
than overwriting. Bump the version in the manifest before rebuilding.
The command line
For scripted or repeated builds there is a standalone tool, hydpack, under
Tools/hydpack/:
hydpack keygen <out.key>
hydpack pack <moduleDir> <out.hyd> [signing.key]
hydpack inspect <package.hyd> header, signature, boot order
hydpack verify <package.hyd> hash every entry
hydpack compare <package.hyd> <moduleDir> byte-compare against a source tree
hydpack list <package.hyd> [substring] entries, largest first
Exit codes are meant for scripting: 0 succeeded, 1 failed or bad package, 2 bad usage.
verify fails if either the signature is invalid or any payload hash
does not match — checking one without the other proves very little.
compare is the one to reach for when a packaged module misbehaves and a loose
one does not. It tells you whether the package actually contains what you think it does.
Signing
Signing uses an Ed25519 key over the package manifest, and it answers one question: is
this package from this author, unmodified. Generate a key with hydpack keygen,
and set the server's package-signing-key so the editor can sign what it builds.
Guard the key. keygen deliberately refuses to overwrite an
existing key file, because silently replacing one destroys an identity — every package
signed afterwards carries a different fingerprint, and anyone who pinned the old one sees a
mismatch with no explanation.
Signing is not copy protection, and it is worth being clear about that. Anything the
browser renders, the browser can save; the engine has to be able to open packages, so it
holds what it needs to. What signing genuinely provides is integrity and authenticity.
A package is read-only. Permanently.
This is the part to understand before you package anything, because it is absolute:
- A
.hyd is written once and never modified, appended to, patched or repaired
in place.
- There is no unseal and no overlay that shadows package content for authoring.
- The editor is hard‑locked against packaged modules. Every save path
refuses. The module list marks the row (packaged — read‑only) before you
start editing, and a save attempt returns a specific refusal explaining why rather than a
generic failure.
To change a packaged module you edit its source tree and build a new package. Which means
the rule that matters is: keep the source tree. A package is an output, not
a storage format, and it is not a backup.
The dev loop: loose beats packed
Packages live in /Packages and are discovered separately from
/Modules. When both contain a module with the same id, the unpacked one
in /Modules wins.
That is deliberate and it is your development loop: keep your source tree in
/Modules while you work, and the package you built earlier is simply ignored. You
do not have to move or delete anything to get back to editing.
To test the package itself, move the loose tree aside and restart.
Runtime data does not live in the module
Because a package cannot be written to, anything the game generates had to move out of
module trees: mission logs and player profiles are written to Logs and
Profiles at the installation root, not inside the module they came from.
There is no package sidecar and no per‑package data directory. If your module expects
to write something into its own folder at runtime, it will not work packaged.
What the container is
Enough detail to reason about it, for anyone who needs to:
[0] Header, 64 bytes — magic "HYD1", version, entry count, index offset and hash
[64] Blob region — payloads, 8-byte aligned, ordered by boot priority
[...] Index — path, offset, length, compression method, flags, hash
[EOF] Trailer — manifest hash and signature
Entries are plain (offset, length) spans in one file, read positionally. That
is the property the format exists for: any number of readers in parallel, no locking, no
per‑reader state — which is what a web server serving thousands of assets to a dozen
consoles actually needs. It also keeps HTTP range requests working, so large video and music
still seek.
Boot order puts module.xml first, then
World/, then remaining XML, then HTML, then scripts and stylesheets, with bulk
media last.
Only text is compressed — scripts, markup, XML, CSS, JSON, shaders.
Images, audio, video and models are stored uncompressed because they already are compressed,
and deflating them costs processing on every read for no gain.
A packaged module produces byte‑identical asset keys to a loose one, which is why
everything else in the engine cannot tell the difference — including
module layering. A package takes part in the same later‑wins precedence as
any other module.
Before you ship
- Bump the version in the manifest.
- Build the package.
hydpack verify — signature and every payload hash.
hydpack compare against the source tree.
- Move the loose tree aside, restart, and play it. A package that boots is not the same
claim as a package that plays.
- Put the loose tree somewhere safe. It is the only way to ever change this again.