Secrets and Variables
AuraBoot configuration is layered. Each layer overrides the one below it. Knowing the layer order is critical: a hotfix injected at the wrong layer either takes no effect or leaks into a git-tracked file.
Three-layer model
| Layer | Source | Precedence | Typical use |
|---|---|---|---|
| 1. Program arguments | --spring.data.redis.host=... | Highest | One-off override, production launchers |
| 2. Environment variables | DATABASE_PASSWORD, SPRING_DATA_REDIS_HOST | Middle | Container deployments, CI |
| 3. External vault | Hashicorp Vault / cloud KMS via spring.config.import | Lowest of the three (still above application.yml) | Long-lived secrets, rotation |
The packaged application.yml only contains non-sensitive defaults; it should never carry a real password or API key.
Sensitive fields
The following must never be committed and must come from layer 2 or 3:
| Field | Notes |
|---|---|
spring.datasource.password | PostgreSQL primary credentials |
spring.data.redis.host / password | Redis is required by the enterprise build at startup |
security.jwt.secret | Signs platform JWTs; rotation invalidates sessions |
aura.storage.minio.access-key / secret-key | Object storage |
aura.mq.kafka.bootstrap-servers credentials | Plain bootstrap host is fine to log; SASL secret is not |
Connector credentials (cr_csp_connector_pid resolver) | Third-party API keys, OAuth tokens |
| Webhook signing secrets | See Webhooks |
| LLM provider keys | OpenAI / Claude / local model gateway tokens |
When to use
- Bootstrapping a fresh environment: prefer environment variables for portability.
- Production with frequent rotation: bind to an external vault and never hard-code.
- Local development: use a gitignored
.envfile plus a launcher that translates it into program arguments.
How it works
Spring Boot resolves configuration in declared precedence order. AuraBoot extends this with a connector credential resolver that looks up bearer / basic / api-key payloads by cr_csp_connector_pid and injects them into outbound requests without persisting plaintext in DSL.
# application.yml (committed, contains no secrets)
spring:
config:
import:
- "optional:vault://secret/auraboot/${SPRING_PROFILES_ACTIVE}"
datasource:
url: "${DATABASE_URL:jdbc:postgresql://localhost:5432/aura_boot?charSet=UTF8}"
username: "${DATABASE_USERNAME:auraboot}"
password: "${DATABASE_PASSWORD:}" # required in production, no default
data:
redis:
host: "${REDIS_HOST:localhost}" # injected via SPRING_DATA_REDIS_HOST when Redis is enabled
port: "${REDIS_PORT:6379}"
security:
jwt:
secret: "${JWT_SECRET}" # requiredFor a one-off override (for example during incident response), pass it as a program argument so it does not leak into the process environment of every child shell:
java -jar platform.jar \
--spring.profiles.active=prod \
--spring.data.redis.host=10.0.3.7 \
--spring.datasource.hikari.maximum-pool-size=50Rotation
# 1. Generate a new secret; demote the current one to "previous" so old tokens still verify
NEW_SECRET="$(openssl rand -base64 64)"
# JWT_PREVIOUS_SECRET=$JWT_SECRET, JWT_PREVIOUS_KID=$JWT_KID
# JWT_SECRET=$NEW_SECRET, JWT_KID=key-$(date +%Y%m%d)
# 2. Roll-restart each node so the new config takes effect
# (the platform does not enable Spring Cloud /actuator/refresh)
# New tokens are signed with the new secret; old tokens still verify via previous-secret
# 3. Verify the new JWT works: call a protected endpoint with the new token
curl -sS "$AURA_API_URL/api/auth/me" \
-H "Authorization: Bearer $NEW_TOKEN"
# 4. After one token-expiry window (default 86400s), remove the JWT_PREVIOUS_* env varsThe platform supports dual keys via security.jwt.previous-secret / previous-kid: during a rolling restart old tokens still verify, so you do not have to invalidate all active sessions at once. Retire the old key after one expiry window. Schedule it during a maintenance window, or stagger nodes behind a session-affinity-aware load balancer.
Verify
git grepagainst the repository turns up no committed secrets (pg_password,jwt.secret:, raw bearer tokens)./actuator/configprops(admin only) shows masked values for all fields under the sensitive-fields list.- Server logs at INFO level do not echo secrets; structured-log filters mask
password,secret,token,apiKey. - A rotated secret takes effect within one refresh cycle on every node; old credentials are rejected.
- CI pipelines source secrets from the CI vault integration, never from plaintext repository variables.