Skip to content
Conduit

skill

cloud-bootstrap

A skill for Claude Code on the Web that bootstraps cloud provider credentials (GCP, AWS, Azure) and stores them encrypted in your repo.

ipeirotis/cloud-bootstrapcommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/ipeirotis/cloud-bootstrap itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 25, 2026

cloud-bootstrap

Version License: MIT Claude Skill Providers

Encrypted cloud credentials that survive Claude Code sessions — set up once, authenticate forever.

A skill for Claude Code on the Web that bootstraps cloud provider credentials (GCP, AWS, Azure) and stores them encrypted in your repo. One install, and every future session auto-authenticates in seconds.


Features at a Glance

  • Zero-touch session auth — SessionStart hook decrypts and authenticates automatically, every session
  • Multi-provider — GCP, AWS, and Azure in the same repo, each with independent credentials
  • Multi-team — Each team member has their own encrypted key and passphrase, no secrets shared
  • AES-256-CBC encryption — Credentials are encrypted with OpenSSL; only the passphrase stays outside the repo
  • Automatic key rotation warnings — Flags credentials older than 180 days
  • Minimal IAM footprint — Proposes least-privilege roles, never escalates on its own
  • 7 built-in workflows — Setup, onboarding, authentication, rotation, escalation, multi-provider, and uninstall
  • One-line install — curl | bash and you're done

How It Compares

ApproachPersistent across sessions?Works in Claude Code Web?No external service needed?Per-user encryption?
cloud-bootstrapYesYesYesYes
GCP Secret ManagerYesNeeds API access firstNoNo
AWS Secrets ManagerYesNeeds API access firstNoNo
HashiCorp VaultYesNo (requires server)NoConfigurable
.env filesNo (wiped each session)PartiallyYesNo
Paste token each sessionNoYesYesN/A

cloud-bootstrap solves the chicken-and-egg problem: you need credentials to access a secret manager, but you need the secret manager to store credentials.


The Problem

Claude Code on the Web has no persistent storage across sessions except the repo itself. If your project needs cloud access, you need a way to store credentials that:

  • Survives across sessions
  • Travels with the repo
  • Doesn't expose secrets in git history
  • Lets the agent authenticate without human intervention each session

How It Works

First user (one-time setup):

  1. You tell the agent which cloud provider and project to use
  2. The agent proposes minimum roles; you approve
  3. You generate a short-lived token locally and paste it in
  4. The agent creates a service account, encrypts the key with openssl, and commits the encrypted file
  5. In future sessions, the agent decrypts and authenticates automatically

Additional team members:

  1. A teammate opens the repo in Claude Code on the Web
  2. The agent sees the existing cloud config but no credentials for this user
  3. The teammate generates a short-lived token and pastes it in
  4. The agent creates a new key for the same service account, encrypted with the teammate's own passphrase
  5. No passwords are shared between team members

Each person's encrypted credentials are stored as .cloud-credentials.<git-email>.enc. The service account, roles, and project config are shared.

The only secret not in the repo is the encryption passphrase, which you set as an environment variable in Claude Code on the Web. You can use a provider-specific variable (GCP_CREDENTIALS_KEY, AWS_CREDENTIALS_KEY, AZURE_CREDENTIALS_KEY) or a universal one (CLOUD_CREDENTIALS_KEY). The skill checks the provider-specific variable first and falls back to the universal one.

This means if you work with multiple providers across different repos, you can use distinct passphrases per provider. If you only use one provider, just set CLOUD_CREDENTIALS_KEY and forget about it.

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Your Repository                          │
│                                                             │
│  .cloud-config.json          ← provider, project, roles    │
│  .cloud-credentials.*.enc   ← AES-256-CBC encrypted keys   │
│  .claude/hooks/cloud-auth.sh ← SessionStart auto-auth      │
│  .claude/skills/cloud-bootstrap/SKILL.md ← this skill      │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                    Each Session                              │
│                                                             │
│  1. SessionStart hook fires                                 │
│  2. Decrypts credentials using env var passphrase           │
│  3. Activates cloud CLI (gcloud/aws/az)                     │
│  4. Deletes plaintext key immediately                       │
│  5. Cloud access ready — no human intervention              │
└─────────────────────────────────────────────────────────────┘

Quick Start

# 1. Install the skill (from your repo root)
curl -sSL https://raw.githubusercontent.com/ipeirotis/cloud-bootstrap/main/install.sh | bash

# 2. Set your encryption passphrase in Claude Code on the Web
#    (Settings → Environment Variables)
#    CLOUD_CREDENTIALS_KEY = <your-secret-passphrase>

# 3. Tell Claude:
#    "Set up GCP credentials for project my-project-id"

# That's it. Every future session authenticates automatically.

Install

One-liner (from your repo root):

curl -sSL https://raw.githubusercontent.com/ipeirotis/cloud-bootstrap/main/install.sh | bash

Manual install (if you prefer to see each step):

# From the repo root
# Every file from one commit: main can move between downloads
# Resolved with git, not GitHub's REST API, whose unauthenticated quota is
# shared by everyone behind the same IP address
SHA=$(git ls-remote https://github.com/ipeirotis/cloud-bootstrap.git refs/heads/main | cut -f1)
printf '%s' "$SHA" | grep -qxE '[0-9a-f]{40}' || { echo "ERROR: could not resolve the current release commit."; exit 1; }
BASE=https://raw.githubusercontent.com/ipeirotis/cloud-bootstrap/$SHA
DEST=.claude/skills/cloud-bootstrap

# MANIFEST lists every distributed file (including scripts/discard-credential.sh,
# which the workflows call when a new credential has to be revoked)
# Download everything to a temp dir first; install only if every file arrived,
# so a failed download never leaves a mix of old and new files
STAGE=$(mktemp -d) && [ -n "$STAGE" ] && [ -d "$STAGE" ] \
  || { echo "ERROR: could not create a staging directory; nothing changed."; exit 1; }
trap 'rm -rf "$STAGE"' EXIT
# To a file first: a download cut off midway must fail here, not leave a
# shorter list that installs only part of the skill
curl -fsSL "$BASE/MANIFEST" -o "$STAGE/.manifest" \
  || { echo "ERROR: could not download MANIFEST; nothing changed."; exit 1; }
FILES=$(grep -v -e '^[[:space:]]*#' -e '^[[:space:]]*$' "$STAGE/.manifest"); rm -f "$STAGE/.manifest"
[ -n "$FILES" ] || { echo "ERROR: MANIFEST is empty; nothing changed."; exit 1; }
# Refuse any path outside the skill before creating a directory or file
for FILE in $FILES; do
  case "$FILE" in /*|*..*) echo "ERROR: MANIFEST lists an unsafe path ($FILE); nothing changed."; exit 1 ;; esac
done
for FILE in $FILES; do
  mkdir -p "$STAGE/$(dirname "$FILE")"
  curl -fsSL "$BASE/$FILE" -o "$STAGE/$FILE" || { echo "ERROR: could not download $FILE; nothing changed."; rm -rf "$STAGE"; exit 1; }
done
# As install.sh does: every file SKILL.md or a workflow names, and the revocation helper,
# must have arrived before the old copy is replaced
for REQ in VERSION SKILL.md scripts/discard-credential.sh \
           $(cat "$STAGE/SKILL.md" "$STAGE"/workflows/*.md 2>/dev/null \
             | grep -oE '(workflows|references|scripts)/[A-Za-z0-9_-]+\.(md|sh)' | sort -u); do
  [ -s "$STAGE/$REQ" ] || { echo "ERROR: the release lacks $REQ; nothing changed."; exit 1; }
done
# Record what was installed, as install.sh does, so update.sh can later remove
# files a newer release drops
printf '%s\n' $FILES > "$STAGE/.installed-files"
# Swap the complete staged copy in with renames (as update.sh does): a failure
# leaves the existing installation untouched, never a mix of two releases
mkdir -p "$(dirname "$DEST")"
NEW=$(mktemp -d "$(dirname "$DEST")/.cloud-bootstrap.new.XXXXXX") \
  || { echo "ERROR: could not create a directory next to $DEST; nothing changed."; exit 1; }
OLD_DIR="$(dirname "$DEST")/.cloud-bootstrap.old.$$"
trap 'rm -rf "$STAGE" "$NEW"; if [ -d "$OLD_DIR" ] && [ ! -e "$DEST" ]; then mv "$OLD_DIR" "$DEST"; fi' EXIT
cp -R "$STAGE"/. "$NEW"/ && chmod 755 "$NEW" \
  || { echo "ERROR: could not prepare the new copy; nothing changed."; exit 1; }
if [ -e "$DEST" ]; then mv "$DEST" "$OLD_DIR" || { echo "ERROR: could not move the old copy aside; nothing changed."; exit 1; }; fi
mv "$NEW" "$DEST" || { echo "ERROR: could not move the new copy into place; the old one is restored."; exit 1; }
rm -rf "$OLD_DIR"

git add "$DEST"
git commit -m "Add cloud-bootstrap skill" -- "$DEST"   # only the skill, not anything else already staged

Or just tell Claude Code on the Web:

"Clone the cloud-bootstrap skill from https://github.com/ipeirotis/cloud-bootstrap into .claude/skills/cloud-bootstrap/ in this repo and commit it."

Updating

Check which version you have and whether a newer one is available:

curl -sSL https://raw.githubusercontent.com/ipeirotis/cloud-bootstrap/main/update.sh | bash

This will:

  1. Show your installed version and the latest version
  2. Display the changelog entries you'd be getting
  3. Ask for confirmation on your terminal before updating (where no terminal is available, run ... | bash -s -- --yes)

You can also check your installed version at any time: cat .claude/skills/cloud-bootstrap/VERSION.

Prerequisites

  • Encryption passphrase set as an environment variable in Claude Code on the Web: either GCP_CREDENTIALS_KEY / AWS_CREDENTIALS_KEY / AZURE_CREDENTIALS_KEY, or the universal CLOUD_CREDENTIALS_KEY
  • A cloud account with permission to create service accounts and assign roles
  • openssl available in the environment (standard on CCoW)

Supported Providers

ProviderBootstrap CommandWhat Gets CreatedTeam Limit
GCPgcloud auth print-access-tokenService account + JSON key per user~10 (keys per SA)
AWSaws sts get-session-tokenIAM group + IAM user per team memberBounded by the account's IAM user quota
Azureaz account get-access-tokenService principal + client secret per userBounded: secrets share the app manifest's credential limit, and rotated or departed members' secrets count until removed

Files Created in Your Repo

FilePurposeCommitted?
.cloud-credentials.<email>.encEncrypted key, one per team memberYes
.cloud-config.jsonProvider, project ID, roles (shared)Yes
.claude/hooks/cloud-auth.shSessionStart hook: installs CLI + authenticatesYes
.claude/settings.jsonHook configurationYes
CLAUDE.md (Cloud Credentials section)Human/agent-readable auth docsYes
credentials.jsonNever (plaintext key).gitignored

Multi-provider repos: When using multiple providers, credential files are named .cloud-credentials.<provider>.<email>.enc and .cloud-config.json uses a providers array format. See SKILL.md for details.

Security Model

  • Each team member's credentials are encrypted with their own passphrase (never shared)
  • Credentials are encrypted with AES-256-CBC (via openssl)
  • Plaintext keys exist only momentarily during setup and authentication, and are deleted immediately
  • The agent cannot escalate its own permissions; it must ask you
  • Bootstrap tokens expire in ~1 hour
  • Encryption passphrases never enter the repo
  • Revoking a team member's access: delete their .cloud-credentials.<email>.enc file and remove their key/user from the cloud provider

Troubleshooting

SymptomLikely CauseFix
"No credentials key found"Passphrase env var not setSet CLOUD_CREDENTIALS_KEY in Claude Code Web settings
Decryption fails silentlyWrong passphrase or corrupted .enc fileRe-run setup with the correct passphrase
403 after authenticationService account missing a roleAsk your admin to grant the needed role (skill will suggest which one)
CLI not found after hook runsInstall failed or PATH issueRe-open session; hook retries install on next start
Credential age warningKey older than 180 daysRun credential rotation workflow

License

MIT

↑↓ navigate openesc close