Facetguides

Using another registry

Point the CLI at a fork, a private registry or a local build.

The CLI fetches components at runtime rather than bundling them, so their source is a setting. Four ways to change it, most specific first.

Resolution order

#SourceSet byUse for
1FACET_REGISTRY_DIRenv, a local directoryTesting a registry build without publishing it
2--registry <url|path>, or registry in facet.jsonflag or configForks, private registries, version pins
3FACET_REGISTRY_URLenv, a URLCI, or a machine-wide default
4https://facet.astra-void.xyz/rbuilt inThe published registry

A value starting with http:// or https:// is read as a URL. Anything else is resolved as a filesystem path.

Each of the first three
FACET_REGISTRY_DIR=site/r npx facet-rbxts list
npx facet-rbxts add button --registry https://ui.example.com/r
FACET_REGISTRY_URL=https://ui.example.com/r npx facet-rbxts list
facet.json — persistent, and committed with the project
{
"registry": "https://ui.example.com/r"
}

The config field is the one for a team — everyone who clones the project gets the same registry.

What a registry has to serve

Three kinds of file over plain HTTP (or a directory with the same layout):

<base>/index.json the index — every item, its files, dependencies, and tokens
<base>/button.json one payload per item, source text inlined
<base>/utils.json

The CLI reads index.json first and refuses anything whose version is not 1, telling the user to upgrade rather than guessing. Item payloads are fetched lazily, only for what is being added, removed or diffed.

A 404 on an item reads as a typo, a 404 on the index as a moved registry. Requests carry accept: application/json and time out after 15 seconds.

The full shape is in the registry format reference.

Running a fork

The Facet repo builds its registry with one script, and a fork inherits it:

In a fork of astra-void/facet
pnpm registry:check # structural validation — names, types, dependency resolution
pnpm registry:build # registry/ → site/

site/ is generated and never committed, so the published registry cannot drift from registry/. The build emits the index, one JSON per item, and an immutable r/<sha>/ copy of both. It also emits a landing page, a CNAME, a .nojekyll, schema.json and revisions.json. Without the .nojekyll, Pages runs Jekyll, which eats underscore-prefixed paths.

schema.json is generated from the FacetConfig type rather than checked in, so it cannot lag the CLI. It is typed to cover every key, so a new config field does not compile until it is described.

Test against the working tree before publishing anything:

The CLI, against a local build
FACET_REGISTRY_DIR=site/r npx facet-rbxts list
FACET_REGISTRY_DIR=site/r npx facet-rbxts add button --cwd ../some-project

Adding your own components to a fork

Three edits, and it is not done until all three are made:

  1. The source in registry/src/ui (or lib, hooks), addressing other registry files with ~/ — never a relative path, which the CLI cannot rewrite.
  2. An entry in registry/registry.ts declaring every import as a dependency (npm) or a registryDependency, plus the semantic tokens its classes name so facet doctor can check them.
  3. A scene in apps/playground — the only place anyone sees it render.

registry:check enforces the structural half. Unique names, known item types, resolvable registry dependencies, no file claimed by two items, and one spec per npm package across the whole registry. facet add unions dependency strings, so two spellings would both reach the package manager.

What it cannot check is a missing npm dependency — an import the entry never declared, which ships a file that cannot compile.

A private registry

Nothing about the format requires GitHub Pages — any static host works, and so does a directory on disk. The CLI has no credential support, so for anything behind auth point FACET_REGISTRY_DIR at a checkout your build already has access to.