Files
stack/apps/api/src/vault/vault.health.ts
Jason Woltje dd171b287f
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
feat(#353): Create VaultService NestJS module for OpenBao Transit
Implements secure credential encryption using OpenBao Transit API with
automatic fallback to AES-256-GCM when OpenBao is unavailable.

Features:
- AppRole authentication with automatic token renewal at 50% TTL
- Transit encrypt/decrypt with 4 named keys
- Automatic fallback to CryptoService when OpenBao unavailable
- Auto-detection of ciphertext format (vault:v1: vs AES)
- Request timeout protection (5s default)
- Health indicator for monitoring
- Backward compatible with existing AES-encrypted data

Security:
- ERROR-level logging for fallback
- Proper error propagation (no silent failures)
- Request timeouts prevent hung operations
- Secure credential file reading

Migrations:
- Account encryption middleware uses VaultService
- Uses TransitKey.ACCOUNT_TOKENS for OAuth tokens
- Backward compatible with existing encrypted data

Tests: 56 tests passing (36 VaultService + 20 middleware)

Closes #353

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:13:05 -06:00

52 lines
1.2 KiB
TypeScript

/**
* Vault Health Indicator
*
* Health check for OpenBao connectivity and encryption status.
*/
import { Injectable } from "@nestjs/common";
import { VaultService } from "./vault.service";
export interface VaultHealthStatus {
status: "up" | "down";
available: boolean;
fallbackMode: boolean;
endpoint: string;
message?: string;
}
@Injectable()
export class VaultHealthIndicator {
constructor(private readonly vaultService: VaultService) {}
/**
* Check OpenBao health status
*
* @returns Health status object
*/
check(): VaultHealthStatus {
try {
const status = this.vaultService.getStatus();
return {
status: status.available ? "up" : "down",
available: status.available,
fallbackMode: status.fallbackMode,
endpoint: status.endpoint,
message: status.available
? "OpenBao Transit encryption enabled"
: "Using fallback AES-256-GCM encryption",
};
} catch (error: unknown) {
const errorMsg = error instanceof Error ? error.message : "Unknown error";
return {
status: "down",
available: false,
fallbackMode: true,
endpoint: "unknown",
message: `Health check failed: ${errorMsg}`,
};
}
}
}