Extract pbj's PIN-protected key recovery mechanism into a standalone GitHub CLI extension that can securely store and recover any secret via GitHub Actions workflows.
Rationale:
- Clear metaphor: a lockbox stores valuables securely
- Short, memorable, CLI-friendly
- Emphasizes security without being technical
- Natural verb usage: "lockbox your secrets"
A GitHub CLI extension that:
- Stores secrets in GitHub Secrets (encrypted at rest by GitHub)
- Uses PIN-protected split-key encryption for recovery
- Triggers GitHub Actions workflows to output encrypted blobs
- Recovers secrets locally by decrypting with user's PIN
- Never exposes raw secrets in logs or workflow outputs
- Encryption Keys: Recover keys across devices (pbj's current use)
- API Tokens: Securely recover tokens for CI/CD setup
- SSH Keys: Bootstrap SSH access on new machines
- Certificates: Recover SSL/TLS certificates
- Config Files: Secure recovery of sensitive configuration
- Database Credentials: Safe recovery of connection strings
- Any Secret: Generic secret storage/recovery with PIN protection
User's PIN (4 digits) + Static Padding (28 bytes) = 32-byte AES Key
↓
Encrypts secret before logging to GitHub Actions
↓
Recovery: User provides PIN → Decryption → Original secret
Security Properties:
- PIN alone: insufficient (needs padding from code)
- Code alone: insufficient (needs user's PIN)
- Workflow logs: encrypted blob only (needs PIN to decrypt)
- 237 bits effective entropy (13.3 bits PIN + 224 bits padding)
-
CLI Tool (
gh-lockbox)- Written in Ruby (reuse pbj's crypto code)
- Installable as gh extension
- Works with any GitHub repository
-
GitHub Actions Workflow (
.github/workflows/lockbox-recovery.yml)- Manually triggered (workflow_dispatch)
- Encrypts secret with PIN before output
- One workflow per secret (parameterized)
-
GitHub Secrets Storage
LOCKBOX_<NAME>_VALUE: The actual secretLOCKBOX_<NAME>_PIN: The user's PIN- Both write-only, secured by GitHub
# Install extension
gh extension install ahoward/gh-lockbox
# Store a secret with PIN protection
gh lockbox store my-api-key
→ Prompts for secret value (hidden input)
→ Prompts for 4-digit PIN (masked with *)
→ Confirms PIN
→ Stores LOCKBOX_MY_API_KEY_VALUE and LOCKBOX_MY_API_KEY_PIN
→ Creates/updates workflow file
→ Output: ✓ Locked: my-api-key (recover with 'gh lockbox recover my-api-key')
# List stored secrets
gh lockbox list
→ Shows all LOCKBOX_* secrets in current repo
→ Output: my-api-key, db-password, ssl-cert (3 secrets)
# Recover a secret (on new device)
gh lockbox recover my-api-key
→ Triggers lockbox-recovery workflow (parameterized)
→ Waits for completion
→ Prompts for PIN (3 attempts)
→ Decrypts encrypted blob from logs
→ Outputs secret to STDOUT (or --output FILE)
→ Output: <secret-value>
# Update PIN for existing secret
gh lockbox repin my-api-key
→ Prompts for new PIN
→ Updates LOCKBOX_MY_API_KEY_PIN
→ Output: ✓ PIN updated for my-api-key
# Remove a secret
gh lockbox remove my-api-key
→ Confirms deletion
→ Removes GitHub Secrets
→ Removes workflow file
→ Output: ✓ Removed: my-api-key
# Help
gh lockbox help
→ Shows full usage and examples.github/workflows/lockbox-recovery.yml:
name: Lockbox Recovery
on:
workflow_dispatch:
inputs:
secret_name:
description: 'Name of secret to recover'
required: true
type: string
jobs:
recover:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Encrypt secret with PIN
run: |
# Use gh-lockbox's internal encryption command
gh lockbox __internal_encrypt__ ${{ inputs.secret_name }}
env:
SECRET_VALUE: ${{ secrets[format('LOCKBOX_{0}_VALUE', inputs.secret_name)] }}
SECRET_PIN: ${{ secrets[format('LOCKBOX_{0}_PIN', inputs.secret_name)] }}Benefits:
- Single workflow for all secrets
- No per-secret workflow files cluttering repo
- Dynamic secret lookup via
secrets[format(...)]
Alternative: Per-secret workflows (like pbj currently does)
- Simpler dynamic lookup
- More visible in Actions UI
- Trade-off: more files vs simpler logic
Tasks:
- Create new repo:
ahoward/gh-lockbox - Extract from pbj:
- PIN input system (
prompt_pin,get_pin_with_confirmation) - Encryption layer (
encrypt_with_pin,decrypt_with_pin) - Split-key derivation (
derive_pin_key)
- PIN input system (
- Create Ruby gem structure
- Write unit tests for crypto functions
- Document security model
Deliverable: Working crypto library with tests
Tasks:
- Implement
gh lockbox store <name>- Secret input (hidden)
- PIN input (masked)
- Store to GitHub Secrets
- Implement
gh lockbox list- Query GitHub Secrets API
- Filter for
LOCKBOX_*pattern
- Implement
gh lockbox remove <name>- Confirm deletion
- Remove from GitHub Secrets
- Write integration tests
- Create man page
Deliverable: Basic store/list/remove working
Tasks:
- Create workflow template
- Implement
gh lockbox recover <name>- Trigger workflow with parameter
- Poll for completion
- Parse encrypted blob from logs
- Prompt for PIN
- Decrypt and output
- Implement
gh lockbox repin <name> - Add retry logic (3 attempts)
- Add verbose mode (
-v)
Deliverable: Full recovery working end-to-end
Tasks:
- Comprehensive documentation
- Example use cases
- Security audit
- Performance optimization
- Error handling improvements
- README with quickstart
- Submit to gh extension marketplace
- Write blog post
Deliverable: Public release v1.0.0
ahoward/gh-lockbox/
├── bin/
│ └── gh-lockbox # Main executable
├── lib/
│ ├── lockbox.rb # Core library
│ ├── lockbox/
│ │ ├── crypto.rb # Encryption/decryption
│ │ ├── pin.rb # PIN input handling
│ │ ├── github.rb # GitHub API interactions
│ │ ├── workflow.rb # Workflow management
│ │ └── version.rb # Version constant
├── templates/
│ └── lockbox-recovery.yml # Workflow template
├── test/
│ ├── test_crypto.rb
│ ├── test_pin.rb
│ └── test_integration.rb
├── docs/
│ ├── SECURITY.md # Security model
│ ├── USAGE.md # Detailed usage
│ └── EXAMPLES.md # Common use cases
├── README.md
├── LICENSE
└── gh-lockbox.gemspec
-
Generic Secret Storage
- pbj: hardcoded for encryption keys
- lockbox: works with any secret
-
Single Workflow
- pbj: separate workflow file
- lockbox: parameterized workflow (optional)
-
Better UX
- pbj: recovery embedded in clipboard tool
- lockbox: dedicated purpose, clear commands
-
Modular Design
- pbj: monolithic script
- lockbox: proper gem structure, testable
-
Multiple Secrets
- pbj: one key per repo
- lockbox: unlimited secrets per repo
-
No Auto-Storage
- pbj: auto-stores key on first use
- lockbox: explicit
storecommand
-
No Git Integration
- pbj: integrated with clipboard git commits
- lockbox: standalone secret management
-
Simpler Scope
- pbj: clipboard + key management + git + PIN
- lockbox: ONLY secret storage/recovery
Protected Against:
- ✅ Workflow log exposure (encrypted blobs)
- ✅ Secrets API read access (PIN required for decryption)
- ✅ Compromised GitHub Secrets (PIN not stored plaintext)
- ✅ Fork/clone attacks (secrets not in repo)
NOT Protected Against:
- ❌ Compromised GitHub account (attacker can read secrets)
- ❌ Compromised local machine (PIN entered on device)
- ❌ Keylogger (PIN typed by user)
- ❌ Malicious workflow modifications (can exfiltrate secrets)
-
PIN Strength
- Recommend 4+ digits
- Consider alphanumeric PINs (future enhancement)
- Document PIN != password (convenience/recovery balance)
-
Padding Management
- PERMANENT static padding (never change)
- Document in SECURITY.md
- Include warning about backward compatibility
-
Workflow Security
- Workflow file should be reviewed before use
- Consider workflow approval requirements
- Document trust model
-
Secret Hygiene
- Delete workflow runs after recovery
- Rotate secrets periodically
- Use lockbox as backup, not primary storage
Users of pbj can migrate their key recovery:
# In pbj repo
cat ~/.pbj/.pbj-key # Copy this value
# Install gh-lockbox
gh extension install ahoward/gh-lockbox
# Store pbj key in lockbox
gh lockbox store pbj-encryption-key
# (paste the key value)
# (enter your pbj PIN)
# Later, recover on new device
gh lockbox recover pbj-encryption-key > ~/.pbj/.pbj-key
chmod 600 ~/.pbj/.pbj-key# Install from GitHub
gh extension install ahoward/gh-lockbox
# Or from source (development)
git clone https://github.com/ahoward/gh-lockbox
cd gh-lockbox
gh extension install .
# Upgrade
gh extension upgrade lockbox
# Uninstall
gh extension remove lockbox- GitHub CLI (
gh) installed - Ruby 2.7+ (for crypto operations)
- Git repository with GitHub Actions enabled
- GitHub account with repo access
# Store secret globally (accessible from any repo)
gh lockbox store --global my-api-key
# Recover in any repo
gh lockbox recover my-api-key# Store with multiple PINs (each team member has own PIN)
gh lockbox store --team db-password
→ Prompt for PINs for alice, bob, carol
→ Any one PIN can recover# Store multiple related secrets as a template
gh lockbox template create aws-creds
→ Prompts for: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
→ Stores as bundle with single PIN
gh lockbox template recover aws-creds
→ Recovers all values
→ Outputs as env vars or JSON# Use passphrase instead of 4-digit PIN
gh lockbox store --passphrase my-important-key
→ Full entropy passphrase (not just 4 digits)
→ More secure, less convenient
# Use hardware security key
gh lockbox store --yubikey ssl-cert
→ PIN stored on YubiKey
→ Recovery requires physical key# Export encrypted backup (offline storage)
gh lockbox export my-secrets.lockbox.enc
→ Encrypted file with all secrets
→ Can restore without GitHub access
gh lockbox import my-secrets.lockbox.enc
→ Restores to GitHub SecretsAdoption:
- 100+ stars on GitHub in 3 months
- 10+ repos using it (beyond author)
- Featured in GitHub CLI marketplace
Reliability:
- 99%+ successful recovery rate
- <5 bug reports per month
- <1 security issue reported
Community:
- 5+ external contributors
- 10+ feature requests/discussions
- Integration in other tools (like pbj)
- Quick overview
- Installation
- 5-minute quickstart
- Common use cases
- Security notice
- Contributing
- Detailed threat model
- Cryptographic implementation
- Split-key security explained
- PIN strength recommendations
- Known limitations
- Responsible disclosure
- Complete command reference
- All options and flags
- Advanced workflows
- Troubleshooting
- FAQ
- Recovering encryption keys
- Storing API tokens
- SSH key bootstrap
- Certificate management
- Database credentials
- Multi-device workflows
- CI/CD integration
Tagline: "Your secrets, locked tight. One PIN to recover."
Tweet:
🔐 gh-lockbox: PIN-protected secret recovery for GitHub
Store secrets in GitHub Secrets, recover them anywhere with just your PIN. Uses split-key encryption—neither PIN nor code alone can decrypt.
Perfect for: API keys, encryption keys, SSH keys, certs
gh extension install ahoward/gh-lockbox
Blog Post Title: "gh-lockbox: How We Built PIN-Protected Secret Recovery for GitHub Actions"
Target Audience:
- DevOps engineers managing secrets across environments
- Developers working on multiple machines
- Open source maintainers sharing access
- Security-conscious teams needing secret rotation
- Anyone using GitHub Actions for deployment
Week 1: Core crypto extraction + tests Week 2: CLI interface (store/list/remove) Week 3: Recovery system + workflows Week 4: Polish, docs, release
Target Release: v1.0.0 in 4 weeks
-
Workflow Strategy: Single parameterized vs per-secret workflows?
- Recommend: Single parameterized (cleaner)
-
Secret Naming:
LOCKBOX_*prefix or dedicated namespace?- Recommend:
LOCKBOX_*prefix (clear, grepable)
- Recommend:
-
Ruby Dependency: Require Ruby or bundle as standalone binary?
- Recommend: Require Ruby (simpler, gh extensions commonly do this)
-
Pin Length: Fixed 4 digits or configurable?
- Recommend: Start with 4, add
--pin-lengthflag in v1.1
- Recommend: Start with 4, add
-
Workflow Location:
.github/workflows/or custom location?- Recommend: Standard
.github/workflows/(GitHub convention)
- Recommend: Standard
✅ v1.0.0 is successful if:
- Can store and recover arbitrary secrets
- Works on Linux and macOS
- Zero raw secrets in workflow logs
- Comprehensive test coverage (>80%)
- Clear documentation
- No known security vulnerabilities
- Positive community feedback
Next Steps:
- Review and approve this plan
- Create
ahoward/gh-lockboxrepository - Begin Phase 1: Extract crypto code
- Set up testing infrastructure
- Write v1.0.0 specification
Author: Claude + @ahoward Date: 2025-11-04 Status: Proposal - Awaiting Review