update security migration documents

This commit is contained in:
Cytown
2026-03-24 13:38:13 +08:00
parent ce1619051d
commit de11f95b65
7 changed files with 1063 additions and 783 deletions
+114 -22
View File
@@ -454,6 +454,70 @@ This design also enables **multi-agent support** with flexible provider selectio
- **Load balancing**: Distribute requests across multiple endpoints
- **Centralized configuration**: Manage all providers in one place
#### 🔒 Security Configuration (Recommended)
PicoClaw supports separating sensitive data (API keys, tokens, secrets) from your main configuration by storing them in a `.security.yml` file.
**Key Benefits:**
- **Security**: Sensitive data is never in your main config file
- **Easy sharing**: Share config.json without exposing API keys
- **Version control**: Add `.security.yml` to `.gitignore`
- **Flexible deployment**: Different environments can use different security files
**Quick Setup:**
1. Create `~/.picoclaw/.security.yml` with your API keys:
```yaml
model_list:
gpt-5.4:
api_keys:
- "sk-proj-your-actual-openai-key"
claude-sonnet-4.6:
api_keys:
- "sk-ant-your-actual-anthropic-key"
channels:
telegram:
token: "your-telegram-bot-token"
web:
brave:
api_keys:
- "BSAyour-brave-api-key"
glm_search:
api_key: "your-glm-search-api-key"
```
2. Set proper permissions:
```bash
chmod 600 ~/.picoclaw/.security.yml
```
3. Remove sensitive fields from `config.json` (recommended):
```json
{
"model_list": [
{
"model_name": "gpt-5.4",
"model": "openai/gpt-5.4"
// api_key loaded from .security.yml
}
],
"channels": {
"telegram": {
"enabled": true"
// token loaded from .security.yml
}
}
}
```
**How it works:**
- Values from `.security.yml` are automatically mapped to config fields
- No special syntax needed — just omit sensitive fields from config.json
- If a field exists in both files, `.security.yml` value takes precedence
- You can mix direct values in config.json with security values
For complete documentation, see [`security_configuration.md`](security_configuration.md).
#### All Supported Vendors
| Vendor | `model` Prefix | Default API Base | Protocol | API Key |
@@ -515,16 +579,20 @@ This design also enables **multi-agent support** with flexible provider selectio
}
```
> **Security Note**: You can remove `api_key` fields from your config and store them in `.security.yml` instead. See [Security Configuration](#-security-configuration-recommended) above for details.
#### Vendor-Specific Examples
> **Tip**: You can omit `api_key` fields and store them in `.security.yml` for better security. See [Security Configuration](#-security-configuration-recommended).
<details>
<summary><b>OpenAI</b></summary>
```json
{
"model_name": "gpt-5.4",
"model": "openai/gpt-5.4",
"api_key": "sk-..."
"model": "openai/gpt-5.4"
// api_key: set in .security.yml
}
```
@@ -536,8 +604,8 @@ This design also enables **multi-agent support** with flexible provider selectio
```json
{
"model_name": "ark-code-latest",
"model": "volcengine/ark-code-latest",
"api_key": "sk-..."
"model": "volcengine/ark-code-latest"
// api_key: set in .security.yml
}
```
@@ -549,8 +617,8 @@ This design also enables **multi-agent support** with flexible provider selectio
```json
{
"model_name": "glm-4.7",
"model": "zhipu/glm-4.7",
"api_key": "your-key"
"model": "zhipu/glm-4.7"
// api_key: set in .security.yml
}
```
@@ -562,8 +630,8 @@ This design also enables **multi-agent support** with flexible provider selectio
```json
{
"model_name": "deepseek-chat",
"model": "deepseek/deepseek-chat",
"api_key": "sk-..."
"model": "deepseek/deepseek-chat"
// api_key: set in .security.yml
}
```
@@ -575,8 +643,8 @@ This design also enables **multi-agent support** with flexible provider selectio
```json
{
"model_name": "claude-sonnet-4.6",
"model": "anthropic/claude-sonnet-4.6",
"api_key": "sk-ant-your-key"
"model": "anthropic/claude-sonnet-4.6"
// api_key: set in .security.yml
}
```
@@ -616,8 +684,8 @@ For direct Anthropic API access or custom endpoints that only support Anthropic'
{
"model_name": "my-custom-model",
"model": "openai/custom-model",
"api_base": "https://my-proxy.com/v1",
"api_key": "sk-..."
"api_base": "https://my-proxy.com/v1"
// api_key: set in .security.yml
}
```
@@ -629,6 +697,33 @@ PicoClaw strips only the outer `litellm/` prefix before sending the request, so
Configure multiple endpoints for the same model name — PicoClaw will automatically round-robin between them:
**Option 1: Multiple API Keys in .security.yml (Recommended)**
```yaml
# .security.yml
model_list:
gpt-5.4:
api_keys:
- "sk-proj-key-1"
- "sk-proj-key-2"
```
```json
// config.json
{
"model_list": [
{
"model_name": "gpt-5.4",
"model": "openai/gpt-5.4",
"api_base": "https://api.openai.com/v1"
// api_keys loaded from .security.yml
}
]
}
```
**Option 2: Multiple Model Entries**
```json
{
"model_list": [
@@ -685,6 +780,8 @@ This keeps the runtime lightweight while making new OpenAI-compatible backends m
}
```
> **Note**: The `providers` format is deprecated. Use the new `model_list` format with `.security.yml` for better security.
</details>
<details>
@@ -701,18 +798,10 @@ This keeps the runtime lightweight while making new OpenAI-compatible backends m
"dm_scope": "per-channel-peer",
"backlog_limit": 20
},
"providers": {
"openrouter": {
"api_key": "sk-or-v1-xxx"
},
"groq": {
"api_key": "gsk_xxx"
}
},
"channels": {
"telegram": {
"enabled": true,
"token": "123456:ABC...",
"enabled": true"
// token: set in .security.yml
"allow_from": ["123456789"]
}
},
@@ -731,6 +820,8 @@ This keeps the runtime lightweight while making new OpenAI-compatible backends m
}
```
> **Note**: Sensitive fields (`api_key`, `token`, etc.) can be omitted and stored in `.security.yml` for better security.
</details>
### Scheduled Tasks / Reminders
@@ -754,6 +845,7 @@ Scheduled tasks persist across restarts and are stored in `~/.picoclaw/workspace
| Topic | Description |
| ----- | ----------- |
| [Security Configuration](security_configuration.md) | Store API keys and secrets in separate `.security.yml` file |
| [Sensitive Data Filtering](sensitive_data_filtering.md) | Filter API keys and tokens from tool results before sending to LLM |
| [Hook System](hooks/README.md) | Event-driven hooks: observers, interceptors, approval hooks |
| [Steering](steering.md) | Inject messages into a running agent loop between tool calls |
+1 -1
View File
@@ -31,7 +31,7 @@ enc://AAAA...base64...
{
"model_name": "gpt-4o",
"model": "openai/gpt-4o",
"api_key": "enc://AAAA...base64...",
// "api_key": "enc://AAAA...base64..." move to .security.yml
"api_base": "https://api.openai.com/v1"
}
]
+644
View File
@@ -0,0 +1,644 @@
# Security Configuration
## Overview
PicoClaw supports separating sensitive data (API keys, tokens, secrets, passwords) from the main configuration by storing them in a `.security.yml` file. This improves security by:
1. **Separation of concerns**: Configuration settings and secrets are in separate files
2. **Easier sharing**: The main config can be shared without exposing sensitive data
3. **Better version control**: `.security.yml` should be added to `.gitignore`
4. **Flexible deployment**: Different environments can use different security files
## File Structure
```
~/.picoclaw/
├── config.json # Main configuration (safe to share)
└── .security.yml # Security data (never share)
```
## How It Works
The security configuration works through **direct field mapping**, NOT through `ref:` string references. The system automatically loads values from `.security.yml` and applies them to the corresponding fields in `config.json`.
### Key Points:
- Values in `.security.yml` are automatically mapped to corresponding fields in the config
- The mapping is based on field names and structure, not on reference strings
- If a value exists in `.security.yml`, it **overrides** the value in `config.json`
- You can omit sensitive fields from `config.json` entirely (recommended)
## Security Configuration Structure
### Complete Example: .security.yml
```yaml
# Model API Keys
# All models MUST use `api_keys` (plural) array format
# Even a single key must be provided as an array with one element
model_list:
gpt-5.4:
api_keys:
- "sk-proj-your-actual-openai-key-1"
- "sk-proj-your-actual-openai-key-2" # Optional: Multiple keys for failover
claude-sonnet-4.6:
api_keys:
- "sk-ant-your-actual-anthropic-key" # Single key in array format
# Channel Tokens
channels:
telegram:
token: "your-telegram-bot-token"
feishu:
app_secret: "your-feishu-app-secret"
encrypt_key: "your-feishu-encrypt-key"
verification_token: "your-feishu-verification-token"
discord:
token: "your-discord-bot-token"
weixin:
token: "your-weixin-token"
qq:
app_secret: "your-qq-app-secret"
dingtalk:
client_secret: "your-dingtalk-client-secret"
slack:
bot_token: "your-slack-bot-token"
app_token: "your-slack-app-token"
matrix:
access_token: "your-matrix-access-token"
line:
channel_secret: "your-line-channel-secret"
channel_access_token: "your-line-channel-access-token"
onebot:
access_token: "your-onebot-access-token"
wecom:
token: "your-wecom-token"
encoding_aes_key: "your-wecom-encoding-aes-key"
wecom_app:
corp_secret: "your-wecom-app-corp-secret"
token: "your-wecom-app-token"
encoding_aes_key: "your-wecom-app-encoding-aes-key"
wecom_aibot:
secret: "your-wecom-aibot-secret"
token: "your-wecom-aibot-token"
encoding_aes_key: "your-wecom-aibot-encoding-aes-key"
pico:
token: "your-pico-token"
irc:
password: "your-irc-password"
nickserv_password: "your-irc-nickserv-password"
sasl_password: "your-irc-sasl-password"
# Web Tool API Keys
web:
brave:
api_keys:
- "BSAyour-brave-api-key-1"
- "BSAyour-brave-api-key-2" # Optional: Multiple keys for failover
tavily:
api_keys:
- "tvly-your-tavily-api-key" # Single key in array format
perplexity:
api_keys:
- "pplx-your-perplexity-api-key" # Single key in array format
glm_search:
api_key: "your-glm-search-api-key" # GLMSearch uses single key format (not array)
baidu_search:
api_key: "your-baidu-search-api-key"
# Skills Registry Tokens
skills:
github:
token: "your-github-token"
clawhub:
auth_token: "your-clawhub-auth-token"
```
## Usage
### Step 1: Create .security.yml
Create or copy the security file:
```bash
cp security.example.yml ~/.picoclaw/.security.yml
```
### Step 2: Fill in your actual values
Edit `~/.picoclaw/.security.yml` and replace placeholder values with your actual API keys and tokens.
### Step 3: Set proper permissions
```bash
chmod 600 ~/.picoclaw/.security.yml
```
### Step 4: Simplify config.json (Recommended)
You can now remove sensitive fields from `config.json` since they're loaded from `.security.yml`:
**Before:**
```json
{
"model_list": [
{
"model_name": "gpt-5.4",
"model": "openai/gpt-5.4",
"api_base": "https://api.openai.com/v1",
"api_key": "sk-your-actual-api-key-here"
}
],
"channels": {
"telegram": {
"enabled": true,
"token": "1234567890:ABCdefGHIjklMNOpqrsTUVwxyz"
}
}
}
```
**After:**
```json
{
"model_list": [
{
"model_name": "gpt-5.4",
"model": "openai/gpt-5.4",
"api_base": "https://api.openai.com/v1"
// api_key is now loaded from .security.yml
}
],
"channels": {
"telegram": {
"enabled": true"
// token is now loaded from .security.yml
}
}
}
```
### Step 5: Verify
Restart PicoClaw and verify it loads correctly:
```bash
picoclaw --version
```
## Field Mapping Rules
### Models
**In .security.yml:**
```yaml
model_list:
<model_name>:
api_keys:
- "key-1"
- "key-2"
```
**Mapping:**
- Field `api_keys` (array) maps to the model's API keys
- The `<model_name>` must match the `model_name` field in `config.json`
- Supports indexed names (e.g., "gpt-5.4:0") - the system will also try the base name ("gpt-5.4")
### Channels
Each channel maps its fields directly:
**In .security.yml:**
```yaml
channels:
telegram:
token: "value"
feishu:
app_secret: "value"
encrypt_key: "value"
verification_token: "value"
discord:
token: "value"
```
**Mapping:**
- `channels.telegram.token``config.channels.telegram.token`
- `channels.feishu.app_secret``config.channels.feishu.app_secret`
- etc.
### Web Tools
**Brave, Tavily, Perplexity:**
```yaml
web:
brave:
api_keys:
- "key-1"
- "key-2"
```
- Use `api_keys` (plural) array format
**GLMSearch:**
```yaml
web:
glm_search:
api_key: "single-key-here"
```
- Use `api_key` (singular) single string format
**BaiduSearch:**
```yaml
web:
baidu_search:
api_key: "your-key"
```
- Use `api_key` (singular) single string format
### Skills
**In .security.yml:**
```yaml
skills:
github:
token: "value"
clawhub:
auth_token: "value"
```
## API Key Formats
### Models - Single key
Use array format with one element:
```yaml
model_list:
gpt-5.4:
api_keys:
- "sk-your-key"
```
### Models - Multiple keys (Load Balancing & Failover)
Use array format with multiple elements:
```yaml
model_list:
gpt-5.4:
api_keys:
- "sk-your-key-1"
- "sk-your-key-2"
- "sk-your-key-3"
```
**Benefits:**
- **Load balancing**: Requests are distributed across multiple keys
- **Failover**: Automatic switching to another key if one fails
- **Rate limit management**: Distribute usage across multiple keys
- **High availability**: Reduce downtime during API provider issues
### Web Tools (Brave/Tavily/Perplexity) - Single key
```yaml
web:
brave:
api_keys:
- "BSA-your-key"
```
### Web Tools (Brave/Tavily/Perplexity) - Multiple keys
```yaml
web:
brave:
api_keys:
- "BSA-key-1"
- "BSA-key-2"
```
### Web Tool (GLMSearch/BaiduSearch) - Single key only
```yaml
web:
glm_search:
api_key: "your-glm-key" # Single string (NOT array)
baidu_search:
api_key: "your-baidu-key" # Single string (NOT array)
```
## Model Name Matching
The system supports intelligent model name matching in `.security.yml`:
### Example 1: Exact Match
**config.json:**
```json
{
"model_name": "gpt-5.4:0"
}
```
**.security.yml (exact match with index):**
```yaml
model_list:
gpt-5.4:0:
api_keys: ["key-1"]
```
### Example 2: Base Name Match
**config.json:**
```json
{
"model_name": "gpt-5.4:0"
}
```
**.security.yml (base name without index):**
```yaml
model_list:
gpt-5.4:
api_keys: ["key-1", "key-2"]
```
Both methods work. The base name match allows you to use simpler keys in `.security.yml` even when your config uses indexed model names for load balancing.
## Backward Compatibility
The system maintains full backward compatibility:
1. **Direct values**: You can still use direct values in `config.json` (not recommended for production)
2. **Mixed usage**: You can have some fields in `.security.yml` and others in `config.json`
3. **Optional security file**: If `.security.yml` doesn't exist, the system will only use values from `config.json`
4. **Override behavior**: If a field exists in both files, `.security.yml` value takes precedence
## Environment Variables
You can override any security value using environment variables:
**For models:**
```bash
export PICOCLAW_CHANNELS_TELEGRAM_TOKEN="token-from-env"
```
**For channels:**
```bash
export PICOCLAW_CHANNELS_TELEGRAM_TOKEN="token-from-env"
export PICOCLAW_CHANNELS_FEISHU_APP_SECRET="secret-from-env"
```
**For web tools:**
```bash
export PICOCLAW_TOOLS_WEB_BRAVE_API_KEY="key-from-env"
export PICOCLAW_TOOLS_WEB_BAIDU_API_KEY="baidu-key-from-env"
```
Environment variables have the highest priority and will override both `config.json` and `.security.yml` values.
The pattern is: `PICOCLAW_<SECTION>_<KEY>_<FIELD>` with underscores separating path segments and converted to uppercase.
## Security Best Practices
1. **Never commit `.security.yml`** to version control
2. **Add to .gitignore**: Ensure `.security.yml` is in your `.gitignore` file
3. **Set file permissions**: `chmod 600 ~/.picoclaw/.security.yml`
4. **Use different keys** for different environments (dev, staging, production)
5. **Rotate keys regularly** and update `.security.yml`
6. **Backup securely**: Encrypt backups containing `.security.yml`
7. **Review access**: Ensure only authorized users have read access to the file
## API
### loadSecurityConfig
```go
func loadSecurityConfig(securityPath string) (*SecurityConfig, error)
```
Loads the security configuration from `.security.yml`. Returns an empty `SecurityConfig` if the file doesn't exist.
### saveSecurityConfig
```go
func saveSecurityConfig(securityPath string, sec *SecurityConfig) error
```
Saves the security configuration to `.security.yml` with `0o600` permissions.
### applySecurityConfig
```go
func applySecurityConfig(cfg *Config, sec *SecurityConfig) error
```
Applies security configuration to the main config by copying values from `.security.yml` to the corresponding fields in the config.
### securityPath
```go
func securityPath(configPath string) string
```
Returns the path to `.security.yml` relative to the config file.
## Example: Complete Configuration
### config.json
```json
{
"version": 1,
"agents": {
"defaults": {
"workspace": "~/picoclaw-workspace",
"model_name": "gpt-5.4"
}
},
"model_list": [
{
"model_name": "gpt-5.4",
"model": "openai/gpt-5.4",
"api_base": "https://api.openai.com/v1"
},
{
"model_name": "claude-sonnet-4.6",
"model": "anthropic/claude-sonnet-4.6",
"api_base": "https://api.anthropic.com/v1"
}
],
"channels": {
"telegram": {
"enabled": true
}
},
"tools": {
"web": {
"brave": {
"enabled": true
}
}
}
}
```
### .security.yml
```yaml
model_list:
gpt-5.4:
api_keys:
- "sk-proj-actual-openai-key-1"
- "sk-proj-actual-openai-key-2"
claude-sonnet-4.6:
api_keys:
- "sk-ant-actual-anthropic-key"
channels:
telegram:
token: "1234567890:ABCdefGHIjklMNOpqrsTUVwxyz"
web:
brave:
api_keys:
- "BSAactualbravekey-1"
- "BSAactualbravekey-2"
tavily:
api_keys:
- "tvly-your-tavily-key"
glm_search:
api_key: "your-glm-key"
baidu_search:
api_key: "your-baidu-key"
```
## Testing
Run the security configuration tests:
```bash
go test ./pkg/config -run TestSecurityConfig
```
## Troubleshooting
### Error: "failed to load security config"
- Verify `.security.yml` exists in the same directory as `config.json`
- Check the YAML syntax is valid (use a YAML validator)
- Ensure file permissions allow reading
### Error: "model security entry not found"
- Ensure the model name in `config.json` matches exactly in `.security.yml`
- Check that the `model_list` section exists in `.security.yml`
- For models with indexed names (e.g., "gpt-5.4:0"), ensure the exact name is used or check the base name without index
- Verify the YAML structure is correct (proper indentation)
### Multiple API Keys Not Working
- Ensure you're using `api_keys` (plural) in `.security.yml` for models and web tools (except GLMSearch/BaiduSearch)
- Check that the array format is correct in YAML (proper indentation with dashes)
- Remember: Models, Brave, Tavily, Perplexity MUST use `api_keys` (array format)
- GLMSearch and BaiduSearch MUST use `api_key` (single string format)
### Load Balancing/Failover Issues
- Verify all API keys in the `api_keys` array are valid
- Check that all keys have the same rate limits and permissions
- Monitor logs to see which keys are being used and failing
- Ensure the `api_keys` array is properly formatted in YAML
### Keys Not Being Applied
- Check that `.security.yml` is in the same directory as `config.json`
- Verify the file permissions allow reading (`chmod 600 ~/.picoclaw/.security.yml`)
- Ensure the YAML structure matches the expected format
- Check for typos in field names (case-sensitive)
- Verify the model/channel names match exactly (case-sensitive)
## Migration Guide
### Step 1: Backup your config
```bash
cp ~/.picoclaw/config.json ~/.picoclaw/config.json.backup
```
### Step 2: Create .security.yml
```bash
cp security.example.yml ~/.picoclaw/.security.yml
```
### Step 3: Fill in your API keys
Edit `~/.picoclaw/.security.yml` and replace placeholder values with your actual keys.
### Step 4: Remove sensitive fields from config.json
Remove or comment out sensitive fields from `config.json`:
- `api_key` fields from `model_list` entries
- `token` fields from `channels`
- `api_key` fields from `tools.web`
- `token`/`auth_token` fields from `tools.skills`
### Step 5: Set proper permissions
```bash
chmod 600 ~/.picoclaw/.security.yml
```
### Step 6: Test
```bash
picoclaw --version
```
### Step 7: Verify functionality
Test your models and channels to ensure everything works correctly.
### Step 8: Clean up (optional)
If everything works, you can delete the backup:
```bash
rm ~/.picoclaw/config.json.backup
```
## Advanced: Encrypted API Keys
PicoClaw supports encrypting API keys in the security file for additional protection.
### Setup
1. Set a passphrase via environment variable:
```bash
export PICOCLAW_CREDENTIAL_PASSPHRASE="your-secure-passphrase"
```
2. When saving config, API keys will be encrypted automatically:
```go
SaveConfig(path, config)
```
### Encrypted Format
Encrypted keys are stored as:
```yaml
model_list:
gpt-5.4:
api_keys:
- "enc://encrypted-base64-string"
```
The system automatically decrypts keys at runtime when loading the configuration.
### Benefits
- Additional layer of security
- Keys are encrypted at rest
- Passphrase can be managed separately from the config file
### Important Notes
- Always backup your passphrase securely
- If you lose the passphrase, you'll lose access to encrypted keys
- Use a strong, unique passphrase
- Never commit the passphrase to version control