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

# Repositories and Tags

> Organize commits, publish images, and share VM state across accounts

**Repositories** (repos) are named collections of VM image tags, scoped to your organization. A **tag** inside a repository is a named pointer to a specific commit (e.g. `my-app:latest` or `my-app:v1.2`). Repos can be **private** (the default, visible only to your org) or **public** (discoverable and forkable by any Vers user).

If you're already comfortable with OCI registries like Docker Hub, the mental model maps over directly. A repo is the thing people pull from; a tag is the moving pointer; a commit is the immutable snapshot the tag currently names. The difference is what's inside: a Vers commit is a full VM snapshot (filesystem **and** memory), not a layered container image.

## Why Repos Exist

Repos solve two different problems. It helps to keep them separate in your head, because you'll likely use both.

### 1. Sharing images across accounts

Commits are private to the org that created them. There's no way to hand someone a commit ID from another org and have them restore it. Repos are the mechanism for cross-account distribution: mark a repo public, tag the commits you want to share, and anyone with a Vers account can fork them into their own org.

This is how Vers publishes **supported images**, pre-baked VMs for common use cases that the Vers team maintains and refreshes. The currently supported set:

| Repository        | What's in it                                                                    |
| ----------------- | ------------------------------------------------------------------------------- |
| `node:latest`     | Node.js LTS with npm, git, curl, nginx, iproute2                                |
| `python:latest`   | Python 3 with pip, venv, git, curl, nginx, iproute2                             |
| `rust:latest`     | Rust stable with cargo, git, curl, nginx, iproute2                              |
| `go:latest`       | Go 1.22 with git, curl, nginx, iproute2                                         |
| `pi-agent:latest` | pi coding agent pre-installed with tmux/FIFO scaffold, ready for remote driving |
| `cc-agent:latest` | Claude Code pre-installed with the runtime environment it needs                 |

You can fork any of them with one command and get a running VM plus a copy of the commit in your own repo, ready to customize further.

<Info>
  Supported images are rebaked on a regular cadence so that `<name>:latest` stays current with the upstream toolchain. If you need to pin a specific build, capture the commit ID at fork time (`vers repo fork` output includes it) and use that commit ID directly later.
</Info>

### 2. Organizing your own commits

The other use case is purely private: a place to collect commits you care about, under names you can remember. Instead of saving commit IDs in a scratchpad, you tag them:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers repo create my-app
vers repo tag create my-app latest c7e4a2f1…
vers repo tag create my-app known-good c7e4a2f1…
vers repo tag create my-app pre-migration 9b8d3c10…
```

From then on, the CLI (`vers run-commit <repo>:<tag> --ref`) and the API (`{"ref": "my-app:latest"}`) both accept `repo:tag` references anywhere you'd use a commit ID, which makes your workflows much easier to read and rerun.

A common pattern: your CI builds a fresh VM image for your app on every merge to `main`, points `my-app:latest` at the new commit, and the team pulls a pre-baked VM matching trunk in seconds instead of spending minutes on apt-get and migrations. See [CI: maintaining a `latest` image](/examples/ci-latest-image) for the full recipe.

## The Object Model

```
Organization (your account)
│
├── Repository "my-app"              (private or public)
│   ├── Tag "latest"      → commit c7e4…
│   ├── Tag "v1.0"        → commit 1a2b…
│   └── Tag "staging"     → commit 88ff…
│
├── Repository "internal-tools"      (private)
│   └── Tag "latest"      → commit 4d5e…
│
└── Repository "public-demo"         (public, forkable by others)
    └── Tag "latest"      → commit 9b8d…
```

| Concept        | What it is                                                                         | Scope                                                            |
| -------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| **Repository** | A named container for tags. Has a visibility flag.                                 | Per-org                                                          |
| **Tag**        | A movable pointer from a name to a single commit.                                  | Per-repo                                                         |
| **Commit**     | Immutable snapshot of a VM's full state.                                           | Per-org (referenceable across orgs only via public repos + fork) |
| **Reference**  | The `repo_name:tag_name` string (and `org_name/repo_name:tag_name` for cross-org). | n/a                                                              |

A few properties worth internalizing:

* **Tags are movable.** Moving a tag is a cheap pointer update; the commit it used to point to still exists as long as some VM or other tag still references it.
* **Deleting a repo or tag does not delete commits.** Commit garbage collection is a separate concern; removing the label doesn't destroy the artifact.
* **Repo names are per-org.** Two different orgs can both have a `my-app` repo; they're disambiguated by org namespace (`acme/my-app` vs `globex/my-app`) for public lookups.
* **Names are validated.** Repo names: 1-64 characters, alphanumeric plus hyphens, underscores, and dots. Tag names: 1-128 characters, same character set.
* **Tagging requires org ownership of the commit.** You can only tag commits your org owns. To tag a commit from somebody else's public repo, fork it first, forking is what actually copies the commit into your org.

## References: `repo:tag` and `org/repo:tag`

There are two reference shapes:

| Form           | When to use                                     | Example              |
| -------------- | ----------------------------------------------- | -------------------- |
| `repo:tag`     | Operations scoped to your own org               | `my-app:latest`      |
| `org/repo:tag` | Cross-org ops (currently only `vers repo fork`) | `vers/python:latest` |

### Where refs are accepted

Via the **CLI**, pass `--ref`:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers run-commit my-app:latest --ref --vm-alias dev
```

Via the **API** (`POST /api/v1/vm/from_commit`), send the `ref` field instead of `commit_id`:

```jsonc theme={"theme":{"light":"min-light","dark":"min-dark"}}
// Before
{ "commit_id": "c7e4a2f1-…" }

// After
{ "ref": "my-app:latest" }
```

There's also `POST /api/v1/vm/branch/by_ref/{repo_name}/{tag_name}` when you want to branch from a ref rather than restore via `from_commit`.

<Info>
  `--ref` is specifically required on the CLI because the positional argument is otherwise sent as `{"commit_id": "..."}`. Without `--ref`, a repo:tag-shaped argument is rejected up front with a hint, since sending it as `commit_id` would fail UUID parsing on the server.
</Info>

Cross-org `org/repo:tag` references are only consumed by `vers repo fork` and the public-repository API. You can't `from_commit`/`run-commit --ref` directly against another org's ref. The expected path is always fork-then-use.

## Visibility and Publishing

Every repo has an `is_public` flag, false by default. You flip it with:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers repo visibility my-app --public          # publish
vers repo visibility my-app --public=false    # unpublish
```

When a repo is public:

* Any authenticated Vers user can discover it via `GET /api/v1/public/repositories`
* Any user can read its tags and fork it into their own org
* Your org name becomes part of its public identity: `your-org/my-app:latest`

Private repos are only visible to members of your own org. There's no in-between "share with these accounts" state right now; it's public or private.

<Warning>
  Publishing a repo exposes the commits its tags point to. The commits themselves don't become public objects in isolation (they're only fetchable via the public repo surface), but they are fetchable, so treat them like you'd treat a public Docker image. Don't tag commits that contain secrets in `/etc/environment`, `~/.netrc`, private keys, etc.
</Warning>

## Forking

A **fork** takes a public repo's tag in another org and produces, in your org:

1. A new running VM, branched from the source commit (via a copy-on-write clone, so this is fast; it does not boot and re-image a fresh VM)
2. A new commit, captured from that VM, owned by your org
3. A repository (either a new one, or an existing same-named one; see below)
4. A tag pointing at the new commit

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers repo fork vers/pi-agent:latest
# ✓ Forked → pi-agent:latest
#   VM:     vm-7a1c…
#   Commit: c9e4…
```

You can override the target repo and tag names:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers repo fork vers/pi-agent:latest --repo-name my-agent-base --tag-name v0
```

The result is fully independent. No upstream relationship is tracked. Modify the VM, re-commit, move tags, delete the source locally, whatever. Nothing links back. To pull fresh upstream changes later, run `vers repo fork` again.

### Fork and existing repos

If a repo of the target name already exists in your org, the fork **reuses it** and adds the new tag to it. You only get an error in a race where two simultaneous forks both try to insert the same repo name.

What isn't handled automatically: if the target repo already has a tag with the target tag name, the tag insert will fail. Pass `--tag-name` to pick a different name, or delete the existing tag first with `vers repo tag delete`.

## Typical Workflows

### Personal organization (just tag your commits)

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
vers run --vm-alias dev
vers connect
# … set up a project, install deps …
exit

vers commit
# → commit c7e4…

vers repo create my-app
vers repo tag create my-app latest c7e4…

# Weeks later, on any machine:
vers run-commit my-app:latest --ref --vm-alias dev
```

### Team CI: always-current dev environment

1. Developer creates `my-app` repo once, makes it public if it should be forkable by contractors, private otherwise.
2. CI action on every merge to `main`:
   * Forks the previous `my-app:latest` into a scratch VM (or branches from it)
   * Runs `git pull`, reinstalls deps, runs migrations, warms caches
   * Commits the VM
   * Points `my-app:latest` at the new commit with `vers repo tag update my-app latest --commit <id>`
3. Team members pull the current build with `vers run-commit my-app:latest --ref` (or the equivalent API call) and land on a pre-baked dev VM matching trunk.

See [CI: maintaining a `latest` image](/examples/ci-latest-image) for a full walkthrough with example scripts.

### Consuming a supported image

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# Fork gives you a running VM, a commit, and a tagged repo in your org
vers repo fork vers/python:latest --repo-name py-base --tag-name latest
# ✓ Forked → py-base:latest
#   VM:     vm-2b4a…
#   Commit: f1a7…

# Connect to the VM the fork created
vers connect vm-2b4a…

# Or spin up a fresh VM from the fork's commit later
vers run-commit f1a7… --vm-alias my-py-env
```

See [Forking a supported image](/examples/fork-supported-image) for the full walkthrough.

### Release tagging

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
# After a successful release build
vers repo tag create my-app v1.4.2 c9e4…
vers repo tag update my-app latest --commit c9e4…

# Roll back latest in a pinch
vers repo tag update my-app latest --commit <previous-commit>
```

## CLI Cheat Sheet

| Command                                                  | Purpose                                         |
| -------------------------------------------------------- | ----------------------------------------------- |
| [`vers repo create`](/cli-reference/repo-create)         | Create a new repository                         |
| [`vers repo list`](/cli-reference/repo-list)             | List your org's repositories                    |
| [`vers repo get`](/cli-reference/repo-get)               | Show details of a repo                          |
| [`vers repo delete`](/cli-reference/repo-delete)         | Delete one or more repos (keeps commits)        |
| [`vers repo visibility`](/cli-reference/repo-visibility) | Toggle public / private                         |
| [`vers repo fork`](/cli-reference/repo-fork)             | Fork a public repo into your org                |
| [`vers repo tag create`](/cli-reference/repo-tag-create) | Create a tag pointing at a commit               |
| [`vers repo tag list`](/cli-reference/repo-tag-list)     | List tags in a repo                             |
| [`vers repo tag get`](/cli-reference/repo-tag-get)       | Show tag details                                |
| [`vers repo tag update`](/cli-reference/repo-tag-update) | Move a tag to a new commit, or edit description |
| [`vers repo tag delete`](/cli-reference/repo-tag-delete) | Delete tags (keeps commits)                     |

## API Surface

All endpoints require `Authorization: Bearer <token>` unless marked public.

### Your org's repositories

| Method   | Path                                          | Purpose                                  |
| -------- | --------------------------------------------- | ---------------------------------------- |
| `GET`    | `/api/v1/repositories`                        | List your org's repos                    |
| `POST`   | `/api/v1/repositories`                        | Create a repo                            |
| `GET`    | `/api/v1/repositories/{repo_name}`            | Get one                                  |
| `DELETE` | `/api/v1/repositories/{repo_name}`            | Delete (tags removed, commits preserved) |
| `PATCH`  | `/api/v1/repositories/{repo_name}/visibility` | Set `is_public`                          |
| `POST`   | `/api/v1/repositories/fork`                   | Fork a public repo from another org      |

### Tags inside a repo

| Method   | Path                                               | Purpose                          |
| -------- | -------------------------------------------------- | -------------------------------- |
| `GET`    | `/api/v1/repositories/{repo_name}/tags`            | List tags                        |
| `POST`   | `/api/v1/repositories/{repo_name}/tags`            | Create a tag                     |
| `GET`    | `/api/v1/repositories/{repo_name}/tags/{tag_name}` | Get a tag                        |
| `PATCH`  | `/api/v1/repositories/{repo_name}/tags/{tag_name}` | Move tag and/or edit description |
| `DELETE` | `/api/v1/repositories/{repo_name}/tags/{tag_name}` | Delete tag                       |

### Public discovery (readable by any authenticated user)

| Method | Path                                                  | Purpose                               |
| ------ | ----------------------------------------------------- | ------------------------------------- |
| `GET`  | `/api/v1/public/repositories`                         | List all public repos across all orgs |
| `GET`  | `/api/v1/public/repositories/{org}/{repo}`            | Get one by org + name                 |
| `GET`  | `/api/v1/public/repositories/{org}/{repo}/tags`       | List tags                             |
| `GET`  | `/api/v1/public/repositories/{org}/{repo}/tags/{tag}` | Get a tag                             |

### Using references when restoring VMs

`POST /api/v1/vm/from_commit` accepts three alternative body shapes:

```jsonc theme={"theme":{"light":"min-light","dark":"min-dark"}}
// 1. By commit ID (original form, always supported)
{ "commit_id": "c7e4a2f1-…-…" }

// 2. By legacy org-scoped tag (pre-repos commit_tags)
{ "tag_name": "my-old-tag" }

// 3. By repo:tag reference (new, recommended path for repos)
{ "ref": "my-app:latest" }
```

There's also `POST /api/v1/vm/branch/by_ref/{repo_name}/{tag_name}` when you want to branch from a ref instead of restoring via `from_commit`.

## Behavior Notes and Gotchas

* **Deleting a repo keeps its commits.** The repo row and its tags are removed (tags cascade via FK). Commits remain reachable through any surviving tag, legacy `commit_tags` entry, or VM that still references them.
* **Deleting a tag keeps the commit.** Same reason.
* **Tag moves are atomic pointer updates.** No VMs are touched by moving `my-app:latest` from one commit to another; it's a metadata-only change.
* **Fork creates a running VM and a commit.** The branching step uses a Ceph CoW clone so it's fast (seconds, not minutes), even for large images. The step that takes real time is the post-branch commit.
* **Fork is a copy, not a reference.** No upstream relationship is tracked after completion. If the source repo updates, you won't see it unless you fork again.
* **Fork into existing repo reuses it.** Same-name target repo? The fork adds tags to it rather than erroring. A pre-existing tag with the same target tag name will fail the tag insert. Use `--tag-name` or delete the existing tag first.
* **Repo names are unique per-org**, not globally. Public references always include the org name (`acme/my-app`).
* **Refs are case-sensitive** and exact-match. No `@latest` parsing, no partial matches.
* **Tagging requires org ownership of the commit.** You cannot create a tag in your repo that points to a commit another org owns, even if it's visible via a public repo. The way you "import" someone else's commit is `vers repo fork`; the fork re-commits the VM under your org.

## Related

* [Concepts](/concepts/overview) - VMs, HEAD, commits, branching
* [`vers run-commit`](/cli-reference/run-commit) - create a VM from a commit ID or `repo:tag` ref (`--ref`)
* [Forking a supported image](/examples/fork-supported-image) - bootstrap an env from a Vers-maintained repo
* [CI: maintaining a `latest` image](/examples/ci-latest-image) - team workflow for always-current dev VMs
* [API Introduction](/api-reference/introduction) - broader API walkthrough
