Envisible
Encrypt secrets inline in your config and .env files with ENC[…] markers — local keypair or GCP/AWS/Azure KMS. Safe to commit. Ships a setup skill for AI coding agents.
npx -y skills add rubysolo/envisible --skill envisibleAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
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.
SKILL.md
17.3 KB, as published. Nobody here has run it
Envisible setup
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.
Mental model
- Markers: anywhere in a text file,
ENC[plaintext]marks a value to encrypt. After encryption, it becomesENC[v1:base64...](local NaCl keypair) orENC[v2:base64...](cloud KMS envelope). Surrounding text is preserved, so partial-value encryption works:postgres://user:ENC[v1:...]@host/db. - Two key modes — pick one per project:
- Local NaCl keypair —
envisible.pub(commit) +envisible.key(NEVER commit). Anyone with the private key can decrypt. Zero infra, no network at runtime. - Cloud KMS (GCP / AWS / Azure) —
envisible.pubcarries both the public key and a resource pointer. The private half stays in KMS. Decrypt-time callsDecryptvia the cloud SDK's default credential chain. Commitenvisible.pub.
- Local NaCl keypair —
- Encryption needs only the public key; decryption needs the private key (NaCl mode) or KMS access (cloud mode). Plan key distribution accordingly.
Step 1 — Discover the project
Before changing anything, run these in parallel:
- Confirm envisible is installed and KMS-capable:
which envisible,envisible version, andenvisible kms --help.
- If
kmsis unavailable, upgrade to a KMS-capable release before choosing KMS mode. - Do this check anywhere envisible is installed, not just on your laptop (Docker images, CI runners, release/runtime environments).
- If missing, install:
go install github.com/rubysolo/envisible@latest(needs Go)- or
brew tap rubysolo/tools && brew install envisible(macOS/Linux) - or download a release from https://github.com/rubysolo/envisible/releases
-
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
.gitignoreforenvisible.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 withenvisible 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.
Step 2 — Choose a key mode
Ask the user, framing the tradeoff:
- Local keypair if: solo / small team, no cloud account, simple infra, OK with manually getting
envisible.keyto each developer and to production. - Cloud KMS if: team has GCP/AWS/Azure already, wants no shared private key file, wants per-identity audit logs and rotation, OK adding a network call at boot.
If they're unsure, recommend local for now — migration to KMS later is mechanical (decrypt → kms init → encrypt again).
Step 3a — Set up local keypair
envisible keygen # writes envisible.pub and envisible.key
Then:
- Add
envisible.keyto.gitignoreif not already covered. If*.keyis already ignored, you're good — but verify withgit check-ignore -v envisible.key. - Commit
envisible.pub— it's the encryption key and is safe to share. - Plan distribution of
envisible.key— out-of-band channel (password manager, 1Password vault, secure file share). Never paste into chat / PR / ticketing.
Step 3b — Set up cloud KMS
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:
- Commit
envisible.pub— required at decrypt time in v2 mode (it carries the KMS pointer). This is non-negotiable; the file is safe. - Wire runtime correctly for Docker/Cloud Run KMS mode:
- Commit
envisible.pubinto the image/repo. - Do not inject
envisible.keyorENVISIBLE_KEY. - Start with
--pub(not--key) and the target env file, for example:envisible run --pub /app/envisible.pub -f /app/.env -- ./myapp - Ensure the runtime identity can decrypt.
- Verify IAM/roles for every identity that needs to decrypt (dev laptops, CI, production):
- For deployed containers, the critical identity is the runtime identity (for example, the Cloud Run service account), not only the CI/deploy identity.
- GCP:
roles/cloudkms.cryptoKeyDecrypteron the crypto key - AWS:
kms:Decrypt(andkms:GetPublicKeyforkms init) - Azure:
keys/decrypt(andkeys/getfor init)
- Note on GCP IAM scope:
envisible.pubmay reference a specificcryptoKeyVersion, but IAM bindings are typically granted on the parentcryptoKeyresource. - Do not add
envisible.keyto.gitignorefor any special reason — it shouldn't exist in KMS mode. If it does, delete it.
Step 4 — Mark and encrypt the secrets
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 runenvisible check path/to/file— it exits non-zero if anyENC[...]marker is still unencrypted (i.e. doesn't start withv1:orv2:). -
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.
Step 5 — Wire decryption into the runtime
This is project-specific. Common patterns:
Env-style files (.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
Config files (yaml / json / toml)
The app reads these directly — envisible run only injects env vars, so emit decrypted config at boot or build:
- Boot-time: shell out and write to a temp file:
envisible decrypt --strip config.yaml > /tmp/config.yaml && ./myapp --config /tmp/config.yaml - Build-time (immutable artifacts): decrypt during the build into the image. Acceptable only if the image itself is treated as a secret (private registry, restricted pulls).
- In-process (preferred for libraries): shell out to
envisible decrypt --striponce at startup and parse the result, instead of teaching every config-loader aboutENC[...].
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.
Dockerfile
Don't bake envisible.key into the image layers. Either:
- Mount it at runtime (Compose / Kubernetes secret / ECS task secret).
- For KMS mode, give the container an IAM role / workload identity — no key file needed.
Step 6 — Git integration
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.
Step 7 — CI/CD
In CI, the agent typically needs to decrypt at runtime (for tests / deploys) but not encrypt. Two patterns:
Local-keypair CI
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.
KMS CI
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
Step 8 — Document for the team
Add a short section to the project README (or a SECRETS.md) covering:
- Key mode (local vs which KMS provider/resource).
- How a new developer gets bootstrapped:
- Local mode: "Ask <name> for
envisible.key; drop it in the repo root; do not commit it." - KMS mode: "Run
gcloud auth application-default login(or equivalent). Confirm decrypt works withenvisible decrypt .env | head."
- Local mode: "Ask <name> for
- How to add a new secret: edit the file, wrap the value in
ENC[plaintext], runenvisible encrypt -i <file>, commit. - How to edit an existing secret:
envisible edit <file>— opens decrypted in$EDITOR, re-encrypts on save. - Rotation: when, who, and the command (
envisible kms rotate --to <new-resource> file1 file2 ...for KMS; for local, generate new keys and re-encrypt every file).
Verification checklist (do this before reporting done)
- No plaintext form of any encrypted secret remains in the repo. Grep for old values.
-
envisible check <file>passes on every modified file. -
envisible run -e .env -- env | grep <KEY>prints the expected decrypted value. -
git statusshowsenvisible.pubstaged andenvisible.keynot present (local mode) — checkgit check-ignore -v envisible.key. - The app's normal start command, prefixed with
envisible run, boots successfully. - CI changes pass on a draft branch before merging.
Common pitfalls
- Plaintext leaked in git history. Encrypting now doesn't remove a value committed earlier. If the secret was ever in a public or shared history, rotate the underlying credential (DB password, API token) — don't just encrypt the leaked one.
- Wrong file got
-i.envisible encrypt -ioverwrites; 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.- CI can't decrypt in KMS mode → the runner identity is missing the role. Test locally with
gcloud auth print-access-token/aws sts get-caller-identityto confirm credential plumbing before blaming envisible. - GCP local auth confusion:
gcloud auth loginand ADC are not the same. If local KMS calls fail, rungcloud auth application-default loginand retry. - KMS mode without committing
envisible.pub. Decryption fails with no obvious error — the resource pointer lives in that file. Commit it. - Mixing v1 and v2 in the same file works during migration (envisible builds a composite decryptor when both
envisible.keyand a v2-flaggedenvisible.pubexist), but don't leave it that way long-term. Pick a mode and re-encrypt. - Banner mixed with the child's stderr under
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-qto suppress it in that case. (Stdout is clean by default; capturing or piping decrypted values needs no flag.)
Reference: commands the agent will use
| 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.