# Grants reference

Grants provide credentials to container runs. Each grant type injects authentication for specific hosts. Credentials are stored encrypted on your host machine and injected at the network layer by Moat's TLS-intercepting proxy. The container process does not have direct access to raw tokens.

Store a credential with `moat grant <provider>`, then use it in runs with `--grant <provider>` or in `moat.yaml`.

## Grant types

| Grant | Hosts matched | Header injected | Credential source |
|-------|---------------|-----------------|-------------------|
| `github` | `api.github.com`, `github.com` | `Authorization: Bearer ...` | gh CLI, `GITHUB_TOKEN`/`GH_TOKEN`, or PAT prompt |
| `claude` | `api.anthropic.com` | `Authorization: Bearer ...` | `claude setup-token` or imported OAuth |
| `anthropic` | `api.anthropic.com` | `x-api-key: ...` | API key from `console.anthropic.com` |
| `openai` | `api.openai.com`, `chatgpt.com`, `*.openai.com` | `Authorization: Bearer ...` | `OPENAI_API_KEY` or prompt |
| `gemini` | `generativelanguage.googleapis.com` (API key) or `cloudcode-pa.googleapis.com` (OAuth) | `x-goog-api-key: ...` (API key) or `Authorization: Bearer ...` (OAuth) | Gemini CLI OAuth, `GEMINI_API_KEY`, or prompt |
| `graphite` | `api.graphite.com`, `*.graphite.com` | `Authorization: token ...` | `GRAPHITE_TOKEN`, `GT_TOKEN`, or prompt |
| `meta` | `graph.facebook.com`, `graph.instagram.com` | `Authorization: Bearer ...` | `META_ACCESS_TOKEN` or prompt |
| `npm` | Per-registry (e.g., `registry.npmjs.org`, `npm.company.com`) | `Authorization: Bearer ...` | `.npmrc`, `NPM_TOKEN`, or manual |
| `aws` | All AWS service endpoints | AWS `credential_process` (STS temporary credentials) | IAM role assumption via STS |
| `ssh:<host>` | Specified host only | SSH agent forwarding (not HTTP) | Host SSH agent (`SSH_AUTH_SOCK`) |
| `mcp-<name>` | Host from MCP server `url` field | Configured per-server header | Interactive prompt |
| `gitlab` | `gitlab.com`, `*.gitlab.com` | `PRIVATE-TOKEN: ...` | `GITLAB_TOKEN`, `GL_TOKEN`, or prompt |
| `brave-search` | `api.search.brave.com` | `X-Subscription-Token: ...` | `BRAVE_API_KEY`, `BRAVE_SEARCH_API_KEY`, or prompt |
| `elevenlabs` | `api.elevenlabs.io` | `xi-api-key: ...` | `ELEVENLABS_API_KEY` or prompt |
| `linear` | `api.linear.app` | `Authorization: ...` | `LINEAR_API_KEY` or prompt |
| `vercel` | `api.vercel.com`, `*.vercel.com` | `Authorization: Bearer ...` | `VERCEL_TOKEN` or prompt |
| `sentry` | `sentry.io`, `*.sentry.io` | `Authorization: Bearer ...` | `SENTRY_AUTH_TOKEN` or prompt |
| `datadog` | `*.datadoghq.com` | `DD-API-KEY: ...` | `DD_API_KEY`, `DATADOG_API_KEY`, or prompt |

Run `moat grant providers` to list all providers, including any [custom providers](#custom-providers) you've added.

## GitHub

### CLI command

```bash
moat grant github
```

No flags. The command automatically detects your credential source.

### Credential sources (in order of preference)

1. **gh CLI** -- Uses the token from `gh auth token` if the GitHub CLI is installed and authenticated
2. **Environment variable** -- Falls back to `GITHUB_TOKEN` or `GH_TOKEN` if set
3. **Personal Access Token** -- Interactive prompt for manual PAT entry

### What it injects

The proxy injects an `Authorization: Bearer <token>` header for requests to:

- `api.github.com`
- `github.com`

The container receives `GH_TOKEN` set to a format-valid placeholder so the gh CLI works without prompting.

### Refresh behavior

Tokens sourced from `gh auth token` or environment variables are refreshed every 30 minutes. PATs entered manually are static.

### moat.yaml

```yaml
grants:
  - github
```

### Example

```bash
$ moat grant github

Found gh CLI authentication
Use token from gh CLI? [Y/n]: y
Validating token...
Authenticated as: octocat
GitHub credential saved

$ moat run --grant github ./my-project
```

## Anthropic / Claude

Anthropic credentials are split into two separate grant types:

- **`claude`** -- OAuth tokens from Claude Pro/Max subscriptions. Restricted by Anthropic's ToS to Claude Code only. Uses `Authorization: Bearer` auth.
- **`anthropic`** -- API keys from `console.anthropic.com`. Works with any tool or agent. Uses `x-api-key` auth.

Each command grants its own credential type. Both can coexist and `moat grant list` shows both.

### CLI commands

```bash
moat grant claude       # OAuth token (for moat claude / Claude Code)
moat grant anthropic    # API key (for any agent or tool)
```

No flags.

### `moat grant claude`

Presents a menu of OAuth token sources:

1. **Claude subscription** -- Runs `claude setup-token` to obtain a long-lived OAuth token. Requires a Claude Pro/Max subscription and the Claude CLI installed.
2. **Existing OAuth token** -- Paste a token from a previous `claude setup-token` run.
3. **Import existing credentials** -- Imports OAuth tokens from your local Claude Code installation.

Stored as `claude.enc`.

### `moat grant anthropic`

Prompts for an API key directly, or uses `ANTHROPIC_API_KEY` from the environment.

Stored as `anthropic.enc`.

### What it injects

The proxy injects credentials for requests to `api.anthropic.com`:

- **`claude` grant**: `Authorization: Bearer <token>` with OAuth beta flag. Container receives `CLAUDE_CODE_OAUTH_TOKEN` placeholder.
- **`anthropic` grant**: `x-api-key: <key>`. Container receives `ANTHROPIC_API_KEY` placeholder.

### Refresh behavior

OAuth tokens imported from a local Claude Code installation do not auto-refresh. When the token expires, run a Claude Code session on your host to refresh it, then re-import with `moat grant claude`.

API keys do not expire.

### `moat claude` grant resolution

When you run `moat claude`, the credential is selected automatically:

1. If `claude` exists, use it (preferred for Claude Code)
2. If only `anthropic` exists, use it as fallback
3. If neither exists, error with instructions to run `moat grant claude`

### Using both grants

You can grant both and use them together. This is useful when Claude Code needs its OAuth token and sub-tools in the same container need an API key:

```bash
moat run --grant claude --grant anthropic ./my-project
```

### moat.yaml

```yaml
grants:
  - claude        # OAuth token for Claude Code
  - anthropic     # API key for any tool
```

### Backward compatibility

Existing users with an OAuth token stored under `anthropic` (the old single-provider model) are auto-migrated: `moat claude` detects the OAuth token prefix, copies it to `claude.enc`, and removes the old `anthropic` entry.

### Examples

```bash
# Grant an OAuth token for Claude Code
$ moat grant claude

Choose authentication method:

  1. Claude subscription (OAuth token)
     Runs 'claude setup-token' to get a long-lived token.
  2. Existing OAuth token
  3. Import existing Claude Code credentials

Enter choice [1-3]: 1

Running 'claude setup-token' to obtain authentication token...
Credential saved to ~/.moat/credentials/claude.enc

# Grant an API key for general use
$ moat grant anthropic

Enter your Anthropic API key: ••••••••
Validating API key...
API key is valid.
Credential saved to ~/.moat/credentials/anthropic.enc

# Use Claude Code (picks up OAuth token automatically)
$ moat claude ./my-project

# Use both in a single run
$ moat run --grant claude --grant anthropic ./my-project
```

## OpenAI

### CLI command

```bash
moat grant openai
```

No flags.

### Credential sources

1. **Environment variable** -- Uses `OPENAI_API_KEY` if set
2. **Interactive prompt** -- Prompts for an API key

### What it injects

The proxy injects an `Authorization: Bearer <token>` header for requests to `api.openai.com`, `chatgpt.com`, and `*.openai.com`.

The container receives `OPENAI_API_KEY` set to a format-valid placeholder so OpenAI SDKs work without prompting.

### Refresh behavior

API keys do not expire or refresh.

### moat.yaml

```yaml
grants:
  - openai
```

### Example

```bash
$ moat grant openai
Enter your OpenAI API key.
You can find or create one at: https://platform.openai.com/api-keys

API Key: ••••••••
Validating API key...
API key is valid.

OpenAI API key saved to ~/.moat/credentials/openai.enc

$ moat codex ./my-project
```

## Gemini

### CLI command

```bash
moat grant gemini
```

No flags. The command detects whether Gemini CLI is installed and presents options accordingly.

### Credential sources

1. **Gemini CLI OAuth (recommended)** -- Imports refresh tokens from a local Gemini CLI installation. Requires Gemini CLI installed and authenticated.
2. **API key** -- Enter an API key directly or set `GEMINI_API_KEY` in your environment.

### What it injects

Gemini routes to different API backends depending on authentication method:

- **API key mode**: The proxy injects an `x-goog-api-key: <key>` header for requests to `generativelanguage.googleapis.com`. The container receives `GEMINI_API_KEY` set to a placeholder value.
- **OAuth mode**: The proxy injects `Authorization: Bearer <token>` for requests to `cloudcode-pa.googleapis.com` and handles token substitution for `oauth2.googleapis.com`. The container receives a placeholder `oauth_creds.json` in `~/.gemini/`.

### Refresh behavior

OAuth tokens are automatically refreshed by the proxy. Google OAuth tokens expire after 1 hour; Moat refreshes 15 minutes before expiry (every 45 minutes).

API keys do not expire.

### moat.yaml

```yaml
grants:
  - gemini
```

### Example

```bash
$ moat grant gemini

Choose authentication method:

  1. Import Gemini CLI credentials (recommended)
  2. Gemini API key

Enter choice [1 or 2]: 1

Found Gemini CLI credentials.
Validating refresh token...
Refresh token is valid.

Gemini credential saved to ~/.moat/credentials/gemini.enc

$ moat gemini ./my-project
```

## npm

### CLI command

```bash
moat grant npm
moat grant npm --host=<registry-host>
```

### Flags

| Flag | Description |
|------|-------------|
| `--host HOSTNAME` | Specific registry host (e.g., `npm.company.com`) |

Without `--host`, the command auto-discovers registries from `~/.npmrc` and the `NPM_TOKEN` environment variable.

### Credential sources

1. **`.npmrc` file** -- Parses `~/.npmrc` for `//host/:_authToken=` entries and `@scope:registry=` routing
2. **Environment variable** -- Falls back to `NPM_TOKEN` for the default registry (`registry.npmjs.org`)
3. **Manual entry** -- Interactive prompt for a token

### What it injects

The proxy injects an `Authorization: Bearer <token>` header for requests to each registered npm registry host. Each host gets its own credential — multiple registries are supported in a single grant.

The container receives a generated `.npmrc` at `~/.npmrc` with:
- Real scope-to-registry routing (npm needs this to resolve scoped packages)
- Placeholder tokens (`npm_moatProxyInjected00000000`) — the proxy replaces `Authorization` headers at the network layer

### Refresh behavior

npm tokens are static and do not refresh. If a token expires, revoke and re-grant.

### Stacking

Multiple `moat grant npm --host=<host>` calls merge into a single credential. Each call adds or replaces the entry for that host. All registries are injected together at runtime.

### moat.yaml

```yaml
grants:
  - npm
```

### Example

```bash
$ moat grant npm

Choose authentication method:

  1. Import from .npmrc / environment
     Found registries: registry.npmjs.org (default), npm.company.com (@myorg)
     To import a single registry, use: moat grant npm --host=<host>

  2. Enter token manually

Enter choice [1-2]: 1
Validating...
  ✓ registry.npmjs.org — authenticated as "jsmith"
  ✓ npm.company.com — authenticated as "jsmith"
Credential saved to ~/.moat/credentials/npm.enc

$ moat run --grant npm -- npm whoami
jsmith
```

## Graphite

### CLI command

```bash
moat grant graphite
```

No flags.

### Credential sources

1. **Environment variable** -- Uses `GRAPHITE_TOKEN` or `GT_TOKEN` if set
2. **Interactive prompt** -- Prompts for a token with instructions to visit `https://app.graphite.com/activate`

### What it injects

The proxy injects an `Authorization: token <token>` header for requests to `api.graphite.com`. Note: Graphite uses the `token` prefix, not `Bearer`.

The container receives a config file at `~/.config/graphite/user_config` with a placeholder token so the Graphite CLI (`gt`) works without prompting.

### Implied dependencies

Granting `graphite` automatically adds `graphite-cli`, `node`, and `git` as container dependencies. These are installed during image build.

### Refresh behavior

Graphite tokens are static and do not refresh.

### moat.yaml

```yaml
grants:
  - graphite
```

### Example

```bash
$ moat grant graphite
Enter a Graphite auth token.

To get your token:
  1. Visit https://app.graphite.com/activate
  2. Sign in and copy the token
  3. Paste it below

Token: ••••••••
Validating token...
Token validated successfully

$ moat run --grant graphite ./my-project
```

## Meta

### CLI command

```bash
moat grant meta
```

No flags.

### Credential sources

1. **Environment variable** -- Uses `META_ACCESS_TOKEN` if set
2. **Interactive prompt** -- Prompts for an access token

The grant command validates the token against Meta's Graph API before saving.

### What it injects

The proxy injects an `Authorization: Bearer <token>` header for requests to:

- `graph.facebook.com`
- `graph.instagram.com`

No environment variables or config files are set inside the container.

### Environment variables

| Variable | Purpose |
|----------|---------|
| `META_ACCESS_TOKEN` | Access token -- any Meta token type (see below) |
| `META_APP_ID` | Facebook app ID -- needed for token refresh |
| `META_APP_SECRET` | Facebook app secret -- needed for token refresh |

All three can be set as environment variables or entered interactively. `META_ACCESS_TOKEN` is required. `META_APP_ID` and `META_APP_SECRET` are optional -- the grant flow prompts for them after the token, and you can press Enter to skip.

### Token types and refresh

The grant flow accepts any Meta access token. What happens next depends on which kind of token you provide and whether you also provide app credentials (app ID + app secret).

**Long-lived user tokens with app credentials** are the recommended setup. The token expires after ~60 days, but Moat exchanges it for a fresh one once per day, keeping it valid indefinitely. Generate a long-lived token by exchanging a short-lived one through the Graph API Explorer or your app's OAuth flow, and provide your app ID and secret during `moat grant meta`. If a token is compromised, revoke it immediately in the Meta developer dashboard -- do not rely on expiry.

**Short-lived user tokens** (from Graph API Explorer or an OAuth login flow) expire in 1--2 hours. If you provide app credentials, Moat exchanges the short-lived token for a long-lived one on the first refresh cycle, then refreshes it daily. This is a valid way to bootstrap -- you do not need to exchange the token yourself first. Without app credentials, the token expires quickly and there is no way to extend it. This can provide additional security at the cost of capping the amount of time your agent will be able to run independently.

**System user tokens** never expire. They are created in Business Manager > System Users > Generate Token and are typically reserved for business owners or internal infrastructure. Because they never expire and cannot be scoped per-session, a leaked system user token has no automatic damage window. Prefer long-lived user tokens with app credentials for delegated access and agent use.

Summary:

| Token type | App credentials provided? | What happens |
|-----------|--------------------------|-------------|
| Long-lived user | Yes | Exchanged for a fresh long-lived token once per day. **Recommended.** |
| Long-lived user | No | Works for ~60 days, then expires. |
| Short-lived user | Yes | Exchanged for a long-lived token on first refresh, then refreshed daily. |
| Short-lived user | No | Expires in 1--2 hours. Requests fail after that. |
| System user | No | Token never expires. No refresh needed. |
| System user | Yes | Unnecessary but harmless. Refresh is a no-op. |

### API version

The provider uses Meta Graph API v25.0 by default. Override with the `META_API_VERSION` environment variable:

```bash
META_API_VERSION=v26.0 moat grant meta
```

### moat.yaml

```yaml
grants:
  - meta
```

### Examples

**User token with app credentials (recommended):**

```bash
$ moat grant meta

Enter a Meta access token.

To create one:
  1. Go to https://developers.facebook.com/tools/explorer/
  2. Select your app and generate a token with the required permissions

Access token: ••••••••
Authenticated as: Jane Developer

Optional: provide app ID and app secret to enable automatic token refresh.
Press Enter to skip.

App ID (or Enter to skip): ••••••••
App secret: ••••••••
Token refresh enabled

$ moat run --grant meta ./my-project
```

**Via environment variables:**

```bash
$ META_ACCESS_TOKEN=EAAx... META_APP_ID=123456 META_APP_SECRET=abc123 moat grant meta

Using token from META_ACCESS_TOKEN environment variable
Authenticated as: Jane Developer
Found META_APP_ID and META_APP_SECRET for token refresh

$ moat run --grant meta ./my-project
```

## AWS

### CLI command

```bash
moat grant aws --role <ARN> [flags]
```

### Flags

| Flag | Description | Default |
|------|-------------|---------|
| `--role ARN` | IAM role ARN to assume (required) | -- |
| `--region REGION` | AWS region for API calls | From AWS config |
| `--session-duration DURATION` | Session duration (e.g., `1h`, `30m`, `15m`) | `15m` |
| `--external-id ID` | External ID for cross-account role assumption | -- |
| `--aws-profile PROFILE` | AWS shared config profile for role assumption (falls back to `AWS_PROFILE` env var) | -- |

### Credential source

Moat uses your host AWS credentials to call `sts:AssumeRole`. Your host must have valid AWS credentials (via `aws configure`, environment variables, or instance profile), and the target role must have a trust policy allowing your host identity to assume it.

If you use named AWS profiles, pass `--aws-profile` (or set `AWS_PROFILE`) at grant time. The profile is stored with the credential so the proxy daemon uses the correct source identity for role assumption, regardless of the daemon's own environment.

### What it injects

AWS credentials use `credential_process` rather than HTTP header injection:

1. The role ARN and configuration are stored (not temporary credentials)
2. When a run starts, Moat configures `AWS_CONFIG_FILE` in the container with a `credential_process` entry
3. The `credential_process` command calls back to the proxy, which assumes the role and returns fresh temporary credentials
4. The AWS SDK automatically calls the credential process when credentials expire

> **Note:** The `credential_process` mechanism is accessible inside the container. Credentials are temporary (STS), sessions are short (default 15 minutes), and permissions are scoped to the assumed role.

### Refresh behavior

The AWS SDK handles credential refresh automatically via `credential_process`. Each call to the process assumes a fresh role session with the configured duration.

### moat.yaml

```yaml
grants:
  - aws
```

> **Note:** AWS-specific options (role, region, session duration, external ID) are configured at grant time with `moat grant aws`, not in `moat.yaml`. The `moat.yaml` grants field only specifies which grant types to use for a run.

### Example

```bash
$ moat grant aws \
    --role arn:aws:iam::123456789012:role/AgentRole \
    --region us-west-2 \
    --session-duration 30m

Assuming role: arn:aws:iam::123456789012:role/AgentRole
Region: us-west-2
Session duration: 30m0s
Role assumed successfully
AWS credential saved

$ moat run --grant aws ./my-project
```

## SSH

### CLI command

```bash
moat grant ssh --host <hostname>
```

### Flags

| Flag | Description |
|------|-------------|
| `--host HOSTNAME` | Host to grant SSH access to (required) |

### Credential source

Uses your host's SSH agent (`SSH_AUTH_SOCK`). The SSH agent must be running with keys loaded.

### What it injects

SSH grants work differently from other grants. Instead of injecting HTTP headers, Moat proxies SSH agent requests:

1. An SSH agent proxy starts inside the container
2. The proxy connects to your host's SSH agent
3. Key listing and signing requests are forwarded, but only for keys mapped to the granted host
4. Private keys never enter the container

### Refresh behavior

SSH agent requests are forwarded in real time. No refresh mechanism is needed.

### moat.yaml

```yaml
grants:
  - ssh:github.com
```

The host name is part of the grant identifier, separated by a colon.

### Example

```bash
$ moat grant ssh --host github.com

Using key: user@host (SHA256:...)
Granted SSH access to github.com

$ moat run --grant ssh:github.com -- git clone git@github.com:my-org/my-project.git
```

## MCP

### CLI command

```bash
moat grant mcp <name>
```

The `<name>` argument matches the MCP server name in `moat.yaml`. The credential is stored as `mcp-<name>`.

### Credential source

Interactive prompt for a credential value (hidden input).

### What it injects

The proxy injects the credential into the HTTP header specified by `auth.header` in the MCP server configuration. Injection occurs for requests matching the host in the MCP server's `url` field.

### Refresh behavior

MCP credentials are static. Revoke and re-grant to update them.

### moat.yaml

MCP grants are referenced in the top-level `mcp:` field, not in `grants:`:

```yaml
mcp:
  - name: context7
    url: https://mcp.context7.com/mcp
    auth:
      grant: mcp-context7
      header: CONTEXT7_API_KEY
```

### Example

```bash
$ moat grant mcp context7
Enter credential for MCP server 'context7': ••••••••
MCP credential 'mcp-context7' saved

$ moat claude ./my-project
```

## Using grants in runs

### Via CLI flags

Pass `--grant` one or more times:

```bash
moat run --grant github ./my-project
moat run --grant github --grant anthropic ./my-project
moat run --grant ssh:github.com ./my-project
```

### Via moat.yaml

List grants in the `grants` field:

```yaml
grants:
  - github
  - anthropic
  - openai
  - npm
  - ssh:github.com
```

Grants from CLI flags are merged with those in `moat.yaml`.

### Multiple grants

Combine any number of grants in a single run:

```yaml
grants:
  - anthropic
  - github
  - ssh:github.com
  - aws
```

```bash
moat claude --grant github --grant ssh:github.com ./my-project
```

Each grant type injects credentials independently. The proxy matches requests by host and injects the appropriate headers.

## Managing grants

### List stored grants

```bash
moat grant list
moat grant list --json

# List grants in a specific profile
moat grant list --profile work
```

### Revoke a grant

```bash
moat revoke github
moat revoke claude
moat revoke anthropic
moat revoke npm
moat revoke ssh:github.com
moat revoke mcp-context7

# Revoke from a specific profile
moat revoke github --profile work
```

This deletes the encrypted credential file. Future runs cannot use the credential until you grant it again.

## Credential profiles

Profiles maintain separate sets of credentials. Use one profile for personal projects and another for work.

### Setting a profile

Set the active profile with the `--profile` global flag or the `MOAT_PROFILE` environment variable:

```bash
# Grant a credential to a profile
moat grant github --profile work

# Use profile credentials in a run
moat run --grant github --profile work

# List profile credentials
moat grant list --profile work

# Set via environment variable
export MOAT_PROFILE=work
moat grant github
moat run --grant github
```

### Storage layout

Profile credentials are stored separately from the default credential store:

```
~/.moat/credentials/           # Default (no profile)
~/.moat/credentials/profiles/
  work/                        # "work" profile
  personal/                    # "personal" profile
```

Each profile has its own isolated set of encrypted credential files. Granting a credential in one profile does not affect another.

### Profile names

Profile names must start with a letter or digit and contain only letters, digits, hyphens, and underscores.

Valid: `work`, `my-project`, `team_alpha`, `prod1`
Invalid: `-leading-dash`, `has spaces`, `uses.dots`

## Credential storage

Credentials are stored encrypted in `~/.moat/credentials/` (or `~/.moat/credentials/profiles/<name>/` when using a profile). See [Credential management](/moat/concepts/credentials) for encryption and storage details.

## Config-driven providers

The providers from `gitlab` through `datadog` in the [grant types table](#grant-types) are defined as YAML configurations shipped with the binary. They work the same as the Go-implemented providers -- credentials are stored encrypted and injected at the network layer -- but are defined declaratively.

Use them the same way:

```bash
moat grant gitlab
moat run --grant gitlab ./my-project
```

### Custom providers

Add your own providers by creating YAML files in `~/.moat/providers/`. Each file defines a provider with host matching rules, header injection, and credential sources. See [Provider YAML reference](./provider-yaml) for the full schema.

### Listing providers

List all available providers (built-in, packaged, and custom) with:

```bash
moat grant providers
moat grant providers --json
```

## Related pages

- [Credential management](/moat/concepts/credentials) -- How credential injection works conceptually
- [Security model](/moat/concepts/security) -- Threat model and security properties
- [CLI reference](/moat/reference/cli) -- Full CLI command reference, including `moat grant` subcommands
- [moat.yaml reference](/moat/reference/moat-yaml) -- All `moat.yaml` fields, including `grants` and `mcp`
- [Provider YAML reference](./provider-yaml) -- Schema for YAML-defined credential providers

---
[← Moat documentation index](https://majorcontext.com/moat/llms.txt)
