Skip to Content
Conceptsforest.json Reference

forest.json Reference

forest.json is the package manifest. forest init creates it, and forest install, forest remove, and forest publish keep it up to date. In most workflows you never edit it by hand, but every field is plain JSON and safe to edit.

Example

{ "name": "datastream", "author": "stratiz", "description": "Typed networking for Roblox", "version": "1.4.2", "platform": "roblox", "license": "MIT", "root": "src/init.luau", "dependencies": { "forest/signal": "^2.0.1", "user1/promise": { "version": "^3.1.0", "alias": "promise3" } } }

Fields

FieldTypeRequiredSet byDescription
namestringYes (to publish)forest init prompt (forest publish prompts if missing)Package name. Lowercase letters, digits, and hyphens only. Combined with author it forms the full identifier author/name.
authorstringYes (to publish)forest publish promptThe scope the package is published under: your username, or an organization you’re an admin/owner of.
versionstringYes (to publish)forest init (seeds 0.1.0), then forest publishSemVer  MAJOR.MINOR.PATCH (prerelease/build suffixes allowed, e.g. 1.2.0-rc.1). Managed by the publish flow. See Versioning your own package.
descriptionstringNoforest publish promptShort description shown in search and on the package page.
platformstringYesforest initTarget platform: "roblox" or "uefn". Install and publish both require it, and it decides how a package is laid out and imported.
licensestringYes (to publish)forest publish (detected from your LICENSE file)An SPDX identifier  (e.g. MIT, Apache-2.0) or "SEE LICENSE IN <file>" for a custom license. The registry verifies the declared license against the packaged license text.
rootstringRoblox onlyforest init prompt (auto-detected by forest publish if missing)Relative path to the package’s entry-point module (e.g. src/init.luau), always with forward slashes. See the entry point rule. Also decides where dependencies install during development: into the dependency folder next to the root module (Packages/ unless renamed via packagesDir). Not used on UEFN, where the whole package folder is published.
packagesDirstringNoforest init --packages-dir (absent without the flag)Name of the folder dependencies install into; absent means the default Packages. Must start with a letter, then letters, digits, underscores, or hyphens, max 64 characters; Windows reserved device names (CON, NUL, COM1, …) are rejected in any casing. Roblox only: a UEFN project has one shared ForestPackages mount whose name is part of every package’s using imports, so it can’t be renamed. See Renaming the dependency folder.
dependenciesobjectNo (defaults to {})forest install / forest removeMap of full package identifiers to version specs. See below.
overridesobjectNoforest overrideMap of scope/name to a version range that a transitive dependency is forced onto during resolution. Applies only to your project; not carried along when you publish.
excludesobjectNoforest excludeMap of scope/name to a range of versions banned from resolution, for direct and transitive dependencies alike. Applies only to your project; not carried along when you publish.

Unknown fields are ignored by the registry.

UEFN packages use the same manifest with two differences: there is no root field (a UEFN package is a folder of Verse modules, not a single entry module), and the project’s compatibilityVersion is read from your .uefnproject and attached at publish time rather than stored in forest.json. See Authoring & Publishing.

Dependency entries

Each key in dependencies is a full package identifier (scope/name). The value takes one of two forms:

A version range string, the common case. forest i forest/signal writes a caret range of the version it installed:

"forest/signal": "^2.0.1"

An object with version and alias, written when you install with -a/--alias on Roblox. The alias controls the folder name the package is installed under (and therefore the name you require it by). Aliases may not start with _ or .:

"user1/promise": { "version": "^3.1.0", "alias": "promise3" }

Without an alias, the folder name defaults to the package name without its scope (forest/signal becomes packages/signal). Aliases are how two packages with the same name from different scopes coexist in one project.

Aliases are a Roblox feature. UEFN does not support them: Verse imports are already scope-namespaced, so name clashes are disambiguated with qualified access instead. See Scopes & Collisions.

For range syntax and how ranges resolve, see Dependencies & Versioning.

Renaming the dependency folder

On Roblox, dependencies install into a folder named Packages by default. Pass --packages-dir <name> to forest init to pick a different name, recorded as packagesDir:

"packagesDir": "roblox_packages"

The name is per-package and travels with every published version: your own dependencies mount under it while you develop, and when a consumer installs your package the same folder name is recreated inside it, so requires like require(script.roblox_packages.signal) keep working in every consumer’s tree. Packages that never set the field (including all mirrored Wally packages) keep the default Packages.

A valid name starts with a letter, followed by letters, digits, underscores, or hyphens, up to 64 characters. Windows reserved device names (CON, PRN, AUX, NUL, COM1 through COM9, LPT1 through LPT9) are rejected regardless of case, so con and lpt3 are rejected too. Hyphenated names are legal, but your own code has to bracket-index them (script["my-packages"]).

packagesDir is a Roblox field. A UEFN project has a single shared ForestPackages mount, and that name is written into every package’s using imports, so a package authored against a renamed mount could never co-install with everything else. The registry rejects the field on UEFN publishes.

forest-lock.json

Alongside the manifest, the CLI maintains forest-lock.json: the exact resolved version and SHA-256 integrity hash of every package in your dependency tree (direct and transitive). Tarballs are content-addressed by that hash, so the lockfile both pins and verifies every install. The lockfile also records the overrides and excludes it was solved under, so editing either map (even by hand) triggers a re-resolve on the next install.

Commit forest-lock.json to source control. With a lockfile, forest install reproduces the identical tree on every machine; without one, ranges are re-resolved and may pick newer versions.
Last updated on