Log inGet started

Package (asset type)

A package is a shippable, heterogeneous library folder. It bundles components, modules, scenes, presets, tools, and any other typed assets under a single addressable identity (@<owner>::<name>) that…

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 opt into via world.installLibrary and resolve sub-assets against (@<owner>::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.
    • 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/libs/@<owner>/<Name>.package/
--   package.yaml   (metadata scaffold)
--   README.md      (instance README template)

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. world.installLibrary("@<owner>::<name>") resolves the package and mounts its identity in the current world. Once installed, sub-assets are require-able as @<owner>::<name>.<sub_module>.
  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. Uninstall. world.uninstallLibrary("@<owner>::<name>") removes the package's mount; sub-asset references become unresolved.
  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.
  • world.listLibraries() — packages currently installed in this world.

Authoring conventions

  • Name packages by their feature (voxels, thirdPersonController, dialogSystem), not by their owner — the owner is the @<owner>:: 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