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

# Daytona

> Use Mesa with Daytona sandboxes for secure, high-performance agent workflows.

[Daytona](https://daytona.io/) provides secure, high-performance sandboxes that work well with Mesa. This guide shows the full end-to-end flow: use the Mesa SDK outside the sandbox to set up resources and sign a short-lived access token, then use the Daytona SDK to inject that token and mount Mesa inside the sandbox.

The general flow for any sandbox integration is:

1. **Outside the sandbox** — the orchestrator holds the private key. Use the Mesa SDK (TypeScript or Python) to create repos, then sign a short-lived access token locally with `mesa.tokens.create(...)`. Only the token crosses into the sandbox; the private key never does.
2. **Inside the sandbox** — install the `mesa` CLI and run `mesa mount --daemonize` with the token in `MESA_ACCESS_TOKEN`.
3. **Run your agent** — `cd` into the mount path and launch your agent (e.g. Claude Code, Codex, or a custom agent). Any file edits are automatically persisted back to Mesa.

For details on FUSE setup, system dependencies, and container configuration, see [POSIX Mount](/content/mesafs/posix-mount).

## Image setup

First, ensure that your Daytona image is properly configured. This example uses Daytona's [declarative image builder](https://www.daytona.io/docs/en/declarative-builder/) to install Mesa and configure FUSE in the image.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Image } from "@daytona/sdk";

  // Define a declarative image with Mesa dependencies
  const mesaImage = Image.base("ubuntu:24.04").runCommands(
    // Install Mesa required system dependencies
    "apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/*",
    "curl -fsSL https://mesa.dev/install.sh | sh -s -- --version 0.46.0 --yes",
    // Enable user_allow_other in FUSE config. This is required for non-root
    // users to access the mounted filesystem.
    "sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf",
  );
  ```

  ```python Python theme={null}
  from daytona import Image

  mesa_image = Image.base("ubuntu:24.04").run_commands(
      "apt-get update && apt-get install -y --no-install-recommends "
      "ca-certificates curl && rm -rf /var/lib/apt/lists/*",
      "curl -fsSL https://mesa.dev/install.sh | sh -s -- --version 0.46.0 --yes",
      # Enable user_allow_other in FUSE config. This is required for non-root
      # users to access the mounted filesystem.
      "sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf",
  )
  ```

  ```dockerfile Dockerfile theme={null}
  FROM ubuntu:24.04

  RUN apt-get update && apt-get install -y --no-install-recommends \
      ca-certificates curl \
      && rm -rf /var/lib/apt/lists/*
  RUN curl -fsSL https://mesa.dev/install.sh | sh -s -- --version 0.46.0 --yes
  RUN sed -i 's/^#user_allow_other/user_allow_other/' /etc/fuse.conf
  ```
</CodeGroup>

The image installs the latest Mesa CLI when Daytona builds it. Rebuild the image to pick up a newer CLI.

## Create and mount

The following examples create a temporary repo, mount it in a Daytona sandbox, write and read a file, and then delete both resources.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { Daytona } from "@daytona/sdk";
  import { Mesa } from "@mesadev/sdk";

  const mesa = new Mesa({ privateKey: process.env.MESA_PRIVATE_KEY! });
  const daytona = new Daytona();
  const sandbox = await daytona.create(
    {
      image: mesaImage,
      ephemeral: true,
      ttlMinutes: 30, // 30 minutes
    },
    {
      timeout: 10 * 60, // 10 minutes
    },
  );

  let repo: { name: string; org: string } | undefined;

  try {
    repo = await mesa.repos.create({ name: `daytona-${Date.now()}` });

    // Sign a self-expiring access token for the sandbox. This is signed locally
    // with your private key and no network call. Tokens are not stored anywhere:
    // when the TTL elapses, the token is dead. Nothing to revoke.
    // Pick a TTL that covers your agent session (max 4 hours).
    const { token } = await mesa.tokens.create({
      authors: [{ name: "Sandbox Agent", email: "agent@example.com" }],
      scopes: ["read", "write"],
      repos: [`${repo.org}/${repo.name}`],
      ttl_seconds: 30 * 60, // 30 minutes
    });

    // Pass the token only to the mount command. The private key never enters the
    // sandbox, and the token is never persisted to disk.
    // By default, MesaFS mounts every repo the token can access. Since this token
    // is scoped to the temporary repo, that is the only repo in the mount.
    const mount = await sandbox.process.executeCommand("mesa mount --daemonize", undefined, {
      MESA_ACCESS_TOKEN: token,
    });
    if (mount.exitCode !== 0) throw new Error(mount.result);

    const home = await sandbox.getUserHomeDir();
    const repoPath = `${home}/.local/share/mesa/mnt/${repo.org}/${repo.name}`;

    const result = await sandbox.process.executeCommand(
      "printf 'Hello from Daytona and Mesa!\\n' > hello-from-daytona.txt && cat hello-from-daytona.txt",
      repoPath,
    );
    if (result.exitCode !== 0) throw new Error(result.result);
    console.log(result.result);
  } finally {
    try {
      await sandbox.delete();
    } finally {
      if (repo) await mesa.repos.delete({ repo: repo.name });
    }
  }
  ```

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

  from daytona import CreateSandboxFromImageParams, Daytona
  from mesa_sdk import Mesa


  async def main():
      async with Mesa(private_key=os.environ["MESA_PRIVATE_KEY"]) as mesa:
          daytona = Daytona()
          sandbox = daytona.create(
              CreateSandboxFromImageParams(
                  image=mesa_image,
                  ephemeral=True,
                  ttl_minutes=30,  # 30 minutes
              ),
              timeout=10 * 60,  # 10 minutes
          )
          repo = None

          try:
              repo = await mesa.repos.create(
                  name=f"daytona-{int(time.time() * 1000)}"
              )

              # Sign a self-expiring access token for the sandbox. This is signed
              # locally with your private key and no network call. Tokens are not
              # stored anywhere: when the TTL elapses, the token is dead. Nothing
              # to revoke. Pick a TTL that covers your agent session (max 4 hours).
              token = await mesa.tokens.create(
                  authors=[{"name": "Sandbox Agent", "email": "agent@example.com"}],
                  scopes=["read", "write"],
                  repos=[f"{repo.org}/{repo.name}"],
                  ttl_seconds=30 * 60,  # 30 minutes
              )

              # Pass the token only to the mount command. The private key never enters
              # the sandbox, and the token is never persisted to disk.
              # By default, MesaFS mounts every repo the token can access. Since this
              # token is scoped to the temporary repo, that is the only repo in the mount.
              mount = sandbox.process.exec(
                  "mesa mount --daemonize",
                  env={"MESA_ACCESS_TOKEN": token.token},
              )
              if mount.exit_code != 0:
                  raise RuntimeError(mount.result)

              home = sandbox.get_user_home_dir()
              repo_path = f"{home}/.local/share/mesa/mnt/{repo.org}/{repo.name}"
              result = sandbox.process.exec(
                  "printf 'Hello from Daytona and Mesa!\\n' > hello-from-daytona.txt "
                  "&& cat hello-from-daytona.txt",
                  cwd=repo_path,
              )
              if result.exit_code != 0:
                  raise RuntimeError(result.result)
              print(result.result)
          finally:
              try:
                  sandbox.delete()
              finally:
                  if repo is not None:
                      await mesa.repos.delete(repo=repo.name)


  asyncio.run(main())
  ```

  ```bash CLI theme={null}
  # By default, MesaFS mounts every repo allowed by MESA_ACCESS_TOKEN. Scope the
  # token to the repos this sandbox should be able to access.
  mesa mount --daemonize

  cd "$HOME/.local/share/mesa/mnt/my-org"/daytona-*
  printf 'Hello from Daytona and Mesa!\n' > hello-from-daytona.txt
  ```
</CodeGroup>

For runnable versions that open an interactive shell in the mounted repo, see the [TypeScript](https://github.com/mesa-dot-dev/examples/tree/main/daytona-shell) and [Python](https://github.com/mesa-dot-dev/examples/tree/main/daytona-python-shell) examples.

<Warning title="Inject the token directly, not through Daytona Secrets">
  Daytona's Secrets API only substitutes placeholders in HTTPS request headers. MesaFS authenticates over gRPC (HTTP/2), which the substitution proxy doesn't handle, so a token passed as a Secret never reaches the mount. Inject `MESA_ACCESS_TOKEN` as a plain environment variable instead.
</Warning>

<Warning title="Access tokens have a fixed lifetime">
  An access token is minted once with a fixed TTL and is never refreshed: there is no
  background rotation and no credential hot-swap. Tokens default to a 15 minute TTL and max out at 4 hours, so
  mint one whose TTL covers the whole agent session. After it expires, filesystem operations in the sandbox fail
  with authentication errors. To continue past expiry, mint a fresh token on the host (the private key lives only
  outside the sandbox) and remount inside the sandbox with the new token in `MESA_ACCESS_TOKEN`.
</Warning>

## Tips

* **Use access tokens, not the private key, inside sandboxes.** Tokens expire on their own, can't be used to sign further credentials, and leave nothing behind to clean up. See [Authentication](/content/concepts/authentication) for details.
* **Use `--daemonize`.** Always run `mesa mount --daemonize` in sandbox environments so Mesa runs as a background process and doesn't block your agent's terminal.
* **Don't forget `user_allow_other`.** This is the most common setup issue in sandbox environments. See [POSIX Mount](/content/mesafs/posix-mount#allow_other-and-user_allow_other) for more info.
