A simple, secure password manager in JavaScript using age encryption. This is a TypeScript port of the shell script from https://github.com/tonidy/pa-cli, providing cross-platform compatibility and modern features.
- Age Encryption: Uses the modern age encryption format for secure password storage
- Cross-Platform: Works on macOS, Linux, and Windows (including WSL)
- Hardware Support: Supports YubiKey, Secure Enclave (macOS), and other age plugins
- Git Integration: Automatic git tracking of password changes
- Credential Storage: Integrates with system credential stores (Keychain, libsecret, Credential Manager)
- Fuzzy Search: Interactive password selection with fzf
- CLI Interface: Command-line interface matching the original pa script
npm install -g @kdbx/pak
Or use directly with npx:
npx @kdbx/pak --help
Note: The
pak
command is available after installation. In Indonesia, using "Pak" isn’t just about calling someone "sir" — it’s about showing respect, building community, and being part of a culture that values kindness and humility.
So whether you're asking for help or giving a command, saying "Pak" makes everything sound more friendly and respectful — just like how locals do it.
And now, you're using pak as a command-line tool 😄.
# Add a password (will prompt to generate or enter manually)
pak add mysite
# Show a password
pak show mysite
# List all passwords
pak list
# Edit a password with your $EDITOR
pak edit mysite
# Delete a password
pak del mysite
# Search passwords with fzf
pak find
# Search and perform action
pak find show # or edit, del
# Git operations
pak git log
pak git status
# Version information
pak version
# Secure Enclave information (macOS only)
pak se-info
# Convert recipients between formats
pak convert age1se1qfn44rsw... yubikey
pak convert age1yubikey1qfn44rsw... se
PAK provides comprehensive support for Apple's Secure Enclave through age-plugin-se, offering hardware-backed encryption with biometric authentication.
# Install age-plugin-se
brew install age-plugin-se
# Verify installation
pak se-info
- Hardware-backed encryption: Keys stored in dedicated security hardware
- Biometric authentication: Touch ID/Face ID for key access
- Non-extractable keys: Private keys cannot be copied or moved
- Access control options: Multiple authentication methods available
- Recipient conversion: Compatible with age-plugin-yubikey format
- Native integration: TypeScript/JavaScript API for direct SE operations (new!)
- Performance optimized: Native SE operations avoid CLI overhead
- Automatic fallback: Graceful fallback to CLI when native operations fail
- any-biometry: Touch ID or Face ID
- any-biometry-or-passcode: Touch ID/Face ID OR device passcode
- passcode: Device passcode only
- current-biometry: Only currently enrolled biometrics (removing/adding fingerprints affects access)
- current-biometry-and-passcode: Current biometrics AND device passcode
# Auto-select access control (non-interactive)
export PA_SE_ACCESS_CONTROL=any-biometry-or-passcode
# Auto-confirm Secure Enclave usage (non-interactive)
export PA_SE_AUTO_CONFIRM=1
# Check Secure Enclave support
pak se-info
# Generate identity with custom access control
PA_SE_ACCESS_CONTROL=any-biometry pak add mysite
# Convert between plugin formats
pak convert age1se1qfn44rsw0xvmez3pky46nghmnd5up0jpj97nd39zptlh83a0nja6skde3ak yubikey
# Output: age1yubikey1qfn44rsw0xvmez3pky46nghmnd5up0jpj97nd39zptlh83a0nja6skde3ak
# Use converted recipient for encryption on systems without age-plugin-se
echo "secret" | age -r age1yubikey1qfn44rsw0xvmez3pky46nghmnd5up0jpj97nd39zptlh83a0nja6skde3ak
- macOS 13.0 (Ventura) or later
- Mac with Apple Silicon or Intel T2 Security Chip
- age-plugin-se installed
Use the SE integration programmatically:
const { AgeManager, AppleSecureEnclave } = require('@kdbx/pak');
// Initialize with SE support
const config = {
useAgeBinary: false, // Use native SE integration
seAccessControl: 'any-biometry-or-passcode'
};
const ageManager = new AgeManager(config);
// Check SE availability
const isAvailable = await ageManager.isSecureEnclaveAvailable();
// Generate SE identity
const identity = await ageManager.generateSecureEnclaveIdentity('any-biometry');
// Use direct SE module
const secureEnclave = new AppleSecureEnclave({
accessControl: 'any-biometry-or-passcode',
recipientType: 'piv-p256',
useNative: true
});
const keyPair = await secureEnclave.generateKeyPair('any-biometry');
const capabilities = await secureEnclave.getCapabilities();
PAK can be configured through environment variables, a config.json
file, or programmatically.
Configure the password manager behavior with these environment variables:
# Data directory (default: ~/.local/share/pa)
export PA_DIR=~/.local/share/pa
# Default password length (default: 50)
export PA_LENGTH=50
# Password character pattern (default: A-Za-z0-9-_)
export PA_PATTERN=A-Za-z0-9-_
# Disable git tracking
export PA_NOGIT=1
# Disable system credential storage
export PA_NO_KEYRING=1
# Editor command (default: vi)
export EDITOR=nano
# Force age binary usage (instead of JS library)
export PA_USE_AGE_BINARY=1
# Custom age binary path
export PA_AGE_BINARY_PATH=/opt/homebrew/bin/age
# Secure Enclave access control
export PA_SE_ACCESS_CONTROL=any-biometry-or-passcode
# Auto-confirm Secure Enclave usage
export PA_SE_AUTO_CONFIRM=1
Create a config.json
file in your working directory:
{
"paDir": "~/.local/share/pa",
"paLength": 50,
"paPattern": "A-Za-z0-9-_",
"paNoGit": false,
"paNoKeyring": false,
"editor": "vi",
"useAgeBinary": true,
"ageBinaryPath": "/opt/homebrew/bin/age",
"seAccessControl": "any-biometry-or-passcode",
"seAutoConfirm": false
}
Configuration is applied in this order (highest priority first):
- Programmatic options (API usage)
config.json
file- Environment variables
- Default values
PAK supports three age encryption backends:
- JavaScript Library (default): Fast, embedded, works without external dependencies
- Native Secure Enclave: Direct hardware integration for maximum security (macOS only)
- Age Binary with Plugins: Full age plugin support (Secure Enclave, YubiKey, etc.)
For CLI-based Secure Enclave usage, you need to install the age-plugin-se:
# Install age-plugin-se for CLI backend
brew install age-plugin-se
The age binary is automatically used when:
useAgeBinary: true
is set in configPA_USE_AGE_BINARY=1
environment variable is set- Secure Enclave or YubiKey recipients/identities are detected
- Hardware encryption is initialized
# Force age binary usage for full plugin support
export PA_USE_AGE_BINARY=1
pak add mysite # Will use age binary with Touch ID support
- Keychain Integration: Stores encryption key passphrases in macOS Keychain
- Secure Enclave: Full support for age-plugin-se hardware-backed encryption
- Touch ID/Face ID: Biometric authentication for Secure Enclave keys
- Hardware Security: Keys stored in dedicated security hardware, cannot be extracted
- libsecret Integration: Uses secret-tool for credential storage
- Memory Storage: Prefers /dev/shm for temporary files
- Credential Manager: Integrates with Windows Credential Manager
- WSL Support: Full support for Windows Subsystem for Linux
- PowerShell Integration: Uses PowerShell for credential operations
- YubiKey: Supports age-plugin-yubikey for hardware security keys
- FIDO2: Compatible with FIDO2 security keys
- Age Plugins: Extensible through age plugin system
- Age Encryption: Modern, secure file encryption
- Secure Random Generation: Cryptographically secure password generation
- Memory Protection: Temporary files stored in secure locations
- Key Management: Automatic key generation and management
- Passphrase Protection: Optional passphrase protection for keys
- Hardware Security: Support for hardware-backed encryption
~/.local/share/pa/
├── identities # Age private keys
├── recipients # Age public keys
└── passwords/ # Encrypted password files
├── .git/ # Git repository (optional)
├── .gitattributes # Git diff configuration
└── *.age # Encrypted password files
You can also use PAK programmatically:
import { PasswordManager } from '@kdbx/pak';
const pm = new PasswordManager();
// Add a password
await pm.add('mysite', { generate: true, length: 32 });
// Retrieve a password
const password = await pm.show('mysite');
// List all passwords
const passwords = await pm.list();
// Delete a password
await pm.delete('mysite');
git clone https://github.com/tonidy/pak-cli.git
cd pak-cli
npm install
npm run build
npm test
npm run dev -- add mysite
- age-encryption: TypeScript implementation of age encryption
- commander: Command-line interface framework
- keytar: Cross-platform credential storage (optional)
- fzf: Fuzzy finder for password search
- git: Version control for password tracking
- age-plugin-se: Secure Enclave support (macOS)
- age-plugin-yubikey: YubiKey support
- Node.js: 16.0.0 or higher
- Operating Systems: macOS, Linux, Windows
- Age Format: Compatible with age 1.0+ and rage
If you're migrating from the original pa shell script:
- Your existing password store is compatible
- Set
PA_DIR
to your existing password directory - Age keys and encrypted files work without modification
- Git history is preserved
If you're already using age-plugin-se with pa-cli:
- Install PAK and age-plugin-se:
npm install -g @kdbx/pak
brew install age-plugin-se
- Point to your existing password directory:
export PA_DIR=/path/to/your/existing/pa/directory
- Verify your Secure Enclave setup:
pak se-info
- Your existing age-plugin-se identity and recipients files will work seamlessly
- All encrypted passwords remain accessible with your existing Touch ID/biometric authentication
If you have recipients in different formats, you can convert them:
# Convert Secure Enclave recipient to YubiKey format
pak convert age1se1qfn44rsw0xvmez3pky46nghmnd5up0jpj97nd39zptlh83a0nja6skde3ak yubikey
# Convert YubiKey recipient to Secure Enclave format
pak convert age1yubikey1qfn44rsw0xvmez3pky46nghmnd5up0jpj97nd39zptlh83a0nja6skde3ak se
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
MIT License - see LICENSE file for details.
If you discover a security vulnerability, please report it through GitHub Security Advisories instead of using the issue tracker or email. This ensures proper handling and responsible disclosure of security issues.
- Shell script source: pa-cli by tonidy
- Original pa shell script by biox
- age encryption by Filippo Valsorda
- typage TypeScript implementation