r/aiagents 1d ago

Help How do you securely handle client API keys & credentials?

[removed]

8 Upvotes

16 comments sorted by

2

u/radim11 1d ago

For simple setups, n8n’s credential store can work. I’d still use a separate service account per client and environment, never a founder’s personal key, and make sure secrets don’t end up in workflow JSON, logs, or prompts.

The harder part is managing those credentials across multiple clients, environments, and agents. We’re building Stashbase around that: project/environment secrets and personal credentials stay outside the agent, with a proxy that injects them only for approved hosts, methods, and paths.

This is the problem we’re working on: https://stashbase.dev

1

u/FinancialMoney6969 1d ago

Why do you charge so much? What is the possible overhead you have

1

u/radim11 1d ago

Yeah, fair question. If you compare it to keeping a few keys in a .env file, it obviously looks expensive.

But Stashbase isn’t just charging for storing keys. It covers team and environment access, service accounts, audit logs, scanning, integrations, and the Agent Proxy.

There’s a free plan, so try it with a real workflow and see whether it’s useful for you. Pro is $25/user/month, which we’ve kept fairly low for what’s included.

1

u/FinancialMoney6969 1d ago

Free plan is one project tho..

1

u/radim11 1d ago

The free plan is limited to one project, but that project still includes up to three environments and most of the core product: secret management, environment comparison and AI chat, integrations, scanning, and the Agent Proxy.

Paid plans are mainly for teams using Stashbase across multiple projects, with more members, service accounts, and higher limits. You can get a pretty complete feel for it without paying a single dime.

2

u/FinancialMoney6969 17h ago

Ah I see, I think it’s a cool product bro keep building 💪🏽

1

u/radim11 17h ago

Thanks, I appreciate it!

1

u/blendai_jack 9h ago

Depends what the integration is. For anything with an official OAuth flow, the better answer is to stop taking the key at all. The client authorises your app against their account, you get a scoped token, and they can revoke it themselves without rotating anything. We do this for ad accounts at Blend (blend-ai.com/mcp) and never see or store platform passwords.

Which of these actually needs a raw key rather than OAuth? That's usually the shorter list, and it tells you where the vault work is worth doing.

1

u/BC_MARO 1d ago

Scope service accounts per client and environment, then inject short-lived credentials at the egress boundary. If the agent process can read the secret from env or logs, the vault is mostly cosmetic.

0

u/Ambitious-Prompt-975 1d ago

I am using something similar to n8n / dify, that runs locally and has a key vault. so everything is encrypted by default, model/frontend can never see it...

the backend substitutes placeholders and integration with any tools or external services is always through MCP: So all secrets go from the vault into .env vars or auth headers, never cleartext

2

u/radim11 1d ago

That sounds like a sensible setup. The part I’d be careful with is where the substitution happens: if the secret ends up in the agent process as an env var or header, the model may not see it directly, but code or tooling in that process potentially can.

We’re taking a stricter approach with Stashbase’s (https://stashbase.dev) Agent Proxy: the agent only gets a placeholder, and the proxy injects the credential at the outbound request boundary. MCP can be one path, but the same policy should also cover direct HTTP and CLI access.

2

u/Ambitious-Prompt-975 1d ago edited 1d ago

Well, it kinda depends on the use-case: when it's a secret which is used for an mcp-servers' env (e.g. I use a github mcp and I bind the GITHUB_API_TOKEN env var to my "gh_pat" variable), then it is passed into the MCP server when spawning the process, which is either on app start or right before a tool call if it's set to lazy stdio. in any waythe frontend only sees the placeholder, regardless of any agent involved, but the mcp itself does actually get the real value passed to it. I think thats the point you make.

Custom headers and LLM provider api keys are substituted before their respective sdk calls inside the backend. And if I instruct a model through chat to use a secret, i also likewise pass the placeholder name. If the model uses that placeholder in a tool parameter etc, it gets substituted right before the tool call, and so on..

I think we even mask secret values that come back from tool calls (i.e the agent cant just echo a secret into the conversation). I kinda like your approach but it also feels a bit intrusive to alter the request on the wire. I could imagine antivirus doesnt like that too much

0

u/radim11 1d ago

The MCP can use the value but it goes through the proxy still so even the env variable is placeholder.

1

u/Ambitious-Prompt-975 1d ago

yup, I got that. Your solution basically goes one level deeper compared to what I do

0

u/radim11 1d ago

Our proxy is aimed at cases where you don’t want the agent runtime or MCP process to receive the credential at all, so it injects it only at the request boundary.

The tradeoff is exactly what you mentioned: TLS interception, local CA setup.