Keep your secrets manager.
Keep real keys out of chat.
Keep your existing key storage and permissions. Give agents decoys, believable fakes, or references instead of live keys. Your trusted software uses the real key for authorised requests. Start with one tool integration.
Your existing proxy and deny.sh.
A proxy makes requests on an agent’s behalf without exposing its keys. Keep that protection. Add real and decoy control files only where you can keep them separate.
| Question | Existing proxy | With deny.sh alongside it |
|---|---|---|
| Agent cannot reach the key | A correctly enforced proxy keeps the key out of model context. | Keep that proxy. Resolve the real credential inside the trusted runtime. |
| A boundary fails | Exposure depends on what the failed boundary made reachable. | Only a decoy is exposed if the real control file and passwords remain separated. A runtime compromise can still expose the real key. |
| You need a detection signal | Provider and proxy logging depend on your setup. | An armed fingerprint observed at a monitored deny.sh decrypt endpoint can notify configured channels. Offline decrypts and provider-side tests are not automatically observed. |
| A value must be disclosed | Access control does not itself offer an alternate plaintext for a ciphertext. | The same ciphertext and password pair can yield a chosen decoy using a different control file. External evidence and provider validation can still distinguish it. |
| What the layer controls | Access to credentials and permitted operations. | The encrypted representation and decoy monitoring, alongside your access policy. Neither layer prevents all authorised-tool misuse. |
Keep the resolved credential inside your trusted runtime. Read the limits.
"We already have a secrets manager."
Keep it. Your existing system may already protect keys from agents through a proxy. deny.sh adds chosen decoy messages and optional monitored alerts where those features fit your needs.
Stores keys and controls access
Encryption at rest, access policies, rotation, leases. Your access policy decides which software may be reading the secret. Keep real keys out of agent-accessible tools and results.
- Encrypts stored secrets
- Restricts unauthorised access
- Supports key rotation
Adds decoys and monitored alerts
When your integration restricts the model to decoy material, a disclosure of its context exposes the decoy. The real credential stays in the trusted tool runtime. This does not protect against compromise of that runtime or misuse of an authorised tool.
- Keeps real credentials outside model context when integrated at the tool boundary
- Provides plausible decoy material; external evidence can still distinguish it
- Alerts when an armed decoy reaches a monitored decrypt endpoint
- Connects alerts to your rotation workflow. Revocation depends on your integration and credential provider
The one-line version
- Your secrets manager answers "who may retrieve or use this key?"
- deny.sh answers "what leaks when my own agent is hijacked?"
- Use both only where the extra capability meets a real requirement.
The threats each layer actually covers.
Not a teardown of your secrets manager. A map of where its job ends and deny.sh begins.
| Threat | Secrets manager (Vault, Doppler, AWS Secrets Manager, 1Password) |
deny.sh |
|---|---|---|
| Stolen backup / disk at rest | Depends on setup | Not its job |
| Unauthorised human access | Depends on setup | Not its job |
| Key rotation & leasing | Depends on setup | Not its job |
| Agent reads its own context aloud (prompt injection) | Depends on how credentials are resolved | Decoy only, if real keys stay in the trusted runtime |
| Forensic "prove which plaintext is real" | Not a deniability feature | Real and decoy plaintexts; external evidence still matters |
| Monitored decoy decrypt attempt | Access logs and configured alerts | Armed fingerprint observed at a monitored decrypt endpoint |
Use your secrets manager for storage and access controls. Use a correctly integrated deniable layer to keep real credentials outside model context, and connect monitored decrypt events to your response workflow.
deny.sh sits between your agent and the secret.
Your software retrieves and uses the real credential, returning only approved results to the model. Bring your own key (BYOK) can add AWS Key Management Service (KMS) protection to supported stored data.
Your secrets manager stays the system of record for the real credential, exactly as today.
deny.sh resolves it inside the tool boundary. The agent calls deny_decrypt(); the real key is fetched on the privileged side and used for the outbound call. It never enters the model's context.
Restrict the agent to the decoy. For web and core SDK encryption, both passwords are always required; the chosen control file selects the message. Monitoring is separate and requires a registered fingerprint at a monitored endpoint.
BYOK: control an added encryption layer
When BYOK is active, supported stored ciphertext (encrypted data) has an extra encryption layer using a data key protected by your AWS KMS customer-managed key. deny.sh calls only kms:GenerateDataKey and kms:Decrypt, assumes a role in your account with a non-optional sts:ExternalId guard, and cannot perform future unwrapping after key access is revoked. Copies already obtained cannot be recalled. Four-step BYOK walkthrough →
Drop deny.sh in next to what you run.
The credential keeps living in your secrets manager. You add one resolve call at the tool boundary. Here is the shape in the four places agent teams actually wire secrets.
Register the secret that holds the control payload; deny.sh reads it through your own ASM, in your account, on every resolve.
# keep the real key in ASM, as today
aws secretsmanager create-secret \
--name prod/stripe/live \
--secret-string "$STRIPE_KEY"
# register it with deny.sh as the custodian source
curl -X POST https://deny.sh/v1/integrations/secrets-manager/config \
-H "Authorization: Bearer $DENY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"secretArn": "arn:aws:secretsmanager:us-east-1:123…:secret:prod/stripe/live",
"iamRoleArn": "arn:aws:iam::123…:role/deny-sh-custodian",
"region": "us-east-1"
}'
Resolve inside execute(). The model receives the API result, never the key.
import { vaultGet } from "deny-sh/client"; // reads DENY_API_KEY from env
// inside your tool's execute()
const key = await vaultGet("stripe/live", process.env.VAULT_PW);
// real key used here, on the privileged side
const res = await fetch(stripeUrl, {
headers: { Authorization: `Bearer ${key}` }
});
return res.json(); // only this reaches the LLM
Keep keys in trusted workflow steps, away from untrusted code. A compromised step that can resolve the real credential can still leak it.
# .github/workflows/deploy.yml
- name: Resolve runtime credential
run: |
printf '%s' "$DENY_CTRL_HEX" | xxd -r -p > "$RUNNER_TEMP/real.ctrl"
STRIPE_KEY="$(deny-sh decrypt -i secrets/stripe.enc -c "$RUNNER_TEMP/real.ctrl" \
-p1 "$DENY_PW1" -p2 "$DENY_PW2")" # plaintext on stdout; never echo it
rm -f "$RUNNER_TEMP/real.ctrl"
STRIPE_KEY="$STRIPE_KEY" ./deploy.sh
env:
DENY_CTRL_HEX: ${{ secrets.DENY_REAL_CTRL_HEX }}
DENY_PW1: ${{ secrets.DENY_PW1 }}
DENY_PW2: ${{ secrets.DENY_PW2 }}
Keep your existing provider as the source of truth. Give the agent only decoy material; a leaked file containing the real key is still a credential leak.
# data source stays on your existing provider
data "vault_generic_secret" "stripe" {
path = "secret/prod/stripe"
}
# encrypt the provider value; keep stripe.ctrl in trusted storage only
resource "null_resource" "wrap" {
provisioner "local-exec" {
command = "printf '%s' \"$STRIPE\" | deny-sh encrypt -p1 \"$DENY_PW1\" -p2 \"$DENY_PW2\" -o secrets/stripe.enc -co secrets/stripe.ctrl"
environment = { STRIPE = data.vault_generic_secret.stripe.data["key"] }
}
}
Snippets are illustrative shapes. Runnable, CI-typechecked examples for each framework live at github.com/deny-sh-crypto/deny-sh-integrations.
Do I have to choose?
No. That's the entire point of this page.
Does deny.sh replace Vault / Doppler / AWS Secrets Manager?
No. Keep them. They remain your system of record and your at-rest protection. deny.sh adds decoy representations and optional monitored alerts. The agent exposes only a decoy if your integration keeps the real key out of its reach.
Where does the real credential actually live?
Wherever it lives today. With the AWS Secrets Manager custodian integration it stays in your ASM, in your account, and deny.sh resolves through it. When BYOK is active, supported stored records are protected by your AWS KMS key, so you can block future unwrapping by revoking access. Copies already obtained are not withdrawn.
What does my agent's model see?
The result of the privileged call, never the credential. The key is resolved inside the tool boundary and used on the privileged side; the model only ever receives the API response.
What's the smallest way to try this alongside what I have?
Grab a free API key, wrap one credential, and wire one tool call. Your secrets manager doesn't change. Test the successful request, a blocked request, error handling and logs before expanding.
What to know before production.
Access to the resolved credential defeats the separation. This is not a blanket fail-safe: protect the trusted runtime and test authorised-tool misuse as well as model-context exposure.