Log inGet started

Package (asset type)

Updated 22 August 2026

A library is the addressable root scope content resolves under (@builtin, @<name>) — a whole world mounted into this one under a namespace, via zm.installLib / world.installLibrary. A package is an asset that lives inside such a root, and one library holds as many packages as its author put there. A package is installed by guid the way every asset is (zeromind.install, zm.installAsset).

When to use one

  • You're authoring a coherent feature suite (character controller stack, voxel engine, UI kit, animation library) and want to ship it as one install target.
  • You want a stable identity worlds can install and resolve sub-assets against (@<lib>::pkg.sub_module).
  • You want versioning + a per-package metadata layer that travels with the contents.

If you're authoring inside a single world and don't need a separate install lifecycle, you don't need a package — drop your .component / .module / etc. directly in /zero/source/. Packages are for cross-world distribution.

Where it lives

  • Source: /zero/source/.../<Name>.package/ (or, for legacy packages, a folder containing package.yaml at its root).
  • Identity: <Name> (the .package suffix collapses in identity — mirrors .module). A file at <Name>.package/<sub>/init.luau resolves under the same identity it would at <Name>/<sub>/init.luau.
  • Folder shape (container-style — open child set):
    • package.yaml — metadata (name, version, description). Required.
    • README.md — instance documentation. Required.
    • .metadata — agent-editable tags + free-form fields. Required.
    • Sub-asset folders: any combination of .component/, .module/, .scene/, .preset/, .tool/, .toolbox/, .material/, .shader/, etc.

The validator permits unlisted children (allow_unlisted: true) by design — packages are open containers.

How to create one

asset.create("package", "<Name>")
-- Creates: /zero/source/<Name>.package/
--   package.yaml   (metadata scaffold)
--   README.md      (instance README template)

-- `folder` places it in a subfolder of /source instead of the root:
asset.create("package", "<Name>", { folder = "systems" })

Then scaffold sub-assets inside it:

local pkg = asset.resolve("<Name>.package")
asset.create("component", "Spinner", { into = pkg })
asset.create("module", "util", { into = pkg })

How it operates

  1. Registration. Writing package.yaml indexes the package with the package registry.
  2. Install. A package is installed by guid, the way every asset is: zeromind.install from the shared library, or tools.use("zm", "installAsset", <guid>) inside the engine. The install writes the package's files (and the deps its .refs record) into this world's source, so its sub-assets resolve here. tools.use("zm", "preview", <guid>) reports what an install would write before it writes anything.
  3. Sub-asset resolution. Because .package collapses in identity, sub-asset paths work whether the package folder is suffixed (<Name>.package/<sub>) or not (<Name>/<sub>).
  4. Updates. tools.use("zm", "updates") lists installed content with upstream changes available; re-running the install pulls them.
  5. Metadata. package.yaml::name overrides the folder stem if present (rare); version + description are surfaced in the package index.
  6. Hot reload. Edits to sub-assets hot-reload as normal; edits to package.yaml reload package metadata.

Discovery

  • asset.list("package") — every registered package.
  • asset.inspect("<name>") — sub-asset catalog, source, this type README.
  • cat /zero/source/<Name>.package — same summary.
  • library.list() — the library assets registered in this world, including the ones packages under an installed library contribute.

Authoring conventions

  • Name packages by their feature (voxels, cameraRigs, dialogSystem), not by their owner — the host library is the @<lib>:: scope prefix.
  • Cluster cohesive sub-assets together. A "character controller" package should ship the controller component, its preset configs, its supporting modules, and a demo scene — not just the component.
  • Document the package's surface in README.md — what install installs, what each sub-asset does, how they relate. Agents reading cat /zero/source/<Name>.package get the full picture in one shot.
  • Version conservatively — breaking a package means breaking every world that installed it. Use semantic version bumps.

Common pitfalls

  • Missing package.yaml. Without it the folder isn't recognized as a package even if it has a .package/ suffix.
  • Dependency installs. A package that depends on another package must declare it (in package.yaml::dependencies once supported) and the parent world must install dependencies in order.
  • Identity collapse. Sub-asset paths skip the .package suffix. Don't include .package in require strings.

Referencing inside a package: the ~ rule

~ means this package, resolved structurally from the referring file's own location. It is the only package-specific syntax. It is rename-safe (no package name is ever written) and library-agnostic (it never names the host library, so it survives @builtin being renamed or the package being mounted elsewhere). ~.tail maps to <pkg>.package/tail from anywhere inside the package.

Referencing fromUseExample
An asset/module inside this package~.tailrequire("~.modules.myThing"), shader: "~.shaders.myThingBlend"
A different package under the same rootroot-relative identityrequire("@root::systems.otherPkg.modules.x")
A different library's public API (a deliberate external dependency)@otherlib::…require("@physics::modules.rigidbody")

Never write @builtin:: (or any host-library name) to reference your own package — that couples the package to the library it currently lives in and breaks if the library is renamed or the files move. Use ~.. It works from Luau require() and from asset-reference strings in data files (a material's mat.yaml shader: field, a component's default asset ref); the engine resolves it against the referring file's own package. In a .luau file holding a ~. ref as a string, resolve it explicitly against your own path: asset.resolve("~.materials.myThingStandard", "material", __FILE__).

~. is the package-relative self-reference; @root:: is the root-relative one (the library root, or the world source tree for non-library content). The core/requiring-modules guide covers the root model in full.

A package that ships substantial functionality should include a .guide asset (a <name>.guide/guide.md folder anywhere under the package); the guides tool discovers it live, so collaborators find the system the way they find built-in guides. A package's test suite reaches the shared Test framework through its enclosing asset: local Test = asset.containing(__FILE__).modules.shared (asset.containing returns the deepest enclosing typed asset, the .testSuite, not the outer .package).

  • Any of .component, .module, .scene, .preset, .tool, .toolbox, .material, .shader, .style, .service, .importer — packages bundle these.
  • asset-type
  • reference