Unlock Skill
Load project secrets from Bitwarden Secrets Manager into the current environment using the bws CLI. Fully stateless — no vault unlock or session tokens required.
Configuration
| Item | Value |
|------|-------|
| BWS project ID | $BWS_PROJECT_ID env var, or 5022ea9c-e711-4f4e-bf5f-b3df0181a41d (default project ID for Troy's vault — override via BWS_PROJECT_ID. Default as of 2026-03-04 — verify if errors occur) |
| Access token env var | TROY |
| bws install docs | https://bitwarden.com/help/secrets-manager-cli/ |
Implementation
When the user invokes /unlock, follow these steps:
Step 1: Detect platform and locate bws
Windows (win32):
where.exe bws 2>$null || (Test-Path "$env:USERPROFILE\bin\bws.exe")
Linux/macOS:
command -v bws || test -x "$HOME/bin/bws"
If not found, tell the user to install bws from https://bitwarden.com/help/secrets-manager-cli/ and stop.
Step 2: Get access token
The access token is stored in the TROY environment variable.
Windows — check process env first, then Windows user env:
$token = $env:TROY
if (-not $token) { $token = [System.Environment]::GetEnvironmentVariable('TROY', 'User') }
Linux/macOS — check process env:
TOKEN="$TROY"
If empty, tell the user:
- Windows: Set via
[System.Environment]::SetEnvironmentVariable('TROY', 'your-token', 'User') - Linux/macOS: Add
export TROY="your-token"to~/.bashrcor~/.zshrc
Then stop.
Step 3: Fetch secrets and export
Run bws with the access token set for that single command, then parse the JSON output and export each secret as an environment variable.
Windows:
# Use BWS_PROJECT_ID env var if set, otherwise fall back to default
# Default project ID for Troy's vault — override via BWS_PROJECT_ID
# Default as of 2026-03-04 — verify if errors occur
$projectId = if ($env:BWS_PROJECT_ID) { $env:BWS_PROJECT_ID } else { '5022ea9c-e711-4f4e-bf5f-b3df0181a41d' }
$env:BWS_ACCESS_TOKEN = $token
$json = bws secret list $projectId 2>&1
Remove-Item env:BWS_ACCESS_TOKEN -ErrorAction SilentlyContinue
$secrets = $json | ConvertFrom-Json
foreach ($s in $secrets) {
# Validate key name: only alphanumeric and underscores allowed
if ($s.key -notmatch '^[A-Za-z_][A-Za-z0-9_]*$') {
Write-Warning "Skipping invalid key name: $($s.key)"
continue
}
[System.Environment]::SetEnvironmentVariable($s.key, $s.value, 'Process')
}
Linux/macOS:
SECURITY: No eval with external data. Use Python to write a temporary script with properly escaped values, source it, then delete it.
# Use BWS_PROJECT_ID env var if set, otherwise fall back to default
# Default project ID for Troy's vault — override via BWS_PROJECT_ID
# Default as of 2026-03-04 — verify if errors occur
PROJECT_ID="${BWS_PROJECT_ID:-5022ea9c-e711-4f4e-bf5f-b3df0181a41d}"
JSON=$(BWS_ACCESS_TOKEN="$TOKEN" bws secret list "$PROJECT_ID" 2>&1)
# Generate safe export statements using shlex.quote() — never eval raw values
EXPORT_FILE=$(mktemp /tmp/bws-exports.XXXXXX)
chmod 600 "$EXPORT_FILE"
echo "$JSON" | python3 -c "
import json, sys, shlex, re
data = json.load(sys.stdin)
for s in data:
key = s['key']
value = s['value']
# Validate key: must be a valid shell variable name
if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', key):
print(f'# SKIPPED invalid key: {key!r}', file=sys.stderr)
continue
# Use shlex.quote() to safely escape the value for shell
safe_value = shlex.quote(value)
print(f'export {key}={safe_value}')
" > "$EXPORT_FILE"
# Source the safe exports, then remove the temp file
source "$EXPORT_FILE"
rm -f "$EXPORT_FILE"
Step 4: Report results
Print the count and list of secret names loaded (never print values). Example output:
Loaded 8 secret(s) from Bitwarden Secrets Manager:
ANTHROPIC_API_KEY
OPENAI_API_KEY
GOOGLE_API_KEY
NOTION_API_KEY
PUSHOVER_API_TOKEN
PUSHOVER_USER_KEY
NOTION_VOICE_CAPTURES_DB_ID
NOTION_WEEKLY_SUMMARIES_DB_ID
Error Handling
| Error | Action |
|-------|--------|
| bws not found | Print install URL and stop |
| TROY env var empty | Print platform-specific setup instructions and stop |
| bws secret list fails | Print the error output from bws and stop |
| JSON parse fails | Print raw output for debugging and stop |
| Invalid key name in secrets | Log warning, skip that secret, continue with remaining secrets |
| Temp file creation fails | Print error and stop — do not fall back to eval |
Performance
Typically completes in under 5 seconds. Single bws CLI call with JSON parsing.
Examples
Standard unlock at session start:
/unlock
Output:
Loaded 8 secret(s) from Bitwarden Secrets Manager:
ANTHROPIC_API_KEY
OPENAI_API_KEY
GOOGLE_API_KEY
NOTION_API_KEY
PUSHOVER_API_TOKEN
PUSHOVER_USER_KEY
NOTION_VOICE_CAPTURES_DB_ID
NOTION_WEEKLY_SUMMARIES_DB_ID
Proactive trigger before research:
User: /research-topic "RAG system best practices"
Claude: I need API keys to run multi-provider research. Let me load them first.
→ Runs /unlock automatically
→ Loaded 8 secret(s) from Bitwarden Secrets Manager
→ Proceeds with research-topic
When bws CLI is not installed:
/unlock
Output:
bws CLI not found. Install it from:
https://bitwarden.com/help/secrets-manager-cli/
When access token is not configured:
/unlock
Output:
TROY environment variable is not set.
Windows: [System.Environment]::SetEnvironmentVariable('TROY', 'your-token', 'User')
Linux/macOS: Add export TROY="your-token" to ~/.bashrc or ~/.zshrc
Security Considerations
- No eval with external data: The Linux/macOS path uses
shlex.quote()to escape secret values and writes to a temporary file withchmod 600permissions, then sources it. The dangerousevalpattern that could allow shell injection through crafted secret values is eliminated. - Key name validation: Both Windows and Linux/macOS paths validate that secret key names match
^[A-Za-z_][A-Za-z0-9_]*$before using them as environment variable names. Invalid keys are skipped with a warning. - Temp file security: The export file is created with
mktemp(unpredictable filename) and restricted to owner-only read/write (chmod 600). It is deleted immediately after sourcing. - Access token is set in env only for the duration of the
bwscall, then cleared. - Secret values are never printed — only key names.
- No persistent files are written — secrets exist only in process environment.
- The project ID is not sensitive — it is useless without the access token.
Scan to join WeChat group