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 agent.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 |
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 you’ve added.
GitHub
CLI command
moat grant github
No flags. The command automatically detects your credential source.
Credential sources (in order of preference)
- gh CLI — Uses the token from
gh auth tokenif the GitHub CLI is installed and authenticated - Environment variable — Falls back to
GITHUB_TOKENorGH_TOKENif set - 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.comgithub.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.
agent.yaml
grants:
- github
Example
$ 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. UsesAuthorization: Bearerauth.anthropic— API keys fromconsole.anthropic.com. Works with any tool or agent. Usesx-api-keyauth.
Each command grants its own credential type. Both can coexist and moat grant list shows both.
CLI commands
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:
- Claude subscription — Runs
claude setup-tokento obtain a long-lived OAuth token. Requires a Claude Pro/Max subscription and the Claude CLI installed. - Existing OAuth token — Paste a token from a previous
claude setup-tokenrun. - 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:
claudegrant:Authorization: Bearer <token>with OAuth beta flag. Container receivesCLAUDE_CODE_OAUTH_TOKENplaceholder.anthropicgrant:x-api-key: <key>. Container receivesANTHROPIC_API_KEYplaceholder.
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:
- If
claudeexists, use it (preferred for Claude Code) - If only
anthropicexists, use it as fallback - 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:
moat run --grant claude --grant anthropic ./my-project
agent.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
# 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
moat grant openai
No flags.
Credential sources
- Environment variable — Uses
OPENAI_API_KEYif set - 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.
agent.yaml
grants:
- openai
Example
$ 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
moat grant gemini
No flags. The command detects whether Gemini CLI is installed and presents options accordingly.
Credential sources
- Gemini CLI OAuth (recommended) — Imports refresh tokens from a local Gemini CLI installation. Requires Gemini CLI installed and authenticated.
- API key — Enter an API key directly or set
GEMINI_API_KEYin 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 togenerativelanguage.googleapis.com. The container receivesGEMINI_API_KEYset to a placeholder value. - OAuth mode: The proxy injects
Authorization: Bearer <token>for requests tocloudcode-pa.googleapis.comand handles token substitution foroauth2.googleapis.com. The container receives a placeholderoauth_creds.jsonin~/.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.
agent.yaml
grants:
- gemini
Example
$ 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
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
.npmrcfile — Parses~/.npmrcfor//host/:_authToken=entries and@scope:registry=routing- Environment variable — Falls back to
NPM_TOKENfor the default registry (registry.npmjs.org) - 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 replacesAuthorizationheaders 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.
agent.yaml
grants:
- npm
Example
$ 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
AWS
CLI command
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 | — |
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.
What it injects
AWS credentials use credential_process rather than HTTP header injection:
- The role ARN and configuration are stored (not temporary credentials)
- When a run starts, Moat configures
AWS_CONFIG_FILEin the container with acredential_processentry - The
credential_processcommand calls back to the proxy, which assumes the role and returns fresh temporary credentials - The AWS SDK automatically calls the credential process when credentials expire
Note: The
credential_processmechanism 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.
agent.yaml
grants:
- aws
Note: AWS-specific options (role, region, session duration, external ID) are configured at grant time with
moat grant aws, not inagent.yaml. Theagent.yamlgrants field only specifies which grant types to use for a run.
Example
$ 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
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:
- An SSH agent proxy starts inside the container
- The proxy connects to your host’s SSH agent
- Key listing and signing requests are forwarded, but only for keys mapped to the granted host
- Private keys never enter the container
Refresh behavior
SSH agent requests are forwarded in real time. No refresh mechanism is needed.
agent.yaml
grants:
- ssh:github.com
The host name is part of the grant identifier, separated by a colon.
Example
$ 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
moat grant mcp <name>
The <name> argument matches the MCP server name in agent.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.
agent.yaml
MCP grants are referenced in the top-level mcp: field, not in grants::
mcp:
- name: context7
url: https://mcp.context7.com/mcp
auth:
grant: mcp-context7
header: CONTEXT7_API_KEY
Example
$ 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:
moat run --grant github ./my-project
moat run --grant github --grant anthropic ./my-project
moat run --grant ssh:github.com ./my-project
Via agent.yaml
List grants in the grants field:
grants:
- github
- anthropic
- openai
- npm
- ssh:github.com
Grants from CLI flags are merged with those in agent.yaml.
Multiple grants
Combine any number of grants in a single run:
grants:
- anthropic
- github
- ssh:github.com
- aws
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
moat grant list
moat grant list --json
# List grants in a specific profile
moat grant list --profile work
Revoke a grant
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:
# 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 for encryption and storage details.
Config-driven providers
The providers from gitlab through datadog in the grant types table 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:
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 for the full schema.
Listing providers
List all available providers (built-in, packaged, and custom) with:
moat grant providers
moat grant providers --json
Related pages
- Credential management — How credential injection works conceptually
- Security model — Threat model and security properties
- CLI reference — Full CLI command reference, including
moat grantsubcommands - agent.yaml reference — All
agent.yamlfields, includinggrantsandmcp - Provider YAML reference — Schema for YAML-defined credential providers