- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
10 KiB
Phoenix Vault Integration Guide
Last Updated: 2026-01-31
Document Version: 1.0
Status: Active Documentation
Date: 2026-01-19
Status: ✅ Ready for Integration
Purpose: Guide for integrating Phoenix services with Vault cluster
Overview
This guide provides step-by-step instructions for integrating Phoenix services (API, Portal, and other components) with the deployed Vault cluster.
Prerequisites
- Vault cluster deployed and operational
- AppRole authentication configured
- AppRole credentials available
- Phoenix services ready for integration
AppRole Credentials
AppRole credentials are stored in:
.secure/vault-credentials/phoenix-approle-credentials-YYYYMMDD.txt
Phoenix API:
- Role ID:
27f213e2-f15e-b6de-3cf4-db2f02029dd5 - Secret ID: (stored in credentials file)
Phoenix Portal:
- Role ID:
70278dee-a85e-9007-c769-46b71a8c1460 - Secret ID: (stored in credentials file)
Integration Methods
Method 1: Environment Variables (Recommended)
Set environment variables in your Phoenix service configuration:
export VAULT_ADDR=http://192.168.11.200:8200
export VAULT_ROLE_ID=<your-role-id>
export VAULT_SECRET_ID=<your-secret-id>
Method 2: Configuration Files
Create a Vault configuration file (e.g., vault-config.json):
{
"vault_addr": "http://10.160.0.40:8200",
"role_id": "<your-role-id>",
"secret_id": "<your-secret-id>"
}
⚠️ Security: Never commit configuration files with credentials to Git. Use environment variables or secure secret management.
Phoenix API Integration
Node.js/TypeScript Example
import Vault from 'node-vault';
// Initialize Vault client
const vault = Vault({
endpoint: process.env.VAULT_ADDR || 'http://192.168.11.200:8200',
token: await authenticateWithAppRole(
process.env.VAULT_ROLE_ID!,
process.env.VAULT_SECRET_ID!
)
});
// Authenticate with AppRole
async function authenticateWithAppRole(roleId: string, secretId: string): Promise<string> {
const response = await fetch(`${process.env.VAULT_ADDR}/v1/auth/approle/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ role_id: roleId, secret_id: secretId })
});
const data = await response.json();
return data.auth.client_token;
}
// Get database credentials
async function getDatabaseCredentials() {
const response = await vault.read('secret/data/phoenix/database/postgres');
return {
username: response.data.data.username,
password: response.data.data.password,
host: response.data.data.host,
port: response.data.data.port,
database: response.data.data.database
};
}
// Get JWT secrets
async function getJWTSecrets() {
const response = await vault.read('secret/data/phoenix/api/jwt-secrets');
return {
accessTokenSecret: response.data.data['access-token-secret'],
refreshTokenSecret: response.data.data['refresh-token-secret']
};
}
Python Example
import hvac
import os
# Initialize Vault client
client = hvac.Client(url=os.getenv('VAULT_ADDR', 'http://10.160.0.40:8200'))
# Authenticate with AppRole
role_id = os.getenv('VAULT_ROLE_ID')
secret_id = os.getenv('VAULT_SECRET_ID')
client.auth.approle.login(role_id=role_id, secret_id=secret_id)
# Get database credentials
db_secrets = client.secrets.kv.v2.read_secret_version(path='phoenix/database/postgres')
db_config = db_secrets['data']['data']
# Get JWT secrets
jwt_secrets = client.secrets.kv.v2.read_secret_version(path='phoenix/api/jwt-secrets')
jwt_config = jwt_secrets['data']['data']
Phoenix Portal Integration
Similar to API integration, but use the phoenix-portal AppRole:
// Portal-specific Vault configuration
const vault = Vault({
endpoint: process.env.VAULT_ADDR || 'http://192.168.11.200:8200',
token: await authenticateWithAppRole(
process.env.VAULT_PORTAL_ROLE_ID!,
process.env.VAULT_PORTAL_SECRET_ID!
)
});
// Get JWT secrets for portal
const jwtSecrets = await vault.read('secret/data/phoenix/api/jwt-secrets');
Secret Paths Reference
Available Secret Paths
| Path | Description | Access Policy |
|---|---|---|
secret/data/phoenix/api/jwt-secrets |
JWT signing secrets | phoenix-api, phoenix-portal |
secret/data/phoenix/api/api-keys |
API keys | phoenix-api |
secret/data/phoenix/database/postgres |
PostgreSQL credentials | phoenix-api |
secret/data/phoenix/database/redis |
Redis credentials | phoenix-api |
secret/data/phoenix/keycloak/admin-credentials |
Keycloak admin | phoenix-api |
secret/data/phoenix/keycloak/oidc-secrets |
OIDC client secrets | phoenix-api |
secret/data/phoenix/services/blockchain |
Blockchain RPC/keys | phoenix-api |
secret/data/phoenix/services/integrations |
Integration tokens | phoenix-api |
Reading Secrets
# Using Vault CLI
export VAULT_ADDR=http://192.168.11.200:8200
export VAULT_TOKEN=$(vault write -field=token auth/approle/login \
role_id=$VAULT_ROLE_ID secret_id=$VAULT_SECRET_ID)
# Read secret
vault kv get secret/phoenix/database/postgres
Token Management
Token Lifecycle
- Token TTL: 1 hour (default)
- Token Max TTL: 4 hours
- Secret ID TTL: 24 hours
Token Renewal
Tokens should be renewed before expiration:
// Renew token
async function renewToken(token: string): Promise<void> {
await fetch(`${process.env.VAULT_ADDR}/v1/auth/token/renew-self`, {
method: 'POST',
headers: {
'X-Vault-Token': token
}
});
}
Automatic Token Refresh
Implement automatic token refresh in your services:
class VaultClient {
private token: string | null = null;
private tokenExpiry: Date | null = null;
async getToken(): Promise<string> {
if (!this.token || this.isTokenExpired()) {
await this.authenticate();
}
return this.token!;
}
private isTokenExpired(): boolean {
if (!this.tokenExpiry) return true;
return new Date() >= this.tokenExpiry;
}
private async authenticate(): Promise<void> {
const response = await fetch(`${process.env.VAULT_ADDR}/v1/auth/approle/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
role_id: process.env.VAULT_ROLE_ID,
secret_id: process.env.VAULT_SECRET_ID
})
});
const data = await response.json();
this.token = data.auth.client_token;
this.tokenExpiry = new Date(Date.now() + data.auth.lease_duration * 1000);
}
}
Error Handling
Common Errors
1. Authentication Failed
Error: invalid role_id or secret_id
Solution: Verify AppRole credentials are correct.
2. Permission Denied
Error: permission denied
Solution: Check that the AppRole has the correct policy attached.
3. Secret Not Found
Error: no secret found at path
Solution: Verify the secret path exists and is accessible.
Retry Logic
Implement retry logic for transient failures:
async function getSecretWithRetry(path: string, maxRetries = 3): Promise<any> {
for (let i = 0; i < maxRetries; i++) {
try {
return await vault.read(path);
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
Security Best Practices
-
Never Hardcode Credentials
- Use environment variables
- Use secure secret injection
- Rotate credentials regularly
-
Use Least Privilege
- Each service should only access secrets it needs
- Use separate AppRoles for different services
- Review policies regularly
-
Monitor Access
- Enable audit logging
- Monitor token usage
- Alert on suspicious activity
-
Token Management
- Implement automatic token renewal
- Handle token expiration gracefully
- Use short token TTLs
-
Network Security
- Use TLS in production
- Restrict network access to Vault
- Use firewall rules
Testing Integration
Test Script
#!/bin/bash
# Test Vault integration
export VAULT_ADDR=http://192.168.11.200:8200
export VAULT_ROLE_ID=<your-role-id>
export VAULT_SECRET_ID=<your-secret-id>
# Authenticate
TOKEN=$(vault write -field=token auth/approle/login \
role_id=$VAULT_ROLE_ID secret_id=$VAULT_SECRET_ID)
export VAULT_TOKEN=$TOKEN
# Test secret access
echo "Testing secret access..."
vault kv get secret/phoenix/api/jwt-secrets
vault kv get secret/phoenix/database/postgres
echo "✅ Integration test passed"
Troubleshooting
Cannot Connect to Vault
- Check network connectivity:
curl http://10.160.0.40:8200/v1/sys/health
- Verify Vault is unsealed:
vault status
- Check firewall rules
Authentication Fails
- Verify credentials:
vault read auth/approle/role/phoenix-api/role-id
- Check AppRole is enabled:
vault auth list
- Verify policy is attached:
vault read auth/approle/role/phoenix-api
Permission Denied
- Check policy:
vault policy read phoenix-api-policy
- Verify secret path:
vault kv list secret/phoenix/
- Check token capabilities:
vault token capabilities secret/data/phoenix/api/jwt-secrets
Migration from Existing Secrets
If migrating from existing .env files or other secret storage:
-
Inventory Current Secrets
- List all secrets currently in use
- Map to Vault paths
-
Create Secrets in Vault
vault kv put secret/phoenix/database/postgres \ username=current_username \ password=current_password \ host=current_host \ port=5432 \ database=phoenix -
Update Services
- Replace
.envfile reading with Vault client - Test thoroughly
- Deploy gradually
- Replace
-
Remove Old Secrets
- Delete
.envfiles after migration - Update
.gitignoreif needed
- Delete
Related Documentation
- Phoenix Vault Cluster Deployment
- Vault TLS Configuration
- Master Secrets Inventory
- HashiCorp Vault Documentation
Status: ✅ Ready for Integration
Last Updated: 2026-01-19