Close Your Laptop: Claude Code on Bedrock AgentCore
Overview
If you have ever set an AI coding agent like Claude Code loose on a large code modification task, you know the anxiety. The task will run for 45 minutes, recursive calls will compile code, search tests, and fix bugs. You are locked to your machine. If your Wi-Fi drops for a second, your laptop battery dies, or you close your laptop to go to lunch, the terminal session breaks, the shell process is killed, and the entire agent context is lost.
For agents to become true team members rather than simple command-line utilities, we need to shift our paradigm from "local developer assistants" to "cloud-hosted coworkers."
This post shows how to build a fully secure, persistent, and asynchronous AI coding environment in the cloud. By deploying the Amazon Bedrock AgentCore Runtime (which runs in isolated Firecracker microVMs) using SST v4, you can dispatch long-running engineering tasks, close your laptop, and let the agent work.
1. The Persistent Shell Sandbox
To build a persistent cloud-hosted coding assistant, we need to leverage the security and persistence features of AWS Bedrock AgentCore Runtime:
flowchart LR
subgraph "Bedrock AgentCore Sandbox (Firecracker)"
CC[Claude Code Binary] <-->|Local Host| Contract[HTTP main.js]
Workspace[Session Storage /mnt/workspace] <--> CC
end
Developer[Developer Machine] <-->|SigV4 agentcore exec| AgentCoreService[Bedrock AgentCore Service]
AgentCoreService <-->|PTY WebSocket| CC
CC <-->|Model API calls| Bedrock[Amazon Bedrock Claude Sonnet 4.6]
1.1 Amazon Bedrock AgentCore Runtime
Amazon Bedrock AgentCore provides the underlying host environment. Each session runs in an isolated Firecracker microVM, ensuring a zero-trust, secure sandbox.
- Persistent Session Storage: By mounting Managed Session Storage at
/mnt/workspace, files, git history, and runtime environments persist across session pauses and restarts. - WebSocket TTY Terminal: The
bedrock-agentcore:InvokeAgentRuntimeCommandWithWebSocketStreamaction exposes a bidirectional WebSocket connection. It allows developers to attach a PTY shell directly into the running AgentCore session, providing a true SSH-like debugging experience. - Concurrency & Expire Rules: Each AgentCore runtime supports up to 10 concurrent active shell sessions. During its Preview phase, Managed Session Storage has a 14-day idle expiry policy, and data is reset when the runtime version updates.
2. The Asynchronous Collaboration Model
Moving the agent to the cloud changes the developer user experience. Instead of sitting and watching terminal output, you adopt an asynchronous, event-driven workflow:
flowchart TD
Human[Developer] -->|1. Start Long-running Task| CC[Claude Code inside Shell]
Human -->|2. Disconnect/Close Laptop| Idle[Laptop Offline]
CC -->|3. Runs Asynchronously in Sandbox| Workspace[Persistent Workspace]
Human -->|4. Reconnect later| Exec[exec.sh with session-id]
Exec -->|5. Hot-plug PTY WebSocket| CC
Human -->|6. Review Output / Commit| CC
- Dispatch: You assign a bug or a task to the agent.
- Disconnect: You close your laptop. The container continues to run inside AWS.
- Hot-Plug Debugging: If the agent runs into a blocker—a compile error it can't resolve or an ambiguous requirement—you can attach a WebSocket terminal, resolve the blocker manually, detach from the session, and close your laptop again. The agent picks up exactly where it left off.
3. The Pivot: From CDK to SST v4
During the initial design phase, the project utilized the @aws/agentcore-cdk construct and the @aws/agentcore CLI. However, this approach introduced several limitations that led to an infrastructure pivot:
3.1 Over-Privileged Default Roles
The CDK L3 construct automatically generated IAM execution roles with broad, wildcard model access (foundation-model/* and inference-profile/*). Because CloudFormation custom resources wrapped the role compilation, there was no clean way to narrow down the permissions.
By pivoting to SST v4 using naked Pulumi providers (aws and aws-native), we built the infrastructure entirely in TypeScript. This allows us to define a custom least-privilege execution role scoped strictly to the cross-region inference profile (us.anthropic.claude-sonnet-4-6) and the specific models required.
3.2 Simplified Build Pipeline
Rather than a local Docker asset build, the @aws/agentcore-cdk L3 construct wired up a remote build pipeline: a CodeBuild::Project, a Lambda::Function, and a custom resource to compile the container image. This isn't a CDK limitation — plain CDK's DockerImageAsset builds locally too — but it's what the construct's abstraction gave us.
Pivoting to SST v4 let us use Pulumi's native Docker-build provider directly, with the same local-build-then-push shape as a plain CDK asset, minus the L3 construct's three extra remote-build layers.
4. The Node.js 24 Runtime
The runtime container is built on Node.js 24, aligned with the project's Node standards. Building it surfaced three engineering lessons worth knowing before you write your own contract server:
4.1 Claude Code is a Self-Contained Native Binary
While Claude Code is distributed via npm, the npm package is merely a wrapper. The actual tool is installed via a native installer script:
1curl -fsSL https://claude.ai/install.sh | bash
It bundles its own runtime and ripgrep binary, meaning that Claude Code does not depend on the container's Node.js environment to run.
4.2 Minimal HTTP Contract Server
For AgentCore to identify the container as healthy, it expects a web server on port 8080 responding to:
GET /ping(Liveness checks)POST /invocations(Invocation handler)
We implemented this as a zero-dependency CommonJS server in main.js using Node's native http module.
4.3 Node ADOT Observability Gotchas
Node's AWS Distro for OpenTelemetry (ADOT) has two behaviors worth knowing upfront:
- No Binary Wrapper: ADOT has no standalone CLI command to launch your app under. It's loaded via the
--requireflag instead:1node --require @aws/aws-distro-opentelemetry-node-autoinstrumentation/register main.js - CommonJS Requirement: ADOT patches Node's
require()hooks at startup. It does not support ESMimportsyntax out-of-the-box. If the project uses ESM, ADOT will silently fail to send traces. Therefore, the contract server must use CommonJS syntax (require), andpackage.jsonmust not contain"type": "module".
5. Login Shell Environment Persistence
A critical pitfall in the container configuration relates to how Bedrock handles interactive shells.
The environment variables specified in the Dockerfile ENV block or the AgentCore Runtime configuration only reach processes spawned under PID 1 (such as the contract server). However, when a developer runs agentcore exec --it, Bedrock spawns a fresh login shell. This shell does not inherit environment variables from PID 1.
Without these variables (CLAUDE_CODE_USE_BEDROCK=1 and ANTHROPIC_MODEL), Claude Code will default to the Anthropic API instead of Bedrock, causing it to fail with "Not logged in" errors.
To solve this, write the Bedrock configuration directly to /etc/profile.d/:
1RUN printf '%s\n' \
2 'export AWS_REGION=us-east-1' \
3 'export CLAUDE_CODE_USE_BEDROCK=1' \
4 'export ANTHROPIC_MODEL=us.anthropic.claude-sonnet-4-6' \
5 'export PATH="/home/bedrock_agentcore/.local/bin:$PATH"' \
6 > /etc/profile.d/claude-bedrock.sh \
7 && chmod 0644 /etc/profile.d/claude-bedrock.sh
This forces every login shell to load the necessary environment variables and add Claude Code to the PATH.
6. Code Implementation
Let's look at the key implementation files from the PoC repository.
6.1 sst.config.ts
The entrypoint imports our infrastructure resources:
1/// <reference path="./.sst/platform/config.d.ts" />
2
3export default $config({
4 app(input) {
5 return {
6 name: "agentcore-claude-shell-poc",
7 removal: input?.stage === "prod" ? "retain" : "remove",
8 protect: input?.stage === "prod",
9 home: "aws",
10 providers: {
11 aws: {
12 region: "us-east-1",
13 defaultTags: {
14 tags: {
15 Project: "agentcore-claude-shell-poc",
16 Stage: input?.stage ?? "dev",
17 ManagedBy: "sst",
18 },
19 },
20 },
21 "aws-native": {
22 version: "1.69.0",
23 region: "us-east-1",
24 },
25 },
26 };
27 },
28 async run() {
29 const runtime = await import("./infra/runtime");
30 return {
31 runtimeArn: runtime.runtimeArn,
32 runtimeId: runtime.runtimeId,
33 };
34 },
35});
6.2 infra/image.ts
Pulumi's @pulumi/docker-build provider builds the runtime image locally and pushes it to ECR. The imageUri export is pinned by digest, so the runtime resource always references the exact pushed image rather than a mutable :latest tag:
1import * as path from "node:path";
2import * as dockerBuild from "@pulumi/docker-build";
3
4// `aws` is an SST global — declared in .sst/platform/config.d.ts. No import needed.
5
6// The Pulumi program runs from .sst/platform, so docker-build resolves relative paths from
7// there. Anchor on the absolute project root via $cli.paths.root.
8const contextDir = path.join($cli.paths.root, "app", "claude_shell_poc");
9
10export const repository = new aws.ecr.Repository("ClaudeShellRepo", {
11 name: "jeffclaudeshellpoc/claudeshellpoc",
12 imageTagMutability: "MUTABLE",
13 forceDelete: true,
14 imageScanningConfiguration: { scanOnPush: true },
15});
16
17const authToken = aws.ecr.getAuthorizationTokenOutput({
18 registryId: repository.registryId,
19});
20
21export const image = new dockerBuild.Image("ClaudeShellImage", {
22 context: { location: contextDir },
23 dockerfile: { location: `${contextDir}/Dockerfile` },
24 platforms: ["linux/arm64"],
25 tags: [$interpolate`${repository.repositoryUrl}:latest`],
26 push: true,
27 registries: [
28 {
29 address: repository.repositoryUrl,
30 username: authToken.userName,
31 password: authToken.password,
32 },
33 ],
34});
35
36// Pinned-by-digest URI — runtime always references the exact pushed image.
37export const imageUri = $interpolate`${repository.repositoryUrl}@${image.digest}`;
A few details worth highlighting:
- No
@pulumi/awsimport:awsis exposed as an SST global. The same pattern applies toawsnativelater. This eliminates the typical Pulumi boilerplate. linux/arm64: AgentCore happily runs ARM containers, and the build host (an EC2 Graviton instance in this case) is also ARM. No cross-arch QEMU emulation needed.- Digest pinning (
${repositoryUrl}@${image.digest}) ensures Pulumi correctly detects image changes and triggers a runtime update.
6.3 infra/iam.ts
The self-built runtime execution role restricts Bedrock invocation strictly to Claude Sonnet 4.6 — no foundation-model/* wildcard. The role also carries the baseline grants AgentCore needs (ECR pull, CloudWatch Logs, X-Ray) so we can drop the L3 construct entirely. Showing the headline statements:
1import { repository } from "./image";
2
3// `aws` is an SST global — no import needed.
4
5const region = "us-east-1";
6const accountId = aws.getCallerIdentityOutput({}).accountId;
7const inferenceProfile = "us.anthropic.claude-sonnet-4-6";
8
9export const role = new aws.iam.Role("ClaudeShellRuntimeRole", {
10 assumeRolePolicy: JSON.stringify({
11 Version: "2012-10-17",
12 Statement: [
13 {
14 Effect: "Allow",
15 Principal: { Service: "bedrock-agentcore.amazonaws.com" },
16 Action: "sts:AssumeRole",
17 },
18 ],
19 }),
20 description: "Least-privilege execution role for the Claude shell AgentCore runtime",
21});
22
23new aws.iam.RolePolicy("ClaudeShellRuntimePolicy", {
24 role: role.id,
25 policy: $resolve([accountId, repository.arn]).apply(([acct, repoArn]) =>
26 JSON.stringify({
27 Version: "2012-10-17",
28 Statement: [
29 {
30 Sid: "BedrockSonnetOnly",
31 Effect: "Allow",
32 Action: ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
33 Resource: [
34 `arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-4-6*`,
35 `arn:aws:bedrock:${region}:${acct}:inference-profile/${inferenceProfile}`,
36 ],
37 },
38 {
39 Sid: "BedrockProfileMetadata",
40 Effect: "Allow",
41 Action: ["bedrock:GetInferenceProfile"],
42 Resource: `arn:aws:bedrock:${region}:${acct}:inference-profile/${inferenceProfile}`,
43 },
44 {
45 Sid: "BedrockMarketplace",
46 Effect: "Allow",
47 Action: ["aws-marketplace:ViewSubscriptions", "aws-marketplace:Subscribe"],
48 Resource: "*",
49 Condition: { StringEquals: { "aws:CalledViaLast": "bedrock.amazonaws.com" } },
50 },
51 // Plus EcrAuth, EcrPull, LogsDescribe, LogsWrite, Xray, BedrockListProfiles
52 // — see infra/iam.ts in the repo for the full nine-statement policy.
53 ],
54 }),
55 ),
56});
57
58export const executionRoleArn = role.arn;
Two patterns worth calling out:
aws.getCallerIdentityOutput({}).accountIdreturns a PulumiOutput<string>. Composing it into resource ARNs uses$resolve(...).apply(...)rather than awaiting a Promise — the latter would JSON-stringify a[object Promise]literal into the policy.- The marketplace statement with
aws:CalledViaLastis the same pattern as Claude Code's official Bedrock docs sample. It scopes marketplace subscription actions to calls that originated from Bedrock — narrower than a bare wildcard.
6.4 infra/runtime.ts
The runtime resource itself uses Pulumi's aws-native provider, pointing at the ECR image and the self-built role:
1import { imageUri } from "./image";
2import { executionRoleArn } from "./iam";
3
4// `awsnative` and `aws` are SST globals.
5
6export const runtime = new awsnative.bedrockagentcore.Runtime("ClaudeShellRuntime", {
7 agentRuntimeName: "JeffClaudeShellPoc_ClaudeShellPoc",
8 description: "Container runtime with Claude Code preinstalled for interactive shell validation.",
9 roleArn: executionRoleArn,
10 agentRuntimeArtifact: {
11 containerConfiguration: { containerUri: imageUri },
12 },
13 networkConfiguration: {
14 networkMode: awsnative.bedrockagentcore.RuntimeNetworkMode.Public,
15 },
16 protocolConfiguration: awsnative.bedrockagentcore.RuntimeProtocolConfiguration.Http,
17 environmentVariables: {
18 AWS_REGION: "us-east-1",
19 CLAUDE_CODE_USE_BEDROCK: "1",
20 ANTHROPIC_MODEL: "us.anthropic.claude-sonnet-4-6",
21 },
22 filesystemConfigurations: [
23 { sessionStorage: { mountPath: "/mnt/workspace" } },
24 ],
25});
26
27export const runtimeArn = runtime.agentRuntimeArn;
28export const runtimeId = runtime.agentRuntimeId;
6.5 app/claude_shell_poc/Dockerfile
The Node 24 runtime image preinstalls Claude Code using the native installer and configures ADOT:
1FROM public.ecr.aws/docker/library/node:24-bookworm-slim
2
3# Shell tooling for the interactive demo. No nodejs/npm here — the base image
4# provides Node 24, and Claude Code installs as a self-contained native binary.
5RUN apt-get update && apt-get install -y --no-install-recommends \
6 bash \
7 ca-certificates \
8 curl \
9 git \
10 jq \
11 less \
12 procps \
13 && rm -rf /var/lib/apt/lists/*
14
15# The node:24 base already ships a `node` user at UID/GID 1000; remove it so we
16# can keep bedrock_agentcore at the conventional UID 1000.
17RUN userdel -r node 2>/dev/null || true; \
18 groupadd -g 1000 bedrock_agentcore && \
19 useradd -m -u 1000 -g 1000 bedrock_agentcore && \
20 mkdir -p /mnt/workspace
21
22WORKDIR /app
23
24ENV NODE_ENV=production \
25 DOCKER_CONTAINER=1 \
26 HOME=/home/bedrock_agentcore \
27 AWS_REGION=us-east-1 \
28 CLAUDE_CODE_USE_BEDROCK=1 \
29 ANTHROPIC_MODEL=us.anthropic.claude-sonnet-4-6 \
30 PATH="/home/bedrock_agentcore/.local/bin:/app/node_modules/.bin:$PATH"
31
32# Docker ENV only reaches the CMD process (PID 1). `agentcore exec --it` spawns
33# a fresh login shell that does NOT inherit PID 1's env, so without this it falls
34# back to the Anthropic API and fails with "Not logged in". Mirror the Bedrock
35# settings into /etc/profile.d so every interactive/login shell picks them up,
36# and make sure the user's local bin (claude) is on PATH there too.
37RUN printf '%s\n' \
38 'export AWS_REGION=us-east-1' \
39 'export CLAUDE_CODE_USE_BEDROCK=1' \
40 'export ANTHROPIC_MODEL=us.anthropic.claude-sonnet-4-6' \
41 'export PATH="/home/bedrock_agentcore/.local/bin:$PATH"' \
42 > /etc/profile.d/claude-bedrock.sh \
43 && chmod 0644 /etc/profile.d/claude-bedrock.sh
44
45# Install the Node ADOT auto-instrumentation (provides `opentelemetry-instrument`).
46COPY package.json ./
47RUN npm install --omit=dev --no-audit --no-fund
48
49# Install Claude Code as a self-contained native binary (official native installer).
50# It does NOT depend on the image's Node; it bundles its own runtime + ripgrep.
51USER bedrock_agentcore
52RUN curl -fsSL https://claude.ai/install.sh | bash \
53 && /home/bedrock_agentcore/.local/bin/claude --version
54USER root
55
56COPY --chown=bedrock_agentcore:bedrock_agentcore . .
57RUN chown -R bedrock_agentcore:bedrock_agentcore /app /mnt/workspace /home/bedrock_agentcore
58
59USER bedrock_agentcore
60
61EXPOSE 8080 8000 9000
62
63# Node ADOT has no `opentelemetry-instrument` binary (that's the Python distro);
64# it ships a `--require`-able register hook (package "./register" export). Load it
65# via --require so auto-instrumentation patches require() before main.js runs.
66CMD ["node", "--require", "@aws/aws-distro-opentelemetry-node-autoinstrumentation/register", "main.js"]
6.6 scripts/exec.sh
The connection tool reads the runtime ARN from the SST output file (.sst/outputs.json) and establishes a pinned session:
1#!/usr/bin/env bash
2set -euo pipefail
3
4ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5cd "$ROOT"
6
7REGION="${AGENTCORE_REGION:-us-east-1}"
8
9# Session pinning. By default we reuse ONE fixed session so repeated `exec`
10# attach to the same microVM (warm, same /mnt/workspace) until it idles out
11# (idleRuntimeSessionTimeout) or hits maxLifetime. Override to start a fresh
12# session via either the SESSION_ID env var or the first positional arg.
13# ./scripts/exec.sh # reuse default fixed session
14# ./scripts/exec.sh my-other-session-id # use a different (new) session
15# SESSION_ID=... ./scripts/exec.sh # same, via env
16# AgentCore requires the session id to be 33-256 characters.
17DEFAULT_SESSION_ID="agentcore-claude-shell-poc-default-session"
18SESSION_ID="${1:-${SESSION_ID:-$DEFAULT_SESSION_ID}}"
19
20if (( ${#SESSION_ID} < 33 || ${#SESSION_ID} > 256 )); then
21 echo "Session id must be 33-256 characters (got ${#SESSION_ID}): '$SESSION_ID'" >&2
22 exit 1
23fi
24
25# Jq is required to read the SST outputs unless RUNTIME_ARN is supplied directly.
26if [[ -z "${RUNTIME_ARN:-}" ]] && ! command -v jq >/dev/null 2>&1; then
27 echo "exec.sh needs 'jq' to read .sst/outputs.json (install: apt-get install jq / brew install jq), or set RUNTIME_ARN explicitly." >&2
28 exit 1
29fi
30
31# Resolve the runtime ARN. Precedence:
32# 1. Explicit RUNTIME_ARN env override
33# 2. SST deploy outputs (.sst/outputs.json — written by `sst deploy`)
34RUNTIME_ARN="${RUNTIME_ARN:-}"
35if [[ -z "$RUNTIME_ARN" && -f "$ROOT/.sst/outputs.json" ]]; then
36 RUNTIME_ARN="$(jq -r '.runtimeArn // empty' "$ROOT/.sst/outputs.json" 2>/dev/null || true)"
37fi
38
39if [[ -z "$RUNTIME_ARN" || "$RUNTIME_ARN" == "null" ]]; then
40 echo "Runtime ARN not found. Run 'npm run deploy' first, or set RUNTIME_ARN explicitly." >&2
41 exit 1
42fi
43
44echo "Attaching to session: $SESSION_ID" >&2
45
46# Interactive, SigV4-authenticated shell into the runtime. This is an operator
47# tool, not IaC — the runtime itself is managed by SST (infra/runtime.ts).
48exec npx @aws/agentcore@0.19.0 exec --it \
49 --runtime "$RUNTIME_ARN" --region "$REGION" --session-id "$SESSION_ID"
7. Operational Lessons from Running the PoC
A handful of operational details only become visible after you actually deploy and shell into the runtime. None of them are blockers, but each one will surprise you the first time you hit it:
7.1 Three Distinct "Sessions" — Don't Confuse Them
AgentCore exposes three layers of session that each have their own lifecycle:
- Agent runtime — the deployed container artifact (the resource declared in
infra/runtime.ts). Lives until yousst remove. - Runtime session — keyed by
--session-idon theagentcore execcall. Holds the persistent filesystem state (/mnt/workspace,/tmp, etc.). Survives shell disconnects. Expires after 14 days of inactivity. - Shell session — the WebSocket TTY connection itself. Has its own 10-second confirmation timeout and 1-of-5 reconnect policy. Disconnecting and reattaching with the same
--session-idreconnects to the same runtime session, but it spawns a new shell session — you'll see a[new shell session (previous session expired)]notice on reconnect.
Concretely: a file you wrote to /tmp/probe.txt in one shell remains visible in a new shell session only if you reconnect with the same --session-id. Switching --session-id gives you a fresh runtime session with an empty filesystem. The exec.sh script in §6.6 hardcodes a DEFAULT_SESSION_ID, which is exactly what you want for solo use — every reconnect lands on the same warm workspace.
7.2 One-Shot exec Has an Argument-Splitting Quirk
The agentcore exec command nominally supports a one-shot mode (agentcore exec --runtime ... -- claude --print "explain this codebase"). In practice, the multi-word prompt argument gets split into separate argv entries somewhere in the CLI plumbing, and Claude Code receives only the first word. The production-friendly path is to stay inside an interactive shell — run agentcore exec --it, then invoke Claude (or any other multi-arg command) inside the shell where standard quoting works.
7.3 Watch Out for INIT_CWD Inheritance
If you run the PoC's deploy commands from a host shell that inherits INIT_CWD from a parent npm/pnpm process (e.g. a wrapper script that itself was launched by npm run), the agentcore CLI's project-root detection can mis-resolve and report No agentcore project found. The SST infra/agentcore.ts of the original prototype worked around this by setting INIT_CWD explicitly before invoking the deploy script. The current SST-native build sidesteps the CLI entirely, so this footgun mostly applies if you mix SST deploy with manual agentcore CLI invocations.
8. Cost Model and Closing Thoughts
Running Claude Code on Bedrock AgentCore introduces three cost surfaces, all of them modest at PoC scale:
- AgentCore runtime compute is billed only while a runtime session is active. The 14-day idle-expiry policy means a session that no one touches for two weeks gets reclaimed automatically — you don't pay rent on forgotten experiments.
- Bedrock model tokens are billed per
bedrock:InvokeModelcall. Pinning to a single inference profile (us.anthropic.claude-sonnet-4-6) makes the bill auditable: every Bedrock charge on the account is attributable to this PoC. - ECR storage is the trivial line item. Setting
forceDelete: trueon the repository (see §6.2) meanssst removeactually deletes the repo and all images. Without it, ECR keeps every image you ever pushed and you'll find old PoC artifacts on the bill long after you forgot the project.
Even at active development pace, these charges are negligible compared to developer downtime. When running a complex task locally, a developer is pinned to their terminal — unable to close the laptop, change networks, or switch contexts. Moving execution to a persistent AgentCore container with a self-built least-privilege role lets developers dispatch long-running engineering tasks, shut the laptop, and retrieve results asynchronously. The end-to-end picture is roughly four moving parts: SST v4 + Pulumi as the IaC, AgentCore Runtime as the secure host, Claude Code on Bedrock as the model layer, and agentcore exec --it as the operator entry point. Everything else is plumbing.
Related posts:
- Agent Toolkit for AWS: What It Changes for Claude Code — the Bedrock AgentCore tool-calling layer that pairs with this runtime setup.
- Build on AWS Faster with Claude Code and AWS Skills — the skills workflow this AgentCore runtime is built to run unattended.
- Secure AWS Credentials with credential_process — how the runtime's Bedrock calls get scoped, least-privilege AWS credentials.
- Claude Code Cost Per Project on AWS — attributing the Bedrock token spend this runtime generates back to a project.