secret-adapters
SKILL.md
Secret Management Adapters (密鑰管理適配器)
Overview
@rytass/secret 系列套件提供統一的密鑰管理介面,目前支援 HashiCorp Vault 作為後端儲存。
套件清單
| 套件 | 說明 | 用途 |
|---|---|---|
@rytass/secret |
基礎介面 | 定義 SecretManager 抽象類別 |
@rytass/secret-adapter-vault |
Vault 適配器 | HashiCorp Vault 完整實現 |
@rytass/secret-adapter-vault-nestjs |
NestJS 模組 | Vault 的 NestJS 依賴注入整合 |
SecretManager 抽象類別
import { SecretManager } from '@rytass/secret';
// 所有 Secret Adapter 都繼承此抽象類別
abstract class SecretManager {
constructor(project: string);
get project(): string; // 取得初始化時的 path
abstract get<T>(key: string): Promise<T> | T;
abstract set<T>(key: string, value: T): Promise<void> | void;
abstract delete(key: string): Promise<void> | void;
}
Quick Start
安裝
# 直接使用 Vault
npm install @rytass/secret-adapter-vault
# NestJS 專案
npm install @rytass/secret-adapter-vault-nestjs
基本使用(離線模式)
import { VaultSecret } from '@rytass/secret-adapter-vault';
const vault = new VaultSecret('apps/myapp/config', {
host: 'https://vault.company.com',
online: false, // 離線模式(預設)
auth: {
account: 'myapp-service',
password: 'secure-password',
},
onReady: () => {
// 同步操作,無需 await
const dbPassword = vault.get<string>('DATABASE_PASSWORD');
// 修改本地快取(不立即同步到 Vault)
vault.set('API_KEY', 'new-value');
vault.delete('OLD_CONFIG');
// 或者立即同步到 Vault(syncToOnline = true)
vault.set('ANOTHER_KEY', 'value', true); // 立即同步
vault.delete('TEMP_KEY', true); // 立即同步
// 批次同步變更到 Vault
vault.sync(); // 如果版本不符會拋錯
vault.sync(true); // force = true,強制覆蓋
},
});
NestJS 整合
// app.module.ts
import { VaultModule } from '@rytass/secret-adapter-vault-nestjs';
@Module({
imports: [
VaultModule.forRoot({
path: '/secret/data/myapp',
fallbackFile: '.env.local', // Vault 不可用時的備用
}),
],
})
export class AppModule {}
// config.service.ts
@Injectable()
export class ConfigService {
constructor(private readonly vault: VaultService) {}
// get() 預設泛型為 string,可省略
async getDatabaseUrl(): Promise<string> {
return this.vault.get('DATABASE_URL'); // 等同 get<string>()
}
// set() 和 delete() 也支援 syncToOnline 參數
async updateConfig(key: string, value: string): Promise<void> {
await this.vault.set(key, value); // syncToOnline = false(預設)
await this.vault.set(key, value, true); // syncToOnline = true,立即同步
}
async removeConfig(key: string): Promise<void> {
await this.vault.delete(key); // syncToOnline = false(預設)
await this.vault.delete(key, true); // syncToOnline = true,立即同步
}
}
Core Concepts
線上模式 vs 離線模式
| 特性 | 線上模式 | 離線模式(預設) |
|---|---|---|
| 操作方式 | 即時連接 Vault | 使用本地快取 |
get() 返回 |
Promise<T> |
T(同步) |
set()/delete() 返回 |
Promise<void> |
Promise<void>(syncToOnline 時)或同步操作本地快取 |
| 效能 | 有網路延遲 | 極快 |
| 適用場景 | 需即時更新 | 頻繁讀取 |
| Token 管理 | 自動續期(預設 TTL 約 32 天) | 初始化時取得 |
狀態管理
enum VaultSecretState {
INIT = 'INIT', // 初始化中
READY = 'READY', // 可用
TERMINATED = 'TERMINATED', // 已終止
}
// 檢查狀態
if (vault.state === VaultSecretState.READY) {
// 可以安全操作
}
事件驅動
enum VaultEvents {
INITED = 'INITED', // 初始化開始
READY = 'READY', // Vault 準備就緒
TOKEN_RENEWED = 'TOKEN_RENEWED', // Token 已續期
TERMINATED = 'TERMINATED', // 連線已終止
ERROR = 'ERROR', // 發生錯誤
}
完整型別定義
以下所有型別皆從 @rytass/secret-adapter-vault 導出:
import {
VaultSecret,
VaultSecretState,
VaultEvents,
VaultAuthMethods,
VaultAuthMethodAccountPassword,
VaultSecretOptions,
VaultSecretOnlineOptions,
VaultSecretOfflineOptions,
VaultGetType,
VaultSetType,
VaultDeleteType,
VaultTokenRetrieveSuccessResponse,
VaultAPIFailedResponse,
VaultTokenRetrieveResponse,
VaultGetSecretSuccessResponse,
VaultGetSecretResponse,
} from '@rytass/secret-adapter-vault';
認證型別:
// 帳號密碼認證
interface VaultAuthMethodAccountPassword {
account: string;
password: string;
}
// 支援的認證方式(目前僅支援帳號密碼)
type VaultAuthMethods = VaultAuthMethodAccountPassword;
選項型別:
// 通用選項
interface VaultSecretOptions {
host: string; // Vault 伺服器位址
auth: VaultAuthMethods; // 認證方式
online?: boolean; // 是否使用線上模式(預設 false)
tokenTTL?: number; // Token TTL(毫秒),預設 2764724(約 32 天)
onError?: (error: string) => void; // 錯誤回調
onReady?: () => void; // 準備就緒回調
}
// 線上模式選項(online: true)
// 注意:獨立定義,非繼承自 VaultSecretOptions
interface VaultSecretOnlineOptions {
host: string;
auth: VaultAuthMethods;
online: true;
tokenTTL?: number;
onError?: (error: string) => void;
onReady?: () => void;
}
// 離線模式選項(online: false 或省略)
// 注意:獨立定義,非繼承自 VaultSecretOptions
interface VaultSecretOfflineOptions {
host: string;
auth: VaultAuthMethods;
online?: false;
tokenTTL?: number;
onError?: (error: string) => void;
onReady?: () => void;
}
條件型別(根據模式決定返回值):
// get() 返回型別:線上模式為 Promise<T>,離線模式為 T
type VaultGetType<O extends VaultSecretOptions, T> = O extends VaultSecretOnlineOptions ? Promise<T> : T;
// set() 返回型別
type VaultSetType<O extends VaultSecretOptions> = O extends VaultSecretOnlineOptions ? Promise<void> : void;
// delete() 返回型別
type VaultDeleteType<O extends VaultSecretOptions> = O extends VaultSecretOnlineOptions ? Promise<void> : void;
API 回應型別:
// API 失敗回應
interface VaultAPIFailedResponse {
errors: string[];
}
// Token 取得成功回應
// 注意:token_type 使用內部 enum VaultTokenType(未導出)
type VaultTokenRetrieveSuccessResponse = {
auth: {
client_token: string;
accessor: string;
policies: string[];
token_policies: string[];
metadata: Record<string, string> | null;
lease_duration: number;
renewable: boolean;
entity_id: string;
token_type: 'service' | 'batch'; // VaultTokenType enum(未導出)
orphan: boolean;
mfa_requirement: null;
num_uses: number;
};
} & VaultAPIBaseInfo<null>;
// Token 取得回應(成功或失敗)
type VaultTokenRetrieveResponse = VaultTokenRetrieveSuccessResponse | VaultAPIFailedResponse;
// Secret 取得成功回應
type VaultGetSecretSuccessResponse = VaultAPIBaseInfo<{
data: Record<string, unknown>;
metadata: {
created_time: string;
custom_metadata: null;
deletion_time: string;
destroyed: boolean;
version: number;
};
}>;
// Secret 取得回應(成功或失敗)
type VaultGetSecretResponse = VaultGetSecretSuccessResponse | VaultAPIFailedResponse;
Common Patterns
線上模式使用
const vault = new VaultSecret('apps/myapp/config', {
host: 'https://vault.company.com',
online: true,
auth: {
account: 'service-account',
password: 'password',
},
});
// 需要 await
const secret = await vault.get<string>('SECRET_KEY');
await vault.set('NEW_KEY', 'value');
await vault.delete('OLD_KEY');
TypeORM 整合
@Module({
imports: [
VaultModule.forRoot({ path: '/secret/data/database' }),
TypeOrmModule.forRootAsync({
imports: [VaultModule],
inject: [VaultService],
useFactory: async (vault: VaultService) => ({
type: 'postgres',
host: await vault.get<string>('DB_HOST'),
port: await vault.get<number>('DB_PORT'),
username: await vault.get<string>('DB_USERNAME'),
password: await vault.get<string>('DB_PASSWORD'),
database: await vault.get<string>('DB_NAME'),
}),
}),
],
})
export class DatabaseModule {}
JWT 模組整合
@Module({
imports: [
VaultModule.forRoot({ path: '/secret/data/auth' }),
JwtModule.registerAsync({
imports: [VaultModule],
inject: [VaultService],
useFactory: async (vault: VaultService) => ({
secret: await vault.get<string>('JWT_SECRET'),
signOptions: {
expiresIn: await vault.get<string>('JWT_EXPIRY') || '1h',
},
}),
}),
],
})
export class AuthModule {}
錯誤處理與備用機制
const vault = new VaultSecret('apps/myapp/config', {
host: 'https://vault.company.com',
online: false,
auth: { account: 'user', password: 'pass' },
onError: (error) => {
console.error('Vault error:', error);
// 可在此切換到備用配置
},
onReady: () => {
console.log('Vault ready');
},
});
定期同步(離線模式)
// 離線模式下定期同步變更
setInterval(async () => {
try {
// sync() 會檢查版本,如果 Vault 上的版本與快取不符會拋出錯誤
await vault.sync();
console.log('Synced to Vault');
} catch (error) {
if (error.message.includes('version is not match')) {
// 版本不符時,可選擇強制覆蓋
await vault.sync(true); // force = true
console.log('Force synced to Vault');
} else {
console.warn('Sync failed:', error);
}
}
}, 300000); // 每 5 分鐘
set() / delete() 的 syncToOnline 參數
// 離線模式下,set() 和 delete() 預設只修改本地快取
vault.set('KEY', 'value'); // syncToOnline = false(預設)
vault.delete('KEY'); // syncToOnline = false(預設)
// 如果需要立即同步到 Vault,傳入 syncToOnline = true
vault.set('KEY', 'value', true); // 立即同步到 Vault
vault.delete('KEY', true); // 立即同步到 Vault
優雅關閉
process.on('SIGTERM', () => {
vault.terminate();
process.exit(0);
});
NestJS Module Reference
VaultModuleOptions
// NestJS 模組配置選項
interface VaultModuleOptions {
path: string; // Vault secret path(預設 '/')
fallbackFile?: string; // Vault 不可用時的備用 .env 檔案
}
VaultService 方法簽名
class VaultService {
// get() 預設泛型為 string,可省略
async get<T = string>(key: string): Promise<T>;
// set() 支援 syncToOnline 參數
async set<T = string>(key: string, value: T, syncToOnline = false): Promise<void>;
// delete() 支援 syncToOnline 參數
async delete(key: string, syncToOnline = false): Promise<void>;
}
Environment Variables (NestJS)
# 必需(若未設置 VAULT_HOST,會自動切換至備用模式)
VAULT_HOST=https://vault.example.com:8200
VAULT_ACCOUNT=your-username
VAULT_PASSWORD=your-password
注意:
path不是透過環境變數設定,而是在VaultModule.forRoot({ path: '...' })中指定。
API Reference
詳細 API 文件請參閱 reference.md。
Troubleshooting
連線失敗
- 確認
VAULT_HOST使用 HTTPS - 檢查網路連線和防火牆
- 驗證帳號密碼正確
Token 過期
線上模式會自動續期。如果仍然過期:
- 檢查
tokenTTL設定 - 確認 Vault 伺服器時間同步
- 查看 Vault Token 政策設定
NestJS 備用模式
當 VAULT_HOST 未設置時或連線失敗時,VaultService 會切換至備用模式:
get()從 ConfigService(環境變數)讀取set()和delete()會拋出錯誤:"Cannot set/delete value when fallback to env file is enabled."
// 備用模式下的行為
try {
await vaultService.set('KEY', 'value');
} catch (error) {
// Error: Cannot set value when fallback to env file is enabled.
console.log('Vault 備用模式不支援寫入操作');
}
Weekly Installs
6
Repository
rytass/utilsGitHub Stars
6
First Seen
Feb 5, 2026
Security Audits
Installed on
amp6
github-copilot6
replit6
codex6
kimi-cli6
gemini-cli6