In this episode we'll cover the Transit Secrets Engine — encryption as an API service. Vault encrypts and decrypts sensitive data (credit cards, national IDs, health data) without ever storing the data. We'll practice key creation, encryption, decryption, and key rotation.

After covering Dynamic Database Secrets in episode 5 — temporary credentials that auto-destroy — this episode covers a topic that answers a different but equally critical question: how to secure the data itself, not just the credentials that access it. The answer: the Transit Secrets Engine, also known as Encryption-as-a-Service (EaaS).
Why does this topic matter in the real world? Think about applications that handle PII (Personally Identifiable Information): credit card numbers, national ID numbers, BPJS numbers, medical records, or email addresses. Regulations like GDPR, PCI-DSS, and Indonesia's PDP Act require sensitive data to be encrypted at rest. But here's the classic problem: where to store the encryption keys? If keys are stored in application code, in environment variables, or in config files — that's the same as putting the padlock key right next to the padlock.
Vault Transit radically changes this pattern: applications never see the encryption key at all. The application only sends data to Vault, Vault holds the key, then returns the result. Let's break down why this matters so much and how it works.
Technically, anyone can encrypt data in their applications — for example with an AES library in Node.js, Python, or Go. But the problem isn't the ability to encrypt, it's key management. When every application team encrypts on their own, here's what happens:
| Aspect | In-App Encryption | Encryption-as-a-Service |
|---|---|---|
| Key location | In app code / env vars | Centralized in Vault, apps never see it |
| Key rotation | Redeploy the app | One API command, without changing the app |
| Algorithm consistency | Depends on each team | Standard, centrally managed |
| Encryption usage audit | None | Complete via Vault audit logs |
| Secret knowledge exposure to developers | High | Zero — developers only need the transit path |
| Implementation complexity | Requires cryptography expertise | One HTTP call |
Important
The core principle behind EaaS: separate keys from data. Applications may, and indeed should, store encrypted data, but the encryption keys must live in a separate, centralized, controlled, and audited place. This is exactly the same philosophy as cloud KMS (Key Management Service) — but with the freedom to run on-premise and multi-cloud.
In short, the Transit workflow is:
vault write transit/encrypt/my-app-key plaintext=$(echo "..." | base64).vault write transit/decrypt/my-app-key ciphertext=....The key point that's often misunderstood: Vault never stores the data. Ciphertext lives in the application's database; Vault is just an encryption machine holding the keys. If you delete the data in the application's database, it's gone from the entire system — Vault has no copy.
vault secrets enable transitOutput:
Success! Enabled the transit secrets engine at: transit/Every use of Transit starts with a key. The key name is the only "address" the application needs to know:
vault write -f transit/keys/my-app-keyOutput:
Key Value
--- -----
allow_plaintext_backup false
auto_rotate_period 0s
deletion_allowed false
derived false
exportable false
keys map[1:map[creation_time:2026-08-02T09:00:00Z name:my-app-key version:1]]
latest_version 1
min_available_version 0
min_decryption_version 1
supports_decryption true
supports_derivation true
supports_encryption true
supports_signing true
type aes256-gcm96Notice a few important things:
latest_version: 1 — keys have a versioning system like KV. This version is crucial for rotation.type: aes256-gcm96 — the default key type, AES-256 with GCM (authenticated encryption). A very strong 256-bit key.supports_decryption: true — this key can be used for both encryption and decryption.Note
The -f flag in vault write -f transit/keys/my-app-key means "don't ask for any values" — because creating a key doesn't require extra arguments. This pattern is also used for rotation operations later.
Transit accepts plaintext input in base64 format. Why base64? Because encryption works on bytes, and base64 is the standard way to represent bytes (including binary or unicode character data) as text safe to send over an API.
vault write transit/encrypt/my-app-key plaintext=$(echo -n "NIK: 3273121234567890" | base64)Output:
Key Value
--- -----
ciphertext vault:v1:k2x9mQ8pLzV7nB4cX1dF6gH3jR5sT0wY...The resulting ciphertext has the format vault:v1:.... The vault: part marks the ciphertext's origin, and v1 is the key version used for encryption. This format matters because when the key is rotated, Vault can still decrypt old data encrypted with a previous key version.
vault write transit/decrypt/my-app-key ciphertext="vault:v1:k2x9mQ8pLzV7nB4cX1dF6gH3jR5sT0wY..."Output:
Key Value
--- -----
plaintext TklLOiAzMjczMTIxMjM0NTY3ODkwTo see the original data, decode the base64 result:
echo -n "TklLOiAzMjczMTIxMjM0NTY3ODkw" | base64 -dOutput:
NIK: 3273121234567890Let's summarize the entire encrypt-decrypt flow in one code-group for easy comparison:
vault write transit/encrypt/my-app-key \
plaintext=$(echo -n "NIK: 3273121234567890" | base64)This is Transit's main strength over in-app encryption: zero-downtime key rotation. When you rotate a key:
vault write -f transit/keys/my-app-key/rotateOutput:
Success! Data written to: transit/keys/my-app-key/rotateWhat happens behind the scenes:
2 (check with vault read transit/keys/my-app-key).vault read transit/keys/my-app-keyOutput:
Key Value
--- -----
latest_version 2
min_available_version 0
min_decryption_version 1
keys map[1:map[creation_time:2026-08-02T09:00:00Z name:my-app-key version:1] 2:map[creation_time:2026-08-02T10:00:00Z name:my-app-key version:2]]Notice that keys now contains both version 1 and 2. Version 1 isn't deleted — it's kept for decrypting old data. This is what's called key versioning.
To list all keys available on the transit mount:
vault list transit/keysOutput:
Keys
----
my-app-keyNow that we understand the primitives, let's see how this pattern is used in real applications. The most common flow is called encrypt-at-write, decrypt-at-read: when the application stores PII in the database, the data is first encrypted via Vault; when the data is read back for display, the application decrypts it via Vault. The database only stores ciphertext.
vault write transit/encrypt/my-app-key \
plaintext=$(echo -n "arman@devvnull.local" | base64)From the application's side, the logic is simple: call the Vault API twice — once when storing, once when displaying. The remarkable part: the application never knows its encryption key, and sensitive data is never stored in its original form in the database. If the database leaks, what leaks is only ciphertext — useless without Vault.
After rotation, old ciphertext is still encrypted with version 1. For optimal security, old data should be re-encrypted to the new version — removing dependence on the old key. This process is called re-wrapping:
vault write transit/rewrap/my-app-key \
ciphertext="vault:v1:k2x9mQ8pLzV7nB4cX1dF6gH3jR5sT0wY..."Output:
Key Value
--- -----
ciphertext vault:v2:rT4uW0xYqN9sL7bVc2eF5gH8jK1mP0nZ...Notice how the v1 in the input ciphertext becomes v2 in the output. The same data, but now encrypted with key version 2 — without ever exposing the plaintext to the application. This is why rewrap is safer than decrypt-then-encrypt: the data is never "opened" inside the application.
Tip
For keys with routine rotation (for example, a 90-day rotation policy), combine auto_rotate_period when creating the key — e.g. vault write transit/keys/app period="8760h" for automatic yearly rotation. Vault will rotate the key periodically without manual intervention, and thanks to versioning, all old ciphertext remains decryptable.
To ground this, here are scenarios where Transit is used in the real world:
| Scenario | Data Encrypted | Why Transit Fits |
|---|---|---|
| E-commerce / Payment gateway | Credit card numbers, CVV (with tokenization) | Meets PCI-DSS, key rotation without downtime |
| Fintech & Banking | National ID numbers, account numbers, transaction balances | PDP Act, audit trail of encryption usage |
| Hospitals / Health-tech | Medical records, lab results, patient data | Health data is highly sensitive, at-rest encryption required |
| Multi-tenant SaaS | Customer API keys, PII | Per-tenant encryption with different keys (derived + context) |
| E-commerce apps | Addresses, customer phone numbers | Compliance & trust |
Warning
It's important to understand Transit's boundaries: Transit doesn't store data and doesn't replace the database or storage function. It's just an encryption machine. For large volumes of data, send it to Transit in batches; for needs requiring centralized secret storage, use KV. Don't confuse "encrypting data" (Transit) with "storing secrets" (KV) — they're often thought of as the same, yet their architectural roles differ.
| Mistake | Symptom | Solution |
|---|---|---|
| Not encoding plaintext to base64 | Error: plaintext is required / corrupted data | Always `echo -n "..." |
| Storing plaintext in Vault "to be safe" | Data ends up scattered in many places | Ciphertext in the app database, plaintext not stored |
| Forgetting that old ciphertext remains valid after rotation | Panic when decrypting old data | That's a feature! Old versions are kept for decryption |
| Assuming rewrap = decrypt | Trying to decrypt first, then encrypt again | Use rewrap so plaintext never leaves |
| Using the same key for all tenants | One app leak = all data readable | Consider derived keys + per-app context |
| Never rotating keys | Risk if one key leaks | Schedule routine rotation (manual / auto_rotate_period) |
In episode 6, we've covered the Transit Secrets Engine (Encryption-as-a-Service): why applications shouldn't manage their own encryption keys, how Transit works as an encryption machine via API, the full create key → encrypt → decrypt flow, and the key rotation and re-wrap mechanisms that allow key changes without downtime and without exposing plaintext.
The essence of this episode: Vault Transit separates keys from data — the application manages data, Vault manages keys. With this, compliance with regulations like PCI-DSS and the PDP Act becomes far easier to meet, because at-rest encryption is done by keys that are centralized, audited, and easy to rotate.
In episode 7, we'll cover the secrets engine that protects the communication layer: the PKI Secrets Engine — how Vault acts as an automatic Certificate Authority, issuing short-lived TLS certificates for internal services, and solving the classic problem of certificates expiring without anyone knowing. Keep your enthusiasm up!