From envisible
Set up envisible to manage encrypted secrets in a project's config and env files. Use when the user wants to encrypt secrets in a repo, mentions envisible, wants to replace plaintext credentials in `.env` / yaml / json / toml with `ENC[...]` markers, or needs to migrate an existing project to encrypted-at-rest secrets.
How this skill is triggered — by the user, by Claude, or both
Slash command
/envisible:envisibleThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are integrating [envisible](https://github.com/rubysolo/envisible) into a project. Envisible encrypts inline `ENC[...]` markers in any text file (`.env`, yaml, json, toml, ini, dotenv-style) and decrypts them at runtime — either by injecting into the environment for a child process (`envisible run -- <cmd>`) or by emitting plaintext for code/build pipelines (`envisible decrypt`).
You are integrating envisible into a project. Envisible encrypts inline ENC[...] markers in any text file (.env, yaml, json, toml, ini, dotenv-style) and decrypts them at runtime — either by injecting into the environment for a child process (envisible run -- <cmd>) or by emitting plaintext for code/build pipelines (envisible decrypt).
This skill walks the agent through a clean setup. Do not skip discovery — half the work is finding the secrets that are already there.
ENC[plaintext] marks a value to encrypt. After encryption, it becomes ENC[v1:base64...] (local NaCl keypair) or ENC[v2:base64...] (cloud KMS envelope). Surrounding text is preserved, so partial-value encryption works: postgres://user:ENC[v1:...]@host/db.envisible.pub (commit) + envisible.key (NEVER commit). Anyone with the private key can decrypt. Zero infra, no network at runtime.envisible.pub carries both the public key and a resource pointer. The private half stays in KMS. Decrypt-time calls Decrypt via the cloud SDK's default credential chain. Commit envisible.pub.Before changing anything, run these in parallel:
which envisible, envisible version, and envisible kms --help.kms is unavailable, upgrade to a KMS-capable release before choosing KMS mode.go install github.com/rubysolo/envisible@latest (needs Go)brew tap rubysolo/tools && brew install envisible (macOS/Linux)Find candidate files for secrets:
# env-style files
find . -name '.env*' -not -path './node_modules/*' -not -path './.git/*'
# configs that often hold credentials
find . \( -name '*.yaml' -o -name '*.yml' -o -name '*.json' -o -name '*.toml' \) \
-not -path './node_modules/*' -not -path './.git/*' -not -path './vendor/*'
Scan for plaintext-looking secrets — grep -RInE '(password|secret|token|api[_-]?key|aws_|private[_-]?key)\s*[:=]' --include='*.{env,yaml,yml,json,toml,ini}' . — and surface them to the user for triage before you encrypt anything.
Check .gitignore for envisible.key, *.key, .env. Note what's already protected.
Read package.json / Makefile / Procfile / docker-compose.yml / CI workflow files — these are where the runtime command lives, and they're what you'll wrap with envisible run.
Report findings to the user with a short table: which files have secrets, which are gitignored, what's already encrypted (look for ENC[v1: or ENC[v2:). Do not auto-encrypt without confirmation.
Ask the user, framing the tradeoff:
envisible.key to each developer and to production.If they're unsure, recommend local for now — migration to KMS later is mechanical (decrypt → kms init → encrypt again).
envisible keygen # writes envisible.pub and envisible.key
Then:
envisible.key to .gitignore if not already covered. If *.key is already ignored, you're good — but verify with git check-ignore -v envisible.key.envisible.pub — it's the encryption key and is safe to share.envisible.key — out-of-band channel (password manager, 1Password vault, secure file share). Never paste into chat / PR / ticketing.Pick the provider the user already authenticates with. The three flows differ only in the resource string.
Before proceeding, re-check the installed binary supports KMS commands:
envisible version
envisible kms --help
If kms is unavailable, stop and upgrade envisible first.
GCP:
gcloud kms keyrings create my-app --location us
gcloud kms keys create my-key \
--keyring my-app --location us \
--purpose asymmetric-encryption \
--default-algorithm rsa-decrypt-oaep-2048-sha256
# Important: envisible uses Application Default Credentials (ADC),
# not only gcloud CLI login state.
gcloud auth application-default login
envisible kms init --provider gcp \
--resource projects/PROJECT/locations/us/keyRings/my-app/cryptoKeys/my-key/cryptoKeyVersions/1
Important for GCP: envisible kms init and KMS-backed decrypts use ADC. gcloud auth login
alone is often not enough for local runs.
AWS:
aws kms create-key --key-spec RSA_2048 --key-usage ENCRYPT_DECRYPT \
--description "envisible for <project>"
# capture KeyId from the JSON
aws kms create-alias --alias-name alias/my-app --target-key-id <KEY_ID>
envisible kms init --provider aws \
--resource arn:aws:kms:us-east-1:ACCOUNT:key/<KEY_ID>
Azure:
az keyvault key create --vault-name myvault --name my-key --kty RSA --size 2048
envisible kms init --provider azure \
--resource https://myvault.vault.azure.net/keys/my-key/<VERSION>
If the user already has a key provisioned (Terraform, console), skip the create step and run envisible kms init straight against the existing resource.
If the project already manages cloud infrastructure with Terraform/Terragrunt, prefer Terraform
to create and IAM-bind the KMS key and use envisible kms init only to materialize envisible.pub.
Avoid mixing envisible kms create with Terraform-managed infrastructure long-term.
Then:
envisible.pub — required at decrypt time in v2 mode (it carries the KMS pointer). This is non-negotiable; the file is safe.envisible.pub into the image/repo.envisible.key or ENVISIBLE_KEY.--pub (not --key) and the target env file, for example:
envisible run --pub /app/envisible.pub -f /app/.env -- ./myapproles/cloudkms.cryptoKeyDecrypter on the crypto keykms:Decrypt (and kms:GetPublicKey for kms init)keys/decrypt (and keys/get for init)envisible.pub may reference a specific cryptoKeyVersion, but IAM bindings are typically granted on the parent cryptoKey resource.envisible.key to .gitignore for any special reason — it shouldn't exist in KMS mode. If it does, delete it.For each plaintext secret the user confirmed in Step 1:
Wrap it in ENC[...] with the plaintext inside. Partial-value encryption is fine:
# before
database:
url: postgres://user:[email protected]:5432/app
# after, before encryption
database:
url: postgres://user:ENC[hunter2]@db.internal:5432/app
Encrypt in place:
envisible encrypt -i path/to/file.yaml
envisible encrypt -i .env
Verify — grep the file for ENC[..] markers and confirm everything is encrypted; the file should no longer contain plaintext. Also run envisible check path/to/file — it exits non-zero if any ENC[...] marker is still unencrypted (i.e. doesn't start with v1: or v2:).
Test round-trip:
envisible decrypt --strip path/to/file.yaml | diff - <original-plaintext-version>
(or just spot-check envisible decrypt path/to/file.yaml | head).
Integrate with existing lint/static analysis:
If the project already has a lint or static analysis command (e.g. make lint, npm run lint), wire envisible check into that command so secret checks always run with the rest of your code quality gates. This ensures
unencrypted secrets are caught early and consistently. If there isn't an existing lint step, consider adding a new
one that runs envisible check against all candidate files.
This is project-specific. Common patterns:
.env)For apps that read env vars at startup, prefix the plaintext launch command with envisible run -e <envfile> --:
# before
node server.js
# after
envisible run -e .env -- node server.js
In package.json:
{
"scripts": {
"start": "envisible run -e .env -- node server.js",
"dev": "envisible run -e .env.development -- nodemon server.js"
}
}
In Procfile:
web: envisible run -e .env -- gunicorn app:app
In a Makefile:
run:
envisible run -e .env -- ./bin/myapp
In docker-compose.yml, mount the key and prefix the command:
services:
app:
image: myapp:latest
volumes:
- ./envisible.key:/app/envisible.key:ro
- ./envisible.pub:/app/envisible.pub:ro
- ./.env:/app/.env:ro
command: envisible run -e .env -- ./myapp
The app reads these directly — envisible run only injects env vars, so emit decrypted config at boot or build:
envisible decrypt --strip config.yaml > /tmp/config.yaml && ./myapp --config /tmp/config.yaml
envisible decrypt --strip once at startup and parse the result, instead of teaching every config-loader about ENC[...].Envisible writes decrypted content to stdout and all informational banners (ℹ Loading…, ✔ Starting…, KMS summaries) to stderr, so VAR=$(envisible decrypt --strip f) and envisible decrypt … | jq are clean by default. Pass -q only when you also want to silence the stderr chatter (cleaner CI logs, quieter interactive sessions); it's a global flag that works on every subcommand.
Don't bake envisible.key into the image layers. Either:
Both are optional but worth offering:
envisible git install-hook # pre-commit hook that runs `envisible check`
envisible git setup # configures a git diff driver
For the diff driver to apply, add to .gitattributes:
*.env diff=envisible
*.yaml diff=envisible
config/*.json diff=envisible
After this, git diff shows decrypted changes locally (only for people with the key), while the stored blobs remain encrypted.
In CI, the agent typically needs to decrypt at runtime (for tests / deploys) but not encrypt. Two patterns:
Store envisible.key as a CI secret. Example for GitHub Actions:
- name: Restore envisible key
run: echo "${{ secrets.ENVISIBLE_KEY }}" > envisible.key && chmod 600 envisible.key
- name: Run tests
run: envisible run -e .env.test -- npm test
Set the secret to the base64-encoded contents or the raw key — match what your runner can store. Avoid printing the key.
Authenticate the runner to the cloud (workload identity federation for GitHub Actions → GCP/AWS, OIDC, etc.), then envisible run just works:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: ...
service_account: ...
- run: envisible run -e .env.test -- npm test
In both cases, add a lint job in CI that runs envisible check against all candidate files so unencrypted markers fail PRs:
- name: Check for unencrypted secrets
run: |
for f in .env .env.production config/*.yaml; do
[ -e "$f" ] || continue
envisible check "$f"
done
Add a short section to the project README (or a SECRETS.md) covering:
envisible.key; drop it in the repo root; do not commit it."gcloud auth application-default login (or equivalent). Confirm decrypt works with envisible decrypt .env | head."ENC[plaintext], run envisible encrypt -i <file>, commit.envisible edit <file> — opens decrypted in $EDITOR, re-encrypts on save.envisible kms rotate --to <new-resource> file1 file2 ... for KMS; for local, generate new keys and re-encrypt every file).envisible check <file> passes on every modified file.envisible run -e .env -- env | grep <KEY> prints the expected decrypted value.git status shows envisible.pub staged and envisible.key not present (local mode) — check git check-ignore -v envisible.key.envisible run, boots successfully.-i. envisible encrypt -i overwrites; back up or stage first so a mistake is reversible (git checkout -- file).ENC[] marker inside a string that gets templated/escaped. YAML anchors, JSON inside a string, shell $()... encryption operates on raw bytes between the brackets, so triple-check anything not literal.gcloud auth print-access-token / aws sts get-caller-identity to confirm credential plumbing before blaming envisible.gcloud auth login and ADC are not the same. If local KMS calls fail, run gcloud auth application-default login and retry.envisible.pub. Decryption fails with no obvious error — the resource pointer lives in that file. Commit it.envisible.key and a v2-flagged envisible.pub exist), but don't leave it that way long-term. Pick a mode and re-encrypt.envisible run. The banner goes to stderr, which is the right place — but if the child also writes structured stderr (e.g., JSON logs another tool parses), the banner will interleave. Pass -q to suppress it in that case. (Stdout is clean by default; capturing or piping decrypted values needs no flag.)| Goal | Command |
|---|---|
| Install (Go) | go install github.com/rubysolo/envisible@latest |
| Install (Homebrew) | brew tap rubysolo/tools && brew install envisible |
| Generate local keypair | envisible keygen |
| Init from existing KMS key | envisible kms init --provider {gcp,aws,azure} --resource <ref> |
| Provision KMS key | envisible kms create --provider gcp --project P --location L --keyring R --name K |
| Encrypt in place | envisible encrypt -i <file> |
| Decrypt to stdout | envisible decrypt <file> |
| Decrypt to stdout, no markers | envisible decrypt --strip <file> |
| Silence informational banner on stderr | envisible -q <subcommand> ... |
Edit in $EDITOR (decrypt → edit → encrypt) | envisible edit <file> |
| Run with decrypted env injected | envisible run -e <envfile> -- <cmd> <args...> |
| CI lint for unencrypted markers | for f in <files>; do envisible check "$f"; done |
| Install pre-commit hook | envisible git install-hook |
| Set up git diff driver | envisible git setup |
| Rotate KMS version | envisible kms rotate --to <new-resource> <file>... |
For full flag details: envisible <command> --help. README at https://github.com/rubysolo/envisible.
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub rubysolo/envisible --plugin envisible