> ## Documentation Index
> Fetch the complete documentation index at: https://docs.r5d.chat/llms.txt
> Use this file to discover all available pages before exploring further.

# Releases and publishing

> How images and Helm chart versions are built, published, and consumed.

Every release publishes four artifacts from one commit, all sharing a single
version number:

| Artifact            | Location                                          |
| ------------------- | ------------------------------------------------- |
| Control-plane image | `ghcr.io/ricsam/r5d-chat-control-plane:<version>` |
| Workspace image     | `ghcr.io/ricsam/r5d-chat-workspace:<version>`     |
| Helm chart, OCI     | `oci://ghcr.io/ricsam/charts/r5d-chat`            |
| Helm repository     | `https://charts.r5d.dev`                          |

The application source is proprietary and is not published. Images carry no
`org.opencontainers.image.source` label, and the chart references only the
documentation site and the images, so no public artifact points at the private
repository.

## Version coupling

The chart's `version` and `appVersion` are stamped from the release tag, and
`image.tag` and `workspaceImage.tag` default to empty. When empty, the chart
resolves images from its own `appVersion`.

This means installing chart `0.3.0` runs the images built for `0.3.0`, with no
way for the chart and its images to drift apart. Overriding a tag is an explicit
opt-out:

```bash theme={null}
helm upgrade --install r5d-chat r5d-chat/r5d-chat \
  --set-string image.tag=deploy-20250219-abc1234
```

## Cutting a release

<Steps>
  <Step title="Make sure main is green">
    CI runs formatting, lint, typecheck, unit tests, database tests, a build,
    chart lint and render, and a build of both images on every push and pull
    request.
  </Step>

  <Step title="Tag the commit">
    The tag drives the version; the working `Chart.yaml` does not need editing.

    ```bash theme={null}
    git tag v0.2.0
    git push origin v0.2.0
    ```
  </Step>

  <Step title="The release workflow runs">
    In order: resolve and validate the version, build and push both images,
    package and push the OCI chart, then publish the Helm repository to
    `charts.r5d.dev`.
  </Step>

  <Step title="Verify the published artifacts">
    ```bash theme={null}
    helm repo update r5d-chat
    helm search repo r5d-chat --versions
    docker manifest inspect ghcr.io/ricsam/r5d-chat-control-plane:0.2.0 > /dev/null
    ```
  </Step>
</Steps>

You can also run the workflow manually from the Actions tab with an explicit
version, which is useful for re-publishing after an infrastructure failure.

## Where the Helm repository lives

`charts.r5d.dev` is a Cloudflare R2 bucket served through a custom domain. The
release workflow uploads with `scripts/r2-sync.ts`, a small SigV4 client so that
publishing needs nothing beyond Bun.

Upload order matters and is deliberate: chart archives are uploaded first and
`index.yaml` last, so the index never advertises a version that cannot yet be
downloaded. Archives are cached immutably; `index.yaml` is cached for one minute
so a new release becomes visible quickly.

## What the workflow guarantees

<AccordionGroup>
  <Accordion title="Versions are validated" icon="check">
    The version must match semver, optionally with a prerelease suffix. An
    invalid tag fails before anything is published.
  </Accordion>

  <Accordion title="Images publish before the chart" icon="list-ol">
    The chart job depends on the image job, so a chart version never references
    images that do not exist.
  </Accordion>

  <Accordion title="Provenance and SBOM are attached" icon="shield">
    Images are pushed with build provenance and an SBOM for supply-chain
    verification.
  </Accordion>

  <Accordion title="Older chart versions are preserved" icon="clock-rotate-left">
    The publish step downloads the existing index and its packages, merges the new
    version, and republishes. Previously released versions stay installable, so
    rollbacks keep working.
  </Accordion>

  <Accordion title="The result is verified after publishing" icon="magnifying-glass">
    The workflow asserts the new version appears in `index.yaml` with its
    `.tgz`, then adds the live repository and resolves the new version through
    `helm show chart` before reporting success.
  </Accordion>
</AccordionGroup>

## Consuming releases

<Tabs>
  <Tab title="Helm repository">
    ```bash theme={null}
    helm repo add r5d-chat https://charts.r5d.dev
    helm repo update
    helm search repo r5d-chat --versions
    helm install r5d-chat r5d-chat/r5d-chat --version 0.2.0
    ```
  </Tab>

  <Tab title="OCI registry">
    ````bash helm show chart oci://ghcr.io/ricsam/charts/r5d-chat --version 0.2.0 theme={null}
    helm install r5d-chat oci://ghcr.io/ricsam/charts/r5d-chat --version 0.2.0 ```
    </Tab>

    <Tab title="Images only">
      ```bash
      docker pull ghcr.io/ricsam/r5d-chat-control-plane:0.2.0
      docker pull ghcr.io/ricsam/r5d-chat-workspace:0.2.0
    ````
  </Tab>
</Tabs>

<Warning>
  `latest` is published for convenience but should not be deployed. It makes
  rollbacks ambiguous and causes pods to change behavior on an unrelated
  restart. Always pin a version.
</Warning>

## Publishing requirements

Publishing depends on repository secrets for the Helm repository bucket:

* `R2_ACCOUNT_ID`, `R2_BUCKET`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`.
* The R2 token is scoped to object read/write on the charts bucket only.
* The workflow requests `packages: write` for GHCR; no personal access token is
  required.

## Package visibility

New GHCR packages are private by default. All three published packages are
public so that a self-hosted installation needs no credentials:

* `r5d-chat-control-plane`
* `r5d-chat-workspace`
* `charts/r5d-chat`

<Note>
  A newly created package reverts to private, so after the first release that
  creates `charts/r5d-chat`, set it to public under its package settings on
  GitHub. Otherwise `helm install` from the OCI reference fails for anonymous
  users while the `charts.r5d.dev` repository keeps working.
</Note>
