{"id":38535605,"url":"https://github.com/itsfarseen/age-store","last_synced_at":"2026-01-17T07:01:09.905Z","repository":{"id":312325350,"uuid":"1047128289","full_name":"itsfarseen/age-store","owner":"itsfarseen","description":"Share encrypted files across teams.","archived":false,"fork":false,"pushed_at":"2025-09-14T09:18:25.000Z","size":178,"stargazers_count":3,"open_issues_count":2,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-11T14:34:44.407Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/itsfarseen.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-29T19:36:08.000Z","updated_at":"2025-09-14T09:15:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"99bbca5d-bf35-4767-affa-d1c985609d5f","html_url":"https://github.com/itsfarseen/age-store","commit_stats":null,"previous_names":["itsfarseen/age-store"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/itsfarseen/age-store","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsfarseen%2Fage-store","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsfarseen%2Fage-store/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsfarseen%2Fage-store/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsfarseen%2Fage-store/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/itsfarseen","download_url":"https://codeload.github.com/itsfarseen/age-store/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itsfarseen%2Fage-store/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28503019,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T06:57:29.758Z","status":"ssl_error","status_checked_at":"2026-01-17T06:56:03.931Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-17T07:01:08.025Z","updated_at":"2026-01-17T07:01:09.730Z","avatar_url":"https://github.com/itsfarseen.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Age Store\n\n[![CI/CD](https://github.com/itsfarseen/age-store/actions/workflows/ci.yml/badge.svg)](https://github.com/itsfarseen/age-store/actions/workflows/ci.yml)\n[![GitHub release](https://img.shields.io/github/v/release/itsfarseen/age-store)](https://github.com/itsfarseen/age-store/releases/latest)\n\n\u003e **📋 See [CHANGELOG.md](CHANGELOG.md) for version history and migration instructions**\n\nA 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.\n\n## Why Age Store?\n\n- **Built on Standards**: Uses the widely-adopted `age` encryption tool under the hood\n- **Dead Simple**: Just a single Python script with an intuitive CLI\n- **No Dependencies**: Only requires Python 3.6+ and the standard `age` tool\n- **Team-Friendly**: Easy multi-user access control with public key sharing\n- **Secure by Design**: Leverages age's proven cryptography and file format\n\n## Quick Setup (30 seconds)\n\n```bash\n# 1. Download age-store\ncurl -O https://raw.githubusercontent.com/itsfarseen/age-store/main/age-store.py\nchmod +x age-store.py\n\n# 2. Initialize yourself as a user\n./age-store.py init-user\n\n# 3. Bootstrap the store (first time only)\n./age-store.py admin bootstrap myusername\n\n# 4. Add your first secret\n./age-store.py add my-secret-file\n\n# 5. View it anytime\n./age-store.py view my-secret-file\n```\n\nThat's it! Your secrets are now encrypted and ready to share with your team.\n\n## Prerequisites\n\n- Python 3.6+\n- [age](https://age-encryption.org/) (`apt install age` or `brew install age`)\n\n## Everyday Usage\n\n```bash\n# Add any file to the encrypted store\n./age-store.py add config.json\n./age-store.py add .env\n\n# View files instantly \n./age-store.py view config.json\n./age-store.py ls\n\n# Bundle multiple files with size headers\n./age-store.py bundle config.json .env secrets.txt\n\n# Launch shell with secrets as environment variables\n./age-store.py env-shell app.env\n\n# Share access with teammates\n./age-store.py admin add-user alice age1abc123...\n./age-store.py admin list-users\n```\n\n## How It Works\n\nAge 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.\n\nWhen 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.\n\n## Team Collaboration\n\n```bash\n# Alice shares her public key\nalice$ ./age-store.py show-pubkey\nAge public key: age1alice123...\n\n# Bob adds Alice to the store\nbob$ ./age-store.py admin add-user alice age1alice123...\n\n# Now Alice can access all secrets\nalice$ ./age-store.py view shared-config.json\n```\n\n## Commands Reference\n\n### Core Commands\n- `init-user [--unencrypted]` - Generate your age keypair (run once). By default creates encrypted keypair; use `--unencrypted` for plaintext\n- `show-pubkey` - Display your public key to share with teammates\n- `version` - Show version information\n- `doctor` - Run health checks and diagnostics\n\n### File Operations\n- `add \u003cfile\u003e [--force]` - Encrypt and store any file. Use `--force` to overwrite existing files\n- `view \u003cfile\u003e` - Decrypt and view a stored file (specify name without .enc extension)\n- `bundle \u003cfile1\u003e \u003cfile2\u003e ...` - Decrypt and output multiple files with headers showing file sizes in format `-- \u003csize\u003e \u003cfilename\u003e`\n  ```\n  $ ./age-store.py bundle config.json .env\n  -- 156 config.json\n  {\"api_key\": \"secret123\", \"db_host\": \"localhost\"}\n  \n  -- 45 .env\n  DATABASE_URL=postgresql://user:pass@localhost/db\n  ```\n- `env-shell \u003cenv_file\u003e [options] [-- \u003cargs\u003e...]` - Launch shell with environment variables loaded from secrets\n  ```\n  # Create env file mapping variables to secret files\n  $ echo \"API_KEY=api-key.txt\" \u003e app.env\n  $ echo \"DB_PASSWORD=db-pass.txt\" \u003e\u003e app.env\n  \n  # Launch shell with secrets as environment variables (shows prompt)\n  $ ./age-store.py env-shell app.env\n  (age-store:app) user@host:~$ echo $API_KEY\n  \n  # Use custom shell and pass arguments\n  $ ./age-store.py env-shell app.env --shell /bin/zsh -- -c 'echo $API_KEY'\n  \n  # Disable prompt modification\n  $ ./age-store.py env-shell app.env --no-prompt\n  \n  # Use custom prompt prefix\n  $ ./age-store.py env-shell app.env --custom-prompt \"my-app\"\n  (my-app) user@host:~$ \n  \n  # Use hook script for additional environment variables (can set AGE_STORE_PROMPT)\n  $ echo '#!/bin/bash\\necho \"COMPUTED_VAR=computed_value\"\\necho \"AGE_STORE_PROMPT=prod-env\"' \u003e hook.sh \u0026\u0026 chmod +x hook.sh\n  $ ./age-store.py env-shell app.env --hook ./hook.sh\n  (prod-env) user@host:~$ echo $COMPUTED_VAR $AGE_STORE_ENV\n  ```\n  \n  **Shell Prompt Options:**\n  - By default, modifies shell prompt to show environment name: `(age-store:\u003cenv-file\u003e) user@host:~$`\n  - `--no-prompt`: Disable prompt modification\n  - `--custom-prompt \u003ctext\u003e`: Use custom prompt prefix instead of default\n  - Hook scripts can set `AGE_STORE_PROMPT` environment variable (CLI `--custom-prompt` takes precedence)\n  \n  **Available Environment Variables:**\n  - `AGE_STORE_ENV`: Set to the path of the loaded .env file\n  - `AGE_STORE_PROMPT`: Can be set by hook scripts to customize prompt (overridden by `--custom-prompt`)\n- `ls` - List all available encrypted files\n\n### Team Management (Admin)\n- `admin bootstrap \u003cusername\u003e` - Initialize the store with initial user (first time only)\n- `admin add-user \u003cusername\u003e \u003cage_pubkey\u003e` - Give someone access by adding their public key\n- `admin remove-user \u003cusername\u003e` - Revoke a user's access\n- `admin list-users` - Show all users with access\n- `admin rotate-master-key` - Generate new master keypair and re-encrypt for all users\n\n### Migration Tools\n- `migrate encrypt-user-secret` - Convert plaintext `user-secret.age` to encrypted `user-secret.age.enc`\n\n## File Layout\n\n```\nyour-project/\n├── age-store.py          # The tool (single file)\n├── user-secret.age       # Your private key\n├── master-key.age.enc   # Shared master key (encrypted)\n├── users.json           # Team roster\n└── store/               # Your encrypted files\n    ├── config.json.enc\n    ├── secrets.env.enc\n    └── ssh-key.enc\n```\n\n## Why Not [Other Tool]?\n\n- **vs HashiCorp Vault**: No server setup, no complex policies - just files\n- **vs pass**: Built for teams from day one, not retrofitted\n- **vs 1Password CLI**: No subscription, open source, standard crypto\n- **vs git-crypt**: Works with any files, not just git repos\n- **vs SOPS**: SOPS is a complex dependency; Age Store is a single Python script you can copy into your project\n\nAge Store gives you Vault-like team secret sharing with the simplicity of a single script.\n\n## Configuration\n\nAge Store can be configured by modifying the constants at the top of the script:\n\n```python\n# Constants\nSTORE_DIR = Path(\"store\")\nUSERS_CONFIG_FILE = Path(\"users.json\")\nUSER_SECRET_FILE = Path(\"user-secret.age\")\nMASTER_KEY_FILE = Path(\"master-key.age.enc\")\n```\n\n- `STORE_DIR`: Directory where encrypted files are stored (default: `store/`)\n- `USERS_CONFIG_FILE`: File mapping usernames to public keys (default: `users.json`)\n- `USER_SECRET_FILE`: Current user's private key file (default: `user-secret.age`)\n- `MASTER_KEY_FILE`: Encrypted master key file (default: `master-key.age.enc`)\n\nSimply edit these paths in the script to customize file locations for your project structure.\n\n## Testing\n\nAge Store includes a comprehensive test suite using shellspec:\n\n```bash\n# Run all tests\nmake -C tests\n\n# Run specific test\nmake -C tests test-filter TEST=\"test name\"\n\n# Docker tests across different distributions\nmake -C tests/docker ubuntu    # Test on Ubuntu\nmake -C tests/docker arch      # Test on Arch Linux  \nmake -C tests/docker alpine    # Test on Alpine Linux\nmake -C tests/docker all       # Test on all distributions\n\n# Filter docker tests by name\nmake -C tests/docker ubuntu TEST_FILTER=\"test name\"\nmake -C tests/docker all TEST_FILTER=\"test name\"\n```\n\nThe test suite validates all functionality across multiple Linux distributions to ensure compatibility.\n\n## Known Issues\n\nSee [KNOWN_ISSUES.md](KNOWN_ISSUES.md) for platform-specific compatibility issues.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitsfarseen%2Fage-store","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fitsfarseen%2Fage-store","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitsfarseen%2Fage-store/lists"}