Integrations
The same rules for handling secrets, in the format each AI client reads
What this is
One set of rules for handing a credential to a person: never write it into chat, a commit or a log, create a one-time link instead, and send the link and its password through different channels. Every AI client reads that guidance from a different file, under a different name, so this page ships the same rules in each format. Claude calls them skills, Cursor calls them rules, Copilot calls them instructions, and Codex reads AGENTS.md.
The files described here go in your repository, not in Vanisec's. They assume the Vanisec MCP server is connected, which the MCP page covers per client. Every file below is read straight out of the repository when this page is built, so what you see is what is checked in.
Claude Code
Two commands in Claude Code, and the skills below are installed along with the MCP server. This is the only client with a one-command route.
/plugin marketplace add clouddrove/vanisec
/plugin install vanisec@vanisecWithout the plugin, copy a skill directory to .claude/skills/<skill-name>/SKILL.md in your repository, or to ~/.claude/skills/ for every project. Cursor reads .claude/skills/ too.
The 3 skills
A skill loads only when the model judges it relevant, which is why it can carry the full detail rather than the summary an always-on instruction file has to be.
vanisec-rotate-credential
Use when a credential has to be replaced and the new one handed to someone. Covers rotating an AWS access key, a database password, an API token, a service account key, a webhook signing secret or a CI variable, including after a suspected leak or when someone leaves the team. Gives the ordering that avoids an outage (share the new one and confirm receipt before revoking the old one) and keeps the new value out of the conversation transcript by generating it with the Vanisec MCP server.
Source: skills/vanisec-rotate-credential/SKILL.md. View on GitHub
Read the skill
Rotating a credential and handing over the new one
Rotation is two jobs at once: replacing a credential, and getting the replacement to whoever needs it. Doing them in the wrong order causes an outage. Doing the handover carelessly leaves the new credential in a chat log, which defeats the point of rotating.
The vanisec-share-secret skill covers the two Vanisec tools and how to choose between them. This skill is about the sequence.
The order, and it is not negotiable
- Create the new credential. Prefer
vanisec_generate_secret, so the new value never enters this conversation. Usevanisec_create_secretonly when the value has to be issued by the system that owns it (an AWS access key, a token minted by a provider console or CLI) and it is therefore already in hand. - Send the link and the password through different channels. Together they grant access, so splitting them is what makes the handover safe.
- Wait for the recipient to confirm they have the new credential and that it works. The link opens once and is then destroyed. If it expired, or the wrong person opened it, go back to step 1 and issue another credential. Confirmation has to come from the recipient; there is no retrieval tool and no way to check from here whether a link was opened.
- Only then revoke or delete the old credential.
Revoking first is the failure worth avoiding. The moment the old credential is gone, everything still using it breaks: running services, cron jobs, CI pipelines, other people's local setups. If anything then goes wrong in steps 2 or 3, nobody has a working credential at all, and the fastest way out is usually somebody pasting a secret into a chat window.
The exception is an active compromise. If the credential is known to be in the wrong hands, revoke immediately and accept the outage. Say so explicitly when you make that call, so the user knows the breakage is deliberate.
Before step 4, list what else holds the old value
A credential is rarely used in one place. Ask for or work out the full list and give it to the user before anything is revoked:
- configuration files and environment files on servers and laptops
- CI and CD variables, and repository or organisation level secrets
- secret managers and parameter stores
- running services that read the value at startup and need a restart
- container images or manifests that carry it
- scheduled jobs, monitoring integrations, third party webhooks
Anything on that list that is not updated will break at step 4 rather than at step 1, which is why the list has to exist before the revoke.
Generating the replacement
Reach for vanisec_generate_secret whenever the new value is something you are free to choose:
- database and service passwords,
type: "password" - API tokens and bearer tokens,
type: "token" - signing keys, encryption keys and webhook secrets,
type: "hex"
Defaults are 24 characters for password, 32 for token and 64 for hex. Only override length when the receiving system enforces its own limit, and never downward for convenience. Set expiresIn short (1 or 6 hours) when the recipient is waiting, since a rotation handover is usually time boxed anyway.
The tool puts the link password on the system clipboard and returns only the URL. If it reports that no clipboard is available, do not fall back to pasting the credential into the conversation. See the share skill for the options.
When the provider issues the value
Some credentials cannot be generated locally. A cloud access key, an OAuth client secret, a managed database password reset by the provider: in each case the provider hands you the value, and it exists before Vanisec is involved.
Run the provider's own command or console flow, then pass the result to vanisec_create_secret. Be honest about the cost: the value is in the transcript from the moment it is passed in. Keep it out of intermediate steps where you can, for example by having the user capture the output themselves rather than printing it into the conversation.
Typical shapes of the commands involved, as illustration only. Check the provider's current documentation rather than copying these:
- Cloud access keys: create a new key for the identity, verify it, then delete the old key by its identifier.
- Managed databases: modify the instance or role with a new password, then restart or reload whatever pools connections.
- Git hosting and SaaS APIs: mint a new token with the same scopes, replace it everywhere, then revoke the previous one from the tokens page.
Never write out a value that looks like a real credential, in a command, in an example or in a summary. Refer to it by name.
Closing out
Once the old credential is revoked, confirm two things and say them plainly:
- the old value no longer authenticates anywhere
- every consumer from the list above is on the new value
If the rotation was triggered by a leak, also note where the old value was exposed, so the same channel is not used for the handover.
vanisec-self-host
Use when running Vanisec on your own infrastructure rather than the public instance, or when someone asks how to deploy it with Docker Compose, the container image, or the Helm chart on Kubernetes. Covers the required Redis configuration, every environment variable the app actually reads, what the bundled Helm chart does and does not template, the reverse proxy setting that rate limiting depends on, and how to point the Vanisec MCP server at your own instance.
Source: skills/vanisec-self-host/SKILL.md. View on GitHub
Read the skill
Self-hosting Vanisec
Vanisec is a Next.js app with Redis behind it. Redis stores ciphertext and a hash of a verifier, nothing else, so the server cannot read a secret even with full access to its own database. Self-hosting does not change the encryption model, it changes who runs the box.
Everything below is what is in the repository at https://github.com/clouddrove/vanisec. There is no separate configuration service and no database other than Redis.
Docker Compose
The fastest path. docker-compose.yml defines two services:
redis, fromredis:7-alpine, started withredis-server --appendonly yes, persisting to a namedredis-datavolume, with aredis-cli pinghealthcheckapp, built from the repositoryDockerfile, published on port 3000, depending on Redis being healthy, withrestart: unless-stopped
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
docker-compose up -d --build
docker-compose logs -f appThe app is then on http://localhost:3000.
Two things about the compose file are worth changing before it goes anywhere real:
- Redis has no password. Authentication is present but commented out: uncomment the
requirepassform of the Rediscommand, then point the app'sREDIS_URLatredis://:password@redis:6379/3, and update the healthcheck to pass-a. GA_IDdefaults to CloudDrove's own Google Analytics measurement ID. SetGA_IDto your own, or to an empty value, unless you want to send traffic data to that property.
Other commands the repository documents:
docker-compose down # stop
docker-compose down -v # stop and drop the Redis volumeThe container image
Prebuilt images are published to GitHub Container Registry by CI on pushes to master:
docker pull ghcr.io/clouddrove/vanisec:latestThe Dockerfile builds in three stages on node:26-alpine and produces a Next.js standalone output. The runtime stage runs as a non-root user (uid 1001, group 1001), listens on port 3000 with HOSTNAME=0.0.0.0, and starts with node server.js.
The image copies the whole source tree in the builder stage rather than listing paths, so .dockerignore is the single place that decides what stays out. If you add a root level source file, check .dockerignore rather than the Dockerfile.
Environment variables
These are the variables the application code actually reads. Anything not on this list does nothing.
| Variable | Purpose | Default |
|---|---|---|
REDIS_URL | Redis connection URL. Required in practice. | redis://localhost:6379/3 |
REDIS_PASSWORD | Redis password, when it is not already in REDIS_URL. | unset |
REDIS_DB | Redis database index. Ignored when REDIS_URL already selects one. | 3 |
TRUSTED_PROXY_HOPS | Number of reverse proxies in front of the app. | 1 |
NEXT_PUBLIC_BASE_URL | Public base URL, used for metadata, robots.txt, the sitemap, and the links the hosted MCP endpoint returns. | https://vanisec.clouddrove.com |
GA_ID | Google Analytics measurement ID, read at runtime. | unset |
NEXT_PUBLIC_GA_ID | Same, but baked in at build time. | unset |
NODE_ENV | Environment mode. | production in the images |
REDIS_URL accepts redis://[password@]host:port[/database]. When the URL path already selects a database, REDIS_DB is ignored, so redis://host/0 is honoured rather than silently overridden.
Set NEXT_PUBLIC_BASE_URL to your own domain. Left at its default, a self-hosted instance advertises the public one in its metadata and sitemap.
TRUSTED_PROXY_HOPS deserves its own paragraph
Rate limiting identifies callers by IP, read from X-Forwarded-For. Every proxy appends to that header, so only the rightmost entries are trustworthy; anything further left came from the caller and can be forged. The app takes the Nth entry from the right, where N is TRUSTED_PROXY_HOPS.
| Deployment | Value |
|---|---|
| Single ingress or load balancer | 1 |
| CDN in front of an ingress | 2 |
| No proxy, app exposed directly | 1 |
Set too high, it reads an entry the caller controls, and one client can forge unlimited identities and walk past rate limiting entirely. Set too low, everyone behind the proxy shares a bucket and one noisy client rate limits the rest. Count your actual hops.
Kubernetes with the bundled Helm chart
The chart lives at _infra/helm/vanisec. It is a complete chart, not a skeleton: Chart.yaml (apiVersion v2, chart version 1.0.0, appVersion 1.0.0), values.yaml, and templates for the Deployment, Service, Ingress, HorizontalPodAutoscaler, ServiceAccount, and an optional embedded Redis (Deployment, Service and PersistentVolumeClaim in one file).
helm install vanisec ./_infra/helm/vanisec
helm install vanisec ./_infra/helm/vanisec -f my-values.yaml
helm upgrade vanisec ./_infra/helm/vanisecDefaults worth knowing:
replicaCount: 2, imageghcr.io/clouddrove/vanisec:latest- Service is
ClusterIPon port 80, targeting container port 3000 - Ingress is disabled by default. Enabling it uses
className: nginxand a placeholder host, so setingress.hostsandingress.tlsyourself. - Autoscaling is disabled by default. Enabled, it runs 2 to 10 replicas against 80 percent CPU and memory targets.
- Pods run as uid 1001 with
runAsNonRoot, all capabilities dropped, no privilege escalation, and a read only root filesystem. - Liveness and readiness probes both hit
/.
Redis, embedded or external
redis.enabled: true (the default) deploys Redis 7-alpine inside the release, with an 8Gi PersistentVolumeClaim when redis.persistence.enabled is true and an emptyDir when it is not. An emptyDir means every unopened secret is lost when the pod moves.
redis.enabled: false skips all of that, and you point env.REDIS_URL at your own Redis or a managed service. That is the right choice for anything production shaped.
The default env.REDIS_URL is redis://vanisec-redis:6379/3, which matches the embedded Redis Service only when the release is named vanisec. Install under any other release name and you have to update env.REDIS_URL to match, because the Service is named after the release.
Setting redis.password adds --requirepass to the embedded Redis and passes -a to its probes, but it does not flow into the app. Set env.REDIS_PASSWORD as well, or put the password in env.REDIS_URL, or the app will not authenticate.
Two gaps in the chart to know about
- The Deployment template renders only
NODE_ENV,REDIS_URL,REDIS_PASSWORD,NEXT_PUBLIC_BASE_URL,NEXT_PUBLIC_GA_IDandGA_ID. There is noTRUSTED_PROXY_HOPSinvalues.yamlor in the template, so behind an ingress the app falls back to its default of 1. Add it to the template if your hop count is different. - Secrets go into the Deployment as plain values from
values.yaml, not through a Kubernetes Secret. If you setenv.REDIS_PASSWORDorredis.password, it is readable in the rendered manifest and inhelm get values.
A third thing to check when redis.enabled is true: the app Service selects on the chart's name and instance labels only, while the embedded Redis pods carry those same labels plus a component label. Verify the Service endpoints after install and narrow the selector if it has picked up the Redis pod.
Local development
For running from source rather than a container:
- Node.js 20.x or higher, Redis 7.x or higher
npm install, thennpm run dev, with a.env.localholdingREDIS_URLandNEXT_PUBLIC_BASE_URLnpm run buildthennpm startfor a production build
Redis on its own is enough to develop against: docker run -d -p 6379:6379 redis:7-alpine.
Pointing the MCP server at your instance
The @clouddrove/vanisec-mcp package targets https://vanisec.clouddrove.com unless VANISEC_BASE_URL says otherwise. Set it in the env block of the client's MCP configuration:
{
"mcpServers": {
"vanisec": {
"command": "npx",
"args": ["-y", "@clouddrove/vanisec-mcp"],
"env": {
"VANISEC_BASE_URL": "https://vanisec.example.com"
}
}
}
}Trailing slashes are stripped, so either form works. The server posts to {base}/api/secrets and returns links of the form {base}/secret/{id}. It logs the base URL it is targeting to stderr at startup, which is the quickest way to confirm the variable took effect.
Your instance also serves the hosted MCP endpoint at POST {base}/api/mcp for clients that cannot run a local process. That endpoint offers vanisec_create_secret only and encrypts on the server, so it is not zero-knowledge; prefer the local package. The links it returns come from NEXT_PUBLIC_BASE_URL when that is set, and from the request origin otherwise, which is another reason to set it.
Operational notes
- Secret creation is rate limited to 30 per 10 minutes per IP, and the hosted MCP endpoint to 20 per 10 minutes per IP. Rate limit counters live in the same Redis instance.
- Payloads are capped: 16MB on the request body, and 12,000,000 characters of ciphertext.
- Secrets are stored with a Redis TTL matching their expiry, and retrieval is an atomic fetch and delete, which is what makes them one-time. A racing second reader gets nothing.
- Terminate TLS at your ingress or reverse proxy. Encryption happens in the browser or in the MCP client, but the link and the password still travel over the wire.
- Back up Redis only if you have a reason to. Its entire contents expire on their own, and a backup of ciphertext plus verifier hashes is of no use to anyone without the passwords.
Every other client
No plugin route here, so these are copied by hand. Every file is already named as its target filename, so nothing needs renaming. Take the ones for the clients your team uses: shipping all of them costs context on every request.
| Copy this | To here in your repository | Read by |
|---|---|---|
agents-md/AGENTS.md | AGENTS.md (repo root) | Codex, Cursor, VS Code Copilot, Copilot cloud agent, Copilot code review, Copilot CLI, Windsurf and Devin, Zed |
cursor/vanisec.mdc | .cursor/rules/vanisec.mdc | Cursor Agent |
copilot/copilot-instructions.md | .github/copilot-instructions.md | every Copilot surface, including github.com web chat |
copilot/vanisec.instructions.md | .github/instructions/vanisec.instructions.md | VS Code, Visual Studio, JetBrains, Xcode, Copilot cloud agent, Copilot code review, Copilot CLI |
copilot/share-credential.prompt.md | .github/prompts/share-credential.prompt.md | VS Code, Visual Studio, JetBrains only |
agent-skills/vanisec-share-secret/SKILL.md | .agents/skills/vanisec-share-secret/SKILL.md | Codex, Cursor. Also .claude/skills/ for Claude Code and Cursor |
AGENTS.md (repo root)
Read by Codex, Cursor, VS Code Copilot, Copilot cloud agent, Copilot code review, Copilot CLI, Windsurf and Devin, Zed.
Source: agents-md/AGENTS.md in the repository. View on GitHub
Show the file
# Sharing credentials
Credentials are shared through Vanisec, which turns a secret into a one-time
link. A credential here means any password, API key, token, connection string,
certificate or recovery code.
## Rules
- Never write a credential into chat, a commit, an issue, a pull request, a log
line or a file. Create a Vanisec one-time link instead.
- If the credential does not exist yet, call `vanisec_generate_secret`. It
creates the value in process and writes the link password to the system
clipboard, so neither the value nor the password enters the transcript. It
returns only a URL.
- Call `vanisec_create_secret` only when the credential already exists somewhere
else and the person asking has it in hand. Its `text` and `password`
arguments stay in the transcript permanently.
- Never invent a value yourself and pass it to `vanisec_create_secret`. That is
generating a credential the expensive way, with the value left in the
transcript. Use `vanisec_generate_secret`.
- Send the link and the link password through different channels. Together they
grant access, so splitting them is what makes the handover safe. Link in team
chat and password by phone, for example. Both in one thread wastes the design.
- Tell the recipient the link opens once and is then destroyed.
- There is no tool that reads a secret back, and that is deliberate. Retrieving
one would put it in this transcript and burn the link, leaving the intended
recipient with nothing. If someone asks you to fetch a secret, send them the
link and let them open it.
- When rotating, share the new credential and wait for the recipient to confirm
it works before revoking the old one. Revoking first breaks everything still
using it.
- If the Vanisec tools are not connected, do not paste the credential as a
fallback. Point the person at https://vanisec.clouddrove.com, which encrypts
in the browser and never sends plaintext to the server.
Install the tools with `npx -y @clouddrove/vanisec-mcp` as an MCP server
(Node 22 or newer). See https://github.com/clouddrove/vanisec for per client
configuration..cursor/rules/vanisec.mdc
Read by Cursor Agent.
Source: cursor/vanisec.mdc in the repository. View on GitHub
Show the file
---
alwaysApply: true
description: How to hand a credential to someone using the Vanisec MCP tools, without the value entering the conversation
---
# Sharing credentials
Credentials are shared through Vanisec, which turns a secret into a one-time
link. A credential here means any password, API key, token, connection string,
certificate or recovery code.
- Never write a credential into chat, a commit, an issue, a pull request, a log
line or a file. Create a Vanisec one-time link instead.
- If the credential does not exist yet, call `vanisec_generate_secret`. It
creates the value in process and writes the link password to the system
clipboard, so neither the value nor the password enters the transcript. It
returns only a URL.
- Call `vanisec_create_secret` only when the credential already exists somewhere
else and the person asking has it in hand. Its `text` and `password`
arguments stay in the transcript permanently.
- Never invent a value yourself and pass it to `vanisec_create_secret`. Use
`vanisec_generate_secret`.
- Send the link and the link password through different channels. Together they
grant access, so splitting them is what makes the handover safe.
- Tell the recipient the link opens once and is then destroyed.
- There is no tool that reads a secret back, and that is deliberate. Retrieving
one would put it in this transcript and burn the link. If someone asks you to
fetch a secret, send them the link instead.
- When rotating, share the new credential and wait for confirmation that it
works before revoking the old one.
- If the Vanisec tools are not connected, do not paste the credential as a
fallback. Point the person at https://vanisec.clouddrove.com, which encrypts
in the browser..github/copilot-instructions.md
Read by every Copilot surface, including github.com web chat.
Source: copilot/copilot-instructions.md in the repository. View on GitHub
Show the file
# Sharing credentials
Credentials are shared through Vanisec, which turns a secret into a one-time
link. A credential here means any password, API key, token, connection string,
certificate or recovery code.
## Rules
- Never write a credential into chat, a commit, an issue, a pull request, a log
line or a file. Create a Vanisec one-time link instead.
- If the credential does not exist yet, call `vanisec_generate_secret`. It
creates the value in process and writes the link password to the system
clipboard, so neither the value nor the password enters the transcript. It
returns only a URL.
- Call `vanisec_create_secret` only when the credential already exists somewhere
else and the person asking has it in hand. Its `text` and `password`
arguments stay in the transcript permanently.
- Never invent a value yourself and pass it to `vanisec_create_secret`. That is
generating a credential the expensive way, with the value left in the
transcript. Use `vanisec_generate_secret`.
- Send the link and the link password through different channels. Together they
grant access, so splitting them is what makes the handover safe. Link in team
chat and password by phone, for example. Both in one thread wastes the design.
- Tell the recipient the link opens once and is then destroyed.
- There is no tool that reads a secret back, and that is deliberate. Retrieving
one would put it in this transcript and burn the link, leaving the intended
recipient with nothing. If someone asks you to fetch a secret, send them the
link and let them open it.
- When rotating, share the new credential and wait for the recipient to confirm
it works before revoking the old one. Revoking first breaks everything still
using it.
- If the Vanisec tools are not connected, do not paste the credential as a
fallback. Point the person at https://vanisec.clouddrove.com, which encrypts
in the browser and never sends plaintext to the server.
Install the tools with `npx -y @clouddrove/vanisec-mcp` as an MCP server
(Node 22 or newer). See https://github.com/clouddrove/vanisec for per client
configuration..github/instructions/vanisec.instructions.md
Read by VS Code, Visual Studio, JetBrains, Xcode, Copilot cloud agent, Copilot code review, Copilot CLI.
Source: copilot/vanisec.instructions.md in the repository. View on GitHub
Show the file
---
name: Vanisec credential sharing
description: Which Vanisec tool to call, with its parameters and limits
applyTo: '**'
---
# Vanisec tool reference
`.github/copilot-instructions.md` carries the rules. This file is the detail
behind them: which tool, which arguments, and what the service accepts.
## Which tool
| Tool | Secret value | Link password | Link |
|------|--------------|---------------|------|
| `vanisec_generate_secret` | never in the conversation | clipboard only | in the conversation |
| `vanisec_create_secret` | in the conversation | in the conversation | in the conversation |
Prefer `vanisec_generate_secret`. Reach for `vanisec_create_secret` only when
the value was issued elsewhere, by a cloud console or a provider CLI, and the
person asking already holds it. Never invent a value and pass it in.
If the request is ambiguous, such as a request to send someone a password for a
staging box, ask whether the credential already exists. If nobody has issued it
yet, generate.
## `vanisec_generate_secret`
| Parameter | Required | Notes |
|-----------|----------|-------|
| `type` | yes | `password`, `token` or `hex` |
| `length` | no | `password` 24 by default (12 to 128), `token` 32 (16 to 128), `hex` 64 (16 to 256, even only) |
| `expiresIn` | no | hours, one of 1, 6, 24, 72, 168, defaults to 24 |
`password` mixes letters, digits and punctuation. `token` is letters and digits
only, which suits API keys and anything travelling through a URL or a shell.
`hex` is lowercase hex digits, for signing and encryption keys. Do not shorten a
credential for convenience.
The password goes to the clipboard before the link is created, so a machine with
no clipboard fails without leaving a live link behind. If that happens, say so
rather than working around it. The two real options are running the server
somewhere with a clipboard, or accepting `vanisec_create_secret` with a password
the user chooses.
## `vanisec_create_secret`
| Parameter | Required | Notes |
|-----------|----------|-------|
| `text` | yes | the secret to share |
| `password` | yes | protects the link, sent to the recipient separately |
| `expiresIn` | no | hours, one of 1, 6, 24, 72, 168, defaults to 24 |
The password has to be a real password, not a hint and not something guessable
from the surrounding conversation.
## Expiry
Only 1, 6, 24, 72 and 168 hours are accepted; anything else is rejected. Use 1
when the recipient is waiting, 6 for the same working day, 24 when you do not
know, 72 across a weekend, 168 only when they are away. Shorter is better. The
link is destroyed on first open, so expiry only bounds how long an unopened link
stays live.
## Limits
Secret creation is rate limited to 30 per 10 minutes per IP address. A create is
never retried automatically: a retry of a create that actually succeeded would
leave two live one-time links for one secret and you would only learn of one. If
a call fails, treat the outcome as unknown before trying again..github/prompts/share-credential.prompt.md
Read by VS Code, Visual Studio, JetBrains only.
Source: copilot/share-credential.prompt.md in the repository. View on GitHub
Show the file
---
name: share-credential
description: Hand a credential to someone as a Vanisec one-time link, without the value entering the conversation
agent: agent
argument-hint: what the credential is for, and who receives it
---
Share a credential using Vanisec, for the purpose and recipient given in the chat input.
Work through this in order.
1. Establish whether the credential already exists. If it has not been issued
yet, it does not exist, and you should generate it.
2. If it does not exist, call `vanisec_generate_secret`. Pick `type` from the
use: `password` for a login, `token` for an API key or bearer token, `hex`
for a signing or encryption key. Leave `length` alone unless the receiving
system enforces a limit. Set `expiresIn` to 1 if the recipient is waiting,
otherwise leave the default of 24.
3. If it already exists and the user has it in hand, call
`vanisec_create_secret` with their value and a password they choose. Do not
invent the value yourself, and do not invent the password. Say plainly that
both are now in the transcript.
4. Report back with the link, who it is for, and when it expires. Never repeat
the credential itself.
5. Remind the user to send the link password through a different channel from
the link, and to tell the recipient that the link opens once and is then
destroyed.
Do not offer to read the secret back. No such tool exists, by design..agents/skills/vanisec-share-secret/SKILL.md
Read by Codex, Cursor. Also .claude/skills/ for Claude Code and Cursor.
Source: agent-skills/vanisec-share-secret/SKILL.md in the repository. View on GitHub
Show the file
---
name: vanisec-share-secret
description: Use when someone needs to hand a secret to another person, or asks for a one-time link, a self-destructing link, or a safe way to send a password, API key, token, connection string, certificate or recovery code. Covers which of the two Vanisec MCP tools to call, how to keep the secret value and the link password out of the conversation transcript, choosing an expiry, and getting the link and the password to the recipient. Also covers what to do when the Vanisec MCP server is not connected.
license: MIT
---
# Sharing a secret with Vanisec
Vanisec turns a secret into a one-time link. Opening the link once destroys the
secret. Encryption happens on the machine running the MCP server, so Vanisec
only ever stores ciphertext, never the plaintext, the password or the key.
Two tools exist. Choosing the wrong one is the mistake this skill exists to
prevent.
## Choose the tool before anything else
**If the secret does not exist yet, call `vanisec_generate_secret`.** It creates
the value in process, shares it, and writes the link password to the system
clipboard. Neither the value nor the password is ever returned to you, so
neither one enters the conversation.
**Call `vanisec_create_secret` only when the secret already exists somewhere
else and the person asking already has it in hand.** Rotating a database
password they typed out, forwarding a key issued by a cloud console, passing on
a config value from a file: those are cases where the value already exists and
`vanisec_generate_secret` cannot produce it.
Never make up a value yourself and pass it to `vanisec_create_secret`. That is
the same as generating it, except the value is now permanently in the
transcript.
If the request is ambiguous ("send Priya a password for the staging box"), ask
whether the credential already exists. If nobody has issued it yet, generate.
## Why the choice matters
`vanisec_create_secret` takes `text` and `password` as arguments. Everything
passed to a tool is part of the conversation. It is in the transcript, in
whatever the client persists to disk or to a server, and in any context that is
later replayed. Deleting the message does not reliably remove it. Encrypting the
secret afterwards does not help, because the plaintext was already spoken.
`vanisec_generate_secret` never returns either value. The conversation ends up
holding only a URL, which is useless on its own.
| Tool | Secret value | Link password | Link |
|------|--------------|---------------|------|
| `vanisec_create_secret` | in conversation | in conversation | in conversation |
| `vanisec_generate_secret` | never | clipboard only | in conversation |
## `vanisec_generate_secret`
| Parameter | Required | Notes |
|-----------|----------|-------|
| `type` | yes | `password`, `token` or `hex` |
| `length` | no | see the table below |
| `expiresIn` | no | hours, one of 1, 6, 24, 72, 168, defaults to 24 |
| `type` | Default length | Allowed range |
|--------|----------------|---------------|
| `password` | 24 | 12 to 128 |
| `token` | 32 | 16 to 128 |
| `hex` | 64 | 16 to 256, even numbers only |
`password` mixes letters, digits and punctuation, so it suits anything a human
or a login form will accept. `token` is letters and digits only, which is
usually what you want for API keys, bearer tokens and anything that travels
through a URL or a shell. `hex` is lowercase hex digits, for signing keys,
encryption keys and other values a system expects as raw bytes. The defaults are
fine unless the receiving system imposes its own limit; do not shorten a
credential for convenience.
The tool writes the link password to the clipboard before it creates the link.
That ordering is deliberate: if there is no clipboard, the tool fails and no
live link is left behind. It uses `pbcopy` on macOS, `wl-copy` or `xclip` on
Linux, and `clip` on Windows.
**If it reports that no clipboard is available**, the machine is likely a
container or an SSH session with no display. Do not try to work around it. Tell
the user what happened and give them the two real options:
- run the MCP server somewhere with a clipboard, or
- accept `vanisec_create_secret` with a password they choose, understanding that
the value and the password stay in the transcript.
A human operator can also set `VANISEC_ALLOW_INLINE_PASSWORD=1` in their client
configuration, which makes the tool return the password in the conversation
instead of using the clipboard. That is their decision to make in their own
config, not something to suggest as a quick fix, because it removes the
protection the tool exists to provide.
## `vanisec_create_secret`
| Parameter | Required | Notes |
|-----------|----------|-------|
| `text` | yes | the secret to share |
| `password` | yes | protects the link, sent to the recipient separately |
| `expiresIn` | no | hours, one of 1, 6, 24, 72, 168, defaults to 24 |
The password must be a real password, not a hint and not something guessable
from the surrounding conversation. Anyone who gets the link and guesses the
password gets the secret.
## Choosing `expiresIn`
Only 1, 6, 24, 72 and 168 hours are accepted. Any other number is rejected
before the request leaves the machine. Omitting it gives 24.
- `1` when the recipient is online now and waiting.
- `6` for the same working day.
- `24` (the default) when you do not know exactly when they will look.
- `72` across a weekend.
- `168` (seven days) when the recipient is away or in a distant timezone. This is
the longest the service allows, so treat it as the exception.
Shorter is better. The link is destroyed on first open, so expiry only bounds
how long an unopened link stays live.
## Getting the link and the password to the recipient
The link and the password together grant access. Either one alone is useless.
Send them through **different channels**, so no single compromised inbox, chat
log or screen share yields both.
For example: link in the team chat, password by phone or SMS. Or link by email,
password through a voice call. Pasting both into the same thread wastes the
entire design.
Tell the recipient two things: the link opens once and is then destroyed, and
they should open it somewhere they can actually store the secret, not on a phone
in a taxi.
## There is no retrieval tool, on purpose
You cannot read a Vanisec secret from here, and no such tool will be added. A
retrieved secret would land in this conversation and in the transcript, so a
one-time secret would stop being one-time in any way that matters. It would also
burn the link, leaving the intended recipient with nothing.
Recipients open the link in a browser and enter the password there. If someone
asks you to fetch a secret for them, explain this and send them the link.
## When Vanisec MCP is not connected
If neither tool is available, do not paste the secret into the conversation as a
fallback. Point the user at one of these instead:
- The web UI at https://vanisec.clouddrove.com, where the secret is encrypted in
the browser and never reaches the server in plaintext.
- Installing the local MCP server, which is what makes
`vanisec_generate_secret` possible:
`claude mcp add vanisec -- npx -y @clouddrove/vanisec-mcp` (Node 22 or newer).
- A hosted MCP endpoint exists at `POST https://vanisec.clouddrove.com/api/mcp`
for clients that cannot run a local process. It offers `vanisec_create_secret`
only, and it is not zero-knowledge: the secret and the password reach the
server in the request body and are encrypted there. Prefer the local package
wherever it can run.
## Limits worth knowing
- Secret creation is rate limited to 30 per 10 minutes per IP address. The
hosted MCP endpoint is limited to 20 per 10 minutes per IP.
- A create is never retried automatically. A retry of a create that actually
succeeded would leave two live one-time links for one secret, and the caller
would only learn of one. If a call fails, treat the outcome as unknown before
trying again.The warnings
The ways to install these files correctly and still have nothing happen. Each of these fails silently: no error, no warning, just a client that never sees the rules.
Zed reads one file and stops
Zed does not merge instruction files. It reads the first match from this ordered list and ignores everything after it:
.rules
.cursorrules
.windsurfrules
.clinerules
.github/copilot-instructions.md
AGENT.md
AGENTS.md
CLAUDE.md
GEMINI.mdTwo consequences.
A repo with both .github/copilot-instructions.md and AGENTS.md gives Zed only the Copilot file. That is why the two files we ship carry the same rules. If you edit one, edit the other, or Zed users quietly get whichever you neglected.
A stray legacy .cursorrules beats both of them. It sits higher in the list, so Zed reads it and nothing else, including nothing we ship. Cursor's own documentation now calls .cursorrules legacy. If your repo still has one, migrate its contents to .cursor/rules/*.mdc and delete it. The same applies to .rules, .windsurfrules and .clinerules if you have them.
From integrations/README.md. View on GitHub
Copilot prompt files run in three editors, and the key is agent:
Invoked as /share-credential in chat. Prompt files exist only in VS Code, Visual Studio and JetBrains. They do nothing in the cloud agent, in code review, in Copilot CLI, in Eclipse, in Xcode or on github.com. Skip this file if your team is not on one of those three.
The frontmatter key is agent. mode no longer exists. Any guide still showing mode: agent predates the rename.
| Field | Meaning |
|---|---|
description | optional |
name | the / name, defaults to the file name |
argument-hint | hint shown in the chat input |
agent | ask, agent, plan, or a custom agent name. Defaults to agent when tools is set |
model | defaults to the model picker |
tools | tool or tool set names. <server name>/* includes a whole MCP server |
We set agent: agent and leave tools unset, because setting tools restricts the run to that list. If you want to pin it to Vanisec only, add tools: vanisec/*, using whatever name you gave the server in your MCP config.
chat.promptFiles is no longer documented. Prompt files are always on.
From integrations/copilot/README.md. View on GitHub
Cursor rules take globs, Cursor skills take paths
alwaysApply, description, globs. There is no name, no priority, no type, no paths and no attachmentType. Adding any of those does nothing.
globs is a comma separated unquoted string, not a YAML list:
globs: app/**/*.ts, lib/**/*.tsA YAML list there will not match anything. Note that Cursor skills use a paths field which does take a list. The two schemas are different; do not carry one across to the other.
From integrations/cursor/README.md. View on GitHub
Next
These files describe tools that have to exist. Install the MCP server first, or the instructions name tools nobody can call.