# Why a blind-server vault can't issue dynamic secrets

> Dynamic secrets need a server that holds a privileged credential and uses it alone. A server that can't decrypt values can't, so Crunu doesn't offer them.

URL: https://crunu.com/blog/why-blind-server-vaults-skip-dynamic-secrets
Language: en
Updated: 2026-10-30

A dynamic secret is a credential the vault creates when someone asks for it, such as a database user that lives for an hour, and deletes when its lease ends. To do that, the vault's server has to hold a privileged credential of its own and use it whenever a request arrives, with no person present. A blind server, one that stores values only as ciphertext and has no key to decrypt them, can't hold such a credential in a form it can use. That's why Crunu doesn't offer dynamic secrets and won't: it would need a key on the server, which is the one thing the design rules out. What it offers instead is narrower: short-lived cloud credentials through the cloud's own OIDC, service tokens with leases and usage limits, and a checklist of what to reissue. If you need per-request database users, a vault whose server can decrypt, such as HashiCorp Vault, is the right tool.

This post explains the trade-off with the mechanics of each side. The guide on [who can decrypt your secrets](https://crunu.com/guides/who-can-decrypt-your-secrets) compares the wider market.

## What dynamic secrets are

HashiCorp Vault's database secrets engine is the reference example. It "generates database credentials dynamically based on configured roles", and applications use "Vault's leasing mechanism to more easily roll keys" ([Vault: database secrets engine](https://developer.hashicorp.com/vault/docs/secrets/databases)). The documentation's example role gives credentials a default lifetime of one hour and a maximum of 24. When a lease ends, Vault relies on "its own internal revocation system to ensure that users become invalid within a reasonable time of the lease expiring."

The benefits are real. Applications "no longer need to hardcode credentials", and because "every service is accessing the database with unique credentials, it makes auditing much easier": a query can be traced "to the specific instance of a service based on the SQL username." A leaked credential expires on its own, usually before anyone notices it leaked.

## What the vault needs to make them

The setup step shows the price. Vault is configured with a connection URL plus the `username` and `password` of a database account that "must have the appropriate permissions to perform actions upon other database users." The documentation calls it the "root" user, and recommends rotating its password right away "such that the vault user is not accessible by any users other than Vault itself."

Read that last sentence from the other side: after rotation, the only party that can use the database's most privileged account is the Vault server. It has to be, because it creates and drops users on demand, at any hour, for any authorized client. That only works if the server can read the root password whenever it needs to.

Vault's design is built for that. A Vault server starts sealed, able to read its storage but not decrypt it, and unsealing is "the process of obtaining the plaintext root key that is necessary to read the decryption key" ([Vault: seal/unseal](https://developer.hashicorp.com/vault/docs/concepts/seal)). From then on, the running server can decrypt what it stores in order to answer requests.

*Figure: Diagram of who can decrypt HashiCorp Vault values, split into your side and the provider side. On your side, your team’s devices, your machines, and CI runners can decrypt. On the provider side, the HashiCorp Vault server holds the key and can decrypt values too.*

## Why a blind server can't

The argument is short.

1. To create a database user, something has to log in to the database with a privileged credential.
2. For that to happen on request, with nobody present, the server answering requests has to be able to use that credential.
3. A credential the server can use is a credential the server can read. Whoever controls the server, an operator, an attacker with a database dump and the key, a compromised host, can read it too.
4. A blind server holds values only as ciphertext and has no key for any of them. It can store a root password, but it can't use one.

You could move the issuing step to a party that does hold the key: a person's browser, a machine running an agent. But then credentials are created only when that party is online and willing, which is no longer "on demand", and that party becomes the one holding root credentials for everyone else.

Other products show the same trade-off in their own settings. Phase can run an app end to end encrypted or with server-side encryption, and its documentation says that to use dynamic secrets, which it offers for AWS IAM on its Enterprise tier, "You need to Enable Server-side Encryption (SSE) for the App" ([Phase: dynamic secrets](https://docs.phase.dev/console/dynamic-secrets)). In the comparison data behind our guides, every product that offers dynamic secrets lets some server-side component decrypt: the vendor's service, your own server, or a gateway you run.

## What Crunu does instead

Crunu's server stores ciphertext, public keys, signed documents and metadata such as names. Each environment has its own key, sealed separately to every person and machine allowed to read it, and those keys open only on devices. Nothing in the server can log in anywhere on your behalf.

*Figure: A diagram of Crunu’s keys in six levels, with your device on the left and the server on the right. Your password and Secret Key stay on your device and are never sent. Together they make your account key, which never leaves the device either. It decrypts your own X25519 and Ed25519 keys, which the server holds only encrypted. Owners and admins also hold the organization’s signing and recovery keys, sealed to them. The organization’s signing key certifies who may receive each environment key. Every environment has its own key, and the server holds only sealed copies of it. Each version is encrypted with AES-256-GCM, padded to 4 KB and signed by its author, and the server holds only that ciphertext.*

That rules out dynamic secrets, but not short lifetimes in general. They come from elsewhere:

- **Cloud credentials from the cloud.** GitHub Actions, GitLab CI and Bitbucket Pipelines can exchange a job's OIDC token for temporary AWS, Google Cloud or Azure credentials directly with the cloud. The cloud's own token service, which already holds the keys, does the "dynamic" part. No vault has to sit in the middle. The guide to [CI/CD secrets with OIDC](https://crunu.com/guides/ci-cd-secrets-with-oidc) shows each pair.
- **Leases on access, not on values.** A Crunu [service token](https://crunu.com/learn/service-token) has a lease, an optional limit on how many times it can be used (from 1 to 1,000,000), optional network restrictions and an optional CI binding (OIDC) to a repository and branch. A machine's agent works offline only until its lease runs out, by default 24 hours, and after revocation it wipes the environments from memory, within minutes while the machine is online.
- **A list instead of an expiry.** Static values, such as a database password, stay static. When someone leaves, the exposure report shows which versions of which environments they received, and the reissue checklist lists the values still in use. You reissue them at the provider; Crunu doesn't do it for you.

Be clear about the gap. These measures narrow who can fetch a database password and for how long they can keep fetching it. They don't make the password itself expire. If a password leaks, it works until you change it.

The glossary entries on [dynamic secrets](https://crunu.com/learn/dynamic-secrets) and [ephemeral credentials](https://crunu.com/learn/ephemeral-credentials) separate the two ideas.

## When to choose a vault that can decrypt

Dynamic secrets are worth the trade-off when:

- many service instances share a database and you need to trace each query to one instance;
- an auditor or a policy requires credentials that expire per session or per deployment;
- you have the people to run the server: unsealing after restarts, storage, backups and upgrades. [Running Vault Community for a small team](https://crunu.com/blog/running-vault-community-small-team) covers what that involves.

In those cases, a Vault-style server is the right tool, and you accept that its server can decrypt what it stores. [Crunu vs HashiCorp Vault](https://crunu.com/alternatives/hashicorp-vault) compares the two in more detail, including where Vault is stronger.

They're less compelling when the credentials are third-party API keys that can't be issued per request anyway, when a single database has one application user, or when the team that would run the vault is also the team writing the application.

## Using both

The two models don't exclude each other. A team can let Vault issue database credentials for the services that need them, and keep the static values, such as third-party API keys, signing secrets, and the credentials an application uses to sign in to Vault, in a vault whose server can't read them. The question to ask for each value is the same: does this need to be created on demand, or does it need to be unreadable by the server that stores it? Few values need both.

## Questions to ask any vendor

- Which component can decrypt the credential used to create dynamic secrets?
- Is that component the same one that serves requests to every client?
- What happens to issued credentials if that component is compromised?
- Which values in your setup actually need to be created on demand?

## Sources

- [Database secrets engine, HashiCorp Vault documentation](https://developer.hashicorp.com/vault/docs/secrets/databases), checked October 30, 2026.
- [Seal/Unseal, HashiCorp Vault documentation](https://developer.hashicorp.com/vault/docs/concepts/seal), checked October 30, 2026.
- [Dynamic secrets, Phase Docs](https://docs.phase.dev/console/dynamic-secrets), checked October 30, 2026.
