← All guided builds

Guided build · 22

Publish a signed Library to grid ns

Scaffold and test a Luau Library, prove control of a publisher namespace, sign the exact release locally, and publish an immutable public coordinate.

You will finish with: A tested Luau Library, anchored publisher identity, and immutable public release with an exact coordinate and verifiable digest.

30 min + activationAdvancedSource checked for Grid 0.61.0Reviewed 2026-08-24
On this page

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 extension and identifies your-namespace/double@1.0.0. The generated fixture calls double with 21 and expects 42.

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), and PASS your-namespace/double@1.0.0 double (luau) with result 42.

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 on pending_review or rejected. The live Public Registry currently reports open-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.0 and engine luau;
  • 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:

  1. In grid-extension.json, change the top-level package version from 1.0.0 to 1.0.1.
  2. Leave main.luau, the export contract, and the fixture unchanged.
  3. Run extension dev, extension validate, and the existing double fixture test.
  4. Run universal publish --dry-run again and confirm the prepared coordinate is pkg:your-namespace/double@1.0.1.
  5. Explain why the new release and request digests differ from 1.0.0 even 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 42 and 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 published with 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.
Build statusReached the expected checkpoint?