docs← Back to article

Markdown for LLMs

Publishing a release

The source Markdown for this article. Copy it into your assistant or download it as a text file.

Download this articlePlain text ↗
# Publishing a release

A release moves a package from a working directory to an installable,
content-hashed artifact that other packages can pin. The registry is a
directory layout, an address and not an authority: descriptors promise
bytes, and after download the bytes prove the promise by hash.

Environment: the public `law-v0.1.1` release, run from the root of the
[lab bundle](/corpus/lab/#before-you-start). The example continues the
lab's [versions exercise](/corpus/lab/solutions/#versions-exercise),
which has already published the five lab packages to
`/tmp/lab-work/registry` and authored revision `0.2.0` of
`labparcels.registry` in `/tmp/lab-work/reg02`. Status letters follow
the [legend on the topic index](/corpus/#how-this-topic-marks-confidence).

## Example: pack, publish, and pin one revision

One package, three steps. All commands and outputs below are verbatim
from the [versions exercise](/corpus/lab/solutions/#versions-exercise).

**1. Pack and 2. publish** revision `0.2.0` of `labparcels.registry`
into the existing lab registry:

```shell
law pack --out /tmp/lab-work/artifacts/registry-0.2.0 --project /tmp/lab-work/reg02
law publish /tmp/lab-work/artifacts/registry-0.2.0 --registry lab=/tmp/lab-work/registry
```

```text
labparcels.registry@0.2.0 → /tmp/lab-work/artifacts/registry-0.2.0: contentHash sha256:fc69df17896dd8c60a4a4889e2313fd3228b234444bf0e36d98a94372ab96ce2, worldHash sha256:b3056c550cfaf9c58fa84befda4557d3b6c5020fdfc8c324086335343998e597, 3 files
labparcels.registry@0.2.0 → lab (/tmp/lab-work/registry): contentHash sha256:fc69df17896dd8c60a4a4889e2313fd3228b234444bf0e36d98a94372ab96ce2, worldHash sha256:b3056c550cfaf9c58fa84befda4557d3b6c5020fdfc8c324086335343998e597
versions labparcels.registry: 0.1.0, 0.2.0
changed: p/labparcels.registry/0.2.0/package.lawir.json
changed: p/labparcels.registry/0.2.0/world.lawir.json
changed: p/labparcels.registry/0.2.0.json
```

The first line is `pack`: it names the artifact directory and two
hashes. The content hash pins the package bytes; the world hash pins
the package together with its closure. The remaining lines are
`publish`: the same two hashes now stand in the registry, the registry
lists both known versions, and the `changed:` lines name the
descriptor (`0.2.0.json`) and the two blobs it wrote.

What this result means: any project that can read the `lab` registry
can now pin `labparcels.registry@0.2.0`, and the install will verify
the downloaded bytes against `sha256:fc69df17…`. Nothing in an existing
consumer changed yet.

**3. Pin** the new revision from a consumer, here a scratch copy of the
fees package at `/tmp/lab-work/fees-upd`:

```shell
law update 'labparcels.registry@0.2.0' --project /tmp/lab-work/fees-upd --registry lab=/tmp/lab-work/registry
```

```text
lab ← /tmp/lab-work/registry (--registry)
~ labparcels.registry 0.1.0 -> 0.2.0
changed: .law/transport.json
changed: deps/labparcels.registry.lawir.json
changed: law.lock
changed: law.toml
changed: package.law
```

The consumer's manifest, lock, pinned bytes, and import line now name
`0.2.0`. In the lab run its five scenarios still pass after the move,
so the consumer can commit the new pin. Previewing the move first and
checking currency afterwards are covered on
[**Upgrades and replay**](/corpus/releases/replay-and-upgrades/).

## The three steps in general

First `pack` builds the release artifact: a directory or a portable
`.arxo` container. Then `publish` places it into a registry directory,
or prepares a signed candidate from a manifest plus a bytes directory.
Downstream packages then pin the exact version with `add` or `update`.
The directory flow is **available in the public release** (S).

The command catalog records these usage strings for the first two
steps:

```text
law pack --out <dir|file.arxo> | --answers … | --runs … [--project <dir>] [--json]
law publish <artifact> (--registry <ID>=<dir> | --candidate <manifest.json> --bytes <dir>) [--json]
```

`publish` refuses to run without a pack artifact, as a bare run shows:

```text
$ law publish
law: REFUSAL: law publish: needs a `law pack` artifact directory
```

The refusal writes nothing. Run `law pack` first and pass its output
directory to `publish`.

A hosted registry service beyond the directory layout is
**unconfirmed** (U): this guide cannot promise one, so treat any such
endpoint as unconfirmed until it is announced.

## What an artifact directory contains

Each published release keeps its lockfile, its compiled package bytes,
its release record, and its linked world bytes together. The observed
layout for the release `calc.tiers` at `0.1.0`, a law pack artifact in
format `law.package-release/0.1`:

```text
$ ls dist/artifacts/calc.tiers-0.1.0/
law.lock
package.lawir.json
release.json
world.lawir.json
```

The linked world bytes are produced by new-root linking at pack time.
Downstream, fixed-world-artifact execution verifies and runs them but
never links again. The descriptor hash check after download is what
makes the publisher's promise verifiable. (S)

## Two formats, not one

The example above is the left column below: those four files are
exactly what `law pack --out <dir>` produces, and `publish` then places
such a directory into a registry. The right column, the standalone
release, adds a passport and a report store:

| | Law pack artifact | Standalone release |
|---|---|---|
| Format id | `law.package-release/0.1` | `law.package-build/0.3` and later |
| Passport | none: no passport file inside | required: without one it is not a release |
| Checks at build | lockfile, compiled bytes, and linked world packed together | compiles, runs scenarios, replays the saved evaluations, then writes the passport; any failure stops the build |
| Downstream artifacts | registry placement; `.arxo` container for offline moves | the passport travels with the release; quality, replay, and performance reports attach later |
| Report location | the hash check at install time; no separate report store | hash-addressed reports stored beside the release, never inside it |

## Offline transfer with containers

The `.arxo` container carries pins plus bytes for offline moves. `add`
takes a container file in place of a package name, and `install`
restores from one through its transfer flag. Neither form reads any
registry, so a container copied on removable media moves a closure with
no network at all. The transfer re-verifies content hashes on arrival,
so the carried bytes are as trustworthy as a registry download. (S)

Canon profiles publish through the same channel: the registry carries a
signed profile descriptor, so installers can check the pinned graph
before downloading any package bytes. (S)

## What to read next

- [Versions protocol](/protocols/versions/) for version selection rules.
- [Schema catalog](/protocols/schemas/) for descriptor, lockfile, and profile
  formats.
- [Command-line reference](/cli/) for the pack, publish, add, and install
  commands.