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

# Private snapshots

> Build snapshots for your organisation and reach them with an API key.

The snapshots in the public catalogue are the ones everyone shares. A snapshot
you build in the [console](https://console.vpod.sh) belongs to your organisation
and is not in that catalogue, so reaching it takes an API key.

Everything else stays the same. A private snapshot is named, pulled, cached and
booted exactly like a public one.

## Get a key

Create one under **Settings → API keys** in the console. The full value is shown
once, at creation, and never again.

There are two kinds and they are not interchangeable:

| Kind        | Prefix     | Where it belongs                                     | What protects it                            |
| :---------- | :--------- | :--------------------------------------------------- | :------------------------------------------ |
| Secret      | `vpod_sk_` | CLI, Node, Python. Anywhere you control the machine. | The key itself. Treat it like a password.   |
| Publishable | `vpod_pk_` | A browser tab, where anyone can read it in devtools. | An allowlist of origins you set on the key. |

Each surface refuses the wrong kind rather than failing later:

* A `vpod_pk_` key in the CLI, Node or Python is rejected. Publishable keys are
  guarded by an origin allowlist, and nothing outside a browser sends an
  `Origin` the server can trust, so the key would buy you nothing.
* A `vpod_sk_` key in a browser is rejected, because shipping a secret key to a
  browser publishes it.

<Warning>
  The origin allowlist is the only thing making a publishable key safe, so the
  console requires one when you create the key. A key with an empty allowlist
  refuses every embed rather than allowing all of them.
</Warning>

## Use it

Passing a key switches the registry from the public catalogue to your
organisation's.

<CodeGroup>
  ```bash CLI theme={null}
  export VPOD_API_KEY=vpod_sk_...

  vpod list                    # your org's snapshots
  vpod pull my-snapshot
  vpod my-snapshot
  ```

  ```ts TypeScript theme={null}
  import { Sandbox } from "@capsule-run/vpod";

  await using sandbox = await Sandbox.create({
      snapshot: "my-snapshot",
      apiKey: process.env.VPOD_API_KEY,
  });
  ```

  ```python Python theme={null}
  import os

  from vpod import Sandbox

  with Sandbox.create(
      snapshot="my-snapshot",
      api_key=os.environ["VPOD_API_KEY"],
  ) as sandbox:
      ...
  ```
</CodeGroup>

All three read `VPOD_API_KEY` from the environment when you do not pass a key
explicitly, so the argument is only needed when the key comes from somewhere
else, such as a secret manager.

In a browser, pass the publishable key instead:

```ts theme={null}
const sandbox = await Sandbox.create({
    snapshot: "my-snapshot",
    apiKey: "vpod_pk_...",
});
```

## What the key changes

|           | No key             | With a key                    |
| :-------- | :----------------- | :---------------------------- |
| Registry  | `registry.vpod.sh` | `api.vpod.sh`                 |
| Catalogue | Public snapshots   | Your organisation's snapshots |

The registry follows the key automatically. You do not point it anywhere.

<Warning>
  The two catalogues **replace** each other rather than merging. With a key set,
  `vsnap-base` and the other public snapshots are no longer resolvable by name, so
  a `VPOD_API_KEY` exported in your shell profile will break a `vpod pull
    vsnap-base` you expected to work. Set the key per command, or per project, when
  you use both.
</Warning>

The key travels as a `Bearer` header, and only to origins matching the
registry's own. On the private registry the snapshot blobs are served from that
same origin, so they are authenticated too. A catalogue that points its
downloads somewhere else gets no key, which is what stops a hostile one from
collecting yours.

## Two keys on one machine

The catalogue is cached per key, so `list` shows what that key reaches and
switching keys does not show you a stale answer. The cache file is named after
a fingerprint of the key, which is a hash: the key itself is never written to
disk.

Snapshot files are named by id and checked against the digest the catalogue
gives. If two organisations happen to use the same id for different snapshots,
the digest will not match and the file is downloaded again rather than reused.

Cleanup is scoped the same way. When a snapshot disappears from the catalogue,
the local copy is removed only if it was downloaded by that same registry and
key, so tidying up one organisation's cache never deletes another's.

## When it does not work

A refusal from the registry says which case you are in.

**`401`/`403` with a key sent.** The key is revoked, or it belongs to a
different organisation than the snapshot you named. The CLI retries once with a
freshly fetched catalogue first, so a stale signed URL is already ruled out by
the time you see this.

**`401`/`403` with no key sent.** You asked a private registry for something
without credentials. Set `VPOD_API_KEY` or pass the key.

**`unknown snapshot`.** The name is not in the catalogue you can reach. The
message tells you whether a key was sent, which is the fast way to tell "I typo'd
the name" from "I forgot the key". Run `vpod list` to see what the key reaches.
