https://github.com/itsfarseen/age-store
Share encrypted files across teams.
https://github.com/itsfarseen/age-store
Last synced: 6 months ago
JSON representation
Share encrypted files across teams.
- Host: GitHub
- URL: https://github.com/itsfarseen/age-store
- Owner: itsfarseen
- License: mit
- Created: 2025-08-29T19:36:08.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2025-09-14T09:18:25.000Z (10 months ago)
- Last Synced: 2025-10-11T14:34:44.407Z (10 months ago)
- Language: Python
- Size: 174 KB
- Stars: 3
- Watchers: 0
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Age Store
[](https://github.com/itsfarseen/age-store/actions/workflows/ci.yml)
[](https://github.com/itsfarseen/age-store/releases/latest)
> **📋 See [CHANGELOG.md](CHANGELOG.md) for version history and migration instructions**
A simple, secure secret management system built on the proven [age encryption](https://age-encryption.org/) standard. Age Store makes it easy to share encrypted files across teams using familiar command-line tools.
## Why Age Store?
- **Built on Standards**: Uses the widely-adopted `age` encryption tool under the hood
- **Dead Simple**: Just a single Python script with an intuitive CLI
- **No Dependencies**: Only requires Python 3.6+ and the standard `age` tool
- **Team-Friendly**: Easy multi-user access control with public key sharing
- **Secure by Design**: Leverages age's proven cryptography and file format
## Quick Setup (30 seconds)
```bash
# 1. Download age-store
curl -O https://raw.githubusercontent.com/itsfarseen/age-store/main/age-store.py
chmod +x age-store.py
# 2. Initialize yourself as a user
./age-store.py init-user
# 3. Bootstrap the store (first time only)
./age-store.py admin bootstrap myusername
# 4. Add your first secret
./age-store.py add my-secret-file
# 5. View it anytime
./age-store.py view my-secret-file
```
That's it! Your secrets are now encrypted and ready to share with your team.
## Prerequisites
- Python 3.6+
- [age](https://age-encryption.org/) (`apt install age` or `brew install age`)
## Everyday Usage
```bash
# Add any file to the encrypted store
./age-store.py add config.json
./age-store.py add .env
# View files instantly
./age-store.py view config.json
./age-store.py ls
# Bundle multiple files with size headers
./age-store.py bundle config.json .env secrets.txt
# Launch shell with secrets as environment variables
./age-store.py env-shell app.env
# Share access with teammates
./age-store.py admin add-user alice age1abc123...
./age-store.py admin list-users
```
## How It Works
Age Store uses a master keypair to encrypt all secrets. This master private key is encrypted with the age public keys of all authorized users. To access a secret, users decrypt the master key with their personal age private key, then decrypt the secret files.
When users are added, the master key is re-encrypted for all current users plus the new one. When users are removed, a new master keypair is generated, all secrets are re-encrypted, and the new master key is shared only with remaining users.
## Team Collaboration
```bash
# Alice shares her public key
alice$ ./age-store.py show-pubkey
Age public key: age1alice123...
# Bob adds Alice to the store
bob$ ./age-store.py admin add-user alice age1alice123...
# Now Alice can access all secrets
alice$ ./age-store.py view shared-config.json
```
## Commands Reference
### Core Commands
- `init-user [--unencrypted]` - Generate your age keypair (run once). By default creates encrypted keypair; use `--unencrypted` for plaintext
- `show-pubkey` - Display your public key to share with teammates
- `version` - Show version information
- `doctor` - Run health checks and diagnostics
### File Operations
- `add [--force]` - Encrypt and store any file. Use `--force` to overwrite existing files
- `view ` - Decrypt and view a stored file (specify name without .enc extension)
- `bundle ...` - Decrypt and output multiple files with headers showing file sizes in format `-- `
```
$ ./age-store.py bundle config.json .env
-- 156 config.json
{"api_key": "secret123", "db_host": "localhost"}
-- 45 .env
DATABASE_URL=postgresql://user:pass@localhost/db
```
- `env-shell [options] [-- ...]` - Launch shell with environment variables loaded from secrets
```
# Create env file mapping variables to secret files
$ echo "API_KEY=api-key.txt" > app.env
$ echo "DB_PASSWORD=db-pass.txt" >> app.env
# Launch shell with secrets as environment variables (shows prompt)
$ ./age-store.py env-shell app.env
(age-store:app) user@host:~$ echo $API_KEY
# Use custom shell and pass arguments
$ ./age-store.py env-shell app.env --shell /bin/zsh -- -c 'echo $API_KEY'
# Disable prompt modification
$ ./age-store.py env-shell app.env --no-prompt
# Use custom prompt prefix
$ ./age-store.py env-shell app.env --custom-prompt "my-app"
(my-app) user@host:~$
# Use hook script for additional environment variables (can set AGE_STORE_PROMPT)
$ echo '#!/bin/bash\necho "COMPUTED_VAR=computed_value"\necho "AGE_STORE_PROMPT=prod-env"' > hook.sh && chmod +x hook.sh
$ ./age-store.py env-shell app.env --hook ./hook.sh
(prod-env) user@host:~$ echo $COMPUTED_VAR $AGE_STORE_ENV
```
**Shell Prompt Options:**
- By default, modifies shell prompt to show environment name: `(age-store:) user@host:~$`
- `--no-prompt`: Disable prompt modification
- `--custom-prompt `: Use custom prompt prefix instead of default
- Hook scripts can set `AGE_STORE_PROMPT` environment variable (CLI `--custom-prompt` takes precedence)
**Available Environment Variables:**
- `AGE_STORE_ENV`: Set to the path of the loaded .env file
- `AGE_STORE_PROMPT`: Can be set by hook scripts to customize prompt (overridden by `--custom-prompt`)
- `ls` - List all available encrypted files
### Team Management (Admin)
- `admin bootstrap ` - Initialize the store with initial user (first time only)
- `admin add-user ` - Give someone access by adding their public key
- `admin remove-user ` - Revoke a user's access
- `admin list-users` - Show all users with access
- `admin rotate-master-key` - Generate new master keypair and re-encrypt for all users
### Migration Tools
- `migrate encrypt-user-secret` - Convert plaintext `user-secret.age` to encrypted `user-secret.age.enc`
## File Layout
```
your-project/
├── age-store.py # The tool (single file)
├── user-secret.age # Your private key
├── master-key.age.enc # Shared master key (encrypted)
├── users.json # Team roster
└── store/ # Your encrypted files
├── config.json.enc
├── secrets.env.enc
└── ssh-key.enc
```
## Why Not [Other Tool]?
- **vs HashiCorp Vault**: No server setup, no complex policies - just files
- **vs pass**: Built for teams from day one, not retrofitted
- **vs 1Password CLI**: No subscription, open source, standard crypto
- **vs git-crypt**: Works with any files, not just git repos
- **vs SOPS**: SOPS is a complex dependency; Age Store is a single Python script you can copy into your project
Age Store gives you Vault-like team secret sharing with the simplicity of a single script.
## Configuration
Age Store can be configured by modifying the constants at the top of the script:
```python
# Constants
STORE_DIR = Path("store")
USERS_CONFIG_FILE = Path("users.json")
USER_SECRET_FILE = Path("user-secret.age")
MASTER_KEY_FILE = Path("master-key.age.enc")
```
- `STORE_DIR`: Directory where encrypted files are stored (default: `store/`)
- `USERS_CONFIG_FILE`: File mapping usernames to public keys (default: `users.json`)
- `USER_SECRET_FILE`: Current user's private key file (default: `user-secret.age`)
- `MASTER_KEY_FILE`: Encrypted master key file (default: `master-key.age.enc`)
Simply edit these paths in the script to customize file locations for your project structure.
## Testing
Age Store includes a comprehensive test suite using shellspec:
```bash
# Run all tests
make -C tests
# Run specific test
make -C tests test-filter TEST="test name"
# Docker tests across different distributions
make -C tests/docker ubuntu # Test on Ubuntu
make -C tests/docker arch # Test on Arch Linux
make -C tests/docker alpine # Test on Alpine Linux
make -C tests/docker all # Test on all distributions
# Filter docker tests by name
make -C tests/docker ubuntu TEST_FILTER="test name"
make -C tests/docker all TEST_FILTER="test name"
```
The test suite validates all functionality across multiple Linux distributions to ensure compatibility.
## Known Issues
See [KNOWN_ISSUES.md](KNOWN_ISSUES.md) for platform-specific compatibility issues.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.