> ## 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.

# Authentication

> Understand Mesa API keys, ephemeral tokens, and how mounts authenticate.

Mesa has two kinds of credentials that can be used for programmatic access:

1. **API keys**: long-lived credentials meant for use in trusted environments (ex. your application server).
2. **Ephemeral tokens** (JWTs): short-lived access tokens meant for use in ephemeral, less-trusted environments (ex. an agent sandbox).

API keys with admin scope can be used to mint both API keys and ephemeral tokens, but ephemeral tokens cannot be used to mint more credentials.

Keep API keys in trusted infrastructure. Use ephemeral tokens anywhere you would be uncomfortable leaving a durable secret behind: agent sandboxes, disposable VMs, container sessions, or per-user agent jobs.

## Basic Usage

The recommended pattern depends on how you mount MesaFS:

* For [app mounts](/content/mesafs/app-mount), construct the SDK client with an API key in your trusted backend. When you call `mesa.fs.mount(...)`, the SDK signs a scoped JWT under the hood and uses it for that mount.
* For [POSIX mounts](/content/mesafs/posix-mount), mint a scoped, ephemeral token in your trusted backend, pass it into the sandbox as `MESA_API_KEY`, then run `mesa mount` inside the sandbox.

<CodeGroup>
  ```typescript POSIX Mount theme={null}
  import { Mesa } from "@mesadev/sdk";

  // Runs in your trusted backend with a durable API key.
  const mesa = new Mesa({
    apiKey: process.env.MESA_API_KEY,
    org: "my-org",
  });

  // Mint a short-lived token for the sandbox.
  const { token } = await mesa.tokens.create({
    scopes: ["read", "write"],
    repos: ["my-org/agent-workspace"],
    ttl_seconds: 60 * 60,
  });

  // Set up your preferred sandbox...

  // Pass only the token into the less-trusted environment as MESA_API_KEY.
  await sandbox.commands.run("mesa mount --daemonize", {
    env: {
      MESA_ORG: "my-org",
      MESA_API_KEY: token,
    },
  });
  ```

  ```typescript App Mount (TS) theme={null}
  import { Mesa } from "@mesadev/sdk";

  // Runs in your trusted backend with a durable API key.
  const mesa = new Mesa({
    apiKey: process.env.MESA_API_KEY,
    org: "my-org",
  });

  // The SDK signs a repo-scoped JWT for this mount automatically.
  const fs = await mesa.fs.mount({
    repos: [{ name: "agent-workspace", bookmark: "main" }],
    ttl: 60 * 60,
  });

  const bash = fs.bash({ cwd: "/my-org/agent-workspace" });
  await bash.exec("cat README.md");
  ```

  ```python App Mount (Py) theme={null}
  import os
  from mesa_sdk import Mesa, RepoConfig

  # Runs in your trusted backend with a durable API key.
  mesa = Mesa(api_key=os.environ["MESA_API_KEY"], org="my-org")

  # The SDK signs a repo-scoped JWT for this mount automatically.
  async with mesa.fs.mount(
      repos=[RepoConfig(name="agent-workspace", bookmark="main")],
      ttl=60 * 60,
  ) as fs:
      bash = fs.bash(cwd="/my-org/agent-workspace")
      await bash.exec("cat README.md")
  ```
</CodeGroup>

Tokens are used for the lifetime of the mount. They default to a 1 hour TTL, have a maximum TTL of 24 hours, and are not refreshed automatically. Choose a `ttl` that covers the mount's work.

## REST and Git APIs

The REST API and Git server both accept a bearer credential, which can be either a durable API key or ephemeral JWT.

### REST API

Include the credential in the `Authorization` header:

```bash theme={null}
curl -H "Authorization: Bearer $MESA_API_KEY" \
  https://api.mesa.dev/v1/my-org/repos
```

### Git API

Use the credential as the password when cloning or pushing:

```bash theme={null}
git clone https://t:$MESA_API_KEY@api.mesa.dev/my-org/my-repo.git
```

## Minting Credentials

### Ephemeral Tokens

Use `mesa.tokens.create(...)` when you need to hand a credential to another environment, such as a sandbox where the Mesa CLI will run.

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

  const mesa = new Mesa({
    apiKey: process.env.MESA_API_KEY,
    org: "my-org",
  });

  const { token, expires_at } = await mesa.tokens.create({
    scopes: ["read", "write"],
    repos: ["my-org/agent-workspace"],
    ttl_seconds: 60 * 60, // 1 hour; max 86400
  });

  console.log(expires_at);
  ```

  ```python Python theme={null}
  import os
  from mesa_sdk import Mesa

  mesa = Mesa(api_key=os.environ["MESA_API_KEY"], org="my-org")

  minted = await mesa.tokens.create(
      scopes=["read", "write"],
      repos=["my-org/agent-workspace"],
      ttl_seconds=60 * 60,  # 1 hour; max 86400
  )

  token = minted.token
  print(minted.expires_at)
  ```
</CodeGroup>

The scopes of the minted token are clamped to the permissions of the API key used to initialize the SDK client.

Ephemeral tokens cannot be individually revoked or refreshed. They expire on their own. To end access early, revoke the signing API key.

### API Keys

API keys are long-lived bearer credentials. Use them for trusted services that need to create repos, manage API keys, mint tokens, or construct SDK clients.

Your first API key is created in the [Mesa dashboard](https://app.mesa.dev). If you haven't done this yet, follow the [Quickstart](/content/getting-started/quickstart) to create the initial key.

Construct SDK clients with an API key:

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

  const mesa = new Mesa({
    apiKey: process.env.MESA_API_KEY,
    org: "my-org",
  });
  ```

  ```python Python theme={null}
  import os
  from mesa_sdk import Mesa

  mesa = Mesa(api_key=os.environ["MESA_API_KEY"], org="my-org")
  ```
</CodeGroup>

Create additional API keys through the dashboard, SDK, or REST API:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const key = await mesa.apiKeys.create({
    org: "my-org",
    name: "Production backend",
    scopes: ["admin"],
  });

  // Store this securely. The full key is only shown once.
  console.log(key.key);
  ```

  ```python Python theme={null}
  key = await mesa.api_keys.create(
      org="my-org",
      name="Production backend",
      scopes=["admin"],
  )

  # Store this securely. The full key is only shown once.
  print(key.key)
  ```
</CodeGroup>

Create narrower keys where possible:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await mesa.apiKeys.create({
    org: "my-org",
    name: "Read-only analytics",
    scopes: ["read"],
  });

  await mesa.apiKeys.create({
    org: "my-org",
    name: "CI",
    scopes: ["read", "write"],
    expires_in_seconds: 60 * 60 * 24 * 90,
  });
  ```

  ```python Python theme={null}
  await mesa.api_keys.create(
      org="my-org",
      name="Read-only analytics",
      scopes=["read"],
  )

  await mesa.api_keys.create(
      org="my-org",
      name="CI",
      scopes=["read", "write"],
      expires_in_seconds=60 * 60 * 24 * 90,
  )
  ```
</CodeGroup>

API keys may be created with `expires_in_seconds`, or without an expiration. Revoking an API key immediately invalidates the key and every ephemeral token signed from it.

<CodeGroup>
  ```typescript TypeScript theme={null}
  await mesa.apiKeys.revoke({
    org: "my-org",
    id: "key_abc123",
  });
  ```

  ```python Python theme={null}
  await mesa.api_keys.revoke(
      org="my-org",
      key_id="key_abc123",
  )
  ```
</CodeGroup>

## Scopes

Scopes are hierarchical. Each level includes the permissions from the levels below it.

| Scope   | Description                                                                                                   |
| ------- | ------------------------------------------------------------------------------------------------------------- |
| `read`  | Clone, fetch, and view repositories, bookmarks, changes, commits, diffs, and content                          |
| `write` | Everything `read` can do, plus push, create/update/delete repos, and create/move/delete bookmarks and changes |
| `admin` | Everything `write` can do, plus API key and webhook management                                                |

API keys/JWTs can be org-wide or restricted to specific repositories. Use repo-scoped credentials when a service only needs access to a subset of repos.

An ephemeral token cannot exceed the scopes of the API used when minting the token.

## Common Failures

| Symptom                                       | Likely cause                                                              |
| --------------------------------------------- | ------------------------------------------------------------------------- |
| `UNAUTHORIZED`                                | The API key or token is missing, malformed, expired, or revoked           |
| `FORBIDDEN`                                   | The credential is valid but lacks the required scope or repository access |
| Mount starts but later fails with auth errors | The mount's credential expired; re-mint a token and remount               |
| Sandbox can access too many repos             | The token was not repo-scoped; mint it with `repos: ["org/repo"]`         |
