Documentation menu

Guides

Publishing

Last updated October 23, 2026

The mechanics of the embr publish command have their own page; this one is about the registry's rules.

Scopes#

First publish claims the scope for your GitHub account. From then on the scope belongs to that account — names within it are yours to manage.

Versions are immutable#

Republishing an existing version is a 409. There is no unpublish from the CLI — delisting is registry moderation, and existing lockfiles keep working even for delisted packages. Ship a new version to fix a mistake.

Two marks can change after publish: a yank and a deprecation. Neither changes the files of a version.

Several targets#

A package can ship one release for each target, with one [[target]] table for each. A target ID is the environment for Luau, else the environment and the language, such as roblox-alloy. Each target of a version has its own archive and its own index entry. See several targets.

embr publish                        # every target
embr publish --target roblox-alloy  # one target only

Yank a version#

A yank keeps a version out of new installs. Use it for a release that is broken or unsafe.

embr yank acme/rocket@1.2.0
embr yank acme/rocket@1.2.0 --target lune   # one target only
embr yank acme/rocket@1.2.0 --undo               # restore it
  • Fresh resolution skips a yanked version. "Latest" on the website skips it too.
  • A project whose ember.lock pins the version still installs it, with a warning. This is the cargo rule, and it keeps existing projects working.
  • The archive stays downloadable, for the same reason.
  • Without --target, the yank covers every target of the version.
  • The older --environment <name> still works. It means the Luau target of that environment.
  • When every version of a package is yanked, search no longer lists it.

A yank is not a takedown. Anyone with the version in a lockfile still gets it. For malware or a leaked secret, also send a report with the Report button on the package page.

Deprecate a package or a version#

A deprecation tells users to move off a package, or off one version. The reason says why and what to use instead.

embr deprecate acme/rocket "Use acme/rocket2 instead."
embr deprecate acme/rocket@1.2.0 "Breaks on Lune 0.9; use 1.2.1."
embr deprecate acme/rocket@1.2.0 "Lune only." --target lune
embr deprecate acme/rocket --undo
  • A deprecated package or version still resolves and installs. Each install that picks it prints the reason as a warning.
  • The reason is 1 to 300 characters. A new reason replaces the old one.
  • --target <id> narrows a version's deprecation to one target. It needs the scope/name@version form.
  • The website shows the reason in a banner on the package page and marks the package in search.

Only the scope's owners can yank or deprecate. Both commands sign in the way embr publish does, and they share its rate limit of 10 calls per 10 minutes.

Logos#

[package] logo names a picture of the package: a .png, .jpg, or .webp file inside it, at most 512 KiB. The registry shows it in place of the box icon. See [package].

A logo must follow the policies like any other content. Anyone can report a package for an inappropriate logo with the Report button on its page.

Models#

A Roblox package can ship .rbxm models next to its code. The registry checks each model when you publish: no scripts, one root, binary only, and real code beside it. See Models in packages.

Authors grant co-ownership#

Every login in [package] authors is appended to the scope's owner list on publish and can then publish to the entire scope, not just that package. The list is append-only — removal is a manual moderation edit. Usernames are shape-checked but not verified to exist, so a typo silently grants a nonexistent account. Treat the authors field like an access control list, because it is one. This scope-wide grant is subject to change in a future release.

Authentication#

embr publish signs in with the GitHub device flow, requesting no OAuth scopes — the registry only maps the token to a GitHub login. The token is stored in ~/.ember/credentials.toml (0600 on unix). embr auth logs in ahead of time, stores a token you already have, shows the login, and logs out.

Provenance#

The repository field in a manifest is self-declared: a version can point at any repository it likes. Provenance is what turns that link into a fact. When a version is published from GitHub Actions with id-token: write, the registry verifies the workflow's OIDC token and stores where the version was really built — repository, commit, workflow, and run — and the package page presents it in a provenance panel. Setup is a single permission line; see embr publish.

Hand-published versions carry no provenance and are not marked as suspicious. Absence means no claim was made, not that a claim failed.

Practical rules#

  • Tarballs are capped at 10 MB.
  • private = true makes publish refuse the package.
  • Check what you ship with embr publish --dry-run (full file list); the manifest's includes/excludes shape the selection.
  • The registry's policies cover names, takedowns, and what being public means.