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

# Releases

export const PublisherBadge = ({size = "md"}) => {
  return <Tooltip tip="Members with the publisher role can execute this action." cta="Workspace roles" href="/admin/users/access-control">
            <Badge icon="git-merge" color="green" size={size}>publisher</Badge>
        </Tooltip>;
};

export const AdminBadge = ({size = "md"}) => {
  return <Tooltip tip="Workspace owners and administrators can execute this action." cta="Workspace roles" href="/admin/users/access-control">
            <Badge icon="shield-check" color="blue" size={size}>admin</Badge>
        </Tooltip>;
};

export const PlatformUnsupportedBadge = () => {
  return <Tooltip tip="Platform API does not support this operation">
      <span className="inline-flex items-center gap-1.5 rounded-lg px-2 py-0.5 text-sm text-[rgb(var(--gray-500))] dark:text-[rgb(var(--gray-400))]" style={{
    marginLeft: '-0.5rem',
    cursor: 'default'
  }}>
        <Icon icon="ban" size={13} color="currentColor" /> Platform API
      </span>
    </Tooltip>;
};

export const PlatformApiBadge = ({endpoint, label = "Platform API"}) => {
  const href = `/references/platform-api/2026-08-17/endpoints/${endpoint}`;
  return <Tooltip tip="Open the Platform API reference for this operation">
      <a href={href} target="_blank" rel="noopener noreferrer" style={{
    textDecoration: 'none',
    borderBottom: 'none'
  }}>
        <span className="inline-flex items-center gap-1.5 rounded-lg px-2 py-0.5 text-sm text-[rgb(var(--gray-500))] dark:text-[rgb(var(--gray-400))] hover:bg-[rgb(var(--gray-100))] dark:hover:bg-[rgb(var(--gray-800))] hover:text-[rgb(var(--gray-700))] dark:hover:text-[rgb(var(--gray-200))]" style={{
    marginLeft: '-0.5rem'
  }}>
          {label} <Icon icon="arrow-up-right" size={13} color="currentColor" />
        </span>
      </a>
    </Tooltip>;
};

export const Framed = ({image, background, link, alt = "Framed content", borderWidth = "24px", outerRadius = "10px", innerRadius = "6px"}) => {
  const parseShorthand = value => {
    const values = String(value).trim().split(/\s+/);
    switch (values.length) {
      case 1:
        return [values[0], values[0], values[0], values[0]];
      case 2:
        return [values[0], values[1], values[0], values[1]];
      case 3:
        return [values[0], values[1], values[2], values[1]];
      default:
        return values.slice(0, 4);
    }
  };
  const isZero = v => parseFloat(v) === 0;
  const padding = parseShorthand(borderWidth);
  const outer = parseShorthand(outerRadius);
  const cornerMasks = [];
  if (isZero(padding[0]) && isZero(padding[3])) {
    cornerMasks.push(`radial-gradient(circle at 0% 0%, transparent ${outer[0]}, black ${outer[0]})`);
  }
  if (isZero(padding[0]) && isZero(padding[1])) {
    cornerMasks.push(`radial-gradient(circle at 100% 0%, transparent ${outer[1]}, black ${outer[1]})`);
  }
  if (isZero(padding[2]) && isZero(padding[1])) {
    cornerMasks.push(`radial-gradient(circle at 100% 100%, transparent ${outer[2]}, black ${outer[2]})`);
  }
  if (isZero(padding[2]) && isZero(padding[3])) {
    cornerMasks.push(`radial-gradient(circle at 0% 100%, transparent ${outer[3]}, black ${outer[3]})`);
  }
  const backgroundMask = cornerMasks.length > 0 ? cornerMasks.join(", ") : undefined;
  const innerImage = <img src={image} alt={alt} noZoom={link ? true : false} style={{
    display: "block",
    width: "100%",
    margin: 0,
    borderRadius: 0
  }} />;
  return <div style={{
    display: "inline-block",
    position: "relative",
    borderRadius: outerRadius,
    overflow: "hidden"
  }}>
            {}
            {background && <div style={{
    position: "absolute",
    inset: 0,
    backgroundImage: `url(${background})`,
    backgroundSize: "cover",
    maskImage: backgroundMask,
    WebkitMaskImage: backgroundMask,
    maskComposite: "intersect",
    WebkitMaskComposite: "source-in"
  }} />}
            {}
            <div style={{
    position: "relative",
    padding: borderWidth
  }}>
                <div style={{
    borderRadius: innerRadius,
    overflow: "hidden",
    lineHeight: 0
  }}>
                    {link ? <a href={link}>{innerImage}</a> : innerImage}
                </div>
            </div>
        </div>;
};

export const ImmutableBadge = ({size = "sm"}) => {
  return <Tooltip tip="Property cannot be modified">
            <Badge icon="lock" color="gray" size={size}>immutable</Badge>
        </Tooltip>;
};

export const CliBadge = ({command, label = "CLI"}) => {
  const href = `/references/cli/${command}`;
  return <Tooltip tip="Open the CLI reference for this command">
      <a href={href} target="_blank" rel="noopener noreferrer" style={{
    textDecoration: 'none',
    borderBottom: 'none'
  }}>
        <span className="inline-flex items-center gap-1.5 rounded-lg px-2 py-0.5 text-sm text-[rgb(var(--gray-500))] dark:text-[rgb(var(--gray-400))] hover:bg-[rgb(var(--gray-100))] dark:hover:bg-[rgb(var(--gray-800))] hover:text-[rgb(var(--gray-700))] dark:hover:text-[rgb(var(--gray-200))]" style={{
    marginLeft: '-0.5rem'
  }}>
          {label} <Icon icon="arrow-up-right" size={13} color="currentColor" />
        </span>
      </a>
    </Tooltip>;
};

<Framed background="https://assets.mirurobotics.com/docs/v04/images/releases/background.jpeg" image="https://assets.mirurobotics.com/docs/v04/images/releases/header:page.png" borderWidth="36px 0 0 56px" innerRadius="8px 0 0 0" />

A **release** is a version of software and its compatible config schemas.

Within a fleet of robots, multiple software versions may be deployed—each release defines which config schemas are valid for that version.

For example, software release `v1.7.0` might specify the following schemas:

| **Config Type** | **Schema Version** |
| --------------- | ------------------ |
| `Mobility`      | **SCH-FmoDN**      |
| `Perception`    | **SCH-3JcLq**      |
| `Manipulation`  | **SCH-HHDRn**      |

Before deploying config instances to a device, the deployment's release (`v1.7.0`) is used to verify that all required config instances are present and that each instance complies with the release's schemas.

## Properties

<ParamField path="version" type="string">
  <ImmutableBadge />

  A [semantic version](https://semver.org/), unique for a given release.

  Versions must be dot-separated integers. You may optionally use a `v` prefix, a prerelease suffix (e.g. `-alpha.X`, `-beta.X`, `-rc.X`), or a build suffix (e.g. `+build-metadata`).

  Examples: `v1`, `1`, `v2.1`, `2.1`, `v3.2.1`, `3.2.1`, `v4.3.2-beta.1`, `4.3.2-rc.1`, `v5.4.3+metadata`, `6.5.4-beta.2+metadata`
</ParamField>

<ParamField path="config schemas" type="[] Config Schema">
  <ImmutableBadge />

  The [config schemas](/cfg-mgmt/concepts/schemas/overview#properties) that define compatible config instances for the release.

  Examples: `SCH-FmoDN`, `SCH-3JcLq`, `SCH-HHDRn`
</ParamField>

<ParamField path="file rules" type="[] File Rule">
  <ImmutableBadge />

  The [file rules](/data-uploads/concepts/file-rules/overview#properties) that are part of this release.

  Examples: `FLR-FmoDN`, `FLR-3JcLq`, `FLR-HHDRn`
</ParamField>

## View a release

<PlatformApiBadge endpoint="releases/get" />

To view a release and its schemas, click into the release from the releases page and switch to the **Overview** tab at the top.

<Frame>
  ![Release Overview](https://assets.mirurobotics.com/docs/v04/images/releases/overview.png)
</Frame>

## Create a release  <PublisherBadge />

<CliBadge command="release-create" />

<PlatformApiBadge endpoint="releases/create" />

Releases are created via the CLI — visit the config management [create a release](/cfg-mgmt/create-a-release) guide. To add [file rules](/data-uploads/concepts/file-rules/overview) to a release, visit the data uploads [define file rules](/data-uploads/define-file-rules) guide.

## Duplicate a release  <PublisherBadge />

Duplicating a release creates a new release with the same config schemas and git information as the original.

Of course, since releases require a unique version, the duplicated release must have a *different version* from the original release.

Duplicating a release does not duplicate the release's deployments—only the release, its schemas, and git information are duplicated.

To duplicate a release, click the ellipses (...) on the release you want to duplicate and select **Duplicate**.

<Frame>
  ![Release Ellipses Dropdown](https://assets.mirurobotics.com/docs/v04/images/releases/ellipses-dropdown.png)
</Frame>

A dialog will appear—input the new version name and hit **Create**.

<Frame>
  ![Duplicate Release Dialog](https://assets.mirurobotics.com/docs/v04/images/releases/duplicate-dialog.png)
</Frame>

## Delete a release  <AdminBadge />

<PlatformUnsupportedBadge />

Deleting a release is only possible if no deployments have been delivered to any device with that release. Otherwise, the release cannot be deleted.

To delete a release, click the ellipses (...) on the release you want to delete and hit **Delete**.

<Frame>
  ![Release Ellipses Dropdown](https://assets.mirurobotics.com/docs/v04/images/releases/ellipses-dropdown.png)
</Frame>

A confirmation dialog will appear. Hit **Delete** to confirm the deletion.

<Frame>
  ![Delete Release Dialog](https://assets.mirurobotics.com/docs/v04/images/releases/delete-dialog.png)
</Frame>
