feat(infra): add Spring Cloud Vault secrets management (STA-219)#236
Conversation
…vices (STA-219) Integrate HashiCorp Vault via Spring Cloud Vault 5.0.1 for centralised secrets management. Vault is opt-in (app.vault.enabled=false by default) so existing env-var workflow and all tests continue to work unchanged. - Add spring-cloud-starter-vault-config to platform-infra - Add VaultConfig with @ConditionalOnProperty toggle (matchIfMissing=false) - Add optional:vault:// ConfigData import to all 10 service application.yml - Configure KV v2 backend with per-service and shared (application) contexts - Add vault-init Docker Compose service that seeds dev secrets on startup - Add init-vault.sh script seeding DB creds, API keys, JWT keys per service Closes STA-219 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WalkthroughAdds Spring Cloud Vault support and config.import entries to multiple services, disables Vault in integration-test profiles, consolidates Kafka binder/bindings in several services, adds a vault-init service to docker-compose.dev, and introduces a conditional Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…est configs Fixes duplicate YAML key errors in CI where integration test application.yml files that already had spring.cloud.stream conflicted with the new spring.cloud.vault block from the main application.yml. Also explicitly disables Vault in all IT configs to prevent connection attempts in CI where no Vault instance is running. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
merchant-iam/merchant-iam/src/main/resources/application.yml (1)
92-96:⚠️ Potential issue | 🟠 MajorEnable Vault for JWT secrets and align the property path.
Vault integration exists but is disabled (
app.vault.enabled=falseby default with no profile overrides). The JWT secrets are seeded in Vault atsecret/merchant-iambut the application falls back to${JWT_PRIVATE_KEY_BASE64:}environment variables at runtime. Per PR objectives and merchant-iam security requirements, either:
- Set
app.vault.enabled=truein the appropriate active profile, or- Remove Vault configuration if not yet production-ready and rely solely on environment variables.
Additionally, verify that
merchant-iam.jwt.private-key-base64resolves correctly given the KV default context isapplicationbut secrets are stored under themerchant-iampath.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@merchant-iam/merchant-iam/src/main/resources/application.yml` around lines 92 - 96, The JWT config is falling back to empty env vars because Vault is disabled and the property path may not match where secrets live; either enable Vault for this service by setting app.vault.enabled=true in the active profile (so merchant-iam.jwt.private-key-base64 reads from secret/merchant-iam) or remove/stop referencing Vault and ensure the env var JWT_PRIVATE_KEY_BASE64 is provided; also verify the property key merchant-iam.jwt.private-key-base64 (and fields private-key-base64, issuer, audience, access-token-ttl-seconds, refresh-token-ttl-seconds) resolves against the Vault KV path secret/merchant-iam (adjust context or secret path mapping if Vault’s default context is application).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@api-gateway-iam/api-gateway-iam/src/main/resources/application.yml`:
- Line 13: The YAML currently sets the API gateway transport to an insecure
scheme ("scheme: http"); change this to "scheme: https" and enable/configure TLS
for the gateway by updating the same configuration block that contains "scheme"
(ensure certificate/key or keystore references are provided and any
corresponding ports/redirect settings are adjusted accordingly), and verify
related properties (e.g., "port", "tls", "keystore" or "ssl" entries) are
present so the IAM gateway serves over HTTPS rather than plain HTTP.
In `@blockchain-custody/blockchain-custody/src/main/resources/application.yml`:
- Around line 13-15: Current Vault config uses plaintext HTTP and a hardcoded
fallback dev token; change the configuration entries so the scheme is
configurable with a secure default and the token has no default fallback:
replace the literal scheme value with a reference to VAULT_SCHEME and default it
to "https" (so production uses HTTPS unless explicitly overridden), and remove
the dev fallback for VAULT_TOKEN so the token config is required (e.g., token
should reference ${VAULT_TOKEN} with no default). Update any code or deployment
docs to instruct local dev to explicitly set VAULT_SCHEME=http and
VAULT_TOKEN=dev-root-token in docker-compose/.env when needed.
In
`@compliance-travel-rule/compliance-travel-rule/src/main/resources/application.yml`:
- Line 13: The configuration entry "scheme: http" exposes Vault communication
over plaintext; change it to "scheme: https" and enable TLS by configuring Vault
TLS parameters (trust store/CA, client certs if mutual TLS is required, and
hostname verification) for the compliance service so Vault connections are
encrypted and validated; update the Vault-related config block (the "scheme"
entry and any Vault client settings used by the compliance-travel-rule service)
to reference the HTTPS endpoint and provide the appropriate keystore/truststore
or CA bundle and TLS verification options.
In `@fiat-off-ramp/fiat-off-ramp/src/main/resources/application.yml`:
- Line 13: The YAML uses a hardcoded scheme value ("scheme: http"); change it to
use an environment-backed default like the pattern shown in ledger-accounting
(e.g., replace the literal with a property substitution such as scheme:
${VAULT_SCHEME:https}) so the scheme can be configured via VAULT_SCHEME and
defaults to https; update the application.yml entry for the scheme key
accordingly.
In `@fiat-on-ramp/fiat-on-ramp/src/main/resources/application.yml`:
- Line 13: The config entry "scheme: http" exposes insecure plain-HTTP; change
the value to "https" (or make it configurable via an environment variable) and
ensure the corresponding TLS configuration/certificates are provided so the
service uses HTTPS; update the same config block where "scheme" is defined to
use "https" (or reference an env var like ${SCHEME:https}) and adjust any
server/ingress TLS settings accordingly.
In `@fx-liquidity-engine/fx-liquidity-engine/src/main/resources/application.yml`:
- Line 13: The Vault `scheme` setting currently defaults to `http` which risks
sending tokens in plaintext; update the `scheme` key in application.yml to
default to HTTPS using an environment override (e.g., set `scheme` to
`${VAULT_SCHEME:https}`) or add a clear comment / documentation that non-local
deployments must set VAULT_SCHEME=https and enable TLS, ensuring the `scheme`
key and any Vault client initialization honor this env var.
In `@ledger-accounting/ledger-accounting/src/main/resources/application.yml`:
- Line 13: Replace the hardcoded "scheme: http" property for the Vault client
with an environment-configurable property (e.g., read from VAULT_SCHEME with a
secure default of "https") so the Vault connection can be TLS-enabled in
production; update the configuration key where "scheme" is defined (the Vault
client configuration entry named "scheme") to reference the environment variable
and ensure deployment manifests/README set VAULT_SCHEME as needed for
non-default environments.
In `@merchant-iam/merchant-iam/src/main/resources/application.yml`:
- Line 13: The configuration currently sets Vault/transport scheme to "http"
which is insecure; update the application.yml entry (the "scheme" key) to
"https" and ensure the IAM service's Vault client configuration (where "scheme"
is referenced) uses TLS — validate that the Vault URL, any "host" and "port"
settings, and TLS-related options (certificate truststore/ca, verify_peer flags)
are present and configured so the service connects over HTTPS with certificate
verification enabled (adjust any Vault client initialization or environment
overrides that reference scheme to avoid falling back to http).
In `@merchant-onboarding/merchant-onboarding/src/main/resources/application.yml`:
- Line 13: Update the hardcoded scheme value for the Vault configuration by
replacing the literal "http" for the scheme key with an
environment-variable-backed default like VAULT_SCHEME that falls back to
"https"; specifically modify the scheme entry (the "scheme:" key in
application.yml) to use the pattern used in ledger-accounting (e.g., scheme:
${VAULT_SCHEME:https}) so the service defaults to HTTPS but remains configurable
via VAULT_SCHEME.
In
`@payment-orchestrator/payment-orchestrator/src/main/resources/application.yml`:
- Line 13: The Vault client config in application.yml currently sets scheme:
http which is insecure for production; change the Vault scheme value to https
(replace scheme: http with scheme: https) and ensure the Vault URL/address uses
the https:// form and that TLS settings are provided (truststore/keystore or JVM
trust configuration and any required client certificates/mTLS settings) so the
application establishes TLS when connecting to Vault (look for the Vault-related
properties and any VaultClient/VaultTemplate configuration to verify TLS
settings are applied).
---
Outside diff comments:
In `@merchant-iam/merchant-iam/src/main/resources/application.yml`:
- Around line 92-96: The JWT config is falling back to empty env vars because
Vault is disabled and the property path may not match where secrets live; either
enable Vault for this service by setting app.vault.enabled=true in the active
profile (so merchant-iam.jwt.private-key-base64 reads from secret/merchant-iam)
or remove/stop referencing Vault and ensure the env var JWT_PRIVATE_KEY_BASE64
is provided; also verify the property key merchant-iam.jwt.private-key-base64
(and fields private-key-base64, issuer, audience, access-token-ttl-seconds,
refresh-token-ttl-seconds) resolves against the Vault KV path
secret/merchant-iam (adjust context or secret path mapping if Vault’s default
context is application).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: aaf0d2bf-686d-41b7-a8e7-1c0e4eb1bdfc
⛔ Files ignored due to path filters (1)
infra/local/vault/init-vault.shis excluded by!infra/local/**
📒 Files selected for processing (25)
api-gateway-iam/api-gateway-iam/src/main/resources/application-integration-test.ymlapi-gateway-iam/api-gateway-iam/src/main/resources/application.ymlblockchain-custody/blockchain-custody/src/integration-test/resources/application-integration-test.ymlblockchain-custody/blockchain-custody/src/main/resources/application.ymlcompliance-travel-rule/compliance-travel-rule/src/integration-test/resources/application-integration-test.ymlcompliance-travel-rule/compliance-travel-rule/src/main/resources/application.ymldocker-compose.dev.ymlfiat-off-ramp/fiat-off-ramp/src/integration-test/resources/application-integration-test.ymlfiat-off-ramp/fiat-off-ramp/src/main/resources/application.ymlfiat-on-ramp/fiat-on-ramp/src/integration-test/resources/application-integration-test.ymlfiat-on-ramp/fiat-on-ramp/src/main/resources/application.ymlfx-liquidity-engine/fx-liquidity-engine/src/integration-test/resources/application-integration-test.ymlfx-liquidity-engine/fx-liquidity-engine/src/main/resources/application.ymlledger-accounting/ledger-accounting/src/integration-test/resources/application-integration-test.ymlledger-accounting/ledger-accounting/src/main/resources/application.ymlmerchant-iam/merchant-iam/src/integration-test/resources/application-integration-test.ymlmerchant-iam/merchant-iam/src/main/resources/application-integration-test.ymlmerchant-iam/merchant-iam/src/main/resources/application.ymlmerchant-onboarding/merchant-onboarding/src/integration-test/resources/application-integration-test.ymlmerchant-onboarding/merchant-onboarding/src/main/resources/application.ymlpayment-orchestrator/payment-orchestrator/src/integration-test/resources/application-integration-test.ymlpayment-orchestrator/payment-orchestrator/src/main/resources/application.ymlplatform-infra/build.gradle.ktsplatform-infra/src/main/java/com/stablecoin/payments/platform/infrastructure/vault/VaultConfig.javaplatform-infra/src/test/java/com/stablecoin/payments/platform/infrastructure/vault/VaultConfigTest.java
| enabled: ${app.vault.enabled:false} | ||
| host: ${VAULT_HOST:localhost} | ||
| port: ${VAULT_PORT:8200} | ||
| scheme: http |
There was a problem hiding this comment.
Security: Same scheme: http concern.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@api-gateway-iam/api-gateway-iam/src/main/resources/application.yml` at line
13, The YAML currently sets the API gateway transport to an insecure scheme
("scheme: http"); change this to "scheme: https" and enable/configure TLS for
the gateway by updating the same configuration block that contains "scheme"
(ensure certificate/key or keystore references are provided and any
corresponding ports/redirect settings are adjusted accordingly), and verify
related properties (e.g., "port", "tls", "keystore" or "ssl" entries) are
present so the IAM gateway serves over HTTPS rather than plain HTTP.
| enabled: ${app.vault.enabled:false} | ||
| host: ${VAULT_HOST:localhost} | ||
| port: ${VAULT_PORT:8200} | ||
| scheme: http |
There was a problem hiding this comment.
Security: Same scheme: http concern.
Compliance service handles sensitive data. TLS for Vault communication is especially critical here.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@compliance-travel-rule/compliance-travel-rule/src/main/resources/application.yml`
at line 13, The configuration entry "scheme: http" exposes Vault communication
over plaintext; change it to "scheme: https" and enable TLS by configuring Vault
TLS parameters (trust store/CA, client certs if mutual TLS is required, and
hostname verification) for the compliance service so Vault connections are
encrypted and validated; update the Vault-related config block (the "scheme"
entry and any Vault client settings used by the compliance-travel-rule service)
to reference the HTTPS endpoint and provide the appropriate keystore/truststore
or CA bundle and TLS verification options.
…ck (STA-219) Addresses CodeRabbit review: scheme defaults to HTTPS for production safety, token has no fallback to prevent accidental auth with dev-root-token. Local dev sets VAULT_SCHEME=http and VAULT_TOKEN=dev-root-token via docker-compose. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@api-gateway-iam/api-gateway-iam/src/main/resources/application.yml`:
- Around line 5-7: Add explicit fail-fast and configurable import for Vault:
update application.yml and related VaultConfig.java expectations so that
spring.cloud.vault.fail-fast is set via an env override (VAULT_FAIL_FAST) and
spring.config.import is driven by VAULT_CONFIG_IMPORT; specifically ensure the
default import remains optional:vault:// for local/dev but allow
VAULT_CONFIG_IMPORT to switch to vault:// (non-optional) and have
spring.cloud.vault.fail-fast=${VAULT_FAIL_FAST:true} so production/staging can
set VAULT_FAIL_FAST=true and VAULT_CONFIG_IMPORT=vault:// to enforce strict
Vault validation when app.vault.enabled=true.
In
`@compliance-travel-rule/compliance-travel-rule/src/main/resources/application.yml`:
- Around line 5-13: Change the Vault configuration to fail-closed by removing
the optional import and enabling Vault by default: replace "config.import:
optional:vault://" with a hard import "config.import: vault://", and change
"cloud.vault.enabled: ${app.vault.enabled:false}" to enable by default (e.g.,
"${app.vault.enabled:true}" or simply "true"); apply the same changes to the
corresponding entries in application-local.yml and
application-integration-test.yml so the service fails startup if Vault is
unavailable rather than silently falling back to defaults.
- Around line 16-19: The YAML currently configures only the KV secrets backend
(kv.backend: secret) and leaves DB credentials wired to env fallbacks
(DB_USER/DB_PASS) instead of using Vault dynamic DB credentials; update the
spring.cloud.vault configuration to enable the Vault database secrets engine and
point Spring Cloud Vault at it so that spring.datasource.username and
spring.datasource.password are populated from Vault’s database backend (ensure a
PostgreSQL plugin-mounted database engine and a compliance-travel-rule role
exist in Vault); specifically replace or augment the kv-only config by adding
database backend settings under spring.cloud.vault and map the database role so
Spring Cloud Vault (2025.1.1) can rotate and inject credentials rather than
using DB_USER/DB_PASS.
In `@fiat-off-ramp/fiat-off-ramp/src/main/resources/application.yml`:
- Around line 5-7: The current static YAML uses config.import: optional:vault://
and hardcoded vault settings (vault.enabled: false and auth method TOKEN);
update configuration to make the import mode and auth backend
environment-driven: replace the fixed config.import with a value sourced from an
environment variable (e.g., defaulting to optional:vault:// but allowing
required:vault:// when an env var indicates production), make vault.enabled
driven by an environment variable instead of the literal false, and make the
vault auth backend (the TOKEN setting) configurable via an environment variable
(e.g., vault.auth.method) so KUBERNETES/APPROLE can be selected without code
changes; adjust any code or startup config that reads these keys (config.import,
vault.enabled, vault.auth.*) to respect the new env-driven values.
- Around line 14-15: Change the hardcoded Vault authentication by making the
authentication property environment-configurable and supporting AppRole as an
alternative: replace the static "authentication: TOKEN" with an env-backed value
like "authentication: ${VAULT_AUTHENTICATION:TOKEN}" and ensure token remains
env-driven ("token: ${VAULT_TOKEN:}"); update any Vault-related bootstrap (e.g.,
VaultConfig) to branch on the authentication value and load AppRole fields
(role_id/secret_id) when authentication == "APPROLE". Apply the same pattern
across all microservices using Vault.
In `@fiat-on-ramp/fiat-on-ramp/src/main/resources/application.yml`:
- Around line 5-19: Make Vault mandatory and allow non-token auth by updating
the YAML keys: change config.import from optional:vault:// to vault://, set
spring.cloud.vault.fail-fast=true, set app.vault.enabled=true (or remove the
default false), and replace hardcoded cloud.vault.authentication: TOKEN with a
configurable value sourced from an environment variable (e.g.,
CLOUD_VAULT_AUTH_METHOD or VAULT_AUTH_METHOD) so production can use KUBERNETES
or APPROLE; ensure these values can be overridden per profile via env vars like
VAULT_CONFIG_IMPORT, VAULT_FAIL_FAST, app.vault.enabled, and VAULT_AUTH_METHOD.
In `@ledger-accounting/ledger-accounting/src/main/resources/application.yml`:
- Around line 20-24: The application.yml contains an unused Spring Cloud Stream
Kafka binder section (stream.kafka.binder) that is ignored because the code uses
Spring Kafka (spring.kafka.bootstrap-servers and `@KafkaListener/KafkaTemplate`);
remove the entire stream.kafka.binder block to avoid confusion and ensure
developers edit spring.kafka.bootstrap-servers instead; search for the keys
"stream.kafka.binder" and "spring.kafka.bootstrap-servers" and delete the binder
configuration while keeping the existing spring.kafka.bootstrap-servers entry
intact.
In `@merchant-iam/merchant-iam/src/main/resources/application.yml`:
- Around line 5-10: Current application.yml uses optional Vault import and a
default disabled flag (config.import: optional:vault:// and cloud.vault.enabled:
${app.vault.enabled:false}); create an application-prod.yml profile that
overrides these keys to enforce Vault in production by setting config.import:
vault:// (remove "optional:") and cloud.vault.enabled: true so the service fails
closed when Vault is unavailable—update the Spring profile activation for prod
as needed and ensure the production deployment uses the "prod" profile.
In `@merchant-onboarding/merchant-onboarding/src/main/resources/application.yml`:
- Around line 6-15: The application.yml currently uses the optional:vault://
import and an empty default VAULT_TOKEN which undermines fail-closed behavior;
update application.yml to either replace the import prefix optional:vault://
with mandatory:vault:// OR add spring.cloud.vault.fail-fast=true under
cloud.vault when app.vault.enabled=true, and remove the empty default for token
(use token: ${VAULT_TOKEN} without a fallback) so a missing token fails startup;
also add a short comment in VaultConfig.java documenting that
fail-fast/mandatory import is required in enabled environments and that
VAULT_TOKEN must be provided.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 36f5591f-f457-4543-9823-516254286113
📒 Files selected for processing (10)
api-gateway-iam/api-gateway-iam/src/main/resources/application.ymlblockchain-custody/blockchain-custody/src/main/resources/application.ymlcompliance-travel-rule/compliance-travel-rule/src/main/resources/application.ymlfiat-off-ramp/fiat-off-ramp/src/main/resources/application.ymlfiat-on-ramp/fiat-on-ramp/src/main/resources/application.ymlfx-liquidity-engine/fx-liquidity-engine/src/main/resources/application.ymlledger-accounting/ledger-accounting/src/main/resources/application.ymlmerchant-iam/merchant-iam/src/main/resources/application.ymlmerchant-onboarding/merchant-onboarding/src/main/resources/application.ymlpayment-orchestrator/payment-orchestrator/src/main/resources/application.yml
| config: | ||
| import: optional:vault:// | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Checking for strict Vault overrides in runtime/deployment configs..."
rg -n --hidden -g '!**/.git/**' \
-e 'optional:vault://' \
-e '\bvault://\b' \
-e 'VAULT_CONFIG_IMPORT|SPRING_CONFIG_IMPORT' \
-e 'spring\.cloud\.vault\.fail-fast|VAULT_FAIL_FAST' \
-e 'app\.vault\.enabled'Repository: Puneethkumarck/stablebridge-platform
Length of output: 3600
🏁 Script executed:
cat api-gateway-iam/api-gateway-iam/src/main/resources/application.yml | head -25Repository: Puneethkumarck/stablebridge-platform
Length of output: 614
🏁 Script executed:
cat -n platform-infra/src/main/java/com/stablecoin/payments/platform/infrastructure/vault/VaultConfig.java | head -60Repository: Puneethkumarck/stablebridge-platform
Length of output: 2075
🏁 Script executed:
find . -type f \( -name "*.yml" -o -name "*.yaml" -o -name "application-*.properties" \) | xargs grep -l "VAULT_CONFIG_IMPORT\|spring\.config\.import" 2>/dev/null | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
grep -r "fail-fast\|failFast" . --include="*.yml" --include="*.yaml" --include="*.properties" 2>/dev/null | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 129
🏁 Script executed:
fd -e yml -e yaml -o -exec grep -l "app\.vault\.enabled.*true" {} \; 2>/dev/null | head -10Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
Vault configuration lacks fail-fast safety across all microservices.
The optional:vault:// pattern is systemic across 10+ services and intentionally documented in VaultConfig.java. However, it creates an unaddressed gap: when operators set app.vault.enabled=true, they expect strict Vault validation, but the optional import allows silent fallback to default/environment credentials if Vault is unavailable. This conflicts with the @ConditionalOnProperty guard and exposes secrets-management drift in production-like environments.
Spring Cloud Vault does not enable spring.cloud.vault.fail-fast by default. Add explicit fail-fast configuration and environment variable override for production deployments across all services:
spring.cloud.vault.fail-fast=${VAULT_FAIL_FAST:true}
spring.config.import=${VAULT_CONFIG_IMPORT:optional:vault://}Set VAULT_FAIL_FAST=true and VAULT_CONFIG_IMPORT=vault:// (non-optional) in production and staging environments to enforce strict Vault dependency when app.vault.enabled=true.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@api-gateway-iam/api-gateway-iam/src/main/resources/application.yml` around
lines 5 - 7, Add explicit fail-fast and configurable import for Vault: update
application.yml and related VaultConfig.java expectations so that
spring.cloud.vault.fail-fast is set via an env override (VAULT_FAIL_FAST) and
spring.config.import is driven by VAULT_CONFIG_IMPORT; specifically ensure the
default import remains optional:vault:// for local/dev but allow
VAULT_CONFIG_IMPORT to switch to vault:// (non-optional) and have
spring.cloud.vault.fail-fast=${VAULT_FAIL_FAST:true} so production/staging can
set VAULT_FAIL_FAST=true and VAULT_CONFIG_IMPORT=vault:// to enforce strict
Vault validation when app.vault.enabled=true.
| config: | ||
| import: optional:vault:// | ||
|
|
||
| cloud: | ||
| vault: | ||
| enabled: ${app.vault.enabled:false} | ||
| host: ${VAULT_HOST:localhost} | ||
| port: ${VAULT_PORT:8200} | ||
| scheme: ${VAULT_SCHEME:https} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify prod/profile overrides do not keep Vault optional/disabled.
fd 'application*.yml' -x rg -n -C2 'optional:vault://|app\.vault\.enabled|spring\.cloud\.vault\.enabled|spring\.config\.import'Repository: Puneethkumarck/stablebridge-platform
Length of output: 1999
🏁 Script executed:
#!/bin/bash
# List all application*.yml files to identify profile strategy
echo "=== All application*.yml files ==="
find . -name 'application*.yml' -type f | head -20
echo ""
echo "=== Checking for prod/production profile files ==="
find . -name 'application-prod*.yml' -o -name 'application-production*.yml' | head -10
echo ""
echo "=== Searching for app.vault.enabled=true or import:vault:// (non-optional) ==="
rg -n 'app\.vault\.enabled\s*:\s*true|^[\s]*import:\s*vault://' --type yamlRepository: Puneethkumarck/stablebridge-platform
Length of output: 1935
Vault configuration violates fail-closed principle for compliance service.
Lines 6 and 10 use optional:vault:// and default enabled:false, allowing startup without Vault. No production profile overrides this unsafe posture. For AML/sanctions screening, the service must fail startup if Vault is unavailable; silent fallback to default credentials violates compliance requirements.
Required hardening
spring:
config:
- import: optional:vault://
+ import: vault://
cloud:
vault:
- enabled: ${app.vault.enabled:false}
+ enabled: ${app.vault.enabled:true}Apply the same fix to application-local.yml and application-integration-test.yml (use non-optional import; enable by default).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@compliance-travel-rule/compliance-travel-rule/src/main/resources/application.yml`
around lines 5 - 13, Change the Vault configuration to fail-closed by removing
the optional import and enabling Vault by default: replace "config.import:
optional:vault://" with a hard import "config.import: vault://", and change
"cloud.vault.enabled: ${app.vault.enabled:false}" to enable by default (e.g.,
"${app.vault.enabled:true}" or simply "true"); apply the same changes to the
corresponding entries in application-local.yml and
application-integration-test.yml so the service fails startup if Vault is
unavailable rather than silently falling back to defaults.
| kv: | ||
| enabled: true | ||
| backend: secret | ||
| default-context: application |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For the Spring Cloud Vault version used in this repository, what are the exact YAML properties to enable PostgreSQL dynamic credentials and map them to Spring datasource username/password?
💡 Result:
To enable PostgreSQL dynamic credentials in Spring Cloud Vault and map them to the Spring datasource username/password, use the following YAML properties under the spring.cloud.vault section in your application.yml or bootstrap.yml (depending on your Spring Boot version and bootstrap configuration). Key properties: - spring.cloud.vault.database.enabled: true - Enables the generic database backend (recommended for PostgreSQL). - spring.cloud.vault.database.role: - Specifies the Vault database role that defines the PostgreSQL credentials generation (e.g., 'myapp-role'). Optional but recommended for mapping: - spring.cloud.vault.database.username-property: spring.datasource.username - spring.cloud.vault.database.password-property: spring.datasource.password Optional for custom backend path (if Vault database is mounted at a non-default path like 'database/'): - spring.cloud.vault.database.backend: database Example YAML configuration: spring: config: import: optional:vault:// cloud: vault: database: enabled: true role: myapp-postgresql-role backend: database # Optional, default 'database' username-property: spring.datasource.username password-property: spring.datasource.password # Vault connection config (token, uri, etc.) uri: https://vault.example.com:8200 token: ${VAULT_TOKEN} # Or use other auth methods scheme: https # Skip SSL verification if using self-signed certs (not for prod) ssl: trust-store: classpath:vault-truststore.jks skip-verify: false Dependencies required: - spring-cloud-starter-vault-config - spring-cloud-vault-config-databases (for database support) - PostgreSQL JDBC driver: org.postgresql:postgresql Notes: - This uses the generic database backend, which supports PostgreSQL via Vault's postgresql-database-plugin. Older spring.cloud.vault.postgresql.* properties are deprecated. - Ensure Vault has a 'database' secrets engine mounted (e.g., at 'database/'), configured with PostgreSQL connection details and a role with creation_statements for dynamic users. - Credentials are automatically renewed by Spring Cloud Vault. - For Spring Boot 3.x+, use spring.config.import=vault:// (ConfigData API); for older, enable bootstrap. - The repository likely uses a recent version (e.g., 4.x as of 2026), where generic database is standard. The query mentions "the Spring Cloud Vault version used in this repository," but no specific repository was identified in searches. The configuration above is standard across recent versions per official docs.
Citations:
- 1: https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/
- 2: https://cloud.spring.io/spring-cloud-vault/multi/multi_vault.config.backends.database-backends.html
- 3: https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/config-data.html
- 4: https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/secret-backends.html
- 5: https://docs.spring.io/spring-cloud-vault/reference/secret-backends.html
- 6: https://docs.enterprise.spring.io/spring-cloud-vault/docs/4.0.5/reference/html/
- 7: https://www.baeldung.com/spring-cloud-vault
🏁 Script executed:
fd -t f "pom.xml" -o "build.gradle" compliance-travel-ruleRepository: Puneethkumarck/stablebridge-platform
Length of output: 223
🏁 Script executed:
cat -n compliance-travel-rule/compliance-travel-rule/src/main/resources/application.yml | head -50Repository: Puneethkumarck/stablebridge-platform
Length of output: 1662
🏁 Script executed:
fd -t f pom.xml compliance-travel-ruleRepository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
cat -n compliance-travel-rule/compliance-travel-rule/pom.xml | head -100Repository: Puneethkumarck/stablebridge-platform
Length of output: 165
🏁 Script executed:
find compliance-travel-rule -name "pom.xml" -type fRepository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
git ls-files compliance-travel-rule | grep -E "(pom\.xml|build\.gradle)"Repository: Puneethkumarck/stablebridge-platform
Length of output: 279
🏁 Script executed:
cat -n compliance-travel-rule/compliance-travel-rule/build.gradle.ktsRepository: Puneethkumarck/stablebridge-platform
Length of output: 878
🏁 Script executed:
find . -name "build.gradle.kts" -path "*/gradle*" -prune -o -type f -name "*.gradle.kts" -print | head -5Repository: Puneethkumarck/stablebridge-platform
Length of output: 368
🏁 Script executed:
cat -n build.gradle.kts | head -150Repository: Puneethkumarck/stablebridge-platform
Length of output: 3912
🏁 Script executed:
cat -n gradle.propertiesRepository: Puneethkumarck/stablebridge-platform
Length of output: 752
Vault database backend missing—KV-only config does not enable PostgreSQL dynamic credentials.
Lines 16–19 configure KV secrets exclusively (backend: secret). Datasource credentials remain hardcoded with env fallbacks (lines 33–34: DB_USER, DB_PASS), bypassing dynamic rotation. For AML/sanctions compliance audit requirements, replace static credentials with Vault's database backend.
Add the following under spring.cloud.vault:
Vault database backend configuration
cloud:
vault:
+ database:
+ enabled: true
+ backend: database
+ role: compliance-travel-rule
kv:
enabled: true
backend: secret
default-context: applicationEnsure Vault's database secrets engine is mounted with PostgreSQL plugin and a compliance-travel-rule role configured. Spring Cloud Vault 2025.1.1 automatically rotates and maps credentials to spring.datasource.username and spring.datasource.password.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@compliance-travel-rule/compliance-travel-rule/src/main/resources/application.yml`
around lines 16 - 19, The YAML currently configures only the KV secrets backend
(kv.backend: secret) and leaves DB credentials wired to env fallbacks
(DB_USER/DB_PASS) instead of using Vault dynamic DB credentials; update the
spring.cloud.vault configuration to enable the Vault database secrets engine and
point Spring Cloud Vault at it so that spring.datasource.username and
spring.datasource.password are populated from Vault’s database backend (ensure a
PostgreSQL plugin-mounted database engine and a compliance-travel-rule role
exist in Vault); specifically replace or augment the kv-only config by adding
database backend settings under spring.cloud.vault and map the database role so
Spring Cloud Vault (2025.1.1) can rotate and inject credentials rather than
using DB_USER/DB_PASS.
| config: | ||
| import: optional:vault:// | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find and read the application.yml file
find . -path "*fiat-off-ramp/src/main/resources/application.yml" -type f | head -5Repository: Puneethkumarck/stablebridge-platform
Length of output: 144
🏁 Script executed:
# List the structure to confirm we have the right path
ls -la fiat-off-ramp/fiat-off-ramp/src/main/resources/ 2>/dev/null || find . -name "application.yml" -type f 2>/dev/null | head -10Repository: Puneethkumarck/stablebridge-platform
Length of output: 307
🏁 Script executed:
# Read the full application.yml file
cat -n fiat-off-ramp/fiat-off-ramp/src/main/resources/application.yml 2>/dev/null || cat -n fiat-off-ramp/src/main/resources/application.yml 2>/dev/nullRepository: Puneethkumarck/stablebridge-platform
Length of output: 6394
Enable environment-driven Vault requirements and auth flexibility.
Line 6 uses optional:vault://, allowing graceful degradation when Vault is unavailable. Combined with line 10's default enabled: false, this creates an opt-in posture that may not enforce Vault in environments where it should be mandatory. Production deployments should control this behavior explicitly.
Additionally, line 14 hardcodes TOKEN authentication, limiting flexibility for stronger methods (KUBERNETES, APPROLE) without code changes. Make both the import mode and auth backend environment-configurable.
Suggested hardening
config:
- import: optional:vault://
+ import: ${VAULT_CONFIG_IMPORT:optional:vault://}
cloud:
vault:
enabled: ${app.vault.enabled:false}
+ fail-fast: ${VAULT_FAIL_FAST:false}
+ authentication: ${VAULT_AUTH_METHOD:TOKEN}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fiat-off-ramp/fiat-off-ramp/src/main/resources/application.yml` around lines
5 - 7, The current static YAML uses config.import: optional:vault:// and
hardcoded vault settings (vault.enabled: false and auth method TOKEN); update
configuration to make the import mode and auth backend environment-driven:
replace the fixed config.import with a value sourced from an environment
variable (e.g., defaulting to optional:vault:// but allowing required:vault://
when an env var indicates production), make vault.enabled driven by an
environment variable instead of the literal false, and make the vault auth
backend (the TOKEN setting) configurable via an environment variable (e.g.,
vault.auth.method) so KUBERNETES/APPROLE can be selected without code changes;
adjust any code or startup config that reads these keys (config.import,
vault.enabled, vault.auth.*) to respect the new env-driven values.
| authentication: TOKEN | ||
| token: ${VAULT_TOKEN:} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Locate and read the application.yml file
find . -name "application.yml" -type f 2>/dev/null | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 803
🏁 Script executed:
# Once located, read the file to verify the Vault configuration
cat -n fiat-off-ramp/fiat-off-ramp/src/main/resources/application.yml 2>/dev/null | head -50Repository: Puneethkumarck/stablebridge-platform
Length of output: 1699
🏁 Script executed:
# Search for other Vault configurations in the codebase
rg "vault" -i --type yaml --type properties --type java -A 3 -B 1 | head -100Repository: Puneethkumarck/stablebridge-platform
Length of output: 114
🏁 Script executed:
# Check if there are any environment variable references for authentication method
rg "VAULT_AUTH" -i --type yaml --type properties --type javaRepository: Puneethkumarck/stablebridge-platform
Length of output: 114
🏁 Script executed:
# Check Vault configuration in other modules
for file in ./api-gateway-iam/api-gateway-iam/src/main/resources/application.yml \
./blockchain-custody/blockchain-custody/src/main/resources/application.yml \
./ledger-accounting/ledger-accounting/src/main/resources/application.yml; do
echo "=== $file ==="
grep -A 5 "vault:" "$file" 2>/dev/null | head -15
doneRepository: Puneethkumarck/stablebridge-platform
Length of output: 992
🏁 Script executed:
# Search for any environment variable configurations related to VAULT_AUTH
rg "VAULT_AUTH|authentication.*VAULT" --type yaml -A 2 -B 2 2>/dev/nullRepository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Check if there are any Java configurations for Vault auth methods
rg "AppRole|KUBERNETES|CERT|AUTH" --type java -i -l 2>/dev/null | head -10Repository: Puneethkumarck/stablebridge-platform
Length of output: 1298
🏁 Script executed:
# Look for pom.xml or gradle files to identify Spring Cloud Vault version
find . -name "pom.xml" -o -name "build.gradle" | xargs grep -l "spring-cloud-vault" 2>/dev/null | head -5Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Read the VaultConfig.java file
cat -n platform-infra/src/main/java/com/stablecoin/payments/platform/infrastructure/vault/VaultConfig.java 2>/dev/nullRepository: Puneethkumarck/stablebridge-platform
Length of output: 2075
🏁 Script executed:
# Look for pom.xml files
find . -name "pom.xml" 2>/dev/null | head -10Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Check Spring Cloud version in any found pom.xml
find . -name "pom.xml" -type f 2>/dev/null -exec grep -l "spring-cloud" {} \; | head -3 | xargs cat 2>/dev/null | grep -A 2 "spring-cloud-vault\|spring-cloud-starter-vault" | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Find and check pom.xml in a microservice directory
cat fiat-off-ramp/pom.xml 2>/dev/null | grep -A 10 "spring-cloud-vault\|spring-cloud-starter-vault\|<version>" | head -30Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Also check parent pom
cat pom.xml 2>/dev/null | grep -A 10 "spring-cloud-vault\|spring-cloud-starter-vault\|cloud.version" | head -40Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Search for any existing AppRole or auth configuration in Java code
rg "app-role|appRole|APPROLE" --type yaml --type java -iRepository: Puneethkumarck/stablebridge-platform
Length of output: 945
🌐 Web query:
Spring Cloud Vault AppRole authentication YAML configuration property names
💡 Result:
The YAML configuration properties for Spring Cloud Vault AppRole authentication are under the spring.cloud.vault.app-role namespace. Key properties include: spring: cloud: vault: authentication: APPROLE app-role: role-id: your-role-id secret-id: your-secret-id app-role-path: approle # optional, path of the AppRole mount Additional properties mentioned: - app-role-path: sets the path of the approle authentication mount. - secret-id: sets the SecretId (can be omitted if not required). Note: Official docs note that full AppRole configurations (push/pull/wrapped modes) may require a custom AppRoleAuthentication bean as not all combinations are derivable from properties. AppRole is limited to simple pull mode in reactive setups. Example from docs: spring: cloud: vault: app-role: role-id: bde2076b-cccb-3cf0-d57e-bca7b1e83a52 secret-id: 1696536f-1976-73b1-b241-0b4213908d39
Citations:
- 1: https://docs.spring.io/spring-cloud-vault/reference/authentication.html
- 2: https://cloud.spring.io/spring-cloud-vault/multi/multi_vault.config.authentication.html
🌐 Web query:
Spring Cloud Vault YAML authentication method property reference
💡 Result:
The primary YAML property for specifying the Vault authentication method in Spring Cloud Vault is spring.cloud.vault.authentication. This selects the authentication backend (e.g., TOKEN, APPID, APPROLE, AWS_EC2, CERT, KUBERNETES, etc.). Token authentication is the default if not specified. Example in bootstrap.yml or application.yml (for legacy bootstrap or with spring.cloud.bootstrap.enabled=true): spring: cloud: vault: authentication: APPID app-id: user-id: IP_ADDRESS token: hvs.CAESIJ... # for TOKEN For AppRole: spring: cloud: vault: authentication: APPROLE app-role: role-id: bde2076b-cccb-3cf0-d57e-bca7b1e83a52 secret-id: 1696536f-1976-73b1-b241-0b4213908d39 For AWS-EC2: spring: cloud: vault: authentication: AWS_EC2 aws-ec2: role: my-role With Spring Boot 2.4+, prefer spring.config.import=vault:// in application.yml, but authentication properties remain under spring.cloud.vault.*. Detailed configuration per method is in the official authentication docs.
Citations:
- 1: https://docs.spring.io/spring-cloud-vault/reference/authentication.html
- 2: https://cloud.spring.io/spring-cloud-vault/1.1.x/multi/multi_vault.config.authentication.html
- 3: https://cloud.spring.io/spring-cloud-vault/2.1.x/multi/multi_vault.config.authentication.html
- 4: https://cloud.spring.io/spring-cloud-vault/multi/multi_vault.config.authentication.html
Externalize Vault authentication method—hardcoding TOKEN prevents production-grade backends.
Line 14 locks authentication to TOKEN across all microservices, blocking AppRole and Kubernetes auth strategies that the architecture already documents as supported (see VaultConfig). This forces static token dependency and limits operational flexibility.
Update authentication to accept environment override with TOKEN as default, and configure AppRole as an alternative:
Fix
- authentication: TOKEN
+ authentication: ${VAULT_AUTH_METHOD:TOKEN}
token: ${VAULT_TOKEN:}
+ app-role:
+ role-id: ${VAULT_APPROLE_ROLE_ID:}
+ secret-id: ${VAULT_APPROLE_SECRET_ID:}Apply this across all microservices using Vault.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| authentication: TOKEN | |
| token: ${VAULT_TOKEN:} | |
| authentication: ${VAULT_AUTH_METHOD:TOKEN} | |
| token: ${VAULT_TOKEN:} | |
| app-role: | |
| role-id: ${VAULT_APPROLE_ROLE_ID:} | |
| secret-id: ${VAULT_APPROLE_SECRET_ID:} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fiat-off-ramp/fiat-off-ramp/src/main/resources/application.yml` around lines
14 - 15, Change the hardcoded Vault authentication by making the authentication
property environment-configurable and supporting AppRole as an alternative:
replace the static "authentication: TOKEN" with an env-backed value like
"authentication: ${VAULT_AUTHENTICATION:TOKEN}" and ensure token remains
env-driven ("token: ${VAULT_TOKEN:}"); update any Vault-related bootstrap (e.g.,
VaultConfig) to branch on the authentication value and load AppRole fields
(role_id/secret_id) when authentication == "APPROLE". Apply the same pattern
across all microservices using Vault.
| config: | ||
| import: optional:vault:// | ||
|
|
||
| cloud: | ||
| vault: | ||
| enabled: ${app.vault.enabled:false} | ||
| host: ${VAULT_HOST:localhost} | ||
| port: ${VAULT_PORT:8200} | ||
| scheme: ${VAULT_SCHEME:https} | ||
| authentication: TOKEN | ||
| token: ${VAULT_TOKEN:} | ||
| kv: | ||
| enabled: true | ||
| backend: secret | ||
| default-context: application |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
`For the Spring Cloud Vault version used by this repo, confirm:
- behavior of
spring.config.import=optional:vault://when Vault is unavailable, - recommended production enforcement pattern (
vault://+ fail-fast), - supported values for
spring.cloud.vault.authenticationand preferred non-token methods (Kubernetes/AppRole).`
💡 Result:
- With spring.config.import=optional:vault://, if Vault is unavailable (e.g., connection failure), the import fails gracefully: the application continues startup without Vault properties, logging the issue. Vault context paths with HTTP 404 are always skipped. {value: Graceful failure, continues without Vault properties, citations: [1,2,7], confidence: high} 2) Recommended production pattern: spring.config.import=vault:// (mandatory, default) for fail-fast on Vault unavailability + spring.cloud.vault.fail-fast=true to fail startup if Vault authentication or connection fails or if context paths return 404. This ensures the app does not start with missing secrets. {value: vault:// + spring.cloud.vault.fail-fast=true, citations: [1,2,14,15], confidence: high} 3) Supported values for spring.cloud.vault.authentication include: TOKEN (default), APPID, APPROLE, AWS_EC2, AWS_IAM, AZURE_MSI, CERT, CUBBYHOLE, GCP_GCE, GCP_IAM, GITHUB, KUBERNETES, PCF. Preferred non-token methods: KUBERNETES (using service account token file, e.g., /var/run/secrets/kubernetes.io/serviceaccount/token) and APPROLE (machine auth with role-id/secret-id). {value: TOKEN, APPID, APPROLE, AWS_EC2, etc.; Preferred: KUBERNETES, APPROLE, citations: [9,13,17,19,21], confidence: high}
Citations:
- 1: https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/
- 2: https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/config-data.html
- 3: Invalid configuration is not fatal spring-cloud/spring-cloud-vault#685
- 4: https://docs.spring.io/spring-cloud-vault/reference/authentication.html
- 5: https://cloud.spring.io/spring-cloud-vault/2.1.x/multi/multi_vault.config.authentication.html
- 6: https://docs.spring.io/spring-cloud-vault/reference/advanced-topics.html
- 7: spring.cloud.vault.fail-fast - does it still work? spring-cloud/spring-cloud-vault#143
- 8: https://docs.spring.io/spring-vault/docs/current/api/org/springframework/vault/authentication/KubernetesAuthentication.html
- 9: https://cloud.spring.io/spring-cloud-vault/multi/multi_vault.config.authentication.gcpiam.html
- 10: https://docs.spring.io/spring-vault/reference/vault/authentication.html
🏁 Script executed:
# Find dependency files to determine Spring Cloud Vault version
find . -type f \( -name "pom.xml" -o -name "build.gradle" -o -name "build.gradle.kts" \) | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 1164
🏁 Script executed:
# Check for application.yml variants (prod, dev, test profiles)
find fiat-on-ramp -type f -name "application*.yml" -o -name "application*.yaml" | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 230
🏁 Script executed:
# Search for Vault-related configuration across the project
rg -i "vault|VAULT" --type yaml fiat-on-ramp/fiat-on-ramp/src/main/resources/ -A 2 -B 2Repository: Puneethkumarck/stablebridge-platform
Length of output: 1229
🏁 Script executed:
# Check Spring Cloud Vault version in the fiat-on-ramp service
head -100 ./fiat-on-ramp/fiat-on-ramp/build.gradle.ktsRepository: Puneethkumarck/stablebridge-platform
Length of output: 686
🏁 Script executed:
# Check if there are production/deployment overrides
cat fiat-on-ramp/fiat-on-ramp/src/integration-test/resources/application-integration-test.ymlRepository: Puneethkumarck/stablebridge-platform
Length of output: 972
🏁 Script executed:
# Search for any kubernetes or deployment manifests that might override Vault config
find fiat-on-ramp -type f \( -name "*.yaml" -o -name "*.yml" \) ! -path "*/build/*" | grep -E "(k8s|kubernetes|docker|deploy)" | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Check parent build.gradle.kts for Spring Cloud Vault dependency
cat ./build.gradle.kts | grep -A 100 -E "(dependencyManagement|vault|spring.*cloud)" | head -150Repository: Puneethkumarck/stablebridge-platform
Length of output: 1346
🏁 Script executed:
# Check buildSrc plugin for Spring Cloud Vault configuration
find ./buildSrc -type f -name "*.kts" -o -name "*.gradle" | xargs grep -l -i vaultRepository: Puneethkumarck/stablebridge-platform
Length of output: 64
🏁 Script executed:
# Check root settings.gradle or any other Spring Boot common config
find . -maxdepth 2 -type f -name "*.kts" | xargs grep -l -i "spring.*cloud.*vault" 2>/dev/nullRepository: Puneethkumarck/stablebridge-platform
Length of output: 113
🏁 Script executed:
# Check platform-infra for Spring Cloud Vault
cat ./platform-infra/build.gradle.kts | grep -i vaultRepository: Puneethkumarck/stablebridge-platform
Length of output: 161
🏁 Script executed:
# Find springCloudVersion value
grep -r "springCloudVersion" ./gradle.properties || grep -r "springCloudVersion\s*=" ./build.gradle.kts | head -5Repository: Puneethkumarck/stablebridge-platform
Length of output: 107
🏁 Script executed:
# Search for spring-cloud-vault in any gradle files
rg "spring-cloud-vault" --type gradleRepository: Puneethkumarck/stablebridge-platform
Length of output: 228
🏁 Script executed:
# Check for any Kubernetes manifests or deployment configs in fiat-on-ramp or parent dirs
find . -type f \( -name "*.yaml" -o -name "*.yml" \) -path "*kubernetes*" -o -path "*k8s*" -o -path "*helm*" | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 784
🏁 Script executed:
# Look for any production or deployment configuration files
find . -type f -name "application*.yml" | xargs grep -l "vault" 2>/dev/nullRepository: Puneethkumarck/stablebridge-platform
Length of output: 1885
🏁 Script executed:
# Check if there's a deployment folder with environment-specific configs
find . -type f -name "*.md" -o -name "README*" | xargs grep -l -i "vault.*production\|deployment" 2>/dev/null | head -5Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Check if fiat-on-ramp has a k8s deployment manifest
find ./infra/k8s -type f -name "*fiat-on-ramp*" -o -path "*fiat-on-ramp*"Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Search K8s configmaps for Vault configuration
find ./infra/k8s -type f -name "*.yaml" | xargs grep -l "VAULT\|vault" 2>/dev/nullRepository: Puneethkumarck/stablebridge-platform
Length of output: 64
🏁 Script executed:
# Check the main k8s base kustomization to understand deployment structure
cat ./infra/k8s/base/kustomization.yamlRepository: Puneethkumarck/stablebridge-platform
Length of output: 238
🏁 Script executed:
# Check what s10, s11, s13 are (possibly service names)
cat ./infra/k8s/base/s10/deployment.yaml | head -50Repository: Puneethkumarck/stablebridge-platform
Length of output: 1309
🏁 Script executed:
# Check if there are kustomize overlays for production
find ./infra/k8s -type d -name "overlays" -o -name "prod" -o -name "production"Repository: Puneethkumarck/stablebridge-platform
Length of output: 100
🏁 Script executed:
# Check if there's any kustomize patch or configmap that sets Vault env vars
find ./infra/k8s -type f \( -name "*.yaml" -o -name "*.yml" \) | xargs grep -E "app\.vault\.enabled|VAULT_" 2>/dev/null | head -20Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# List overlay directories
ls -la ./infra/k8s/overlays/Repository: Puneethkumarck/stablebridge-platform
Length of output: 239
🏁 Script executed:
# Check for environment-specific Vault configuration in overlays
find ./infra/k8s/overlays -type f \( -name "*.yaml" -o -name "*.yml" \) | xargs grep -E "vault|VAULT|app\.vault" 2>/dev/null | head -30Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
# Check if fiat-on-ramp has a dedicated k8s service definition
find ./infra/k8s -type f -name "*fiat*"Repository: Puneethkumarck/stablebridge-platform
Length of output: 62
Configure Vault for production enforcement
spring.config.import: optional:vault:// (line 6) permits graceful startup without secrets, and enabled: false (line 10) by default. Line 14 hardcodes TOKEN authentication, preventing stronger methods (Kubernetes, AppRole) in production.
Production deployments must enforce Vault: set spring.config.import=vault:// (mandatory), spring.cloud.vault.fail-fast=true, app.vault.enabled=true, and configure a non-token authentication method via environment variables.
Suggested changes
spring:
config:
- import: optional:vault://
+ import: ${VAULT_CONFIG_IMPORT:optional:vault://}
cloud:
vault:
enabled: ${app.vault.enabled:false}
+ fail-fast: ${VAULT_FAIL_FAST:false}
host: ${VAULT_HOST:localhost}
port: ${VAULT_PORT:8200}
scheme: ${VAULT_SCHEME:https}
- authentication: TOKEN
+ authentication: ${VAULT_AUTH_METHOD:TOKEN}
token: ${VAULT_TOKEN:}Ensure prod/staging profiles override these variables: VAULT_CONFIG_IMPORT=vault://, VAULT_FAIL_FAST=true, app.vault.enabled=true, VAULT_AUTH_METHOD=KUBERNETES (or APPROLE).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@fiat-on-ramp/fiat-on-ramp/src/main/resources/application.yml` around lines 5
- 19, Make Vault mandatory and allow non-token auth by updating the YAML keys:
change config.import from optional:vault:// to vault://, set
spring.cloud.vault.fail-fast=true, set app.vault.enabled=true (or remove the
default false), and replace hardcoded cloud.vault.authentication: TOKEN with a
configurable value sourced from an environment variable (e.g.,
CLOUD_VAULT_AUTH_METHOD or VAULT_AUTH_METHOD) so production can use KUBERNETES
or APPROLE; ensure these values can be overridden per profile via env vars like
VAULT_CONFIG_IMPORT, VAULT_FAIL_FAST, app.vault.enabled, and VAULT_AUTH_METHOD.
| stream: | ||
| kafka: | ||
| binder: | ||
| brokers: ${KAFKA_BROKERS:localhost:9092} | ||
| auto-create-topics: false |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for Spring Cloud Stream usage patterns in ledger-accounting
echo "=== Checking for Spring Cloud Stream annotations/interfaces ==="
rg -n '@EnableBinding|@Input|@Output|@StreamListener|Supplier<|Consumer<|Function<' \
ledger-accounting/ledger-accounting/src/main/java/ || echo "No Spring Cloud Stream patterns found"
echo ""
echo "=== Checking for spring-cloud-stream dependency ==="
rg -n 'spring-cloud-stream' ledger-accounting/ledger-accounting/build.gradle* || echo "No spring-cloud-stream dependency found"
echo ""
echo "=== Verifying Kafka usage is plain Spring Kafka ==="
rg -n '@KafkaListener|KafkaTemplate' ledger-accounting/ledger-accounting/src/main/java/Repository: Puneethkumarck/stablebridge-platform
Length of output: 2644
Remove unused Spring Cloud Stream Kafka binder configuration.
The service integrates Kafka exclusively via Spring Kafka (@KafkaListener, KafkaTemplate), which reads from spring.kafka.bootstrap-servers (line 55). No Spring Cloud Stream functional bindings or annotations exist in the codebase. The spring.cloud.stream.kafka.binder block (lines 20-24) is silently ignored at runtime and creates confusion—developers modifying brokers here will observe no effect on the running service.
Proposed fix — remove unused binder config
cloud:
vault:
enabled: ${app.vault.enabled:false}
host: ${VAULT_HOST:localhost}
port: ${VAULT_PORT:8200}
scheme: ${VAULT_SCHEME:https}
authentication: TOKEN
token: ${VAULT_TOKEN:}
kv:
enabled: true
backend: secret
default-context: application
- stream:
- kafka:
- binder:
- brokers: ${KAFKA_BROKERS:localhost:9092}
- auto-create-topics: false📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| stream: | |
| kafka: | |
| binder: | |
| brokers: ${KAFKA_BROKERS:localhost:9092} | |
| auto-create-topics: false |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ledger-accounting/ledger-accounting/src/main/resources/application.yml`
around lines 20 - 24, The application.yml contains an unused Spring Cloud Stream
Kafka binder section (stream.kafka.binder) that is ignored because the code uses
Spring Kafka (spring.kafka.bootstrap-servers and `@KafkaListener/KafkaTemplate`);
remove the entire stream.kafka.binder block to avoid confusion and ensure
developers edit spring.kafka.bootstrap-servers instead; search for the keys
"stream.kafka.binder" and "spring.kafka.bootstrap-servers" and delete the binder
configuration while keeping the existing spring.kafka.bootstrap-servers entry
intact.
| config: | ||
| import: optional:vault:// | ||
|
|
||
| cloud: | ||
| vault: | ||
| enabled: ${app.vault.enabled:false} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify if production profile(s) enforce Vault (non-optional import + enabled=true)
fd 'application.*\.ya?ml$' merchant-iam/merchant-iam/src/main/resources -x sh -c '
f="$1"
echo "===== $f ====="
rg -n -C2 "on-profile|spring\\.config\\.import|import:|app\\.vault\\.enabled|spring\\.cloud\\.vault\\.enabled|authentication:" "$f"
' sh {}Repository: Puneethkumarck/stablebridge-platform
Length of output: 628
🏁 Script executed:
#!/bin/bash
# List all YAML config files in merchant-iam resources directory
echo "=== All YAML config files in merchant-iam resources ==="
find merchant-iam/merchant-iam/src/main/resources -name "*.yml" -o -name "*.yaml" | sort
echo -e "\n=== Check for all application profiles (including prod variants) ==="
ls -la merchant-iam/merchant-iam/src/main/resources/application*.yml 2>/dev/null || echo "No application*.yml files found"
echo -e "\n=== Full content of all application*.yml files ==="
for f in merchant-iam/merchant-iam/src/main/resources/application*.yml; do
if [ -f "$f" ]; then
echo "===== $(basename $f) ====="
cat -n "$f"
fi
doneRepository: Puneethkumarck/stablebridge-platform
Length of output: 7555
IAM service requires mandatory Vault—fail-open default is a security violation.
Lines 6 and 10 allow the service to start without Vault entirely. This violates security-critical requirements for identity and access management: no dedicated production profile enforces Vault, leaving the fail-open configuration as the actual production default. Environment variables can enable Vault at runtime, but YAML config should explicitly enforce non-optional Vault.
Create an application-prod.yml profile that sets import: vault:// (non-optional) and enabled: true, ensuring centralized secrets and rotation controls are mandatory in production.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@merchant-iam/merchant-iam/src/main/resources/application.yml` around lines 5
- 10, Current application.yml uses optional Vault import and a default disabled
flag (config.import: optional:vault:// and cloud.vault.enabled:
${app.vault.enabled:false}); create an application-prod.yml profile that
overrides these keys to enforce Vault in production by setting config.import:
vault:// (remove "optional:") and cloud.vault.enabled: true so the service fails
closed when Vault is unavailable—update the Spring profile activation for prod
as needed and ensure the production deployment uses the "prod" profile.
| import: optional:vault:// | ||
|
|
||
| cloud: | ||
| vault: | ||
| enabled: ${app.vault.enabled:false} | ||
| host: ${VAULT_HOST:localhost} | ||
| port: ${VAULT_PORT:8200} | ||
| scheme: ${VAULT_SCHEME:https} | ||
| authentication: TOKEN | ||
| token: ${VAULT_TOKEN:} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Vault-related config across merchant-onboarding =="
fd 'application.*\.ya?ml$' merchant-onboarding | xargs -I{} sh -c 'echo "--- {}"; rg -n "app\\.vault\\.enabled|spring\\.config\\.import|spring\\.cloud\\.vault|VAULT_TOKEN|authentication" "{}" || true'
echo
echo "== Look for tests/guards asserting fail-closed behavior when Vault is enabled =="
rg -n "VaultConfigTest|vault.*enabled|missing.*token|VAULT_TOKEN|fail-fast|spring\\.cloud\\.vault\\.enabled" -SRepository: Puneethkumarck/stablebridge-platform
Length of output: 4601
🏁 Script executed:
cat -n platform-infra/src/main/java/com/stablecoin/payments/platform/infrastructure/vault/VaultConfig.javaRepository: Puneethkumarck/stablebridge-platform
Length of output: 2075
🏁 Script executed:
cat -n merchant-onboarding/merchant-onboarding/src/integration-test/resources/application-integration-test.yml | head -30Repository: Puneethkumarck/stablebridge-platform
Length of output: 900
🏁 Script executed:
rg -A 10 "optional:vault://" merchant-onboarding/Repository: Puneethkumarck/stablebridge-platform
Length of output: 1167
🏁 Script executed:
rg -n "VAULT_TOKEN|token.*required|failFast|validation" merchant-onboarding/merchant-onboarding/src/main/java merchant-onboarding/merchant-onboarding/src/main/resources/ -iRepository: Puneethkumarck/stablebridge-platform
Length of output: 1014
🏁 Script executed:
rg -n "optional:vault|mandatory:vault|spring.config.import" --type yamlRepository: Puneethkumarck/stablebridge-platform
Length of output: 1103
🌐 Web query:
Spring Cloud Vault optional: prefix fail-closed behavior when VAULT_TOKEN empty
💡 Result:
In Spring Cloud Vault, when using the Config Data API (spring.config.import=vault://...), Vault config locations support an "optional" modifier in the location string, such as "vault://secret/myapp?optional=true". This makes the location optional, meaning the application startup does not fail if the Vault path is unavailable (e.g., HTTP 404). However, 404s are always skipped regardless. The "fail-closed" behavior refers to spring.cloud.vault.fail-fast=true (default false), which causes the application to fail startup if Vault connectivity or authentication issues occur, such as an empty or invalid VAULT_TOKEN (spring.cloud.vault.token). If VAULT_TOKEN is empty and token authentication is used (default), VaultTemplate creation fails with "Token must not be empty", halting startup (fail-closed). This happens before loading config properties. For optional locations, the fail-fast primarily applies to path existence (404), not authentication. Authentication failure (empty token) fails early during Vault client bootstrap, independent of optional. To achieve fail-closed on empty token with optional Vault, set spring.cloud.vault.fail-fast=true; the app fails on auth error. Without it, if bootstrap succeeds without token (e.g., other auth), optional skips unavailable paths gracefully (fail-open for those paths). Token is sourced from spring.cloud.vault.token, VAULT_TOKEN env, or ~/.vault-token. Prefix in docs refers to ?prefix= for property name prefixing, not config prefix.
Citations:
- 1: https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/
- 2: https://docs.spring.io/spring-cloud-vault/docs/current/reference/html/config-data.html
- 3: https://stackoverflow.com/questions/65657141/spring-cloud-vault-token-spring-cloud-vault-token-must-not-be-empty
- 4: Token must not be empty using Kubernetes authentication spring-cloud/spring-cloud-vault#200
- 5: https://docs.spring.io/spring-cloud-vault/reference/advanced-topics.html
- 6: https://docs.spring.io/spring-cloud-vault/reference/authentication.html
🏁 Script executed:
rg -n "fail-fast|failFast" merchant-onboarding/ --type yamlRepository: Puneethkumarck/stablebridge-platform
Length of output: 62
🏁 Script executed:
rg -n "spring.cloud.vault.fail-fast" . --type yamlRepository: Puneethkumarck/stablebridge-platform
Length of output: 62
Verify Vault configuration enforces fail-closed semantics when enabled.
The optional:vault:// prefix (line 6) allows the application to start even if Vault paths are unavailable, contradicting the fail-closed requirement for enabled environments. When app.vault.enabled=true, either:
- Change to
mandatory:vault://to truly fail-closed on Vault unavailability, or - Explicitly set
spring.cloud.vault.fail-fast=trueand document the behavior in VaultConfig.java
The empty ${VAULT_TOKEN:} default (line 15) will cause bootstrap failure during VaultTemplate creation, but only if authentication is reached—optional path loading may not enforce this consistently across all Vault operations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@merchant-onboarding/merchant-onboarding/src/main/resources/application.yml`
around lines 6 - 15, The application.yml currently uses the optional:vault://
import and an empty default VAULT_TOKEN which undermines fail-closed behavior;
update application.yml to either replace the import prefix optional:vault://
with mandatory:vault:// OR add spring.cloud.vault.fail-fast=true under
cloud.vault when app.vault.enabled=true, and remove the empty default for token
(use token: ${VAULT_TOKEN} without a fallback) so a missing token fails startup;
also add a short comment in VaultConfig.java documenting that
fail-fast/mandatory import is required in enabled environments and that
VAULT_TOKEN must be provided.
Summary
VaultConfig— opt-in toggle (app.vault.enabled=falseby default) — existing env-var workflow unaffectedspring.config.import: optional:vault://in all serviceapplication.yml— services boot without Vaultsecret/applicationvault-initDocker Compose service runs after Vault is healthyFiles
VaultConfig.java—@ConditionalOnProperty(matchIfMissing=false), documents Vault architectureVaultConfigTest.java— 3 tests (enabled, disabled, absent)init-vault.sh— seeds per-service DB creds, API keys, JWT keys, webhook secretsdocker-compose.dev.yml—vault-initservice with health check dependencyapplication.yml—spring.cloud.vault+spring.config.importblocksplatform-infra/build.gradle.kts—spring-cloud-starter-vault-configdependencyDesign Decisions
matchIfMissing=falseensures zero impact on tests and existing dev workflowoptional:vault://: ConfigData import is optional — no Vault needed for tests/CIsecret/{spring.application.name}for service-specific,secret/applicationfor sharedTest plan
Closes #215
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Chores
Tests