What you will publish
A real Luau Library with one typed function, one generated manifest, one tested fixture, and one immutable public coordinate. You will validate and sign the exact release locally before asking the Public Registry to retain it.
The workflow has two deliberate boundaries:
- Steps 1–4 stay on your machine. The dry-run makes no network request.
- Steps 5 and 7 create durable external state. Enroll and publish only when you control the namespace and intend the release to be public.
Use your-namespace throughout this tutorial, replacing it with your own unused publisher
name before you run a command. Publisher and Library name segments must be lowercase, begin
with a letter or digit, and contain only lowercase letters, digits, ., _, or -.
1. Scaffold a complete Library
gridctl extension new ./extensions/your-namespace-double \
--name your-namespace/double \
--language luau \
--version 1.0.0
The destination must not already exist. Grid creates the Luau source, a typed test fixture,
editor bindings, development configuration, and exactly one publishable root manifest:
grid-extension.json.
The generated function body is intentionally small enough to inspect in full:
--!strict
return {
double = function(value: number): number
return grid.fn.PRODUCT(value, 2)
end,
}
Checkpoint: the command begins with
scaffolded verified Luau extensionand identifiesyour-namespace/double@1.0.0. The generated fixture callsdoublewith21and expects42.
Do not add a second artifact manifest to this directory. Universal publishing accepts
exactly one of grid-extension.json, grid-model.json, grid-template.json, or
grid-domain-pack.json at the artifact root.
2. Execute and validate the package locally
gridctl extension dev ./extensions/your-namespace-double
gridctl extension validate ./extensions/your-namespace-double
gridctl extension test ./extensions/your-namespace-double \
--export double \
--fixture ./extensions/your-namespace-double/test-fixtures/double.json
extension dev refreshes the source digest, executes the fixture, and regenerates the
bindings. The dedicated validation and test commands then prove the same package contract
and callable behavior independently.
Checkpoint: look for
extension development pipeline passed,valid your-namespace/double@1.0.0 (luau, 1 export), andPASS your-namespace/double@1.0.0 double (luau)with result42.
A slim Grid CLI built without the Luau compiler must fail with
GRID_EXTENSION_ENGINE_UNAVAILABLE. Missing execution support is not a skipped or passing
test.
3. Create the publisher key outside the project
Keep publisher secrets outside the Library and outside version control:
mkdir -p ../grid-publisher-secrets
chmod 700 ../grid-publisher-secrets
openssl genpkey -algorithm ED25519 \
-out ../grid-publisher-secrets/publisher-private.pem
openssl pkey \
-in ../grid-publisher-secrets/publisher-private.pem \
-pubout \
-out ../grid-publisher-secrets/publisher-public.pem
chmod 600 ../grid-publisher-secrets/publisher-private.pem
Grid expects a PKCS#8 Ed25519 private key. Signing happens locally; the private key is not uploaded. Back it up according to your organization’s secret-recovery policy before it anchors a durable namespace.
4. Dry-run the exact signed release
gridctl publish ./extensions/your-namespace-double \
--private-key ../grid-publisher-secrets/publisher-private.pem \
--dry-run
This runs the artifact-specific Core checks, signs the release, and stops before any network request or credential lookup.
Expected output has this shape:
prepared signed package pkg:your-namespace/double@1.0.0 (dry run; no network request made)
state prepared
release sha256:...
request sha256:...
objects 3 (... bytes; 0 uploaded)
Record the release and request digests. The three immutable publication objects are the
release envelope, grid-extension.json, and main.luau. Test fixtures, editor support,
development configuration, and the local lockfile are not published.
5. Enroll the publisher namespace
This step requests durable public state. Run it only after replacing your-namespace and
confirming you control the key:
gridctl publisher enroll \
--namespace your-namespace \
--private-key ../grid-publisher-secrets/publisher-private.pem \
--registry https://publish.grids365.org \
--credential-out ../grid-publisher-secrets/grid-publish.token \
--request-out ../grid-publisher-secrets/grid-publisher-enrollment.json
Both output paths must be new and different. On Unix, Grid creates them with mode 0600.
The grdpat_v1_... token is generated locally. The signed request sends its SHA-256 digest,
your public key, namespace, and key proof—not the private key or raw token.
Activation checkpoint: continue only when the returned enrollment state is
approved. Stop onpending_revieworrejected. The live Public Registry currently reportsopen-request-reviewed-activation, and its policy requires reviewed publisher identity. A review boundary is not an error and must not be bypassed.
Preserve the enrollment result and its statusUrl. Grid 0.61 does not expose a separate
publisher status command, so a pending request resumes through that status URL and the
Registry’s operator review channel. Do not create replacement credentials while the same
review is in flight. Grid 0.61 also has no documented self-service credential-recovery or
key-rotation command; keep durable, access-controlled recovery copies of both the publisher
key and token.
The concise Registry publishing page describes the common automatic path after Core verification. The current service status and public policy are authoritative when activation requires review.
6. Load the credential without putting it in arguments
After approval, load the token into the current shell:
export GRID_REGISTRY_PUBLISH_TOKEN="$(tr -d '\n' < ../grid-publisher-secrets/grid-publish.token)"
Do not print the variable. --credential-env passes the environment-variable name to
gridctl; the token value never appears in the command’s arguments.
7. Publish the immutable release
This command submits durable public state:
gridctl publish ./extensions/your-namespace-double \
--private-key ../grid-publisher-secrets/publisher-private.pem \
--registry https://publish.grids365.org \
--credential-env GRID_REGISTRY_PUBLISH_TOKEN
Expected output includes the exact coordinate, submission id, state, release digest, request digest, and object counts:
submitted signed package pkg:your-namespace/double@1.0.0
submission ...
state ...
release sha256:...
request sha256:...
objects 3 (...)
If no package bytes changed after the dry-run, both digests must match the values you
recorded. Successful nonterminal states can include verifying, awaiting_review, or
publishing; published is the completion checkpoint. Re-running the identical command
is idempotent and retrieves the later state.
Different bytes under the same version conflict by design. Correct a release by changing
the version and publishing a new immutable coordinate—never by attempting to overwrite
1.0.0.
Clear the shell credential when you are finished:
unset GRID_REGISTRY_PUBLISH_TOKEN
8. Verify what consumers can resolve
After the CLI reports state published, open:
https://grids365.org/packages/your-namespace/double
Verify all of the following against the CLI result:
- version
1.0.0and engineluau; - the publisher anchor;
- the exact release digest;
- the exact release coordinate
grid:your-namespace/double@1.0.0.
The two prefixes are intentional. CLI publication output and Grid source use
pkg:your-namespace/double@1.0.0, while the catalog page and gridctl package install use
grid:your-namespace/double@1.0.0.
Before USE can resolve the Library, a consumer must acquire it into a workspace with an
operator-reviewed, out-of-band Registry pin:
gridctl package install grid:your-namespace/double@1.0.0 \
--workspace . \
--ns https://grids365.org \
--ns-pin 'ed25519:<operator-reviewed-out-of-band-fingerprint>' \
--ns-state ~/.grid/ns-public \
--engine luau
Do not copy the pin from the Registry website itself: the catalog cannot be the independent source of its own trust anchor. Follow the Registry trust model for that verification step.
For Grid Desktop, review and save the Package Center runtime intent and activate the workspace separately. Installation alone neither activates extension code nor grants its declared capabilities.
The model can then import the installed workspace package:
USE "pkg:your-namespace/double@1.0.0" AS math
Input = 21
Output = math.double(Input AS value)
USE "pkg:…" resolves local installed package state. Evaluation never performs ambient
Registry acquisition.
Try it yourself
Prepare—but do not publish—a version-only identity exercise:
- In
grid-extension.json, change the top-level packageversionfrom1.0.0to1.0.1. - Leave
main.luau, the export contract, and the fixture unchanged. - Run
extension dev,extension validate, and the existingdoublefixture test. - Run universal
publish --dry-runagain and confirm the prepared coordinate ispkg:your-namespace/double@1.0.1. - Explain why the new release and request digests differ from
1.0.0even though the publisher key is unchanged.
Keep the exercise local, then restore 1.0.0. A no-op version bump is useful for learning
identity, but it is not a release worth adding to permanent public history.
What signing proves—and what it does not
- The publisher signature proves key control and byte integrity, not safety.
- Registry verification proves the closed artifact contract, not business correctness.
- Compatibility evidence is separate, target-specific, and can expire.
- Public policy permits quarantine, refused promotion, frozen mutable pointers, and signed revocation or replacement facts.
- The public service supports the current stable Grid Core release and the immediately preceding minor; a security fix can retire an unsafe client sooner.
- Published coordinates are immutable. Recovery, key rotation, and namespace stewardship require deliberate operator procedures.
You are done when
- The generated fixture returns
42and all local checks pass. - The offline dry-run records an exact release and request digest.
- Namespace enrollment is approved without exposing the private key or token.
- The network submission reaches
publishedwith the same digests. - The public Library page shows the expected version, engine, publisher anchor, and digest.
- You can explain the difference between publisher trust, Registry verification, and compatibility evidence.