The manifest
[dependencies] & [indices]
Last updated October 23, 2026
[dependencies] records what the project uses; [indices] names where
dependencies can come from.
[indices]
wally = "https://github.com/UpliftGames/wally-index"
pesde = "https://github.com/pesde-pkg/index"
[dependencies]
Promise = { name = "evaera/promise", version = "^4.0.0", index = "wally" }
Bin = { name = "chief/bin", version = "^" }
Traits = { workspace = "chief/traits", version = "^" }
The two dependency forms#
- Registry:
{ name = "scope/pkg", version = "…", index = "key" }—indexis a key into[indices]; absent means the default index (below).namefollows the package name rules. - Workspace:
{ workspace = "scope/pkg", version = "^" }— another member of the same workspace, linked live during development.versionis optional and defaults to^.
Aliases are the keys#
The [dependencies] key is the dependency's alias: it names the
generated link file (so it is the name you require), and it is how
overrides address edges — case-sensitively,
exactly as spelled. Only declared dependencies get a top-level link;
transitive packages are reached through each package's own nested links.
Aliases containing dots or whitespace cannot be addressed by overrides.
A key can also be the lowercase scope/name of a package, in quotes.
embr add --scoped writes that form. Two packages with
the same short name, such as mayari/core and chief/core, can then both
be dependencies:
[dependencies]
"mayari/core" = { name = "mayari/core", version = "^0.8" }
"chief/core" = { name = "chief/core", version = "^0.2" }
Install links a scoped key in a folder named after the scope, at
packages/<environment>/<scope>/<name>.luau.
- A key must be a plain file name, or a lowercase
scope/name. A key that names no link file, such as"../x"or"a/b/c", fails before install changes the disk. - A plain key and a scope folder with the same name in one tree are refused, because the two names would mean two things:
Dependency keys 'quill' and 'quill/init' collide in the roblox tree: 'quill.luau' and the 'quill' scope folder share a name; rename one of them
Version requirements#
Standard semver requirements (^4.0.0, ~1.2, =2.0.0,
>=4, <5). A bare ^ or * (or an empty string) means "whatever is
newest". embr install re-resolves on every full run, so ^ picks up new
releases; embr install --locked replays the lockfile instead.
In the workspace form, version means something different: it is a
version type (^, ~, =, *) or a full requirement applied to the
member's current version only at publish time. During development it
is ignored entirely — you always get the member's current code. See
Workspaces.
entry#
Which module inside the package its link file points at. Most packages name their own, and Ember works the rest out (see the detection order) — including a guess for packages that name nothing at all, which the install tells you about:
warning: synttx/vow names no entry point; guessed src/vow (set `entry` on the dependency to choose another)
entry is how you correct that guess, or supply one where Ember refused to
guess. With or without the file extension:
[dependencies]
Vow = { name = "synttx/vow", version = "^", index = "wally", entry = "src/vow" }
It is stated per package, not per alias, so the same value is used
wherever that package turns up in the install — including a dependent's
nested link to it. A transitive package Ember cannot find an entry for is
fixed by declaring it yourself with an entry.
An entry that isn't there is reported and ignored, falling back to
detection, rather than writing a link file that points at nothing. Inside
an overrides value the key parses but has no
effect.
target#
Which environment tree the dependency installs under, overriding the one it
would land in. It takes any of the seven
environments: roblox, roblox_server, lune,
lute, seal, zune, or luau. The old names still work: shared for
roblox, and server for roblox_server. A package published for Roblox
can be pulled into the server root
instead of the project's own. It also picks the matching target of a
multi-target index entry.
[dependencies]
Lapis = { name = "brinkokevin/lapis", version = "^", target = "roblox_server" }
Path = { name = "acme/path", version = "^", target = "lune" }
A target also takes a dependency for another environment on purpose.
Without it, that dependency fails the install. Ember then checks the
packages under the dependency against the environment that target names.
A target cannot cross a language. See
What a project installs.
A typo is refused, naming the entry that stated it.
Direct dependencies only, and that is a rule about the tree rather
than a shortcut. Every other entry reaches the resolver with a context
inherited from the root of its subtree, and moving one out of that tree
would strand its dependents: they link what is installed alongside them and
nothing else. So a target Ember only ever meets transitively — an
overrides value, or a
workspace member's own dependency while the root
installs — has no effect. It is honored when that member installs itself,
where the same entry is a direct dependency.
Named indices#
[indices] maps a name to a git repository. Three formats are
auto-detected: wally (root config.json), pesde (root config.toml),
and Ember's own index (pesde format whose entries carry a baked download
URL). Any git repository in one of those formats works as a custom index.
Forest is the one index that is not a git
repository. Its URL is its API, https://api.forest.dev, and Ember knows
it by that host:
[indices]
forest = "https://api.forest.dev"
[dependencies]
DataStream = { name = "stratiz/datastream", version = "^2", index = "forest" }
Forest packages install into roblox. A package that Forest copies from
Wally keeps its wally.toml, so its realm picks roblox or roblox_server.
embr index add manages the table, and the names
wally, pesde, and forest are shorthands there: it fills in their
URLs automatically. In the manifest itself there are no shorthand values;
every entry is a full URL.
The key default is special: it overrides Ember's own registry
(github.com/ember-luau/index) for dependencies that name no index.
Bare dependencies go to Ember's registry unless [indices] default
says otherwise.