Quick start
This tutorial walks through running an agent with credential injection. By the end, you’ll understand how Moat executes code in containers, injects credentials, and captures observability data.
Prerequisites:
- Moat installed (Installation)
- Docker running, or Apple containers installed (macOS 26+)
- GitHub authentication (one of:
ghCLI,GITHUB_TOKENenv var, or Personal Access Token)
Step 1: Grant GitHub credentials
Store a GitHub credential that Moat can inject into runs:
$ moat grant github
Found gh CLI authentication
Use token from gh CLI? [Y/n]: y
Validating token...
Authenticated as: your-username
GitHub credential saved to ~/.moat/credentials/github.enc
If you don’t have gh CLI configured, you’ll be prompted to enter a Personal Access Token. The credential is encrypted and stored in ~/.moat/credentials/.
This is a one-time setup. The credential persists across runs until you revoke it with moat revoke github.
Step 2: Run a command with credential injection
Run curl inside a container with the GitHub credential injected:
$ moat run --grant github -- curl -s https://api.github.com/user
{
"login": "your-username",
"id": 1234567,
"name": "Your Name"
}
What happened:
- Moat created a container with the default
ubuntu:22.04image - Started a TLS-intercepting proxy
- Routed container traffic through the proxy
- The proxy detected a request to
api.github.comand injected anAuthorization: Bearer <token>header - GitHub returned your user profile
Step 3: Verify the token was not exposed
The credential was injected at the network layer, not via environment variables:
$ moat run --grant github -- env | grep -i github
# (no output)
$ moat run --grant github -- env | grep -i token
# (no output)
The container process has no access to the raw token. It can only make authenticated requests to GitHub through the proxy.
Step 4: View the network trace
Moat records all HTTP requests made through the proxy:
$ moat trace --network
[10:23:44.512] GET https://api.github.com/user 200 (89ms)
Add -v for headers and response bodies:
$ moat trace --network -v
[10:23:44.512] GET https://api.github.com/user 200 (89ms)
Request Headers:
User-Agent: curl/7.81.0
Accept: */*
Authorization: Bearer [REDACTED]
Response Headers:
Content-Type: application/json; charset=utf-8
X-RateLimit-Remaining: 4999
...
The injected Authorization header is redacted in traces.
Step 5: View container logs
Container stdout and stderr are captured with timestamps:
$ moat logs
[2025-01-21T10:23:44.512Z] {"login": "your-username", ...}
Step 6: Create an agent.yaml
For repeated runs, create a configuration file. Make a new directory:
mkdir my-agent
cd my-agent
Create agent.yaml:
name: my-agent
dependencies:
- node@20
grants:
- github
env:
NODE_ENV: development
Create a script to test. Save as check-repos.js:
const https = require('https');
const options = {
hostname: 'api.github.com',
path: '/user/repos?per_page=5',
headers: { 'User-Agent': 'moat-example' }
};
https.get(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const repos = JSON.parse(data);
repos.forEach(repo => console.log(`- ${repo.full_name}`));
});
});
Run the agent:
$ moat run -- node check-repos.js
- your-username/repo-1
- your-username/repo-2
- your-username/repo-3
- your-username/repo-4
- your-username/repo-5
Moat:
- Read
agent.yamland determined the base image (node:20fromnode@20) - Injected the GitHub credential (from
grants: [github]) - Set the environment variable
NODE_ENV=development - Ran
node check-repos.js
Step 7: List and clean up runs
List all runs:
$ moat list
NAME RUN ID STATE SERVICES
my-agent run_a1b2c3d4e5f6 stopped
my-agent run_f6e5d4c3b2a1 stopped
(none) run_1a2b3c4d5e6f stopped
View system status including disk usage:
$ moat status
Runs:
Active: 0
Stopped: 3
Images:
node:20 (245 MB)
ubuntu:22.04 (78 MB)
Disk Usage:
Runs: 12 MB
Images: 323 MB
Clean up stopped runs:
$ moat clean
This will remove:
- 3 stopped runs
- 0 unused images
Proceed? [y/N]: y
Removed 3 runs
Next steps
- Sandboxing — How container isolation works
- Credential management — Credential storage and injection
- Running Claude Code — Use Moat with Claude Code