Mesa uses API keys for authentication. Each key is scoped to an organization and can be granted specific permissions to control access to repositories and operations.
API Keys
API keys provide programmatic access to the Depot API and Git operations. Keys are prefixed with mesa_ and should be kept secure.
Creating API Keys
You can create API keys through the SDK or API. This is useful if you want to generate scoped keys to put in a sandbox or within CI:
import { Mesa } from "@mesadev/sdk";
const mesa = new Mesa({ apiKey: process.env.MESA_API_KEY });
const key = await mesa.admin.createApiKey({
org: "my-org",
body: {
name: "CI/CD Pipeline",
scopes: ["git:read", "git:write", "repo:read"],
},
});
// Store this securely - the full key is only shown once!
console.log(key.key); // mesa_abc123...
API keys are only displayed once when created. Store them securely in your secrets manager or environment variables.
Using API Keys
SDK Authentication
import { Mesa } from "@mesadev/sdk";
const mesa = new Mesa({
apiKey: "<API_KEY>",
});
Or using environment variables:
export MESA_API_KEY=<API_KEY>
const mesa = new Mesa({
apiKey: process.env.MESA_API_KEY,
});
HTTP API Authentication
Include the API key in the Authorization header:
curl -H "Authorization: Bearer <API_KEY>" \
https://api.mesa.dev/my-org/repos
Git Authentication
Use the API key as the password when cloning or pushing:
# Clone with API key
git clone https://t:<API_KEY>@api.mesa.dev/my-org/my-repo.git
Managing API Keys
Listing Keys
const { apiKeys } = await mesa.admin.listApiKeys({ org: "my-org" });
for (const key of apiKeys) {
console.log(`${key.name}: ${key.id} (${key.scopes.join(", ")})`);
}
The full API key value is never returned when listing keys - only the key ID, name, and metadata.
Revoking Keys
await mesa.admin.revokeApiKey({
org: "my-org",
id: "key_abc123",
});
Revoked keys are immediately invalidated and cannot be used for authentication.
Permission Scopes
API keys are granted specific scopes that control what operations they can perform. Use the principle of least privilege - only grant the scopes necessary for each use case.
Available Scopes
| Scope | Description |
|---|
git:read | Clone and fetch repositories |
git:write | Push commits to repositories (includes git:read) |
repo:read | Read repository metadata, branches, commits, and content |
repo:create | Create new repositories and update existing ones |
repo:delete | Delete repositories |
webhook:read | List repository webhooks |
webhook:write | Create and delete repository webhooks |
admin | Full access to all operations including API key management |
Scope Hierarchy
Some scopes implicitly include others:
git:write includes git:read - A key with write access can also read
admin includes all scopes - Full access to everything
Scope Requirements by Operation
Repository Operations
| Operation | Required Scope |
|---|
| List repositories | repo:read |
| Get repository | repo:read |
| Create repository | repo:create |
| Update repository | repo:create |
| Delete repository | repo:delete |
Content & History
| Operation | Required Scope |
|---|
| Get file content | repo:read |
| List branches | repo:read |
| List commits | repo:read |
| Get commit | repo:read |
| Get diff | repo:read |
Git Operations
| Operation | Required Scope |
|---|
| Clone (git fetch) | git:read |
| Push (git push) | git:write |
| Create commit (API) | git:write |
| Create branch | git:write |
| Delete branch | git:write |
Webhooks
| Operation | Required Scope |
|---|
| List webhooks | webhook:read |
| Create webhook | webhook:write |
| Delete webhook | webhook:write |
Administration
| Operation | Required Scope |
|---|
| Create API key | admin |
| List API keys | admin |
| Revoke API key | admin |
Default Scopes
When creating an API key without specifying scopes, the following defaults are applied:
["git:read", "git:write", "repo:read", "repo:create"]
This provides common access for development workflows without repository deletion or admin capabilities.
Best Practices
Use Scoped Keys
Create separate API keys for different purposes with minimal required scopes:
// CI/CD key - only needs to read and write code
await mesa.admin.createApiKey({
org: "my-org",
body: {
name: "GitHub Actions",
scopes: ["git:read", "git:write", "repo:read"],
},
});
// Read-only key for analytics
await mesa.admin.createApiKey({
org: "my-org",
body: {
name: "Analytics Service",
scopes: ["repo:read"],
},
});
// Deployment key - can create repos but not delete
await mesa.admin.createApiKey({
org: "my-org",
body: {
name: "Deployment Service",
scopes: ["git:read", "git:write", "repo:read", "repo:create"],
},
});
Error Handling
Authentication Errors
try {
await mesa.repos.list({ org: "my-org" });
} catch (error) {
if (error.code === "UNAUTHORIZED") {
// Invalid or missing API key
console.error("Check your API key");
}
}
Permission Errors
try {
await mesa.repos.delete({ org: "my-org", repo: "important-repo" });
} catch (error) {
if (error.code === "FORBIDDEN") {
// API key lacks required scope
console.error("API key needs repo:delete scope");
}
}
Organization Access
API keys are scoped to a single organization. Attempting to access a different organization returns a forbidden error:
// Key belongs to "org-a"
const mesa = new Mesa({ apiKey: orgAKey });
// This will fail - wrong organization
await mesa.repos.list({ org: "org-b" }); // Error: FORBIDDEN