Skill: zuiho-kai/vault (version 1.1.2)

--- name: vault description: Secure local password storage tool with AES-256-GCM encryption. Store, retrieve, and manage passwords with CLI commands. version: 1.1.2 author: zuiho-kai homepage: https://github.com/zuiho-kai/openclaw-vault tags: [password, vault, security, credentials, password-manager, cli, encryption] metadata: openclaw: requires: bins: [node, npm] env: [VAULT_MASTER_KEY] --- # vault **Use when** you need secure local storage for passwords, API keys, or credentials. 🔒 **AES-256-GCM encryption** - This plugin stores passwords encrypted using industry-standard AES-256-GCM encryption with a master key. ## Features - 🔒 AES-256-GCM encryption for all stored passwords - 📝 Simple command-line interface - 🗂️ Key management and listing - 💾 JSON-based local storage (encrypted) - 🕐 Automatic timestamp tracking - 🔑 Master key protection ## Installation ```bash clawhub install vault ``` ## Usage ### Set a password ```bash vault gemini sk-abc123xyz ``` ### Show a password ```bash vault gemini show ``` ### Remove a password ```bash vault gemini remove ``` ### List all keys ```bash vault list ``` ## Configuration ### Master Key (Required) Set your master encryption key via environment variable: ```bash export VAULT_MASTER_KEY="your-secure-master-key-here" ``` Or in your OpenClaw config: ```json { "plugins": { "vault": { "masterKey": "your-secure-master-key-here", "storageFile": ".vault/passwords.json" } } } ``` **Options:** - `masterKey` - Master encryption key (can also use VAULT_MASTER_KEY env var) - `storageFile` (default: `.vault/passwords.json`) - Storage file path relative to home directory ⚠️ **Important**: Keep your master key secure! Without it, you cannot decrypt stored passwords. ## Security 🔒 **Encryption Details**: - **Algorithm**: AES-256-GCM (Galois/Counter Mode) - **Key Derivation**: scrypt with random salt per password - **IV**: Random 12-byte initialization vector per password (GCM recommended size) - **Salt**: Random 32-byte salt per password, stored with encrypted data - **Authentication**: GCM authentication tag for integrity verification **Security Best Practices**: - Use a strong, unique master key (minimum 32 characters recommended) - Store master key securely (environment variable or secure config) - Set strict file permissions: `chmod 600 ~/.vault/passwords.json` - Add `.vault/` to your `.gitignore` - Never commit your master key to version control - Use system-level disk encryption for additional protection - Backup your master key securely - lost keys mean lost passwords **Suitable for**: - Development/testing credentials - API keys and tokens - Personal passwords - Team shared credentials (with secure key distribution) ## Examples ```bash # Save API keys vault openai sk-proj-abc123 vault anthropic sk-ant-xyz789 # View a key vault openai show # Output: Password for 'openai': sk-proj-abc123 # List all keys vault list # Output: # Stored passwords: # • openai (created: 2026-02-17T..., updated: 2026-02-17T...) # • anthropic (created: 2026-02-17T..., updated: 2026-02-17T...) # Remove a key vault openai remove ``` ## Links - GitHub: https://github.com/zuiho-kai/openclaw-vault - Issues: https://github.com/zuiho-kai/openclaw-vault/issues

path: CHANGELOG.md
content: # Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.2] - 2026-02-17

### Security
- **CRITICAL**: Fixed security issue with fixed salt value
- Now uses random 32-byte salt per password (stored with encrypted data)
- Changed IV size from 16 bytes to 12 bytes (GCM recommended size)
- Each password now has unique salt for better key derivation security

### Changed
- Added `requiredEnv: ["VAULT_MASTER_KEY"]` to openclaw.plugin.json
- Updated encryption implementation to generate and store random salt per entry
- Modified decrypt function to use stored salt for key derivation
- Updated documentation to reflect security improvements

### Fixed
- Registry metadata now properly declares required VAULT_MASTER_KEY environment variable
- Addressed security concern about fixed salt reducing KDF effectiveness

## [1.1.0] - 2026-02-17

### Added
- **AES-256-GCM encryption** for all stored passwords
- Master key support via `VAULT_MASTER_KEY` environment variable or config
- Encryption class with scrypt key derivation
- Random IV (initialization vector) generation per password
- GCM authentication tags for integrity verification

### Changed
- Updated from plain text to encrypted storage format
- Modified `VaultStorage` class to support encryption/decryption
- Updated all documentation to reflect encryption features
- Changed description from "simple" to "secure" password storage
- Version bumped to 1.1.0

### Security
- **BREAKING**: Requires master key to be set (VAULT_MASTER_KEY or config.masterKey)
- Passwords now encrypted with AES-256-GCM before storage
- Each password uses unique random IV for enhanced security
- Authentication tags ensure data integrity
- Existing plain text passwords will need to be re-entered after upgrade

## [1.0.1] - 2026-02-17

### Changed
- Updated documentation to clearly state plain text storage
- Removed misleading "encryptionEnabled" config option (was not implemented)
- Added explicit security warnings about appropriate use cases
- Changed description from "secure" to "simple" password storage

### Security
- Clarified that this plugin uses plain text storage
- Added warnings about not using for production/sensitive credentials
- Recommended proper password managers for high-value secrets

## [1.0.0] - 2026-02-17

### Added
- Initial release
- Password storage and retrieval
- Commands: set, show, remove, list
- JSON-based local storage
- Automatic timestamp tracking (created/updated)
- Basic configuration support

### Security
- Plain text storage (encryption planned for future release)
- Local file system storage in user home directory


path: index.js
content: /**
 * OpenClaw Vault Plugin
 *
 * 安全密码管理工具（支持 AES-256-GCM 加密）
 *
 * 命令:
 *   vault <key> <password>  - 设置密码
 *   vault <key> show        - 显示密码
 *   vault <key> remove      - 删除密码
 *   vault list              - 列出所有密钥
 */

import { readFile, writeFile, mkdir } from 'fs/promises';
import { join, dirname } from 'path';
import { homedir } from 'os';
import { existsSync } from 'fs';
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto';

// ============================================================================
// 加密工具
// ============================================================================

class Encryption {
  constructor(masterKey) {
    this.masterKey = masterKey;
    this.algorithm = 'aes-256-gcm';
  }

  encrypt(text) {
    // 生成随机盐和 IV（12 字节 IV 是 GCM 推荐大小）
    const salt = randomBytes(32);
    const iv = randomBytes(12);

    // 使用随机盐派生密钥
    const key = scryptSync(this.masterKey, salt, 32);
    const cipher = createCipheriv(this.algorithm, key, iv);

    let encrypted = cipher.update(text, 'utf8', 'base64');
    encrypted += cipher.final('base64');

    const authTag = cipher.getAuthTag();

    return {
      encrypted,
      salt: salt.toString('base64'),
      iv: iv.toString('base64'),
      authTag: authTag.toString('base64')
    };
  }

  decrypt(encryptedData) {
    // 使用存储的盐派生密钥
    const key = scryptSync(
      this.masterKey,
      Buffer.from(encryptedData.salt, 'base64'),
      32
    );

    const decipher = createDecipheriv(
      this.algorithm,
      key,
      Buffer.from(encryptedData.iv, 'base64')
    );

    decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'base64'));

    let decrypted = decipher.update(encryptedData.encrypted, 'base64', 'utf8');
    decrypted += decipher.final('utf8');

    return decrypted;
  }
}

// ============================================================================
// 存储管理
// ============================================================================

class VaultStorage {
  constructor(storagePath, encryption) {
    this.storagePath = join(homedir(), storagePath);
    this.encryption = encryption;
  }

  async ensureStorage() {
    const dir = dirname(this.storagePath);
    if (!existsSync(dir)) {
      await mkdir(dir, { recursive: true });
    }
    if (!existsSync(this.storagePath)) {
      await writeFile(this.storagePath, JSON.stringify({}, null, 2));
    }
  }

  async load() {
    await this.ensureStorage();
    const content = await readFile(this.storagePath, 'utf-8');
    return JSON.parse(content);
  }

  async save(data) {
    await this.ensureStorage();
    await writeFile(this.storagePath, JSON.stringify(data, null, 2));
  }

  async set(key, password) {
    const data = await this.load();
    const encryptedPassword = this.encryption.encrypt(password);

    data[key] = {
      [REDACTED_SECRET],
      createdAt: data[key]?.createdAt || new Date().toISOString(),
      updatedAt: new Date().toISOString()
    };
    await this.save(data);
    return true;
  }

  async get(key) {
    const data = await this.load();
    const entry = data[key];

    if (!entry) {
      return null;
    }

    // 解密密码
    const decryptedPassword = this.encryption.decrypt(entry.password);

    return {
      [REDACTED_SECRET],
      createdAt: entry.createdAt,
      updatedAt: entry.updatedAt
    };
  }

  async remove(key) {
    const data = await this.load();
    if (!(key in data)) {
      return false;
    }
    delete data[key];
    await this.save(data);
    return true;
  }

  async list() {
    const data = await this.load();
    return Object.keys(data).map(key => ({
      key,
      createdAt: data[key].createdAt,
      updatedAt: data[key].updatedAt
    }));
  }
}

// ============================================================================
// 插件导出
// ============================================================================

export default function vaultPlugin(api) {
  const config = api.getConfig() || {};
  const storagePath = config.storageFile || '.vault/passwords.json';

  // 获取主密钥
  const masterKey = process.env.VAULT_MASTER_KEY || config.masterKey;

  if (!masterKey) {
    console.error('⚠️  VAULT_MASTER_KEY not set!');
    console.error('Please set environment variable: export VAULT_MASTER_KEY="your-secure-key"');
    console.error('Or add to config: { "plugins": { "vault": { "masterKey": "your-secure-key" } } }');
    throw new Error('VAULT_MASTER_KEY is required for encryption');
  }

  const encryption = new Encryption(masterKey);
  const storage = new VaultStorage(storagePath, encryption);

  // 注册 vault 命令
  api.registerSkill({
    id: 'vault',
    name: 'Vault',
    description: 'Password management tool',

    async execute(args) {
      const parts = args.trim().split(/\s+/);

      // vault list
      if (parts[0] === 'list') {
        const entries = await storage.list();
        if (entries.length === 0) {
          return { success: true, message: 'No passwords stored.' };
        }

        const list = entries.map(e =>
          `• ${e.key} (created: ${e.createdAt}, updated: ${e.updatedAt})`
        ).join('\n');

        return {
          success: true,
          message: `Stored passwords:\n${list}`
        };
      }

      // vault <key> show
      if (parts.length === 2 && parts[1] === 'show') {
        const key = parts[0];
        const entry = await storage.get(key);

        if (!entry) {
          return { success: false, message: `Password for '${key}' not found.` };
        }

        return {
          success: true,
          message: `Password for '${key}': ${entry.password}`
        };
      }

      // vault <key> remove
      if (parts.length === 2 && parts[1] === 'remove') {
        const key = parts[0];
        const removed = await storage.remove(key);

        if (!removed) {
          return { success: false, message: `Password for '${key}' not found.` };
        }

        return {
          success: true,
          message: `Password for '${key}' removed.`
        };
      }

      // vault <key> <password>
      if (parts.length === 2) {
        const [key, password] = parts;
        await storage.set(key, password);

        return {
          success: true,
          message: `Password for '${key}' saved.`
        };
      }

      // 无效命令
      return {
        success: false,
        message: `Usage:
  vault <key> <password>  - Set password
  vault <key> show        - Show password
  vault <key> remove      - Remove password
  vault list              - List all keys`
      };
    }
  });

  return {
    id: 'vault',
    name: 'Vault',
    version: '1.1.2'
  };
}


path: openclaw.plugin.json
content: {
  "id": "vault",
  "kind": "skill",
  "name": "Vault",
  "version": "1.1.2",
  "description": "安全的本地密码存储工具（AES-256-GCM 加密）",
  "requiredEnv": ["VAULT_MASTER_KEY"],
  "configSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "masterKey": {
        "type": "string",
        "description": "主加密密钥（也可使用 VAULT_MASTER_KEY 环境变量）"
      },
      "storageFile": {
        "type": "string",
        "default": ".vault/passwords.json",
        "description": "密码存储文件路径（相对于用户目录）"
      }
    }
  },
  "uiHints": {
    "masterKey": {
      "label": "Master Key",
      "placeholder": "your-secure-master-key",
      "help": "Master encryption key (can also use VAULT_MASTER_KEY env var)",
      "secret": true
    },
    "storageFile": {
      "label": "Storage File",
      "placeholder": ".vault/passwords.json",
      "advanced": true,
      "help": "Password storage file path relative to home directory"
    }
  }
}


path: package-lock.json
content: {
  "name": "openclaw-vault",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "openclaw-vault",
      "version": "1.0.0",
      "license": "MIT",
      "engines": {
        "node": ">=18.0.0"
      }
    }
  }
}


path: package.json
content: {
  "name": "openclaw-vault",
  "version": "1.1.2",
  "description": "OpenClaw plugin for secure local password storage with AES-256-GCM encryption",
  "type": "module",
  "main": "index.js",
  "keywords": [
    "openclaw",
    "plugin",
    "password",
    "vault",
    "security",
    "credentials",
    "encryption",
    "aes-256-gcm"
  ],
  "author": "zuiho-kai",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/zuiho-kai/openclaw-vault.git"
  },
  "bugs": {
    "url": "https://github.com/zuiho-kai/openclaw-vault/issues"
  },
  "homepage": "https://github.com/zuiho-kai/openclaw-vault#readme",
  "dependencies": {},
  "engines": {
    "node": ">=18.0.0"
  }
}


path: README.md
content: # Vault - Secure Password Storage

🔒 **AES-256-GCM encryption** - Secure local password storage with industry-standard encryption.

Secure local password storage tool with CLI interface and AES-256-GCM encryption.

## Features

- 🔒 AES-256-GCM encryption for all stored passwords
- 📝 Simple command-line interface
- 🗂️ Key management and listing
- 💾 JSON-based local storage (encrypted)
- 🕐 Automatic timestamp tracking
- 🔑 Master key protection

## 安装

```bash
cd /path/to/openclaw-vault
npm install
openclaw plugins link $(pwd)
```

## 配置主密钥

**必需**: 设置主加密密钥

通过环境变量：

```bash
export VAULT_MASTER_KEY="your-secure-master-key-here"
```

或在 OpenClaw 配置中：

```json
{
  "plugins": {
    "vault": {
      "masterKey": "your-secure-master-key-here",
      "storageFile": ".vault/passwords.json"
    }
  }
}
```

⚠️ **重要**: 请妥善保管主密钥！没有它将无法解密已存储的密码。

## 使用方法

### 设置密码

```bash
vault gemini sk-abc123xyz
```

保存密钥 `gemini` 的密码为 `sk-abc123xyz`。

### 显示密码

```bash
vault gemini show
```

显示密钥 `gemini` 的密码。

### 删除密码

```bash
vault gemini remove
```

删除密钥 `gemini` 的密码。

### 列出所有密钥

```bash
vault list
```

显示所有已保存的密钥及其创建/更新时间。

## 配置

在 OpenClaw 配置中可以自定义：

```json
{
  "plugins": {
    "vault": {
      "masterKey": "your-secure-master-key-here",
      "storageFile": ".vault/passwords.json"
    }
  }
}
```

### 配置选项

- `masterKey`: 主加密密钥（也可使用 VAULT_MASTER_KEY 环境变量）
- `storageFile`: 密码存储文件路径（相对于用户主目录）

## 存储位置

默认存储在 `~/.vault/passwords.json`（加密格式）

## 安全特性

🔒 **加密详情**:

- **算法**: AES-256-GCM (Galois/Counter Mode)
- **密钥派生**: scrypt 使用随机盐（每个密码独立）
- **初始化向量**: 每个密码使用随机 12 字节 IV（GCM 推荐大小）
- **盐值**: 每个密码使用随机 32 字节盐，与加密数据一起存储
- **认证**: GCM 认证标签确保完整性验证

**安全最佳实践**：
1. 使用强壮且唯一的主密钥（建议至少 32 字符）
2. 安全存储主密钥（环境变量或安全配置）
3. 设置严格的文件权限：`chmod 600 ~/.vault/passwords.json`
4. 将 `.vault/` 添加到 `.gitignore`
5. 永远不要将主密钥提交到版本控制
6. 使用系统级磁盘加密提供额外保护
7. 安全备份主密钥 - 丢失密钥意味着丢失所有密码

**适用于**：
- 开发/测试凭据
- API 密钥和令牌
- 个人密码
- 团队共享凭据（需安全分发密钥）

## 示例

```bash
# 保存 API 密钥
vault openai sk-proj-abc123
vault anthropic sk-ant-xyz789

# 查看密钥
vault openai show
# 输出: Password for 'openai': sk-proj-abc123

# 列出所有密钥
vault list
# 输出:
# Stored passwords:
# • openai (created: 2026-02-17T..., updated: 2026-02-17T...)
# • anthropic (created: 2026-02-17T..., updated: 2026-02-17T...)

# 删除密钥
vault openai remove
```

## 许可证

MIT

## 作者

zuiho-kai


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)