# ncrypted > One-time, end-to-end encrypted secrets for AI agents. An agent encrypts a password, API key or sensitive note locally, pays 0.005 USDC (x402, Base), and gets a link that works exactly once. Share it with a human, email it, or hand it to another agent. Reading is free and destroys the secret. ## Quick start (recommended: encryption happens on your side) - MCP server: `npx -y @seidl-family/ncrypted-mcp`. Tools: ncrypted_create, ncrypted_read, ncrypted_status, ncrypted_burn, ncrypted_wallet. - CLI: `echo "s3cret" | npx ncrypted create --ttl 1h`, `npx ncrypted read ` - SDK: `import { Ncrypted } from "ncrypted"; const { link } = await new Ncrypted().create("s3cret");` - Claude Code: `/plugin marketplace add Seidl-Family/ncrypted` then `/plugin install ncrypted@ncrypted` - Codex: `codex plugin marketplace add Seidl-Family/ncrypted`; Gemini CLI: `gemini extensions install https://github.com/Seidl-Family/ncrypted` - Hosted MCP (x402-capable clients; server sees plaintext briefly): https://ncrypted.ai/mcp - Wallet: `npx ncrypted wallet new` creates ~/.config/ncrypted/wallet.key; fund it with USDC on Base. Check with the ncrypted_wallet tool. ## Getting secrets without seeing them - Need a secret from the user? Call ncrypted_request (label = what and why), send them the returned link, then ncrypted_request_fulfill with destination vercel_env or dotenv. Never ask the user to paste a secret into chat. - Have a link and need the value in Vercel or .env? ncrypted_to_vercel_env / ncrypted_to_dotenv write it directly (Vercel access is checked first, so a bad destination doesn't consume the link). - CLI: `npx ncrypted request "Stripe key for billing"`, then `npx ncrypted receive --vercel-key STRIPE_SECRET_KEY --target production`. ## Pricing - 3 free secrets per day per IP (TTL <= 24h, no email), then 0.005 USDC via x402. Requests, reads and deliveries are free. - No wallet? ncrypted_wallet_setup creates one locally and returns a funding link (/fund) for the owner. ## Rules for agents - Never paste the secret value into chat, logs or tickets. Share only the link. - The link contains the decryption key after "#"; treat it like the secret. - A link opens once. Reading it (ncrypted_read or the web page) destroys it. - For high-sensitivity email handoffs, set a passphrase and send it through a different channel. ## HTTP API (https://ncrypted.ai) - POST /api/v1/secrets [x402, $0.005 USDC on Base] body {v:1, ciphertext, iv, salt, hasPassphrase, ttl?} -> {id, url, expiresAt, receiptToken}. Link = url + "#" + base64url(32-byte key). - POST /api/v1/secrets/{id}/reveal [free] -> encrypted payload, deleted atomically. 410 if gone. - GET /api/v1/secrets/{id}/status [free, Bearer receiptToken] -> {status: pending|read|burned|expired} - DELETE /api/v1/secrets/{id} [free, Bearer receiptToken] -> burns it - POST /api/v1/secrets/{id}/email [free, Bearer receiptToken, once; paid secrets only] body {to, link, note?} - GET /api/v1/quota -> {freePerDay, freeRemaining, freeMaxTtl, price} - POST /api/v1/requests {publicKey (X25519, base64url), label, ttl?} -> {id, url, expiresAt, pollToken}. Link = url + "#" + publicKey. - POST /api/v1/requests/{id}/fulfill {v:1, epk, iv, ciphertext} (sealed in the browser; once) - POST /api/v1/requests/{id}/collect [Bearer pollToken] -> 202 pending | 200 {sealed} (one-time) ## Crypto AES-256-GCM. key = HKDF-SHA256(ikm = linkKey [|| PBKDF2-SHA256(passphrase, salt, 600000)], salt, info "ncrypted v1"). 12-byte IV, 16-byte salt, all base64url. Reference implementation: https://github.com/Seidl-Family/ncrypted/blob/main/packages/sdk/src/crypto.ts ## Guides - [Why agents need one-time secrets](https://ncrypted.ai/guides/why): What goes wrong when agents handle credentials in chat, and the primitive that fixes it. - [Claude Code](https://ncrypted.ai/guides/claude-code): Install the plugin: skill plus local MCP server. - [Set Vercel env vars without the agent seeing them](https://ncrypted.ai/guides/vercel): Request a secret from the user and write it straight into a Vercel project. - [Vercel AI SDK](https://ncrypted.ai/guides/ai-sdk): Drop-in AI SDK tools: one-time links and Vercel env delivery, encrypted in your server process. - [OpenAI Agents SDK](https://ncrypted.ai/guides/openai-agents): Attach the ncrypted MCP server to an OpenAI agent. - [Mastra](https://ncrypted.ai/guides/mastra): Load the ncrypted tools into a Mastra agent. - [LangChain](https://ncrypted.ai/guides/langchain): ncrypted as LangChain tools, in Python or JS. - [CrewAI](https://ncrypted.ai/guides/crewai): Give a CrewAI agent the ncrypted tools with the mcps field. - [HTTP API and x402](https://ncrypted.ai/guides/http-api): Call ncrypted directly from any language. - All guides in one file: https://ncrypted.ai/llms-full.txt ## Links - OpenAPI: https://ncrypted.ai/openapi.json - Source: https://github.com/Seidl-Family/ncrypted # Guides ## Why agents need one-time secrets https://ncrypted.ai/guides/why What goes wrong when agents handle credentials in chat, and the primitive that fixes it. Agents now provision infrastructure, rotate keys and hand work to other agents. The default way they move a credential is to put it in the conversation: the user pastes a key, the agent echoes it into a command, another agent receives it in a message. Every copy lands in transcripts, provider logs, tool traces and sometimes tickets or commits, where it outlives its purpose. ### the primitive ncrypted turns a secret into a link that opens exactly once. It's encrypted on the sender's machine (AES-256-GCM), the key travels in the link's #fragment so the server only stores ciphertext, and the first read deletes it atomically. Unread links expire. ### secrets the agent never sees The bigger win is keeping the value out of the model entirely. The agent sends the user a request link; the user pastes the value into a page that encrypts it in the browser to a key held only on the agent's machine; the local MCP server then writes it straight into a Vercel env var or .env file. The model only ever handles the link and the variable name. ### pricing The first 3 secrets per day are free (TTL up to 24h). After that each costs 0.005 USDC, paid over x402 from a local Base wallet: `npx ncrypted wallet new` (or the ncrypted_wallet_setup tool) creates it and returns a funding link. Reads, requests and deliveries are free. --- ## Claude Code https://ncrypted.ai/guides/claude-code Install the plugin: skill plus local MCP server. ``` /plugin marketplace add Seidl-Family/ncrypted /plugin install ncrypted@ncrypted ``` Then just ask: "send the staging DB password to Sam", "put my Stripe key in Vercel production", or paste an ncrypted link and ask Claude to add it to .env. The bundled skill teaches Claude when to use each tool. ### when to use it Use ncrypted whenever a credential has to move: to a person, to another agent, or into a Vercel env var or .env file. Never paste the value into chat, tickets or logs; share the one-time link instead. When you need a secret from the user, send them a request link (ncrypted_request) instead of asking them to paste it. ### pricing The first 3 secrets per day are free (TTL up to 24h). After that each costs 0.005 USDC, paid over x402 from a local Base wallet: `npx ncrypted wallet new` (or the ncrypted_wallet_setup tool) creates it and returns a funding link. Reads, requests and deliveries are free. --- ## Set Vercel env vars without the agent seeing them https://ncrypted.ai/guides/vercel Request a secret from the user and write it straight into a Vercel project. Works with any MCP client running the local server (Claude Code, Cursor, Codex, Gemini CLI). It uses your existing `vercel login` session, or VERCEL_TOKEN, and the project linked in .vercel/project.json unless you name one. Variables are created as sensitive for production and preview. ### with an agent ``` you: put my Stripe key in Vercel production agent: ncrypted_request("Stripe secret key for billing, Vercel production") -> https://ncrypted.ai/r/…#… (you open it and paste the key) agent: ncrypted_request_fulfill(id, destination: "vercel_env", key: "STRIPE_SECRET_KEY", targets: ["production"]) agent: STRIPE_SECRET_KEY is set on production. Redeploy to use it. ``` ### from the command line ``` npx ncrypted request "Stripe secret key for billing" # send the printed url to whoever has the key, then: npx ncrypted receive --vercel-key STRIPE_SECRET_KEY --target production # or, if someone already sent you an ncrypted link: npx ncrypted vercel-env "https://ncrypted.ai/s/…#…" --key STRIPE_SECRET_KEY --target production ``` ### safety Vercel access is checked before the one-time link is consumed. If the write still fails, the value is re-saved to a fresh one-time link that's returned in the error, so it isn't lost. This keeps secrets out of transcripts by default; it doesn't stop an agent that later runs `vercel env pull`. --- ## Vercel AI SDK https://ncrypted.ai/guides/ai-sdk Drop-in AI SDK tools: one-time links and Vercel env delivery, encrypted in your server process. ``` npm install ncrypted ai zod ``` ``` import { generateText } from "ai"; import { ncryptedTools } from "ncrypted/ai-sdk"; const { text } = await generateText({ model, // your model tools: ncryptedTools(), // ncrypted_create, ncrypted_read, ncrypted_status, ncrypted_burn, ncrypted_to_vercel_env system: "When a credential must be shared or installed, use the ncrypted tools. Never paste secrets into chat.", prompt: "Create a one-time link for the new admin password so I can send it to Sam.", }); ``` Encryption happens in your server process; ncrypted only stores ciphertext. Pick a subset with `ncryptedTools({ include: ["ncrypted_create", "ncrypted_read"] })`. `ncrypted_to_vercel_env` needs VERCEL_TOKEN and writes the value without the model ever seeing it. Payments come from NCRYPTED_WALLET_PRIVATE_KEY once the free tier is used up. ### via MCP instead ``` import { createMCPClient } from "@ai-sdk/mcp"; import { Experimental_StdioMCPTransport } from "@ai-sdk/mcp/mcp-stdio"; const ncrypted = await createMCPClient({ transport: new Experimental_StdioMCPTransport({ command: "npx", args: ["-y", "@seidl-family/ncrypted-mcp"] }), }); const tools = await ncrypted.tools(); // includes the request-a-secret tools; close() when done ``` ### pricing The first 3 secrets per day are free (TTL up to 24h). After that each costs 0.005 USDC, paid over x402 from a local Base wallet: `npx ncrypted wallet new` (or the ncrypted_wallet_setup tool) creates it and returns a funding link. Reads, requests and deliveries are free. --- ## OpenAI Agents SDK https://ncrypted.ai/guides/openai-agents Attach the ncrypted MCP server to an OpenAI agent. ``` npm install @openai/agents ``` ``` import { Agent, MCPServerStdio, run } from "@openai/agents"; const ncrypted = new MCPServerStdio({ command: "npx", args: ["-y", "@seidl-family/ncrypted-mcp"] }); await ncrypted.connect(); const agent = new Agent({ name: "Ops", instructions: "When a credential must be shared or installed, use the ncrypted tools. Never paste secrets into chat.", mcpServers: [ncrypted], }); try { const result = await run(agent, "Ask me for the Postgres URL and put it in Vercel preview as DATABASE_URL."); console.log(result.finalOutput); } finally { await ncrypted.close(); } ``` ### pricing The first 3 secrets per day are free (TTL up to 24h). After that each costs 0.005 USDC, paid over x402 from a local Base wallet: `npx ncrypted wallet new` (or the ncrypted_wallet_setup tool) creates it and returns a funding link. Reads, requests and deliveries are free. --- ## Mastra https://ncrypted.ai/guides/mastra Load the ncrypted tools into a Mastra agent. ``` npm install @mastra/core @mastra/mcp ``` ``` import { Agent } from "@mastra/core/agent"; import { MCPClient } from "@mastra/mcp"; const mcp = new MCPClient({ servers: { ncrypted: { command: "npx", args: ["-y", "@seidl-family/ncrypted-mcp"] } }, }); export const opsAgent = new Agent({ name: "ops", instructions: "When a credential must be shared or installed, use the ncrypted tools. Never paste secrets into chat.", model, // your model tools: await mcp.listTools(), }); ``` ### pricing The first 3 secrets per day are free (TTL up to 24h). After that each costs 0.005 USDC, paid over x402 from a local Base wallet: `npx ncrypted wallet new` (or the ncrypted_wallet_setup tool) creates it and returns a funding link. Reads, requests and deliveries are free. --- ## LangChain https://ncrypted.ai/guides/langchain ncrypted as LangChain tools, in Python or JS. ### python ``` pip install "ncrypted[langchain]" # add [pay] to pay beyond the free tier ``` ``` from ncrypted.langchain import ncrypted_tools tools = ncrypted_tools() # ncrypted_create, ncrypted_read, ncrypted_status, ncrypted_burn (sync + async) # pass to create_agent / bind_tools as usual ``` ### javascript (via MCP) ``` npm install @langchain/mcp-adapters ``` ``` import { MultiServerMCPClient } from "@langchain/mcp-adapters"; const client = new MultiServerMCPClient({ mcpServers: { ncrypted: { transport: "stdio", command: "npx", args: ["-y", "@seidl-family/ncrypted-mcp"] }, }, }); const tools = await client.getTools(); // pass to createAgent / bindTools as usual // ... await client.close(); ``` ### pricing The first 3 secrets per day are free (TTL up to 24h). After that each costs 0.005 USDC, paid over x402 from a local Base wallet: `npx ncrypted wallet new` (or the ncrypted_wallet_setup tool) creates it and returns a funding link. Reads, requests and deliveries are free. --- ## CrewAI https://ncrypted.ai/guides/crewai Give a CrewAI agent the ncrypted tools with the mcps field. ``` uv add crewai mcp ``` ``` from crewai import Agent from crewai.mcp import MCPServerStdio ops = Agent( role="Ops engineer", goal="Ship changes without leaking credentials", backstory="Uses ncrypted for every secret; never pastes secrets into chat.", mcps=[MCPServerStdio(command="npx", args=["-y", "@seidl-family/ncrypted-mcp"])], ) ``` The MCP server needs Node.js (npx). For a pure-Python setup, use the ncrypted package: `pip install "ncrypted[pay]"`, then `from ncrypted import NcryptedSync` inside your own CrewAI tool. ### pricing The first 3 secrets per day are free (TTL up to 24h). After that each costs 0.005 USDC, paid over x402 from a local Base wallet: `npx ncrypted wallet new` (or the ncrypted_wallet_setup tool) creates it and returns a funding link. Reads, requests and deliveries are free. --- ## HTTP API and x402 https://ncrypted.ai/guides/http-api Call ncrypted directly from any language. Encrypt client-side (AES-256-GCM, key = HKDF-SHA256 of a random 32-byte link key; see the reference implementation), then POST the ciphertext. Without a payment header you get the free tier while it lasts; after that the response is an x402 402 with payment requirements for 0.005 USDC on Base. ``` POST /api/v1/secrets {v:1, ciphertext, iv, salt, hasPassphrase, ttl?} -> {id, url, expiresAt, receiptToken} POST /api/v1/secrets/{id}/reveal -> payload (deleted atomically) GET /api/v1/quota -> {freeRemaining, price, ...} POST /api/v1/requests {publicKey, label, ttl?} -> {id, url, pollToken} POST /api/v1/requests/{id}/collect Bearer pollToken -> 202 | {sealed} ``` Full reference: /openapi.json and /llms.txt. Reference crypto: github.com/Seidl-Family/ncrypted/blob/main/packages/sdk/src/crypto.ts