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@vanisec

Without 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
  1. Create the new credential. Prefer vanisec_generate_secret, so the new value never enters this conversation. Use vanisec_create_secret only 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.
  2. Send the link and the password through different channels. Together they grant access, so splitting them is what makes the handover safe.
  3. 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.
  4. 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, from redis:7-alpine, started with redis-server --appendonly yes, persisting to a named redis-data volume, with a redis-cli ping healthcheck
  • app, built from the repository Dockerfile, published on port 3000, depending on Redis being healthy, with restart: unless-stopped
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1

docker-compose up -d --build
docker-compose logs -f app

The 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 requirepass form of the Redis command, then point the app's REDIS_URL at redis://:password@redis:6379/3, and update the healthcheck to pass -a.
  • GA_ID defaults to CloudDrove's own Google Analytics measurement ID. Set GA_ID to 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 volume
The container image

Prebuilt images are published to GitHub Container Registry by CI on pushes to master:

docker pull ghcr.io/clouddrove/vanisec:latest

The 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.

VariablePurposeDefault
REDIS_URLRedis connection URL. Required in practice.redis://localhost:6379/3
REDIS_PASSWORDRedis password, when it is not already in REDIS_URL.unset
REDIS_DBRedis database index. Ignored when REDIS_URL already selects one.3
TRUSTED_PROXY_HOPSNumber of reverse proxies in front of the app.1
NEXT_PUBLIC_BASE_URLPublic base URL, used for metadata, robots.txt, the sitemap, and the links the hosted MCP endpoint returns.https://vanisec.clouddrove.com
GA_IDGoogle Analytics measurement ID, read at runtime.unset
NEXT_PUBLIC_GA_IDSame, but baked in at build time.unset
NODE_ENVEnvironment 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.

DeploymentValue
Single ingress or load balancer1
CDN in front of an ingress2
No proxy, app exposed directly1

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/vanisec

Defaults worth knowing:

  • replicaCount: 2, image ghcr.io/clouddrove/vanisec:latest
  • Service is ClusterIP on port 80, targeting container port 3000
  • Ingress is disabled by default. Enabling it uses className: nginx and a placeholder host, so set ingress.hosts and ingress.tls yourself.
  • 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_ID and GA_ID. There is no TRUSTED_PROXY_HOPS in values.yaml or 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 set env.REDIS_PASSWORD or redis.password, it is readable in the rendered manifest and in helm 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, then npm run dev, with a .env.local holding REDIS_URL and NEXT_PUBLIC_BASE_URL
  • npm run build then npm start for 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.

vanisec-share-secret

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.

Source: skills/vanisec-share-secret/SKILL.md. View on GitHub

Read the skill

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.

ToolSecret valueLink passwordLink
vanisec_create_secretin conversationin conversationin conversation
vanisec_generate_secretneverclipboard onlyin conversation
vanisec_generate_secret
ParameterRequiredNotes
typeyespassword, token or hex
lengthnosee the table below
expiresInnohours, one of 1, 6, 24, 72, 168, defaults to 24
typeDefault lengthAllowed range
password2412 to 128
token3216 to 128
hex6416 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
ParameterRequiredNotes
textyesthe secret to share
passwordyesprotects the link, sent to the recipient separately
expiresInnohours, 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.

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 thisTo here in your repositoryRead by
agents-md/AGENTS.mdAGENTS.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.mdcCursor Agent
copilot/copilot-instructions.md.github/copilot-instructions.mdevery Copilot surface, including github.com web chat
copilot/vanisec.instructions.md.github/instructions/vanisec.instructions.mdVS Code, Visual Studio, JetBrains, Xcode, Copilot cloud agent, Copilot code review, Copilot CLI
copilot/share-credential.prompt.md.github/prompts/share-credential.prompt.mdVS Code, Visual Studio, JetBrains only
agent-skills/vanisec-share-secret/SKILL.md.agents/skills/vanisec-share-secret/SKILL.mdCodex, 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.md

Two 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.

FieldMeaning
descriptionoptional
namethe / name, defaults to the file name
argument-hinthint shown in the chat input
agentask, agent, plan, or a custom agent name. Defaults to agent when tools is set
modeldefaults to the model picker
toolstool 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/**/*.ts

A 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.