imds-broker vends AWS credentials over a local IMDSv2-compatible HTTP endpoint, so any tool that expects to be running on EC2 just works — locally.
It's for developers running Docker containers, local CLI tools, and AI agents that need AWS access without leaking long-lived credentials into environments where they don't belong. Credentials stay in your AWS config or SSO session on the host. Consumers only ever see short-lived tokens fetched from a URL.
flowchart LR
classDef consumer fill:#E8F1FF,stroke:#3B82F6,color:#0B2A4A
classDef broker fill:#FFF4E5,stroke:#F59E0B,color:#4A2E00
classDef aws fill:#E9FBEF,stroke:#10B981,color:#053B24
subgraph Host["Developer machine"]
direction LR
Creds["~/.aws/config<br/>SSO session"]
Serve["imds-broker serve<br/>(single profile)"]:::broker
MCP["imds-broker mcp<br/>(create/stop servers)"]:::broker
Creds --> Serve
Creds --> MCP
end
Container["Docker container<br/>AWS_EC2_METADATA_SERVICE_ENDPOINT"]:::consumer
CLI["Local CLI / test suite<br/>AWS_EC2_METADATA_SERVICE_ENDPOINT"]:::consumer
Agent["AI agent (Claude, etc.)<br/>MCP client"]:::consumer
Serve -- IMDSv2 over bridge gateway --> Container
Serve -- IMDSv2 over loopback --> CLI
Agent -- create_server / stop_server --> MCP
MCP -- IMDSv2 endpoint URL --> Agent
Container --> AWS(("AWS APIs")):::aws
CLI --> AWS
Agent --> AWS
Main commands:
serve— run a single IMDS server for one AWS profile. Point Docker containers or local tools at it viaAWS_EC2_METADATA_SERVICE_ENDPOINT. This is the primary day-to-day mode.mcp— expose an MCP stdio server so AI agents can create and stop IMDS servers on demand for specific profiles.profiles— list the AWS profiles that would be visible to the MCP server, as JSON. Useful for scripting.doctor— check the host-side broker configuration and sandbox assumptions.
Most local AWS workflows either bake credentials into a container, copy ~/.aws into an image, or export AWS_ACCESS_KEY_ID into a subprocess. That's fine for throwaway work, but:
- SSO credentials expire and have to be re-exported constantly.
- Static IAM keys leak into shell history, container images, and CI logs.
- AI agents running in sandboxes often can't run
aws sso loginor assume roles themselves.
imds-broker sidesteps all of that. The consumer learns a URL. The broker, on the host, resolves credentials fresh for every request, upgrading long-lived IAM keys to short-lived STS session tokens before handing anything out.
Pre-built binaries for Linux, macOS, and Windows (amd64/arm64) are published to GitHub Releases. Every artifact carries a build-provenance attestation — see Verifying releases.
mise (recommended)
mise installs directly from GitHub Releases via its
GitHub backend; it
verifies the artifact checksum and, with github_attestations enabled (the
current default), its build-provenance attestation:
mise use -g github:jamestelfer/imds-brokerInstall script
Each release ships a self-contained installer (generated with binstaller) that detects your platform and checks the download against checksums embedded in the script — no separate checksum file is fetched:
curl -fsSL https://github.com/jamestelfer/imds-broker/releases/latest/download/install.sh | shIt installs to ~/.local/bin; pass -b for another directory and a tag to pin
a version:
curl -fsSL https://github.com/jamestelfer/imds-broker/releases/latest/download/install.sh \
| sh -s -- -b /usr/local/bin <TAG>The script carries a build-provenance attestation, so you can verify it before running it (with an authenticated GitHub CLI). This transitively covers the binary too: a verified script is guaranteed to hold the genuine checksums it then enforces on the download.
curl -fsSL -O https://github.com/jamestelfer/imds-broker/releases/latest/download/install.sh
gh attestation verify install.sh --repo jamestelfer/imds-broker
sh install.shHomebrew (macOS)
brew install jamestelfer/tap/imds-brokernpm
npm install -g @jamestelfer/imds-brokerNix
nix profile install github:jamestelfer/imds-brokerManual download
Download the archive for your platform from the
releases page, verify its
provenance, and put the binary on your PATH:
OS=linux ARCH=amd64 # or darwin/windows, arm64
curl -fsSLO "https://github.com/jamestelfer/imds-broker/releases/latest/download/imds-broker_${OS}_${ARCH}.tar.gz"
gh attestation verify "imds-broker_${OS}_${ARCH}.tar.gz" --repo jamestelfer/imds-broker
tar -xzf "imds-broker_${OS}_${ARCH}.tar.gz" imds-broker
install -m 0755 imds-broker ~/.local/bin/Windows archives are .zip. See Verifying releases for
what the attestation proves and for checksum-only verification.
go install
go install github.com/jamestelfer/imds-broker/cmd/imds-broker@latestRelease artifacts — the binary archives and the generated install.sh — carry a
build-provenance attestation
(SLSA) generated by the release workflow with Sigstore
keyless signing — there is no long-lived signing key. Each artifact is bound, by
digest, to the source commit and the workflow that produced it.
To verify a downloaded artifact, install the GitHub CLI (≥ 2.49.0) and run:
# e.g. ARTIFACT=imds-broker_linux_amd64.tar.gz
gh attestation verify "$ARTIFACT" --repo jamestelfer/imds-brokerTo additionally pin the signing workflow, add
--signer-workflow jamestelfer/imds-broker/.github/workflows/release.yml. The
attestation is a Sigstore bundle, so cosign can
verify it too; checksums.txt is still published for
sha256sum --check checksums.txt.
Run a single IMDS server for a named AWS profile:
imds-broker serve --profile my-profile [--region us-east-1]On startup the endpoint URL is logged to stderr:
... INFO IMDS server listening url=http://127.0.0.1:PORT profile=my-profile
Point any AWS SDK at it:
export AWS_EC2_METADATA_SERVICE_ENDPOINT=http://127.0.0.1:PORT
aws s3 lsThe broker listens on all interfaces and auto-discovers the Docker bridge gateway, so containers can reach it without --network host. The connection filter still rejects anything outside loopback, the Docker bridge, and your local LAN.
# Linux (host network):
docker run --rm \
--network host \
-e AWS_EC2_METADATA_SERVICE_ENDPOINT=http://127.0.0.1:PORT \
amazon/aws-cli s3 ls
# macOS / Windows (Docker Desktop):
docker run --rm \
-e AWS_EC2_METADATA_SERVICE_ENDPOINT=http://host.docker.internal:PORT \
amazon/aws-cli s3 lsNo credentials enter the container — only the endpoint URL.
Use --quiet to suppress stderr output. The URL is also written to the log file at ~/.local/state/sandy/logs/imds-broker/.
imds-broker mcp runs an MCP stdio server that exposes three tools: list_profiles, create_server, and stop_server. An agent calls create_server with a profile name, receives an endpoint URL, does its work with AWS_EC2_METADATA_SERVICE_ENDPOINT set to that URL, and calls stop_server when finished.
This lets an agent running in a sandboxed environment — where it has no shell access to AWS credentials and can't assume roles directly — still operate against AWS using whichever profiles the user has pre-approved.
Use imds-broker with a real sandbox boundary: Anthropic sandbox-runtime, a Docker container, or a VM. Run imds-broker mcp outside that boundary so it is the agent's only route to host AWS credentials.
Claude Code starts MCP servers from the host context. That is the point: the broker can read host AWS config, while the agent's tools cannot. The profile filter limits which host profiles Claude can request; it does not limit what those AWS profiles can do. Use least-privilege AWS profiles for agent workflows.
flowchart LR
classDef host fill:#FFF4E5,stroke:#F59E0B,color:#4A2E00
classDef sandbox fill:#E8F1FF,stroke:#3B82F6,color:#0B2A4A
classDef blocked fill:#FEE2E2,stroke:#EF4444,color:#4A0606
subgraph Host["Host"]
AWS["~/.aws + SSO cache"]:::host
BrokerCfg["imds-broker config"]:::host
Broker["imds-broker mcp"]:::host
end
subgraph Boundary["sandbox-runtime / Docker / VM"]
Agent["Claude tools"]:::sandbox
Workspace["Project workspace"]:::sandbox
end
AWS --> Broker
BrokerCfg --> Broker
Agent -->|MCP: list_profiles / create_server| Broker
Broker -->|IMDS endpoint URL| Agent
Agent -. denied .-> AWS:::blocked
Agent -. denied .-> BrokerCfg:::blocked
Recommended Claude Code settings excerpt:
{
"permissions": {
"disableBypassPermissionsMode": "disable",
"deny": [
"Read(~/.aws/**)",
"Read(~/.config/imds-broker/**)",
"Edit(~/.aws/**)",
"Edit(~/.config/imds-broker/**)"
]
},
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false
}
}Add any non-standard AWS_CONFIG_FILE, AWS_SHARED_CREDENTIALS_FILE, or XDG_CONFIG_HOME locations to the same deny list.
These Read and Edit denies block Claude's direct file tools. When sandboxing is enabled, Claude Code also merges them into the OS sandbox boundary for Bash, so subprocesses and child processes cannot read or modify those paths indirectly. That indirect block matters: without it, an allowed Bash command could run Python, Node, aws, or another program that opens credential files itself.
Do not add $HOME, ~/.aws, ~/.config, ~/.claude, or the broker config directory to sandbox filesystem allow-lists.
Configure broker defaults in ${XDG_CONFIG_HOME:-$HOME/.config}/imds-broker/config.yaml:
profile-filter: ".*ViewOnly.*"
region: "ap-southeast-2"
log-level: "info"Register the broker normally with Claude Code:
claude mcp add imds-broker -- imds-broker mcpRuntime inputs override file defaults when the broker launch environment is host-controlled:
imds-broker mcp --profile-filter "my-team-.*"
IMDS_BROKER_PROFILE_FILTER="my-team-.*" imds-broker mcpIf Claude can read AWS credentials, edit broker config, or influence the broker process environment, the filter is advisory.
Prints profiles matching the effective filter as a JSON array. Use it to check what the MCP server would expose to the agent:
imds-broker profiles [--profile-filter REGEX]Runs local diagnostics for the broker setup:
imds-broker doctordoctor reports the config path, whether a config file was found, the effective profile filter, the effective region and log level defaults, the number of discoverable local profiles, and the number matched by the filter.
doctor is read-only and does not call AWS APIs. It prints human-readable text for operators. It does not list matched profile names; use imds-broker profiles for JSON profile output.
doctor checks broker configuration, not container, VM, or sandbox-runtime policy. If the sandbox can read AWS credentials, edit broker config, or influence broker launch inputs, the filter is advisory.
- Reads credentials from your local AWS config files or an active SSO session on demand.
- Validates them via STS on first use.
- Wraps static IAM credentials with STS
GetSessionTokenso clients always receive short-lived, rotatable tokens. - Listens on an ephemeral port on all interfaces, but the listener is fail-closed: connections from anywhere outside loopback, the Docker bridge network, and your LAN are rejected before any HTTP parsing.
- Fully implements the IMDSv2 token + metadata flow, so any AWS SDK that supports EC2 instance credential resolution works, including older SDKs that pre-date newer credential providers.
- AWS credentials must already exist on the host. The broker reads from local AWS config or an active SSO session; it does not mint credentials from nothing.
- Ports are ephemeral. Each server binds to a random available port. Read it from stderr or the log file and pass it to your container or tool. There is no option to pin a fixed port yet.
- Default profile filter is restrictive. Configure
profile-filterin${XDG_CONFIG_HOME:-$HOME/.config}/imds-broker/config.yamlif your profile names do not match the built-in default. Use runtime filter overrides only from host-controlled launch configuration. - No persistent state. When the broker process exits, all running servers stop. Clients caching the endpoint will need to reconnect after a restart.
- Docker Desktop networking.
--network hostisn't supported on Docker Desktop; usehost.docker.internalinstead. The Linux Docker bridge is discovered automatically.
The IMDSv2 token design and error handling are derived from Ben Kehoe's imds-credential-server. Thanks to Ben for the original implementation and releasing it for others to discover.